diff --git a/web/src/containers/build/BuildList.jsx b/web/src/containers/build/BuildList.jsx
index b0a3bc3f53..2902985ad6 100644
--- a/web/src/containers/build/BuildList.jsx
+++ b/web/src/containers/build/BuildList.jsx
@@ -22,8 +22,15 @@ import {
DataListItem,
DataListItemRow,
DataListItemCells,
+ Flex,
+ FlexItem,
+ Switch,
} from '@patternfly/react-core'
-import { OutlinedClockIcon } from '@patternfly/react-icons'
+import {
+ AngleDownIcon,
+ AngleRightIcon,
+ OutlinedClockIcon
+} from '@patternfly/react-icons'
import 'moment-duration-format'
import * as moment from 'moment'
@@ -39,75 +46,175 @@ class BuildList extends React.Component {
// page. Without this flag we might then even use this (with more
// information) on the /builds page.
- constructor() {
- super()
+ constructor(props) {
+ 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 = {
- selectedBuildId: null,
+ visibleNonFinalBuilds: retriedJobs,
+ retriedJobs: retriedJobs,
}
}
- handleSelectDataListItem = (buildId) => {
- this.setState({
- selectedBuildId: buildId,
+ sortedBuilds = () => {
+ const { builds } = this.props
+ 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() {
- const { builds, tenant } = this.props
- const { selectedBuildId } = this.state
+ handleFinalSwitch = isChecked => {
+ const { retriedJobs } = 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
+ {/* Hide the icon to maintain alignment between final and non-final elements */}
+
+
+ }
+ 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 (
-
+
+ { this.handleToggleVisibleNonFinalBuilds(build.job_name) }}
+ title={retryAltText}
+ style={{ cursor: 'pointer' }} />
+
+ )
+ }
+
+ render() {
+ const { tenant } = this.props
+ const { visibleNonFinalBuilds, retriedJobs } = this.state
+
+ let retrySwitch = retriedJobs.length > 0 ?
+
+ Show retries
+
+ :
+ <>>
+
+ const sortedBuilds = this.sortedBuilds()
+
+ return (
+
+ {retrySwitch}
+
+
+
+
)
}
}
diff --git a/web/src/index.css b/web/src/index.css
index 806cbf2392..990317ae17 100644
--- a/web/src/index.css
+++ b/web/src/index.css
@@ -51,6 +51,11 @@ a.refresh {
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 */
.zuul-build-table td {
font-size: var(--pf-global--FontSize--md);