diff --git a/src/Components/SmartComponents/CVEDetailsPage/CVEDetailsPage.js b/src/Components/SmartComponents/CVEDetailsPage/CVEDetailsPage.js index 0888541fe..5c82b7938 100644 --- a/src/Components/SmartComponents/CVEDetailsPage/CVEDetailsPage.js +++ b/src/Components/SmartComponents/CVEDetailsPage/CVEDetailsPage.js @@ -53,10 +53,13 @@ const CVEDetailsPage = () => { dispatch(fetchCveDetails(cveName)); }, [dispatch, cveName]); - const refreshInventory = () => ( - dispatch(clearInventoryStore()), - inventoryRef.current.onRefreshData(({ page: 1 })) - ); + const refreshInventory = () => { + dispatch(clearInventoryStore()); + + // timestamp is used to force inventory to refresh + // if it wasn't there inventory might ignore request to refresh because parameters are the same + inventoryRef.current.onRefreshData(({ timestamp: Date.now(), page: 1 })); + }; const showBusinessRiskModal = cves => { setBusinessRisk( diff --git a/src/Components/SmartComponents/SystemsExposedTable/SystemsExposedTable.js b/src/Components/SmartComponents/SystemsExposedTable/SystemsExposedTable.js index 02a28a175..53352dad1 100644 --- a/src/Components/SmartComponents/SystemsExposedTable/SystemsExposedTable.js +++ b/src/Components/SmartComponents/SystemsExposedTable/SystemsExposedTable.js @@ -17,9 +17,16 @@ import { changeExposedSystemsParameters, clearInventoryStore, selectRows, - changeColumnsCveDetail + changeColumnsCveDetail, + fetchAffectedSystemIdsByCve } from '../../../Store/Actions/Actions'; -import { SYSTEMS_EXPOSED_HEADER, SYSTEMS_EXPOSED_ALLOWED_PARAMS, PERMISSIONS } from '../../../Helpers/constants'; +import { + SYSTEMS_EXPOSED_HEADER, + SYSTEMS_EXPOSED_ALLOWED_PARAMS, + PERMISSIONS, + ANSIBLE_REMEDIATION, + RULE_PRESENCE_OPTIONS +} from '../../../Helpers/constants'; import { TableVariant } from '@patternfly/react-table'; import { InventoryTable } from '@redhat-cloud-services/frontend-components/Inventory'; import ErrorHandler from '../../PresentationalComponents/ErrorHandler/ErrorHandler'; @@ -28,18 +35,22 @@ import { clearNotifications } from '@redhat-cloud-services/frontend-components-notifications/redux'; import { EmptyStateNoSystems } from '../../PresentationalComponents/EmptyStates/EmptyStates'; -import { SystemsExposedTableToolbar } from './SystemsExposedTableToolbar'; -import { useColumnManagement, useGetEntities, useRbac } from '../../../Helpers/Hooks'; +import { useBulkSelect, useColumnManagement, useGetEntities, useRbac } from '../../../Helpers/Hooks'; import * as APIHelper from '../../../Helpers/APIHelper'; import Spinner from '@redhat-cloud-services/frontend-components/Spinner'; +import { buildActiveFilters, exportConfig, removeFilters } from '../../../Helpers/TableToolbarHelper'; +import Remediation from '../Remediation/Remediation'; +import securityRuleFilter from '../../PresentationalComponents/Filters/PrimaryToolbarFilters/SecurityRuleFilter'; +import statusFilter from '../../PresentationalComponents/Filters/PrimaryToolbarFilters/StatusFilter'; +import useSearchFilter from '../../PresentationalComponents/Filters/PrimaryToolbarFilters/SearchFilter'; +import useOsVersionFilter from '../../PresentationalComponents/Filters/PrimaryToolbarFilters/OsVersionFilter'; +import remediationFilter from '../../PresentationalComponents/Filters/PrimaryToolbarFilters/RemediationFilter'; const SystemsExposedTable = ({ intl, cveName, cveStatusDetails, filterRuleValues, hasSecurityRule, canEditPairStatus, canExport, inventoryRef, refreshInventory }) => { - const [[ - canReadHostsInventory], - isLoadingInventory] = useRbac([ + const [[canReadHostsInventory], isLoadingInventory] = useRbac([ PERMISSIONS.readHosts ], 'inventory'); @@ -130,7 +141,66 @@ const SystemsExposedTable = ({ .map(column => ({ ...inventoryColumns.find(({ key }) => column.key === key), ...column })); }; - // TODO: let InventoryTable render its own toolbar instead of using custom one + const rawData = { data: items, meta: { totalItems }, isLoaded }; + + const remediableSystems = selectedRows.filter(system => system.remediation === ANSIBLE_REMEDIATION); + + const kebabOptions = [ + ...isLoaded ? [ + + ] : [], + ...canEditPairStatus ? [{ + label: intl.formatMessage(messages.editStatus), + onClick: () => showStatusModal([cveStatusDetails], selectedRows), + props: { isDisabled: !selectedRowsCount } + }] : [], + { + label: intl.formatMessage(messages.columnManagementModalTitle), + onClick: () => setColumnManagementModalOpen(true) + } + ]; + + const searchFilter = useSearchFilter( + 'filter', + messages.systemsSearchName, + messages.searchFilterByName, + parameters.filter, + apply + ); + + const advisoryFilter = useSearchFilter( + 'advisory', + messages.advisoryName, + messages.searchFilterByAdvisoryName, + parameters.advisory, + apply + ); + + const osVersionFilter = useOsVersionFilter( + parameters.rhel_version, + apply + ); + + const bulkSelectProps = useBulkSelect({ + rawData, + selectedRows, + selectedRowsCount, + handleSelect, + fetchResource: ops => fetchAffectedSystemIdsByCve({ id: cveName, ...parameters, ...ops }) + }); + + if (isAllExpanded) { + if (rawData.data.filter(row => row.isOpen === undefined).length > 0) { + expandAll(rawData, false); + } + } + return ( @@ -141,9 +211,12 @@ const SystemsExposedTable = ({ - {isLoadingInventory ? : + {StatusModal && } + {ColumnManagementModal} + + {isLoadingInventory ? : error?.hasError && !canReadHostsInventory - ? + ? : { @@ -191,34 +264,47 @@ const SystemsExposedTable = ({ showActions={totalItems !== 0} onExpandClick={(_e, _i, isOpen, { id }) => dispatch(expandRow(id, isOpen))} hideFilters={{ all: true }} - noSystemsTable={} - > - - {StatusModal && } - - { ColumnManagementModal } - - } + noSystemsTable={} + expandAll={hasSecurityRule ? { + isAllExpanded, + onClick: () => expandAll(rawData, isAllExpanded), + isDisabled: totalItems === 0 + } : undefined} + actionsConfig={{ + actions: kebabOptions, + dropdownProps: { ouiaId: 'toolbar-actions' } + }} + exportConfig={canExport && { + isDisabled: totalItems === 0, + ouiaId: 'export', + ...exportConfig({ downloadReport }) + }} + activeFiltersConfig={{ + filters: buildActiveFilters({ ...parameters }, filterRuleValues), + onDelete: (_, chips) => removeFilters(chips, apply), + deleteTitle: intl.formatMessage(messages.resetFilters) + }} + bulkSelect={bulkSelectProps} + filterConfig={{ + items: [ + searchFilter, + securityRuleFilter( + apply, + parameters, + filterRuleValues, + { + isDynamic: true, + dropdownItems: RULE_PRESENCE_OPTIONS.filter(item => item.value !== 'true') + } + ), + statusFilter(apply, parameters), + advisoryFilter, + ...osVersionFilter, + remediationFilter(apply, parameters) + ] + }} + /> + } ); diff --git a/src/Components/SmartComponents/SystemsExposedTable/SystemsExposedTableToolbar.js b/src/Components/SmartComponents/SystemsExposedTable/SystemsExposedTableToolbar.js deleted file mode 100644 index 948527af1..000000000 --- a/src/Components/SmartComponents/SystemsExposedTable/SystemsExposedTableToolbar.js +++ /dev/null @@ -1,166 +0,0 @@ -import React, { Fragment } from 'react'; -import propTypes from 'prop-types'; -import { useIntl } from 'react-intl'; -import { PrimaryToolbar } from '@redhat-cloud-services/frontend-components/PrimaryToolbar'; -import Remediation from '../Remediation/Remediation'; -import messages from '../../../Messages'; -import securityRuleFilter from '../../PresentationalComponents/Filters/PrimaryToolbarFilters/SecurityRuleFilter'; -import statusFilter from '../../PresentationalComponents/Filters/PrimaryToolbarFilters/StatusFilter'; -import useOsVersionFilter from '../../PresentationalComponents/Filters/PrimaryToolbarFilters/OsVersionFilter'; -import { exportConfig, buildActiveFilters, removeFilters } from '../../../Helpers/TableToolbarHelper'; -import { dataShape } from '../../../Helpers/MiscHelper'; -import { useBulkSelect } from '../../../Helpers/Hooks'; -import useSearchFilter from '../../PresentationalComponents/Filters/PrimaryToolbarFilters/SearchFilter'; -import { - fetchAffectedSystemIdsByCve -} from '../../../Store/Actions/Actions'; -import { - ANSIBLE_REMEDIATION, RULE_PRESENCE_OPTIONS -} from '../../../Helpers/constants'; -import remediationFilter from '../../PresentationalComponents/Filters/PrimaryToolbarFilters/RemediationFilter'; - -export const SystemsExposedTableToolbar = ({ - rawData, - cveName, - cveStatusDetails, - selectedRows, - selectedRowsCount, - filterRuleValues, - expandAll, - hasSecurityRule, - isAllExpanded, - parameters, - methods, - canEditPairStatus, - canExport, - children -}) => { - const intl = useIntl(); - const { apply, showStatusModal, handleSelect, downloadReport, setColumnManagementModalOpen } = methods; - const { isLoaded, meta } = rawData; - - const remediableSystems = selectedRows.filter(system => system.remediation === ANSIBLE_REMEDIATION); - - const kebabOptions = ['', - ...canEditPairStatus ? [{ - label: intl.formatMessage(messages.editStatus), - onClick: () => showStatusModal([cveStatusDetails], selectedRows), - props: { isDisabled: !selectedRowsCount } - }] : [], - { - label: intl.formatMessage(messages.columnManagementModalTitle), - onClick: () => setColumnManagementModalOpen(true) - } - ]; - - const searchFilter = useSearchFilter( - 'filter', - messages.systemsSearchName, - messages.searchFilterByName, - parameters.filter, - apply - ); - - const advisoryFilter = useSearchFilter( - 'advisory', - messages.advisoryName, - messages.searchFilterByAdvisoryName, - parameters.advisory, - apply - ); - - const osVersionFilter = useOsVersionFilter( - parameters.rhel_version, - apply - ); - - const bulkSelectProps = useBulkSelect({ - rawData, - selectedRows, - selectedRowsCount, - handleSelect, - fetchResource: ops => fetchAffectedSystemIdsByCve({ id: cveName, ...parameters, ...ops }) - }); - - if (isAllExpanded) { - if (rawData.data.filter(row => row.isOpen === undefined).length > 0) { - expandAll(rawData, false); - } - } - - return - - )} - actionsConfig={{ - actions: kebabOptions, - dropdownProps: { ouiaId: 'toolbar-actions' } - }} - activeFiltersConfig={{ - filters: buildActiveFilters({ ...parameters }, filterRuleValues), - onDelete: (_, chips) => removeFilters(chips, apply), - deleteTitle: intl.formatMessage(messages.resetFilters) - }} - bulkSelect={bulkSelectProps} - filterConfig={{ - items: [ - searchFilter, - securityRuleFilter( - apply, - parameters, - filterRuleValues, - { - isDynamic: true, - dropdownItems: RULE_PRESENCE_OPTIONS.filter(item => item.value !== 'true') - } - ), - statusFilter(apply, parameters), - advisoryFilter, - ...osVersionFilter, - remediationFilter(apply, parameters) - ] - }} - expandAll={hasSecurityRule ? { - isAllExpanded, - onClick: () => expandAll(rawData, isAllExpanded), - isDisabled: meta.totalItems === 0 - } : undefined} - /> - {children} - ; -}; - -SystemsExposedTableToolbar.propTypes = { - rawData: dataShape, - parameters: propTypes.object, - cveName: propTypes.string, - cveStatusDetails: propTypes.object, - filterRuleValues: propTypes.array, - expandAll: propTypes.func, - hasSecurityRule: propTypes.bool, - isAllExpanded: propTypes.bool, - selectedRows: propTypes.array, - selectedRowsCount: propTypes.number, - children: propTypes.node, - methods: propTypes.shape({ - apply: propTypes.func, - handleSelect: propTypes.func, - showStatusModal: propTypes.func, - downloadReport: propTypes.func, - setColumnManagementModalOpen: propTypes.func - }), - canEditPairStatus: propTypes.bool, - canExport: propTypes.bool -}; diff --git a/src/Components/SmartComponents/SystemsExposedTable/SystemsExposedTableToolbar.test.js b/src/Components/SmartComponents/SystemsExposedTable/SystemsExposedTableToolbar.test.js deleted file mode 100644 index 739ba27ea..000000000 --- a/src/Components/SmartComponents/SystemsExposedTable/SystemsExposedTableToolbar.test.js +++ /dev/null @@ -1,68 +0,0 @@ -import { Provider } from "react-redux"; -import { BrowserRouter as Router } from "react-router-dom"; -import configureStore from "redux-mock-store"; - -import { mountWithIntl } from "../../../Helpers/MiscHelper"; -import { SystemsExposedTableToolbar } from "./SystemsExposedTableToolbar"; -import useOsVersionFilter from "../../PresentationalComponents/Filters/PrimaryToolbarFilters/OsVersionFilter"; -import toJson from "enzyme-to-json"; -import { systems } from "../../../Helpers/fixtures"; - -const mockStore = configureStore([(store) => (next) => (action) => {}]); - -let store = mockStore({}); - -const applyMock = jest.fn(); -const handleSelectMock = jest.fn(); -const doOptOutMock = jest.fn(); - -jest.mock( - "../../PresentationalComponents/Filters/PrimaryToolbarFilters/OsVersionFilter" -); - -let wrapper; - -describe("SystemsExpostedTableToolbar component", () => { - beforeEach(() => { - wrapper = mountWithIntl( - - - - - - ); - }); - - it("Renders toolbar", () => { - expect( - wrapper.find({ - ouiaId: "PrimaryToolbar", - }) - ).toHaveLength(1); - }); - - it("Toolbar matches snapshot", () => { - expect(toJson(wrapper.find('Toolbar'))).toMatchSnapshot(); - }); - - it("The default filter is set by name", () => { - const container = wrapper.find('button[data-ouia-component-id="ConditionalFilter"]'); - expect(container).toHaveLength(1); - expect(container.text()).toBe('Name'); - }) -}); diff --git a/src/Components/SmartComponents/SystemsExposedTable/__snapshots__/SystemsExposedTableToolbar.test.js.snap b/src/Components/SmartComponents/SystemsExposedTable/__snapshots__/SystemsExposedTableToolbar.test.js.snap deleted file mode 100644 index 27cb6105e..000000000 --- a/src/Components/SmartComponents/SystemsExposedTable/__snapshots__/SystemsExposedTableToolbar.test.js.snap +++ /dev/null @@ -1,1974 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`SystemsExpostedTableToolbar component Toolbar matches snapshot 1`] = ` - -
- -
-
- - -
- -
- - - Select none (0 items) - , - - Select page (6 items) - , - - Select all (10 items) - , - ] - } - isOpen={false} - onSelect={[Function]} - ouiaId="bulk-select" - ouiaSafe={true} - toggle={ - - - - - , - ] - } - /> - } - > - - Select none (0 items) - , - - Select page (6 items) - , - - Select all (10 items) - , - ] - } - isFlipEnabled={true} - isGrouped={false} - isOpen={false} - isPlain={false} - isText={false} - menuAppendTo="inline" - onSelect={[Function]} - position="left" - removeFindDomNode={false} - toggle={ - - - - - , - ] - } - /> - } - zIndex={9999} - > -
- -
- - -
-
, - } - } - splitButtonItems={ - [ - - - - - , - ] - } - > -
- - - - -
- - -
-
, - } - } - toggleVariant="default" - > - - -
- -
- - - -
- - -
- - -
- -
- - Name - , - - Security rules - , - - Status - , - - Advisory name - , - - Operating System - , - - Remediation - , - ] - } - isOpen={false} - onSelect={[Function]} - ouiaId="ConditionalFilter" - toggle={ - - - - Name - - - } - > - - Name - , - - Security rules - , - - Status - , - - Advisory name - , - - Operating System - , - - Remediation - , - ] - } - isFlipEnabled={true} - isGrouped={false} - isOpen={false} - isPlain={false} - isText={false} - menuAppendTo="inline" - onSelect={[Function]} - position="left" - removeFindDomNode={false} - toggle={ - - - - Name - - - } - zIndex={9999} - > -
- - -
, - } - } - > - - -
, - } - } - toggleVariant="default" - > - - - -
- - -
- - -
- - - - - - - - - - - - -
-
-
- - -
- - - - - - -
- - - Export to CSV - , - - Export to JSON - , - ] - } - isOpen={false} - isPlain={true} - onSelect={[Function]} - ouiaId="Export" - toggle={ - - - - } - > - - Export to CSV - , - - Export to JSON - , - ] - } - isFlipEnabled={true} - isGrouped={false} - isOpen={false} - isPlain={true} - isText={false} - menuAppendTo="inline" - onSelect={[Function]} - position="left" - removeFindDomNode={false} - toggle={ - - - - } - zIndex={9999} - > -
- - -
, - } - } - toggleIndicator={null} - > - - -
, - } - } - toggleVariant="default" - > - - - - - - - - -
- -
- - Manage columns - , - ] - } - isOpen={false} - isPlain={true} - onSelect={[Function]} - ouiaId="Actions" - toggle={ - - } - > - - Manage columns - , - ] - } - isFlipEnabled={true} - isGrouped={false} - isOpen={false} - isPlain={true} - isText={false} - menuAppendTo="inline" - onSelect={[Function]} - position="left" - removeFindDomNode={false} - toggle={ - - } - zIndex={9999} - > -
- - -
, - } - } - > - - -
, - } - } - > - - - - - - - -
-
- - -
-
, - } - } - id="ins-primary-data-toolbar-expandable-content-6" - isExpanded={false} - showClearFiltersButton={false} - > -
- - -
- - -
- -
- - -
-
- -
- - - - -
-
- - Systems with Vulnerability analysis enabled - -
    -
  • - - -
    - - Enabled systems - - - - -
    -
    -
    -
  • -
