Skip to content

Commit 874e1df

Browse files
sojinantony01heloiselui
authored andcommitted
fix: controlled tag component for selected prop carbon-design-system#19228 (carbon-design-system#19231)
* fix: controlled tag component for selected prop carbon-design-system#19228 * fix: fix tests for tag * fix: controlled tag component for selected prop carbon-design-system#19228 * fix: controlled tag added a default selected prop and tests carbon-design-system#19228 * fix: controlled tag added a default selected prop and tests carbon-design-system#19228 * fix: controlled tag added a default selected prop and tests carbon-design-system#19228 * fix: controlled tag fix tests carbon-design-system#19228 * fix: add profile * fix: add profile
1 parent 3e72c17 commit 874e1df

File tree

5 files changed

+95
-4
lines changed

5 files changed

+95
-4
lines changed

.all-contributorsrc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1875,6 +1875,15 @@
18751875
"contributions": [
18761876
"code"
18771877
]
1878+
},
1879+
{
1880+
"login": "sojinantony01",
1881+
"name": "Sojin antony",
1882+
"avatar_url": "https://avatars.githubusercontent.com/u/29255847?v=4",
1883+
"profile": "https://github.com/sojinantony01",
1884+
"contributions": [
1885+
"code"
1886+
]
18781887
}
18791888
],
18801889
"commitConvention": "none"

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,10 @@ check out our [Contributing Guide](/.github/CONTRIBUTING.md) and our
338338
<td align="center"><a href="https://github.com/vcherneny"><img src="https://avatars.githubusercontent.com/u/11604315?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vlad Cherneny</b></sub></a><br /><a href="https://github.com/carbon-design-system/carbon/commits?author=vcherneny" title="Code">💻</a></td>
339339
<td align="center"><a href="https://github.com/murito"><img src="https://avatars.githubusercontent.com/u/2628140?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Francisco Alcalá</b></sub></a><br /><a href="https://github.com/carbon-design-system/carbon/commits?author=murito" title="Code">💻</a></td>
340340
<td align="center"><a href="https://www.linkedin.com/in/sujithcs"><img src="https://avatars.githubusercontent.com/u/43125517?v=4?s=100" width="100px;" alt=""/><br /><sub><b>SUJITH C S</b></sub></a><br /><a href="https://github.com/carbon-design-system/carbon/commits?author=Code-Suji" title="Code">💻</a></td>
341+
</tr>
342+
<tr>
341343
<td align="center"><a href="https://github.com/mariyageorge01"><img src="https://avatars.githubusercontent.com/u/166684108?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mariya George</b></sub></a><br /><a href="https://github.com/carbon-design-system/carbon/commits?author=mariyageorge01" title="Code">💻</a></td>
344+
<td align="center"><a href="https://github.com/sojinantony01"><img src="https://avatars.githubusercontent.com/u/29255847?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sojin antony</b></sub></a><br /><a href="https://github.com/carbon-design-system/carbon/commits?author=sojinantony01" title="Code">💻</a></td>
342345
</tr>
343346
</table>
344347

packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7063,6 +7063,9 @@ Map {
70637063
"className": Object {
70647064
"type": "string",
70657065
},
7066+
"defaultSelected": Object {
7067+
"type": "bool",
7068+
},
70667069
"disabled": Object {
70677070
"type": "bool",
70687071
},

packages/react/src/components/Tag/SelectableTag.tsx

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { Tooltip } from '../Tooltip';
2323
import { Text } from '../Text';
2424
import { isEllipsisActive } from './isEllipsisActive';
2525
import mergeRefs from '../../tools/mergeRefs';
26+
import { useControllableState } from '../../internal/useControllableState';
2627
export interface SelectableTagBaseProps {
2728
/**
2829
* Provide a custom className that is applied to the containing <span>
@@ -59,6 +60,11 @@ export interface SelectableTagBaseProps {
5960
*/
6061
selected?: boolean;
6162

63+
/**
64+
* Specify the default state of the selectable tag.
65+
*/
66+
defaultSelected?: boolean;
67+
6268
/**
6369
* Specify the size of the Tag. Currently supports either `sm`,
6470
* `md` (default) or `lg` sizes.
@@ -85,17 +91,22 @@ const SelectableTag = forwardRef(
8591
renderIcon,
8692
onChange,
8793
onClick,
88-
selected = false,
94+
selected,
8995
size,
9096
text,
97+
defaultSelected = false,
9198
...other
9299
}: SelectableTagProps<T>,
93100
forwardRef: ForwardedRef<HTMLButtonElement>
94101
) => {
95102
const prefix = usePrefix();
96103
const tagRef = useRef<HTMLButtonElement>(null);
97104
const tagId = id || `tag-${useId()}`;
98-
const [selectedTag, setSelectedTag] = useState(selected);
105+
const [selectedTag, setSelectedTag] = useControllableState({
106+
value: selected,
107+
onChange: onChange,
108+
defaultValue: defaultSelected,
109+
});
99110
const tagClasses = classNames(`${prefix}--tag--selectable`, className, {
100111
[`${prefix}--tag--selectable-selected`]: selectedTag,
101112
});
@@ -116,7 +127,6 @@ const SelectableTag = forwardRef(
116127

117128
const handleClick = (e: MouseEvent<HTMLButtonElement>) => {
118129
setSelectedTag(!selectedTag);
119-
onChange?.(!selectedTag);
120130
onClick?.(e);
121131
};
122132

@@ -201,6 +211,11 @@ SelectableTag.propTypes = {
201211
*/
202212
selected: PropTypes.bool,
203213

214+
/**
215+
* Specify the default state of the selectable tag.
216+
*/
217+
defaultSelected: PropTypes.bool,
218+
204219
/**
205220
* Specify the size of the Tag. Currently supports either `sm`,
206221
* `md` (default) or `lg` sizes.

packages/react/src/components/Tag/Tag-test.js

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright IBM Corp. 2016, 2023
2+
* Copyright IBM Corp. 2016, 2025
33
*
44
* This source code is licensed under the Apache-2.0 license found in the
55
* LICENSE file in the root directory of this source tree.
@@ -308,4 +308,65 @@ describe('Tag', () => {
308308
render(<SelectableTag type="red" text="Test Tag" ref={ref} />);
309309
expect(ref.current).toBeInstanceOf(HTMLButtonElement);
310310
});
311+
it('Controlled selectable tag', () => {
312+
const ref = React.createRef();
313+
314+
const { rerender } = render(
315+
<SelectableTag type="red" text="Test Tag" ref={ref} selected={true} />
316+
);
317+
318+
expect(ref.current).toBeInstanceOf(HTMLButtonElement);
319+
expect(screen.getByRole('button', { name: 'Test Tag' })).toHaveAttribute(
320+
'aria-pressed',
321+
'true'
322+
);
323+
324+
rerender(
325+
<SelectableTag type="red" text="Test Tag" ref={ref} selected={false} />
326+
);
327+
328+
expect(screen.getByRole('button', { name: 'Test Tag' })).toHaveAttribute(
329+
'aria-pressed',
330+
'false'
331+
);
332+
});
333+
it('Controlled selectable tag, should call onChange', async () => {
334+
const onChange = jest.fn();
335+
336+
render(
337+
<SelectableTag text="Tag content" onChange={onChange} selected={true} />
338+
);
339+
340+
const selectableTag = screen.getByRole('button', { name: 'Tag content' });
341+
await userEvent.click(selectableTag);
342+
expect(onChange).toHaveBeenCalledWith(false);
343+
});
344+
it('Controlled selectable tag should be selected by default if defaultSelected is true', () => {
345+
const onChange = jest.fn();
346+
347+
render(
348+
<SelectableTag
349+
text="Tag content"
350+
onChange={onChange}
351+
defaultSelected={true}
352+
/>
353+
);
354+
355+
const selectableTag = screen.getByRole('button', { name: 'Tag content' });
356+
expect(selectableTag).toHaveAttribute('aria-pressed', 'true');
357+
});
358+
it('Controlled selectable tag should not be be selected by default if defaultSelected is false', () => {
359+
const onChange = jest.fn();
360+
361+
render(
362+
<SelectableTag
363+
text="Tag content"
364+
onChange={onChange}
365+
defaultSelected={false}
366+
/>
367+
);
368+
369+
const selectableTag = screen.getByRole('button', { name: 'Tag content' });
370+
expect(selectableTag).toHaveAttribute('aria-pressed', 'false');
371+
});
311372
});

0 commit comments

Comments
 (0)