summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--web/src/Misc.jsx7
-rw-r--r--web/src/containers/build/BuildTable.jsx125
-rw-r--r--web/src/containers/build/BuildsetTable.jsx92
-rw-r--r--web/src/containers/build/Misc.jsx44
-rw-r--r--web/src/index.css23
-rw-r--r--web/src/pages/Builds.jsx2
-rw-r--r--web/src/pages/Buildsets.jsx2
7 files changed, 143 insertions, 152 deletions
diff --git a/web/src/Misc.jsx b/web/src/Misc.jsx
index ba2d62b02..e209ed042 100644
--- a/web/src/Misc.jsx
+++ b/web/src/Misc.jsx
@@ -92,4 +92,9 @@ function buildExternalTableLink(buildish) {
return null
}
-export { removeHash, ExternalLink, buildExternalLink, buildExternalTableLink }
+// https://github.com/kitze/conditional-wrap
+// appears to be the first implementation of this pattern
+const ConditionalWrapper = ({ condition, wrapper, children }) =>
+ condition ? wrapper(children) : children
+
+export { removeHash, ExternalLink, buildExternalLink, buildExternalTableLink, ConditionalWrapper }
diff --git a/web/src/containers/build/BuildTable.jsx b/web/src/containers/build/BuildTable.jsx
index 00e6b3eba..c7cb15601 100644
--- a/web/src/containers/build/BuildTable.jsx
+++ b/web/src/containers/build/BuildTable.jsx
@@ -15,7 +15,6 @@
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
-import { Link } from 'react-router-dom'
import {
Button,
EmptyState,
@@ -47,8 +46,14 @@ import * as moment from 'moment'
import { BuildResult, BuildResultWithIcon, IconProperty } from './Misc'
import { buildExternalTableLink } from '../../Misc'
-function BuildTable(props) {
- const { builds, fetching, onClearFilters, tenant, timezone } = props
+function BuildTable({
+ builds,
+ fetching,
+ onClearFilters,
+ tenant,
+ timezone,
+ history,
+}) {
const columns = [
{
title: <IconProperty icon={<BuildIcon />} value="Job" />,
@@ -87,28 +92,23 @@ function BuildTable(props) {
]
function createBuildRow(build) {
- // This link will be defined on each cell of the current row as this is the
- // only way to define a valid HTML link on a table row. Although we could
- // simply define an onClick handler on the whole row and programatically
- // switch to the buildresult page, this wouldn't provide the same
- // look-and-feel as a plain HTML link.
- const buildResultLink = (
- <Link
- to={`${tenant.linkPrefix}/build/${build.uuid}`}
- className="zuul-stretched-link"
- />
- )
- const build_link = buildExternalTableLink(build)
+ const changeOrRefLink = buildExternalTableLink(build)
return {
+ // Pass the build's uuid as row id, so we can use it later on in the
+ // action handler to build the link to the build result page for each row.
+ id: build.uuid,
cells: [
{
// To allow passing anything else than simple string values to a table
// cell, we must use the title attribute.
title: (
<>
- {buildResultLink}
- <BuildResultWithIcon result={build.result} colored={build.voting}>
+ <BuildResultWithIcon
+ result={build.result}
+ colored={build.voting}
+ link={`${tenant.linkPrefix}/build/${build.uuid}`}
+ >
{build.job_name}
{!build.voting && ' (non-voting)'}
</BuildResultWithIcon>
@@ -116,74 +116,36 @@ function BuildTable(props) {
),
},
{
- title: (
- <>
- {buildResultLink}
- <span>{build.project}</span>
- </>
- ),
+ title: build.project,
},
{
- title: (
- <>
- {buildResultLink}
- <span>{build.branch ? build.branch : build.ref}</span>
- </>
- ),
+ title: build.branch ? build.branch : build.ref,
},
{
- title: (
- <>
- {buildResultLink}
- <span>{build.pipeline}</span>
- </>
- ),
+ title: build.pipeline,
},
{
- title: (
- <>
- {buildResultLink}
- {build_link && (
- <span style={{ zIndex: 1, position: 'relative' }}>
- {build_link}
- </span>
- )}
- </>
- ),
+ title: changeOrRefLink && changeOrRefLink,
},
{
- title: (
- <>
- {buildResultLink}
- <span>
- {moment
- .duration(build.duration, 'seconds')
- .format('h [hr] m [min] s [sec]')}
- </span>
- </>
- ),
+ title: moment
+ .duration(build.duration, 'seconds')
+ .format('h [hr] m [min] s [sec]'),
},
{
- title: (
- <>
- {buildResultLink}
- <span>
- {build.start_time &&
- moment
- .utc(build.start_time)
- .tz(timezone)
- .format('YYYY-MM-DD HH:mm:ss')}
- </span>
- </>
- ),
+ title: moment
+ .utc(build.start_time)
+ .tz(timezone)
+ .format('YYYY-MM-DD HH:mm:ss'),
},
{
- title: (
- <>
- {buildResultLink}
- <BuildResult result={build.result} colored={build.voting} />
- </>
- ),
+ title: (
+ <BuildResult
+ result={build.result}
+ link={`${tenant.linkPrefix}/build/${build.uuid}`}
+ colored={build.voting}
+ />
+ ),
},
],
}
@@ -209,6 +171,9 @@ function BuildTable(props) {
}
let rows = []
+ // For the fetching row we don't need any actions, so we keep them empty by
+ // default.
+ let actions = []
if (fetching) {
rows = createFetchingRow()
// The dataLabel property is used to show the column header in a list-like
@@ -219,6 +184,18 @@ function BuildTable(props) {
columns[0].dataLabel = ''
} else {
rows = builds.map((build) => createBuildRow(build))
+ // This list of actions will be applied to each row in the table. For
+ // row-specific actions we must evaluate the individual row data provided to
+ // the onClick handler.
+ actions = [
+ {
+ title: 'Show build result',
+ onClick: (event, rowId, rowData) =>
+ // The row's id contains the build's uuid, so we can use this to build
+ // the correct link.
+ history.push(`${tenant.linkPrefix}/build/${rowData.id}`),
+ },
+ ]
}
return (
@@ -228,6 +205,7 @@ function BuildTable(props) {
variant={TableVariant.compact}
cells={columns}
rows={rows}
+ actions={actions}
className="zuul-build-table"
>
<TableHeader />
@@ -261,6 +239,7 @@ BuildTable.propTypes = {
onClearFilters: PropTypes.func.isRequired,
tenant: PropTypes.object.isRequired,
timezone: PropTypes.string.isRequired,
+ history: PropTypes.object.isRequired,
}
export default connect((state) => ({
diff --git a/web/src/containers/build/BuildsetTable.jsx b/web/src/containers/build/BuildsetTable.jsx
index dcd7efb0c..f0947971d 100644
--- a/web/src/containers/build/BuildsetTable.jsx
+++ b/web/src/containers/build/BuildsetTable.jsx
@@ -15,7 +15,6 @@
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
-import { Link } from 'react-router-dom'
import {
Button,
EmptyState,
@@ -43,8 +42,13 @@ import {
import { BuildResult, BuildResultWithIcon, IconProperty } from './Misc'
import { buildExternalTableLink } from '../../Misc'
-function BuildsetTable(props) {
- const { buildsets, fetching, onClearFilters, tenant } = props
+function BuildsetTable({
+ buildsets,
+ fetching,
+ onClearFilters,
+ tenant,
+ history,
+}) {
const columns = [
{
title: <IconProperty icon={<CubeIcon />} value="Project" />,
@@ -69,68 +73,41 @@ function BuildsetTable(props) {
]
function createBuildsetRow(buildset) {
- // This link will be defined on each cell of the current row as this is the
- // only way to define a valid HTML link on a table row. Although we could
- // simply define an onClick handler on the whole row and programatically
- // switch to the buildresult page, this wouldn't provide the same
- // look-and-feel as a plain HTML link.
- const buildsetResultLink = (
- <Link
- to={`${tenant.linkPrefix}/buildset/${buildset.uuid}`}
- className="zuul-stretched-link"
- />
- )
- const buildset_link = buildExternalTableLink(buildset)
+ const changeOrRefLink = buildExternalTableLink(buildset)
return {
+ // Pass the buildset's uuid as row id, so we can use it later on in the
+ // action handler to build the link to the build result page for each row.
+ id: buildset.uuid,
cells: [
{
// To allow passing anything else than simple string values to a table
// cell, we must use the title attribute.
title: (
- <>
- {buildsetResultLink}
- <BuildResultWithIcon result={buildset.result}>
- {buildset.project}
- </BuildResultWithIcon>
- </>
+ <BuildResultWithIcon
+ result={buildset.result}
+ link={`${tenant.linkPrefix}/buildset/${buildset.uuid}`}>
+ {buildset.project}
+ </BuildResultWithIcon>
),
},
{
- title: (
- <>
- {buildsetResultLink}
- <span>{buildset.branch ? buildset.branch : buildset.ref}</span>
- </>
- ),
+ title: buildset.branch ? buildset.branch : buildset.ref,
},
{
- title: (
- <>
- {buildsetResultLink}
- <span>{buildset.pipeline}</span>
- </>
- ),
+ title: buildset.pipeline,
},
{
- title: (
- <>
- {buildsetResultLink}
- {buildset_link && (
- <span style={{ zIndex: 1, position: 'relative' }}>
- {buildset_link}
- </span>
- )}
- </>
- ),
+ title: changeOrRefLink && changeOrRefLink,
},
{
- title: (
- <>
- {buildsetResultLink}
- <BuildResult result={buildset.result} />
- </>
- ),
+ title: (
+ <BuildResult
+ result={buildset.result}
+ link={`${tenant.linkPrefix}/buildset/${buildset.uuid}`}
+ >
+ </BuildResult>
+ ),
},
],
}
@@ -156,6 +133,9 @@ function BuildsetTable(props) {
}
let rows = []
+ // For the fetching row we don't need any actions, so we keep them empty by
+ // default.
+ let actions = []
if (fetching) {
rows = createFetchingRow()
// The dataLabel property is used to show the column header in a list-like
@@ -166,6 +146,18 @@ function BuildsetTable(props) {
columns[0].dataLabel = ''
} else {
rows = buildsets.map((buildset) => createBuildsetRow(buildset))
+ // This list of actions will be applied to each row in the table. For
+ // row-specific actions we must evaluate the individual row data provided to
+ // the onClick handler.
+ actions = [
+ {
+ title: 'Show buildset result',
+ onClick: (event, rowId, rowData) =>
+ // The row's id contains the buildset's uuid, so we can use it to
+ // build the correct link.
+ history.push(`${tenant.linkPrefix}/buildset/${rowData.id}`),
+ },
+ ]
}
return (
@@ -175,6 +167,7 @@ function BuildsetTable(props) {
variant={TableVariant.compact}
cells={columns}
rows={rows}
+ actions={actions}
className="zuul-build-table"
>
<TableHeader />
@@ -207,6 +200,7 @@ BuildsetTable.propTypes = {
fetching: PropTypes.bool.isRequired,
onClearFilters: PropTypes.func.isRequired,
tenant: PropTypes.object.isRequired,
+ history: PropTypes.object.isRequired,
}
export default connect((state) => ({ tenant: state.tenant }))(BuildsetTable)
diff --git a/web/src/containers/build/Misc.jsx b/web/src/containers/build/Misc.jsx
index f77f7de7a..1d02992c0 100644
--- a/web/src/containers/build/Misc.jsx
+++ b/web/src/containers/build/Misc.jsx
@@ -14,13 +14,17 @@
import * as React from 'react'
import PropTypes from 'prop-types'
-import { Label } from '@patternfly/react-core'
+import { Link } from 'react-router-dom'
+import {
+ Label,
+} from '@patternfly/react-core'
import {
CheckIcon,
ExclamationIcon,
QuestionIcon,
TimesIcon,
} from '@patternfly/react-icons'
+import { ConditionalWrapper } from '../../Misc'
const RESULT_ICON_CONFIGS = {
SUCCESS: {
@@ -82,15 +86,25 @@ const DEFAULT_RESULT_ICON_CONFIG = {
}
function BuildResult(props) {
- const { result, colored = true } = props
+ const { result, link = undefined, colored = true } = props
const iconConfig = RESULT_ICON_CONFIGS[result] || DEFAULT_RESULT_ICON_CONFIG
const color = colored ? iconConfig.color : 'inherit'
- return <span style={{ color: color }}>{result}</span>
+ return (
+ <span style={{ color: color }}>
+ <ConditionalWrapper
+ condition={link}
+ wrapper={children => <Link to={link} style={{ color: color }}>{children}</Link>}
+ >
+ {result}
+ </ConditionalWrapper>
+ </span>
+ )
}
BuildResult.propTypes = {
result: PropTypes.string,
+ link: PropTypes.string,
colored: PropTypes.bool,
}
@@ -117,7 +131,7 @@ BuildResultBadge.propTypes = {
}
function BuildResultWithIcon(props) {
- const { result, colored = true, size = 'sm' } = props
+ const { result, link = undefined, colored = true, size = 'sm' } = props
const iconConfig = RESULT_ICON_CONFIGS[result] || DEFAULT_RESULT_ICON_CONFIG
// Define the verticalAlign based on the size
@@ -132,20 +146,26 @@ function BuildResultWithIcon(props) {
return (
<span style={{ color: color }}>
- <Icon
- size={size}
- style={{
- marginRight: 'var(--pf-global--spacer--sm)',
- verticalAlign: verticalAlign,
- }}
- />
- {props.children}
+ <ConditionalWrapper
+ condition={link}
+ wrapper={children => <Link to={link} style={{ color: color }}>{children}</Link>}
+ >
+ <Icon
+ size={size}
+ style={{
+ marginRight: 'var(--pf-global--spacer--sm)',
+ verticalAlign: verticalAlign,
+ }}
+ />
+ {props.children}
+ </ConditionalWrapper>
</span>
)
}
BuildResultWithIcon.propTypes = {
result: PropTypes.string,
+ link: PropTypes.string,
colored: PropTypes.bool,
size: PropTypes.string,
children: PropTypes.node,
diff --git a/web/src/index.css b/web/src/index.css
index 83c73b613..2f6602c1c 100644
--- a/web/src/index.css
+++ b/web/src/index.css
@@ -51,10 +51,6 @@ a.refresh {
font-weight: bold;
}
-.zuul-build-list:hover a {
- text-decoration: none;
-}
-
/* Keep the normal font-size for compact tables */
.zuul-build-table td {
font-size: var(--pf-global--FontSize--md);
@@ -98,20 +94,13 @@ a.refresh {
.zuul-build-table tbody tr:hover td:first-child::before {
background-color: var(--pf-global--active-color--100);
}
-}
-/* Make a link stretch the whole parent element (in this case the whole table
- cell) */
-.zuul-stretched-link::after {
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- z-index: 0;
- pointer-events: auto;
- content: "";
- background-color: rgba(0,0,0,0);
+ /* Hide the action column with the build link on larger screen. This is only
+ needed for the mobile version as we can't use the "magnifying-glass icon
+ on hover" effect there. */
+ .zuul-build-table .pf-c-table__action {
+ display: none;
+ }
}
/*
diff --git a/web/src/pages/Builds.jsx b/web/src/pages/Builds.jsx
index 144f9ab85..bb4e4cf47 100644
--- a/web/src/pages/Builds.jsx
+++ b/web/src/pages/Builds.jsx
@@ -151,6 +151,7 @@ class BuildsPage extends React.Component {
}
render() {
+ const { history } = this.props
const { builds, fetching, filters } = this.state
return (
<PageSection variant={PageSectionVariants.light}>
@@ -163,6 +164,7 @@ class BuildsPage extends React.Component {
builds={builds}
fetching={fetching}
onClearFilters={this.handleClearFilters}
+ history={history}
/>
</PageSection>
)
diff --git a/web/src/pages/Buildsets.jsx b/web/src/pages/Buildsets.jsx
index 602f4d0d8..a9372e8b0 100644
--- a/web/src/pages/Buildsets.jsx
+++ b/web/src/pages/Buildsets.jsx
@@ -139,6 +139,7 @@ class BuildsetsPage extends React.Component {
}
render() {
+ const { history } = this.props
const { buildsets, fetching, filters } = this.state
return (
<PageSection variant={PageSectionVariants.light}>
@@ -151,6 +152,7 @@ class BuildsetsPage extends React.Component {
buildsets={buildsets}
fetching={fetching}
onClearFilters={this.handleClearFilters}
+ history={history}
/>
</PageSection>
)