PF4: Update "fetching info ..." and refresh animation
Currently the "refresh" animations look quite different depending on the page and the type of event (refresh button, initial page load, ...). This tries to make a start by updating the "Fetching info ..." animation (shown on initial page load) with Patternfly's "empty state" pattern [1] in combination with an animated spinner. For "Refreshable" pages, a similar animation is used in the upper right corner (like before) but with an updated spinner and icon. By using a dedicated React component rather than a base class, the "refresh" button can be more nicely integrated into the layout of the page/container and it doesn't look "out of scope" for the refreshable component. For the API page I've removed the refresh completely since it wasn't implemented at all. [1] https://www.patternfly.org/v4/documentation/react/components/emptystate Change-Id: I2274c212f14aece27ff49bfc7900d9b1a0fd01d0
This commit is contained in:
parent
141347e082
commit
ed9d0446d5
@ -64,6 +64,7 @@ import {
|
|||||||
} from '@patternfly/react-icons'
|
} from '@patternfly/react-icons'
|
||||||
|
|
||||||
import ErrorBoundary from './containers/ErrorBoundary'
|
import ErrorBoundary from './containers/ErrorBoundary'
|
||||||
|
import { Fetching } from './containers/Fetching'
|
||||||
import SelectTz from './containers/timezone/SelectTz'
|
import SelectTz from './containers/timezone/SelectTz'
|
||||||
import logo from './images/logo.svg'
|
import logo from './images/logo.svg'
|
||||||
import { clearError } from './actions/errors'
|
import { clearError } from './actions/errors'
|
||||||
@ -126,7 +127,7 @@ class App extends React.Component {
|
|||||||
const allRoutes = []
|
const allRoutes = []
|
||||||
|
|
||||||
if (info.isFetching) {
|
if (info.isFetching) {
|
||||||
return (<h2>Fetching info...</h2>)
|
return <Fetching />
|
||||||
}
|
}
|
||||||
this.menu
|
this.menu
|
||||||
// Do not include '/tenants' route in white-label setup
|
// Do not include '/tenants' route in white-label setup
|
||||||
|
74
web/src/containers/Fetching.jsx
Normal file
74
web/src/containers/Fetching.jsx
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// Copyright 2020 BMW Group
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
// not use this file except in compliance with the License. You may obtain
|
||||||
|
// a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
// License for the specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
import * as React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Title,
|
||||||
|
EmptyState,
|
||||||
|
EmptyStateVariant,
|
||||||
|
Spinner,
|
||||||
|
} from '@patternfly/react-core'
|
||||||
|
|
||||||
|
import { SyncIcon } from '@patternfly/react-icons'
|
||||||
|
|
||||||
|
function Fetchable(props) {
|
||||||
|
const { isFetching, fetchCallback } = props
|
||||||
|
let content = null
|
||||||
|
|
||||||
|
if (isFetching) {
|
||||||
|
content = (
|
||||||
|
<div>
|
||||||
|
<Spinner size="md" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
content = (
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
isInline icon={<SyncIcon />}
|
||||||
|
onClick={() => {fetchCallback({force: true})}}
|
||||||
|
style={{textDecoration: 'none'}}
|
||||||
|
>
|
||||||
|
refresh
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{float: 'right'}}>
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Fetchable.propTypes = {
|
||||||
|
isFetching: PropTypes.bool,
|
||||||
|
fetchCallback: PropTypes.func,
|
||||||
|
}
|
||||||
|
|
||||||
|
function Fetching() {
|
||||||
|
return (
|
||||||
|
<EmptyState variant={EmptyStateVariant.small}>
|
||||||
|
<Spinner />
|
||||||
|
<Title headingLevel="h4" size="lg">
|
||||||
|
Fetching info...
|
||||||
|
</Title>
|
||||||
|
</EmptyState>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Fetchable, Fetching }
|
@ -1,57 +0,0 @@
|
|||||||
// Copyright 2018 Red Hat, Inc
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
// Boiler plate code to manage refresh button
|
|
||||||
|
|
||||||
import * as React from 'react'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
import {
|
|
||||||
Icon,
|
|
||||||
Spinner
|
|
||||||
} from 'patternfly-react'
|
|
||||||
|
|
||||||
|
|
||||||
class Refreshable extends React.Component {
|
|
||||||
static propTypes = {
|
|
||||||
tenant: PropTypes.object,
|
|
||||||
remoteData: PropTypes.object,
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount () {
|
|
||||||
if (this.props.tenant.name) {
|
|
||||||
this.updateData()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate (prevProps) {
|
|
||||||
if (this.props.tenant.name !== prevProps.tenant.name) {
|
|
||||||
this.updateData()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderSpinner () {
|
|
||||||
const { remoteData } = this.props
|
|
||||||
return (
|
|
||||||
<Spinner loading={ remoteData.isFetching }>
|
|
||||||
{/* Lint warning jsx-a11y/anchor-is-valid */}
|
|
||||||
{/* eslint-disable-next-line */}
|
|
||||||
<a className="refresh" onClick={() => {this.updateData(true)}}>
|
|
||||||
<Icon type="fa" name="refresh" /> refresh
|
|
||||||
</a>
|
|
||||||
</Spinner>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Refreshable
|
|
@ -18,16 +18,17 @@ import PropTypes from 'prop-types'
|
|||||||
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
||||||
|
|
||||||
import { fetchBuildIfNeeded } from '../actions/build'
|
import { fetchBuildIfNeeded } from '../actions/build'
|
||||||
import Refreshable from '../containers/Refreshable'
|
import { Fetchable } from '../containers/Fetching'
|
||||||
import Build from '../containers/build/Build'
|
import Build from '../containers/build/Build'
|
||||||
import Summary from '../containers/build/Summary'
|
import Summary from '../containers/build/Summary'
|
||||||
|
|
||||||
|
|
||||||
class BuildPage extends Refreshable {
|
class BuildPage extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
match: PropTypes.object.isRequired,
|
match: PropTypes.object.isRequired,
|
||||||
remoteData: PropTypes.object,
|
remoteData: PropTypes.object,
|
||||||
tenant: PropTypes.object
|
tenant: PropTypes.object,
|
||||||
|
dispatch: PropTypes.func,
|
||||||
}
|
}
|
||||||
|
|
||||||
updateData = (force) => {
|
updateData = (force) => {
|
||||||
@ -37,7 +38,15 @@ class BuildPage extends Refreshable {
|
|||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
document.title = 'Zuul Build'
|
document.title = 'Zuul Build'
|
||||||
super.componentDidMount()
|
if (this.props.tenant.name) {
|
||||||
|
this.updateData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate (prevProps) {
|
||||||
|
if (this.props.tenant.name !== prevProps.tenant.name) {
|
||||||
|
this.updateData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
@ -45,9 +54,12 @@ class BuildPage extends Refreshable {
|
|||||||
const build = remoteData.builds[this.props.match.params.buildId]
|
const build = remoteData.builds[this.props.match.params.buildId]
|
||||||
return (
|
return (
|
||||||
<PageSection variant={PageSectionVariants.light}>
|
<PageSection variant={PageSectionVariants.light}>
|
||||||
<div style={{float: 'right'}}>
|
<PageSection style={{paddingRight: '5px'}}>
|
||||||
{this.renderSpinner()}
|
<Fetchable
|
||||||
</div>
|
isFetching={remoteData.isFetching}
|
||||||
|
fetchCallback={this.updateData}
|
||||||
|
/>
|
||||||
|
</PageSection>
|
||||||
{build &&
|
{build &&
|
||||||
<Build build={build} active='summary'>
|
<Build build={build} active='summary'>
|
||||||
<Summary build={build}/>
|
<Summary build={build}/>
|
||||||
|
@ -18,16 +18,18 @@ import PropTypes from 'prop-types'
|
|||||||
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
||||||
|
|
||||||
import { fetchBuildIfNeeded } from '../actions/build'
|
import { fetchBuildIfNeeded } from '../actions/build'
|
||||||
import Refreshable from '../containers/Refreshable'
|
import { Fetchable } from '../containers/Fetching'
|
||||||
import Build from '../containers/build/Build'
|
import Build from '../containers/build/Build'
|
||||||
import Console from '../containers/build/Console'
|
import Console from '../containers/build/Console'
|
||||||
|
|
||||||
|
|
||||||
class BuildConsolePage extends Refreshable {
|
class BuildConsolePage extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
match: PropTypes.object.isRequired,
|
match: PropTypes.object.isRequired,
|
||||||
remoteData: PropTypes.object,
|
remoteData: PropTypes.object,
|
||||||
tenant: PropTypes.object
|
tenant: PropTypes.object,
|
||||||
|
dispatch: PropTypes.func,
|
||||||
|
location: PropTypes.object,
|
||||||
}
|
}
|
||||||
|
|
||||||
updateData = (force) => {
|
updateData = (force) => {
|
||||||
@ -37,7 +39,15 @@ class BuildConsolePage extends Refreshable {
|
|||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
document.title = 'Zuul Build'
|
document.title = 'Zuul Build'
|
||||||
super.componentDidMount()
|
if (this.props.tenant.name) {
|
||||||
|
this.updateData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate (prevProps) {
|
||||||
|
if (this.props.tenant.name !== prevProps.tenant.name) {
|
||||||
|
this.updateData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
@ -47,9 +57,12 @@ class BuildConsolePage extends Refreshable {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PageSection variant={PageSectionVariants.light}>
|
<PageSection variant={PageSectionVariants.light}>
|
||||||
<div style={{float: 'right'}}>
|
<PageSection style={{paddingRight: '5px'}}>
|
||||||
{this.renderSpinner()}
|
<Fetchable
|
||||||
</div>
|
isFetching={remoteData.isFetching}
|
||||||
|
fetchCallback={this.updateData}
|
||||||
|
/>
|
||||||
|
</PageSection>
|
||||||
{build && build.output &&
|
{build && build.output &&
|
||||||
<Build build={build} active='console'>
|
<Build build={build} active='console'>
|
||||||
<Console
|
<Console
|
||||||
|
@ -18,16 +18,17 @@ import PropTypes from 'prop-types'
|
|||||||
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
||||||
|
|
||||||
import { fetchBuildIfNeeded } from '../actions/build'
|
import { fetchBuildIfNeeded } from '../actions/build'
|
||||||
import Refreshable from '../containers/Refreshable'
|
import { Fetchable } from '../containers/Fetching'
|
||||||
import Build from '../containers/build/Build'
|
import Build from '../containers/build/Build'
|
||||||
import Manifest from '../containers/build/Manifest'
|
import Manifest from '../containers/build/Manifest'
|
||||||
|
|
||||||
|
|
||||||
class BuildLogsPage extends Refreshable {
|
class BuildLogsPage extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
match: PropTypes.object.isRequired,
|
match: PropTypes.object.isRequired,
|
||||||
remoteData: PropTypes.object,
|
remoteData: PropTypes.object,
|
||||||
tenant: PropTypes.object
|
tenant: PropTypes.object,
|
||||||
|
dispatch: PropTypes.func,
|
||||||
}
|
}
|
||||||
|
|
||||||
updateData = (force) => {
|
updateData = (force) => {
|
||||||
@ -37,7 +38,15 @@ class BuildLogsPage extends Refreshable {
|
|||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
document.title = 'Zuul Build'
|
document.title = 'Zuul Build'
|
||||||
super.componentDidMount()
|
if (this.props.tenant.name) {
|
||||||
|
this.updateData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate (prevProps) {
|
||||||
|
if (this.props.tenant.name !== prevProps.tenant.name) {
|
||||||
|
this.updateData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
@ -45,9 +54,12 @@ class BuildLogsPage extends Refreshable {
|
|||||||
const build = remoteData.builds[this.props.match.params.buildId]
|
const build = remoteData.builds[this.props.match.params.buildId]
|
||||||
return (
|
return (
|
||||||
<PageSection variant={PageSectionVariants.light}>
|
<PageSection variant={PageSectionVariants.light}>
|
||||||
<div style={{float: 'right'}}>
|
<PageSection style={{paddingRight: '5px'}}>
|
||||||
{this.renderSpinner()}
|
<Fetchable
|
||||||
</div>
|
isFetching={remoteData.isFetching}
|
||||||
|
fetchCallback={this.updateData}
|
||||||
|
/>
|
||||||
|
</PageSection>
|
||||||
{build && build.manifest &&
|
{build && build.manifest &&
|
||||||
<Build build={build} active='logs'>
|
<Build build={build} active='logs'>
|
||||||
<Manifest tenant={this.props.tenant} build={build}/>
|
<Manifest tenant={this.props.tenant} build={build}/>
|
||||||
|
@ -18,15 +18,16 @@ import PropTypes from 'prop-types'
|
|||||||
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
||||||
|
|
||||||
import { fetchBuildsetIfNeeded } from '../actions/build'
|
import { fetchBuildsetIfNeeded } from '../actions/build'
|
||||||
import Refreshable from '../containers/Refreshable'
|
import { Fetchable } from '../containers/Fetching'
|
||||||
import Buildset from '../containers/build/Buildset'
|
import Buildset from '../containers/build/Buildset'
|
||||||
|
|
||||||
|
|
||||||
class BuildsetPage extends Refreshable {
|
class BuildsetPage extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
match: PropTypes.object.isRequired,
|
match: PropTypes.object.isRequired,
|
||||||
remoteData: PropTypes.object,
|
remoteData: PropTypes.object,
|
||||||
tenant: PropTypes.object
|
tenant: PropTypes.object,
|
||||||
|
dispatch: PropTypes.func,
|
||||||
}
|
}
|
||||||
|
|
||||||
updateData = (force) => {
|
updateData = (force) => {
|
||||||
@ -36,17 +37,29 @@ class BuildsetPage extends Refreshable {
|
|||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
document.title = 'Zuul Buildset'
|
document.title = 'Zuul Buildset'
|
||||||
super.componentDidMount()
|
if (this.props.tenant.name) {
|
||||||
|
this.updateData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate (prevProps) {
|
||||||
|
if (this.props.tenant.name !== prevProps.tenant.name) {
|
||||||
|
this.updateData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { remoteData } = this.props
|
const { remoteData } = this.props
|
||||||
|
|
||||||
const buildset = remoteData.buildsets[this.props.match.params.buildsetId]
|
const buildset = remoteData.buildsets[this.props.match.params.buildsetId]
|
||||||
return (
|
return (
|
||||||
<PageSection variant={PageSectionVariants.light}>
|
<PageSection variant={PageSectionVariants.light}>
|
||||||
<div style={{float: 'right'}}>
|
<PageSection style={{paddingRight: '5px'}}>
|
||||||
{this.renderSpinner()}
|
<Fetchable
|
||||||
</div>
|
isFetching={remoteData.isFetching}
|
||||||
|
fetchCallback={this.updateData}
|
||||||
|
/>
|
||||||
|
</PageSection>
|
||||||
{buildset && <Buildset buildset={buildset}/>}
|
{buildset && <Buildset buildset={buildset}/>}
|
||||||
</PageSection>
|
</PageSection>
|
||||||
)
|
)
|
||||||
|
@ -19,10 +19,10 @@ import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
|||||||
|
|
||||||
import { fetchChangeIfNeeded } from '../actions/change'
|
import { fetchChangeIfNeeded } from '../actions/change'
|
||||||
import ChangePanel from '../containers/status/ChangePanel'
|
import ChangePanel from '../containers/status/ChangePanel'
|
||||||
import Refreshable from '../containers/Refreshable'
|
import { Fetchable } from '../containers/Fetching'
|
||||||
|
|
||||||
|
|
||||||
class ChangeStatusPage extends Refreshable {
|
class ChangeStatusPage extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
match: PropTypes.object.isRequired,
|
match: PropTypes.object.isRequired,
|
||||||
tenant: PropTypes.object,
|
tenant: PropTypes.object,
|
||||||
@ -43,7 +43,15 @@ class ChangeStatusPage extends Refreshable {
|
|||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
document.title = this.props.match.params.changeId + ' | Zuul Status'
|
document.title = this.props.match.params.changeId + ' | Zuul Status'
|
||||||
super.componentDidMount()
|
if (this.props.tenant.name) {
|
||||||
|
this.updateData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate (prevProps) {
|
||||||
|
if (this.props.tenant.name !== prevProps.tenant.name) {
|
||||||
|
this.updateData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount () {
|
||||||
@ -58,9 +66,12 @@ class ChangeStatusPage extends Refreshable {
|
|||||||
const change = remoteData.change
|
const change = remoteData.change
|
||||||
return (
|
return (
|
||||||
<PageSection variant={PageSectionVariants.light}>
|
<PageSection variant={PageSectionVariants.light}>
|
||||||
<div style={{float: 'right'}}>
|
<PageSection style={{paddingRight: '5px'}}>
|
||||||
{this.renderSpinner()}
|
<Fetchable
|
||||||
</div><br />
|
isFetching={remoteData.isFetching}
|
||||||
|
fetchCallback={this.updateData}
|
||||||
|
/>
|
||||||
|
</PageSection>
|
||||||
{change && change.map((item, idx) => (
|
{change && change.map((item, idx) => (
|
||||||
<div className='row zuul-change-content' key={idx}>
|
<div className='row zuul-change-content' key={idx}>
|
||||||
<ChangePanel
|
<ChangePanel
|
||||||
|
@ -18,11 +18,11 @@ import PropTypes from 'prop-types'
|
|||||||
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
||||||
|
|
||||||
import Job from '../containers/job/Job'
|
import Job from '../containers/job/Job'
|
||||||
import Refreshable from '../containers/Refreshable'
|
import { Fetchable } from '../containers/Fetching'
|
||||||
import { fetchJobIfNeeded } from '../actions/job'
|
import { fetchJobIfNeeded } from '../actions/job'
|
||||||
|
|
||||||
|
|
||||||
class JobPage extends Refreshable {
|
class JobPage extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
match: PropTypes.object.isRequired,
|
match: PropTypes.object.isRequired,
|
||||||
tenant: PropTypes.object,
|
tenant: PropTypes.object,
|
||||||
@ -37,7 +37,9 @@ class JobPage extends Refreshable {
|
|||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
document.title = 'Zuul Job | ' + this.props.match.params.jobName
|
document.title = 'Zuul Job | ' + this.props.match.params.jobName
|
||||||
super.componentDidMount()
|
if (this.props.tenant.name) {
|
||||||
|
this.updateData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate (prevProps) {
|
componentDidUpdate (prevProps) {
|
||||||
@ -53,9 +55,12 @@ class JobPage extends Refreshable {
|
|||||||
const jobName = this.props.match.params.jobName
|
const jobName = this.props.match.params.jobName
|
||||||
return (
|
return (
|
||||||
<PageSection variant={PageSectionVariants.light}>
|
<PageSection variant={PageSectionVariants.light}>
|
||||||
<div style={{float: 'right'}}>
|
<PageSection style={{paddingRight: '5px'}}>
|
||||||
{this.renderSpinner()}
|
<Fetchable
|
||||||
</div>
|
isFetching={remoteData.isFetching}
|
||||||
|
fetchCallback={this.updateData}
|
||||||
|
/>
|
||||||
|
</PageSection>
|
||||||
{tenantJobs && tenantJobs[jobName] && <Job job={tenantJobs[jobName]} />}
|
{tenantJobs && tenantJobs[jobName] && <Job job={tenantJobs[jobName]} />}
|
||||||
</PageSection>
|
</PageSection>
|
||||||
)
|
)
|
||||||
|
@ -18,34 +18,46 @@ import { connect } from 'react-redux'
|
|||||||
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
||||||
|
|
||||||
import { fetchJobsIfNeeded } from '../actions/jobs'
|
import { fetchJobsIfNeeded } from '../actions/jobs'
|
||||||
import Refreshable from '../containers/Refreshable'
|
import { Fetchable } from '../containers/Fetching'
|
||||||
import Jobs from '../containers/jobs/Jobs'
|
import Jobs from '../containers/jobs/Jobs'
|
||||||
|
|
||||||
|
|
||||||
class JobsPage extends Refreshable {
|
class JobsPage extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
tenant: PropTypes.object,
|
tenant: PropTypes.object,
|
||||||
remoteData: PropTypes.object,
|
remoteData: PropTypes.object,
|
||||||
dispatch: PropTypes.func
|
dispatch: PropTypes.func
|
||||||
}
|
}
|
||||||
|
|
||||||
updateData (force) {
|
updateData = (force) => {
|
||||||
this.props.dispatch(fetchJobsIfNeeded(this.props.tenant, force))
|
this.props.dispatch(fetchJobsIfNeeded(this.props.tenant, force))
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
document.title = 'Zuul Jobs'
|
document.title = 'Zuul Jobs'
|
||||||
super.componentDidMount()
|
if (this.props.tenant.name) {
|
||||||
|
this.updateData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate (prevProps) {
|
||||||
|
if (this.props.tenant.name !== prevProps.tenant.name) {
|
||||||
|
this.updateData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { remoteData } = this.props
|
const { remoteData } = this.props
|
||||||
|
|
||||||
const jobs = remoteData.jobs[this.props.tenant.name]
|
const jobs = remoteData.jobs[this.props.tenant.name]
|
||||||
return (
|
return (
|
||||||
<PageSection variant={PageSectionVariants.light}>
|
<PageSection variant={PageSectionVariants.light}>
|
||||||
<div style={{float: 'right'}}>
|
<PageSection style={{paddingRight: '5px'}}>
|
||||||
{this.renderSpinner()}
|
<Fetchable
|
||||||
</div>
|
isFetching={remoteData.isFetching}
|
||||||
|
fetchCallback={this.updateData}
|
||||||
|
/>
|
||||||
|
</PageSection>
|
||||||
{jobs && jobs.length > 0 &&
|
{jobs && jobs.length > 0 &&
|
||||||
<Jobs
|
<Jobs
|
||||||
jobs={jobs}
|
jobs={jobs}
|
||||||
|
@ -19,10 +19,10 @@ import { Table } from 'patternfly-react'
|
|||||||
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
||||||
|
|
||||||
import { fetchLabelsIfNeeded } from '../actions/labels'
|
import { fetchLabelsIfNeeded } from '../actions/labels'
|
||||||
import Refreshable from '../containers/Refreshable'
|
import { Fetchable, Fetching } from '../containers/Fetching'
|
||||||
|
|
||||||
|
|
||||||
class LabelsPage extends Refreshable {
|
class LabelsPage extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
tenant: PropTypes.object,
|
tenant: PropTypes.object,
|
||||||
remoteData: PropTypes.object,
|
remoteData: PropTypes.object,
|
||||||
@ -35,7 +35,15 @@ class LabelsPage extends Refreshable {
|
|||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
document.title = 'Zuul Labels'
|
document.title = 'Zuul Labels'
|
||||||
super.componentDidMount()
|
if (this.props.tenant.name) {
|
||||||
|
this.updateData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate (prevProps) {
|
||||||
|
if (this.props.tenant.name !== prevProps.tenant.name) {
|
||||||
|
this.updateData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
@ -43,7 +51,7 @@ class LabelsPage extends Refreshable {
|
|||||||
const labels = remoteData.labels[this.props.tenant.name]
|
const labels = remoteData.labels[this.props.tenant.name]
|
||||||
|
|
||||||
if (!labels) {
|
if (!labels) {
|
||||||
return (<p>Loading...</p>)
|
return <Fetching />
|
||||||
}
|
}
|
||||||
|
|
||||||
const headerFormat = value => <Table.Heading>{value}</Table.Heading>
|
const headerFormat = value => <Table.Heading>{value}</Table.Heading>
|
||||||
@ -61,9 +69,12 @@ class LabelsPage extends Refreshable {
|
|||||||
})
|
})
|
||||||
return (
|
return (
|
||||||
<PageSection variant={PageSectionVariants.light}>
|
<PageSection variant={PageSectionVariants.light}>
|
||||||
<div style={{float: 'right'}}>
|
<PageSection style={{paddingRight: '5px'}}>
|
||||||
{this.renderSpinner()}
|
<Fetchable
|
||||||
</div>
|
isFetching={remoteData.isFetching}
|
||||||
|
fetchCallback={this.updateData}
|
||||||
|
/>
|
||||||
|
</PageSection>
|
||||||
<Table.PfProvider
|
<Table.PfProvider
|
||||||
striped
|
striped
|
||||||
bordered
|
bordered
|
||||||
|
@ -19,15 +19,18 @@ import { parse } from 'query-string'
|
|||||||
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
||||||
|
|
||||||
import { fetchLogfileIfNeeded } from '../actions/logfile'
|
import { fetchLogfileIfNeeded } from '../actions/logfile'
|
||||||
import Refreshable from '../containers/Refreshable'
|
import { Fetching } from '../containers/Fetching'
|
||||||
import LogFile from '../containers/logfile/LogFile'
|
import LogFile from '../containers/logfile/LogFile'
|
||||||
|
|
||||||
|
|
||||||
class LogFilePage extends Refreshable {
|
class LogFilePage extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
match: PropTypes.object.isRequired,
|
match: PropTypes.object.isRequired,
|
||||||
remoteData: PropTypes.object,
|
remoteData: PropTypes.object,
|
||||||
tenant: PropTypes.object,
|
tenant: PropTypes.object,
|
||||||
|
dispatch: PropTypes.func,
|
||||||
|
location: PropTypes.object,
|
||||||
|
build: PropTypes.object,
|
||||||
}
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
@ -45,7 +48,9 @@ class LogFilePage extends Refreshable {
|
|||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
document.title = 'Zuul Build Logfile'
|
document.title = 'Zuul Build Logfile'
|
||||||
super.componentDidMount()
|
if (this.props.tenant.name) {
|
||||||
|
this.updateData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
highlightDidUpdate = (lines) => {
|
highlightDidUpdate = (lines) => {
|
||||||
@ -108,13 +113,14 @@ class LogFilePage extends Refreshable {
|
|||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { remoteData } = this.props
|
const { remoteData } = this.props
|
||||||
|
if (remoteData.isFetching) {
|
||||||
|
return <Fetching />
|
||||||
|
}
|
||||||
|
|
||||||
const build = this.props.build.builds[this.props.match.params.buildId]
|
const build = this.props.build.builds[this.props.match.params.buildId]
|
||||||
const severity = parse(this.props.location.search).severity
|
const severity = parse(this.props.location.search).severity
|
||||||
return (
|
return (
|
||||||
<PageSection variant={PageSectionVariants.light}>
|
<PageSection variant={PageSectionVariants.light}>
|
||||||
<div style={{float: 'right'}}>
|
|
||||||
{this.renderSpinner()}
|
|
||||||
</div>
|
|
||||||
{remoteData.data && <LogFile build={build} data={remoteData.data} severity={severity}/>}
|
{remoteData.data && <LogFile build={build} data={remoteData.data} severity={severity}/>}
|
||||||
</PageSection>
|
</PageSection>
|
||||||
)
|
)
|
||||||
|
@ -20,23 +20,31 @@ import * as moment from 'moment'
|
|||||||
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
||||||
|
|
||||||
import { fetchNodesIfNeeded } from '../actions/nodes'
|
import { fetchNodesIfNeeded } from '../actions/nodes'
|
||||||
import Refreshable from '../containers/Refreshable'
|
import { Fetchable } from '../containers/Fetching'
|
||||||
|
|
||||||
|
|
||||||
class NodesPage extends Refreshable {
|
class NodesPage extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
tenant: PropTypes.object,
|
tenant: PropTypes.object,
|
||||||
remoteData: PropTypes.object,
|
remoteData: PropTypes.object,
|
||||||
dispatch: PropTypes.func
|
dispatch: PropTypes.func
|
||||||
}
|
}
|
||||||
|
|
||||||
updateData (force) {
|
updateData = (force) => {
|
||||||
this.props.dispatch(fetchNodesIfNeeded(this.props.tenant, force))
|
this.props.dispatch(fetchNodesIfNeeded(this.props.tenant, force))
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
document.title = 'Zuul Nodes'
|
document.title = 'Zuul Nodes'
|
||||||
super.componentDidMount()
|
if (this.props.tenant.name) {
|
||||||
|
this.updateData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate (prevProps) {
|
||||||
|
if (this.props.tenant.name !== prevProps.tenant.name) {
|
||||||
|
this.updateData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
@ -83,9 +91,12 @@ class NodesPage extends Refreshable {
|
|||||||
})
|
})
|
||||||
return (
|
return (
|
||||||
<PageSection variant={PageSectionVariants.light}>
|
<PageSection variant={PageSectionVariants.light}>
|
||||||
<div style={{float: 'right'}}>
|
<PageSection style={{paddingRight: '5px'}}>
|
||||||
{this.renderSpinner()}
|
<Fetchable
|
||||||
</div>
|
isFetching={remoteData.isFetching}
|
||||||
|
fetchCallback={this.updateData}
|
||||||
|
/>
|
||||||
|
</PageSection>
|
||||||
<Table.PfProvider
|
<Table.PfProvider
|
||||||
striped
|
striped
|
||||||
bordered
|
bordered
|
||||||
|
@ -20,10 +20,9 @@ import 'swagger-ui/dist/swagger-ui.css'
|
|||||||
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
||||||
|
|
||||||
import { fetchOpenApiIfNeeded } from '../actions/openapi'
|
import { fetchOpenApiIfNeeded } from '../actions/openapi'
|
||||||
import Refreshable from '../containers/Refreshable'
|
|
||||||
|
|
||||||
|
|
||||||
class OpenApiPage extends Refreshable {
|
class OpenApiPage extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
tenant: PropTypes.object,
|
tenant: PropTypes.object,
|
||||||
remoteData: PropTypes.object,
|
remoteData: PropTypes.object,
|
||||||
@ -53,9 +52,6 @@ class OpenApiPage extends Refreshable {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<PageSection variant={PageSectionVariants.light}>
|
<PageSection variant={PageSectionVariants.light}>
|
||||||
<div className="pull-right" style={{display: 'flex'}}>
|
|
||||||
{this.renderSpinner()}
|
|
||||||
</div>
|
|
||||||
<div id="swaggerContainer" />
|
<div id="swaggerContainer" />
|
||||||
</PageSection>
|
</PageSection>
|
||||||
)
|
)
|
||||||
|
@ -19,10 +19,10 @@ import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
|||||||
|
|
||||||
import Project from '../containers/project/Project'
|
import Project from '../containers/project/Project'
|
||||||
import { fetchProjectIfNeeded } from '../actions/project'
|
import { fetchProjectIfNeeded } from '../actions/project'
|
||||||
import Refreshable from '../containers/Refreshable'
|
import { Fetchable } from '../containers/Fetching'
|
||||||
|
|
||||||
|
|
||||||
class ProjectPage extends Refreshable {
|
class ProjectPage extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
match: PropTypes.object.isRequired,
|
match: PropTypes.object.isRequired,
|
||||||
tenant: PropTypes.object,
|
tenant: PropTypes.object,
|
||||||
@ -37,7 +37,15 @@ class ProjectPage extends Refreshable {
|
|||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
document.title = 'Zuul Project | ' + this.props.match.params.projectName
|
document.title = 'Zuul Project | ' + this.props.match.params.projectName
|
||||||
super.componentDidMount()
|
if (this.props.tenant.name) {
|
||||||
|
this.updateData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate (prevProps) {
|
||||||
|
if (this.props.tenant.name !== prevProps.tenant.name) {
|
||||||
|
this.updateData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
@ -46,9 +54,12 @@ class ProjectPage extends Refreshable {
|
|||||||
const projectName = this.props.match.params.projectName
|
const projectName = this.props.match.params.projectName
|
||||||
return (
|
return (
|
||||||
<PageSection variant={PageSectionVariants.light}>
|
<PageSection variant={PageSectionVariants.light}>
|
||||||
<div style={{float: 'right'}}>
|
<PageSection style={{paddingRight: '5px'}}>
|
||||||
{this.renderSpinner()}
|
<Fetchable
|
||||||
</div>
|
isFetching={remoteData.isFetching}
|
||||||
|
fetchCallback={this.updateData}
|
||||||
|
/>
|
||||||
|
</PageSection>
|
||||||
{tenantProjects && tenantProjects[projectName] &&
|
{tenantProjects && tenantProjects[projectName] &&
|
||||||
<Project project={tenantProjects[projectName]} />}
|
<Project project={tenantProjects[projectName]} />}
|
||||||
</PageSection>
|
</PageSection>
|
||||||
|
@ -20,31 +20,43 @@ import { Table } from 'patternfly-react'
|
|||||||
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
||||||
|
|
||||||
import { fetchProjectsIfNeeded } from '../actions/projects'
|
import { fetchProjectsIfNeeded } from '../actions/projects'
|
||||||
import Refreshable from '../containers/Refreshable'
|
import { Fetchable, Fetching } from '../containers/Fetching'
|
||||||
|
|
||||||
|
|
||||||
class ProjectsPage extends Refreshable {
|
class ProjectsPage extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
tenant: PropTypes.object,
|
tenant: PropTypes.object,
|
||||||
remoteData: PropTypes.object,
|
remoteData: PropTypes.object,
|
||||||
dispatch: PropTypes.func
|
dispatch: PropTypes.func
|
||||||
}
|
}
|
||||||
|
|
||||||
updateData (force) {
|
updateData = (force) => {
|
||||||
this.props.dispatch(fetchProjectsIfNeeded(this.props.tenant, force))
|
this.props.dispatch(fetchProjectsIfNeeded(this.props.tenant, force))
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
document.title = 'Zuul Projects'
|
document.title = 'Zuul Projects'
|
||||||
super.componentDidMount()
|
if (this.props.tenant.name) {
|
||||||
|
this.updateData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate (prevProps) {
|
||||||
|
if (this.props.tenant.name !== prevProps.tenant.name) {
|
||||||
|
this.updateData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { remoteData } = this.props
|
const { remoteData } = this.props
|
||||||
const projects = remoteData.projects[this.props.tenant.name]
|
const projects = remoteData.projects[this.props.tenant.name]
|
||||||
|
|
||||||
|
// TODO (felix): Can we somehow differentiate between "no projects yet" (due
|
||||||
|
// to fetching) and "no projects at all", so we could show an empty state
|
||||||
|
// in the latter case. The same applies for other pages like labels, nodes,
|
||||||
|
// buildsets, ... as well.
|
||||||
if (!projects) {
|
if (!projects) {
|
||||||
return (<p>Loading...</p>)
|
return <Fetching />
|
||||||
}
|
}
|
||||||
|
|
||||||
const headerFormat = value => <Table.Heading>{value}</Table.Heading>
|
const headerFormat = value => <Table.Heading>{value}</Table.Heading>
|
||||||
@ -86,9 +98,12 @@ class ProjectsPage extends Refreshable {
|
|||||||
})
|
})
|
||||||
return (
|
return (
|
||||||
<PageSection variant={PageSectionVariants.light}>
|
<PageSection variant={PageSectionVariants.light}>
|
||||||
<div style={{float: 'right'}}>
|
<PageSection style={{paddingRight: '5px'}}>
|
||||||
{this.renderSpinner()}
|
<Fetchable
|
||||||
</div>
|
isFetching={remoteData.isFetching}
|
||||||
|
fetchCallback={this.updateData}
|
||||||
|
/>
|
||||||
|
</PageSection>
|
||||||
<Table.PfProvider
|
<Table.PfProvider
|
||||||
striped
|
striped
|
||||||
bordered
|
bordered
|
||||||
|
@ -27,10 +27,10 @@ import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
|||||||
|
|
||||||
import { fetchStatusIfNeeded } from '../actions/status'
|
import { fetchStatusIfNeeded } from '../actions/status'
|
||||||
import Pipeline from '../containers/status/Pipeline'
|
import Pipeline from '../containers/status/Pipeline'
|
||||||
import Refreshable from '../containers/Refreshable'
|
import { Fetchable } from '../containers/Fetching'
|
||||||
|
|
||||||
|
|
||||||
class StatusPage extends Refreshable {
|
class StatusPage extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
location: PropTypes.object,
|
location: PropTypes.object,
|
||||||
tenant: PropTypes.object,
|
tenant: PropTypes.object,
|
||||||
@ -96,10 +96,18 @@ class StatusPage extends Refreshable {
|
|||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
document.title = 'Zuul Status'
|
document.title = 'Zuul Status'
|
||||||
this.loadState()
|
this.loadState()
|
||||||
super.componentDidMount()
|
if (this.props.tenant.name) {
|
||||||
|
this.updateData()
|
||||||
|
}
|
||||||
window.addEventListener('storage', this.loadState)
|
window.addEventListener('storage', this.loadState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidUpdate (prevProps) {
|
||||||
|
if (this.props.tenant.name !== prevProps.tenant.name) {
|
||||||
|
this.updateData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount () {
|
||||||
if (this.timer) {
|
if (this.timer) {
|
||||||
clearTimeout(this.timer)
|
clearTimeout(this.timer)
|
||||||
@ -214,12 +222,15 @@ class StatusPage extends Refreshable {
|
|||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
<PageSection variant={PageSectionVariants.light}>
|
<PageSection variant={PageSectionVariants.light}>
|
||||||
<div className="pull-right" style={{display: 'flex'}}>
|
<div style={{display: 'flex', float: 'right'}}>
|
||||||
{this.renderSpinner()}
|
<Fetchable
|
||||||
|
isFetching={remoteData.isFetching}
|
||||||
|
fetchCallback={this.updateData}
|
||||||
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
defaultChecked={autoReload}
|
defaultChecked={autoReload}
|
||||||
onChange={(e) => {this.setState({autoReload: e.target.checked})}}
|
onChange={(e) => {this.setState({autoReload: e.target.checked})}}
|
||||||
style={{marginTop: '0px'}}>
|
style={{marginTop: '0px', marginLeft: '10px'}}>
|
||||||
auto reload
|
auto reload
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
@ -19,11 +19,11 @@ import { Link } from 'react-router-dom'
|
|||||||
import { Table } from 'patternfly-react'
|
import { Table } from 'patternfly-react'
|
||||||
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
|
||||||
|
|
||||||
import Refreshable from '../containers/Refreshable'
|
import { Fetching } from '../containers/Fetching'
|
||||||
import { fetchTenantsIfNeeded } from '../actions/tenants'
|
import { fetchTenantsIfNeeded } from '../actions/tenants'
|
||||||
|
|
||||||
|
|
||||||
class TenantsPage extends Refreshable {
|
class TenantsPage extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
remoteData: PropTypes.object,
|
remoteData: PropTypes.object,
|
||||||
dispatch: PropTypes.func
|
dispatch: PropTypes.func
|
||||||
@ -43,6 +43,10 @@ class TenantsPage extends Refreshable {
|
|||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { remoteData } = this.props
|
const { remoteData } = this.props
|
||||||
|
if (remoteData.isFetching) {
|
||||||
|
return <Fetching />
|
||||||
|
}
|
||||||
|
|
||||||
const tenants = remoteData.tenants
|
const tenants = remoteData.tenants
|
||||||
const headerFormat = value => <Table.Heading>{value}</Table.Heading>
|
const headerFormat = value => <Table.Heading>{value}</Table.Heading>
|
||||||
const cellFormat = (value) => (
|
const cellFormat = (value) => (
|
||||||
@ -79,9 +83,6 @@ class TenantsPage extends Refreshable {
|
|||||||
})
|
})
|
||||||
return (
|
return (
|
||||||
<PageSection variant={PageSectionVariants.light}>
|
<PageSection variant={PageSectionVariants.light}>
|
||||||
<div style={{float: 'right'}}>
|
|
||||||
{this.renderSpinner()}
|
|
||||||
</div>
|
|
||||||
<Table.PfProvider
|
<Table.PfProvider
|
||||||
striped
|
striped
|
||||||
bordered
|
bordered
|
||||||
|
Loading…
Reference in New Issue
Block a user