-
-
-
-
- - - -
-
-
-
-
- -
-
, - } - } - id="ins-primary-data-toolbar-expandable-content-7" - isExpanded={false} - showClearFiltersButton={false} - > -
- - -
- - -
- -
- -
- - - - -
- - - Export to CSV - , - - Export to JSON - , - ] - } - isOpen={false} - isPlain={true} - onSelect={[Function]} - ouiaId="Export" - toggle={ - - - - } - > - - Export to CSV - , - - Export to JSON - , - ] - } - isFlipEnabled={true} - isGrouped={false} - isOpen={false} - isPlain={true} - isText={false} - menuAppendTo="inline" - onSelect={[Function]} - position="left" - removeFindDomNode={false} - toggle={ - - - - } - zIndex={9999} - > -
- - -
, - } - } - toggleIndicator={null} - > - - -
, - } - } - toggleVariant="default" - > - - - - - - - - -
- -
- - Disable Vulnerability analysis on systems - , - - Enable Vulnerability analysis on systems - , - - Manage columns - , - ] - } - isOpen={false} - isPlain={true} - onSelect={[Function]} - ouiaId="Actions" - toggle={ - - } - > - - Disable Vulnerability analysis on systems - , - - Enable Vulnerability analysis on systems - , - - Manage columns - , - ] - } - isFlipEnabled={true} - isGrouped={false} - isOpen={false} - isPlain={true} - isText={false} - menuAppendTo="inline" - onSelect={[Function]} - position="left" - removeFindDomNode={false} - toggle={ - - } - zIndex={9999} - > -
- - -
, - } - } - > - - -
, - } - } - > - - - - - - - -
-
- - -
-
, - } - } - id="ins-primary-data-toolbar-expandable-content-6" - isExpanded={false} - showClearFiltersButton={false} - > -
- - -
- - -
- -
-
- -
-
- -
- - - - -
-
- - Systems with Vulnerability analysis enabled - -
    -
  • - - -
    - - Enabled systems - - - - -
    -
    -
    -
  • -
-
-
-
-
-
-
-
-
-
- -
-
, - } - } - id="ins-primary-data-toolbar-expandable-content-7" - isExpanded={false} - showClearFiltersButton={false} - > -
- - -
- - -
- -
- -