diff options
author | Zuul <zuul@review.opendev.org> | 2022-08-17 03:09:33 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2022-08-17 03:09:33 +0000 |
commit | 48f1dcef736f1b1ac54c05e34b763717fcf83cfe (patch) | |
tree | 6945be326a333ca01ac74ec7bb691d2a7c6799fa /web | |
parent | 15c2a42969011275304652df68cebe154a9126e6 (diff) | |
parent | 8494ebf397115a86ad222417d0a66baa08e5721a (diff) | |
download | zuul-48f1dcef736f1b1ac54c05e34b763717fcf83cfe.tar.gz |
Merge "Web: fix tabs on project page"
Diffstat (limited to 'web')
-rw-r--r-- | web/src/containers/project/Project.jsx | 79 | ||||
-rw-r--r-- | web/src/containers/project/ProjectVariant.jsx | 106 | ||||
-rw-r--r-- | web/src/index.css | 6 | ||||
-rw-r--r-- | web/src/pages/Project.jsx | 92 |
4 files changed, 139 insertions, 144 deletions
diff --git a/web/src/containers/project/Project.jsx b/web/src/containers/project/Project.jsx index c355b6af7..993d4d927 100644 --- a/web/src/containers/project/Project.jsx +++ b/web/src/containers/project/Project.jsx @@ -1,4 +1,5 @@ // Copyright 2018 Red Hat, Inc +// Copyright 2022 Acme Gating, LLC // // 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 @@ -12,68 +13,44 @@ // License for the specific language governing permissions and limitations // under the License. -import * as React from 'react' +import React, { useState } from 'react' import PropTypes from 'prop-types' import { - Nav, - NavItem, - TabContainer, - TabPane, - TabContent, -} from 'patternfly-react' + Tabs, + Tab, +} from '@patternfly/react-core' import ProjectVariant from './ProjectVariant' -class Project extends React.Component { - static propTypes = { - project: PropTypes.object.isRequired, - } - - state = { - variantIdx: 0, - } +function Project(props) { + const [variantIdx, setVariantIdx] = useState(0) + const { project } = props - renderVariantTitle (variant, selected) { - let title = variant.default_branch - if (selected) { - title = <strong>{title}</strong> - } + function renderVariantTitle (variant) { + let title = variant.source_context.project === project.name ? + variant.source_context.branch : variant.source_context.project return title } - render () { - const { project } = this.props - const { variantIdx } = this.state + return ( + <React.Fragment> + <Tabs activeKey={variantIdx} + onSelect={(event, tabIndex) => setVariantIdx(tabIndex)} + isBox> + {project.configs.map((variant, idx) => ( + <Tab key={idx} eventKey={idx} + title={renderVariantTitle(variant)}> + <ProjectVariant variant={variant} /> + </Tab> + ))} + </Tabs> + </React.Fragment> + ) +} - return ( - <React.Fragment> - <h2>{project.canonical_name}</h2> - <TabContainer id="zuul-project"> - <div> - <Nav bsClass="nav nav-tabs nav-tabs-pf"> - {project.configs.map((variant, idx) => ( - <NavItem - key={idx} - onClick={() => this.setState({variantIdx: idx})}> - <div> - {this.renderVariantTitle(variant, variantIdx === idx)} - </div> - </NavItem> - ))} - </Nav> - <TabContent> - <TabPane> - {project.configs[variantIdx] && ( - <ProjectVariant variant={project.configs[variantIdx]} /> - )} - </TabPane> - </TabContent> - </div> - </TabContainer> - </React.Fragment> - ) - } +Project.propTypes = { + project: PropTypes.object.isRequired, } export default Project diff --git a/web/src/containers/project/ProjectVariant.jsx b/web/src/containers/project/ProjectVariant.jsx index 74be10a55..51a093a32 100644 --- a/web/src/containers/project/ProjectVariant.jsx +++ b/web/src/containers/project/ProjectVariant.jsx @@ -18,64 +18,68 @@ import { connect } from 'react-redux' import { Link } from 'react-router-dom' -class ProjectVariant extends React.Component { - static propTypes = { - tenant: PropTypes.object, - variant: PropTypes.object.isRequired - } +function ProjectVariant(props) { + const { tenant, variant } = props + const rows = [] - render () { - const { tenant, variant } = this.props - const rows = [] + rows.push({label: 'Merge mode', value: variant.merge_mode}) - rows.push({label: 'Merge mode', value: variant.merge_mode}) + if (variant.templates.length > 0) { + const templateList = ( + <ul className='list-group'> + {variant.templates.map((item, idx) => ( + <li className='list-group-item' key={idx}>{item}</li>))} + </ul> + ) + rows.push({label: 'Templates', value: templateList}) + } - if (variant.templates.length > 0) { - const templateList = ( + variant.pipelines.forEach(pipeline => { + // TODO: either adds job link anchor to load the right variant + // and/or show the job variant config in a modal? + const jobList = ( + <React.Fragment> + {pipeline.queue_name && ( + <p><strong>Queue: </strong> {pipeline.queue_name} </p>)} <ul className='list-group'> - {variant.templates.map((item, idx) => ( - <li className='list-group-item' key={idx}>{item}</li>))} + {pipeline.jobs.map((item, idx) => ( + <li className='list-group-item' key={idx}> + <Link to={tenant.linkPrefix + '/job/' + item[0].name}> + {item[0].name} + </Link> + </li> + ))} </ul> - ) - rows.push({label: 'Templates', value: templateList}) - } + </React.Fragment> + ) + rows.push({label: pipeline.name + ' jobs', value: jobList}) + }) - variant.pipelines.forEach(pipeline => { - // TODO: either adds job link anchor to load the right variant - // and/or show the job variant config in a modal? - const jobList = ( - <React.Fragment> - {pipeline.queue_name && ( - <p><strong>Queue: </strong> {pipeline.queue_name} </p>)} - <ul className='list-group'> - {pipeline.jobs.map((item, idx) => ( - <li className='list-group-item' key={idx}> - <Link to={tenant.linkPrefix + '/job/' + item[0].name}> - {item[0].name} - </Link> - </li> - ))} - </ul> - </React.Fragment> - ) - rows.push({label: pipeline.name + ' jobs', value: jobList}) - }) + return ( + <div> + <table className='table table-striped table-bordered'> + <tbody> + {rows.map(item => ( + <tr key={item.label}> + <td style={{width: '10%'}}>{item.label}</td> + <td>{item.value}</td> + </tr> + ))} + </tbody> + </table> + </div> + ) +} - return ( - <div> - <table className='table table-striped table-bordered'> - <tbody> - {rows.map(item => ( - <tr key={item.label}> - <td style={{width: '10%'}}>{item.label}</td> - <td>{item.value}</td> - </tr> - ))} - </tbody> - </table> - </div> - ) +ProjectVariant.propTypes = { + tenant: PropTypes.object, + variant: PropTypes.object.isRequired +} + +function mapStateToProps(state) { + return { + tenant: state.tenant, } } -export default connect(state => ({tenant: state.tenant}))(ProjectVariant) +export default connect(mapStateToProps)(ProjectVariant) diff --git a/web/src/index.css b/web/src/index.css index e186eeeda..e47cfc63c 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -3,6 +3,12 @@ body { padding: 0; } +/* Make the H2 header inline-block so that the refresh icon/button can + share space with it floating on the right. */ +h2 { + display: inline-block; +} + .pf-c-title { padding-bottom: 10px; } diff --git a/web/src/pages/Project.jsx b/web/src/pages/Project.jsx index 1ef757cd2..06e8612c7 100644 --- a/web/src/pages/Project.jsx +++ b/web/src/pages/Project.jsx @@ -1,4 +1,5 @@ // Copyright 2018 Red Hat, Inc +// Copyright 2022 Acme Gating, LLC // // 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 @@ -12,67 +13,74 @@ // License for the specific language governing permissions and limitations // under the License. -import * as React from 'react' +import React, { useEffect, useCallback } from 'react' import { connect } from 'react-redux' import PropTypes from 'prop-types' -import { PageSection, PageSectionVariants } from '@patternfly/react-core' - +import { + PageSection, + PageSectionVariants, + Text, + TextContent, +} from '@patternfly/react-core' import Project from '../containers/project/Project' import JobGraph from '../containers/jobgraph/JobGraph' import { fetchProjectIfNeeded } from '../actions/project' import { Fetchable } from '../containers/Fetching' -class ProjectPage extends React.Component { - static propTypes = { - match: PropTypes.object.isRequired, - tenant: PropTypes.object, - remoteData: PropTypes.object, - dispatch: PropTypes.func - } - - updateData = (force) => { - this.props.dispatch(fetchProjectIfNeeded( - this.props.tenant, this.props.match.params.projectName, force)) - } +function ProjectPage(props) { + const { tenant, fetchProjectIfNeeded, remoteData } = props + const { projectName } = props.match.params + const tenantProjects = remoteData.projects[tenant.name] - componentDidMount () { - document.title = 'Zuul Project | ' + this.props.match.params.projectName - if (this.props.tenant.name) { - this.updateData() + const updateData = useCallback((force) => { + if (tenant.name) { + fetchProjectIfNeeded(tenant, projectName, force) } - } + }, [tenant, projectName, fetchProjectIfNeeded]) - componentDidUpdate (prevProps) { - if (this.props.tenant.name !== prevProps.tenant.name) { - this.updateData() - } - } + useEffect(() => { + document.title = 'Zuul Project | ' + projectName + updateData() + }, [tenant, projectName, updateData]) - render () { - const { remoteData } = this.props - const tenantProjects = remoteData.projects[this.props.tenant.name] - const projectName = this.props.match.params.projectName - return ( + return ( + <> <PageSection variant={PageSectionVariants.light}> - <PageSection style={{paddingRight: '5px'}}> - <Fetchable - isFetching={remoteData.isFetching} - fetchCallback={this.updateData} - /> - </PageSection> + <TextContent> + <Text component="h2">Project {projectName}</Text> + <Fetchable + isFetching={remoteData.isFetching} + fetchCallback={updateData} + /> + </TextContent> {tenantProjects && tenantProjects[projectName] && <> <Project project={tenantProjects[projectName]} /> <JobGraph project={tenantProjects[projectName]} /> </> } - </PageSection> - ) + </PageSection> + </> + ) +} + +ProjectPage.propTypes = { + match: PropTypes.object.isRequired, + tenant: PropTypes.object, + remoteData: PropTypes.object, + fetchProjectIfNeeded: PropTypes.func, +} + +function mapStateToProps(state) { + return { + tenant: state.tenant, + remoteData: state.project, } } -export default connect(state => ({ - tenant: state.tenant, - remoteData: state.project, -}))(ProjectPage) +const mapDispatchToProps = { + fetchProjectIfNeeded, +} + +export default connect(mapStateToProps, mapDispatchToProps)(ProjectPage) |