From 9d5eea1e7645d5fbda220c3c55542d6115f16dde Mon Sep 17 00:00:00 2001 From: Riddhi Bansal <41935566+riddhybansal@users.noreply.github.com> Date: Tue, 1 Oct 2024 19:15:26 +0530 Subject: [PATCH] fix: added typescript types to fluid multiselect (#17583) --- ...lect.js => FluidFilterableMultiSelect.tsx} | 170 +++++++- ...leton.js => FluidMultiSelect.Skeleton.tsx} | 12 +- .../FluidMultiSelect/FluidMultiSelect.tsx | 387 ++++++++++++++++++ .../FluidMultiSelect/{index.js => index.tsx} | 4 +- .../src/components/FluidNumberInput/index.tsx | 4 +- .../src/components/FluidSearch/index.tsx | 4 +- .../src/components/FluidSelect/index.tsx | 4 +- .../src/components/FluidTimePicker/index.tsx | 4 +- .../MultiSelect/FilterableMultiSelect.tsx | 1 + 9 files changed, 566 insertions(+), 24 deletions(-) rename packages/react/src/components/FluidMultiSelect/{FluidMultiSelect.js => FluidFilterableMultiSelect.tsx} (55%) rename packages/react/src/components/FluidMultiSelect/{FluidMultiSelect.Skeleton.js => FluidMultiSelect.Skeleton.tsx} (79%) create mode 100644 packages/react/src/components/FluidMultiSelect/FluidMultiSelect.tsx rename packages/react/src/components/FluidMultiSelect/{index.js => index.tsx} (63%) diff --git a/packages/react/src/components/FluidMultiSelect/FluidMultiSelect.js b/packages/react/src/components/FluidMultiSelect/FluidFilterableMultiSelect.tsx similarity index 55% rename from packages/react/src/components/FluidMultiSelect/FluidMultiSelect.js rename to packages/react/src/components/FluidMultiSelect/FluidFilterableMultiSelect.tsx index 8108aae6fa9a..e3658518f720 100644 --- a/packages/react/src/components/FluidMultiSelect/FluidMultiSelect.js +++ b/packages/react/src/components/FluidMultiSelect/FluidFilterableMultiSelect.tsx @@ -6,16 +6,167 @@ */ import PropTypes from 'prop-types'; -import React from 'react'; +import React, { ForwardedRef, FunctionComponent, RefObject } from 'react'; import classnames from 'classnames'; import { FilterableMultiSelect } from '../FilterableMultiSelect'; import MultiSelect from '../MultiSelect'; import { usePrefix } from '../../internal/usePrefix'; import { FormContext } from '../FluidForm/FormContext'; +import { + UseComboboxProps, + UseMultipleSelectionProps, + UseSelectProps, +} from 'downshift'; +import { MultiSelectProps } from '../MultiSelect/MultiSelect'; +import { FilterableMultiSelectProps } from '../MultiSelect/FilterableMultiSelect'; + +interface OnChangeData { + selectedItems: ItemType[] | null; +} + +interface SharedOptions { + locale: string; +} + +export interface FluidFilterableMultiSelectProps + extends FilterableMultiSelectProps { + /** + * Specify an optional className to be applied to the outer FluidForm wrapper + */ + className?: string; + /** + * Specify the text that should be read for screen readers that describes total items selected + */ + clearSelectionDescription?: string; + /** + * Specify the text that should be read for screen readers to clear selection. + */ + clearSelectionText?: string; + /** + * Specify the direction of the multiselect dropdown. Can be either top or bottom. + */ + direction?: 'top' | 'bottom'; + /** + * Specify whether the `` should be disabled + */ + disabled?: boolean; + /** + * Additional props passed to Downshift. + * + * **Use with caution:** anything you define here overrides the components' + * internal handling of that prop. Downshift APIs and internals are subject to + * change, and in some cases they can not be shimmed by Carbon to shield you + * from potentially breaking changes. + */ + downshiftProps?: Partial>; + /** + * Specify a custom `id` for the `` + */ + id: string; + /** + * Allow users to pass in arbitrary items from their collection that are + * pre-selected + */ + initialSelectedItems?: ItemType[]; + /** + * Specify if the currently selected value is invalid. + */ + invalid?: boolean; + /** + * Provide the text that is displayed when the control is in an invalid state + */ + invalidText?: React.ReactNode; + /** + * Specify if the `FluidMultiSelect` should render its menu items in condensed mode + */ + isCondensed?: boolean; + /** + * Function to render items as custom components instead of strings. + * Defaults to null and is overridden by a getter + */ + itemToElement?: FunctionComponent; + /** + * Helper function passed to downshift that allows the library to render a + * given item to a string label. By default, it extracts the `label` field + * from a given item to serve as the item label in the list. Consider + * declaring function with `useCallback` to prevent unnecessary re-renders. + */ + itemToString?(item: ItemType): string; + /** + * We try to stay as generic as possible here to allow individuals to pass + * in a collection of whatever kind of data structure they prefer + */ + items: ItemType[]; + /** + * Generic `label` that will be used as the textual representation of what + * this field is for + */ + label: NonNullable; + /** + * Specify the locale of the control. Used for the default `compareItems` + * used for sorting the list of items in the control. + */ + locale?: string; + /** + * `onChange` is a utility for this controlled component to communicate to a + * consuming component what kind of internal state changes are occurring. + */ + onChange?(data: OnChangeData): void; + /** + * **Filterable variant only** - `onInputValueChange` is a utility for this controlled component to communicate to + * the currently typed input. + */ + onInputValueChange?: UseComboboxProps['onInputValueChange']; + /** + * `onMenuChange` is a utility for this controlled component to communicate to a + * consuming component that the menu was open(`true`)/closed(`false`). + */ + onMenuChange?(open: boolean): void; + /** + * Whether or not the Multiselect is readonly + */ + readOnly?: boolean; + /** + * For full control of the selected items + */ + selectedItems?: ItemType[]; + /** + * Specify feedback (mode) of the selection. + * `top`: selected item jumps to top + * `fixed`: selected item stays at it's position + * `top-after-reopen`: selected item jump to top after reopen dropdown + */ + selectionFeedback?: 'top' | 'fixed' | 'top-after-reopen'; -const FluidMultiSelect = React.forwardRef(function FluidMultiSelect( - { className, isCondensed, isFilterable, ...other }, - ref + /** + * Provide the title text that will be read by a screen reader when + * visiting this control + */ + titleText?: React.ReactNode; + /** + * Callback function for translating ListBoxMenuIcon SVG title + */ + translateWithId?: (id: string) => string; + /** + * Specify title to show title on hover + */ + useTitleInItem?: boolean; + /** + * Specify whether the control is currently in warning state + */ + warn?: boolean; + /** + * Provide the text that is displayed when the control is in warning state + */ + warnText?: React.ReactNode; +} +const FluidMultiSelect = React.forwardRef(function FluidMultiSelect( + { + className, + isCondensed, + ...other + }: FluidFilterableMultiSelectProps, + ref: ForwardedRef ) { const prefix = usePrefix(); const classNames = classnames( @@ -26,11 +177,7 @@ const FluidMultiSelect = React.forwardRef(function FluidMultiSelect( return ( - {isFilterable ? ( - - ) : ( - - )} + ); }); @@ -104,11 +251,6 @@ FluidMultiSelect.propTypes = { */ isCondensed: PropTypes.bool, - /** - * Specify if the `FluidMultiSelect` should render the `filterable` variant of `FluidMultiSelect` - */ - isFilterable: PropTypes.bool, - /** * Function to render items as custom components instead of strings. * Defaults to null and is overridden by a getter diff --git a/packages/react/src/components/FluidMultiSelect/FluidMultiSelect.Skeleton.js b/packages/react/src/components/FluidMultiSelect/FluidMultiSelect.Skeleton.tsx similarity index 79% rename from packages/react/src/components/FluidMultiSelect/FluidMultiSelect.Skeleton.js rename to packages/react/src/components/FluidMultiSelect/FluidMultiSelect.Skeleton.tsx index e7efdc8c6b6a..21f928d2b48f 100644 --- a/packages/react/src/components/FluidMultiSelect/FluidMultiSelect.Skeleton.js +++ b/packages/react/src/components/FluidMultiSelect/FluidMultiSelect.Skeleton.tsx @@ -10,7 +10,17 @@ import React from 'react'; import cx from 'classnames'; import { usePrefix } from '../../internal/usePrefix'; -const FluidMultiSelectSkeleton = ({ className, ...rest }) => { +export interface FluidMultiSelectSkeletonProps { + /** + * Specify an optional className to add. + */ + className?: string; +} + +const FluidMultiSelectSkeleton: React.FC = ({ + className, + ...rest +}) => { const prefix = usePrefix(); const wrapperClasses = cx( className, diff --git a/packages/react/src/components/FluidMultiSelect/FluidMultiSelect.tsx b/packages/react/src/components/FluidMultiSelect/FluidMultiSelect.tsx new file mode 100644 index 000000000000..4d1e00aa9d2e --- /dev/null +++ b/packages/react/src/components/FluidMultiSelect/FluidMultiSelect.tsx @@ -0,0 +1,387 @@ +/** + * Copyright IBM Corp. 2022 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import PropTypes from 'prop-types'; +import React, { ForwardedRef, RefObject } from 'react'; +import classnames from 'classnames'; +import { FilterableMultiSelect } from '../FilterableMultiSelect'; +import MultiSelect from '../MultiSelect'; +import { usePrefix } from '../../internal/usePrefix'; +import { FormContext } from '../FluidForm/FormContext'; +import { UseSelectProps } from 'downshift'; +import { MultiSelectProps } from '../MultiSelect/MultiSelect'; +import { FilterableMultiSelectProps } from '../MultiSelect/FilterableMultiSelect'; + +interface OnChangeData { + selectedItems: ItemType[] | null; +} + +interface SharedOptions { + locale: string; +} + +export interface FluidMultiSelectProps + extends MultiSelectProps { + /** + * Specify an optional className to be applied to the outer FluidForm wrapper + */ + className?: string; + /** + * Specify the text that should be read for screen readers that describes total items selected + */ + clearSelectionDescription?: string; + /** + * Specify the text that should be read for screen readers to clear selection. + */ + clearSelectionText?: string; + /** + * Specify the direction of the multiselect dropdown. Can be either top or bottom. + */ + direction?: 'top' | 'bottom'; + /** + * Specify whether the `` should be disabled + */ + disabled?: boolean; + /** + * Additional props passed to Downshift. + * + * **Use with caution:** anything you define here overrides the components' + * internal handling of that prop. Downshift APIs and internals are subject to + * change, and in some cases they can not be shimmed by Carbon to shield you + * from potentially breaking changes. + */ + downshiftProps?: Partial>; + /** + * Specify a custom `id` for the `` + */ + id: string; + /** + * Allow users to pass in arbitrary items from their collection that are + * pre-selected + */ + initialSelectedItems?: ItemType[]; + /** + * Specify if the currently selected value is invalid. + */ + invalid?: boolean; + /** + * Provide the text that is displayed when the control is in an invalid state + */ + invalidText?: React.ReactNode; + /** + * Specify if the `FluidMultiSelect` should render its menu items in condensed mode + */ + isCondensed?: boolean; + /** + * Specify if the `FluidMultiSelect` should render the `filterable` variant of `FluidMultiSelect` + * @deprecated This prop is deprecated in favor of new component called FluidFilterableMultiSelect and will be removed in the next major release + */ + isFilterable?: boolean; + /** + * Function to render items as custom components instead of strings. + * Defaults to null and is overridden by a getter + */ + itemToElement?: React.JSXElementConstructor; + /** + * Helper function passed to downshift that allows the library to render a + * given item to a string label. By default, it extracts the `label` field + * from a given item to serve as the item label in the list. Consider + * declaring function with `useCallback` to prevent unnecessary re-renders. + */ + itemToString?(item: ItemType): string; + /** + * We try to stay as generic as possible here to allow individuals to pass + * in a collection of whatever kind of data structure they prefer + */ + items: ItemType[]; + /** + * Generic `label` that will be used as the textual representation of what + * this field is for + */ + label: NonNullable; + /** + * Specify the locale of the control. Used for the default `compareItems` + * used for sorting the list of items in the control. + */ + locale?: string; + /** + * `onChange` is a utility for this controlled component to communicate to a + * consuming component what kind of internal state changes are occurring. + */ + onChange?(data: OnChangeData): void; + /** + * **Filterable variant only** - `onInputValueChange` is a utility for this controlled component to communicate to + * the currently typed input. + */ + onInputValueChange?: (inputValue: string) => void; + /** + * `onMenuChange` is a utility for this controlled component to communicate to a + * consuming component that the menu was open(`true`)/closed(`false`). + */ + onMenuChange?(open: boolean): void; + /** + * Whether or not the Multiselect is readonly + */ + readOnly?: boolean; + /** + * For full control of the selected items + */ + selectedItems?: ItemType[]; + /** + * Specify feedback (mode) of the selection. + * `top`: selected item jumps to top + * `fixed`: selected item stays at it's position + * `top-after-reopen`: selected item jump to top after reopen dropdown + */ + selectionFeedback?: 'top' | 'fixed' | 'top-after-reopen'; + + /** + * Provide the title text that will be read by a screen reader when + * visiting this control + */ + titleText?: React.ReactNode; + /** + * Callback function for translating ListBoxMenuIcon SVG title + */ + translateWithId?: (id: string) => string; + /** + * Specify title to show title on hover + */ + useTitleInItem?: boolean; + /** + * Specify whether the control is currently in warning state + */ + warn?: boolean; + /** + * Provide the text that is displayed when the control is in warning state + */ + warnText?: React.ReactNode; +} +const FluidMultiSelect = React.forwardRef(function FluidMultiSelect( + { + className, + isCondensed, + isFilterable, + ...other + }: FluidMultiSelectProps, + ref: ForwardedRef +) { + const prefix = usePrefix(); + const classNames = classnames( + `${prefix}--list-box__wrapper--fluid`, + className, + { [`${prefix}--list-box__wrapper--fluid--condensed`]: isCondensed } + ); + + return ( + + {isFilterable ? ( + // @ts-ignore + } + className={classNames} + {...other} + /> + ) : ( + + )} + + ); +}); + +FluidMultiSelect.propTypes = { + /** + * Specify an optional className to be applied to the outer FluidForm wrapper + */ + className: PropTypes.string, + + /** + * Specify the text that should be read for screen readers that describes total items selected + */ + clearSelectionDescription: PropTypes.string, + + /** + * Specify the text that should be read for screen readers to clear selection. + */ + clearSelectionText: PropTypes.string, + + /** + * Provide a compare function that is used to determine the ordering of + * options. See 'sortItems' for more control. Consider declaring function + * with `useCallback` to prevent unnecessary re-renders. + */ + compareItems: PropTypes.func, + + /** + * Specify the direction of the multiselect dropdown. Can be either top or bottom. + */ + direction: PropTypes.oneOf(['top', 'bottom']), + + /** + * Specify whether the `` should be disabled + */ + disabled: PropTypes.bool, + + /** + * Additional props passed to Downshift. + * + * **Use with caution:** anything you define here overrides the components' + * internal handling of that prop. Downshift APIs and internals are subject to + * change, and in some cases they can not be shimmed by Carbon to shield you + * from potentially breaking changes. + */ + downshiftProps: PropTypes.object, + + /** + * Specify a custom `id` for the `` + */ + id: PropTypes.string.isRequired, + + /** + * Allow users to pass in arbitrary items from their collection that are + * pre-selected + */ + initialSelectedItems: PropTypes.array, + + /** + * Specify if the currently selected value is invalid. + */ + invalid: PropTypes.bool, + + /** + * Provide the text that is displayed when the control is in an invalid state + */ + invalidText: PropTypes.node, + + /** + * Specify if the `FluidMultiSelect` should render its menu items in condensed mode + */ + isCondensed: PropTypes.bool, + + /** + * Specify if the `FluidMultiSelect` should render the `filterable` variant of `FluidMultiSelect` + */ + isFilterable: PropTypes.bool, + + /** + * Function to render items as custom components instead of strings. + * Defaults to null and is overridden by a getter + */ + itemToElement: PropTypes.func, + + /** + * Helper function passed to downshift that allows the library to render a + * given item to a string label. By default, it extracts the `label` field + * from a given item to serve as the item label in the list. Consider + * declaring function with `useCallback` to prevent unnecessary re-renders. + */ + itemToString: PropTypes.func, + + /** + * We try to stay as generic as possible here to allow individuals to pass + * in a collection of whatever kind of data structure they prefer + */ + items: PropTypes.array.isRequired, + + /** + * Generic `label` that will be used as the textual representation of what + * this field is for + */ + label: PropTypes.node.isRequired, + + /** + * Specify the locale of the control. Used for the default `compareItems` + * used for sorting the list of items in the control. + */ + locale: PropTypes.string, + + /** + * `onChange` is a utility for this controlled component to communicate to a + * consuming component what kind of internal state changes are occurring. + */ + onChange: PropTypes.func, + + /** + * **Filterable variant only** - `onInputValueChange` is a utility for this controlled component to communicate to + * the currently typed input. + */ + onInputValueChange: PropTypes.func, + + /** + * `onMenuChange` is a utility for this controlled component to communicate to a + * consuming component that the menu was open(`true`)/closed(`false`). + */ + onMenuChange: PropTypes.func, + + /** + * Whether or not the Multiselect is readonly + */ + readOnly: PropTypes.bool, + + /** + * For full control of the selected items + */ + selectedItems: PropTypes.array, + + /** + * Specify feedback (mode) of the selection. + * `top`: selected item jumps to top + * `fixed`: selected item stays at it's position + * `top-after-reopen`: selected item jump to top after reopen dropdown + */ + selectionFeedback: PropTypes.oneOf(['top', 'fixed', 'top-after-reopen']), + + /** + * Provide a method that sorts all options in the control. Overriding this + * prop means that you also have to handle the sort logic for selected versus + * un-selected items. If you just want to control ordering, consider the + * `compareItems` prop instead. + * + * The return value should be a number whose sign indicates the relative order + * of the two elements: negative if a is less than b, positive if a is greater + * than b, and zero if they are equal. + * + * sortItems : + * (items: Array, { + * selectedItems: Array, + * itemToString: Item => string, + * compareItems: (itemA: string, itemB: string, { + * locale: string + * }) => number, + * locale: string, + * }) => Array + */ + sortItems: PropTypes.func, + + /** + * Provide the title text that will be read by a screen reader when + * visiting this control + */ + titleText: PropTypes.node, + + /** + * Callback function for translating ListBoxMenuIcon SVG title + */ + translateWithId: PropTypes.func, + + /** + * Specify title to show title on hover + */ + useTitleInItem: PropTypes.bool, + + /** + * Specify whether the control is currently in warning state + */ + warn: PropTypes.bool, + + /** + * Provide the text that is displayed when the control is in warning state + */ + warnText: PropTypes.node, +}; + +export default FluidMultiSelect; diff --git a/packages/react/src/components/FluidMultiSelect/index.js b/packages/react/src/components/FluidMultiSelect/index.tsx similarity index 63% rename from packages/react/src/components/FluidMultiSelect/index.js rename to packages/react/src/components/FluidMultiSelect/index.tsx index a11b537f4656..35a4e7e479c4 100644 --- a/packages/react/src/components/FluidMultiSelect/index.js +++ b/packages/react/src/components/FluidMultiSelect/index.tsx @@ -6,7 +6,9 @@ */ import FluidMultiSelect from './FluidMultiSelect'; - +import type { FluidMultiSelectProps } from './FluidMultiSelect'; +import type { FluidMultiSelectSkeletonProps } from './FluidMultiSelect.Skeleton'; +export type { FluidMultiSelectProps, FluidMultiSelectSkeletonProps }; export default FluidMultiSelect; export { FluidMultiSelect }; export { default as FluidMultiSelectSkeleton } from './FluidMultiSelect.Skeleton'; diff --git a/packages/react/src/components/FluidNumberInput/index.tsx b/packages/react/src/components/FluidNumberInput/index.tsx index 8ff834291761..ef7dab9889e6 100644 --- a/packages/react/src/components/FluidNumberInput/index.tsx +++ b/packages/react/src/components/FluidNumberInput/index.tsx @@ -4,8 +4,8 @@ * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ -import { FluidNumberInputProps } from './FluidNumberInput'; -import { FluidNumberInputSkeletonProps } from './FluidNumberInput.Skeleton'; +import type { FluidNumberInputProps } from './FluidNumberInput'; +import type { FluidNumberInputSkeletonProps } from './FluidNumberInput.Skeleton'; export { type FluidNumberInputProps, type FluidNumberInputSkeletonProps }; export { default, default as FluidNumberInput } from './FluidNumberInput'; diff --git a/packages/react/src/components/FluidSearch/index.tsx b/packages/react/src/components/FluidSearch/index.tsx index 1898ea9253b4..3db04164782a 100644 --- a/packages/react/src/components/FluidSearch/index.tsx +++ b/packages/react/src/components/FluidSearch/index.tsx @@ -5,8 +5,8 @@ * LICENSE file in the root directory of this source tree. */ -import { FluidSearchProps } from './FluidSearch'; -import { FluidSearchSkeletonProps } from './FluidSearch.Skeleton'; +import type { FluidSearchProps } from './FluidSearch'; +import type { FluidSearchSkeletonProps } from './FluidSearch.Skeleton'; export { default, default as FluidSearch } from './FluidSearch'; export { type FluidSearchProps, type FluidSearchSkeletonProps }; export { default as FluidSearchSkeleton } from './FluidSearch.Skeleton'; diff --git a/packages/react/src/components/FluidSelect/index.tsx b/packages/react/src/components/FluidSelect/index.tsx index 269ccf5fb8c7..0fd863425dbf 100644 --- a/packages/react/src/components/FluidSelect/index.tsx +++ b/packages/react/src/components/FluidSelect/index.tsx @@ -6,8 +6,8 @@ */ import FluidSelect from './FluidSelect'; -import FluidSelectSkeletonProps from './FluidSelect.Skeleton'; -import FluidSelectProps from './FluidSelect.Skeleton'; +import type FluidSelectSkeletonProps from './FluidSelect.Skeleton'; +import type FluidSelectProps from './FluidSelect.Skeleton'; export type { FluidSelectSkeletonProps, FluidSelectProps }; export default FluidSelect; export { FluidSelect }; diff --git a/packages/react/src/components/FluidTimePicker/index.tsx b/packages/react/src/components/FluidTimePicker/index.tsx index 6bd752f7f623..b2118f51333a 100644 --- a/packages/react/src/components/FluidTimePicker/index.tsx +++ b/packages/react/src/components/FluidTimePicker/index.tsx @@ -6,8 +6,8 @@ */ import FluidTimePicker from './FluidTimePicker'; -import { FluidTimePickerProps } from './FluidTimePicker'; -import { FluidTimePickerSkeletonProps } from './FluidTimePicker.Skeleton'; +import { type FluidTimePickerProps } from './FluidTimePicker'; +import { type FluidTimePickerSkeletonProps } from './FluidTimePicker.Skeleton'; export { default as FluidTimePickerSkeleton } from './FluidTimePicker.Skeleton'; export type { FluidTimePickerProps, FluidTimePickerSkeletonProps }; export default FluidTimePicker; diff --git a/packages/react/src/components/MultiSelect/FilterableMultiSelect.tsx b/packages/react/src/components/MultiSelect/FilterableMultiSelect.tsx index eef35bc96692..368ba6278f61 100644 --- a/packages/react/src/components/MultiSelect/FilterableMultiSelect.tsx +++ b/packages/react/src/components/MultiSelect/FilterableMultiSelect.tsx @@ -102,6 +102,7 @@ type TranslationKey = export interface FilterableMultiSelectProps extends MultiSelectSortingProps, + React.RefAttributes, TranslateWithId { /** * Specify a label to be read by screen readers on the container node