mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Cases] Activate Status & Severity Filters on Load (#172514)
Closes https://github.com/elastic/kibana/issues/172458 --------- Co-authored-by: Antonio <antoniodcoelho@gmail.com>
This commit is contained in:
parent
b0219f99d4
commit
a5e0b66771
4 changed files with 101 additions and 15 deletions
|
@ -11,6 +11,7 @@ import { createAppMockRenderer } from '../../../common/mock';
|
|||
import type { FilterConfig, FilterConfigRenderParams } from './types';
|
||||
import { getCaseConfigure } from '../../../containers/configure/api';
|
||||
import { useFilterConfig } from './use_filter_config';
|
||||
import type { FilterOptions } from '../../../../common/ui';
|
||||
|
||||
jest.mock('../../../containers/configure/api', () => {
|
||||
const originalModule = jest.requireActual('../../../containers/configure/api');
|
||||
|
@ -20,6 +21,18 @@ jest.mock('../../../containers/configure/api', () => {
|
|||
};
|
||||
});
|
||||
|
||||
const emptyFilterOptions: FilterOptions = {
|
||||
search: '',
|
||||
searchFields: [],
|
||||
severity: [],
|
||||
status: [],
|
||||
tags: [],
|
||||
assignees: null,
|
||||
reporters: [],
|
||||
owner: [],
|
||||
category: [],
|
||||
customFields: {},
|
||||
};
|
||||
const getCaseConfigureMock = getCaseConfigure as jest.Mock;
|
||||
|
||||
describe('useFilterConfig', () => {
|
||||
|
@ -67,11 +80,17 @@ describe('useFilterConfig', () => {
|
|||
systemFilterConfig: filters,
|
||||
onFilterOptionsChange,
|
||||
isSelectorView: false,
|
||||
filterOptions: emptyFilterOptions,
|
||||
},
|
||||
});
|
||||
|
||||
expect(onFilterOptionsChange).not.toHaveBeenCalled();
|
||||
rerender({ systemFilterConfig: [], onFilterOptionsChange, isSelectorView: false });
|
||||
rerender({
|
||||
systemFilterConfig: [],
|
||||
onFilterOptionsChange,
|
||||
isSelectorView: false,
|
||||
filterOptions: emptyFilterOptions,
|
||||
});
|
||||
expect(getEmptyOptions).toHaveBeenCalledTimes(1);
|
||||
expect(onFilterOptionsChange).toHaveBeenCalledTimes(1);
|
||||
expect(onFilterOptionsChange).toHaveBeenCalledWith({
|
||||
|
|
|
@ -5,9 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { SetStateAction } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import useLocalStorage from 'react-use/lib/useLocalStorage';
|
||||
import { merge } from 'lodash';
|
||||
import { merge, isEqual, isEmpty } from 'lodash';
|
||||
import type { FilterOptions } from '../../../../common/ui';
|
||||
import { LOCAL_STORAGE_KEYS } from '../../../../common/constants';
|
||||
import type { FilterConfig, FilterConfigState } from './types';
|
||||
|
@ -30,16 +31,61 @@ const mergeSystemAndCustomFieldConfigs = ({
|
|||
return newFilterConfig;
|
||||
};
|
||||
|
||||
const shouldBeActive = ({
|
||||
filter,
|
||||
filterOptions,
|
||||
}: {
|
||||
filter: FilterConfigState;
|
||||
filterOptions: FilterOptions;
|
||||
}) => {
|
||||
return !filter.isActive && !isEmpty(filterOptions[filter.key as keyof FilterOptions]);
|
||||
};
|
||||
|
||||
const useActiveByFilterKeyState = ({ filterOptions }: { filterOptions: FilterOptions }) => {
|
||||
const { appId } = useCasesContext();
|
||||
const [activeByFilterKey, setActiveByFilterKey] = useLocalStorage<FilterConfigState[]>(
|
||||
`${appId}.${LOCAL_STORAGE_KEYS.casesTableFiltersConfig}`,
|
||||
[]
|
||||
);
|
||||
|
||||
/**
|
||||
* Activates filters that aren't active but have a value in the filterOptions
|
||||
*/
|
||||
const newActiveByFilterKey = [...(activeByFilterKey || [])];
|
||||
newActiveByFilterKey.forEach((filter) => {
|
||||
if (shouldBeActive({ filter, filterOptions })) {
|
||||
const currentIndex = newActiveByFilterKey.findIndex((_filter) => filter.key === _filter.key);
|
||||
newActiveByFilterKey.splice(currentIndex, 1);
|
||||
newActiveByFilterKey.push({ key: filter.key, isActive: true });
|
||||
}
|
||||
});
|
||||
|
||||
if (!isEqual(newActiveByFilterKey, activeByFilterKey)) {
|
||||
setActiveByFilterKey(newActiveByFilterKey);
|
||||
}
|
||||
|
||||
return [newActiveByFilterKey, setActiveByFilterKey] as [
|
||||
FilterConfigState[],
|
||||
(value: SetStateAction<FilterConfigState[]>) => void
|
||||
];
|
||||
};
|
||||
|
||||
export const useFilterConfig = ({
|
||||
isSelectorView,
|
||||
onFilterOptionsChange,
|
||||
systemFilterConfig,
|
||||
filterOptions,
|
||||
}: {
|
||||
isSelectorView: boolean;
|
||||
onFilterOptionsChange: (params: Partial<FilterOptions>) => void;
|
||||
systemFilterConfig: FilterConfig[];
|
||||
filterOptions: FilterOptions;
|
||||
}) => {
|
||||
const { appId } = useCasesContext();
|
||||
/**
|
||||
* Initially we won't save any order, it will use the default config as it is defined in the system.
|
||||
* Once the user adds/removes a filter, we start saving the order and the visible state.
|
||||
*/
|
||||
const [activeByFilterKey, setActiveByFilterKey] = useActiveByFilterKeyState({ filterOptions });
|
||||
const { customFieldsFilterConfig } = useCustomFieldsFilterConfig({
|
||||
isSelectorView,
|
||||
onFilterOptionsChange,
|
||||
|
@ -47,14 +93,6 @@ export const useFilterConfig = ({
|
|||
const [filterConfigs, setFilterConfigs] = useState<Map<string, FilterConfig>>(
|
||||
() => new Map([...systemFilterConfig].map((filter) => [filter.key, filter]))
|
||||
);
|
||||
/**
|
||||
* Initially we won't save any order, it will use the default config as it is defined in the system.
|
||||
* Once the user adds/removes a filter, we start saving the order and the visible state.
|
||||
*/
|
||||
const [activeByFilterKey, setActiveByFilterKey] = useLocalStorage<FilterConfigState[]>(
|
||||
`${appId}.${LOCAL_STORAGE_KEYS.casesTableFiltersConfig}`,
|
||||
[]
|
||||
);
|
||||
|
||||
/**
|
||||
* This effect is needed in case a filter (mostly custom field) is removed from the settings
|
||||
|
|
|
@ -12,7 +12,7 @@ import { waitForEuiPopoverOpen } from '@elastic/eui/lib/test/rtl';
|
|||
import { licensingMock } from '@kbn/licensing-plugin/public/mocks';
|
||||
import { waitForComponentToUpdate } from '../../common/test_utils';
|
||||
|
||||
import { CaseStatuses, CustomFieldTypes } from '../../../common/types/domain';
|
||||
import { CaseStatuses, CustomFieldTypes, CaseSeverity } from '../../../common/types/domain';
|
||||
import { SECURITY_SOLUTION_OWNER, OBSERVABILITY_OWNER } from '../../../common/constants';
|
||||
import type { AppMockRenderer } from '../../common/mock';
|
||||
import { createAppMockRenderer } from '../../common/mock';
|
||||
|
@ -857,4 +857,34 @@ describe('CasesTableFilters ', () => {
|
|||
`);
|
||||
});
|
||||
});
|
||||
|
||||
it('should activate a filter when there is a value in the global state as this means that it has a value set in the url', async () => {
|
||||
const previousState = [
|
||||
{ key: 'severity', isActive: false }, // notice severity filter not active
|
||||
{ key: 'status', isActive: false }, // notice status filter not active
|
||||
{ key: 'tags', isActive: true },
|
||||
{ key: 'category', isActive: false },
|
||||
];
|
||||
|
||||
localStorage.setItem('testAppId.cases.list.tableFiltersConfig', JSON.stringify(previousState));
|
||||
|
||||
const overrideProps = {
|
||||
...props,
|
||||
filterOptions: {
|
||||
...DEFAULT_FILTER_OPTIONS,
|
||||
severity: [CaseSeverity.MEDIUM], // but they have values
|
||||
status: [CaseStatuses.open, CaseStatuses['in-progress']],
|
||||
},
|
||||
};
|
||||
|
||||
appMockRender.render(<CasesTableFilters {...overrideProps} />);
|
||||
|
||||
const statusButton = await screen.findByRole('button', { name: 'Status' });
|
||||
expect(statusButton).toBeInTheDocument();
|
||||
expect(within(statusButton).getByLabelText('2 active filters')).toBeInTheDocument();
|
||||
|
||||
const severityButton = await screen.findByRole('button', { name: 'Severity' });
|
||||
expect(severityButton).toBeInTheDocument();
|
||||
expect(within(severityButton).getByLabelText('1 active filters')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,9 +6,8 @@
|
|||
*/
|
||||
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { isEqual } from 'lodash/fp';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiFieldSearch, EuiFilterGroup, EuiButton } from '@elastic/eui';
|
||||
import { mergeWith } from 'lodash';
|
||||
import { mergeWith, isEqual } from 'lodash';
|
||||
import { MoreFiltersSelectable } from './table_filter_config/more_filters_selectable';
|
||||
import type { CaseStatuses } from '../../../common/types/domain';
|
||||
import type { FilterOptions } from '../../containers/types';
|
||||
|
@ -107,7 +106,7 @@ const CasesTableFiltersComponent = ({
|
|||
selectableOptions,
|
||||
activeSelectableOptionKeys,
|
||||
onFilterConfigChange,
|
||||
} = useFilterConfig({ systemFilterConfig, onFilterOptionsChange, isSelectorView });
|
||||
} = useFilterConfig({ systemFilterConfig, onFilterOptionsChange, isSelectorView, filterOptions });
|
||||
|
||||
const handleOnSearch = useCallback(
|
||||
(newSearch) => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue