diff --git a/src/PresentationalComponents/AdvisoryHeader/AdvisoryHeader.js b/src/PresentationalComponents/AdvisoryHeader/AdvisoryHeader.js index a72f25f5b..2aa62f047 100644 --- a/src/PresentationalComponents/AdvisoryHeader/AdvisoryHeader.js +++ b/src/PresentationalComponents/AdvisoryHeader/AdvisoryHeader.js @@ -1,3 +1,4 @@ +import React, { Fragment, lazy, Suspense, useState } from 'react'; import { Button, Content, @@ -12,7 +13,6 @@ import { } from '@patternfly/react-core'; import { processDate } from '@redhat-cloud-services/frontend-components-utilities/helpers'; import propTypes from 'prop-types'; -import React, { Fragment, lazy, Suspense, useState } from 'react'; import messages from '../../Messages'; import WithLoader, { WithLoaderVariants, @@ -51,19 +51,19 @@ const AdvisoryHeader = ({ attributes, isLoading }) => { {attributes.public_date && ( - + <> {intl.formatMessage(messages.labelsPublicDate, { date: processDate(attributes.public_date), })}
-
+ )} {attributes.modified_date && ( - + <> {intl.formatMessage(messages.labelsModifiedDate, { date: processDate(attributes.modified_date), })} - + )}
{isRHAdvisory(attributes.id) && ( diff --git a/src/PresentationalComponents/Filters/RebootFilter.js b/src/PresentationalComponents/Filters/RebootFilter.js index 1774a0543..ec20de24b 100644 --- a/src/PresentationalComponents/Filters/RebootFilter.js +++ b/src/PresentationalComponents/Filters/RebootFilter.js @@ -1,5 +1,5 @@ +import { useMemo } from 'react'; import { conditionalFilterType } from '@redhat-cloud-services/frontend-components/ConditionalFilter'; -import React from 'react'; import { rebootRequired } from '../../Utilities/constants'; import { intl } from '../../Utilities/IntlProvider'; import messages from '../../Messages'; @@ -7,7 +7,7 @@ import messages from '../../Messages'; const rebootFilter = (apply, currentFilter = {}) => { let { reboot_required: currentValue } = currentFilter; - const rebootMap = React.useMemo( + const rebootMap = useMemo( () => rebootRequired.map(({ value, label }) => ({ label, diff --git a/src/PresentationalComponents/Filters/SeverityFilter.js b/src/PresentationalComponents/Filters/SeverityFilter.js index f69f4c9de..082ff8fc5 100644 --- a/src/PresentationalComponents/Filters/SeverityFilter.js +++ b/src/PresentationalComponents/Filters/SeverityFilter.js @@ -1,4 +1,4 @@ -import React from 'react'; +import { useMemo } from 'react'; import { advisorySeverities } from '../../Utilities/constants'; import { intl } from '../../Utilities/IntlProvider'; import messages from '../../Messages'; @@ -8,7 +8,7 @@ import { conditionalFilterType } from '@redhat-cloud-services/frontend-component // Keep UI state as string arrays for PatternFly checkboxes, but collapse `[null]` to bare `null`. const severityFilter = (apply, currentFilter = {}) => { - const advisorySeverityMap = React.useMemo( + const advisorySeverityMap = useMemo( () => advisorySeverities.map(({ value, label }) => ({ label, diff --git a/src/PresentationalComponents/Filters/StatusFilter.js b/src/PresentationalComponents/Filters/StatusFilter.js index 779839778..05a9738b2 100644 --- a/src/PresentationalComponents/Filters/StatusFilter.js +++ b/src/PresentationalComponents/Filters/StatusFilter.js @@ -1,5 +1,5 @@ +import { useMemo } from 'react'; import { conditionalFilterType } from '@redhat-cloud-services/frontend-components/ConditionalFilter'; -import React from 'react'; import { updatableTypes } from '../../Utilities/constants'; import { intl } from '../../Utilities/IntlProvider'; import messages from '../../Messages'; @@ -7,7 +7,7 @@ import messages from '../../Messages'; const statusFilter = (apply, currentFilter = {}) => { let { update_status: currentValue } = currentFilter; - const updatableTypesMap = React.useMemo( + const updatableTypesMap = useMemo( () => updatableTypes.map(({ value, label }) => ({ label, diff --git a/src/PresentationalComponents/Filters/SystemStaleFilter.js b/src/PresentationalComponents/Filters/SystemStaleFilter.js index 9a89ebee3..a0a712e6b 100644 --- a/src/PresentationalComponents/Filters/SystemStaleFilter.js +++ b/src/PresentationalComponents/Filters/SystemStaleFilter.js @@ -1,5 +1,5 @@ +import { useMemo } from 'react'; import { conditionalFilterType } from '@redhat-cloud-services/frontend-components/ConditionalFilter'; -import React from 'react'; import { staleSystems } from '../../Utilities/constants'; import { intl } from '../../Utilities/IntlProvider'; import messages from '../../Messages'; @@ -7,7 +7,7 @@ import messages from '../../Messages'; const systemsStaleFilter = (apply, currentFilter = {}) => { let { stale: currentValue } = currentFilter; - const staleMap = React.useMemo( + const staleMap = useMemo( () => staleSystems.map(({ value, label }) => ({ label, diff --git a/src/PresentationalComponents/Filters/TypeFilter.js b/src/PresentationalComponents/Filters/TypeFilter.js index 1eff0f477..65de9af53 100644 --- a/src/PresentationalComponents/Filters/TypeFilter.js +++ b/src/PresentationalComponents/Filters/TypeFilter.js @@ -1,11 +1,11 @@ +import { useMemo } from 'react'; import { conditionalFilterType } from '@redhat-cloud-services/frontend-components/ConditionalFilter'; -import React from 'react'; import { advisoryTypes } from '../../Utilities/constants'; import { intl } from '../../Utilities/IntlProvider'; import messages from '../../Messages'; const typeFilter = (apply, currentFilter = {}) => { - const advisoryTypesMap = React.useMemo( + const advisoryTypesMap = useMemo( () => advisoryTypes.map(({ value, label }) => ({ label, diff --git a/src/PresentationalComponents/Header/Header.js b/src/PresentationalComponents/Header/Header.js index 8fa8c3352..19a0daa26 100644 --- a/src/PresentationalComponents/Header/Header.js +++ b/src/PresentationalComponents/Header/Header.js @@ -1,11 +1,11 @@ +import React from 'react'; import { Split, SplitItem } from '@patternfly/react-core'; import { PageHeader, PageHeaderTitle } from '@redhat-cloud-services/frontend-components/PageHeader'; import PropTypes from 'prop-types'; -import React from 'react'; import HeaderBreadcrumbs from './HeaderBreadcrumbs'; const Header = ({ title, breadcrumbs, children, headerOUIA, actions }) => ( - + <> {breadcrumbs && } @@ -17,7 +17,7 @@ const Header = ({ title, breadcrumbs, children, headerOUIA, actions }) => ( {children} - + ); Header.propTypes = { diff --git a/src/PresentationalComponents/PackageHeader/PackageHeader.js b/src/PresentationalComponents/PackageHeader/PackageHeader.js index 605e47cfe..7d011938d 100644 --- a/src/PresentationalComponents/PackageHeader/PackageHeader.js +++ b/src/PresentationalComponents/PackageHeader/PackageHeader.js @@ -1,11 +1,11 @@ +import React, { useState } from 'react'; import { Grid, GridItem, Stack, StackItem } from '@patternfly/react-core'; import propTypes from 'prop-types'; -import React, { Fragment } from 'react'; import { truncateDescription } from '../../Utilities/Helpers'; import WithLoader, { WithLoaderVariants } from '../WithLoader/WithLoader'; const PackageHeader = ({ attributes: { description }, isLoading }) => { - const [wordLength, setWordLength] = React.useState(1000); + const [wordLength, setWordLength] = useState(1000); return ( (description && ( @@ -21,7 +21,7 @@ const PackageHeader = ({ attributes: { description }, isLoading }) => { - )) || + )) || <> ); }; diff --git a/src/PresentationalComponents/StatusReports/AdvisoriesStatusReport.js b/src/PresentationalComponents/StatusReports/AdvisoriesStatusReport.js index a4e0552bd..c89c40646 100644 --- a/src/PresentationalComponents/StatusReports/AdvisoriesStatusReport.js +++ b/src/PresentationalComponents/StatusReports/AdvisoriesStatusReport.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import propTypes from 'prop-types'; import { intl } from '../../Utilities/IntlProvider'; import { fetchApplicableAdvisoriesApi } from '../../Utilities/api/api'; @@ -46,8 +46,8 @@ const StatusCard = ({ advisory: { attributes, id } }) => ( ); const AdvisoriesStatusBar = () => { - const [advisories, setAdvisories] = React.useState({}); - React.useEffect(() => { + const [advisories, setAdvisories] = useState({}); + useEffect(() => { fetchApplicableAdvisoriesApi({ limit: 4, sort: '-advisory_type_name,-applicable_systems', diff --git a/src/PresentationalComponents/StatusReports/SystemsStatusReport.js b/src/PresentationalComponents/StatusReports/SystemsStatusReport.js index 43ea8ce82..80879c7d7 100644 --- a/src/PresentationalComponents/StatusReports/SystemsStatusReport.js +++ b/src/PresentationalComponents/StatusReports/SystemsStatusReport.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; import { CheckCircleIcon, BundleIcon, ExclamationTriangleIcon } from '@patternfly/react-icons'; import propTypes from 'prop-types'; @@ -25,7 +25,7 @@ const StatusCard = ({ title, color, Icon, value, filter, apply }) => ( ); const SystemsStatusReport = ({ apply, queryParams }) => { - const [subtotals, setSubtotals] = React.useState({}); + const [subtotals, setSubtotals] = useState({}); const { selectedTags, selectedGlobalTags, systemProfile } = useSelector( ({ GlobalFilterStore }) => GlobalFilterStore, @@ -55,7 +55,7 @@ const SystemsStatusReport = ({ apply, queryParams }) => { return result; }; - React.useEffect(() => { + useEffect(() => { fetchResource(); }, [ queryParams?.filter?.os?.length, diff --git a/src/PresentationalComponents/TableView/TableView.js b/src/PresentationalComponents/TableView/TableView.js index f21d90bad..1d8911519 100644 --- a/src/PresentationalComponents/TableView/TableView.js +++ b/src/PresentationalComponents/TableView/TableView.js @@ -42,13 +42,21 @@ const TableView = ({ [metadata.limit, metadata.offset], ); - const [deleteFilters] = useRemoveFilter(filter, apply, defaultFilters); + const [deleteFilters, deleteFilterGroup] = useRemoveFilter(filter, apply, defaultFilters); const selectedCount = selectedRows && arrayFromObj(selectedRows).length; const { code, hasError, isLoading } = status; const bulkSelectConfig = useBulkSelectConfig(selectedCount, onSelect, metadata, rows, onCollapse); const activeFiltersConfig = useMemo( - () => buildActiveFilterConfig(filter, search, deleteFilters, searchChipLabel, defaultFilters), - [defaultFilters, deleteFilters, filter, search, searchChipLabel], + () => + buildActiveFilterConfig( + filter, + search, + deleteFilters, + deleteFilterGroup, + searchChipLabel, + defaultFilters, + ), + [defaultFilters, deleteFilters, deleteFilterGroup, filter, search, searchChipLabel], ); return ( diff --git a/src/SmartComponents/AdvisoryDetail/AdvisoryDetail.js b/src/SmartComponents/AdvisoryDetail/AdvisoryDetail.js index 09443d0c4..166e0e145 100644 --- a/src/SmartComponents/AdvisoryDetail/AdvisoryDetail.js +++ b/src/SmartComponents/AdvisoryDetail/AdvisoryDetail.js @@ -1,6 +1,6 @@ +import React, { useEffect } from 'react'; import { Stack, StackItem, Content, ContentVariants } from '@patternfly/react-core'; import { Main } from '@redhat-cloud-services/frontend-components/Main'; -import React, { useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { useParams } from 'react-router-dom'; import messages from '../../Messages'; @@ -29,11 +29,11 @@ const AdvisoryDetail = () => { const advisoryDetails = useSelector(({ AdvisoryDetailStore }) => AdvisoryDetailStore); const status = useSelector(({ AdvisoryDetailStore }) => AdvisoryDetailStore.status); - React.useEffect(() => { + useEffect(() => { dispatch(fetchAvisoryDetails({ advisoryName })); }, []); - React.useEffect( + useEffect( () => () => { dispatch(clearEntitiesStore()); dispatch(clearAdvisoryDetailStore()); @@ -43,7 +43,7 @@ const AdvisoryDetail = () => { const { attributes } = advisoryDetails.data; return ( - + <>
{ - + ); }; diff --git a/src/SmartComponents/AdvisorySystems/AdvisorySystems.js b/src/SmartComponents/AdvisorySystems/AdvisorySystems.js index 567c1e42f..ebd0be492 100644 --- a/src/SmartComponents/AdvisorySystems/AdvisorySystems.js +++ b/src/SmartComponents/AdvisorySystems/AdvisorySystems.js @@ -1,5 +1,5 @@ -import propTypes from 'prop-types'; import React, { useState, useEffect } from 'react'; +import propTypes from 'prop-types'; import { useDispatch, useSelector } from 'react-redux'; import ErrorHandler from '../../PresentationalComponents/Snippets/ErrorHandler'; import { @@ -47,7 +47,7 @@ const AdvisorySystems = ({ advisoryName }) => { } return ( - + <> {(isRemediationOpen && ( { decodedParams={decodedParams} apply={apply} /> - + ); }; diff --git a/src/SmartComponents/AdvisorySystems/AdvisorySystemsTable.js b/src/SmartComponents/AdvisorySystems/AdvisorySystemsTable.js index e07ae05e6..a9b620882 100644 --- a/src/SmartComponents/AdvisorySystems/AdvisorySystemsTable.js +++ b/src/SmartComponents/AdvisorySystems/AdvisorySystemsTable.js @@ -20,6 +20,7 @@ import { import { pageDefaultFilters, remediationIdentifiers } from '../../Utilities/constants'; import { arrayFromObj, + buildActiveFilterConfig, persistantParams, remediationProvider, removeUndefinedObjectKeys, @@ -35,7 +36,7 @@ import { import { intl } from '../../Utilities/IntlProvider'; import { ADVISORY_SYSTEMS_COLUMNS, systemsRowActions } from '../Systems/SystemsListAssets'; import AsyncRemediationButton from '../Remediation/AsyncRemediationButton'; -import { buildActiveFiltersConfig, mergeInventoryColumns } from '../../Utilities/SystemsHelpers'; +import { mergeInventoryColumns } from '../../Utilities/SystemsHelpers'; import advisoryStatusFilter from '../../PresentationalComponents/Filters/AdvisoryStatusFilter'; const AdvisorySystemsTable = ({ @@ -57,7 +58,7 @@ const AdvisorySystemsTable = ({ const { systemProfile, selectedTags, filter, search, page, perPage, sort } = queryParams; - const [deleteFilters] = useRemoveFilter( + const [deleteFilters, deleteFilterGroup] = useRemoveFilter( { search, ...filter }, apply, pageDefaultFilters.advisorySystems, @@ -75,10 +76,13 @@ const AdvisorySystemsTable = ({ ], }; - const activeFiltersConfig = buildActiveFiltersConfig( + const activeFiltersConfig = buildActiveFilterConfig( filter, search, deleteFilters, + deleteFilterGroup, + intl.formatMessage(messages.labelsFiltersSystemsSearchTitle), + deleteFilterGroup, pageDefaultFilters.advisorySystems, ); diff --git a/src/SmartComponents/AdvisorySystems/AdvisorySystemsTable.test.js b/src/SmartComponents/AdvisorySystems/AdvisorySystemsTable.test.js index d39b67cbd..e371f1613 100644 --- a/src/SmartComponents/AdvisorySystems/AdvisorySystemsTable.test.js +++ b/src/SmartComponents/AdvisorySystems/AdvisorySystemsTable.test.js @@ -192,6 +192,7 @@ describe('AdvisorySystemsTable.js', () => { deleteTitle: 'Clear filters', filters: [], onDelete: expect.any(Function), + onDeleteGroup: expect.any(Function), }, }), {}, @@ -228,6 +229,7 @@ describe('AdvisorySystemsTable.js', () => { }, ], onDelete: expect.any(Function), + onDeleteGroup: expect.any(Function), }, }), {}, diff --git a/src/SmartComponents/PackageDetail/PackageDetail.js b/src/SmartComponents/PackageDetail/PackageDetail.js index 69f910ce8..778125a14 100644 --- a/src/SmartComponents/PackageDetail/PackageDetail.js +++ b/src/SmartComponents/PackageDetail/PackageDetail.js @@ -1,6 +1,6 @@ +import React, { useEffect } from 'react'; import { Stack, StackItem, Content, ContentVariants } from '@patternfly/react-core'; import { Main } from '@redhat-cloud-services/frontend-components/Main'; -import React, { useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { useParams } from 'react-router-dom'; import messages from '../../Messages'; @@ -25,11 +25,11 @@ const PackageDetail = () => { const packageDetails = useSelector(({ PackageDetailStore }) => PackageDetailStore); const status = useSelector(({ PackageDetailStore }) => PackageDetailStore.status); - React.useEffect(() => { + useEffect(() => { dispatch(fetchPackageDetails({ packageName })); }, []); - React.useEffect( + useEffect( () => () => { dispatch(clearPackageDetailStore()); }, @@ -39,7 +39,7 @@ const PackageDetail = () => { const { attributes } = packageDetails.data; return ( - + <>
{ - + ); }; diff --git a/src/SmartComponents/PackageSystems/PackageSystems.js b/src/SmartComponents/PackageSystems/PackageSystems.js index c00aff7db..4b95f25ab 100644 --- a/src/SmartComponents/PackageSystems/PackageSystems.js +++ b/src/SmartComponents/PackageSystems/PackageSystems.js @@ -84,7 +84,7 @@ const PackageSystems = ({ packageName }) => { [], ); - const [deleteFilters] = useRemoveFilter( + const [deleteFilters, deleteFilterGroup] = useRemoveFilter( { ...filter, search }, apply, pageDefaultFilters.packageSystems, @@ -109,10 +109,11 @@ const PackageSystems = ({ packageName }) => { filter, search, deleteFilters, + deleteFilterGroup, intl.formatMessage(messages.labelsFiltersSystemsSearchTitle), pageDefaultFilters.packageSystems, ), - [deleteFilters, filter, search], + [deleteFilters, deleteFilterGroup, filter, search], ); const constructFilename = (system) => `${system.available_evra}`; @@ -179,65 +180,65 @@ const PackageSystems = ({ packageName }) => { systems, ); + if (status.hasError) { + return ; + } + return ( - <> - {(status.hasError && ) || ( - - mergeInventoryColumns(PACKAGE_SYSTEMS_COLUMNS, inventoryColumns) - } - showTags - getEntities={getEntites} - customFilters={{ - patchParams: { - search, - filter, - systemProfile, - selectedTags, - }, - }} - paginationProps={{ - isDisabled: totalItems === 0, - }} - onLoad={({ mergeWithEntities }) => { - store.replaceReducer( - combineReducers({ - ...defaultReducers, - ...mergeWithEntities( - inventoryEntitiesReducer(PACKAGE_SYSTEMS_COLUMNS, modifyPackageSystems), - persistantParams({ page, perPage, sort, search }, decodedParams), - ), - }), - ); - }} - actionsConfig={{ actions: [null] }} - tableProps={{ - canSelectAll: false, - variant: TableVariant.compact, - className: 'patchCompactInventory', - isStickyHeader: true, - }} - filterConfig={filterConfig} - activeFiltersConfig={activeFiltersConfig} - bulkSelect={onSelect && bulkSelectConfig} - exportConfig={{ - isDisabled: totalItems === 0, - onSelect: onExport, - }} - dedicatedAction={ - - } + + mergeInventoryColumns(PACKAGE_SYSTEMS_COLUMNS, inventoryColumns) + } + showTags + getEntities={getEntites} + customFilters={{ + patchParams: { + search, + filter, + systemProfile, + selectedTags, + }, + }} + paginationProps={{ + isDisabled: totalItems === 0, + }} + onLoad={({ mergeWithEntities }) => { + store.replaceReducer( + combineReducers({ + ...defaultReducers, + ...mergeWithEntities( + inventoryEntitiesReducer(PACKAGE_SYSTEMS_COLUMNS, modifyPackageSystems), + persistantParams({ page, perPage, sort, search }, decodedParams), + ), + }), + ); + }} + actionsConfig={{ actions: [null] }} + tableProps={{ + canSelectAll: false, + variant: TableVariant.compact, + className: 'patchCompactInventory', + isStickyHeader: true, + }} + filterConfig={filterConfig} + activeFiltersConfig={activeFiltersConfig} + bulkSelect={onSelect && bulkSelectConfig} + exportConfig={{ + isDisabled: totalItems === 0, + onSelect: onExport, + }} + dedicatedAction={ + - )} - + } + /> ); }; diff --git a/src/SmartComponents/PackageSystems/PackageSystems.test.js b/src/SmartComponents/PackageSystems/PackageSystems.test.js index c7228ded0..7e172c600 100644 --- a/src/SmartComponents/PackageSystems/PackageSystems.test.js +++ b/src/SmartComponents/PackageSystems/PackageSystems.test.js @@ -99,6 +99,7 @@ describe('PackageSystems.js', () => { deleteTitle: 'Clear filters', filters: [], onDelete: expect.any(Function), + onDeleteGroup: expect.any(Function), }, }), {}, @@ -133,6 +134,7 @@ describe('PackageSystems.js', () => { }, ], onDelete: expect.any(Function), + onDeleteGroup: expect.any(Function), }, }), {}, diff --git a/src/SmartComponents/Packages/Packages.js b/src/SmartComponents/Packages/Packages.js index 766aadea2..a401f7e2e 100644 --- a/src/SmartComponents/Packages/Packages.js +++ b/src/SmartComponents/Packages/Packages.js @@ -1,5 +1,5 @@ +import React, { useEffect, useLayoutEffect, useMemo, useState } from 'react'; import { Main } from '@redhat-cloud-services/frontend-components/Main'; -import React, { useEffect, useLayoutEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import messages from '../../Messages'; import packagesListStatusFilter from '../../PresentationalComponents/Filters/PackagesListStatusFilter'; @@ -19,7 +19,7 @@ import { useSearchParams } from 'react-router-dom'; const Packages = () => { const dispatch = useDispatch(); - const [firstMount, setFirstMount] = React.useState(true); + const [firstMount, setFirstMount] = useState(true); const chrome = useChrome(); useEffect(() => { chrome.updateDocumentTitle(`Packages - Content | RHEL`, true); @@ -27,7 +27,7 @@ const Packages = () => { const [searchParams, setSearchParams] = useSearchParams(); const packageRows = useSelector(({ PackagesListStore }) => PackagesListStore.rows); - const rows = React.useMemo(() => createPackagesRows(packageRows), [packageRows]); + const rows = useMemo(() => createPackagesRows(packageRows), [packageRows]); const status = useSelector(({ PackagesListStore }) => PackagesListStore.status); const metadata = useSelector(({ PackagesListStore }) => PackagesListStore.metadata); @@ -58,15 +58,12 @@ const Packages = () => { ); const onSort = useSortColumn(packagesColumns, apply); - const sortBy = React.useMemo( - () => createSortBy(packagesColumns, metadata.sort, 0), - [metadata.sort], - ); + const sortBy = useMemo(() => createSortBy(packagesColumns, metadata.sort, 0), [metadata.sort]); const onSetPage = useSetPage(metadata.limit, apply); const onPerPageSelect = usePerPageSelect(apply); return ( - + <>
{ searchChipLabel={intl.formatMessage(messages.labelsFiltersPackagesSearchTitle)} />
- + ); }; diff --git a/src/SmartComponents/SystemAdvisories/SystemAdvisories.js b/src/SmartComponents/SystemAdvisories/SystemAdvisories.js index d372951dd..7ae710e3e 100644 --- a/src/SmartComponents/SystemAdvisories/SystemAdvisories.js +++ b/src/SmartComponents/SystemAdvisories/SystemAdvisories.js @@ -1,5 +1,5 @@ -import propTypes from 'prop-types'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import propTypes from 'prop-types'; import { useDispatch, useSelector } from 'react-redux'; import { useSearchParams } from 'react-router-dom'; import publishDateFilter from '../../PresentationalComponents/Filters/PublishDateFilter'; diff --git a/src/SmartComponents/Systems/SystemTable.test.js b/src/SmartComponents/Systems/SystemTable.test.js index 5b96e2b0c..57cfea82f 100644 --- a/src/SmartComponents/Systems/SystemTable.test.js +++ b/src/SmartComponents/Systems/SystemTable.test.js @@ -266,6 +266,7 @@ describe('SystemsTable', () => { }, ], onDelete: expect.any(Function), + onDeleteGroup: expect.any(Function), showDeleteButton: false, }, }), @@ -319,6 +320,7 @@ describe('SystemsTable', () => { }, ], onDelete: expect.any(Function), + onDeleteGroup: expect.any(Function), showDeleteButton: true, }, }), diff --git a/src/SmartComponents/Systems/SystemsMainContent.js b/src/SmartComponents/Systems/SystemsMainContent.js index b9266df6a..fdf95fb71 100644 --- a/src/SmartComponents/Systems/SystemsMainContent.js +++ b/src/SmartComponents/Systems/SystemsMainContent.js @@ -52,7 +52,7 @@ const SystemsMainContent = () => { } return ( - + <> {(isRemediationOpen && ( { decodedParams={decodedParams} /> - + ); }; diff --git a/src/SmartComponents/Systems/SystemsPage.js b/src/SmartComponents/Systems/SystemsPage.js index 2cbbf07a1..cf3ff017d 100644 --- a/src/SmartComponents/Systems/SystemsPage.js +++ b/src/SmartComponents/Systems/SystemsPage.js @@ -13,10 +13,10 @@ const SystemsPage = () => { }, [chrome, intl]); return ( - + <>
- + ); }; diff --git a/src/SmartComponents/Systems/SystemsTable.js b/src/SmartComponents/Systems/SystemsTable.js index 525565e18..2601abe80 100644 --- a/src/SmartComponents/Systems/SystemsTable.js +++ b/src/SmartComponents/Systems/SystemsTable.js @@ -1,4 +1,4 @@ -import React, { useCallback, useMemo, useRef, useState } from 'react'; +import React, { useCallback, useRef, useState, useMemo } from 'react'; import { TableVariant } from '@patternfly/react-table'; import { InventoryTable } from '@redhat-cloud-services/frontend-components/Inventory'; import isDeepEqualReact from 'fast-deep-equal/react'; @@ -11,7 +11,12 @@ import { } from '../../store/Reducers/InventoryEntitiesReducer'; import { exportSystemsCSV, exportSystemsJSON, fetchSystems } from '../../Utilities/api/api'; import { pageDefaultFilters, NO_ADVISORIES_TEXT } from '../../Utilities/constants'; -import { arrayFromObj, hasActiveInventoryFilters, persistantParams } from '../../Utilities/Helpers'; +import { + arrayFromObj, + buildActiveFilterConfig, + hasActiveInventoryFilters, + persistantParams, +} from '../../Utilities/Helpers'; import { useBulkSelectConfig, useGetEntities, @@ -25,12 +30,13 @@ import { SYSTEMS_LIST_COLUMNS, systemsRowActions } from './SystemsListAssets'; import AsyncRemediationButton from '../Remediation/AsyncRemediationButton'; import { buildFilterConfig, - buildActiveFiltersConfig, mergeInventoryColumns, workloadToSystemProfile, } from '../../Utilities/SystemsHelpers'; import { combineReducers } from 'redux'; import propTypes from 'prop-types'; +import { intl } from '../../Utilities/IntlProvider'; +import messages from '../../Messages'; const buildInventorySnapshot = (filters = {}, selectedTags = [], systemProfile = {}) => ({ filters, @@ -108,7 +114,11 @@ const SystemsTable = ({ dispatch(changeTags(tags)); }; - const [deleteFilters] = useRemoveFilter(filter, apply, pageDefaultFilters.systems); + const [deleteFilters, deleteFilterGroup] = useRemoveFilter( + filter, + apply, + pageDefaultFilters.systems, + ); const filterConfig = buildFilterConfig(filter, apply); const applyInventorySnapshot = useCallback((nextSnapshot) => { setInventorySnapshot((previousSnapshot) => @@ -123,13 +133,20 @@ const SystemsTable = ({ ); const activeFiltersConfig = useMemo(() => { - const config = buildActiveFiltersConfig(filter, '', deleteFilters, pageDefaultFilters.systems); + const config = buildActiveFilterConfig( + filter, + '', + deleteFilters, + deleteFilterGroup, + intl.formatMessage(messages.labelsFiltersSystemsSearchTitle), + pageDefaultFilters.systems, + ); return { ...config, showDeleteButton: config.showDeleteButton || hasInventoryFilterDeviation, }; - }, [deleteFilters, filter, hasInventoryFilterDeviation]); + }, [deleteFilters, deleteFilterGroup, filter, hasInventoryFilterDeviation]); const onSelect = useOnSelect(systems, selectedRows, { endpoint: ID_API_ENDPOINTS.systems, diff --git a/src/Utilities/Helpers.js b/src/Utilities/Helpers.js index b3f7419e5..1c5b4b4b5 100644 --- a/src/Utilities/Helpers.js +++ b/src/Utilities/Helpers.js @@ -1,3 +1,4 @@ +import React from 'react'; import { Flex, FlexItem, Tooltip } from '@patternfly/react-core'; import { BugIcon, @@ -11,7 +12,6 @@ import flatten from 'lodash/flatten'; import findIndex from 'lodash/findIndex'; import pickBy from 'lodash/pickBy'; import qs from 'query-string'; -import React from 'react'; import LinesEllipsis from 'react-lines-ellipsis'; import messages from '../Messages'; import AdvisoriesIcon from '../PresentationalComponents/Snippets/AdvisoriesIcon'; @@ -111,10 +111,10 @@ export const getLimitFromPageSize = (limit) => limit; export function truncate(str, max, end) { return str.length > max ? ( - + <> {str.substring(0, max - 1)} ... {end} - + ) : ( str ); @@ -516,6 +516,7 @@ export const buildActiveFilterConfig = ( filters, search, deleteFilters, + deleteFilterGroup, searchChipLabel = 'Search', defaultFilters = emptyDefaultFilters, ) => { @@ -531,6 +532,7 @@ export const buildActiveFilterConfig = ( return { filters: buildFilterChips(visibleFilters, visibleSearch, searchChipLabel), onDelete: deleteFilters, + onDeleteGroup: deleteFilterGroup, deleteTitle: intl.formatMessage( hasDefaultFilterState ? messages.labelsFiltersReset : messages.labelsFiltersClear, ), diff --git a/src/Utilities/Helpers.test.js b/src/Utilities/Helpers.test.js index 98ff0ccfa..f5e3448a1 100644 --- a/src/Utilities/Helpers.test.js +++ b/src/Utilities/Helpers.test.js @@ -323,6 +323,7 @@ describe('Helpers tests', () => { { systems_applicable: ['gt:0'] }, '', jest.fn(), + jest.fn(), 'Name', pageDefaultFilters.packages, ), @@ -337,6 +338,7 @@ describe('Helpers tests', () => { ], onDelete: expect.any(Function), showDeleteButton: false, + onDeleteGroup: expect.any(Function), }); }); @@ -346,6 +348,7 @@ describe('Helpers tests', () => { { systems_applicable: ['eq:0'] }, '', jest.fn(), + jest.fn(), 'Name', pageDefaultFilters.packages, ), @@ -360,15 +363,17 @@ describe('Helpers tests', () => { ], onDelete: expect.any(Function), showDeleteButton: true, + onDeleteGroup: expect.any(Function), }); }); - + it('buildActiveFilterConfig: should show clear filters when a page has no defaults', () => { expect( buildActiveFilterConfig( { advisory_type_name: 'bugfix' }, '', jest.fn(), + jest.fn(), 'Name', pageDefaultFilters.advisories, ), @@ -382,6 +387,7 @@ describe('Helpers tests', () => { }, ], onDelete: expect.any(Function), + onDeleteGroup: expect.any(Function), }); }); diff --git a/src/Utilities/SystemsHelpers.js b/src/Utilities/SystemsHelpers.js index bc0e35ba4..a6cc0c584 100644 --- a/src/Utilities/SystemsHelpers.js +++ b/src/Utilities/SystemsHelpers.js @@ -1,9 +1,6 @@ import staleFilter from '../PresentationalComponents/Filters/SystemStaleFilter'; import systemsUpdatableFilter from '../PresentationalComponents/Filters/SystemsUpdatableFilter'; import systemsWorkloadFilter from '../PresentationalComponents/Filters/WorkloadFilter'; -import { buildActiveFilterConfig } from './Helpers'; -import { intl } from './IntlProvider'; -import messages from '../Messages'; import { defaultCompoundSortValues } from './constants'; export const workloadToSystemProfile = (workloads = []) => ({ @@ -26,15 +23,6 @@ export const buildFilterConfig = (filter, apply) => ({ ], }); -export const buildActiveFiltersConfig = (filter, search, deleteFilters, defaultFilters) => - buildActiveFilterConfig( - filter, - search, - deleteFilters, - intl.formatMessage(messages.labelsFiltersSystemsSearchTitle), - defaultFilters, - ); - export const mergeInventoryColumns = (patchmanColumns, inventoryColumns) => patchmanColumns.map((column) => ({ ...inventoryColumns.find( diff --git a/src/Utilities/hooks/Hooks.js b/src/Utilities/hooks/Hooks.js index 17644e9e2..0064f507b 100644 --- a/src/Utilities/hooks/Hooks.js +++ b/src/Utilities/hooks/Hooks.js @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useRef, useMemo, useState, Fragment } from 'react'; +import React, { useCallback, useEffect, useRef, Fragment, useState, useMemo } from 'react'; import { SortByDirection } from '@patternfly/react-table'; import { useAddNotification } from '@redhat-cloud-services/frontend-components-notifications'; import { downloadFile } from '@redhat-cloud-services/frontend-components-utilities/helpers'; @@ -75,47 +75,50 @@ export const useRemoveFilter = (filters, callback, defaultFilters = { filter: {} const { filter: defaultFilterState, search: defaultSearch } = getDefaultFilterState(defaultFilters); - const removeFilter = useCallback((selected, resetFilters, shouldReset) => { - let newParams = { filter: {} }; - selected.forEach((selectedItem) => { - let { id: categoryId, chips } = selectedItem; - - if (categoryId !== 'search' && !multiValueFilters.includes(categoryId)) { - let activeFilter = filters[categoryId]; - const toRemove = chips.map((item) => item.id?.toString()); - if (Array.isArray(activeFilter)) { - const nextValue = activeFilter.filter((item) => !toRemove.includes(item.toString())); - newParams.filter[categoryId] = - nextValue.length > 0 ? nextValue : defaultFilterState[categoryId]; + const removeFilter = useCallback( + (selected, resetFilters, shouldReset) => { + let newParams = { filter: {} }; + selected.forEach((selectedItem) => { + let { id: categoryId, chips } = selectedItem; + + if (categoryId !== 'search' && !multiValueFilters.includes(categoryId)) { + let activeFilter = filters[categoryId]; + const toRemove = chips.map((item) => item.id?.toString()); + if (Array.isArray(activeFilter)) { + const nextValue = activeFilter.filter((item) => !toRemove.includes(item.toString())); + newParams.filter[categoryId] = + nextValue.length > 0 ? nextValue : defaultFilterState[categoryId]; + } else { + newParams.filter[categoryId] = defaultFilterState[categoryId]; + } + } else if (multiValueFilters.includes(categoryId)) { + const filterValues = + (filters[categoryId] && + ((typeof filters[categoryId] === 'string' && filters[categoryId].split(',')) || + filters[categoryId])) || + []; + + const nextValue = + (filterValues.length !== 1 && + filterValues + .filter((filterValue) => !chips.find((chip) => chip.value === filterValue)) + .join(',')) || + undefined; + + newParams.filter[categoryId] = nextValue ?? defaultFilterState[categoryId]; } else { - newParams.filter[categoryId] = defaultFilterState[categoryId]; + newParams.search = defaultSearch; } - } else if (multiValueFilters.includes(categoryId)) { - const filterValues = - (filters[categoryId] && - ((typeof filters[categoryId] === 'string' && filters[categoryId].split(',')) || - filters[categoryId])) || - []; - - const nextValue = - (filterValues.length !== 1 && - filterValues - .filter((filterValue) => !chips.find((chip) => chip.value === filterValue)) - .join(',')) || - undefined; - - newParams.filter[categoryId] = nextValue ?? defaultFilterState[categoryId]; - } else { - newParams.search = defaultSearch; - } - }); + }); - if (shouldReset) { - newParams = resetFilters(newParams); - } + if (shouldReset) { + newParams = resetFilters(newParams); + } - callback({ ...newParams }); - }); + callback({ ...newParams }); + }, + [filters, defaultFilterState, defaultSearch, callback], + ); const deleteFilterGroup = (__, filters) => { removeFilter(filters);