PF4: Update builds and buildsets tables + use newest patternfly release

This updates the tables shown on the builds and buildsets pages with new
PF4 components. While the data is loading, a fetching animation is shown
and in case no results could be found the page shows an empty state.

Each table row is clickable and points to the build/buildset result page.
The table uses the same hover effect as the build list on the buildset
result page.

This change also updates the used @patternfly/react-core version to
match the current version of @patternfly/react-table [1]. Otherwise,
some of the styles in the different @patternfly/react-core versions
conflict with each other leading to wrong font-weights and wrong text
colors in labels.

[1] patternfly.org/v4/documentation/react/overview/release-notes#202010-release-notes-2020-08-17

Change-Id: I0f5e0815c53d18e8ae3570dc94594c77fecb90ce
This commit is contained in:
Felix Edel
2020-08-11 14:47:50 +02:00
parent fe877d6052
commit 28ecbf42be
7 changed files with 646 additions and 228 deletions

View File

@@ -7,7 +7,8 @@
"license": "Apache-2.0",
"private": true,
"dependencies": {
"@patternfly/react-core": "^4.18.5",
"@patternfly/react-core": "^4.40.4",
"@patternfly/react-table": "^4.15.5",
"axios": "^0.19.0",
"immutability-helper": "^2.8.1",
"js-yaml": "^3.13.0",

View File

@@ -0,0 +1,268 @@
// Copyright 2020 BMW Group
//
// 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
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { Link } from 'react-router-dom'
import {
Button,
EmptyState,
EmptyStateBody,
EmptyStateIcon,
EmptyStateSecondaryActions,
Spinner,
Title,
} from '@patternfly/react-core'
import {
BuildIcon,
CodeBranchIcon,
CodeIcon,
CubeIcon,
OutlinedCalendarAltIcon,
OutlinedClockIcon,
PollIcon,
StreamIcon,
} from '@patternfly/react-icons'
import {
Table,
TableHeader,
TableBody,
TableVariant,
} from '@patternfly/react-table'
import 'moment-duration-format'
import * as moment from 'moment'
import { BuildResult, BuildResultWithIcon, IconProperty } from './Misc'
import { ExternalLink } from '../../Misc'
function BuildTable(props) {
const { builds, fetching, onClearFilters, tenant, timezone } = props
const columns = [
{
title: <IconProperty icon={<BuildIcon />} value="Job" />,
dataLabel: 'Job',
},
{
title: <IconProperty icon={<CubeIcon />} value="Project" />,
dataLabel: 'Project',
},
{
title: <IconProperty icon={<CodeBranchIcon />} value="Branch" />,
dataLabel: 'Branch',
},
{
title: <IconProperty icon={<StreamIcon />} value="Pipeline" />,
dataLabel: 'Pipeline',
},
{
title: <IconProperty icon={<CodeIcon />} value="Change" />,
dataLabel: 'Change',
},
{
title: <IconProperty icon={<OutlinedClockIcon />} value="Duration" />,
dataLabel: 'Duration',
},
{
title: (
<IconProperty icon={<OutlinedCalendarAltIcon />} value="Start time" />
),
dataLabel: 'Start time',
},
{
title: <IconProperty icon={<PollIcon />} value="Result" />,
dataLabel: 'Result',
},
]
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"
/>
)
return {
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}>
{build.job_name}
{!build.voting && ' (non-voting)'}
</BuildResultWithIcon>
</>
),
},
{
title: (
<>
{buildResultLink}
<span>{build.project}</span>
</>
),
},
{
title: (
<>
{buildResultLink}
<span>{build.branch ? build.branch : build.ref}</span>
</>
),
},
{
title: (
<>
{buildResultLink}
<span>{build.pipeline}</span>
</>
),
},
{
title: (
<>
{buildResultLink}
{build.change && (
<span style={{ zIndex: 1, position: 'relative' }}>
<ExternalLink target={build.ref_url}>
{build.change},{build.patchset}
</ExternalLink>
</span>
)}
</>
),
},
{
title: (
<>
{buildResultLink}
<span>
{moment
.duration(build.duration, 'seconds')
.format('h [hr] m [min] s [sec]')}
</span>
</>
),
},
{
title: (
<>
{buildResultLink}
<span>
{moment
.utc(build.start_time)
.tz(timezone)
.format('YYYY-MM-DD HH:mm:ss')}
</span>
</>
),
},
{
title: (
<>
{buildResultLink}
<BuildResult result={build.result} colored={build.voting} />
</>
),
},
],
}
}
function createFetchingRow() {
const rows = [
{
heightAuto: true,
cells: [
{
props: { colSpan: 8 },
title: (
<center>
<Spinner size="xl" />
</center>
),
},
],
},
]
return rows
}
let rows = []
if (fetching) {
rows = createFetchingRow()
// The dataLabel property is used to show the column header in a list-like
// format for smaller viewports. When we are fetching, we don't want the
// fetching row to be prepended by a "Job" column header. The other column
// headers are not relevant here since we only have a single cell in the
// fetcihng row.
columns[0].dataLabel = ''
} else {
rows = builds.map((build) => createBuildRow(build))
}
return (
<>
<Table
aria-label="Builds Table"
variant={TableVariant.compact}
cells={columns}
rows={rows}
className="zuul-build-table"
>
<TableHeader />
<TableBody />
</Table>
{/* Show an empty state in case we don't have any builds but are also not
fetching */}
{!fetching && builds.length === 0 && (
<EmptyState>
<EmptyStateIcon icon={BuildIcon} />
<Title headingLevel="h1">No builds found</Title>
<EmptyStateBody>
No builds match this filter criteria. Remove some filters or clear
all to show results.
</EmptyStateBody>
<EmptyStateSecondaryActions>
<Button variant="link" onClick={onClearFilters}>
Clear all filters
</Button>
</EmptyStateSecondaryActions>
</EmptyState>
)}
</>
)
}
BuildTable.propTypes = {
builds: PropTypes.array.isRequired,
fetching: PropTypes.bool.isRequired,
onClearFilters: PropTypes.func.isRequired,
tenant: PropTypes.object.isRequired,
timezone: PropTypes.string.isRequired,
}
export default connect((state) => ({
tenant: state.tenant,
timezone: state.timezone,
}))(BuildTable)

