From 503cffcd3cf7eff61cf7f1d3ed6209ce9b4d72da Mon Sep 17 00:00:00 2001 From: Martin Michalik <62351699+xmicha82@users.noreply.github.com> Date: Tue, 17 Dec 2024 13:22:33 +0100 Subject: [PATCH] fix(RHINENG-11976): Fix table loading states (#843) Fixes https://issues.redhat.com/browse/RHINENG-11976. --- cypress/utils/table.js | 2 +- package-lock.json | 45 +++---- package.json | 1 + .../AffectedClustersTable.cy.js | 6 +- .../AffectedClustersTable.js | 116 +++++++++--------- .../ClusterRules/ClusterRules.cy.js | 10 +- src/Components/ClusterRules/ClusterRules.js | 107 ++++++++-------- .../ClustersListTable/ClustersListTable.cy.js | 6 +- .../ClustersListTable/ClustersListTable.js | 92 +++++++------- src/Components/RecsListTable/RecsListTable.js | 93 +++++++------- src/Components/WorkloadRules/WorkloadRules.js | 108 ++++++++-------- .../WorkloadsListTable/WorkloadsListTable.js | 97 ++++++++------- 12 files changed, 357 insertions(+), 326 deletions(-) diff --git a/cypress/utils/table.js b/cypress/utils/table.js index ea666b72..a403b8ec 100644 --- a/cypress/utils/table.js +++ b/cypress/utils/table.js @@ -176,9 +176,9 @@ function checkSorting( } }); } else { + cy.get(header).find('button').click(); cy.get(header) .find('button') - .click() .click() // TODO dblclick fails for unknown reason in RecsListTable when sorting by Clusters .then(() => { if (validateURL) { diff --git a/package-lock.json b/package-lock.json index bda90f29..79366cfd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.17.2", "hasInstallScript": true, "dependencies": { + "@patternfly/react-component-groups": "^5.4.0", "@patternfly/react-core": "^5.4.8", "@patternfly/react-icons": "^5.4.2", "@patternfly/react-table": "^5.4.5", @@ -4049,14 +4050,14 @@ "dev": true }, "node_modules/@patternfly/react-component-groups": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@patternfly/react-component-groups/-/react-component-groups-5.0.0.tgz", - "integrity": "sha512-ON4h4SKOCgLRgZLd/FOj44wU19ytvFPjflxPSYU0KfCWlVgb6F62+l316/Va/tzDo/AwZypnUxOEksXDv+C2+A==", - "dependencies": { - "@patternfly/react-core": "^5.1.1", - "@patternfly/react-icons": "^5.1.1", - "@patternfly/react-table": "^5.1.1", - "clsx": "^2.0.0", + "version": "5.5.5", + "resolved": "https://registry.npmjs.org/@patternfly/react-component-groups/-/react-component-groups-5.5.5.tgz", + "integrity": "sha512-Cgp1XxyBWnEDKAQsP+B7A4wlz6Bcp0bjwSMamdOiCR4GALtpBXXGrv6daAomoVCkL9l3zibcAfm/o9d9XBE9Ag==", + "dependencies": { + "@patternfly/react-core": "^5.4.1", + "@patternfly/react-icons": "^5.4.0", + "@patternfly/react-table": "^5.4.1", + "clsx": "^2.1.1", "react-jss": "^10.10.0" }, "peerDependencies": { @@ -9549,9 +9550,9 @@ } }, "node_modules/clsx": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz", - "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", "engines": { "node": ">=6" } @@ -32120,14 +32121,14 @@ "dev": true }, "@patternfly/react-component-groups": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@patternfly/react-component-groups/-/react-component-groups-5.0.0.tgz", - "integrity": "sha512-ON4h4SKOCgLRgZLd/FOj44wU19ytvFPjflxPSYU0KfCWlVgb6F62+l316/Va/tzDo/AwZypnUxOEksXDv+C2+A==", - "requires": { - "@patternfly/react-core": "^5.1.1", - "@patternfly/react-icons": "^5.1.1", - "@patternfly/react-table": "^5.1.1", - "clsx": "^2.0.0", + "version": "5.5.5", + "resolved": "https://registry.npmjs.org/@patternfly/react-component-groups/-/react-component-groups-5.5.5.tgz", + "integrity": "sha512-Cgp1XxyBWnEDKAQsP+B7A4wlz6Bcp0bjwSMamdOiCR4GALtpBXXGrv6daAomoVCkL9l3zibcAfm/o9d9XBE9Ag==", + "requires": { + "@patternfly/react-core": "^5.4.1", + "@patternfly/react-icons": "^5.4.0", + "@patternfly/react-table": "^5.4.1", + "clsx": "^2.1.1", "react-jss": "^10.10.0" } }, @@ -36295,9 +36296,9 @@ } }, "clsx": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz", - "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==" }, "co": { "version": "4.6.0", diff --git a/package.json b/package.json index a4846f54..f4a2c136 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "postinstall": "ts-patch install" }, "dependencies": { + "@patternfly/react-component-groups": "^5.4.0", "@patternfly/react-core": "^5.4.8", "@patternfly/react-icons": "^5.4.2", "@patternfly/react-table": "^5.4.5", diff --git a/src/Components/AffectedClustersTable/AffectedClustersTable.cy.js b/src/Components/AffectedClustersTable/AffectedClustersTable.cy.js index 346884f7..45ed0421 100644 --- a/src/Components/AffectedClustersTable/AffectedClustersTable.cy.js +++ b/src/Components/AffectedClustersTable/AffectedClustersTable.cy.js @@ -163,13 +163,14 @@ describe('non-empty successful affected clusters table', () => { }); it('renders table header', () => { + cy.get('[data-ouia-component-id=SkeletonTable-tr-0]').should('not.exist'); checkTableHeaders(TABLE_HEADERS); }); it('rows show cluster names instead uuids when available', () => { const names = _.map(data, 'name'); // Wait for skeleton to disappear - cy.get('[data-ouia-component-id=loading-skeleton]').should('not.exist'); + cy.get('[data-ouia-component-id=SkeletonTable-tr-0]').should('not.exist'); cy.get(`td[data-label="Name"]`) .then(($els) => { return _.map(Cypress.$.makeArray($els), 'innerText'); @@ -178,7 +179,7 @@ describe('non-empty successful affected clusters table', () => { }); it('names of rows are links', () => { - cy.get('[data-ouia-component-id=loading-skeleton]').should('not.exist'); + cy.get('[data-ouia-component-id=SkeletonTable-tr-0]').should('not.exist'); cy.get(TABLE) .find(TABLE_ROW) .each(($el, index) => { @@ -641,6 +642,7 @@ describe('empty successful affected clusters table', () => { }); it('renders table headers', () => { + cy.get('[data-ouia-component-id=SkeletonTable-tr-0]').should('not.exist'); checkTableHeaders(TABLE_HEADERS); }); }); diff --git a/src/Components/AffectedClustersTable/AffectedClustersTable.js b/src/Components/AffectedClustersTable/AffectedClustersTable.js index 534db44c..451ac259 100644 --- a/src/Components/AffectedClustersTable/AffectedClustersTable.js +++ b/src/Components/AffectedClustersTable/AffectedClustersTable.js @@ -30,7 +30,6 @@ import { AFFECTED_CLUSTERS_VERSION_CELL, FILTER_CATEGORIES, } from '../../AppConstants'; -import Loading from '../Loading/Loading'; import { AFFECTED_CLUSTERS_INITIAL_STATE, resetFilters, @@ -45,6 +44,7 @@ import { addFilterParam as _addFilterParam, } from '../Common/Tables'; import { BASE_PATH } from '../../Routes'; +import { SkeletonTable } from '@patternfly/react-component-groups'; const AffectedClustersTable = ({ query, rule, afterDisableFn }) => { const intl = useIntl(); @@ -97,7 +97,7 @@ const AffectedClustersTable = ({ query, rule, afterDisableFn }) => { type: conditionalFilterType.text, filterValues: { id: 'name-filter', - key: 'name-filter', + // key: 'name-filter', onChange: (event, value) => addFilterParam('text', value), value: filters.text, }, @@ -108,7 +108,7 @@ const AffectedClustersTable = ({ query, rule, afterDisableFn }) => { type: conditionalFilterType.checkbox, filterValues: { id: 'version-filter', - key: 'version-filter', + // key: 'version-filter', onChange: (event, value) => addFilterParam('version', value), value: filters.version, items: uniqBy( @@ -384,59 +384,65 @@ const AffectedClustersTable = ({ query, rule, afterDisableFn }) => { ], }} /> - c.title)} + isSelectable + variant="compact" + /> + ) : ( +
+ ) : noInput ? ( + + ) : ( + + ), }, - title: errorState ? ( - - ) : loadingState ? ( - - ) : noInput ? ( - - ) : ( - - ), - }, - ], - }, - ] - ) : successState ? ( - displayedRows - ) : ( - - ) - } - sortBy={{ - index: filters.sortIndex, - direction: filters.sortDirection, - }} - onSort={onSort} - canSelectAll={false} - onSelect={displayedRows?.length > 0 ? onSelect : undefined} - actions={[ - { - title: 'Disable recommendation for cluster', - onClick: (event, rowIndex) => - handleModalToggle(true, filteredRows[rowIndex].id), - }, - ]} - > - - -
+ ], + }, + ] + ) : successState ? ( + displayedRows + ) : ( + + ) + } + sortBy={{ + index: filters.sortIndex, + direction: filters.sortDirection, + }} + onSort={onSort} + canSelectAll={false} + onSelect={displayedRows?.length > 0 ? onSelect : undefined} + actions={[ + { + title: 'Disable recommendation for cluster', + onClick: (event, rowIndex) => + handleModalToggle(true, filteredRows[rowIndex].id), + }, + ]} + > + + + + )} { it('expand one row then sort', () => { cy.get('#expandable-toggle2').click(); - cy.get(TABLE) - .find('th[data-label=Description]') - .find('button') - .click() - .click(); + cy.get(TABLE).find('th[data-label=Description]').find('button').click(); + cy.get(TABLE).find('th[data-label=Description]').find('button').click(); cy.get(EXPANDABLES).should('have.length', 2); }); @@ -413,7 +410,8 @@ describe('cluster rules table testing the first query parameter', () => { if (order === 'ascending') { cy.get(header).find('button').click(); } else { - cy.get(header).find('button').click().click(); + cy.get(header).find('button').click(); + cy.get(header).find('button').click(); } let sortedDescriptions = _.map( _.orderBy( diff --git a/src/Components/ClusterRules/ClusterRules.js b/src/Components/ClusterRules/ClusterRules.js index 59c97a35..8916f065 100644 --- a/src/Components/ClusterRules/ClusterRules.js +++ b/src/Components/ClusterRules/ClusterRules.js @@ -15,6 +15,7 @@ import { TableHeader, } from '@patternfly/react-table/deprecated'; import { Tooltip } from '@patternfly/react-core'; +import { SkeletonTable } from '@patternfly/react-component-groups'; import { TooltipPosition } from '@patternfly/react-core/dist/js/components/Tooltip'; import PrimaryToolbar from '@redhat-cloud-services/frontend-components/PrimaryToolbar'; @@ -50,7 +51,6 @@ import { updateClusterRulesFilters, } from '../../Services/Filters'; import { getErrorKey, getPluginName } from '../../Utilities/Rule'; -import Loading from '../Loading/Loading'; import { useGetClusterByIdQuery } from '../../Services/SmartProxy'; import useChrome from '@redhat-cloud-services/frontend-components/useChrome'; @@ -456,57 +456,62 @@ const ClusterRules = () => { : activeFiltersConfig } /> - // no Insights results received yet + {loadingState ? ( + c.title)} + variant="compact" + /> + ) : ( +
// no Insights results received yet + ) : ( + // any other problem + ) + ) : noInput ? ( + ) : ( - // any other problem - ) - ) : loadingState ? ( - - ) : noInput ? ( - - ) : ( - - ), - }, - ], - }, - ] - ) : successState ? ( - displayedRows - ) : ( - - ) - } - cells={CLUSTER_RULES_COLUMNS} - sortBy={{ - index: filters.sortIndex, - direction: filters.sortDirection, - }} - onSort={onSort} - variant={TableVariant.compact} - isStickyHeader - canCollapseAll - > - - -
+ + ), + }, + ], + }, + ] + ) : successState ? ( + displayedRows + ) : ( + + ) + } + cells={CLUSTER_RULES_COLUMNS} + sortBy={{ + index: filters.sortIndex, + direction: filters.sortDirection, + }} + onSort={onSort} + variant={TableVariant.compact} + isStickyHeader + canCollapseAll + > + + + + )} ); }; diff --git a/src/Components/ClustersListTable/ClustersListTable.cy.js b/src/Components/ClustersListTable/ClustersListTable.cy.js index b3f8afd1..1faa93bd 100644 --- a/src/Components/ClustersListTable/ClustersListTable.cy.js +++ b/src/Components/ClustersListTable/ClustersListTable.cy.js @@ -581,7 +581,7 @@ describe('clusters list table', () => { it('rows show cluster names instead uuids when available', () => { const names = _.map(data, 'name'); - cy.get('[data-ouia-component-id=loading-skeleton]').should('not.exist'); + cy.get('[data-ouia-component-id=SkeletonTable-tr-0]').should('not.exist'); cy.get(`td[data-label="Name"]`) .then(($els) => { return _.map(Cypress.$.makeArray($els), 'innerText'); @@ -590,7 +590,7 @@ describe('clusters list table', () => { }); it('names of rows are links', () => { - cy.get('[data-ouia-component-id=loading-skeleton]').should('not.exist'); + cy.get('[data-ouia-component-id=SkeletonTable-tr-0]').should('not.exist'); cy.get(TBODY) .children() .each(($el, index) => { @@ -604,7 +604,7 @@ describe('clusters list table', () => { }); it('total risk hits are mapped correctly', () => { - cy.get('[data-ouia-component-id=loading-skeleton]').should('not.exist'); + cy.get('[data-ouia-component-id=SkeletonTable-tr-0]').should('not.exist'); cy.get('table') .find(TBODY) .find(ROW) diff --git a/src/Components/ClustersListTable/ClustersListTable.js b/src/Components/ClustersListTable/ClustersListTable.js index 067ade85..a381e6e5 100644 --- a/src/Components/ClustersListTable/ClustersListTable.js +++ b/src/Components/ClustersListTable/ClustersListTable.js @@ -15,6 +15,7 @@ import { TableHeader, } from '@patternfly/react-table/deprecated'; import { Label, Pagination, Tooltip } from '@patternfly/react-core'; +import { SkeletonTable } from '@patternfly/react-component-groups'; import { PaginationVariant } from '@patternfly/react-core/dist/js/components/Pagination/Pagination'; import PrimaryToolbar from '@redhat-cloud-services/frontend-components/PrimaryToolbar/PrimaryToolbar'; import DateFormat from '@redhat-cloud-services/frontend-components/DateFormat'; @@ -44,7 +45,7 @@ import { compareSemVer, toValidSemVer, } from '../Common/Tables'; -import Loading from '../Loading/Loading'; +// import Loading from '../Loading/Loading'; import messages from '../../Messages'; import { ErrorState, @@ -414,49 +415,54 @@ const ClustersListTable = ({ filterConfig={{ items: filterConfigItems }} activeFiltersConfig={activeFiltersConfig} /> - c.title)} + variant="compact" + /> + ) : ( +
+ ) : ( + + ), }, - title: errorState ? ( - - ) : loadingState ? ( - - ) : ( - - ), - }, - ], - }, - ] - ) : successState ? ( - displayedRows - ) : ( - - ) - } - sortBy={{ - index: filters.sortIndex, - direction: filters.sortDirection, - }} - onSort={onSort} - isStickyHeader - > - - -
+ ], + }, + ] + ) : successState ? ( + displayedRows + ) : ( + + ) + } + sortBy={{ + index: filters.sortIndex, + direction: filters.sortDirection, + }} + onSort={onSort} + isStickyHeader + > + + + + )} { }} activeFiltersConfig={errorState ? undefined : activeFiltersConfig} /> - c.title)} + variant="compact" + /> + ) : ( +
: , }, - title: errorState ? ( - - ) : loadingState ? ( - - ) : ( - - ), - }, - ], - }, - ] - ) : successState ? ( - displayedRows - ) : ( - - ) - } - onCollapse={handleOnCollapse} // TODO: set undefined when there is an empty state - sortBy={{ - index: filters.sortIndex, - direction: filters.sortDirection, - }} - onSort={onSort} - actionResolver={actionResolver} - isStickyHeader - ouiaSafe={!loadingState} - canCollapseAll - > - - -
+ ], + }, + ] + ) : successState ? ( + displayedRows + ) : ( + + ) + } + onCollapse={handleOnCollapse} // TODO: set undefined when there is an empty state + sortBy={{ + index: filters.sortIndex, + direction: filters.sortDirection, + }} + onSort={onSort} + actionResolver={actionResolver} + isStickyHeader + ouiaSafe={!loadingState} + canCollapseAll + > + + + + )} { const dispatch = useDispatch(); @@ -277,57 +277,63 @@ const WorkloadRules = ({ workload, namespaceName }) => { : activeFiltersConfig } /> - + {loadingState ? ( + c.title)} + isExpandable + variant="compact" + /> + ) : ( +
+ ) : ( + + ) + ) : noMatchingRecs ? ( + ) : ( - - ) - ) : loadingState ? ( - - ) : noMatchingRecs ? ( - - ) : ( - - ), - }, - ], - }, - ] - ) : successState ? ( - displayedRows - ) : ( - - ) - } - variant={TableVariant.compact} - isStickyHeader - canCollapseAll - sortBy={{ - index: filters.sortIndex, - direction: filters.sortDirection, - }} - onSort={onSort} - > - - -
+ + ), + }, + ], + }, + ] + ) : successState ? ( + displayedRows + ) : ( + + ) + } + variant={TableVariant.compact} + isStickyHeader + canCollapseAll + sortBy={{ + index: filters.sortIndex, + direction: filters.sortDirection, + }} + onSort={onSort} + > + + + + )} ); }; diff --git a/src/Components/WorkloadsListTable/WorkloadsListTable.js b/src/Components/WorkloadsListTable/WorkloadsListTable.js index a48c1e3c..58ec27a9 100644 --- a/src/Components/WorkloadsListTable/WorkloadsListTable.js +++ b/src/Components/WorkloadsListTable/WorkloadsListTable.js @@ -22,6 +22,7 @@ import DateFormat from '@redhat-cloud-services/frontend-components/DateFormat'; import { Link, useLocation } from 'react-router-dom'; import { BASE_PATH } from '../../Routes'; import { Pagination } from '@patternfly/react-core'; +import { SkeletonTable } from '@patternfly/react-component-groups'; import { conditionalFilterType } from '@redhat-cloud-services/frontend-components/ConditionalFilter/conditionalFilterConstants'; import { useDispatch, useSelector } from 'react-redux'; import { @@ -44,7 +45,6 @@ import { NoMatchingWorkloads, NoDVOInstalledOrDataCollected, } from '../MessageState/EmptyStates'; -import Loading from '../Loading/Loading'; import ShieldSet from '../ShieldSet'; import { filtersAreApplied } from '../../Utilities/Workloads'; @@ -212,7 +212,7 @@ const WorkloadsListTable = ({ label: 'Cluster name', type: 'text', filterValues: { - key: 'cluster_name', + // key: 'cluster_name', onChange: (_event, value) => updateFilters({ ...filters, offset: 0, cluster_name: value }), value: filters.cluster_name, @@ -223,7 +223,7 @@ const WorkloadsListTable = ({ label: 'Namespace name', type: 'text', filterValues: { - key: 'namespace_name', + // key: 'namespace_name', onChange: (_event, value) => updateFilters({ ...filters, offset: 0, namespace_name: value }), value: filters.namespace_name, @@ -236,7 +236,7 @@ const WorkloadsListTable = ({ id: WORKLOADS_TABLE_FILTER_CATEGORIES.severity.urlParam, value: `checkbox-${WORKLOADS_TABLE_FILTER_CATEGORIES.severity.urlParam}`, filterValues: { - key: `${WORKLOADS_TABLE_FILTER_CATEGORIES.severity.urlParam}-filter`, + // key: `${WORKLOADS_TABLE_FILTER_CATEGORIES.severity.urlParam}-filter`, onChange: (_event, value) => addFilterParam('severity', value), value: filters.severity, items: WORKLOADS_TABLE_FILTER_CATEGORIES.severity.values, @@ -312,49 +312,54 @@ const WorkloadsListTable = ({ isError ? { showDeleteButton: false } : activeFiltersConfig } /> - c.title)} + variant="compact" + /> + ) : ( +
+ ) : ( + + ), }, - title: errorState ? ( - - ) : loadingState ? ( - - ) : ( - - ), - }, - ], - }, - ] - ) : successState ? ( - rows - ) : ( - - ) - } - isStickyHeader - sortBy={{ - index: filters.sortIndex, - direction: filters.sortDirection, - }} - onSort={onSort} - > - - -
+ ], + }, + ] + ) : successState ? ( + rows + ) : ( + + ) + } + isStickyHeader + sortBy={{ + index: filters.sortIndex, + direction: filters.sortDirection, + }} + onSort={onSort} + > + + + + )}