Skip to content

Commit

Permalink
feat(tag-dismissible): add decorator
Browse files Browse the repository at this point in the history
  • Loading branch information
ariellalgilmore committed Nov 15, 2024
1 parent 08d09d3 commit 7ca5ddc
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 31 deletions.
7 changes: 4 additions & 3 deletions packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2862,6 +2862,9 @@ Map {
"className": Object {
"type": "string",
},
"decorator": Object {
"type": "node",
},
"disabled": Object {
"type": "bool",
},
Expand Down Expand Up @@ -2894,9 +2897,7 @@ Map {
],
"type": "oneOf",
},
"slug": Object {
"type": "node",
},
"slug": [Function],
"tagTitle": Object {
"type": "string",
},
Expand Down
48 changes: 40 additions & 8 deletions packages/react/src/components/Tag/DismissibleTag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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
*/
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -83,6 +90,7 @@ export type DismissibleTagProps<T extends React.ElementType> = PolymorphicProps<

const DismissibleTag = <T extends React.ElementType>({
className,
decorator,
disabled,
id,
renderIcon,
Expand Down Expand Up @@ -114,12 +122,20 @@ const DismissibleTag = <T extends React.ElementType>({
}
};

let normalizedSlug;
if (slug && slug['type']?.displayName === 'AILabel') {
normalizedSlug = React.cloneElement(slug as React.ReactElement<any>, {
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<any>,
{
size: 'sm',
kind: 'inline',
}
);
}

const tooltipClasses = classNames(
Expand Down Expand Up @@ -149,7 +165,15 @@ const DismissibleTag = <T extends React.ElementType>({
className={`${prefix}--tag__label`}>
{text}
</Text>
{normalizedSlug}
{slug ? (
normalizedDecorator
) : decorator ? (
<div className={`${prefix}--tag__decorator`}>
{normalizedDecorator}
</div>
) : (
''
)}
<Tooltip
label={isEllipsisApplied ? dismissLabel : title}
align="bottom"
Expand All @@ -175,6 +199,11 @@ DismissibleTag.propTypes = {
*/
className: PropTypes.string,

/**
* **Experimental:** Provide a `decorator` component to be rendered inside the `DismissibleTag` component
*/
decorator: PropTypes.node,

/**
* Specify if the `DismissibleTag` is disabled
*/
Expand Down Expand Up @@ -205,7 +234,10 @@ DismissibleTag.propTypes = {
/**
* **Experimental:** Provide a `Slug` component to be rendered inside the `DismissibleTag` 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 rendered inside of a the tag.
Expand Down
32 changes: 32 additions & 0 deletions packages/react/src/components/Tag/Tag-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,38 @@ describe('Tag', () => {
// requirement
expect(accessibilityLabel).toEqual(expect.stringContaining('Close tag'));
});

it('should respect decorator prop', () => {
render(
<DismissibleTag
type="red"
title="Close tag"
text="Tag content"
decorator={<AILabel />}
/>
);

expect(
screen.getByRole('button', { name: 'AI - Show information' })
).toBeInTheDocument();
});

it('should respect deprecated slug prop', () => {
const spy = jest.spyOn(console, 'warn').mockImplementation(() => {});
render(
<DismissibleTag
type="red"
title="Close tag"
text="Tag content"
slug={<AILabel />}
/>
);

expect(
screen.getByRole('button', { name: 'AI - Show information' })
).toBeInTheDocument();
spy.mockRestore();
});
});

it('should allow for a custom label', () => {
Expand Down
33 changes: 13 additions & 20 deletions packages/react/src/components/Tag/Tag.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -227,14 +228,12 @@ export const withAILabel = () => (
{'Tag'}
</Tag>

<Tag
filter
<DismissibleTag
decorator={aiLabel}
className="some-class"
type="purple"
title="Clear Filter">
{'Tag'}
</Tag>
title="Clear Filter"
text="Tag"></DismissibleTag>

<Tag
renderIcon={Asleep}
Expand All @@ -245,15 +244,13 @@ export const withAILabel = () => (
{'Tag'}
</Tag>

<Tag
filter
<DismissibleTag
renderIcon={Asleep}
decorator={aiLabel}
className="some-class"
type="green"
title="Clear Filter">
{'Tag'}
</Tag>
title="Clear Filter"
text="Tag"></DismissibleTag>
<Tag
decorator={
<Tooltip label={'hello'}>
Expand All @@ -266,18 +263,16 @@ export const withAILabel = () => (
{'Tag'}
</Tag>

<Tag
filter
<DismissibleTag
decorator={
<Tooltip label={'hello'}>
<Information />
</Tooltip>
}
className="some-class"
type="purple"
title="Clear Filter">
{'Tag'}
</Tag>
title="Clear Filter"
text="Tag"></DismissibleTag>

<Tag
renderIcon={Asleep}
Expand All @@ -292,8 +287,7 @@ export const withAILabel = () => (
{'Tag'}
</Tag>

<Tag
filter
<DismissibleTag
renderIcon={Asleep}
decorator={
<Tooltip label={'hello'}>
Expand All @@ -302,8 +296,7 @@ export const withAILabel = () => (
}
className="some-class"
type="green"
title="Clear Filter">
{'Tag'}
</Tag>
title="Clear Filter"
text="Tag"></DismissibleTag>
</div>
);

0 comments on commit 7ca5ddc

Please sign in to comment.