diff options
author | Felix Edel <felix.edel@bmw.de> | 2020-09-08 13:53:44 +0000 |
---|---|---|
committer | Felix Edel <felix.edel@bmw.de> | 2020-09-14 14:47:49 +0200 |
commit | 23e2d6a13a3d7bee1e7cefb5294b01a97faf02c9 (patch) | |
tree | 303daa9d4de4a712a7f068889e55683b80b339ca /web/src/pages/Build.jsx | |
parent | 9a729b6b4b22f5a28502d45b2e17be58dade1ff0 (diff) | |
download | zuul-23e2d6a13a3d7bee1e7cefb5294b01a97faf02c9.tar.gz |
Revert "Revert PF4 build page"
The original change was reverted to fix a scrolling bug that was introduced by a different change. Using this commit in combination with [1] should restore the old behaviour.
Applying [2] on top of them should finally get rid of the scrolling issues.
[1]: https://review.opendev.org/#/c/750361/
[2]: https://review.opendev.org/#/c/750322/
This reverts commit b4b5b9fd58dd5d5aeb9d32331e4882d7ef5faf06.
Change-Id: Icd498314762a8edca751413b7ee07b9b72317c5b
Diffstat (limited to 'web/src/pages/Build.jsx')
-rw-r--r-- | web/src/pages/Build.jsx | 233 |
1 files changed, 215 insertions, 18 deletions
diff --git a/web/src/pages/Build.jsx b/web/src/pages/Build.jsx index d4975d972..6384eeb2d 100644 --- a/web/src/pages/Build.jsx +++ b/web/src/pages/Build.jsx @@ -14,13 +14,36 @@ import * as React from 'react' import { connect } from 'react-redux' +import { withRouter } from 'react-router-dom' import PropTypes from 'prop-types' -import { PageSection, PageSectionVariants } from '@patternfly/react-core' +import { + EmptyState, + EmptyStateVariant, + EmptyStateIcon, + PageSection, + PageSectionVariants, + Tab, + Tabs, + TabTitleIcon, + TabTitleText, + Title, +} from '@patternfly/react-core' +import { + BuildIcon, + FileArchiveIcon, + FileCodeIcon, + TerminalIcon, + PollIcon, +} from '@patternfly/react-icons' import { fetchBuildIfNeeded } from '../actions/build' -import { Fetchable } from '../containers/Fetching' +import { EmptyPage } from '../containers/Errors' +import { Fetchable, Fetching } from '../containers/Fetching' +import ArtifactList from '../containers/build/Artifact' import Build from '../containers/build/Build' - +import BuildOutput from '../containers/build/BuildOutput' +import Console from '../containers/build/Console' +import Manifest from '../containers/build/Manifest' class BuildPage extends React.Component { static propTypes = { @@ -30,45 +53,219 @@ class BuildPage extends React.Component { dispatch: PropTypes.func, activeTab: PropTypes.string.isRequired, location: PropTypes.object, + history: PropTypes.object, } updateData = (force) => { - this.props.dispatch(fetchBuildIfNeeded( - this.props.tenant, this.props.match.params.buildId, null, force)) + this.props.dispatch( + fetchBuildIfNeeded( + this.props.tenant, + this.props.match.params.buildId, + force + ) + ) } - componentDidMount () { + componentDidMount() { document.title = 'Zuul Build' if (this.props.tenant.name) { this.updateData() } } - componentDidUpdate (prevProps) { + componentDidUpdate(prevProps) { if (this.props.tenant.name !== prevProps.tenant.name) { this.updateData() } } - render () { - const { remoteData, activeTab, location } = this.props + handleTabClick = (tabIndex, build) => { + // Usually tabs should only be used to display content in-page and not link + // to other pages: + // "Tabs are used to present a set on tabs for organizing content on a + // .page. It must always be used together with a tab content component." + // https://www.patternfly.org/v4/documentation/react/components/tabs + // But as want to be able to reach every tab's content via a dedicated URL + // while having the look and feel of tabs, we could hijack this onClick + // handler to do the link/routing stuff. + const { history, tenant } = this.props + + switch (tabIndex) { + case 'artifacts': + history.push(`${tenant.linkPrefix}/build/${build.uuid}/artifacts`) + break + case 'logs': + history.push(`${tenant.linkPrefix}/build/${build.uuid}/logs`) + break + case 'console': + history.push(`${tenant.linkPrefix}/build/${build.uuid}/console`) + break + default: + // results + history.push(`${tenant.linkPrefix}/build/${build.uuid}`) + } + } + + render() { + const { remoteData, activeTab, location, tenant } = this.props const build = remoteData.builds[this.props.match.params.buildId] const hash = location.hash.substring(1).split('/') + + if (!build && remoteData.isFetching) { + return <Fetching /> + } + + if (!build) { + return ( + <EmptyPage + title="This build does not exist" + icon={BuildIcon} + linkTarget={`${tenant.linkPrefix}/builds`} + linkText="Show all builds" + /> + ) + } + + const fetchable = ( + <Fetchable + isFetching={remoteData.isFetching} + fetchCallback={this.updateData} + /> + ) + + const resultsTabContent = + !build.hosts && remoteData.isFetchingOutput ? ( + <Fetching /> + ) : build.hosts ? ( + <BuildOutput output={build.hosts} /> + ) : ( + <EmptyState variant={EmptyStateVariant.small}> + <EmptyStateIcon icon={PollIcon} /> + <Title headingLevel="h4" size="lg"> + This build does not provide any results + </Title> + </EmptyState> + ) + + const artifactsTabContent = build.artifacts.length ? ( + <ArtifactList artifacts={build.artifacts} /> + ) : ( + <EmptyState variant={EmptyStateVariant.small}> + <EmptyStateIcon icon={FileArchiveIcon} /> + <Title headingLevel="h4" size="lg"> + This build does not provide any artifacts + </Title> + </EmptyState> + ) + + const logsTabContent = + !build.manifest && remoteData.isFetchingManifest ? ( + <Fetching /> + ) : build.manifest ? ( + <Manifest tenant={this.props.tenant} build={build} /> + ) : ( + <EmptyState variant={EmptyStateVariant.small}> + <EmptyStateIcon icon={FileCodeIcon} /> + <Title headingLevel="h4" size="lg"> + This build does not provide any logs + </Title> + </EmptyState> + ) + + const consoleTabContent = + !build.output && remoteData.isFetchingOutput ? ( + <Fetching /> + ) : build.output ? ( + <Console + output={build.output} + errorIds={build.errorIds} + displayPath={hash.length > 0 ? hash : undefined} + /> + ) : ( + <EmptyState variant={EmptyStateVariant.small}> + <EmptyStateIcon icon={TerminalIcon} /> + <Title headingLevel="h4" size="lg"> + This build does not provide any console information + </Title> + </EmptyState> + ) + return ( - <PageSection variant={PageSectionVariants.light}> - <PageSection style={{paddingRight: '5px'}}> - <Fetchable - isFetching={remoteData.isFetching} - fetchCallback={this.updateData} + <> + <PageSection variant={PageSectionVariants.light}> + <Build + build={build} + active={activeTab} + hash={hash} + fetchable={fetchable} /> </PageSection> - {build && <Build build={build} active={activeTab} hash={hash}/>} - </PageSection> + <PageSection variant={PageSectionVariants.light}> + <Tabs + isFilled + activeKey={activeTab} + onSelect={(event, tabIndex) => this.handleTabClick(tabIndex, build)} + > + <Tab + eventKey="results" + title={ + <> + <TabTitleIcon> + <PollIcon /> + </TabTitleIcon> + <TabTitleText>Results</TabTitleText> + </> + } + > + {resultsTabContent} + </Tab> + <Tab + eventKey="artifacts" + title={ + <> + <TabTitleIcon> + <FileArchiveIcon /> + </TabTitleIcon> + <TabTitleText>Artifacts</TabTitleText> + </> + } + > + {artifactsTabContent} + </Tab> + <Tab + eventKey="logs" + title={ + <> + <TabTitleIcon> + <FileCodeIcon /> + </TabTitleIcon> + <TabTitleText>Logs</TabTitleText> + </> + } + > + {logsTabContent} + </Tab> + <Tab + eventKey="console" + title={ + <> + <TabTitleIcon> + <TerminalIcon /> + </TabTitleIcon> + <TabTitleText>Console</TabTitleText> + </> + } + > + {consoleTabContent} + </Tab> + </Tabs> + </PageSection> + </> ) } } -export default connect(state => ({ +export default connect((state) => ({ tenant: state.tenant, remoteData: state.build, -}))(BuildPage) +}))(withRouter(BuildPage)) |