Merge "web UI: add "show retries" toggles on buildset page"
This commit is contained in:
@@ -22,8 +22,15 @@ import {
|
|||||||
DataListItem,
|
DataListItem,
|
||||||
DataListItemRow,
|
DataListItemRow,
|
||||||
DataListItemCells,
|
DataListItemCells,
|
||||||
|
Flex,
|
||||||
|
FlexItem,
|
||||||
|
Switch,
|
||||||
} from '@patternfly/react-core'
|
} from '@patternfly/react-core'
|
||||||
import { OutlinedClockIcon } from '@patternfly/react-icons'
|
import {
|
||||||
|
AngleDownIcon,
|
||||||
|
AngleRightIcon,
|
||||||
|
OutlinedClockIcon
|
||||||
|
} from '@patternfly/react-icons'
|
||||||
import 'moment-duration-format'
|
import 'moment-duration-format'
|
||||||
import * as moment from 'moment'
|
import * as moment from 'moment'
|
||||||
|
|
||||||
@@ -39,33 +46,124 @@ class BuildList extends React.Component {
|
|||||||
// page. Without this flag we might then even use this (with more
|
// page. Without this flag we might then even use this (with more
|
||||||
// information) on the /builds page.
|
// information) on the /builds page.
|
||||||
|
|
||||||
constructor() {
|
constructor(props) {
|
||||||
super()
|
super(props)
|
||||||
|
const { builds } = this.props
|
||||||
|
let retriedJobs = builds.filter((build) => {
|
||||||
|
return !build.final
|
||||||
|
}).map((build) => (build.job_name)
|
||||||
|
).filter((build, idx, self) => {
|
||||||
|
return self.indexOf(build) === idx
|
||||||
|
})
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
selectedBuildId: null,
|
visibleNonFinalBuilds: retriedJobs,
|
||||||
|
retriedJobs: retriedJobs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSelectDataListItem = (buildId) => {
|
sortedBuilds = () => {
|
||||||
this.setState({
|
const { builds } = this.props
|
||||||
selectedBuildId: buildId,
|
const { visibleNonFinalBuilds } = this.state
|
||||||
|
|
||||||
|
return builds.sort((a, b) => {
|
||||||
|
// Group builds by job name, then order by decreasing start time; this will ensure retries are together
|
||||||
|
if (a.job_name === b.job_name) {
|
||||||
|
if (a.start_time < b.start_time) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if (a.start_time > b.start_time) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if (a.job_name > b.job_name) {
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}).filter((build) => {
|
||||||
|
if (build.final || visibleNonFinalBuilds.indexOf(build.job_name) >= 0) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
handleFinalSwitch = isChecked => {
|
||||||
const { builds, tenant } = this.props
|
const { retriedJobs } = this.state
|
||||||
const { selectedBuildId } = this.state
|
this.setState({ visibleNonFinalBuilds: (isChecked ? retriedJobs : []) })
|
||||||
|
}
|
||||||
|
|
||||||
|
handleToggleVisibleNonFinalBuilds = (jobName) => {
|
||||||
|
const { visibleNonFinalBuilds } = this.state
|
||||||
|
const index = visibleNonFinalBuilds.indexOf(jobName)
|
||||||
|
const newVisible =
|
||||||
|
index >= 0 ? [...visibleNonFinalBuilds.slice(0, index), ...visibleNonFinalBuilds.slice(index + 1, visibleNonFinalBuilds.length)] : [...visibleNonFinalBuilds, jobName]
|
||||||
|
this.setState({
|
||||||
|
visibleNonFinalBuilds: newVisible,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
renderRetriesButton = (build, hasRetries) => {
|
||||||
|
const { visibleNonFinalBuilds } = this.state
|
||||||
|
if (!build.final || !hasRetries) {
|
||||||
|
return <DataListCell key={`${build.uuid}-final`} width={1} isIcon={true}>
|
||||||
|
{/* Hide the icon to maintain alignment between final and non-final elements */}
|
||||||
|
<AngleRightIcon visibility="hidden" />
|
||||||
|
</DataListCell >
|
||||||
|
}
|
||||||
|
const isExpanded = (visibleNonFinalBuilds.indexOf(build.job_name) >= 0)
|
||||||
|
const RetryIcon =
|
||||||
|
isExpanded
|
||||||
|
? AngleDownIcon
|
||||||
|
: AngleRightIcon
|
||||||
|
const retryAltText =
|
||||||
|
isExpanded
|
||||||
|
? 'Hide retries for this job'
|
||||||
|
: 'Show retries for this job'
|
||||||
|
// TODO either replace this with an ExpandableSection (but this breaks the layout) or figure out CSS animations for the icon.
|
||||||
return (
|
return (
|
||||||
|
<DataListCell key={`${build.uuid}-final`} width={1} isIcon={true}>
|
||||||
|
<RetryIcon
|
||||||
|
onClick={() => { this.handleToggleVisibleNonFinalBuilds(build.job_name) }}
|
||||||
|
title={retryAltText}
|
||||||
|
style={{ cursor: 'pointer' }} />
|
||||||
|
</DataListCell >
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { tenant } = this.props
|
||||||
|
const { visibleNonFinalBuilds, retriedJobs } = this.state
|
||||||
|
|
||||||
|
let retrySwitch = retriedJobs.length > 0 ?
|
||||||
|
<FlexItem align={{ default: 'alignRight' }}>
|
||||||
|
<span>Show retries </span>
|
||||||
|
<Switch
|
||||||
|
isChecked={visibleNonFinalBuilds === retriedJobs}
|
||||||
|
onChange={this.handleFinalSwitch}
|
||||||
|
isReversed
|
||||||
|
/>
|
||||||
|
</FlexItem> :
|
||||||
|
<></>
|
||||||
|
|
||||||
|
const sortedBuilds = this.sortedBuilds()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex direction={{ default: 'column' }}>
|
||||||
|
{retrySwitch}
|
||||||
|
<FlexItem>
|
||||||
<DataList
|
<DataList
|
||||||
className="zuul-build-list"
|
className="zuul-build-list"
|
||||||
isCompact
|
isCompact
|
||||||
selectedDataListItemId={selectedBuildId}
|
|
||||||
onSelectDataListItem={this.handleSelectDataListItem}
|
|
||||||
style={{ fontSize: 'var(--pf-global--FontSize--md)' }}
|
style={{ fontSize: 'var(--pf-global--FontSize--md)' }}
|
||||||
>
|
>
|
||||||
{builds.map((build) => (
|
{sortedBuilds.map((build) => {
|
||||||
<DataListItem key={build.uuid || build.job_name} id={build.uuid}>
|
function linkWrap(cell) {
|
||||||
<Link
|
return (<Link
|
||||||
to={`${tenant.linkPrefix}/build/${build.uuid}`}
|
to={`${tenant.linkPrefix}/build/${build.uuid}`}
|
||||||
style={{
|
style={{
|
||||||
textDecoration: 'none',
|
textDecoration: 'none',
|
||||||
@@ -74,40 +172,49 @@ class BuildList extends React.Component {
|
|||||||
: 'var(--pf-global--disabled-color--100)',
|
: 'var(--pf-global--disabled-color--100)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DataListItemRow>
|
{cell}
|
||||||
|
</Link>)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<DataListItem key={build.uuid || build.job_name} id={build.uuid}>
|
||||||
|
<DataListItemRow
|
||||||
|
style={!build.final ? { backgroundColor: 'var(--pf-global--BackgroundColor--light-200)' } : {}}>
|
||||||
<DataListItemCells
|
<DataListItemCells
|
||||||
dataListCells={[
|
dataListCells={[
|
||||||
|
this.renderRetriesButton(build, retriedJobs.indexOf(build.job_name) >= 0),
|
||||||
<DataListCell key={build.uuid} width={3}>
|
<DataListCell key={build.uuid} width={3}>
|
||||||
<BuildResultWithIcon
|
{linkWrap(<BuildResultWithIcon
|
||||||
result={build.result}
|
result={build.result}
|
||||||
colored={build.voting}
|
colored={build.voting}
|
||||||
size="sm"
|
size="sm"
|
||||||
>
|
>
|
||||||
{build.job_name}
|
{build.job_name}
|
||||||
{!build.voting && ' (non-voting)'}
|
{!build.voting && ' (non-voting)'}
|
||||||
</BuildResultWithIcon>
|
</BuildResultWithIcon>)}
|
||||||
</DataListCell>,
|
</DataListCell>,
|
||||||
<DataListCell key={`${build.uuid}-time`}>
|
<DataListCell key={`${build.uuid}-time`}>
|
||||||
<IconProperty
|
{linkWrap(<IconProperty
|
||||||
icon={<OutlinedClockIcon />}
|
icon={<OutlinedClockIcon />}
|
||||||
value={moment
|
value={moment
|
||||||
.duration(build.duration, 'seconds')
|
.duration(build.duration, 'seconds')
|
||||||
.format('h [hr] m [min] s [sec]')}
|
.format('h [hr] m [min] s [sec]')}
|
||||||
/>
|
/>)}
|
||||||
</DataListCell>,
|
</DataListCell>,
|
||||||
<DataListCell key={`${build.uuid}-result`}>
|
<DataListCell key={`${build.uuid}-result`}>
|
||||||
<BuildResult
|
{linkWrap(<BuildResult
|
||||||
result={build.result}
|
result={build.result}
|
||||||
colored={build.voting}
|
colored={build.voting}
|
||||||
/>
|
/>)}
|
||||||
</DataListCell>,
|
</DataListCell>,
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</DataListItemRow>
|
</DataListItemRow>
|
||||||
</Link>
|
|
||||||
</DataListItem>
|
</DataListItem>
|
||||||
))}
|
)
|
||||||
|
})}
|
||||||
</DataList>
|
</DataList>
|
||||||
|
</FlexItem>
|
||||||
|
</Flex>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,11 @@ a.refresh {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Remove ugly outline when a Switch is selected */
|
||||||
|
.pf-c-switch {
|
||||||
|
--pf-c-switch__input--focus__toggle--OutlineWidth: 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Keep the normal font-size for compact tables */
|
/* Keep the normal font-size for compact tables */
|
||||||
.zuul-build-table td {
|
.zuul-build-table td {
|
||||||
font-size: var(--pf-global--FontSize--md);
|
font-size: var(--pf-global--FontSize--md);
|
||||||
|
|||||||
Reference in New Issue
Block a user