diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_status_filter.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_status_filter.tsx index 4e7082931b64d..09d7234595857 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_status_filter.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rule_status_filter.tsx @@ -4,14 +4,18 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useState, useCallback } from 'react'; +import React, { useState, useCallback, useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiFilterButton, EuiPopover, EuiFilterGroup, EuiSelectableListItem } from '@elastic/eui'; -import { RuleStatus } from '../../../../types'; - -const statuses: RuleStatus[] = ['enabled', 'disabled', 'snoozed']; - -const getOptionDataTestSubj = (status: RuleStatus) => `ruleStatusFilterOption-${status}`; +import { + EuiFilterButton, + EuiPopover, + EuiFilterGroup, + EuiSelectable, + type EuiSelectableOption, + type EuiSelectableProps, +} from '@elastic/eui'; +import type { RuleStatus } from '../../../../types'; export interface RuleStatusFilterProps { selectedStatuses: RuleStatus[]; @@ -22,6 +26,38 @@ export interface RuleStatusFilterProps { onChange: (selectedStatuses: RuleStatus[]) => void; } +const ruleStateFilterOptions: Array<{ key: RuleStatus; label: string }> = [ + { + key: 'enabled', + label: i18n.translate( + 'xpack.triggersActionsUI.sections.ruleDetails.ruleStateFilter.enabledOptionText', + { + defaultMessage: 'Rule is enabled', + } + ), + }, + { + key: 'disabled', + label: i18n.translate( + 'xpack.triggersActionsUI.sections.ruleDetails.ruleStateFilter.disabledOptionText', + { + defaultMessage: 'Rule is disabled', + } + ), + }, + { + key: 'snoozed', + label: i18n.translate( + 'xpack.triggersActionsUI.sections.ruleDetails.ruleStateFilter.snoozedOptionText', + { + defaultMessage: 'Rule has snoozed', + } + ), + }, +]; + +const getOptionDataTestSubj = (status: RuleStatus) => `ruleStatusFilterOption-${status}`; + export const RuleStatusFilter = (props: RuleStatusFilterProps) => { const { selectedStatuses = [], @@ -34,45 +70,31 @@ export const RuleStatusFilter = (props: RuleStatusFilterProps) => { const [isPopoverOpen, setIsPopoverOpen] = useState(false); - const onFilterItemClick = useCallback( - (newOption: RuleStatus) => () => { - if (selectedStatuses.includes(newOption)) { - onChange(selectedStatuses.filter((option) => option !== newOption)); - return; - } - onChange([...selectedStatuses, newOption]); + const onFilterItemChange: EuiSelectableProps['onChange'] = useCallback( + (newOptions: EuiSelectableOption[]) => { + const selected = newOptions + .filter(({ checked }) => checked === 'on') + .map(({ key }) => key as RuleStatus); + + onChange(selected); }, - [selectedStatuses, onChange] + [onChange] ); const onClick = useCallback(() => { setIsPopoverOpen((prevIsOpen) => !prevIsOpen); }, [setIsPopoverOpen]); - const renderRuleStateOptions = (status: 'enabled' | 'disabled' | 'snoozed') => { - if (status === 'enabled') { - return ( - - ); - } else if (status === 'disabled') { - return ( - - ); - } else if (status === 'snoozed') { - return ( - - ); - } - }; + const selectableOptions = useMemo( + () => + ruleStateFilterOptions.map(({ key, label }) => ({ + key, + label, + 'data-test-subj': optionDataTestSubj(key), + checked: selectedStatuses.includes(key) ? 'on' : undefined, + })), + [optionDataTestSubj, selectedStatuses] + ); return ( @@ -96,18 +118,18 @@ export const RuleStatusFilter = (props: RuleStatusFilterProps) => { } >
- {statuses.map((status) => { - return ( - - {renderRuleStateOptions(status)} - - ); - })} + + {(list) => list} +
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.test.tsx index 363c348407bfe..efedf01a92f84 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rules_list/components/rules_list.test.tsx @@ -397,13 +397,14 @@ describe('rules_list ', () => { ); fireEvent.click((await screen.findAllByTestId('ruleStatusFilterButton'))[0]); fireEvent.click((await screen.findAllByTestId('ruleStatusFilterOption-enabled'))[0]); + expect(loadRulesWithKueryFilter).toHaveBeenLastCalledWith( expect.objectContaining({ - ruleStatusesFilter: ['disabled', 'enabled'], + ruleStatusesFilter: ['enabled', 'disabled'], }) ); expect(onStatusFilterChangeMock).toHaveBeenCalled(); - expect(onStatusFilterChangeMock).toHaveBeenLastCalledWith(['disabled', 'enabled']); + expect(onStatusFilterChangeMock).toHaveBeenLastCalledWith(['enabled', 'disabled']); }); it('can filter by last response', async () => { diff --git a/x-pack/test_serverless/functional/test_suites/observability/rules/rules_list.ts b/x-pack/test_serverless/functional/test_suites/observability/rules/rules_list.ts index aa2ba3ff72fda..4696907ed8ee4 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/rules/rules_list.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/rules/rules_list.ts @@ -30,6 +30,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { const find = getService('find'); const retry = getService('retry'); const toasts = getService('toasts'); + const log = getService('log'); const svlCommonApi = getService('svlCommonApi'); const svlUserManager = getService('svlUserManager'); const supertestWithoutAuth = getService('supertestWithoutAuth'); @@ -711,6 +712,24 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { }); it('should filter rules by the rule status', async () => { + const setRuleStatus = async (testSubj: string, checked: boolean) => { + const readAriaChecked = async (): Promise => { + const statusItem = await testSubjects.find(testSubj); + return statusItem + ? JSON.parse((await statusItem.getAttribute('aria-checked')) || 'null') + : null; + }; + + await retry.try(async () => { + if ((await readAriaChecked()) !== checked) { + await testSubjects.click(testSubj); + await find.waitForDeletedByCssSelector('.euiBasicTable-loading'); + } + + expect(await readAriaChecked()).toEqual(checked); + }); + }; + // Enabled alert const rule1 = await createRule({ supertest, @@ -750,39 +769,33 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { await refreshRulesList(); await assertRulesLength(4); - // Select only enabled + log.debug('ruleStatusFilterButton: Select only enabled'); await testSubjects.click('ruleStatusFilterButton'); - await testSubjects.click('ruleStatusFilterOption-enabled'); - await find.waitForDeletedByCssSelector('.euiBasicTable-loading'); + await setRuleStatus('ruleStatusFilterOption-enabled', true); await assertRulesLength(2); - // Select enabled or disabled (e.g. all) - await testSubjects.click('ruleStatusFilterOption-disabled'); - await find.waitForDeletedByCssSelector('.euiBasicTable-loading'); + log.debug('ruleStatusFilterButton: Select enabled or disabled (e.g. all)'); + await setRuleStatus('ruleStatusFilterOption-disabled', true); await assertRulesLength(4); - // Select only disabled - await testSubjects.click('ruleStatusFilterOption-enabled'); - await find.waitForDeletedByCssSelector('.euiBasicTable-loading'); + log.debug('ruleStatusFilterButton: Select only disabled'); + await setRuleStatus('ruleStatusFilterOption-enabled', false); await assertRulesLength(2); - // Select only snoozed - await testSubjects.click('ruleStatusFilterOption-disabled'); - await testSubjects.click('ruleStatusFilterOption-snoozed'); - await find.waitForDeletedByCssSelector('.euiBasicTable-loading'); + log.debug('ruleStatusFilterButton: Select only snoozed'); + await setRuleStatus('ruleStatusFilterOption-disabled', false); + await setRuleStatus('ruleStatusFilterOption-snoozed', true); await assertRulesLength(2); - // Select disabled or snoozed - await testSubjects.click('ruleStatusFilterOption-disabled'); - await find.waitForDeletedByCssSelector('.euiBasicTable-loading'); + log.debug('ruleStatusFilterButton: Select disabled or snoozed'); + await setRuleStatus('ruleStatusFilterOption-disabled', true); await assertRulesLength(3); - // Select enabled or disabled or snoozed - await testSubjects.click('ruleStatusFilterOption-enabled'); - await find.waitForDeletedByCssSelector('.euiBasicTable-loading'); + log.debug('ruleStatusFilterButton: Select enabled or disabled or snoozed'); + await setRuleStatus('ruleStatusFilterOption-enabled', true); await assertRulesLength(4); - // Clear it again because it is still selected + log.debug('ruleStatusFilterButton: Clear it again because it is still selected'); await testSubjects.click('rules-list-clear-filter'); await find.waitForDeletedByCssSelector('.euiBasicTable-loading'); await assertRulesLength(4);