diff --git a/achecker.js b/achecker.js index 73a952fdf5b7..7723a5cd760b 100644 --- a/achecker.js +++ b/achecker.js @@ -10,7 +10,7 @@ const path = require('path'); module.exports = { - ruleArchive: 'latest', + ruleArchive: '17June2024', policies: ['Custom_Ruleset'], failLevels: ['violation'], reportLevels: [ diff --git a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap index d8115901c241..c644fc56a43f 100644 --- a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap +++ b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap @@ -1278,6 +1278,16 @@ Map { "disabled": Object { "type": "bool", }, + "downshiftActions": Object { + "args": Array [ + Object { + "current": Object { + "type": "any", + }, + }, + ], + "type": "exact", + }, "downshiftProps": Object { "type": "object", }, diff --git a/packages/react/src/components/ComboBox/ComboBox.mdx b/packages/react/src/components/ComboBox/ComboBox.mdx index 15b07079dba6..225c09959019 100644 --- a/packages/react/src/components/ComboBox/ComboBox.mdx +++ b/packages/react/src/components/ComboBox/ComboBox.mdx @@ -49,18 +49,64 @@ next element. ``` -## Combobox `downshiftProps` +## Combobox uses Downshift Our `Combobox` component utilizes [Downshift](https://www.downshift-js.com/) under the hood to help provide complex yet accessible custom dropdown -components. We provide access to the built in Downshift features with this prop. +components. -Use with caution: anything you define here overrides the components' internal -handling of that prop. Downshift internals are subject to change, and in some -cases they can not be shimmed to shield you from potentially breaking changes. +### `downshiftProps` -For more information, checkout the Downshift prop -[documentation](https://www.downshift-js.com/downshift#props-used-in-examples) +`downshiftProps` is made available as a passthrough to the underlying downshift +`useCombobox` hook. + +**Use with caution:** anything you define here overrides the components' +internal handling of that prop. Downshift internals are subject to change, and +in some cases they can not be shimmed to shield you from potentially breaking +changes. + +For more information, checkout the Downshift `useCombobox` props +[documentation](https://github.com/downshift-js/downshift/tree/v9.0.7/src/hooks/useCombobox#basic-props) + +### Combobox `downshiftActions` + +The downshift action methods are made available through the `downshiftActions` +prop. While not recommended, this prop allows you to modify downshift's internal +state without having to fully control the component. + +**Use with caution:** calling these actions modifies the internal state of +downshift. It may conflict with or override the state management used within +Combobox. 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. + +#### `downshiftActions` Usage + +Provide a ref that will be mutated to contain an object of downshift action +functions. These can be called to change the internal state of the downshift +useCombobox hook. + +``` +const downshiftActions = useRef(); + +return ( + { + if (changes.selectedItem === null) { + downshiftActions?.current?.openMenu?.(); + return; + } + }, + }} + /> +); +``` + +For more information, checkout the Downshift `useCombobox` action functions +[documentation](https://github.com/downshift-js/downshift/tree/v9.0.7/src/hooks/useCombobox#actions) ## Placeholders and Labeling diff --git a/packages/react/src/components/ComboBox/ComboBox.stories.js b/packages/react/src/components/ComboBox/ComboBox.stories.js index 87fd1290a310..35383df2cdf3 100644 --- a/packages/react/src/components/ComboBox/ComboBox.stories.js +++ b/packages/react/src/components/ComboBox/ComboBox.stories.js @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import React from 'react'; +import React, { useRef } from 'react'; import { WithLayer } from '../../../.storybook/templates/WithLayer'; @@ -65,6 +65,42 @@ export default { }, }; +export const DownshiftActionsTest = () => { + const downshiftActions = useRef(); + + return ( +
+ {}} + id="carbon-combobox" + items={items} + itemToString={(item) => (item ? item.text : '')} + titleText="ComboBox title" + helperText="Combobox helper text" + downshiftActions={downshiftActions} + downshiftProps={{ + onStateChange: (changes) => { + console.log('onStateChange changes', changes); + + if (changes.selectedItem === null) { + downshiftActions?.current?.openMenu?.(); + return; + } + if (changes?.isOpen && changes?.inputValue === 'Option 1') { + downshiftActions?.current?.setInputValue?.(''); + return; + } + if (!changes?.isOpen && changes?.inputValue !== 'Option 1') { + downshiftActions?.current?.setInputValue?.('Option 1'); + return; + } + }, + }} + /> +
+ ); +}; + export const Default = () => (
disabled?: boolean; /** - * Additional props passed to Downshift. Use with caution: anything you define - * here overrides the components' internal handling of that prop. Downshift - * internals are subject to change, and in some cases they can not be shimmed - * to shield you from potentially breaking changes. + * 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>; + /** + * Provide a ref that will be mutated to contain an object of downshift + * action functions. These can be called to change the internal state of the + * downshift useCombobox hook. + * + * **Use with caution:** calling these actions modifies the internal state of + * downshift. It may conflict with or override the state management used within + * Combobox. 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. + */ + downshiftActions?: React.MutableRefObject< + UseComboboxActions | undefined + >; + /** * Provide helper text that is used alongside the control label for * additional help @@ -336,6 +354,7 @@ const ComboBox = forwardRef( className: containerClassName, direction = 'bottom', disabled = false, + downshiftActions, downshiftProps, helperText, id, @@ -590,17 +609,26 @@ const ComboBox = forwardRef( } const { + // Prop getters getInputProps, getItemProps, getLabelProps, getMenuProps, getToggleButtonProps, + + // State isOpen, highlightedIndex, - selectItem, selectedItem, - toggleMenu, + + // Actions + closeMenu, + openMenu, + reset, + selectItem, setHighlightedIndex, + setInputValue: downshiftSetInputValue, + toggleMenu, } = useCombobox({ items: filterItems(items, itemToString, inputValue), inputValue: inputValue, @@ -637,6 +665,31 @@ const ComboBox = forwardRef( ...downshiftProps, }); + useEffect(() => { + // Used to expose the downshift actions to consumers for use with downshiftProps + // An odd pattern, here we mutate the value stored in the ref provided from the consumer. + // A riff of https://gist.github.com/gaearon/1a018a023347fe1c2476073330cc5509 + if (downshiftActions) { + downshiftActions.current = { + closeMenu, + openMenu, + reset, + selectItem, + setHighlightedIndex, + setInputValue: downshiftSetInputValue, + toggleMenu, + }; + } + }, [ + closeMenu, + openMenu, + reset, + selectItem, + setHighlightedIndex, + downshiftSetInputValue, + toggleMenu, + ]); + const buttonProps = getToggleButtonProps({ disabled: disabled || readOnly, onClick: handleToggleClick(isOpen), @@ -729,7 +782,7 @@ const ComboBox = forwardRef( {...getInputProps({ 'aria-controls': isOpen ? undefined : menuProps.id, placeholder, - ref: { ...mergeRefs(textInput, ref) }, + ref: mergeRefs(textInput, ref), onKeyDown: ( event: KeyboardEvent & { preventDownshiftDefault: boolean; @@ -938,14 +991,30 @@ ComboBox.propTypes = { disabled: PropTypes.bool, /** - * Additional props passed to Downshift. Use with caution: anything you define - * here overrides the components' internal handling of that prop. Downshift - * internals are subject to change, and in some cases they can not be shimmed - * to shield you from potentially breaking changes. + * 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 as React.Validator< UseComboboxProps >, + + /** + * Provide a ref that will be mutated to contain an object of downshift + * action functions. These can be called to change the internal state of the + * downshift useCombobox hook. + * + * **Use with caution:** calling these actions modifies the internal state of + * downshift. It may conflict with or override the state management used within + * Combobox. 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. + */ + downshiftActions: PropTypes.exact({ current: PropTypes.any }), + /** * Provide helper text that is used alongside the control label for * additional help diff --git a/packages/react/src/components/Dropdown/Dropdown.tsx b/packages/react/src/components/Dropdown/Dropdown.tsx index e698437bc058..51109534666c 100644 --- a/packages/react/src/components/Dropdown/Dropdown.tsx +++ b/packages/react/src/components/Dropdown/Dropdown.tsx @@ -109,10 +109,12 @@ export interface DropdownProps disabled?: boolean; /** - * Additional props passed to Downshift. Use with caution: anything you define - * here overrides the components' internal handling of that prop. Downshift - * internals are subject to change, and in some cases they can not be shimmed - * to shield you from potentially breaking changes. + * 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>; @@ -662,10 +664,12 @@ Dropdown.propTypes = { disabled: PropTypes.bool, /** - * Additional props passed to Downshift. Use with caution: anything you define - * here overrides the components' internal handling of that prop. Downshift - * internals are subject to change, and in some cases they can not be shimmed - * to shield you from potentially breaking changes. + * 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 as React.Validator>, diff --git a/packages/react/src/components/FluidMultiSelect/FluidMultiSelect.js b/packages/react/src/components/FluidMultiSelect/FluidMultiSelect.js index 84555e94aaf3..8108aae6fa9a 100644 --- a/packages/react/src/components/FluidMultiSelect/FluidMultiSelect.js +++ b/packages/react/src/components/FluidMultiSelect/FluidMultiSelect.js @@ -69,10 +69,12 @@ FluidMultiSelect.propTypes = { disabled: PropTypes.bool, /** - * Additional props passed to Downshift. Use with caution: anything you define - * here overrides the components' internal handling of that prop. Downshift - * internals are subject to change, and in some cases they can not be shimmed - * to shield you from potentially breaking changes. + * 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, diff --git a/packages/react/src/components/MultiSelect/FilterableMultiSelect.tsx b/packages/react/src/components/MultiSelect/FilterableMultiSelect.tsx index 2c2fa1953c7c..575bd95d59ef 100644 --- a/packages/react/src/components/MultiSelect/FilterableMultiSelect.tsx +++ b/packages/react/src/components/MultiSelect/FilterableMultiSelect.tsx @@ -139,10 +139,12 @@ export interface FilterableMultiSelectProps disabled?: boolean; /** - * Additional props passed to Downshift. Use with caution: anything you define - * here overrides the components' internal handling of that prop. Downshift - * internals are subject to change, and in some cases they can not be shimmed - * to shield you from potentially breaking changes. + * 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?: UseMultipleSelectionProps; @@ -984,10 +986,12 @@ FilterableMultiSelect.propTypes = { disabled: PropTypes.bool, /** - * Additional props passed to Downshift. Use with caution: anything you define - * here overrides the components' internal handling of that prop. Downshift - * internals are subject to change, and in some cases they can not be shimmed - * to shield you from potentially breaking changes. + * 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. */ // @ts-ignore downshiftProps: PropTypes.shape(Downshift.propTypes), diff --git a/packages/react/src/components/MultiSelect/MultiSelect.tsx b/packages/react/src/components/MultiSelect/MultiSelect.tsx index 4290ed14e533..ee89bee18e61 100644 --- a/packages/react/src/components/MultiSelect/MultiSelect.tsx +++ b/packages/react/src/components/MultiSelect/MultiSelect.tsx @@ -136,10 +136,12 @@ export interface MultiSelectProps disabled?: ListBoxProps['disabled']; /** - * Additional props passed to Downshift. Use with caution: anything you define - * here overrides the components' internal handling of that prop. Downshift - * internals are subject to change, and in some cases they can not be shimmed - * to shield you from potentially breaking changes. + * 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>; @@ -854,10 +856,12 @@ MultiSelect.propTypes = { disabled: PropTypes.bool, /** - * Additional props passed to Downshift. Use with caution: anything you define - * here overrides the components' internal handling of that prop. Downshift - * internals are subject to change, and in some cases they can not be shimmed - * to shield you from potentially breaking changes. + * 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 as React.Validator>,