From d0396dcade2661ae1fc7906a03ff9c2c0db59126 Mon Sep 17 00:00:00 2001 From: Sojin Antony Date: Thu, 24 Apr 2025 19:38:27 +0530 Subject: [PATCH 1/9] fix: controlled tag component for selected prop #19228 --- .../react/src/components/Tag/SelectableTag.tsx | 7 +++++++ packages/react/src/components/Tag/Tag-test.js | 17 ++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/packages/react/src/components/Tag/SelectableTag.tsx b/packages/react/src/components/Tag/SelectableTag.tsx index d358528325e2..30cacd71f7ea 100644 --- a/packages/react/src/components/Tag/SelectableTag.tsx +++ b/packages/react/src/components/Tag/SelectableTag.tsx @@ -13,6 +13,7 @@ import React, { MouseEvent, forwardRef, ForwardedRef, + useEffect, } from 'react'; import classNames from 'classnames'; import { useId } from '../../internal/useId'; @@ -101,6 +102,12 @@ const SelectableTag = forwardRef( }); const [isEllipsisApplied, setIsEllipsisApplied] = useState(false); + useEffect(() => { + if (selected !== selectedTag) { + setSelectedTag(selected); + } + }, [selected]); + useLayoutEffect(() => { const newElement = tagRef.current?.getElementsByClassName( `${prefix}--tag__label` diff --git a/packages/react/src/components/Tag/Tag-test.js b/packages/react/src/components/Tag/Tag-test.js index e9d845856291..b4f836edcda3 100644 --- a/packages/react/src/components/Tag/Tag-test.js +++ b/packages/react/src/components/Tag/Tag-test.js @@ -1,5 +1,5 @@ /** - * Copyright IBM Corp. 2016, 2023 + * Copyright IBM Corp. 2016, 2025 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. @@ -308,4 +308,19 @@ describe('Tag', () => { render(); expect(ref.current).toBeInstanceOf(HTMLButtonElement); }); + it('Controlled selected tag', () => { + const ref = React.createRef(); + const { rerender } = render( + + ); + expect(ref.current).toBeInstanceOf(HTMLButtonElement); + expect(selectableTag).toHaveClass(`${prefix}--tag--selectable-selected`); + + rerender( + + ); + expect(selectableTag).container.toHaveClass( + `${prefix}--tag--selectable-selected` + ); + }); }); From a756377f49637087f1b2e20a9a1c38ed57c6d672 Mon Sep 17 00:00:00 2001 From: Sojin Antony Date: Thu, 24 Apr 2025 22:25:53 +0530 Subject: [PATCH 2/9] fix: fix tests for tag --- packages/react/src/components/Tag/Tag-test.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/react/src/components/Tag/Tag-test.js b/packages/react/src/components/Tag/Tag-test.js index b4f836edcda3..b03dc2858b44 100644 --- a/packages/react/src/components/Tag/Tag-test.js +++ b/packages/react/src/components/Tag/Tag-test.js @@ -310,16 +310,19 @@ describe('Tag', () => { }); it('Controlled selected tag', () => { const ref = React.createRef(); - const { rerender } = render( + const { rerender, container } = render( ); + expect(ref.current).toBeInstanceOf(HTMLButtonElement); - expect(selectableTag).toHaveClass(`${prefix}--tag--selectable-selected`); + expect(container.firstChild).toHaveClass( + `${prefix}--tag--selectable-selected` + ); rerender( ); - expect(selectableTag).container.toHaveClass( + expect(container.firstChild).not.toHaveClass( `${prefix}--tag--selectable-selected` ); }); From e23b516f3d85115d7998df3e391e48b24532c0ce Mon Sep 17 00:00:00 2001 From: Sojin Antony Date: Thu, 1 May 2025 09:51:32 +0530 Subject: [PATCH 3/9] fix: controlled tag component for selected prop #19228 --- .../react/src/components/Tag/SelectableTag.tsx | 16 +++++++--------- packages/react/src/components/Tag/Tag-test.js | 14 +++++++++++++- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/packages/react/src/components/Tag/SelectableTag.tsx b/packages/react/src/components/Tag/SelectableTag.tsx index 30cacd71f7ea..cf3b1331db41 100644 --- a/packages/react/src/components/Tag/SelectableTag.tsx +++ b/packages/react/src/components/Tag/SelectableTag.tsx @@ -24,6 +24,7 @@ import { Tooltip } from '../Tooltip'; import { Text } from '../Text'; import { isEllipsisActive } from './isEllipsisActive'; import mergeRefs from '../../tools/mergeRefs'; +import { useControllableState } from '../../internal/useControllableState'; export interface SelectableTagBaseProps { /** * Provide a custom className that is applied to the containing @@ -86,7 +87,7 @@ const SelectableTag = forwardRef( renderIcon, onChange, onClick, - selected = false, + selected, size, text, ...other @@ -96,18 +97,16 @@ const SelectableTag = forwardRef( const prefix = usePrefix(); const tagRef = useRef(null); const tagId = id || `tag-${useId()}`; - const [selectedTag, setSelectedTag] = useState(selected); + const [selectedTag, setSelectedTag] = useControllableState({ + value: selected, + onChange: onChange, + defaultValue: false, + }); const tagClasses = classNames(`${prefix}--tag--selectable`, className, { [`${prefix}--tag--selectable-selected`]: selectedTag, }); const [isEllipsisApplied, setIsEllipsisApplied] = useState(false); - useEffect(() => { - if (selected !== selectedTag) { - setSelectedTag(selected); - } - }, [selected]); - useLayoutEffect(() => { const newElement = tagRef.current?.getElementsByClassName( `${prefix}--tag__label` @@ -123,7 +122,6 @@ const SelectableTag = forwardRef( const handleClick = (e: MouseEvent) => { setSelectedTag(!selectedTag); - onChange?.(!selectedTag); onClick?.(e); }; diff --git a/packages/react/src/components/Tag/Tag-test.js b/packages/react/src/components/Tag/Tag-test.js index b03dc2858b44..a22e74aa84d5 100644 --- a/packages/react/src/components/Tag/Tag-test.js +++ b/packages/react/src/components/Tag/Tag-test.js @@ -308,7 +308,7 @@ describe('Tag', () => { render(); expect(ref.current).toBeInstanceOf(HTMLButtonElement); }); - it('Controlled selected tag', () => { + it('Controlled selectable tag', () => { const ref = React.createRef(); const { rerender, container } = render( @@ -326,4 +326,16 @@ describe('Tag', () => { `${prefix}--tag--selectable-selected` ); }); + it('Controlled selectable tag, should call onChange', async () => { + const onChange = jest.fn(); + + const { container } = render( + + ); + + const selectableTag = container.firstChild; + + await userEvent.click(selectableTag); + expect(onChange).toHaveBeenCalledWith(false); + }); }); From 08750825fffc9d77d1dd3c21476d6e9029aa2900 Mon Sep 17 00:00:00 2001 From: Sojin Antony Date: Tue, 6 May 2025 11:23:48 +0530 Subject: [PATCH 4/9] fix: controlled tag added a default selected prop and tests #19228 --- .../src/components/Tag/SelectableTag.tsx | 13 +++++- packages/react/src/components/Tag/Tag-test.js | 46 ++++++++++++++++--- 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/packages/react/src/components/Tag/SelectableTag.tsx b/packages/react/src/components/Tag/SelectableTag.tsx index cf3b1331db41..081bdd4ea901 100644 --- a/packages/react/src/components/Tag/SelectableTag.tsx +++ b/packages/react/src/components/Tag/SelectableTag.tsx @@ -61,6 +61,11 @@ export interface SelectableTagBaseProps { */ selected?: boolean; + /** + * Specify the default state of the selectable tag. + */ + defaultSelected?: boolean; + /** * Specify the size of the Tag. Currently supports either `sm`, * `md` (default) or `lg` sizes. @@ -90,6 +95,7 @@ const SelectableTag = forwardRef( selected, size, text, + defaultSelected = false, ...other }: SelectableTagProps, forwardRef: ForwardedRef @@ -100,7 +106,7 @@ const SelectableTag = forwardRef( const [selectedTag, setSelectedTag] = useControllableState({ value: selected, onChange: onChange, - defaultValue: false, + defaultValue: defaultSelected, }); const tagClasses = classNames(`${prefix}--tag--selectable`, className, { [`${prefix}--tag--selectable-selected`]: selectedTag, @@ -206,6 +212,11 @@ SelectableTag.propTypes = { */ selected: PropTypes.bool, + /** + * Specify the default state of the selectable tag. + */ + defaultSelected: PropTypes.bool, + /** * Specify the size of the Tag. Currently supports either `sm`, * `md` (default) or `lg` sizes. diff --git a/packages/react/src/components/Tag/Tag-test.js b/packages/react/src/components/Tag/Tag-test.js index a22e74aa84d5..c42c4fb230dc 100644 --- a/packages/react/src/components/Tag/Tag-test.js +++ b/packages/react/src/components/Tag/Tag-test.js @@ -310,32 +310,64 @@ describe('Tag', () => { }); it('Controlled selectable tag', () => { const ref = React.createRef(); - const { rerender, container } = render( + const { rerender } = render( ); expect(ref.current).toBeInstanceOf(HTMLButtonElement); - expect(container.firstChild).toHaveClass( - `${prefix}--tag--selectable-selected` + expect(screen.getByRole('button', { name: 'Test Tag' })).toHaveAttribute( + 'aria-pressed', + 'true' ); rerender( ); - expect(container.firstChild).not.toHaveClass( - `${prefix}--tag--selectable-selected` + expect(screen.getByRole('button', { name: 'Test Tag' })).toHaveAttribute( + 'aria-pressed', + 'false' ); }); it('Controlled selectable tag, should call onChange', async () => { const onChange = jest.fn(); - const { container } = render( + render( ); - const selectableTag = container.firstChild; + const selectableTag = screen.getByRole('button', { name: 'Tag content' }); await userEvent.click(selectableTag); expect(onChange).toHaveBeenCalledWith(false); }); + it('Controlled selectable tag should be selected by default if defaultSelected is true', () => { + const onChange = jest.fn(); + + render( + + ); + + const selectableTag = screen.getByRole('button', { name: 'Tag content' }); + + expect(selectableTag).toHaveAttribute('aria-pressed', 'true'); + }); + it('Controlled selectable tag should not be be selected by default if defaultSelected is false', () => { + const onChange = jest.fn(); + + render( + + ); + + const selectableTag = screen.getByRole('button', { name: 'Tag content' }); + + expect(selectableTag).toHaveAttribute('aria-pressed', 'false'); + }); }); From b28b76f354c26c36e6abae35c65c728f340ed7d1 Mon Sep 17 00:00:00 2001 From: Sojin Antony Date: Tue, 6 May 2025 11:26:38 +0530 Subject: [PATCH 5/9] fix: controlled tag added a default selected prop and tests #19228 --- packages/react/src/components/Tag/Tag-test.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/react/src/components/Tag/Tag-test.js b/packages/react/src/components/Tag/Tag-test.js index c42c4fb230dc..c7e8e9baf574 100644 --- a/packages/react/src/components/Tag/Tag-test.js +++ b/packages/react/src/components/Tag/Tag-test.js @@ -310,6 +310,7 @@ describe('Tag', () => { }); it('Controlled selectable tag', () => { const ref = React.createRef(); + const { rerender } = render( ); @@ -336,7 +337,6 @@ describe('Tag', () => { ); const selectableTag = screen.getByRole('button', { name: 'Tag content' }); - await userEvent.click(selectableTag); expect(onChange).toHaveBeenCalledWith(false); }); @@ -352,7 +352,6 @@ describe('Tag', () => { ); const selectableTag = screen.getByRole('button', { name: 'Tag content' }); - expect(selectableTag).toHaveAttribute('aria-pressed', 'true'); }); it('Controlled selectable tag should not be be selected by default if defaultSelected is false', () => { @@ -367,7 +366,6 @@ describe('Tag', () => { ); const selectableTag = screen.getByRole('button', { name: 'Tag content' }); - expect(selectableTag).toHaveAttribute('aria-pressed', 'false'); }); }); From 58a5600655f4df6d2dc2f8dbb66c98f6fbef8b75 Mon Sep 17 00:00:00 2001 From: Sojin Antony Date: Tue, 6 May 2025 21:48:54 +0530 Subject: [PATCH 6/9] fix: controlled tag added a default selected prop and tests #19228 --- packages/react/src/components/Tag/SelectableTag.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react/src/components/Tag/SelectableTag.tsx b/packages/react/src/components/Tag/SelectableTag.tsx index 081bdd4ea901..6c6320527269 100644 --- a/packages/react/src/components/Tag/SelectableTag.tsx +++ b/packages/react/src/components/Tag/SelectableTag.tsx @@ -13,7 +13,6 @@ import React, { MouseEvent, forwardRef, ForwardedRef, - useEffect, } from 'react'; import classNames from 'classnames'; import { useId } from '../../internal/useId'; From d93550732b46a70f5adaf09a048b9218ec472618 Mon Sep 17 00:00:00 2001 From: Sojin Antony Date: Thu, 8 May 2025 21:09:17 +0530 Subject: [PATCH 7/9] fix: controlled tag fix tests #19228 --- packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap index 6f6c4c91ed32..a40d45707e48 100644 --- a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap +++ b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap @@ -7063,6 +7063,9 @@ Map { "className": Object { "type": "string", }, + "defaultSelected": Object { + "type": "bool", + }, "disabled": Object { "type": "bool", }, From 067eee83eebbe976213291185a65eb684fba7f00 Mon Sep 17 00:00:00 2001 From: Sojin Antony Date: Thu, 8 May 2025 21:39:48 +0530 Subject: [PATCH 8/9] fix: add profile --- packages/react/src/components/Tag/Tag-test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react/src/components/Tag/Tag-test.js b/packages/react/src/components/Tag/Tag-test.js index c7e8e9baf574..0cd8d10e726a 100644 --- a/packages/react/src/components/Tag/Tag-test.js +++ b/packages/react/src/components/Tag/Tag-test.js @@ -324,6 +324,7 @@ describe('Tag', () => { rerender( ); + expect(screen.getByRole('button', { name: 'Test Tag' })).toHaveAttribute( 'aria-pressed', 'false' From a958ac1b0de8e667da7e433a60ecb1d3d5664434 Mon Sep 17 00:00:00 2001 From: Sojin Antony Date: Thu, 8 May 2025 22:44:57 +0530 Subject: [PATCH 9/9] fix: add profile --- .all-contributorsrc | 9 +++++++++ README.md | 1 + 2 files changed, 10 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 32d9937e9809..0275a93e4d12 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1875,6 +1875,15 @@ "contributions": [ "code" ] + }, + { + "login": "sojinantony01", + "name": "Sojin antony", + "avatar_url": "https://avatars.githubusercontent.com/u/29255847?v=4", + "profile": "https://github.com/sojinantony01", + "contributions": [ + "code" + ] } ], "commitConvention": "none" diff --git a/README.md b/README.md index 56fab3900a6e..b7275ce058f9 100644 --- a/README.md +++ b/README.md @@ -341,6 +341,7 @@ check out our [Contributing Guide](/.github/CONTRIBUTING.md) and our
Mariya George

💻 +
Sojin antony

💻