From d280b5e6fa7b8132abb8a1820b2fabff83aa7cb0 Mon Sep 17 00:00:00 2001 From: Radoslaw Szwajkowski Date: Fri, 10 Nov 2023 13:57:04 +0100 Subject: [PATCH] Add type-ahead search to GroupedEnumFilter Reference-Url: http://v4-archive.patternfly.org/v4/demos/filters/design-guidelines/#type-ahead Reference-Url: http://v4-archive.patternfly.org/v4/components/select/#grouped-checkbox-input-with-filtering Signed-off-by: Radoslaw Szwajkowski --- .../components/Filter/GroupedEnumFilter.tsx | 47 ++++++++++++++----- .../Filter/SearchableGroupedEnumFilter.tsx | 11 +++++ .../common/src/components/Filter/index.ts | 1 + .../common/src/components/Filter/types.ts | 4 ++ .../src/components/FilterGroup/matchers.ts | 8 ++++ 5 files changed, 60 insertions(+), 11 deletions(-) create mode 100644 packages/common/src/components/Filter/SearchableGroupedEnumFilter.tsx diff --git a/packages/common/src/components/Filter/GroupedEnumFilter.tsx b/packages/common/src/components/Filter/GroupedEnumFilter.tsx index 733a3e236..2d546564e 100644 --- a/packages/common/src/components/Filter/GroupedEnumFilter.tsx +++ b/packages/common/src/components/Filter/GroupedEnumFilter.tsx @@ -11,7 +11,7 @@ import { import { EnumValue } from '../../utils'; -import { FilterTypeProps } from './types'; +import { FilterTypeProps, InlineFilter } from './types'; /** * This Filter type enables selecting one or many enum values that are separated by groups. @@ -40,7 +40,8 @@ export const GroupedEnumFilter = ({ supportedGroups = [], placeholderLabel, showFilter = true, -}: FilterTypeProps) => { + hasInlineFilter = false, +}: FilterTypeProps & InlineFilter) => { const [isExpanded, setExpanded] = useState(false); // simplify lookup @@ -76,6 +77,36 @@ export const GroupedEnumFilter = ({ compareTo: (option) => option.id === id && option.groupId === groupId, } as SelectOptionObject); + const options = supportedGroups.map(({ label, groupId }) => ( + + {supportedEnumValues + .filter((item) => item.groupId === groupId) + .map(({ id, label }) => ( + + ))} + + )); + + const onFilter = (_, textInput) => { + if (textInput === '') { + return options; + } + + const filteredGroups = options + .map((group) => { + const filteredGroup = React.cloneElement(group, { + children: group.props.children.filter((item) => { + // options are not plain strings but the our toString() method + // converts them in a meaningful way + return item.props.value.toString().toLowerCase().includes(textInput.toLowerCase()); + }), + }); + if (filteredGroup.props.children.length > 0) return filteredGroup; + }) + .filter((newGroup) => newGroup); + return filteredGroups; + }; + return ( <> {/** @@ -123,16 +154,10 @@ export const GroupedEnumFilter = ({ placeholderText={placeholderLabel} isOpen={isExpanded} onToggle={setExpanded} + hasInlineFilter={hasInlineFilter} + onFilter={onFilter} > - {supportedGroups.map(({ label, groupId }) => ( - - {supportedEnumValues - .filter((item) => item.groupId === groupId) - .map(({ id, label }) => ( - - ))} - - ))} + {options} , )} diff --git a/packages/common/src/components/Filter/SearchableGroupedEnumFilter.tsx b/packages/common/src/components/Filter/SearchableGroupedEnumFilter.tsx new file mode 100644 index 000000000..cea87df6e --- /dev/null +++ b/packages/common/src/components/Filter/SearchableGroupedEnumFilter.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { GroupedEnumFilter } from './GroupedEnumFilter'; +import { FilterTypeProps } from './types'; + +/** + * GroupedEnumFilter with inline search enabled. + */ +export const SearchableGroupedEnumFilter = (props: FilterTypeProps) => ( + +); diff --git a/packages/common/src/components/Filter/index.ts b/packages/common/src/components/Filter/index.ts index 9e6c7c398..14d0286fb 100644 --- a/packages/common/src/components/Filter/index.ts +++ b/packages/common/src/components/Filter/index.ts @@ -4,6 +4,7 @@ export * from './DateRangeFilter'; export * from './EnumFilter'; export * from './FreetextFilter'; export * from './GroupedEnumFilter'; +export * from './SearchableGroupedEnumFilter'; export * from './SwitchFilter'; export * from './types'; // @endindex diff --git a/packages/common/src/components/Filter/types.ts b/packages/common/src/components/Filter/types.ts index 27a314619..992c85e86 100644 --- a/packages/common/src/components/Filter/types.ts +++ b/packages/common/src/components/Filter/types.ts @@ -44,3 +44,7 @@ export interface FilterTypeProps { /** Text that explains how to use the filter. */ helperText?: string | React.ReactNode; } + +export interface InlineFilter { + hasInlineFilter?: boolean; +} diff --git a/packages/common/src/components/FilterGroup/matchers.ts b/packages/common/src/components/FilterGroup/matchers.ts index 6cc517c56..7c3f172ac 100644 --- a/packages/common/src/components/FilterGroup/matchers.ts +++ b/packages/common/src/components/FilterGroup/matchers.ts @@ -7,6 +7,7 @@ import { EnumFilter, FreetextFilter, GroupedEnumFilter, + SearchableGroupedEnumFilter, SwitchFilter, } from '../Filter'; @@ -103,6 +104,11 @@ const groupedEnumMatcher = { matchValue: enumMatcher.matchValue, }; +const searchableGroupedEnumMatcher = { + filterType: 'searchableGroupedEnum', + matchValue: enumMatcher.matchValue, +}; + const dateMatcher = { filterType: 'date', matchValue: (value: string) => (filter: string) => areSameDayInUTCZero(value, filter), @@ -122,6 +128,7 @@ export const defaultValueMatchers: ValueMatcher[] = [ freetextMatcher, enumMatcher, groupedEnumMatcher, + searchableGroupedEnumMatcher, sliderMatcher, dateMatcher, dateRangeMatcher, @@ -134,6 +141,7 @@ export const defaultSupportedFilters: Record = { freetext: FreetextFilter, groupedEnum: GroupedEnumFilter, slider: SwitchFilter, + searchableGroupedEnum: SearchableGroupedEnumFilter, }; /**