From 7ca5ddc14751d593f2827796f113b50721a3faf3 Mon Sep 17 00:00:00 2001 From: ariellalgilmore Date: Fri, 15 Nov 2024 10:41:39 -0800 Subject: [PATCH] feat(tag-dismissible): add decorator --- .../__snapshots__/PublicAPI-test.js.snap | 7 +-- .../src/components/Tag/DismissibleTag.tsx | 48 +++++++++++++++---- packages/react/src/components/Tag/Tag-test.js | 32 +++++++++++++ .../react/src/components/Tag/Tag.stories.js | 33 +++++-------- 4 files changed, 89 insertions(+), 31 deletions(-) diff --git a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap index 9c22f56f7a4c..5ad3161f7696 100644 --- a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap +++ b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap @@ -2862,6 +2862,9 @@ Map { "className": Object { "type": "string", }, + "decorator": Object { + "type": "node", + }, "disabled": Object { "type": "bool", }, @@ -2894,9 +2897,7 @@ Map { ], "type": "oneOf", }, - "slug": Object { - "type": "node", - }, + "slug": [Function], "tagTitle": Object { "type": "string", }, diff --git a/packages/react/src/components/Tag/DismissibleTag.tsx b/packages/react/src/components/Tag/DismissibleTag.tsx index eb68a9b6a41d..4d82dfed129b 100644 --- a/packages/react/src/components/Tag/DismissibleTag.tsx +++ b/packages/react/src/components/Tag/DismissibleTag.tsx @@ -11,6 +11,7 @@ import classNames from 'classnames'; import { useId } from '../../internal/useId'; import { usePrefix } from '../../internal/usePrefix'; import { PolymorphicProps } from '../../types/common'; +import deprecate from '../../prop-types/deprecate'; import Tag, { SIZES, TYPES } from './Tag'; import { Close } from '@carbon/icons-react'; import { Tooltip } from '../Tooltip'; @@ -23,6 +24,11 @@ export interface DismissibleTagBaseProps { */ className?: string; + /** + * **Experimental:** Provide a `decorator` component to be rendered inside the `DismissibleTag` component + */ + decorator?: ReactNode; + /** * Specify if the `DismissibleTag` is disabled */ @@ -51,6 +57,7 @@ export interface DismissibleTagBaseProps { size?: keyof typeof SIZES; /** + * @deprecated please use `decorator` instead. * **Experimental:** Provide a `Slug` component to be rendered inside the `DismissibleTag` component */ slug?: ReactNode; @@ -83,6 +90,7 @@ export type DismissibleTagProps = PolymorphicProps< const DismissibleTag = ({ className, + decorator, disabled, id, renderIcon, @@ -114,12 +122,20 @@ const DismissibleTag = ({ } }; - let normalizedSlug; - if (slug && slug['type']?.displayName === 'AILabel') { - normalizedSlug = React.cloneElement(slug as React.ReactElement, { - size: 'sm', - kind: 'inline', - }); + let normalizedDecorator = React.isValidElement(slug ?? decorator) + ? slug ?? decorator + : null; + if ( + normalizedDecorator && + normalizedDecorator['type']?.displayName === 'AILabel' + ) { + normalizedDecorator = React.cloneElement( + normalizedDecorator as React.ReactElement, + { + size: 'sm', + kind: 'inline', + } + ); } const tooltipClasses = classNames( @@ -149,7 +165,15 @@ const DismissibleTag = ({ className={`${prefix}--tag__label`}> {text} - {normalizedSlug} + {slug ? ( + normalizedDecorator + ) : decorator ? ( +
+ {normalizedDecorator} +
+ ) : ( + '' + )} { // requirement expect(accessibilityLabel).toEqual(expect.stringContaining('Close tag')); }); + + it('should respect decorator prop', () => { + render( + } + /> + ); + + expect( + screen.getByRole('button', { name: 'AI - Show information' }) + ).toBeInTheDocument(); + }); + + it('should respect deprecated slug prop', () => { + const spy = jest.spyOn(console, 'warn').mockImplementation(() => {}); + render( + } + /> + ); + + expect( + screen.getByRole('button', { name: 'AI - Show information' }) + ).toBeInTheDocument(); + spy.mockRestore(); + }); }); it('should allow for a custom label', () => { diff --git a/packages/react/src/components/Tag/Tag.stories.js b/packages/react/src/components/Tag/Tag.stories.js index d14e4eb9286b..b95befd4c409 100644 --- a/packages/react/src/components/Tag/Tag.stories.js +++ b/packages/react/src/components/Tag/Tag.stories.js @@ -8,6 +8,7 @@ import React from 'react'; import { default as Tag } from '../Tag'; import TagSkeleton from '../Tag/Tag.Skeleton'; +import DismissibleTag from '../Tag/DismissibleTag'; import { Asleep, View, FolderOpen, Folders } from '@carbon/icons-react'; import Button from '../Button'; import { AILabel, AILabelContent, AILabelActions } from '../AILabel'; @@ -227,14 +228,12 @@ export const withAILabel = () => ( {'Tag'} - - {'Tag'} - + title="Clear Filter" + text="Tag"> ( {'Tag'} - - {'Tag'} - + title="Clear Filter" + text="Tag"> @@ -266,8 +263,7 @@ export const withAILabel = () => ( {'Tag'} - @@ -275,9 +271,8 @@ export const withAILabel = () => ( } className="some-class" type="purple" - title="Clear Filter"> - {'Tag'} - + title="Clear Filter" + text="Tag"> ( {'Tag'} - @@ -302,8 +296,7 @@ export const withAILabel = () => ( } className="some-class" type="green" - title="Clear Filter"> - {'Tag'} - + title="Clear Filter" + text="Tag"> );