diff --git a/packages/ui/src/ui/filters/filters.i18n.ts b/packages/ui/src/ui/filters/filters.i18n.ts index 948485b01d..ddb61e6e49 100644 --- a/packages/ui/src/ui/filters/filters.i18n.ts +++ b/packages/ui/src/ui/filters/filters.i18n.ts @@ -1,2 +1,5 @@ +export const CHUNKS_NOT_FOUND = 'No chunks found'; +export const CHUNKS_SEARCH = 'Search by chunk name'; +export const CHUNKS_SEARCH_CLEAR = 'Clear search'; export const CLEAR = 'Clear all'; export const CHECK = 'Check all'; diff --git a/packages/ui/src/ui/filters/filters.module.css b/packages/ui/src/ui/filters/filters.module.css index 02f68f4173..c457b1fcef 100644 --- a/packages/ui/src/ui/filters/filters.module.css +++ b/packages/ui/src/ui/filters/filters.module.css @@ -116,6 +116,35 @@ text-transform: lowercase; } +.filterGroupSearchWrapper { + padding-bottom: var(--space-xxxsmall); + margin-bottom: var(--space-xxxsmall); + border-bottom: 1px solid var(--color-outline); +} + +.filterGroupSearchNotFound { + padding: var(--space-xxsmall) 0; + text-align: center; + font-size: var(--size-small); + color: var(--color-text-light); +} + +.filterGroupSearchNotFoundClear { + outline: none; + appearance: none; + border: none; + background: none; + cursor: pointer; + white-space: nowrap; + color: var(--color-text-dark); +} + +.filterGroupSearchNotFoundClear:hover, +.filterGroupSearchNotFoundClear:active, +.filterGroupSearchNotFoundClear:focus { + color: var(--color-text-dark); +} + .filterGroupItems { max-height: calc(10 * (var(--space-small) + 2 * var(--space-xxxsmall))); overflow: auto; diff --git a/packages/ui/src/ui/filters/filters.tsx b/packages/ui/src/ui/filters/filters.tsx index 0c627daec9..a4ab80f969 100644 --- a/packages/ui/src/ui/filters/filters.tsx +++ b/packages/ui/src/ui/filters/filters.tsx @@ -1,9 +1,11 @@ -import React, { useCallback } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import cx from 'classnames'; import type { FilterFieldsData, FilterGroupFieldData } from '../../types'; import { FlexStack } from '../../layout/flex-stack'; +import { Stack } from '../../layout/stack'; import { Dropdown } from '../dropdown'; +import { InputSearch } from '../input-search'; import * as I18N from './filters.i18n'; import { getGroupFiltersLabelSuffix, LABELS } from './filters.utils'; import css from './filters.module.css'; @@ -75,6 +77,8 @@ const FilterGroup = (props: FilterGroupProps) => { toggleFilters, } = props; + const [search, setSearch] = useState(''); + const { label: groupLabel, children: groupItems } = data; const areAllGroupItemsChecked = groupItems @@ -108,6 +112,14 @@ const FilterGroup = (props: FilterGroupProps) => { }); }; + const filteredGroupItems = useMemo(() => { + if (!search) { + return groupItems; + } + + return groupItems.filter((item) => item.label.match(new RegExp(`${search}`, 'i'))); + }, [search, groupItems]); + return ( { {({ MenuItem, menuItemClassName }) => { return ( <> + {groupItems.length > 10 && ( +
+ +
+ )}
- {groupItems.map(({ key: itemKey, ...itemData }) => { + {filteredGroupItems.length === 0 && ( + +

{I18N.CHUNKS_NOT_FOUND}

+
+ +
+
+ )} + {filteredGroupItems.map(({ key: itemKey, ...itemData }) => { const id = [groupKey, itemKey].join('.'); const getOnOnlyClick = () => getOnGroupCheck(false, { [id]: true }); @@ -138,29 +174,31 @@ const FilterGroup = (props: FilterGroupProps) => { ); })}
-
- {areAllGroupItemsChecked ? ( - - {I18N.CLEAR} - - ) : ( - - {I18N.CHECK} - - )} -
+ {filteredGroupItems.length !== 0 && ( +
+ {areAllGroupItemsChecked ? ( + + {I18N.CLEAR} + + ) : ( + + {I18N.CHECK} + + )} +
+ )} ); }} diff --git a/packages/ui/src/ui/input/index.js b/packages/ui/src/ui/input/index.ts similarity index 100% rename from packages/ui/src/ui/input/index.js rename to packages/ui/src/ui/input/index.ts diff --git a/packages/ui/src/ui/input/input.jsx b/packages/ui/src/ui/input/input.jsx deleted file mode 100644 index 6b6c4d18cd..0000000000 --- a/packages/ui/src/ui/input/input.jsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import cx from 'classnames'; -import PropTypes from 'prop-types'; - -import css from './input.module.css'; - -export const Input = ({ className, size, ...restProps }) => { - const rootClassName = cx(css.root, className, css[size]); - return ; -}; - -Input.propTypes = { - className: PropTypes.string, - size: PropTypes.oneOf(['small', 'medium', 'large']), -}; - -Input.defaultProps = { - className: '', - size: 'medium', -}; diff --git a/packages/ui/src/ui/input/input.stories.jsx b/packages/ui/src/ui/input/input.stories.tsx similarity index 78% rename from packages/ui/src/ui/input/input.stories.jsx rename to packages/ui/src/ui/input/input.stories.tsx index 7ba2770a82..4d589094c8 100644 --- a/packages/ui/src/ui/input/input.stories.jsx +++ b/packages/ui/src/ui/input/input.stories.tsx @@ -10,4 +10,4 @@ export default { }; export const Default = () => ; -export const WithSize = () => ; +export const WithSize = () => ; diff --git a/packages/ui/src/ui/input/input.tsx b/packages/ui/src/ui/input/input.tsx new file mode 100644 index 0000000000..c81db08a3d --- /dev/null +++ b/packages/ui/src/ui/input/input.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import cx from 'classnames'; + +import css from './input.module.css'; + +interface InputProps { + size?: 'small' | 'medium' | 'large'; +} + +export const Input = (props: InputProps & Omit, 'size'>) => { + const { className = '', size = 'medium', ...restProps } = props; + + const rootClassName = cx(css.root, className, css[size]); + + return ; +};