View File

@@ -0,0 +1,212 @@
// Copyright 2020 BMW Group
//
// 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
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { Link } from 'react-router-dom'
import {
Button,
EmptyState,
EmptyStateBody,
EmptyStateIcon,
EmptyStateSecondaryActions,
Spinner,
Title,
} from '@patternfly/react-core'
import {
BuildIcon,
CodeBranchIcon,
CodeIcon,
CubeIcon,
PollIcon,
StreamIcon,
} from '@patternfly/react-icons'
import {
Table,
TableHeader,
TableBody,
TableVariant,
} from '@patternfly/react-table'
import { BuildResult, BuildResultWithIcon, IconProperty } from './Misc'
import { ExternalLink } from '../../Misc'
function BuildsetTable(props) {
const { buildsets, fetching, onClearFilters, tenant } = props
const columns = [
{
title: <IconProperty icon={<CubeIcon />} value="Project" />,
dataLabel: 'Project',
},
{
title: <IconProperty icon={<CodeBranchIcon />} value="Branch" />,
dataLabel: 'Branch',
},
{
title: <IconProperty icon={<StreamIcon />} value="Pipeline" />,
dataLabel: 'Pipeline',
},
{
title: <IconProperty icon={<CodeIcon />} value="Change" />,
dataLabel: 'Change',
},
{
title: <IconProperty icon={<PollIcon />} value="Result" />,
dataLabel: 'Result',
},
]
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"
/>
)
return {
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>
</>
),
},
{
title: (
<>
{buildsetResultLink}
<span>{buildset.branch ? buildset.branch : buildset.ref}</span>
</>
),
},
{
title: (
<>
{buildsetResultLink}
<span>{buildset.pipeline}</span>
</>
),
},
{
title: (
<>
{buildsetResultLink}
{buildset.change && (
<span style={{ zIndex: 1, position: 'relative' }}>
<ExternalLink target={buildset.ref_url}>
{buildset.change},{buildset.patchset}
</ExternalLink>
</span>
)}
</>
),
},
{
title: (
<>
{buildsetResultLink}
<BuildResult result={buildset.result} />
</>
),
},
],
}
}
function createFetchingRow() {
const rows = [
{
heightAuto: true,
cells: [
{
props: { colSpan: 8 },
title: (
<center>
<Spinner size="xl" />
</center>
),
},
],
},
]
return rows
}
let rows = []
if (fetching) {
rows = createFetchingRow()
// The dataLabel property is used to show the column header in a list-like
// format for smaller viewports. When we are fetching, we don't want the
// fetching row to be prepended by a "Project" column header. The other
// column headers are not relevant here since we only have a single cell in
// the fetching row.
columns[0].dataLabel = ''
} else {
rows = buildsets.map((buildset) => createBuildsetRow(buildset))
}
return (
<>
<Table
aria-label="Builds Table"
variant={TableVariant.compact}
cells={columns}
rows={rows}
className="zuul-build-table"
>
<TableHeader />
<TableBody />
</Table>
{/* Show an empty state in case we don't have any buildsets but are also
not fetching */}
{!fetching && buildsets.length === 0 && (
<EmptyState>
<EmptyStateIcon icon={BuildIcon} />
<Title headingLevel="h1">No buildsets found</Title>
<EmptyStateBody>
No buildsets match this filter criteria. Remove some filters or
clear all to show results.
</EmptyStateBody>
<EmptyStateSecondaryActions>
<Button variant="link" onClick={onClearFilters}>
Clear all filters
</Button>
</EmptyStateSecondaryActions>
</EmptyState>
)}
</>
)
}
BuildsetTable.propTypes = {
buildsets: PropTypes.array.isRequired,
fetching: PropTypes.bool.isRequired,
onClearFilters: PropTypes.func.isRequired,
tenant: PropTypes.object.isRequired,
}
export default connect((state) => ({ tenant: state.tenant }))(BuildsetTable)

