From c52884cfc64972d75e133a698417e8d6dab588a3 Mon Sep 17 00:00:00 2001 From: Alison Joseph Date: Thu, 21 Nov 2024 09:07:56 -0600 Subject: [PATCH] feat: replace slug with decorator ComboBox, MultiSelect, FilterableMultiSelect (#18069) * feat(combobox): replace slug prop with decorator * chore: update tests * feat: add decorator prop to multiselect * feat: add decorator prop to filterablemultiselect * chore: format and snapshot * chore: update form story * feat: add with ailabel stories to fluid and form stories * chore:format --------- Co-authored-by: Guilherme Datilio Ribeiro --- .../__snapshots__/PublicAPI-test.js.snap | 28 +++++----- .../src/components/ComboBox/ComboBox-test.js | 12 +++++ .../components/ComboBox/ComboBox.stories.js | 2 +- .../src/components/ComboBox/ComboBox.tsx | 51 +++++++++++++++---- .../FluidComboBox/FluidComboBox.stories.js | 50 +++++++++++++++++- .../FluidDropdown/FluidDropdown.stories.js | 50 +++++++++++++++++- .../FluidMultiSelect.stories.js | 51 ++++++++++++++++++- .../react/src/components/Form/Form.stories.js | 12 ++--- .../MultiSelect/FilterableMultiSelect.tsx | 51 +++++++++++++++---- .../MultiSelect/MultiSelect.stories.js | 5 +- .../components/MultiSelect/MultiSelect.tsx | 51 +++++++++++++++---- .../__tests__/FilterableMultiSelect-test.js | 13 +++++ .../MultiSelect/__tests__/MultiSelect-test.js | 20 ++++++++ 13 files changed, 338 insertions(+), 58 deletions(-) diff --git a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap index 4e35619514b3..138dc96a9985 100644 --- a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap +++ b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap @@ -1265,6 +1265,9 @@ Map { "className": Object { "type": "string", }, + "decorator": Object { + "type": "node", + }, "direction": Object { "args": Array [ Array [ @@ -1375,9 +1378,7 @@ Map { ], "type": "oneOf", }, - "slug": Object { - "type": "node", - }, + "slug": [Function], "titleText": Object { "type": "node", }, @@ -3559,6 +3560,9 @@ Map { "compareItems": Object { "type": "func", }, + "decorator": Object { + "type": "node", + }, "direction": Object { "args": Array [ Array [ @@ -3773,9 +3777,7 @@ Map { ], "type": "oneOf", }, - "slug": Object { - "type": "node", - }, + "slug": [Function], "sortItems": Object { "type": "func", }, @@ -5240,6 +5242,9 @@ Map { "compareItems": Object { "type": "func", }, + "decorator": Object { + "type": "node", + }, "direction": Object { "args": Array [ Array [ @@ -5454,9 +5459,7 @@ Map { ], "type": "oneOf", }, - "slug": Object { - "type": "node", - }, + "slug": [Function], "sortItems": Object { "type": "func", }, @@ -5494,6 +5497,9 @@ Map { "compareItems": Object { "type": "func", }, + "decorator": Object { + "type": "node", + }, "direction": Object { "args": Array [ Array [ @@ -5581,9 +5587,7 @@ Map { ], "type": "oneOf", }, - "slug": Object { - "type": "node", - }, + "slug": [Function], "sortItems": Object { "type": "func", }, diff --git a/packages/react/src/components/ComboBox/ComboBox-test.js b/packages/react/src/components/ComboBox/ComboBox-test.js index 15576ee7c555..36bca1f45cbb 100644 --- a/packages/react/src/components/ComboBox/ComboBox-test.js +++ b/packages/react/src/components/ComboBox/ComboBox-test.js @@ -285,6 +285,7 @@ describe('ComboBox', () => { }); it('should respect slug prop', async () => { + const spy = jest.spyOn(console, 'warn').mockImplementation(() => {}); const { container } = render( } /> ); @@ -292,6 +293,17 @@ describe('ComboBox', () => { expect(container.firstChild).toHaveClass( `${prefix}--list-box__wrapper--slug` ); + spy.mockRestore(); + }); + + it('should respect decorator prop', async () => { + const { container } = render( + } /> + ); + await waitForPosition(); + expect(container.firstChild).toHaveClass( + `${prefix}--list-box__wrapper--decorator` + ); }); describe('should display initially selected item found in `initialSelectedItem`', () => { diff --git a/packages/react/src/components/ComboBox/ComboBox.stories.js b/packages/react/src/components/ComboBox/ComboBox.stories.js index 3a355fae4d27..451ace723902 100644 --- a/packages/react/src/components/ComboBox/ComboBox.stories.js +++ b/packages/react/src/components/ComboBox/ComboBox.stories.js @@ -197,7 +197,7 @@ export const withAILabel = () => ( itemToString={(item) => (item ? item.text : '')} titleText="ComboBox title" helperText="Combobox helper text" - slug={aiLabel} + decorator={aiLabel} /> ); diff --git a/packages/react/src/components/ComboBox/ComboBox.tsx b/packages/react/src/components/ComboBox/ComboBox.tsx index 1283e740e568..dd49efd7c52c 100644 --- a/packages/react/src/components/ComboBox/ComboBox.tsx +++ b/packages/react/src/components/ComboBox/ComboBox.tsx @@ -204,6 +204,11 @@ export interface ComboBoxProps */ className?: string; + /** + * **Experimental**: Provide a `decorator` component to be rendered inside the `ComboBox` component + */ + decorator?: ReactNode; + /** * Specify the direction of the combobox dropdown. Can be either top or bottom. */ @@ -349,6 +354,7 @@ export interface ComboBoxProps size?: ListBoxSize; /** + * @deprecated please use decorator instead. * **Experimental**: Provide a `Slug` component to be rendered inside the `ComboBox` component */ slug?: ReactNode; @@ -388,6 +394,7 @@ const ComboBox = forwardRef( ariaLabel: deprecatedAriaLabel, autoAlign = false, className: containerClassName, + decorator, direction = 'bottom', disabled = false, downshiftActions, @@ -694,6 +701,7 @@ const ComboBox = forwardRef( [`${prefix}--list-box__wrapper--fluid--invalid`]: isFluid && invalid, [`${prefix}--list-box__wrapper--fluid--focus`]: isFluid && isFocused, [`${prefix}--list-box__wrapper--slug`]: slug, + [`${prefix}--list-box__wrapper--decorator`]: decorator, }, ]); @@ -705,12 +713,20 @@ const ComboBox = forwardRef( // needs to be Capitalized for react to render it correctly const ItemToElement = itemToElement; - // Slug is always size `mini` - let normalizedSlug; - if (slug && slug['type']?.displayName === 'AILabel') { - normalizedSlug = React.cloneElement(slug as React.ReactElement, { - size: 'mini', - }); + // AILabel always size `mini` + let normalizedDecorator = React.isValidElement(slug ?? decorator) + ? (slug ?? decorator) + : null; + if ( + normalizedDecorator && + normalizedDecorator['type']?.displayName === 'AILabel' + ) { + normalizedDecorator = React.cloneElement( + normalizedDecorator as React.ReactElement, + { + size: 'mini', + } + ); } const { @@ -1038,7 +1054,15 @@ const ComboBox = forwardRef( translateWithId={translateWithId} /> - {normalizedSlug} + {slug ? ( + normalizedDecorator + ) : decorator ? ( +
+ {normalizedDecorator} +
+ ) : ( + '' + )} {isOpen ? filterItems(items, itemToString, inputValue).map( @@ -1133,6 +1157,11 @@ ComboBox.propTypes = { */ className: PropTypes.string, + /** + * **Experimental**: Provide a decorator component to be rendered inside the `ComboBox` component + */ + decorator: PropTypes.node, + /** * Specify the direction of the combobox dropdown. Can be either top or bottom. */ @@ -1283,10 +1312,10 @@ ComboBox.propTypes = { */ size: ListBoxPropTypes.ListBoxSize, - /** - * **Experimental**: Provide a `Slug` component to be rendered inside the `ComboBox` component - */ - slug: PropTypes.node, + slug: deprecate( + PropTypes.node, + 'The `slug` prop has been deprecated and will be removed in the next major version. Use the decorator prop instead.' + ), /** * Provide text to be used in a `