diff --git a/change/@fluentui-react-tag-picker-9be006cd-1c03-46c8-a255-b35dd8971bee.json b/change/@fluentui-react-tag-picker-9be006cd-1c03-46c8-a255-b35dd8971bee.json new file mode 100644 index 0000000000000..c80020a4e2f72 --- /dev/null +++ b/change/@fluentui-react-tag-picker-9be006cd-1c03-46c8-a255-b35dd8971bee.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix(react-tag-picker): allows TagPickerGroup to be disabled", + "packageName": "@fluentui/react-tag-picker", + "email": "jiangemma@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-tags-85ae0473-8232-428f-b0a7-cdb10bbd84df.json b/change/@fluentui-react-tags-85ae0473-8232-428f-b0a7-cdb10bbd84df.json new file mode 100644 index 0000000000000..d38069dcc85c3 --- /dev/null +++ b/change/@fluentui-react-tags-85ae0473-8232-428f-b0a7-cdb10bbd84df.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix(react-tags): allows TagGroup to be disabled", + "packageName": "@fluentui/react-tags", + "email": "jiangemma@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-components/react-tag-picker/library/src/components/TagPickerGroup/useTagPickerGroup.ts b/packages/react-components/react-tag-picker/library/src/components/TagPickerGroup/useTagPickerGroup.ts index ce929680d6b12..c2cd563fc5b41 100644 --- a/packages/react-components/react-tag-picker/library/src/components/TagPickerGroup/useTagPickerGroup.ts +++ b/packages/react-components/react-tag-picker/library/src/components/TagPickerGroup/useTagPickerGroup.ts @@ -27,6 +27,7 @@ export const useTagPickerGroup_unstable = ( const selectOption = useTagPickerContext_unstable(ctx => ctx.selectOption); const size = useTagPickerContext_unstable(ctx => tagPickerSizeToTagSize(ctx.size)); const appearance = useTagPickerContext_unstable(ctx => ctx.appearance); + const disabled = useTagPickerContext_unstable(ctx => ctx.disabled); const arrowNavigationProps = useArrowNavigationGroup({ circular: false, @@ -37,6 +38,7 @@ export const useTagPickerGroup_unstable = ( const state = useTagGroup_unstable( { role: 'listbox', + disabled, ...props, ...arrowNavigationProps, size, diff --git a/packages/react-components/react-tag-picker/stories/src/TagPicker/TagPickerDisabled.stories.tsx b/packages/react-components/react-tag-picker/stories/src/TagPicker/TagPickerDisabled.stories.tsx index b2a33b577b206..7eb52b5995d4d 100644 --- a/packages/react-components/react-tag-picker/stories/src/TagPicker/TagPickerDisabled.stories.tsx +++ b/packages/react-components/react-tag-picker/stories/src/TagPicker/TagPickerDisabled.stories.tsx @@ -35,7 +35,6 @@ export const Disabled = () => { {selectedOptions.map(option => ( } diff --git a/packages/react-components/react-tags/library/etc/react-tags.api.md b/packages/react-components/react-tags/library/etc/react-tags.api.md index d1d51b4a98e4f..6644658a93d13 100644 --- a/packages/react-components/react-tags/library/etc/react-tags.api.md +++ b/packages/react-components/react-tags/library/etc/react-tags.api.md @@ -133,6 +133,7 @@ export type TagGroupContextValues = { // @public export type TagGroupProps = ComponentProps & { onDismiss?: TagDismissHandler; + disabled?: boolean; size?: TagSize; appearance?: TagAppearance; dismissible?: boolean; @@ -144,7 +145,7 @@ export type TagGroupSlots = { }; // @public -export type TagGroupState = ComponentState & Required> & { +export type TagGroupState = ComponentState & Required> & { handleTagDismiss: TagDismissHandler; role?: React_2.AriaRole; }; diff --git a/packages/react-components/react-tags/library/src/components/InteractionTag/useInteractionTag.tsx b/packages/react-components/react-tags/library/src/components/InteractionTag/useInteractionTag.tsx index 38ec4e9da28fb..aef03bd7f2cec 100644 --- a/packages/react-components/react-tags/library/src/components/InteractionTag/useInteractionTag.tsx +++ b/packages/react-components/react-tags/library/src/components/InteractionTag/useInteractionTag.tsx @@ -16,7 +16,12 @@ export const useInteractionTag_unstable = ( props: InteractionTagProps, ref: React.Ref, ): InteractionTagState => { - const { handleTagDismiss, size: contextSize, appearance: contextAppearance } = useTagGroupContext_unstable(); + const { + handleTagDismiss, + size: contextSize, + disabled: contextDisabled, + appearance: contextAppearance, + } = useTagGroupContext_unstable(); const id = useId('fui-InteractionTag-', props.id); @@ -32,7 +37,7 @@ export const useInteractionTag_unstable = ( return { appearance, - disabled, + disabled: contextDisabled ? true : disabled, handleTagDismiss, interactionTagPrimaryId, shape, @@ -47,6 +52,7 @@ export const useInteractionTag_unstable = ( getIntrinsicElementProps('div', { ref, ...props, + disabled: contextDisabled ? true : disabled, id, }), { elementType: 'div' }, diff --git a/packages/react-components/react-tags/library/src/components/Tag/useTag.tsx b/packages/react-components/react-tags/library/src/components/Tag/useTag.tsx index 154c39bbae46f..3f0cc40daf686 100644 --- a/packages/react-components/react-tags/library/src/components/Tag/useTag.tsx +++ b/packages/react-components/react-tags/library/src/components/Tag/useTag.tsx @@ -29,6 +29,7 @@ export const useTag_unstable = (props: TagProps, ref: React.Ref { expect(onDismiss).toHaveBeenCalledWith(expect.anything(), { value: '1' }); }); + + it('if disabled, should disable children Tags', () => { + const { getByRole } = render( + + + , + ); + + expect(getByRole('button')).toBeDisabled(); + }); + + it('if disabled, should override children Tags disabled prop', () => { + const { getByRole } = render( + + + , + ); + + expect(getByRole('button')).toBeDisabled(); + }); }); diff --git a/packages/react-components/react-tags/library/src/components/TagGroup/TagGroup.types.ts b/packages/react-components/react-tags/library/src/components/TagGroup/TagGroup.types.ts index aa6bddca63df8..063a840b83f5b 100644 --- a/packages/react-components/react-tags/library/src/components/TagGroup/TagGroup.types.ts +++ b/packages/react-components/react-tags/library/src/components/TagGroup/TagGroup.types.ts @@ -21,6 +21,13 @@ export type TagGroupProps = ComponentProps & { // eslint-disable-next-line @nx/workspace-consistent-callback-type -- can't change type of existing callback onDismiss?: TagDismissHandler; + /** + * A TagGroup can show that it cannot be interacted with. + * + * @default false + */ + disabled?: boolean; + size?: TagSize; appearance?: TagAppearance; dismissible?: boolean; @@ -30,7 +37,7 @@ export type TagGroupProps = ComponentProps & { * State used in rendering TagGroup */ export type TagGroupState = ComponentState & - Required> & { + Required> & { handleTagDismiss: TagDismissHandler; role?: React.AriaRole; }; diff --git a/packages/react-components/react-tags/library/src/components/TagGroup/useTagGroup.ts b/packages/react-components/react-tags/library/src/components/TagGroup/useTagGroup.ts index d04edd443bc3b..bb9cf04706bd8 100644 --- a/packages/react-components/react-tags/library/src/components/TagGroup/useTagGroup.ts +++ b/packages/react-components/react-tags/library/src/components/TagGroup/useTagGroup.ts @@ -15,7 +15,14 @@ import { interactionTagSecondaryClassNames } from '../InteractionTagSecondary/us * @param ref - reference to root HTMLDivElement of TagGroup */ export const useTagGroup_unstable = (props: TagGroupProps, ref: React.Ref): TagGroupState => { - const { onDismiss, size = 'medium', appearance = 'filled', dismissible = false, role = 'toolbar' } = props; + const { + onDismiss, + disabled = false, + size = 'medium', + appearance = 'filled', + dismissible = false, + role = 'toolbar', + } = props; const innerRef = React.useRef(); const { targetDocument } = useFluent(); @@ -55,6 +62,7 @@ export const useTagGroup_unstable = (props: TagGroupProps, ref: React.Ref, role, + 'aria-disabled': disabled, ...arrowNavigationProps, ...props, }), diff --git a/packages/react-components/react-tags/library/src/components/TagGroup/useTagGroupContextValues.ts b/packages/react-components/react-tags/library/src/components/TagGroup/useTagGroupContextValues.ts index c424568a0e146..706babd1c0484 100644 --- a/packages/react-components/react-tags/library/src/components/TagGroup/useTagGroupContextValues.ts +++ b/packages/react-components/react-tags/library/src/components/TagGroup/useTagGroupContextValues.ts @@ -2,11 +2,11 @@ import * as React from 'react'; import type { TagGroupContextValues, TagGroupState } from './TagGroup.types'; export function useTagGroupContextValues_unstable(state: TagGroupState): TagGroupContextValues { - const { handleTagDismiss, size, appearance, dismissible, role } = state; + const { handleTagDismiss, size, disabled, appearance, dismissible, role } = state; return { tagGroup: React.useMemo( - () => ({ handleTagDismiss, size, appearance, dismissible, role }), - [handleTagDismiss, size, appearance, dismissible, role], + () => ({ handleTagDismiss, size, disabled, appearance, dismissible, role }), + [handleTagDismiss, size, disabled, appearance, dismissible, role], ), }; } diff --git a/packages/react-components/react-tags/library/src/contexts/tagGroupContext.tsx b/packages/react-components/react-tags/library/src/contexts/tagGroupContext.tsx index 8901c8ac7a61d..98b824d953b1f 100644 --- a/packages/react-components/react-tags/library/src/contexts/tagGroupContext.tsx +++ b/packages/react-components/react-tags/library/src/contexts/tagGroupContext.tsx @@ -13,7 +13,7 @@ const tagGroupContextDefaultValue: TagGroupContextValue = { * Context shared between TagGroup and its children components */ export type TagGroupContextValue = Required> & - Partial>; + Partial>; export const TagGroupContextProvider = TagGroupContext.Provider; diff --git a/packages/react-components/react-tags/stories/src/TagGroup/TagGroupDisabled.stories.tsx b/packages/react-components/react-tags/stories/src/TagGroup/TagGroupDisabled.stories.tsx new file mode 100644 index 0000000000000..79118ea403ca7 --- /dev/null +++ b/packages/react-components/react-tags/stories/src/TagGroup/TagGroupDisabled.stories.tsx @@ -0,0 +1,53 @@ +import * as React from 'react'; +import { TagGroup, InteractionTag, InteractionTagPrimary, Tag, makeStyles } from '@fluentui/react-components'; + +const WithTags = () => ( + + Tag 1 + Tag 2 + Tag 3 + +); + +const WithInteractionTags = () => ( + + + Tag 1 + + + Tag 2 + + + Tag 3 + + +); + +const useStyles = makeStyles({ + container: { + display: 'flex', + flexDirection: 'column', + rowGap: '10px', + }, +}); + +export const Disabled = () => { + const styles = useStyles(); + return ( +
+ Disabled example with Tag: + + Disabled example with InteractionTag: + +
+ ); +}; + +Disabled.storyName = 'Disabled'; +Disabled.parameters = { + docs: { + description: { + story: 'A TagGroup can be disabled. The collection of Tag/InteractionTag will also be disabled.', + }, + }, +}; diff --git a/packages/react-components/react-tags/stories/src/TagGroup/index.stories.tsx b/packages/react-components/react-tags/stories/src/TagGroup/index.stories.tsx index e2f77c70cf6ed..9c27ceb03acf7 100644 --- a/packages/react-components/react-tags/stories/src/TagGroup/index.stories.tsx +++ b/packages/react-components/react-tags/stories/src/TagGroup/index.stories.tsx @@ -7,6 +7,7 @@ export { Default } from './TagGroupDefault.stories'; export { Dismiss } from './TagGroupDismiss.stories'; export { Sizes } from './TagGroupSizes.stories'; export { WithOverflow } from './TagGroupOverflow.stories'; +export { Disabled } from './TagGroupDisabled.stories'; export default { title: 'Components/Tag/TagGroup',