View File

@@ -38,16 +38,82 @@ a.refresh {
margin-left: var(--pf-global--spacer--md);
}
/* Build Lists */
/*
* Build Lists and Tables
*/
/* Improve the hover effect of selected lines in the selectable data list*/
.pf-c-data-list__item.pf-m-selectable:hover:not(.pf-m-selected),
.pf-c-data-list__item.pf-m-selectable:focus:not(.pf-m-selected) {
/* Improve the hover/focus effect of selected lines */
--pf-c-data-list__item--before--BackgroundColor: var(
--pf-c-data-list__item--m-selected--before--BackgroundColor
);
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);
}
/* Use the same hover effect on table rows like for the selectable data list */
.zuul-build-table tbody tr:hover {
box-shadow: var(--pf-global--BoxShadow--sm-top),
var(--pf-global--BoxShadow--sm-bottom);
}
@media screen and (max-width: 768px) {
/* For the small-screen table layout the before element is already used to
show the column names. Thus, we fall back to the border to show the hover
effect. The drawback with that is, that we can't show a nice transition.
*/
.zuul-build-table tbody tr:hover {
border-left-color: var(--pf-global--active-color--100);
border-left-width: var(--pf-global--BorderWidth--lg);
border-left-style: solid;
/* Compensate the border width with a negative margin */
margin-left: -3px;
}
}
@media screen and (min-width: 769px) {
/* For the larger screens (normal table layout) we can use the before
element on the first table cell to show the same hover effect like for
the data list */
.zuul-build-table tbody tr td:first-child::before {
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: var(--pf-global--BorderWidth--lg);
content: "";
background-color: transparent;
transition: var(--pf-global--Transition);
}
.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);
}
/*
* Build/Buildset result page
*/

View File

@@ -15,9 +15,6 @@
import * as React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { Link } from 'react-router-dom'
import { Table } from 'patternfly-react'
import * as moment from 'moment-timezone'
import 'moment-duration-format'
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
@@ -28,6 +25,7 @@ import {
getFiltersFromUrl,
writeFiltersToUrl,
} from '../containers/FilterToolbar'
import BuildTable from '../containers/build/BuildTable'
class BuildsPage extends React.Component {
static propTypes = {
@@ -39,7 +37,6 @@ class BuildsPage extends React.Component {
constructor(props) {
super()
this.prepareTableHeaders()
this.filterCategories = [
{
key: 'job_name',
@@ -87,7 +84,8 @@ class BuildsPage extends React.Component {
]
this.state = {
builds: null,
builds: [],
fetching: false,
filters: getFiltersFromUrl(props.location, this.filterCategories),
}
}
@@ -100,9 +98,15 @@ class BuildsPage extends React.Component {
// passed as prop and doesn't change since the page itself wasn't
// re-rendered.
const queryString = buildQueryString(filters)
this.setState({builds: null})
fetchBuilds(this.props.tenant.apiPrefix, queryString).then(response => {
this.setState({builds: response.data})
this.setState({ fetching: true })
// TODO (felix): What happens in case of a broken network connection? Is the
// fetching shows infinitely or can we catch this and show an erro state in
// the table instead?
fetchBuilds(this.props.tenant.apiPrefix, queryString).then((response) => {
this.setState({
builds: response.data,
fetching: false,
})
})
}
@@ -137,107 +141,17 @@ class BuildsPage extends React.Component {
})
}
prepareTableHeaders() {
const headerFormat = value => <Table.Heading>{value}</Table.Heading>
const cellFormat = (value) => (
<Table.Cell>{value}</Table.Cell>)
const linkBuildFormat = (value, rowdata) => (
<Table.Cell>
<Link to={this.props.tenant.linkPrefix + '/build/' + rowdata.rowData.uuid}>{value}</Link>
</Table.Cell>
)
const linkChangeFormat = (value, rowdata) => (
<Table.Cell>
<a href={rowdata.rowData.ref_url}>{value ? rowdata.rowData.change+','+rowdata.rowData.patchset : rowdata.rowData.newrev ? rowdata.rowData.newrev.substr(0, 7) : rowdata.rowData.branch}</a>
</Table.Cell>
)
const durationFormat = (value) => (
<Table.Cell>
{moment.duration(value, 'seconds').format('h [hr] m [min] s [sec]')}
</Table.Cell>
)
const timeFormat = (value) => (
<Table.Cell>
{moment.utc(value).tz(this.props.timezone).format('YYYY-MM-DD HH:mm:ss')}
</Table.Cell>
)
this.columns = []
this.filterTypes = []
const myColumns = [
'job',
'project',
'branch',
'pipeline',
'change',
'duration',
'start time',
'result']
myColumns.forEach(column => {
let prop = column
let formatter = cellFormat
// Adapt column name and property name
if (column === 'job') {
prop = 'job_name'
} else if (column === 'start time') {
prop = 'start_time'
formatter = timeFormat
} else if (column === 'change') {
prop = 'change'
formatter = linkChangeFormat
} else if (column === 'result') {
formatter = linkBuildFormat
} else if (column === 'duration') {
formatter = durationFormat
}
const label = column.charAt(0).toUpperCase() + column.slice(1)
this.columns.push({
header: {label: label, formatters: [headerFormat]},
property: prop,
cell: {formatters: [formatter]}
})
if (prop !== 'start_time' && prop !== 'ref_url' && prop !== 'duration'
&& prop !== 'log_url' && prop !== 'uuid') {
this.filterTypes.push({
id: prop,
title: label,
placeholder: 'Filter by ' + label,
filterType: 'text',
})
}
})
// Add build filter at the end
this.filterTypes.push({
id: 'uuid',
title: 'Build',
placeholder: 'Filter by Build UUID',
filterType: 'text',
})
}
renderTable (builds) {
return (
<Table.PfProvider
striped
bordered
columns={this.columns}
>
<Table.Header/>
<Table.Body
rows={builds}
rowKey='uuid'
onRow={(row) => {
switch (row.result) {
case 'SUCCESS':
return { className: 'success' }
default:
return { className: 'warning' }
}
}} />
</Table.PfProvider>)
handleClearFilters = () => {
// Delete the values for each filter category
const filters = this.filterCategories.reduce((filterDict, category) => {
filterDict[category.key] = []
return filterDict
}, {})
this.handleFilterChange(filters)
}
render() {
const { builds, filters } = this.state
const { builds, fetching, filters } = this.state
return (
<PageSection variant={PageSectionVariants.light}>
<FilterToolbar
@@ -245,7 +159,11 @@ class BuildsPage extends React.Component {
onFilterChange={this.handleFilterChange}
filters={filters}
/>
{builds ? this.renderTable(builds) : <p>Loading...</p>}
<BuildTable
builds={builds}
fetching={fetching}
onClearFilters={this.handleClearFilters}
/>
</PageSection>
)
}

View File

@@ -15,8 +15,6 @@
import * as React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { Link } from 'react-router-dom'
import { Table } from 'patternfly-react'
import { PageSection, PageSectionVariants } from '@patternfly/react-core'
import { fetchBuildsets } from '../api'
@@ -26,7 +24,7 @@ import {
getFiltersFromUrl,
writeFiltersToUrl,
} from '../containers/FilterToolbar'
import BuildsetTable from '../containers/build/BuildsetTable'
class BuildsetsPage extends React.Component {
static propTypes = {
@@ -35,9 +33,8 @@ class BuildsetsPage extends React.Component {
history: PropTypes.object,
}
constructor (props) {
constructor(props) {
super()
this.prepareTableHeaders()
this.filterCategories = [
{
key: 'project',
@@ -79,10 +76,10 @@ class BuildsetsPage extends React.Component {
]
this.state = {
buildsets: null,
buildsets: [],
fetching: false,
filters: getFiltersFromUrl(props.location, this.filterCategories),
}
}
updateData = (filters) => {
@@ -93,20 +90,25 @@ class BuildsetsPage extends React.Component {
// passed as prop and doesn't change since the page itself wasn't
// re-rendered.
const queryString = buildQueryString(filters)
this.setState({buildsets: null})
fetchBuildsets(this.props.tenant.apiPrefix, queryString).then(response => {
this.setState({buildsets: response.data})
})
this.setState({ fetching: true })
fetchBuildsets(this.props.tenant.apiPrefix, queryString).then(
(response) => {
this.setState({
buildsets: response.data,
fetching: false,
})
}
)
}
componentDidMount () {
componentDidMount() {
document.title = 'Zuul Buildsets'
if (this.props.tenant.name) {
this.updateData(this.state.filters)
}
}
componentDidUpdate (prevProps) {
componentDidUpdate(prevProps) {
const { filters } = this.state
if (this.props.tenant.name !== prevProps.tenant.name) {
this.updateData(filters)
@@ -127,93 +129,17 @@ class BuildsetsPage extends React.Component {
})
}
prepareTableHeaders() {
const headerFormat = value => <Table.Heading>{value}</Table.Heading>
const cellFormat = (value) => <Table.Cell>{value}</Table.Cell>
const linkChangeFormat = (value, rowdata) => (
<Table.Cell>
<a href={rowdata.rowData.ref_url}>
{value ?
rowdata.rowData.change + ',' + rowdata.rowData.patchset :
rowdata.rowData.newrev ?
rowdata.rowData.newrev.substr(0, 7) :
rowdata.rowData.branch}
</a>
</Table.Cell>
)
const linkBuildsetFormat = (value, rowdata) => (
<Table.Cell>
<Link
to={this.props.tenant.linkPrefix +
'/buildset/' + rowdata.rowData.uuid}>
{value}
</Link>
</Table.Cell>
)
this.columns = []
this.filterTypes = []
const myColumns = [
'project',
'branch',
'pipeline',
'change',
'result']
myColumns.forEach(column => {
let prop = column
let formatter = cellFormat
if (column === 'change') {
formatter = linkChangeFormat
} else if (column === 'result') {
formatter = linkBuildsetFormat
}
const label = column.charAt(0).toUpperCase() + column.slice(1)
this.columns.push({
header: {label: label, formatters: [headerFormat]},
property: prop,
cell: {formatters: [formatter]}
})
if (column !== 'builds') {
this.filterTypes.push({
id: prop,
title: label,
placeholder: 'Filter by ' + label,
filterType: 'text',
})
}
})
// Add buildset filter at the end
this.filterTypes.push({
id: 'uuid',
title: 'Buildset',
placeholder: 'Filter by Buildset UUID',
filterType: 'text',
})
}
renderTable (buildsets) {
return (
<Table.PfProvider
striped
bordered
columns={this.columns}
>
<Table.Header/>
<Table.Body
rows={buildsets}
rowKey='uuid'
onRow={(row) => {
switch (row.result) {
case 'SUCCESS':
return { className: 'success' }
default:
return { className: 'warning' }
}
}} />
</Table.PfProvider>)
handleClearFilters = () => {
// Delete the values for each filter category
const filters = this.filterCategories.reduce((filterDict, category) => {
filterDict[category.key] = []
return filterDict
}, {})
this.handleFilterChange(filters)
}
render() {
const { buildsets, filters } = this.state
const { buildsets, fetching, filters } = this.state
return (
<PageSection variant={PageSectionVariants.light}>
<FilterToolbar
@@ -221,10 +147,14 @@ class BuildsetsPage extends React.Component {
onFilterChange={this.handleFilterChange}
filters={filters}
/>
{buildsets ? this.renderTable(buildsets) : <p>Loading...</p>}
<BuildsetTable
buildsets={buildsets}
fetching={fetching}
onClearFilters={this.handleClearFilters}
/>
</PageSection>
)
}
}
export default connect(state => ({tenant: state.tenant}))(BuildsetsPage)
export default connect((state) => ({ tenant: state.tenant }))(BuildsetsPage)

View File

@@ -1510,33 +1510,51 @@
dependencies:
"@types/node" ">= 8"
"@patternfly/react-core@^4.18.5":
version "4.18.5"
resolved "https://registry.yarnpkg.com/@patternfly/react-core/-/react-core-4.18.5.tgz#465ee3be0e58f7fdead9644ed2667f18eff0d684"
integrity sha512-wUHLXPOklcAVA9nCnmUvGwdfTZnypxNUnA0l+eEiq1QWhQoSRdI7S/HNOelYhpRjMMwPwy3yMsJUjHsXdqv2FQ==
"@patternfly/patternfly@4.31.6":
version "4.31.6"
resolved "https://registry.yarnpkg.com/@patternfly/patternfly/-/patternfly-4.31.6.tgz#ef9919df610171760cd19920a904ca9b09a74593"
integrity sha512-gp8tpbE4Z6C1PIQwNiWMjO5XSr/UGjXs4InL/zmxgZbToyizUxsudwJyCObtdvDNoN57ZJp0gYWYy0tIuwEyMA==
"@patternfly/react-core@^4.40.4":
version "4.40.4"
resolved "https://registry.yarnpkg.com/@patternfly/react-core/-/react-core-4.40.4.tgz#e4409f89327e2fcdcd07a08833c0149e6f2f6966"
integrity sha512-NQuUgIVEty7BBNJMJAVRXejOGRGpRQwgQ8Rw/J/JlgkhtOrCSFX5cEbpAXMXLYWkJrz0++XfRK/FQMoQbvS2hQ==
dependencies:
"@patternfly/react-icons" "^4.3.5"
"@patternfly/react-styles" "^4.3.4"
"@patternfly/react-tokens" "^4.4.4"
"@patternfly/react-icons" "^4.7.2"
"@patternfly/react-styles" "^4.7.2"
"@patternfly/react-tokens" "^4.9.4"
focus-trap "4.0.2"
react-dropzone "9.0.0"
tippy.js "5.1.2"
tslib "^1.11.1"
"@patternfly/react-icons@^4.3.5":
version "4.3.5"
resolved "https://registry.yarnpkg.com/@patternfly/react-icons/-/react-icons-4.3.5.tgz#b98c5af80f4729e6203c8e799ace2f57308b3b9a"
integrity sha512-+GublxpFXR+y/5zygf9q00/LvIvso8jr0mxZGhVxsKmi2dUu7xAvN+T+5vjS9fiMbXf7WXsSPXST/UTiBIVTdQ==
"@patternfly/react-icons@^4.7.2":
version "4.7.2"
resolved "https://registry.yarnpkg.com/@patternfly/react-icons/-/react-icons-4.7.2.tgz#f4ad252cb5682bd95da474ce9ce6ddf7fb3a1ac1"
integrity sha512-r1yCVHxUtRSblo8VwfaUM0d49z4eToZXAI0VzOnfKPRgSmGZrn6l8soQgDDtyQsSDr534Qvm55y/qLrlR9JCnw==
"@patternfly/react-styles@^4.3.4":
version "4.3.4"
resolved "https://registry.yarnpkg.com/@patternfly/react-styles/-/react-styles-4.3.4.tgz#ba62f983f1bd2586d2878d8a38912442ebca85e8"
integrity sha512-d5W5G9g7sr7DthGPFiF6Oa33w8JFJ+ocLZDogyZcS1Oq0BJJX8j+hZNXZfhXxmHoXufxQL6RJ4dOyoa2zEZUvA==
"@patternfly/react-styles@^4.7.2":
version "4.7.2"
resolved "https://registry.yarnpkg.com/@patternfly/react-styles/-/react-styles-4.7.2.tgz#6671a243401ef55adddcb0e0922f5f5f4eea840e"
integrity sha512-r3zyrt1mXcqdXaEq+otl1cGsN0Ou1k8uIJSY+4EGe2A5jLGbX3vBTwUrpPKLB6tUdNL+mZriFf+3oKhWbVZDkw==
"@patternfly/react-tokens@^4.4.4":
version "4.4.4"
resolved "https://registry.yarnpkg.com/@patternfly/react-tokens/-/react-tokens-4.4.4.tgz#4a6fc7234908343087ccbc8e1ae53336d7fc7311"
integrity sha512-vhDBtwkp1PTAqXDjAsUPRf/ewBh2Asong8MPr9ZGeXAeOULW8creW7GJx+JZX9eaEJMA3ESMeZ6wZ5j/yyWMGQ==
"@patternfly/react-table@^4.15.5":
version "4.15.5"
resolved "https://registry.yarnpkg.com/@patternfly/react-table/-/react-table-4.15.5.tgz#7fc3fcd37a6fd4dca00cc32d24c76199ee41a7f1"
integrity sha512-GlyKrEDMY+yLvczj5rWpNKcUp90Ib7alKV9JK8rVLOpTsukQ0QplXxYFsnIrombcaw2V54XVdflZGjsB0GoHEw==
dependencies:
"@patternfly/patternfly" "4.31.6"
"@patternfly/react-core" "^4.40.4"
"@patternfly/react-icons" "^4.7.2"
"@patternfly/react-styles" "^4.7.2"
"@patternfly/react-tokens" "^4.9.4"
lodash "^4.17.19"
tslib "^1.11.1"
"@patternfly/react-tokens@^4.9.4":
version "4.9.4"
resolved "https://registry.yarnpkg.com/@patternfly/react-tokens/-/react-tokens-4.9.4.tgz#71ea3c33045fb29bcc8d98f2c0f07bfcdc89a12c"
integrity sha512-AJpcAvzWXvfThT2mx24rV7OJSHvZnIsOP1bVrXiubpFAJhi/Suq+LGe/lTPUnuSXaflwyDBRZDXWWmJb4yaWqg==
"@semantic-release/commit-analyzer@^6.1.0":
version "6.3.3"
@@ -8896,6 +8914,11 @@ lodash@4.17.15, "lodash@>=3.5 <5", lodash@^4.17.10, lodash@^4.17.11, lodash@^4.1
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
lodash@^4.17.19:
version "4.17.20"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
loglevel@^1.6.6:
version "1.6.8"
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.8.tgz#8a25fb75d092230ecd4457270d80b54e28011171"