From 8b59f968f565ffd64156c8a93d966cd19619cae6 Mon Sep 17 00:00:00 2001 From: YuanboXue-Amber Date: Sat, 6 May 2023 18:01:56 +0800 Subject: [PATCH 01/18] create TagContent slot and new prop `interactive` on Tag to replace TagButton --- .../react-tags/etc/react-tags.api.md | 44 +-- .../react-components/react-tags/package.json | 4 +- .../react-tags/src/TagButton.ts | 1 - .../react-tags/src/TagContent.ts | 1 + .../src/components/Tag/Tag.test.tsx | 9 +- .../react-tags/src/components/Tag/Tag.tsx | 6 +- .../src/components/Tag/Tag.types.ts | 38 +-- .../src/components/Tag/TagContext.ts | 18 ++ .../react-tags/src/components/Tag/index.ts | 1 + .../src/components/Tag/renderTag.tsx | 21 +- .../react-tags/src/components/Tag/useTag.tsx | 30 +-- .../src/components/Tag/useTagContextValues.ts | 15 ++ .../src/components/Tag/useTagStyles.styles.ts | 116 ++------ .../components/TagButton/TagButton.test.tsx | 18 -- .../src/components/TagButton/TagButton.tsx | 19 -- .../components/TagButton/TagButton.types.ts | 15 -- .../src/components/TagButton/index.ts | 5 - .../components/TagButton/renderTagButton.tsx | 35 --- .../src/components/TagButton/useTagButton.tsx | 18 -- .../TagButton/useTagButtonStyles.styles.ts | 122 --------- .../components/TagContent/TagContent.test.tsx | 20 ++ .../src/components/TagContent/TagContent.tsx | 19 ++ .../components/TagContent/TagContent.types.ts | 50 ++++ .../src/components/TagContent/index.ts | 5 + .../TagContent/renderTagContent.tsx | 24 ++ .../components/TagContent/useTagContent.ts | 57 ++++ .../TagContent/useTagContentContextValues.ts} | 4 +- .../TagContent/useTagContentStyles.styles.ts | 153 +++++++++++ .../react-components/react-tags/src/index.ts | 14 +- .../react-tags/src/testing/isConformant.ts | 1 + .../stories/Tag/TagDefault.stories.tsx | 252 +++++++++--------- .../stories/Tag/TagDismiss.stories.tsx | 20 +- .../stories/Tag/TagIcon.stories.tsx | 10 +- .../stories/Tag/TagInteractive.stories.tsx | 62 +++++ .../stories/Tag/TagMedia.stories.tsx | 10 +- .../stories/Tag/TagSecondaryText.stories.tsx | 10 +- .../stories/Tag/TagShape.stories.tsx | 22 +- .../react-tags/stories/Tag/index.stories.tsx | 1 + .../TagButton/TagButtonBestPractices.md | 5 - .../TagButton/TagButtonDefault.stories.tsx | 154 ----------- .../stories/TagButton/TagButtonDescription.md | 0 .../TagButton/TagButtonDismiss.stories.tsx | 41 --- .../TagButton/TagButtonIcon.stories.tsx | 14 - .../TagButton/TagButtonMedia.stories.tsx | 17 -- .../TagButtonSecondaryText.stories.tsx | 13 - .../TagButton/TagButtonShape.stories.tsx | 42 --- .../stories/TagButton/index.stories.tsx | 23 -- 47 files changed, 687 insertions(+), 892 deletions(-) delete mode 100644 packages/react-components/react-tags/src/TagButton.ts create mode 100644 packages/react-components/react-tags/src/TagContent.ts create mode 100644 packages/react-components/react-tags/src/components/Tag/TagContext.ts create mode 100644 packages/react-components/react-tags/src/components/Tag/useTagContextValues.ts delete mode 100644 packages/react-components/react-tags/src/components/TagButton/TagButton.test.tsx delete mode 100644 packages/react-components/react-tags/src/components/TagButton/TagButton.tsx delete mode 100644 packages/react-components/react-tags/src/components/TagButton/TagButton.types.ts delete mode 100644 packages/react-components/react-tags/src/components/TagButton/index.ts delete mode 100644 packages/react-components/react-tags/src/components/TagButton/renderTagButton.tsx delete mode 100644 packages/react-components/react-tags/src/components/TagButton/useTagButton.tsx delete mode 100644 packages/react-components/react-tags/src/components/TagButton/useTagButtonStyles.styles.ts create mode 100644 packages/react-components/react-tags/src/components/TagContent/TagContent.test.tsx create mode 100644 packages/react-components/react-tags/src/components/TagContent/TagContent.tsx create mode 100644 packages/react-components/react-tags/src/components/TagContent/TagContent.types.ts create mode 100644 packages/react-components/react-tags/src/components/TagContent/index.ts create mode 100644 packages/react-components/react-tags/src/components/TagContent/renderTagContent.tsx create mode 100644 packages/react-components/react-tags/src/components/TagContent/useTagContent.ts rename packages/react-components/react-tags/src/{utils/useTagContextValues.ts => components/TagContent/useTagContentContextValues.ts} (57%) create mode 100644 packages/react-components/react-tags/src/components/TagContent/useTagContentStyles.styles.ts create mode 100644 packages/react-components/react-tags/stories/Tag/TagInteractive.stories.tsx delete mode 100644 packages/react-components/react-tags/stories/TagButton/TagButtonBestPractices.md delete mode 100644 packages/react-components/react-tags/stories/TagButton/TagButtonDefault.stories.tsx delete mode 100644 packages/react-components/react-tags/stories/TagButton/TagButtonDescription.md delete mode 100644 packages/react-components/react-tags/stories/TagButton/TagButtonDismiss.stories.tsx delete mode 100644 packages/react-components/react-tags/stories/TagButton/TagButtonIcon.stories.tsx delete mode 100644 packages/react-components/react-tags/stories/TagButton/TagButtonMedia.stories.tsx delete mode 100644 packages/react-components/react-tags/stories/TagButton/TagButtonSecondaryText.stories.tsx delete mode 100644 packages/react-components/react-tags/stories/TagButton/TagButtonShape.stories.tsx delete mode 100644 packages/react-components/react-tags/stories/TagButton/index.stories.tsx diff --git a/packages/react-components/react-tags/etc/react-tags.api.md b/packages/react-components/react-tags/etc/react-tags.api.md index c8e561dc6958cd..949f50ccb4b8b8 100644 --- a/packages/react-components/react-tags/etc/react-tags.api.md +++ b/packages/react-components/react-tags/etc/react-tags.api.md @@ -19,28 +19,39 @@ import type { SlotClassNames } from '@fluentui/react-utilities'; export const renderTag_unstable: (state: TagState, contextValues: TagContextValues) => JSX.Element; // @public -export const renderTagButton_unstable: (state: TagButtonState, contextValues: TagButtonContextValues) => JSX.Element; +export const renderTagContent_unstable: (state: TagContentState, contextValues: TagContentContextValues) => JSX.Element; // @public export const Tag: ForwardRefComponent; -// @public -export const TagButton: ForwardRefComponent; - // @public (undocumented) -export const tagButtonClassNames: SlotClassNames; +export const tagClassNames: SlotClassNames; // @public -export type TagButtonProps = TagProps; +export const TagContent: ForwardRefComponent; // @public (undocumented) -export type TagButtonSlots = TagSlots; +export const tagContentClassNames: SlotClassNames; // @public -export type TagButtonState = TagState; +export type TagContentProps = ComponentProps>; // @public (undocumented) -export const tagClassNames: SlotClassNames; +export type TagContentSlots = { + root: NonNullable>; + media: Slot<'span'>; + icon: Slot<'span'>; + primaryText: Slot<'span'>; + secondaryText: Slot<'span'>; +}; + +// @public +export type TagContentState = ComponentState & Required<{ + avatarSize: AvatarSize; + avatarShape: AvatarShape; + dismissable: boolean; + shape?: 'rounded' | 'circular'; +}>; // @public export type TagProps = ComponentProps> & { @@ -50,33 +61,26 @@ export type TagProps = ComponentProps> & { disabled?: boolean; checked?: boolean; dismissable?: boolean; + interactive?: boolean; }; // @public (undocumented) export type TagSlots = { root: NonNullable>; - media: Slot<'span'>; - content: Slot<'div'>; - icon: Slot<'span'>; - primaryText: Slot<'span'>; - secondaryText: Slot<'span'>; dismissButton: Slot<'button'>; }; // @public -export type TagState = ComponentState & Required & { - avatarSize: AvatarSize | undefined; - avatarShape: AvatarShape | undefined; -}>; +export type TagState = ComponentState & Required>; // @public export const useTag_unstable: (props: TagProps, ref: React_2.Ref) => TagState; // @public -export const useTagButton_unstable: (props: TagButtonProps, ref: React_2.Ref) => TagButtonState; +export const useTagContent_unstable: (props: TagContentProps, ref: React_2.Ref) => TagContentState; // @public -export const useTagButtonStyles_unstable: (state: TagButtonState) => TagButtonState; +export const useTagContentStyles_unstable: (state: TagContentState) => TagContentState; // @public export const useTagStyles_unstable: (state: TagState) => TagState; diff --git a/packages/react-components/react-tags/package.json b/packages/react-components/react-tags/package.json index 0fa7345db834d9..73b618349759be 100644 --- a/packages/react-components/react-tags/package.json +++ b/packages/react-components/react-tags/package.json @@ -23,7 +23,8 @@ "type-check": "tsc -b tsconfig.json", "generate-api": "just-scripts generate-api", "storybook": "start-storybook", - "start": "yarn storybook" + "start": "yarn storybook", + "test-ssr": "test-ssr ./stories/**/*.stories.tsx" }, "devDependencies": { "@fluentui/eslint-plugin": "*", @@ -35,6 +36,7 @@ "dependencies": { "@fluentui/react-aria": "^9.3.18", "@fluentui/react-avatar": "^9.4.10", + "@fluentui/react-context-selector": "^9.1.18", "@fluentui/react-icons": "^2.0.196", "@fluentui/react-jsx-runtime": "9.0.0-alpha.2", "@fluentui/react-tabster": "^9.6.5", diff --git a/packages/react-components/react-tags/src/TagButton.ts b/packages/react-components/react-tags/src/TagButton.ts deleted file mode 100644 index 73bded719a2106..00000000000000 --- a/packages/react-components/react-tags/src/TagButton.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './components/TagButton/index'; diff --git a/packages/react-components/react-tags/src/TagContent.ts b/packages/react-components/react-tags/src/TagContent.ts new file mode 100644 index 00000000000000..c0e6b46040ab8e --- /dev/null +++ b/packages/react-components/react-tags/src/TagContent.ts @@ -0,0 +1 @@ +export * from './components/TagContent/index'; diff --git a/packages/react-components/react-tags/src/components/Tag/Tag.test.tsx b/packages/react-components/react-tags/src/components/Tag/Tag.test.tsx index 480342e78bece2..770f377409ea8c 100644 --- a/packages/react-components/react-tags/src/components/Tag/Tag.test.tsx +++ b/packages/react-components/react-tags/src/components/Tag/Tag.test.tsx @@ -1,11 +1,8 @@ import { Tag } from './Tag'; import { isConformant } from '../../testing/isConformant'; +import { TagProps } from './Tag.types'; -const requiredProps = { - media: 'media', - icon: 'i', - primaryText: 'Primary text', - secondaryText: 'Secondary text', +const requiredProps: TagProps = { dismissable: true, }; @@ -15,4 +12,6 @@ describe('Tag', () => { displayName: 'Tag', requiredProps, }); + + // TODO add more tests here, and create visual regression tests in /apps/vr-tests }); diff --git a/packages/react-components/react-tags/src/components/Tag/Tag.tsx b/packages/react-components/react-tags/src/components/Tag/Tag.tsx index 30cded29f93faa..06ad4be5813da6 100644 --- a/packages/react-components/react-tags/src/components/Tag/Tag.tsx +++ b/packages/react-components/react-tags/src/components/Tag/Tag.tsx @@ -4,16 +4,16 @@ import { renderTag_unstable } from './renderTag'; import { useTagStyles_unstable } from './useTagStyles.styles'; import type { TagProps } from './Tag.types'; import type { ForwardRefComponent } from '@fluentui/react-utilities'; -import { useTagContextValues_unstable } from '../../utils/useTagContextValues'; +import { useTagContextValues_unstable } from './useTagContextValues'; /** * Tag component - TODO: add more docs */ export const Tag: ForwardRefComponent = React.forwardRef((props, ref) => { const state = useTag_unstable(props, ref); - + const contextValues = useTagContextValues_unstable(state); useTagStyles_unstable(state); - return renderTag_unstable(state, useTagContextValues_unstable(state)); + return renderTag_unstable(state, contextValues); }); Tag.displayName = 'Tag'; diff --git a/packages/react-components/react-tags/src/components/Tag/Tag.types.ts b/packages/react-components/react-tags/src/components/Tag/Tag.types.ts index 9d130328b633c0..8b67e68f729da7 100644 --- a/packages/react-components/react-tags/src/components/Tag/Tag.types.ts +++ b/packages/react-components/react-tags/src/components/Tag/Tag.types.ts @@ -1,38 +1,14 @@ -import { AvatarSize, AvatarShape } from '@fluentui/react-avatar'; import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; +export type TagContextValue = Required>; + export type TagContextValues = { - avatar: { - size?: AvatarSize; - shape?: AvatarShape; - }; + tag: TagContextValue; }; export type TagSlots = { root: NonNullable>; - /** - * Slot for an icon or other visual element - */ - media: Slot<'span'>; - - /** - * A layout wrapper for the icon slot, the primaryText and secondaryText slots - */ - content: Slot<'div'>; - - icon: Slot<'span'>; - - /** - * Main text for the Tag. Children of the root slot are automatically rendered here - */ - primaryText: Slot<'span'>; - - /** - * Secondary text that describes or complements the main text - */ - secondaryText: Slot<'span'>; - dismissButton: Slot<'button'>; }; @@ -46,15 +22,11 @@ export type TagProps = ComponentProps> & { disabled?: boolean; checked?: boolean; dismissable?: boolean; + interactive?: boolean; }; /** * State used in rendering Tag */ export type TagState = ComponentState & - Required< - Pick & { - avatarSize: AvatarSize | undefined; - avatarShape: AvatarShape | undefined; - } - >; + Required>; diff --git a/packages/react-components/react-tags/src/components/Tag/TagContext.ts b/packages/react-components/react-tags/src/components/Tag/TagContext.ts new file mode 100644 index 00000000000000..1666fb69c6140c --- /dev/null +++ b/packages/react-components/react-tags/src/components/Tag/TagContext.ts @@ -0,0 +1,18 @@ +import { createContext, ContextSelector, useContextSelector } from '@fluentui/react-context-selector'; +import type { Context } from '@fluentui/react-context-selector'; +import type { TagContextValue } from './Tag.types'; + +export const TagContext: Context = createContext( + undefined, +) as Context; + +const tagContextDefaultValue: TagContextValue = { + dismissable: false, + shape: 'rounded', + size: 'medium', + interactive: false, +}; + +export const TagProvider = TagContext.Provider; +export const useTagContext_unstable = (selector: ContextSelector): T => + useContextSelector(TagContext, (ctx = tagContextDefaultValue) => selector(ctx)); diff --git a/packages/react-components/react-tags/src/components/Tag/index.ts b/packages/react-components/react-tags/src/components/Tag/index.ts index a44c9ce425d76c..6063344cb48fa8 100644 --- a/packages/react-components/react-tags/src/components/Tag/index.ts +++ b/packages/react-components/react-tags/src/components/Tag/index.ts @@ -3,3 +3,4 @@ export * from './Tag.types'; export * from './renderTag'; export * from './useTag'; export * from './useTagStyles.styles'; +export * from './TagContext'; diff --git a/packages/react-components/react-tags/src/components/Tag/renderTag.tsx b/packages/react-components/react-tags/src/components/Tag/renderTag.tsx index 6e382ca446cb48..df641ea71a4761 100644 --- a/packages/react-components/react-tags/src/components/Tag/renderTag.tsx +++ b/packages/react-components/react-tags/src/components/Tag/renderTag.tsx @@ -5,7 +5,7 @@ import { createElement } from '@fluentui/react-jsx-runtime'; import { getSlotsNext } from '@fluentui/react-utilities'; import type { TagState, TagSlots, TagContextValues } from './Tag.types'; -import { AvatarContextProvider } from '@fluentui/react-avatar'; +import { TagContext } from './TagContext'; /** * Render the final JSX of Tag @@ -15,21 +15,10 @@ export const renderTag_unstable = (state: TagState, contextValues: TagContextVal return ( - {slots.content && ( - - {slots.media && ( - - - - )} - {slots.icon && } - {slots.primaryText && ( - {slotProps.root.children} - )} - {slots.secondaryText && } - - )} - {slots.dismissButton && state.dismissable && } + + {slotProps.root.children} + {slots.dismissButton && state.dismissable && } + ); }; diff --git a/packages/react-components/react-tags/src/components/Tag/useTag.tsx b/packages/react-components/react-tags/src/components/Tag/useTag.tsx index 35f804d5c4c2a5..8e614254b7dc0e 100644 --- a/packages/react-components/react-tags/src/components/Tag/useTag.tsx +++ b/packages/react-components/react-tags/src/components/Tag/useTag.tsx @@ -1,20 +1,9 @@ import * as React from 'react'; -import { getNativeElementProps, resolveShorthand } from '@fluentui/react-utilities'; +import { getNativeElementProps } from '@fluentui/react-utilities'; import { DismissRegular, bundleIcon, DismissFilled } from '@fluentui/react-icons'; import type { TagProps, TagState } from './Tag.types'; import { useARIAButtonShorthand } from '@fluentui/react-aria'; -const tagAvatarSizeMap = { - medium: 28, - small: 24, - 'extra-small': 20, -} as const; - -const tagAvatarShapeMap = { - rounded: 'square', - circular: 'circular', -} as const; - const DismissIcon = bundleIcon(DismissFilled, DismissRegular); /** @@ -34,36 +23,27 @@ export const useTag_unstable = (props: TagProps, ref: React.Ref): T shape = 'rounded', size = 'medium', appearance = 'filled-lighter', + interactive = false, } = props; return { components: { root: 'div', - content: 'div', - media: 'span', - icon: 'span', - primaryText: 'span', - secondaryText: 'span', dismissButton: 'button', }, + checked, disabled, dismissable, shape, size, appearance, + interactive, + root: getNativeElementProps('div', { ref, ...props, }), - avatarSize: tagAvatarSizeMap[size], - avatarShape: tagAvatarShapeMap[shape], - media: resolveShorthand(props.media), - - content: resolveShorthand(props.content, { required: true }), - icon: resolveShorthand(props.icon), - primaryText: resolveShorthand(props.primaryText, { required: true }), - secondaryText: resolveShorthand(props.secondaryText), dismissButton: useARIAButtonShorthand(props.dismissButton, { required: props.dismissable, diff --git a/packages/react-components/react-tags/src/components/Tag/useTagContextValues.ts b/packages/react-components/react-tags/src/components/Tag/useTagContextValues.ts new file mode 100644 index 00000000000000..a76e794b568cdf --- /dev/null +++ b/packages/react-components/react-tags/src/components/Tag/useTagContextValues.ts @@ -0,0 +1,15 @@ +import type { TagContextValue, TagContextValues, TagState } from './Tag.types'; + +export function useTagContextValues_unstable(state: TagState): TagContextValues { + const { dismissable, shape, size, interactive } = state; + + // This context is created with "@fluentui/react-context-selector", these is no sense to memoize it + const tag: TagContextValue = { + dismissable, + shape, + size, + interactive, + }; + + return { tag }; +} diff --git a/packages/react-components/react-tags/src/components/Tag/useTagStyles.styles.ts b/packages/react-components/react-tags/src/components/Tag/useTagStyles.styles.ts index 1edf60c2352c94..b82fdece5f21c9 100644 --- a/packages/react-components/react-tags/src/components/Tag/useTagStyles.styles.ts +++ b/packages/react-components/react-tags/src/components/Tag/useTagStyles.styles.ts @@ -1,16 +1,11 @@ import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; import type { TagSlots, TagState } from './Tag.types'; import type { SlotClassNames } from '@fluentui/react-utilities'; -import { tokens, typographyStyles } from '@fluentui/react-theme'; +import { tokens } from '@fluentui/react-theme'; import { createCustomFocusIndicatorStyle } from '@fluentui/react-tabster'; export const tagClassNames: SlotClassNames = { root: 'fui-Tag', - content: 'fui-Tag__content', - media: 'fui-Tag__media', - icon: 'fui-Tag__icon', - primaryText: 'fui-Tag__primaryText', - secondaryText: 'fui-Tag__secondaryText', dismissButton: 'fui-Tag__dismissButton', }; @@ -33,54 +28,6 @@ export const useTagBaseStyles = makeStyles({ ...shorthands.borderRadius(tokens.borderRadiusCircular), }, - media: { - ...shorthands.gridArea('media'), - alignSelf: 'center', - paddingLeft: tokens.spacingHorizontalXXS, - paddingRight: tokens.spacingHorizontalS, - }, - - content: { - display: 'inline-grid', - gridTemplateRows: '1fr auto auto 1fr', - gridTemplateAreas: ` - "media . " - "media primary " - "media secondary" - "media . " - `, - paddingRight: tokens.spacingHorizontalS, - }, - textOnlyContent: { - paddingLeft: tokens.spacingHorizontalS, - }, - - icon: { - display: 'flex', - alignSelf: 'center', - ...shorthands.gridArea('media'), - paddingLeft: '6px', - paddingRight: '2px', - }, - primaryText: { - gridColumnStart: 'primary', - gridRowStart: 'primary', - gridRowEnd: 'secondary', - ...typographyStyles.body1, - paddingLeft: tokens.spacingHorizontalXXS, - paddingRight: tokens.spacingHorizontalXXS, - }, - primaryTextWithSecondaryText: { - ...shorthands.gridArea('primary'), - ...typographyStyles.caption1, - }, - secondaryText: { - ...shorthands.gridArea('secondary'), - paddingLeft: tokens.spacingHorizontalXXS, - paddingRight: tokens.spacingHorizontalXXS, - ...typographyStyles.caption2, - }, - resetButton: { boxSizing: 'content-box', color: 'inherit', @@ -113,20 +60,36 @@ export const useTagBaseStyles = makeStyles({ }); const useTagStyles = makeStyles({ - dismissableContent: { - paddingRight: '2px', - }, dismissButton: { marginRight: '6px', }, }); +const useInteractiveTagStyles = makeStyles({ + dismissButton: { + ...shorthands.padding('0px', '6px'), + ...shorthands.borderLeft(tokens.strokeWidthThin, 'solid', tokens.colorNeutralStroke1), + borderTopLeftRadius: tokens.borderRadiusNone, + borderBottomLeftRadius: tokens.borderRadiusNone, + ...createCustomFocusIndicatorStyle({ + borderTopLeftRadius: tokens.borderRadiusNone, + borderBottomLeftRadius: tokens.borderRadiusNone, + }), + }, + dismissButtonCircular: createCustomFocusIndicatorStyle({ + borderTopRightRadius: tokens.borderRadiusCircular, + borderBottomRightRadius: tokens.borderRadiusCircular, + }), +}); + /** * Apply styling to the Tag slots based on the state */ export const useTagStyles_unstable = (state: TagState): TagState => { const baseStyles = useTagBaseStyles(); - const styles = useTagStyles(); + + const tagStyles = useTagStyles(); + const interactiveTagStyles = useInteractiveTagStyles(); state.root.className = mergeClasses( tagClassNames.root, @@ -134,45 +97,18 @@ export const useTagStyles_unstable = (state: TagState): TagState => { state.shape === 'circular' && baseStyles.rootCircular, state.root.className, ); - if (state.content) { - state.content.className = mergeClasses( - tagClassNames.content, - baseStyles.content, - !state.media && !state.icon && baseStyles.textOnlyContent, - - state.dismissButton && styles.dismissableContent, - state.content.className, - ); - } - if (state.media) { - state.media.className = mergeClasses(tagClassNames.media, baseStyles.media, state.media.className); - } - if (state.icon) { - state.icon.className = mergeClasses(tagClassNames.icon, baseStyles.icon, state.icon.className); - } - if (state.primaryText) { - state.primaryText.className = mergeClasses( - tagClassNames.primaryText, - baseStyles.primaryText, - state.secondaryText && baseStyles.primaryTextWithSecondaryText, - state.primaryText.className, - ); - } - if (state.secondaryText) { - state.secondaryText.className = mergeClasses( - tagClassNames.secondaryText, - baseStyles.secondaryText, - state.secondaryText.className, - ); - } if (state.dismissButton) { state.dismissButton.className = mergeClasses( tagClassNames.dismissButton, baseStyles.resetButton, baseStyles.dismissButton, - styles.dismissButton, + !state.interactive && tagStyles.dismissButton, + + state.interactive && interactiveTagStyles.dismissButton, + state.interactive && state.shape === 'circular' && interactiveTagStyles.dismissButtonCircular, + state.dismissButton.className, ); } diff --git a/packages/react-components/react-tags/src/components/TagButton/TagButton.test.tsx b/packages/react-components/react-tags/src/components/TagButton/TagButton.test.tsx deleted file mode 100644 index 74dd557f5f6efd..00000000000000 --- a/packages/react-components/react-tags/src/components/TagButton/TagButton.test.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { TagButton } from './TagButton'; -import { isConformant } from '../../testing/isConformant'; - -const requiredProps = { - media: 'media', - icon: 'i', - primaryText: 'Primary text', - secondaryText: 'Secondary text', - dismissable: true, -}; - -describe('TagButton', () => { - isConformant({ - Component: TagButton, - displayName: 'TagButton', - requiredProps, - }); -}); diff --git a/packages/react-components/react-tags/src/components/TagButton/TagButton.tsx b/packages/react-components/react-tags/src/components/TagButton/TagButton.tsx deleted file mode 100644 index aff06a81252e8d..00000000000000 --- a/packages/react-components/react-tags/src/components/TagButton/TagButton.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import * as React from 'react'; -import { useTagButton_unstable } from './useTagButton'; -import { renderTagButton_unstable } from './renderTagButton'; -import { useTagButtonStyles_unstable } from './useTagButtonStyles.styles'; -import type { TagButtonProps } from './TagButton.types'; -import type { ForwardRefComponent } from '@fluentui/react-utilities'; -import { useTagContextValues_unstable } from '../../utils/useTagContextValues'; - -/** - * TagButton component - TODO: add more docs - */ -export const TagButton: ForwardRefComponent = React.forwardRef((props, ref) => { - const state = useTagButton_unstable(props, ref); - - useTagButtonStyles_unstable(state); - return renderTagButton_unstable(state, useTagContextValues_unstable(state)); -}); - -TagButton.displayName = 'TagButton'; diff --git a/packages/react-components/react-tags/src/components/TagButton/TagButton.types.ts b/packages/react-components/react-tags/src/components/TagButton/TagButton.types.ts deleted file mode 100644 index ae5531c83167e0..00000000000000 --- a/packages/react-components/react-tags/src/components/TagButton/TagButton.types.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { TagContextValues, TagProps, TagSlots, TagState } from '../Tag/index'; - -export type TagButtonContextValues = TagContextValues; - -export type TagButtonSlots = TagSlots; - -/** - * TagButton Props - */ -export type TagButtonProps = TagProps; - -/** - * State used in rendering TagButton - */ -export type TagButtonState = TagState; diff --git a/packages/react-components/react-tags/src/components/TagButton/index.ts b/packages/react-components/react-tags/src/components/TagButton/index.ts deleted file mode 100644 index 42382817cf40b7..00000000000000 --- a/packages/react-components/react-tags/src/components/TagButton/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './TagButton'; -export * from './TagButton.types'; -export * from './renderTagButton'; -export * from './useTagButton'; -export * from './useTagButtonStyles.styles'; diff --git a/packages/react-components/react-tags/src/components/TagButton/renderTagButton.tsx b/packages/react-components/react-tags/src/components/TagButton/renderTagButton.tsx deleted file mode 100644 index 1d358dcaa60b5d..00000000000000 --- a/packages/react-components/react-tags/src/components/TagButton/renderTagButton.tsx +++ /dev/null @@ -1,35 +0,0 @@ -/** @jsxRuntime classic */ -/** @jsx createElement */ - -import { createElement } from '@fluentui/react-jsx-runtime'; - -import { getSlotsNext } from '@fluentui/react-utilities'; -import type { TagButtonState, TagButtonSlots, TagButtonContextValues } from './TagButton.types'; -import { AvatarContextProvider } from '@fluentui/react-avatar'; - -/** - * Render the final JSX of TagButton - */ -export const renderTagButton_unstable = (state: TagButtonState, contextValues: TagButtonContextValues) => { - const { slots, slotProps } = getSlotsNext(state); - - return ( - - {slots.content && ( - - {slots.media && ( - - - - )} - {slots.icon && } - {slots.primaryText && ( - {slotProps.root.children} - )} - {slots.secondaryText && } - - )} - {slots.dismissButton && state.dismissable && } - - ); -}; diff --git a/packages/react-components/react-tags/src/components/TagButton/useTagButton.tsx b/packages/react-components/react-tags/src/components/TagButton/useTagButton.tsx deleted file mode 100644 index 8d5bfe1d9d8dff..00000000000000 --- a/packages/react-components/react-tags/src/components/TagButton/useTagButton.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import * as React from 'react'; -import { resolveShorthand } from '@fluentui/react-utilities'; -import type { TagButtonProps, TagButtonState } from './TagButton.types'; -import { useTag_unstable } from '../Tag/index'; - -/** - * Create the state required to render TagButton. - * - * The returned state can be modified with hooks such as useTagButtonStyles_unstable, - * before being passed to renderTagButton_unstable. - * - * @param props - props from this instance of TagButton - * @param ref - reference to root HTMLElement of TagButton - */ -export const useTagButton_unstable = (props: TagButtonProps, ref: React.Ref): TagButtonState => { - const content = resolveShorthand(props.content, { required: true, defaultProps: { tabIndex: 0 } }); - return useTag_unstable({ ...props, content }, ref); -}; diff --git a/packages/react-components/react-tags/src/components/TagButton/useTagButtonStyles.styles.ts b/packages/react-components/react-tags/src/components/TagButton/useTagButtonStyles.styles.ts deleted file mode 100644 index fb28d8ef924084..00000000000000 --- a/packages/react-components/react-tags/src/components/TagButton/useTagButtonStyles.styles.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; -import type { TagButtonSlots, TagButtonState } from './TagButton.types'; -import type { SlotClassNames } from '@fluentui/react-utilities'; -import { createCustomFocusIndicatorStyle } from '@fluentui/react-tabster'; -import { tokens } from '@fluentui/react-theme'; -import { useTagBaseStyles } from '../Tag/index'; - -export const tagButtonClassNames: SlotClassNames = { - root: 'fui-TagButton', - content: 'fui-TagButton__content', - media: 'fui-TagButton__media', - icon: 'fui-TagButton__icon', - primaryText: 'fui-TagButton__primaryText', - secondaryText: 'fui-TagButton__secondaryText', - dismissButton: 'fui-TagButton__dismissButton', -}; - -/** - * Styles for the root slot - */ -const useStyles = makeStyles({ - content: { - position: 'relative', - ...createCustomFocusIndicatorStyle( - { - ...shorthands.borderRadius(tokens.borderRadiusMedium), - ...shorthands.outline(tokens.strokeWidthThick, 'solid', tokens.colorStrokeFocus2), - }, - { enableOutline: true }, - ), - }, - - circularContent: createCustomFocusIndicatorStyle(shorthands.borderRadius(tokens.borderRadiusCircular), { - enableOutline: true, - }), - dismissableContent: { - ...createCustomFocusIndicatorStyle({ - borderTopRightRadius: tokens.borderRadiusNone, - borderBottomRightRadius: tokens.borderRadiusNone, - }), - }, - - dismissButton: { - ...shorthands.padding('0px', '6px'), - ...shorthands.borderLeft(tokens.strokeWidthThin, 'solid', tokens.colorNeutralStroke1), - borderTopLeftRadius: tokens.borderRadiusNone, - borderBottomLeftRadius: tokens.borderRadiusNone, - ...createCustomFocusIndicatorStyle({ - borderTopLeftRadius: tokens.borderRadiusNone, - borderBottomLeftRadius: tokens.borderRadiusNone, - }), - }, - dismissButtonCircular: createCustomFocusIndicatorStyle({ - borderTopRightRadius: tokens.borderRadiusCircular, - borderBottomRightRadius: tokens.borderRadiusCircular, - }), - - // TODO add additional classes for fill/outline appearance, different sizes, and state -}); - -/** - * Apply styling to the TagButton slots based on the state - */ -export const useTagButtonStyles_unstable = (state: TagButtonState): TagButtonState => { - const baseStyles = useTagBaseStyles(); - const styles = useStyles(); - - state.root.className = mergeClasses( - tagButtonClassNames.root, - baseStyles.root, - state.shape === 'circular' && baseStyles.rootCircular, - state.root.className, - ); - if (state.content) { - state.content.className = mergeClasses( - tagButtonClassNames.content, - baseStyles.content, - !state.media && !state.icon && baseStyles.textOnlyContent, - - styles.content, - state.shape === 'circular' && styles.circularContent, - state.dismissButton && styles.dismissableContent, - - state.content.className, - ); - } - - if (state.media) { - state.media.className = mergeClasses(tagButtonClassNames.media, baseStyles.media, state.media.className); - } - if (state.icon) { - state.icon.className = mergeClasses(tagButtonClassNames.icon, baseStyles.icon, state.icon.className); - } - if (state.primaryText) { - state.primaryText.className = mergeClasses( - tagButtonClassNames.primaryText, - baseStyles.primaryText, - state.secondaryText && baseStyles.primaryTextWithSecondaryText, - state.primaryText.className, - ); - } - if (state.secondaryText) { - state.secondaryText.className = mergeClasses( - tagButtonClassNames.secondaryText, - baseStyles.secondaryText, - state.secondaryText.className, - ); - } - if (state.dismissButton) { - state.dismissButton.className = mergeClasses( - tagButtonClassNames.dismissButton, - baseStyles.resetButton, - baseStyles.dismissButton, - - styles.dismissButton, - state.shape === 'circular' && styles.dismissButtonCircular, - state.dismissButton.className, - ); - } - - return state; -}; diff --git a/packages/react-components/react-tags/src/components/TagContent/TagContent.test.tsx b/packages/react-components/react-tags/src/components/TagContent/TagContent.test.tsx new file mode 100644 index 00000000000000..078459c6fbf4ea --- /dev/null +++ b/packages/react-components/react-tags/src/components/TagContent/TagContent.test.tsx @@ -0,0 +1,20 @@ +import { TagContent } from './TagContent'; +import { isConformant } from '../../testing/isConformant'; +import { TagContentProps } from './TagContent.types'; + +const requiredProps: TagContentProps = { + media: 'media', + icon: 'i', + primaryText: 'Primary text', + secondaryText: 'Secondary text', +}; + +describe('TagContent', () => { + isConformant({ + Component: TagContent, + displayName: 'TagContent', + requiredProps, + }); + + // TODO add more tests here, and create visual regression tests in /apps/vr-tests +}); diff --git a/packages/react-components/react-tags/src/components/TagContent/TagContent.tsx b/packages/react-components/react-tags/src/components/TagContent/TagContent.tsx new file mode 100644 index 00000000000000..513b28f749c812 --- /dev/null +++ b/packages/react-components/react-tags/src/components/TagContent/TagContent.tsx @@ -0,0 +1,19 @@ +import * as React from 'react'; +import { useTagContent_unstable } from './useTagContent'; +import { renderTagContent_unstable } from './renderTagContent'; +import { useTagContentStyles_unstable } from './useTagContentStyles.styles'; +import type { TagContentProps } from './TagContent.types'; +import type { ForwardRefComponent } from '@fluentui/react-utilities'; +import { useTagContentContextValues_unstable } from './useTagContentContextValues'; + +/** + * TagContent component - TODO: add more docs + */ +export const TagContent: ForwardRefComponent = React.forwardRef((props, ref) => { + const state = useTagContent_unstable(props, ref); + const contextValues = useTagContentContextValues_unstable(state); + useTagContentStyles_unstable(state); + return renderTagContent_unstable(state, contextValues); +}); + +TagContent.displayName = 'TagContent'; diff --git a/packages/react-components/react-tags/src/components/TagContent/TagContent.types.ts b/packages/react-components/react-tags/src/components/TagContent/TagContent.types.ts new file mode 100644 index 00000000000000..1e9db5e1c3d24a --- /dev/null +++ b/packages/react-components/react-tags/src/components/TagContent/TagContent.types.ts @@ -0,0 +1,50 @@ +import { AvatarShape, AvatarSize } from '@fluentui/react-avatar'; +import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; +import { TagContextValue } from '../Tag/index'; + +export type TagContentContextValues = { + avatar: { + shape: AvatarShape; + size: AvatarSize; + }; +}; + +export type TagContentSlots = { + root: Slot<'div', 'button'>; + + /** + * Slot for an visual element, usually an Avatar + */ + media: Slot<'span'>; + + /** + * Slot for an icon + */ + icon: Slot<'span'>; + + /** + * Main text for the Tag. Children of the root slot are automatically rendered here + */ + primaryText: Slot<'span'>; + + /** + * Secondary text that describes or complements the main text + */ + secondaryText: Slot<'span'>; +}; + +/** + * TagContent Props + */ +export type TagContentProps = ComponentProps>; + +/** + * State used in rendering TagContent + */ +export type TagContentState = ComponentState & + Required< + Pick & { + avatarSize: AvatarSize; + avatarShape: AvatarShape; + } + >; diff --git a/packages/react-components/react-tags/src/components/TagContent/index.ts b/packages/react-components/react-tags/src/components/TagContent/index.ts new file mode 100644 index 00000000000000..08e461969e0f38 --- /dev/null +++ b/packages/react-components/react-tags/src/components/TagContent/index.ts @@ -0,0 +1,5 @@ +export * from './TagContent'; +export * from './TagContent.types'; +export * from './renderTagContent'; +export * from './useTagContent'; +export * from './useTagContentStyles.styles'; diff --git a/packages/react-components/react-tags/src/components/TagContent/renderTagContent.tsx b/packages/react-components/react-tags/src/components/TagContent/renderTagContent.tsx new file mode 100644 index 00000000000000..e9d896d8584e2d --- /dev/null +++ b/packages/react-components/react-tags/src/components/TagContent/renderTagContent.tsx @@ -0,0 +1,24 @@ +import * as React from 'react'; +import { getSlots } from '@fluentui/react-utilities'; +import type { TagContentState, TagContentSlots, TagContentContextValues } from './TagContent.types'; +import { AvatarContextProvider } from '@fluentui/react-avatar'; + +/** + * Render the final JSX of TagContent + */ +export const renderTagContent_unstable = (state: TagContentState, contextValues: TagContentContextValues) => { + const { slots, slotProps } = getSlots(state); + + return ( + + {slots.media && ( + + + + )} + {slots.icon && } + {slots.primaryText && {slotProps.root.children}} + {slots.secondaryText && } + + ); +}; diff --git a/packages/react-components/react-tags/src/components/TagContent/useTagContent.ts b/packages/react-components/react-tags/src/components/TagContent/useTagContent.ts new file mode 100644 index 00000000000000..c91969639c1859 --- /dev/null +++ b/packages/react-components/react-tags/src/components/TagContent/useTagContent.ts @@ -0,0 +1,57 @@ +import * as React from 'react'; +import { getNativeElementProps, resolveShorthand } from '@fluentui/react-utilities'; +import type { TagContentProps, TagContentState } from './TagContent.types'; +import { useTagContext_unstable } from '../Tag/index'; + +const tagContentAvatarSizeMap = { + medium: 28, + small: 24, + 'extra-small': 20, +} as const; + +const tagContentAvatarShapeMap = { + rounded: 'square', + circular: 'circular', +} as const; + +/** + * Create the state required to render TagContent. + * + * The returned state can be modified with hooks such as useTagContentStyles_unstable, + * before being passed to renderTagContent_unstable. + * + * @param props - props from this instance of TagContent + * @param ref - reference to root HTMLElement of TagContent + */ +export const useTagContent_unstable = (props: TagContentProps, ref: React.Ref): TagContentState => { + const dismissable = useTagContext_unstable(ctx => ctx.dismissable); + const shape = useTagContext_unstable(ctx => ctx.shape); + const size = useTagContext_unstable(ctx => ctx.size); + const interactive = useTagContext_unstable(ctx => ctx.interactive); + + return { + components: { + root: interactive ? 'button' : 'div', + media: 'span', + icon: 'span', + primaryText: 'span', + secondaryText: 'span', + }, + + avatarShape: tagContentAvatarShapeMap[shape], + avatarSize: tagContentAvatarSizeMap[size], + dismissable, + interactive, + shape, + + media: resolveShorthand(props.media), + icon: resolveShorthand(props.icon), + primaryText: resolveShorthand(props.primaryText, { required: true }), + secondaryText: resolveShorthand(props.secondaryText), + root: getNativeElementProps(interactive ? 'button' : 'div', { + ref, + ...(interactive && { type: 'button' }), + ...props, + }), + }; +}; diff --git a/packages/react-components/react-tags/src/utils/useTagContextValues.ts b/packages/react-components/react-tags/src/components/TagContent/useTagContentContextValues.ts similarity index 57% rename from packages/react-components/react-tags/src/utils/useTagContextValues.ts rename to packages/react-components/react-tags/src/components/TagContent/useTagContentContextValues.ts index d394a39d56c331..14bc83936f6f39 100644 --- a/packages/react-components/react-tags/src/utils/useTagContextValues.ts +++ b/packages/react-components/react-tags/src/components/TagContent/useTagContentContextValues.ts @@ -1,7 +1,7 @@ import * as React from 'react'; -import { TagContextValues, TagState } from '../components/Tag/index'; +import type { TagContentContextValues, TagContentState } from './TagContent.types'; -export function useTagContextValues_unstable(state: TagState): TagContextValues { +export function useTagContentContextValues_unstable(state: TagContentState): TagContentContextValues { const { avatarSize, avatarShape } = state; const avatar = React.useMemo( diff --git a/packages/react-components/react-tags/src/components/TagContent/useTagContentStyles.styles.ts b/packages/react-components/react-tags/src/components/TagContent/useTagContentStyles.styles.ts new file mode 100644 index 00000000000000..7afbfdae243c19 --- /dev/null +++ b/packages/react-components/react-tags/src/components/TagContent/useTagContentStyles.styles.ts @@ -0,0 +1,153 @@ +import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; +import type { TagContentSlots, TagContentState } from './TagContent.types'; +import type { SlotClassNames } from '@fluentui/react-utilities'; +import { tokens, typographyStyles } from '@fluentui/react-theme'; +import { createCustomFocusIndicatorStyle } from '@fluentui/react-tabster'; + +export const tagContentClassNames: SlotClassNames = { + root: 'fui-TagContent', + media: 'fui-TagContent__media', + icon: 'fui-TagContent__icon', + primaryText: 'fui-TagContent__primaryText', + secondaryText: 'fui-TagContent__secondaryText', +}; + +/** + * Styles for the root slot + */ +const useStyles = makeStyles({ + root: { + display: 'inline-grid', + gridTemplateRows: '1fr auto auto 1fr', + gridTemplateAreas: ` + "media . " + "media primary " + "media secondary" + "media . " + `, + paddingRight: tokens.spacingHorizontalS, + }, + textOnlyRoot: { + paddingLeft: tokens.spacingHorizontalS, + }, + nonInteractiveDismissableRoot: { + paddingRight: '2px', + }, + + media: { + ...shorthands.gridArea('media'), + alignSelf: 'center', + paddingLeft: tokens.spacingHorizontalXXS, + paddingRight: tokens.spacingHorizontalS, + }, + + icon: { + display: 'flex', + alignSelf: 'center', + ...shorthands.gridArea('media'), + paddingLeft: '6px', + paddingRight: '2px', + }, + primaryText: { + gridColumnStart: 'primary', + gridRowStart: 'primary', + gridRowEnd: 'secondary', + ...typographyStyles.body1, + paddingLeft: tokens.spacingHorizontalXXS, + paddingRight: tokens.spacingHorizontalXXS, + }, + primaryTextWithSecondaryText: { + ...shorthands.gridArea('primary'), + ...typographyStyles.caption1, + }, + secondaryText: { + ...shorthands.gridArea('secondary'), + paddingLeft: tokens.spacingHorizontalXXS, + paddingRight: tokens.spacingHorizontalXXS, + ...typographyStyles.caption2, + }, +}); + +/** + * Styles for the root slot + */ +const useInteractiveTagContentStyles = makeStyles({ + resetButton: { + boxSizing: 'content-box', + backgroundColor: 'transparent', + color: 'inherit', + fontFamily: 'inherit', + lineHeight: 'normal', + ...shorthands.overflow('visible'), + ...shorthands.padding(0), + ...shorthands.borderStyle('none'), + appearance: 'button', + textAlign: 'unset', + }, + root: { + position: 'relative', + ...createCustomFocusIndicatorStyle({ + ...shorthands.borderRadius(tokens.borderRadiusMedium), + ...shorthands.outline(tokens.strokeWidthThick, 'solid', tokens.colorStrokeFocus2), + }), + }, + circularRoot: createCustomFocusIndicatorStyle(shorthands.borderRadius(tokens.borderRadiusCircular), { + enableOutline: true, + }), + dismissableRoot: { + ...createCustomFocusIndicatorStyle({ + borderTopRightRadius: tokens.borderRadiusNone, + borderBottomRightRadius: tokens.borderRadiusNone, + }), + }, +}); + +/** + * Apply styling to the TagContent slots based on the state + */ +export const useTagContentStyles_unstable = (state: TagContentState): TagContentState => { + const styles = useStyles(); + const interactiveTagContentStyles = useInteractiveTagContentStyles(); + + state.root.className = mergeClasses( + tagContentClassNames.root, + state.interactive && interactiveTagContentStyles.resetButton, + + styles.root, + !state.media && !state.icon && styles.textOnlyRoot, + + !state.interactive && state.dismissable && styles.nonInteractiveDismissableRoot, + + state.interactive && interactiveTagContentStyles.root, + state.interactive && state.shape === 'circular' && interactiveTagContentStyles.circularRoot, + state.interactive && state.dismissable && interactiveTagContentStyles.dismissableRoot, + + state.root.className, + ); + + if (state.media) { + state.media.className = mergeClasses(tagContentClassNames.media, styles.media, state.media.className); + } + + if (state.icon) { + state.icon.className = mergeClasses(tagContentClassNames.icon, styles.icon, state.icon.className); + } + + if (state.primaryText) { + state.primaryText.className = mergeClasses( + tagContentClassNames.primaryText, + styles.primaryText, + state.secondaryText && styles.primaryTextWithSecondaryText, + state.primaryText.className, + ); + } + + if (state.secondaryText) { + state.secondaryText.className = mergeClasses( + tagContentClassNames.secondaryText, + styles.secondaryText, + state.secondaryText.className, + ); + } + return state; +}; diff --git a/packages/react-components/react-tags/src/index.ts b/packages/react-components/react-tags/src/index.ts index 4d1d8029598994..7a26dabaf87916 100644 --- a/packages/react-components/react-tags/src/index.ts +++ b/packages/react-components/react-tags/src/index.ts @@ -1,10 +1,10 @@ export { Tag, renderTag_unstable, tagClassNames, useTagStyles_unstable, useTag_unstable } from './Tag'; export type { TagProps, TagSlots, TagState } from './Tag'; export { - TagButton, - renderTagButton_unstable, - tagButtonClassNames, - useTagButtonStyles_unstable, - useTagButton_unstable, -} from './TagButton'; -export type { TagButtonProps, TagButtonSlots, TagButtonState } from './TagButton'; + TagContent, + renderTagContent_unstable, + tagContentClassNames, + useTagContentStyles_unstable, + useTagContent_unstable, +} from './TagContent'; +export type { TagContentProps, TagContentSlots, TagContentState } from './TagContent'; diff --git a/packages/react-components/react-tags/src/testing/isConformant.ts b/packages/react-components/react-tags/src/testing/isConformant.ts index a3d988f29a1728..fa3a039bca82b1 100644 --- a/packages/react-components/react-tags/src/testing/isConformant.ts +++ b/packages/react-components/react-tags/src/testing/isConformant.ts @@ -6,6 +6,7 @@ export function isConformant( testInfo: Omit, 'componentPath'> & { componentPath?: string }, ) { const defaultOptions: Partial> = { + tsConfig: { configName: 'tsconfig.spec.json' }, tsConfig: { configName: 'tsconfig.spec.json' }, componentPath: require.main?.filename.replace('.test', ''), extraTests: griffelTests as TestObject, diff --git a/packages/react-components/react-tags/stories/Tag/TagDefault.stories.tsx b/packages/react-components/react-tags/stories/Tag/TagDefault.stories.tsx index 85917d78a12326..b1afc414485949 100644 --- a/packages/react-components/react-tags/stories/Tag/TagDefault.stories.tsx +++ b/packages/react-components/react-tags/stories/Tag/TagDefault.stories.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Tag, TagProps } from '@fluentui/react-tags'; +import { Tag, TagContent, TagProps } from '@fluentui/react-tags'; import { Calendar3Day20Regular } from '@fluentui/react-icons'; import { Avatar } from '@fluentui/react-components'; @@ -7,141 +7,151 @@ import { Avatar } from '@fluentui/react-components'; export const Default = (props: Partial) => (
- - } - secondaryText="Secondary text" - dismissable={true} - {...props} - > - Primary text + + + } + secondaryText="Secondary text" + > + Primary text + - - } - dismissable={true} - {...props} - > - Primary text + + + + } + > + Primary text + + + + + + } + > + Primary text + - - } - {...props} - > - Primary text + + + } secondaryText="Secondary text"> + Primary text + - } secondaryText="Secondary text" dismissable={true} {...props} {...props}> - Primary text + + + }>Primary text - } dismissable={true} {...props}> - Primary text + + + }>Primary text - } {...props}> - Primary text + + + Primary text - Primary text
- - } - secondaryText="Secondary text" - dismissable={true} - {...props} - > - Primary text + + + } + secondaryText="Secondary text" + > + Primary text + - - } - dismissable={true} - {...props} - > - Primary text + + + + } + > + Primary text + - - } - {...props} - > - Primary text + + + + } + > + Primary text + - } - secondaryText="Secondary text" - dismissable={true} - {...props} - {...props} - > - Primary text + + + } secondaryText="Secondary text"> + Primary text + - } dismissable={true} {...props}> - Primary text + + + }>Primary text - } {...props}> - Primary text + + + }>Primary text + - Primary text + Primary text
diff --git a/packages/react-components/react-tags/stories/Tag/TagDismiss.stories.tsx b/packages/react-components/react-tags/stories/Tag/TagDismiss.stories.tsx index c5b805116d96d4..d91466ea4c6dd5 100644 --- a/packages/react-components/react-tags/stories/Tag/TagDismiss.stories.tsx +++ b/packages/react-components/react-tags/stories/Tag/TagDismiss.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Avatar, makeStyles } from '@fluentui/react-components'; import { Calendar3Day20Regular } from '@fluentui/react-icons'; -import { Tag } from '@fluentui/react-tags'; +import { Tag, TagContent } from '@fluentui/react-tags'; const useContainerStyles = makeStyles({ root: { @@ -16,16 +16,16 @@ export const Dismiss = () => { const containerStyles = useContainerStyles(); return (
- Primary text - }> - Primary text + + Primary text - } - secondaryText="Secondary text" - > - Primary text + + }>Primary text{' '} + + + } secondaryText="Secondary text"> + Primary text +
); diff --git a/packages/react-components/react-tags/stories/Tag/TagIcon.stories.tsx b/packages/react-components/react-tags/stories/Tag/TagIcon.stories.tsx index 28e79a1dc3e3f4..854fa1ec9ce724 100644 --- a/packages/react-components/react-tags/stories/Tag/TagIcon.stories.tsx +++ b/packages/react-components/react-tags/stories/Tag/TagIcon.stories.tsx @@ -1,14 +1,18 @@ import * as React from 'react'; import { Calendar3Day20Regular } from '@fluentui/react-icons'; -import { Tag } from '@fluentui/react-tags'; +import { Tag, TagContent } from '@fluentui/react-tags'; -export const Icon = () => }>Primary text; +export const Icon = () => ( + + }>Primary text + +); Icon.storyName = 'Icon'; Icon.parameters = { docs: { description: { - story: 'A Tag can render a custom icon if provided.', + story: 'A TagContent can render a custom icon if provided.', }, }, }; diff --git a/packages/react-components/react-tags/stories/Tag/TagInteractive.stories.tsx b/packages/react-components/react-tags/stories/Tag/TagInteractive.stories.tsx new file mode 100644 index 00000000000000..a745d0232770ce --- /dev/null +++ b/packages/react-components/react-tags/stories/Tag/TagInteractive.stories.tsx @@ -0,0 +1,62 @@ +import * as React from 'react'; +import { Tag, TagContent } from '@fluentui/react-tags'; +import { Calendar3Day20Regular } from '@fluentui/react-icons'; +import { Avatar, makeStyles } from '@fluentui/react-components'; + +const useContainerStyles = makeStyles({ + root: { + display: 'flex', + flexDirection: 'column', + rowGap: '10px', + }, +}); + +export const Interactive = () => { + const containerStyles = useContainerStyles(); + + return ( +
+ + { + console.log('Tag with avatar clicked'); + }} + media={ + + } + secondaryText="Secondary text" + > + Primary text + + + + + { + console.log('Tag with icon clicked'); + }} + icon={} + > + Primary text + + +
+ ); +}; + +Interactive.storyName = 'Interactive'; +Interactive.parameters = { + docs: { + description: { + story: 'A Tag can render as an interactive button', + }, + }, +}; diff --git a/packages/react-components/react-tags/stories/Tag/TagMedia.stories.tsx b/packages/react-components/react-tags/stories/Tag/TagMedia.stories.tsx index 941fbec7e0262a..e5bf7e078353b2 100644 --- a/packages/react-components/react-tags/stories/Tag/TagMedia.stories.tsx +++ b/packages/react-components/react-tags/stories/Tag/TagMedia.stories.tsx @@ -1,15 +1,19 @@ import * as React from 'react'; import { Avatar } from '@fluentui/react-components'; -import { Tag } from '@fluentui/react-tags'; +import { Tag, TagContent } from '@fluentui/react-tags'; -export const Media = () => }>Primary text; +export const Media = () => ( + + }>Primary text + +); Media.storyName = 'Media'; Media.parameters = { docs: { description: { - story: 'A tag can render a media, for example an Avatar.', + story: 'A TagContent can render a media, for example an Avatar.', }, }, }; diff --git a/packages/react-components/react-tags/stories/Tag/TagSecondaryText.stories.tsx b/packages/react-components/react-tags/stories/Tag/TagSecondaryText.stories.tsx index 1f16e2b7b0815d..2f903564c179a8 100644 --- a/packages/react-components/react-tags/stories/Tag/TagSecondaryText.stories.tsx +++ b/packages/react-components/react-tags/stories/Tag/TagSecondaryText.stories.tsx @@ -1,13 +1,17 @@ import * as React from 'react'; -import { Tag } from '@fluentui/react-tags'; +import { Tag, TagContent } from '@fluentui/react-tags'; -export const SecondaryText = () => Primary text; +export const SecondaryText = () => ( + + Primary text + +); SecondaryText.storyName = 'SecondaryText'; SecondaryText.parameters = { docs: { description: { - story: 'A Tag can have a secondary text.', + story: 'A TagContent can have a secondary text.', }, }, }; diff --git a/packages/react-components/react-tags/stories/Tag/TagShape.stories.tsx b/packages/react-components/react-tags/stories/Tag/TagShape.stories.tsx index 620b4001ce5e12..0cd17e3f59d1ba 100644 --- a/packages/react-components/react-tags/stories/Tag/TagShape.stories.tsx +++ b/packages/react-components/react-tags/stories/Tag/TagShape.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Avatar, makeStyles } from '@fluentui/react-components'; import { Calendar3Day20Regular } from '@fluentui/react-icons'; -import { Tag } from '@fluentui/react-tags'; +import { Tag, TagContent } from '@fluentui/react-tags'; const useContainerStyles = makeStyles({ root: { @@ -17,16 +17,22 @@ export const Shape = () => { const containerStyles = useContainerStyles(); return (
- }>Rounded - }> - Circular + + }>Rounded + + + }>Circular - } secondaryText="Secondary text"> - Rounded + + } secondaryText="Secondary text"> + Rounded + - } secondaryText="Secondary text"> - Circular + + } secondaryText="Secondary text"> + Circular +
); diff --git a/packages/react-components/react-tags/stories/Tag/index.stories.tsx b/packages/react-components/react-tags/stories/Tag/index.stories.tsx index 2aea59a32ff446..fb7ff1cf761bfb 100644 --- a/packages/react-components/react-tags/stories/Tag/index.stories.tsx +++ b/packages/react-components/react-tags/stories/Tag/index.stories.tsx @@ -9,6 +9,7 @@ export { Media } from './TagMedia.stories'; export { SecondaryText } from './TagSecondaryText.stories'; export { Dismiss } from './TagDismiss.stories'; export { Shape } from './TagShape.stories'; +export { Interactive } from './TagInteractive.stories'; export default { title: 'Preview Components/Tag', diff --git a/packages/react-components/react-tags/stories/TagButton/TagButtonBestPractices.md b/packages/react-components/react-tags/stories/TagButton/TagButtonBestPractices.md deleted file mode 100644 index 08ff8ddeeb5f86..00000000000000 --- a/packages/react-components/react-tags/stories/TagButton/TagButtonBestPractices.md +++ /dev/null @@ -1,5 +0,0 @@ -## Best practices - -### Do - -### Don't diff --git a/packages/react-components/react-tags/stories/TagButton/TagButtonDefault.stories.tsx b/packages/react-components/react-tags/stories/TagButton/TagButtonDefault.stories.tsx deleted file mode 100644 index a9ef63153070a5..00000000000000 --- a/packages/react-components/react-tags/stories/TagButton/TagButtonDefault.stories.tsx +++ /dev/null @@ -1,154 +0,0 @@ -import * as React from 'react'; -import { TagButton, TagButtonProps } from '@fluentui/react-tags'; -import { Calendar3Day20Regular } from '@fluentui/react-icons'; -import { Avatar } from '@fluentui/react-components'; - -// TODO I added many examples here for easier implementation. This story will be simplified to keep only the default example -export const Default = (props: Partial) => ( -
-
- - } - secondaryText="Secondary text" - dismissable={true} - {...props} - > - Primary text - - - } - dismissable={true} - {...props} - > - Primary text - - - } - {...props} - > - Primary text - - } - secondaryText="Secondary text" - dismissable={true} - {...props} - {...props} - > - Primary text - - } dismissable={true} {...props}> - Primary text - - } {...props}> - Primary text - - Primary text -
-
- - } - secondaryText="Secondary text" - dismissable={true} - {...props} - > - Primary text - - - } - dismissable={true} - {...props} - > - Primary text - - - } - {...props} - > - Primary text - - } - secondaryText="Secondary text" - dismissable={true} - {...props} - {...props} - > - Primary text - - } dismissable={true} {...props}> - Primary text - - } {...props}> - Primary text - - - Primary text - -
-
-); diff --git a/packages/react-components/react-tags/stories/TagButton/TagButtonDescription.md b/packages/react-components/react-tags/stories/TagButton/TagButtonDescription.md deleted file mode 100644 index e69de29bb2d1d6..00000000000000 diff --git a/packages/react-components/react-tags/stories/TagButton/TagButtonDismiss.stories.tsx b/packages/react-components/react-tags/stories/TagButton/TagButtonDismiss.stories.tsx deleted file mode 100644 index f10a12d9b45e1e..00000000000000 --- a/packages/react-components/react-tags/stories/TagButton/TagButtonDismiss.stories.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import * as React from 'react'; -import { Avatar, makeStyles } from '@fluentui/react-components'; -import { Calendar3Day20Regular } from '@fluentui/react-icons'; - -import { TagButton } from '@fluentui/react-tags'; - -const useContainerStyles = makeStyles({ - root: { - display: 'flex', - flexDirection: 'column', - rowGap: '10px', - }, -}); - -export const Dismiss = () => { - const containerStyles = useContainerStyles(); - return ( -
- Primary text - }> - Primary text - - } - secondaryText="Secondary text" - > - Primary text - -
- ); -}; - -Dismiss.storyName = 'Dismiss'; -Dismiss.parameters = { - docs: { - description: { - story: 'A TagButton can have a button that dismisses it', - }, - }, -}; diff --git a/packages/react-components/react-tags/stories/TagButton/TagButtonIcon.stories.tsx b/packages/react-components/react-tags/stories/TagButton/TagButtonIcon.stories.tsx deleted file mode 100644 index 621e32e4db1c0e..00000000000000 --- a/packages/react-components/react-tags/stories/TagButton/TagButtonIcon.stories.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import * as React from 'react'; -import { Calendar3Day20Regular } from '@fluentui/react-icons'; -import { TagButton } from '@fluentui/react-tags'; - -export const Icon = () => }>Primary text; - -Icon.storyName = 'Icon'; -Icon.parameters = { - docs: { - description: { - story: 'A TagButton can render a custom icon if provided.', - }, - }, -}; diff --git a/packages/react-components/react-tags/stories/TagButton/TagButtonMedia.stories.tsx b/packages/react-components/react-tags/stories/TagButton/TagButtonMedia.stories.tsx deleted file mode 100644 index 93db18c6b107ba..00000000000000 --- a/packages/react-components/react-tags/stories/TagButton/TagButtonMedia.stories.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import * as React from 'react'; -import { Avatar } from '@fluentui/react-components'; - -import { TagButton } from '@fluentui/react-tags'; - -export const Media = () => ( - }>Primary text -); - -Media.storyName = 'Media'; -Media.parameters = { - docs: { - description: { - story: 'A tag can render a media, for example an Avatar.', - }, - }, -}; diff --git a/packages/react-components/react-tags/stories/TagButton/TagButtonSecondaryText.stories.tsx b/packages/react-components/react-tags/stories/TagButton/TagButtonSecondaryText.stories.tsx deleted file mode 100644 index 26f4145b44d28c..00000000000000 --- a/packages/react-components/react-tags/stories/TagButton/TagButtonSecondaryText.stories.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import * as React from 'react'; -import { TagButton } from '@fluentui/react-tags'; - -export const SecondaryText = () => Primary text; - -SecondaryText.storyName = 'SecondaryText'; -SecondaryText.parameters = { - docs: { - description: { - story: 'A TagButton can have a secondary text.', - }, - }, -}; diff --git a/packages/react-components/react-tags/stories/TagButton/TagButtonShape.stories.tsx b/packages/react-components/react-tags/stories/TagButton/TagButtonShape.stories.tsx deleted file mode 100644 index 3f45196559c4b0..00000000000000 --- a/packages/react-components/react-tags/stories/TagButton/TagButtonShape.stories.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import * as React from 'react'; -import { Avatar, makeStyles } from '@fluentui/react-components'; -import { Calendar3Day20Regular } from '@fluentui/react-icons'; - -import { TagButton } from '@fluentui/react-tags'; - -const useContainerStyles = makeStyles({ - root: { - display: 'grid', - rowGap: '10px', - columnGap: '10px', - gridTemplateColumns: 'auto 1fr', - }, -}); - -export const Shape = () => { - const containerStyles = useContainerStyles(); - return ( -
- }>Rounded - }> - Circular - - - } secondaryText="Secondary text"> - Rounded - - } secondaryText="Secondary text"> - Circular - -
- ); -}; - -Shape.storyName = 'Shape'; -Shape.parameters = { - docs: { - description: { - story: 'A TagButton can be rounded or circular,', - }, - }, -}; diff --git a/packages/react-components/react-tags/stories/TagButton/index.stories.tsx b/packages/react-components/react-tags/stories/TagButton/index.stories.tsx deleted file mode 100644 index 38b25718ef37c5..00000000000000 --- a/packages/react-components/react-tags/stories/TagButton/index.stories.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { TagButton } from '@fluentui/react-tags'; - -import descriptionMd from './TagButtonDescription.md'; -import bestPracticesMd from './TagButtonBestPractices.md'; - -export { Default } from './TagButtonDefault.stories'; -export { Icon } from './TagButtonIcon.stories'; -export { Media } from './TagButtonMedia.stories'; -export { SecondaryText } from './TagButtonSecondaryText.stories'; -export { Dismiss } from './TagButtonDismiss.stories'; -export { Shape } from './TagButtonShape.stories'; - -export default { - title: 'Preview Components/TagButton', - component: TagButton, - parameters: { - docs: { - description: { - component: [descriptionMd, bestPracticesMd].join('\n'), - }, - }, - }, -}; From a19e7b5ea7ae4070635236e8b0fecca139397271 Mon Sep 17 00:00:00 2001 From: YuanboXue-Amber Date: Tue, 9 May 2023 11:38:26 +0800 Subject: [PATCH 02/18] dismissable to dismissible --- .../react-tags/etc/react-tags.api.md | 6 +++--- .../react-tags/src/components/Tag/Tag.test.tsx | 2 +- .../react-tags/src/components/Tag/Tag.types.ts | 6 +++--- .../react-tags/src/components/Tag/TagContext.ts | 2 +- .../react-tags/src/components/Tag/renderTag.tsx | 2 +- .../react-tags/src/components/Tag/useTag.tsx | 6 +++--- .../src/components/Tag/useTagContextValues.ts | 4 ++-- .../components/TagContent/TagContent.types.ts | 2 +- .../src/components/TagContent/useTagContent.ts | 4 ++-- .../TagContent/useTagContentStyles.styles.ts | 4 ++-- .../stories/Tag/TagDefault.stories.tsx | 16 ++++++++-------- .../stories/Tag/TagDismiss.stories.tsx | 6 +++--- .../stories/Tag/TagInteractive.stories.tsx | 2 +- .../react-tags/stories/Tag/TagShape.stories.tsx | 4 ++-- 14 files changed, 33 insertions(+), 33 deletions(-) diff --git a/packages/react-components/react-tags/etc/react-tags.api.md b/packages/react-components/react-tags/etc/react-tags.api.md index 949f50ccb4b8b8..4a925047c48dae 100644 --- a/packages/react-components/react-tags/etc/react-tags.api.md +++ b/packages/react-components/react-tags/etc/react-tags.api.md @@ -49,7 +49,7 @@ export type TagContentSlots = { export type TagContentState = ComponentState & Required<{ avatarSize: AvatarSize; avatarShape: AvatarShape; - dismissable: boolean; + dismissible: boolean; shape?: 'rounded' | 'circular'; }>; @@ -60,7 +60,7 @@ export type TagProps = ComponentProps> & { appearance?: 'filled-darker' | 'filled-lighter' | 'tint' | 'outline'; disabled?: boolean; checked?: boolean; - dismissable?: boolean; + dismissible?: boolean; interactive?: boolean; }; @@ -71,7 +71,7 @@ export type TagSlots = { }; // @public -export type TagState = ComponentState & Required>; +export type TagState = ComponentState & Required>; // @public export const useTag_unstable: (props: TagProps, ref: React_2.Ref) => TagState; diff --git a/packages/react-components/react-tags/src/components/Tag/Tag.test.tsx b/packages/react-components/react-tags/src/components/Tag/Tag.test.tsx index 770f377409ea8c..a56f2a235dce60 100644 --- a/packages/react-components/react-tags/src/components/Tag/Tag.test.tsx +++ b/packages/react-components/react-tags/src/components/Tag/Tag.test.tsx @@ -3,7 +3,7 @@ import { isConformant } from '../../testing/isConformant'; import { TagProps } from './Tag.types'; const requiredProps: TagProps = { - dismissable: true, + dismissible: true, }; describe('Tag', () => { diff --git a/packages/react-components/react-tags/src/components/Tag/Tag.types.ts b/packages/react-components/react-tags/src/components/Tag/Tag.types.ts index 8b67e68f729da7..25d2ffd27d1174 100644 --- a/packages/react-components/react-tags/src/components/Tag/Tag.types.ts +++ b/packages/react-components/react-tags/src/components/Tag/Tag.types.ts @@ -1,6 +1,6 @@ import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; -export type TagContextValue = Required>; +export type TagContextValue = Required>; export type TagContextValues = { tag: TagContextValue; @@ -21,7 +21,7 @@ export type TagProps = ComponentProps> & { appearance?: 'filled-darker' | 'filled-lighter' | 'tint' | 'outline'; disabled?: boolean; checked?: boolean; - dismissable?: boolean; + dismissible?: boolean; interactive?: boolean; }; @@ -29,4 +29,4 @@ export type TagProps = ComponentProps> & { * State used in rendering Tag */ export type TagState = ComponentState & - Required>; + Required>; diff --git a/packages/react-components/react-tags/src/components/Tag/TagContext.ts b/packages/react-components/react-tags/src/components/Tag/TagContext.ts index 1666fb69c6140c..9be741b2cc02b7 100644 --- a/packages/react-components/react-tags/src/components/Tag/TagContext.ts +++ b/packages/react-components/react-tags/src/components/Tag/TagContext.ts @@ -7,7 +7,7 @@ export const TagContext: Context = createContext; const tagContextDefaultValue: TagContextValue = { - dismissable: false, + dismissible: false, shape: 'rounded', size: 'medium', interactive: false, diff --git a/packages/react-components/react-tags/src/components/Tag/renderTag.tsx b/packages/react-components/react-tags/src/components/Tag/renderTag.tsx index df641ea71a4761..17e18197db8875 100644 --- a/packages/react-components/react-tags/src/components/Tag/renderTag.tsx +++ b/packages/react-components/react-tags/src/components/Tag/renderTag.tsx @@ -17,7 +17,7 @@ export const renderTag_unstable = (state: TagState, contextValues: TagContextVal {slotProps.root.children} - {slots.dismissButton && state.dismissable && } + {slots.dismissButton && } ); diff --git a/packages/react-components/react-tags/src/components/Tag/useTag.tsx b/packages/react-components/react-tags/src/components/Tag/useTag.tsx index 8e614254b7dc0e..54281d55dbc70b 100644 --- a/packages/react-components/react-tags/src/components/Tag/useTag.tsx +++ b/packages/react-components/react-tags/src/components/Tag/useTag.tsx @@ -19,7 +19,7 @@ export const useTag_unstable = (props: TagProps, ref: React.Ref): T const { checked = false, disabled = false, - dismissable = false, + dismissible = false, shape = 'rounded', size = 'medium', appearance = 'filled-lighter', @@ -34,7 +34,7 @@ export const useTag_unstable = (props: TagProps, ref: React.Ref): T checked, disabled, - dismissable, + dismissible, shape, size, appearance, @@ -46,7 +46,7 @@ export const useTag_unstable = (props: TagProps, ref: React.Ref): T }), dismissButton: useARIAButtonShorthand(props.dismissButton, { - required: props.dismissable, + required: props.dismissible, defaultProps: { disabled, type: 'button', diff --git a/packages/react-components/react-tags/src/components/Tag/useTagContextValues.ts b/packages/react-components/react-tags/src/components/Tag/useTagContextValues.ts index a76e794b568cdf..2a9b7efed88c17 100644 --- a/packages/react-components/react-tags/src/components/Tag/useTagContextValues.ts +++ b/packages/react-components/react-tags/src/components/Tag/useTagContextValues.ts @@ -1,11 +1,11 @@ import type { TagContextValue, TagContextValues, TagState } from './Tag.types'; export function useTagContextValues_unstable(state: TagState): TagContextValues { - const { dismissable, shape, size, interactive } = state; + const { dismissible, shape, size, interactive } = state; // This context is created with "@fluentui/react-context-selector", these is no sense to memoize it const tag: TagContextValue = { - dismissable, + dismissible, shape, size, interactive, diff --git a/packages/react-components/react-tags/src/components/TagContent/TagContent.types.ts b/packages/react-components/react-tags/src/components/TagContent/TagContent.types.ts index 1e9db5e1c3d24a..c392a46c804acd 100644 --- a/packages/react-components/react-tags/src/components/TagContent/TagContent.types.ts +++ b/packages/react-components/react-tags/src/components/TagContent/TagContent.types.ts @@ -43,7 +43,7 @@ export type TagContentProps = ComponentProps>; */ export type TagContentState = ComponentState & Required< - Pick & { + Pick & { avatarSize: AvatarSize; avatarShape: AvatarShape; } diff --git a/packages/react-components/react-tags/src/components/TagContent/useTagContent.ts b/packages/react-components/react-tags/src/components/TagContent/useTagContent.ts index c91969639c1859..34d88981fdea43 100644 --- a/packages/react-components/react-tags/src/components/TagContent/useTagContent.ts +++ b/packages/react-components/react-tags/src/components/TagContent/useTagContent.ts @@ -24,7 +24,7 @@ const tagContentAvatarShapeMap = { * @param ref - reference to root HTMLElement of TagContent */ export const useTagContent_unstable = (props: TagContentProps, ref: React.Ref): TagContentState => { - const dismissable = useTagContext_unstable(ctx => ctx.dismissable); + const dismissible = useTagContext_unstable(ctx => ctx.dismissible); const shape = useTagContext_unstable(ctx => ctx.shape); const size = useTagContext_unstable(ctx => ctx.size); const interactive = useTagContext_unstable(ctx => ctx.interactive); @@ -40,7 +40,7 @@ export const useTagContent_unstable = (props: TagContentProps, ref: React.Ref) => (
- + ) => ( - + ) => ( - + } secondaryText="Secondary text"> Primary text - + }>Primary text @@ -81,7 +81,7 @@ export const Default = (props: Partial) => (
- + ) => ( - + ) => ( - + } secondaryText="Secondary text"> Primary text - + }>Primary text diff --git a/packages/react-components/react-tags/stories/Tag/TagDismiss.stories.tsx b/packages/react-components/react-tags/stories/Tag/TagDismiss.stories.tsx index d91466ea4c6dd5..688ded56632095 100644 --- a/packages/react-components/react-tags/stories/Tag/TagDismiss.stories.tsx +++ b/packages/react-components/react-tags/stories/Tag/TagDismiss.stories.tsx @@ -16,13 +16,13 @@ export const Dismiss = () => { const containerStyles = useContainerStyles(); return (
- + Primary text - + }>Primary text{' '} - + } secondaryText="Secondary text"> Primary text diff --git a/packages/react-components/react-tags/stories/Tag/TagInteractive.stories.tsx b/packages/react-components/react-tags/stories/Tag/TagInteractive.stories.tsx index a745d0232770ce..5da6be892642d2 100644 --- a/packages/react-components/react-tags/stories/Tag/TagInteractive.stories.tsx +++ b/packages/react-components/react-tags/stories/Tag/TagInteractive.stories.tsx @@ -16,7 +16,7 @@ export const Interactive = () => { return (
- + { console.log('Tag with avatar clicked'); diff --git a/packages/react-components/react-tags/stories/Tag/TagShape.stories.tsx b/packages/react-components/react-tags/stories/Tag/TagShape.stories.tsx index 0cd17e3f59d1ba..59e30bba9ab60e 100644 --- a/packages/react-components/react-tags/stories/Tag/TagShape.stories.tsx +++ b/packages/react-components/react-tags/stories/Tag/TagShape.stories.tsx @@ -24,12 +24,12 @@ export const Shape = () => { }>Circular - + } secondaryText="Secondary text"> Rounded - + } secondaryText="Secondary text"> Circular From bfe0c0e54db9a669e56c210c2d69bdf5bf3367f4 Mon Sep 17 00:00:00 2001 From: YuanboXue-Amber Date: Tue, 9 May 2023 14:17:51 +0800 Subject: [PATCH 03/18] api update --- packages/react-components/react-tags/etc/react-tags.api.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/react-components/react-tags/etc/react-tags.api.md b/packages/react-components/react-tags/etc/react-tags.api.md index 4a925047c48dae..951e2b7dbfa9ab 100644 --- a/packages/react-components/react-tags/etc/react-tags.api.md +++ b/packages/react-components/react-tags/etc/react-tags.api.md @@ -38,7 +38,7 @@ export type TagContentProps = ComponentProps>; // @public (undocumented) export type TagContentSlots = { - root: NonNullable>; + root: Slot<'div', 'button'>; media: Slot<'span'>; icon: Slot<'span'>; primaryText: Slot<'span'>; @@ -46,11 +46,9 @@ export type TagContentSlots = { }; // @public -export type TagContentState = ComponentState & Required<{ +export type TagContentState = ComponentState & Required & { avatarSize: AvatarSize; avatarShape: AvatarShape; - dismissible: boolean; - shape?: 'rounded' | 'circular'; }>; // @public From a9f90d16623c3cce01a43ad3a88373d23b0129cc Mon Sep 17 00:00:00 2001 From: YuanboXue-Amber Date: Tue, 9 May 2023 14:33:18 +0800 Subject: [PATCH 04/18] remove crud --- packages/react-components/react-tags/src/testing/isConformant.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react-components/react-tags/src/testing/isConformant.ts b/packages/react-components/react-tags/src/testing/isConformant.ts index fa3a039bca82b1..a3d988f29a1728 100644 --- a/packages/react-components/react-tags/src/testing/isConformant.ts +++ b/packages/react-components/react-tags/src/testing/isConformant.ts @@ -6,7 +6,6 @@ export function isConformant( testInfo: Omit, 'componentPath'> & { componentPath?: string }, ) { const defaultOptions: Partial> = { - tsConfig: { configName: 'tsconfig.spec.json' }, tsConfig: { configName: 'tsconfig.spec.json' }, componentPath: require.main?.filename.replace('.test', ''), extraTests: griffelTests as TestObject, From 004994b1b0349f863a523f6e5b9ae9549de6a8f2 Mon Sep 17 00:00:00 2001 From: YuanboXue-Amber Date: Tue, 9 May 2023 16:06:59 +0800 Subject: [PATCH 05/18] use props.children as default children for primaryText --- .../react-tags/src/components/TagContent/renderTagContent.tsx | 2 +- .../react-tags/src/components/TagContent/useTagContent.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-components/react-tags/src/components/TagContent/renderTagContent.tsx b/packages/react-components/react-tags/src/components/TagContent/renderTagContent.tsx index e9d896d8584e2d..5129c544a6ff34 100644 --- a/packages/react-components/react-tags/src/components/TagContent/renderTagContent.tsx +++ b/packages/react-components/react-tags/src/components/TagContent/renderTagContent.tsx @@ -17,7 +17,7 @@ export const renderTagContent_unstable = (state: TagContentState, contextValues: )} {slots.icon && } - {slots.primaryText && {slotProps.root.children}} + {slots.primaryText && } {slots.secondaryText && } ); diff --git a/packages/react-components/react-tags/src/components/TagContent/useTagContent.ts b/packages/react-components/react-tags/src/components/TagContent/useTagContent.ts index 34d88981fdea43..0fa3d1c06cb74b 100644 --- a/packages/react-components/react-tags/src/components/TagContent/useTagContent.ts +++ b/packages/react-components/react-tags/src/components/TagContent/useTagContent.ts @@ -46,7 +46,7 @@ export const useTagContent_unstable = (props: TagContentProps, ref: React.Ref Date: Tue, 9 May 2023 16:21:56 +0800 Subject: [PATCH 06/18] remove context selector --- .../react-components/react-tags/package.json | 1 - .../react-tags/src/components/Tag/TagContext.ts | 10 +++------- .../src/components/Tag/useTagContextValues.ts | 17 ++++++++++------- .../src/components/TagContent/useTagContent.ts | 5 +---- 4 files changed, 14 insertions(+), 19 deletions(-) diff --git a/packages/react-components/react-tags/package.json b/packages/react-components/react-tags/package.json index 73b618349759be..df38951b5f032f 100644 --- a/packages/react-components/react-tags/package.json +++ b/packages/react-components/react-tags/package.json @@ -36,7 +36,6 @@ "dependencies": { "@fluentui/react-aria": "^9.3.18", "@fluentui/react-avatar": "^9.4.10", - "@fluentui/react-context-selector": "^9.1.18", "@fluentui/react-icons": "^2.0.196", "@fluentui/react-jsx-runtime": "9.0.0-alpha.2", "@fluentui/react-tabster": "^9.6.5", diff --git a/packages/react-components/react-tags/src/components/Tag/TagContext.ts b/packages/react-components/react-tags/src/components/Tag/TagContext.ts index 9be741b2cc02b7..bc89afe0e80a35 100644 --- a/packages/react-components/react-tags/src/components/Tag/TagContext.ts +++ b/packages/react-components/react-tags/src/components/Tag/TagContext.ts @@ -1,10 +1,7 @@ -import { createContext, ContextSelector, useContextSelector } from '@fluentui/react-context-selector'; -import type { Context } from '@fluentui/react-context-selector'; +import * as React from 'react'; import type { TagContextValue } from './Tag.types'; -export const TagContext: Context = createContext( - undefined, -) as Context; +export const TagContext = React.createContext(undefined); const tagContextDefaultValue: TagContextValue = { dismissible: false, @@ -14,5 +11,4 @@ const tagContextDefaultValue: TagContextValue = { }; export const TagProvider = TagContext.Provider; -export const useTagContext_unstable = (selector: ContextSelector): T => - useContextSelector(TagContext, (ctx = tagContextDefaultValue) => selector(ctx)); +export const useTagContext_unstable = () => React.useContext(TagContext) ?? tagContextDefaultValue; diff --git a/packages/react-components/react-tags/src/components/Tag/useTagContextValues.ts b/packages/react-components/react-tags/src/components/Tag/useTagContextValues.ts index 2a9b7efed88c17..72fc259b70244f 100644 --- a/packages/react-components/react-tags/src/components/Tag/useTagContextValues.ts +++ b/packages/react-components/react-tags/src/components/Tag/useTagContextValues.ts @@ -1,15 +1,18 @@ +import * as React from 'react'; import type { TagContextValue, TagContextValues, TagState } from './Tag.types'; export function useTagContextValues_unstable(state: TagState): TagContextValues { const { dismissible, shape, size, interactive } = state; - // This context is created with "@fluentui/react-context-selector", these is no sense to memoize it - const tag: TagContextValue = { - dismissible, - shape, - size, - interactive, - }; + const tag = React.useMemo( + () => ({ + dismissible, + shape, + size, + interactive, + }), + [dismissible, interactive, shape, size], + ); return { tag }; } diff --git a/packages/react-components/react-tags/src/components/TagContent/useTagContent.ts b/packages/react-components/react-tags/src/components/TagContent/useTagContent.ts index 0fa3d1c06cb74b..dd624c31558ed1 100644 --- a/packages/react-components/react-tags/src/components/TagContent/useTagContent.ts +++ b/packages/react-components/react-tags/src/components/TagContent/useTagContent.ts @@ -24,10 +24,7 @@ const tagContentAvatarShapeMap = { * @param ref - reference to root HTMLElement of TagContent */ export const useTagContent_unstable = (props: TagContentProps, ref: React.Ref): TagContentState => { - const dismissible = useTagContext_unstable(ctx => ctx.dismissible); - const shape = useTagContext_unstable(ctx => ctx.shape); - const size = useTagContext_unstable(ctx => ctx.size); - const interactive = useTagContext_unstable(ctx => ctx.interactive); + const { dismissible, shape, size, interactive } = useTagContext_unstable(); return { components: { From b48a5789c9dad04fe2ece819c9a8cdba12bbafda Mon Sep 17 00:00:00 2001 From: YuanboXue-Amber Date: Tue, 9 May 2023 17:21:27 +0800 Subject: [PATCH 07/18] restore tagContent change --- .../react-tags/src/TagContent.ts | 1 - .../src/components/Tag/Tag.types.ts | 44 +++- .../src/components/Tag/TagContext.ts | 14 - .../react-tags/src/components/Tag/index.ts | 1 - .../src/components/Tag/renderTag.tsx | 19 +- .../react-tags/src/components/Tag/useTag.tsx | 46 +++- .../src/components/Tag/useTagContextValues.ts | 18 +- .../src/components/Tag/useTagStyles.styles.ts | 165 ++++++++++-- .../components/TagContent/TagContent.test.tsx | 20 -- .../src/components/TagContent/TagContent.tsx | 19 -- .../components/TagContent/TagContent.types.ts | 50 ---- .../src/components/TagContent/index.ts | 5 - .../TagContent/renderTagContent.tsx | 24 -- .../components/TagContent/useTagContent.ts | 54 ---- .../TagContent/useTagContentContextValues.ts | 18 -- .../TagContent/useTagContentStyles.styles.ts | 153 ----------- .../react-components/react-tags/src/index.ts | 8 - .../stories/Tag/TagDefault.stories.tsx | 243 ++++++++---------- .../stories/Tag/TagDismiss.stories.tsx | 20 +- .../stories/Tag/TagIcon.stories.tsx | 10 +- .../stories/Tag/TagInteractive.stories.tsx | 60 ++--- .../stories/Tag/TagMedia.stories.tsx | 10 +- .../stories/Tag/TagSecondaryText.stories.tsx | 10 +- .../stories/Tag/TagShape.stories.tsx | 22 +- 24 files changed, 400 insertions(+), 634 deletions(-) delete mode 100644 packages/react-components/react-tags/src/TagContent.ts delete mode 100644 packages/react-components/react-tags/src/components/Tag/TagContext.ts delete mode 100644 packages/react-components/react-tags/src/components/TagContent/TagContent.test.tsx delete mode 100644 packages/react-components/react-tags/src/components/TagContent/TagContent.tsx delete mode 100644 packages/react-components/react-tags/src/components/TagContent/TagContent.types.ts delete mode 100644 packages/react-components/react-tags/src/components/TagContent/index.ts delete mode 100644 packages/react-components/react-tags/src/components/TagContent/renderTagContent.tsx delete mode 100644 packages/react-components/react-tags/src/components/TagContent/useTagContent.ts delete mode 100644 packages/react-components/react-tags/src/components/TagContent/useTagContentContextValues.ts delete mode 100644 packages/react-components/react-tags/src/components/TagContent/useTagContentStyles.styles.ts diff --git a/packages/react-components/react-tags/src/TagContent.ts b/packages/react-components/react-tags/src/TagContent.ts deleted file mode 100644 index c0e6b46040ab8e..00000000000000 --- a/packages/react-components/react-tags/src/TagContent.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './components/TagContent/index'; diff --git a/packages/react-components/react-tags/src/components/Tag/Tag.types.ts b/packages/react-components/react-tags/src/components/Tag/Tag.types.ts index 25d2ffd27d1174..11551294149786 100644 --- a/packages/react-components/react-tags/src/components/Tag/Tag.types.ts +++ b/packages/react-components/react-tags/src/components/Tag/Tag.types.ts @@ -1,14 +1,38 @@ +import { AvatarSize, AvatarShape } from '@fluentui/react-avatar'; import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; -export type TagContextValue = Required>; - export type TagContextValues = { - tag: TagContextValue; + avatar: { + size?: AvatarSize; + shape?: AvatarShape; + }; }; export type TagSlots = { root: NonNullable>; + /** + * Slot for an icon or other visual element + */ + media: Slot<'span'>; + + /** + * A layout wrapper for the icon slot, the primaryText and secondaryText slots + */ + content: Slot<'div', 'button'>; + + icon: Slot<'span'>; + + /** + * Main text for the Tag. Children of the root slot are automatically rendered here + */ + primaryText: Slot<'span'>; + + /** + * Secondary text that describes or complements the main text + */ + secondaryText: Slot<'span'>; + dismissButton: Slot<'button'>; }; @@ -16,17 +40,23 @@ export type TagSlots = { * Tag Props */ export type TagProps = ComponentProps> & { - size?: 'extra-small' | 'small' | 'medium'; - shape?: 'rounded' | 'circular'; appearance?: 'filled-darker' | 'filled-lighter' | 'tint' | 'outline'; + // TODO tag checked state + // checked?: boolean; disabled?: boolean; - checked?: boolean; dismissible?: boolean; interactive?: boolean; + shape?: 'rounded' | 'circular'; + size?: 'extra-small' | 'small' | 'medium'; }; /** * State used in rendering Tag */ export type TagState = ComponentState & - Required>; + Required< + Pick & { + avatarSize: AvatarSize | undefined; + avatarShape: AvatarShape | undefined; + } + >; diff --git a/packages/react-components/react-tags/src/components/Tag/TagContext.ts b/packages/react-components/react-tags/src/components/Tag/TagContext.ts deleted file mode 100644 index bc89afe0e80a35..00000000000000 --- a/packages/react-components/react-tags/src/components/Tag/TagContext.ts +++ /dev/null @@ -1,14 +0,0 @@ -import * as React from 'react'; -import type { TagContextValue } from './Tag.types'; - -export const TagContext = React.createContext(undefined); - -const tagContextDefaultValue: TagContextValue = { - dismissible: false, - shape: 'rounded', - size: 'medium', - interactive: false, -}; - -export const TagProvider = TagContext.Provider; -export const useTagContext_unstable = () => React.useContext(TagContext) ?? tagContextDefaultValue; diff --git a/packages/react-components/react-tags/src/components/Tag/index.ts b/packages/react-components/react-tags/src/components/Tag/index.ts index 6063344cb48fa8..a44c9ce425d76c 100644 --- a/packages/react-components/react-tags/src/components/Tag/index.ts +++ b/packages/react-components/react-tags/src/components/Tag/index.ts @@ -3,4 +3,3 @@ export * from './Tag.types'; export * from './renderTag'; export * from './useTag'; export * from './useTagStyles.styles'; -export * from './TagContext'; diff --git a/packages/react-components/react-tags/src/components/Tag/renderTag.tsx b/packages/react-components/react-tags/src/components/Tag/renderTag.tsx index 17e18197db8875..5bb795c5e4925d 100644 --- a/packages/react-components/react-tags/src/components/Tag/renderTag.tsx +++ b/packages/react-components/react-tags/src/components/Tag/renderTag.tsx @@ -5,7 +5,7 @@ import { createElement } from '@fluentui/react-jsx-runtime'; import { getSlotsNext } from '@fluentui/react-utilities'; import type { TagState, TagSlots, TagContextValues } from './Tag.types'; -import { TagContext } from './TagContext'; +import { AvatarContextProvider } from '@fluentui/react-avatar'; /** * Render the final JSX of Tag @@ -15,10 +15,19 @@ export const renderTag_unstable = (state: TagState, contextValues: TagContextVal return ( - - {slotProps.root.children} - {slots.dismissButton && } - + {slots.content && ( + + {slots.media && ( + + + + )} + {slots.icon && } + {slots.primaryText && } + {slots.secondaryText && } + + )} + {slots.dismissButton && state.dismissible && } ); }; diff --git a/packages/react-components/react-tags/src/components/Tag/useTag.tsx b/packages/react-components/react-tags/src/components/Tag/useTag.tsx index 54281d55dbc70b..dfd34e18a19b4b 100644 --- a/packages/react-components/react-tags/src/components/Tag/useTag.tsx +++ b/packages/react-components/react-tags/src/components/Tag/useTag.tsx @@ -1,8 +1,19 @@ import * as React from 'react'; -import { getNativeElementProps } from '@fluentui/react-utilities'; +import { getNativeElementProps, resolveShorthand } from '@fluentui/react-utilities'; import { DismissRegular, bundleIcon, DismissFilled } from '@fluentui/react-icons'; import type { TagProps, TagState } from './Tag.types'; -import { useARIAButtonShorthand } from '@fluentui/react-aria'; +import { ARIAButtonSlotProps, useARIAButtonShorthand } from '@fluentui/react-aria'; + +const tagAvatarSizeMap = { + medium: 28, + small: 24, + 'extra-small': 20, +} as const; + +const tagAvatarShapeMap = { + rounded: 'square', + circular: 'circular', +} as const; const DismissIcon = bundleIcon(DismissFilled, DismissRegular); @@ -17,33 +28,50 @@ const DismissIcon = bundleIcon(DismissFilled, DismissRegular); */ export const useTag_unstable = (props: TagProps, ref: React.Ref): TagState => { const { - checked = false, + appearance = 'filled-lighter', disabled = false, dismissible = false, + interactive = false, shape = 'rounded', size = 'medium', - appearance = 'filled-lighter', - interactive = false, } = props; + const contentButton = useARIAButtonShorthand(props.content as ARIAButtonSlotProps, { + required: true, + defaultProps: { + disabled, + type: 'button', + }, + }); + return { components: { root: 'div', + content: interactive ? 'button' : 'div', + media: 'span', + icon: 'span', + primaryText: 'span', + secondaryText: 'span', dismissButton: 'button', }, - - checked, disabled, dismissible, + interactive, shape, size, appearance, - interactive, - root: getNativeElementProps('div', { ref, ...props, }), + avatarSize: tagAvatarSizeMap[size], + avatarShape: tagAvatarShapeMap[shape], + media: resolveShorthand(props.media), + + content: interactive ? (contentButton as TagState['content']) : resolveShorthand(props.content, { required: true }), + icon: resolveShorthand(props.icon), + primaryText: resolveShorthand(props.primaryText, { required: true, defaultProps: { children: props.children } }), + secondaryText: resolveShorthand(props.secondaryText), dismissButton: useARIAButtonShorthand(props.dismissButton, { required: props.dismissible, diff --git a/packages/react-components/react-tags/src/components/Tag/useTagContextValues.ts b/packages/react-components/react-tags/src/components/Tag/useTagContextValues.ts index 72fc259b70244f..cc2150595acd55 100644 --- a/packages/react-components/react-tags/src/components/Tag/useTagContextValues.ts +++ b/packages/react-components/react-tags/src/components/Tag/useTagContextValues.ts @@ -1,18 +1,18 @@ import * as React from 'react'; -import type { TagContextValue, TagContextValues, TagState } from './Tag.types'; +import { TagState, TagContextValues } from './Tag.types'; export function useTagContextValues_unstable(state: TagState): TagContextValues { - const { dismissible, shape, size, interactive } = state; + const { avatarSize, avatarShape } = state; - const tag = React.useMemo( + const avatar = React.useMemo( () => ({ - dismissible, - shape, - size, - interactive, + size: avatarSize, + shape: avatarShape, }), - [dismissible, interactive, shape, size], + [avatarShape, avatarSize], ); - return { tag }; + return { + avatar, + }; } diff --git a/packages/react-components/react-tags/src/components/Tag/useTagStyles.styles.ts b/packages/react-components/react-tags/src/components/Tag/useTagStyles.styles.ts index b82fdece5f21c9..19f34385a73495 100644 --- a/packages/react-components/react-tags/src/components/Tag/useTagStyles.styles.ts +++ b/packages/react-components/react-tags/src/components/Tag/useTagStyles.styles.ts @@ -1,18 +1,33 @@ import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; import type { TagSlots, TagState } from './Tag.types'; import type { SlotClassNames } from '@fluentui/react-utilities'; -import { tokens } from '@fluentui/react-theme'; +import { tokens, typographyStyles } from '@fluentui/react-theme'; import { createCustomFocusIndicatorStyle } from '@fluentui/react-tabster'; export const tagClassNames: SlotClassNames = { root: 'fui-Tag', + content: 'fui-Tag__content', dismissButton: 'fui-Tag__dismissButton', + icon: 'fui-Tag__icon', + media: 'fui-Tag__media', + primaryText: 'fui-Tag__primaryText', + secondaryText: 'fui-Tag__secondaryText', }; -/** - * Styles for the root slot - */ -export const useTagBaseStyles = makeStyles({ +export const useTagStyles = makeStyles({ + resetButton: { + boxSizing: 'content-box', + backgroundColor: 'transparent', + color: 'inherit', + fontFamily: 'inherit', + lineHeight: 'normal', + ...shorthands.overflow('visible'), + ...shorthands.padding(0), + ...shorthands.borderStyle('none'), + appearance: 'button', + textAlign: 'unset', + }, + root: { // TODO use makeResetStyle when styles are settled display: 'inline-flex', @@ -28,17 +43,56 @@ export const useTagBaseStyles = makeStyles({ ...shorthands.borderRadius(tokens.borderRadiusCircular), }, - resetButton: { - boxSizing: 'content-box', - color: 'inherit', - fontFamily: 'inherit', - lineHeight: 'normal', - ...shorthands.overflow('visible'), - ...shorthands.padding(0), - ...shorthands.borderStyle('none'), - appearance: 'button', - textAlign: 'unset', + content: { + display: 'inline-grid', + gridTemplateRows: '1fr auto auto 1fr', + gridTemplateAreas: ` + "media . " + "media primary " + "media secondary" + "media . " + `, + paddingRight: tokens.spacingHorizontalS, + }, + textOnlyContent: { + paddingLeft: tokens.spacingHorizontalS, + }, + nonInteractiveDismissibleContent: { + paddingRight: '2px', + }, + + media: { + ...shorthands.gridArea('media'), + alignSelf: 'center', + paddingLeft: tokens.spacingHorizontalXXS, + paddingRight: tokens.spacingHorizontalS, + }, + icon: { + display: 'flex', + alignSelf: 'center', + ...shorthands.gridArea('media'), + paddingLeft: '6px', + paddingRight: '2px', + }, + primaryText: { + gridColumnStart: 'primary', + gridRowStart: 'primary', + gridRowEnd: 'secondary', + ...typographyStyles.body1, + paddingLeft: tokens.spacingHorizontalXXS, + paddingRight: tokens.spacingHorizontalXXS, + }, + primaryTextWithSecondaryText: { + ...shorthands.gridArea('primary'), + ...typographyStyles.caption1, }, + secondaryText: { + ...shorthands.gridArea('secondary'), + paddingLeft: tokens.spacingHorizontalXXS, + paddingRight: tokens.spacingHorizontalXXS, + ...typographyStyles.caption2, + }, + dismissButton: { ...shorthands.padding('0px'), backgroundColor: 'transparent', @@ -55,17 +109,31 @@ export const useTagBaseStyles = makeStyles({ { enableOutline: true }, ), }, + nonInteractiveDismissButton: { + marginRight: '6px', + }, // TODO add additional classes for fill/outline appearance, different sizes, and state }); -const useTagStyles = makeStyles({ - dismissButton: { - marginRight: '6px', +const useInteractiveTagStyles = makeStyles({ + content: { + position: 'relative', + ...createCustomFocusIndicatorStyle({ + ...shorthands.borderRadius(tokens.borderRadiusMedium), + ...shorthands.outline(tokens.strokeWidthThick, 'solid', tokens.colorStrokeFocus2), + }), + }, + circularContent: createCustomFocusIndicatorStyle(shorthands.borderRadius(tokens.borderRadiusCircular), { + enableOutline: true, + }), + dismissibleContent: { + ...createCustomFocusIndicatorStyle({ + borderTopRightRadius: tokens.borderRadiusNone, + borderBottomRightRadius: tokens.borderRadiusNone, + }), }, -}); -const useInteractiveTagStyles = makeStyles({ dismissButton: { ...shorthands.padding('0px', '6px'), ...shorthands.borderLeft(tokens.strokeWidthThin, 'solid', tokens.colorNeutralStroke1), @@ -86,25 +154,66 @@ const useInteractiveTagStyles = makeStyles({ * Apply styling to the Tag slots based on the state */ export const useTagStyles_unstable = (state: TagState): TagState => { - const baseStyles = useTagBaseStyles(); - - const tagStyles = useTagStyles(); + const styles = useTagStyles(); const interactiveTagStyles = useInteractiveTagStyles(); state.root.className = mergeClasses( tagClassNames.root, - baseStyles.root, - state.shape === 'circular' && baseStyles.rootCircular, + styles.root, + state.shape === 'circular' && styles.rootCircular, state.root.className, ); + if (state.content) { + state.content.className = mergeClasses( + tagClassNames.content, + state.interactive && styles.resetButton, + + styles.content, + !state.media && !state.icon && styles.textOnlyContent, + + !state.interactive && state.dismissible && styles.nonInteractiveDismissibleContent, + + state.interactive && interactiveTagStyles.content, + state.interactive && state.shape === 'circular' && interactiveTagStyles.circularContent, + state.interactive && state.dismissible && interactiveTagStyles.dismissibleContent, + + state.content.className, + ); + } + + if (state.media) { + state.media.className = mergeClasses(tagClassNames.media, styles.media, state.media.className); + } + + if (state.icon) { + state.icon.className = mergeClasses(tagClassNames.icon, styles.icon, state.icon.className); + } + + if (state.primaryText) { + state.primaryText.className = mergeClasses( + tagClassNames.primaryText, + styles.primaryText, + state.secondaryText && styles.primaryTextWithSecondaryText, + state.primaryText.className, + ); + } + + if (state.secondaryText) { + state.secondaryText.className = mergeClasses( + tagClassNames.secondaryText, + styles.secondaryText, + state.secondaryText.className, + ); + } + if (state.dismissButton) { state.dismissButton.className = mergeClasses( tagClassNames.dismissButton, - baseStyles.resetButton, - baseStyles.dismissButton, + styles.resetButton, + styles.dismissButton, - !state.interactive && tagStyles.dismissButton, + !state.interactive && styles.nonInteractiveDismissButton, state.interactive && interactiveTagStyles.dismissButton, state.interactive && state.shape === 'circular' && interactiveTagStyles.dismissButtonCircular, diff --git a/packages/react-components/react-tags/src/components/TagContent/TagContent.test.tsx b/packages/react-components/react-tags/src/components/TagContent/TagContent.test.tsx deleted file mode 100644 index 078459c6fbf4ea..00000000000000 --- a/packages/react-components/react-tags/src/components/TagContent/TagContent.test.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { TagContent } from './TagContent'; -import { isConformant } from '../../testing/isConformant'; -import { TagContentProps } from './TagContent.types'; - -const requiredProps: TagContentProps = { - media: 'media', - icon: 'i', - primaryText: 'Primary text', - secondaryText: 'Secondary text', -}; - -describe('TagContent', () => { - isConformant({ - Component: TagContent, - displayName: 'TagContent', - requiredProps, - }); - - // TODO add more tests here, and create visual regression tests in /apps/vr-tests -}); diff --git a/packages/react-components/react-tags/src/components/TagContent/TagContent.tsx b/packages/react-components/react-tags/src/components/TagContent/TagContent.tsx deleted file mode 100644 index 513b28f749c812..00000000000000 --- a/packages/react-components/react-tags/src/components/TagContent/TagContent.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import * as React from 'react'; -import { useTagContent_unstable } from './useTagContent'; -import { renderTagContent_unstable } from './renderTagContent'; -import { useTagContentStyles_unstable } from './useTagContentStyles.styles'; -import type { TagContentProps } from './TagContent.types'; -import type { ForwardRefComponent } from '@fluentui/react-utilities'; -import { useTagContentContextValues_unstable } from './useTagContentContextValues'; - -/** - * TagContent component - TODO: add more docs - */ -export const TagContent: ForwardRefComponent = React.forwardRef((props, ref) => { - const state = useTagContent_unstable(props, ref); - const contextValues = useTagContentContextValues_unstable(state); - useTagContentStyles_unstable(state); - return renderTagContent_unstable(state, contextValues); -}); - -TagContent.displayName = 'TagContent'; diff --git a/packages/react-components/react-tags/src/components/TagContent/TagContent.types.ts b/packages/react-components/react-tags/src/components/TagContent/TagContent.types.ts deleted file mode 100644 index c392a46c804acd..00000000000000 --- a/packages/react-components/react-tags/src/components/TagContent/TagContent.types.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { AvatarShape, AvatarSize } from '@fluentui/react-avatar'; -import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; -import { TagContextValue } from '../Tag/index'; - -export type TagContentContextValues = { - avatar: { - shape: AvatarShape; - size: AvatarSize; - }; -}; - -export type TagContentSlots = { - root: Slot<'div', 'button'>; - - /** - * Slot for an visual element, usually an Avatar - */ - media: Slot<'span'>; - - /** - * Slot for an icon - */ - icon: Slot<'span'>; - - /** - * Main text for the Tag. Children of the root slot are automatically rendered here - */ - primaryText: Slot<'span'>; - - /** - * Secondary text that describes or complements the main text - */ - secondaryText: Slot<'span'>; -}; - -/** - * TagContent Props - */ -export type TagContentProps = ComponentProps>; - -/** - * State used in rendering TagContent - */ -export type TagContentState = ComponentState & - Required< - Pick & { - avatarSize: AvatarSize; - avatarShape: AvatarShape; - } - >; diff --git a/packages/react-components/react-tags/src/components/TagContent/index.ts b/packages/react-components/react-tags/src/components/TagContent/index.ts deleted file mode 100644 index 08e461969e0f38..00000000000000 --- a/packages/react-components/react-tags/src/components/TagContent/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './TagContent'; -export * from './TagContent.types'; -export * from './renderTagContent'; -export * from './useTagContent'; -export * from './useTagContentStyles.styles'; diff --git a/packages/react-components/react-tags/src/components/TagContent/renderTagContent.tsx b/packages/react-components/react-tags/src/components/TagContent/renderTagContent.tsx deleted file mode 100644 index 5129c544a6ff34..00000000000000 --- a/packages/react-components/react-tags/src/components/TagContent/renderTagContent.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import * as React from 'react'; -import { getSlots } from '@fluentui/react-utilities'; -import type { TagContentState, TagContentSlots, TagContentContextValues } from './TagContent.types'; -import { AvatarContextProvider } from '@fluentui/react-avatar'; - -/** - * Render the final JSX of TagContent - */ -export const renderTagContent_unstable = (state: TagContentState, contextValues: TagContentContextValues) => { - const { slots, slotProps } = getSlots(state); - - return ( - - {slots.media && ( - - - - )} - {slots.icon && } - {slots.primaryText && } - {slots.secondaryText && } - - ); -}; diff --git a/packages/react-components/react-tags/src/components/TagContent/useTagContent.ts b/packages/react-components/react-tags/src/components/TagContent/useTagContent.ts deleted file mode 100644 index dd624c31558ed1..00000000000000 --- a/packages/react-components/react-tags/src/components/TagContent/useTagContent.ts +++ /dev/null @@ -1,54 +0,0 @@ -import * as React from 'react'; -import { getNativeElementProps, resolveShorthand } from '@fluentui/react-utilities'; -import type { TagContentProps, TagContentState } from './TagContent.types'; -import { useTagContext_unstable } from '../Tag/index'; - -const tagContentAvatarSizeMap = { - medium: 28, - small: 24, - 'extra-small': 20, -} as const; - -const tagContentAvatarShapeMap = { - rounded: 'square', - circular: 'circular', -} as const; - -/** - * Create the state required to render TagContent. - * - * The returned state can be modified with hooks such as useTagContentStyles_unstable, - * before being passed to renderTagContent_unstable. - * - * @param props - props from this instance of TagContent - * @param ref - reference to root HTMLElement of TagContent - */ -export const useTagContent_unstable = (props: TagContentProps, ref: React.Ref): TagContentState => { - const { dismissible, shape, size, interactive } = useTagContext_unstable(); - - return { - components: { - root: interactive ? 'button' : 'div', - media: 'span', - icon: 'span', - primaryText: 'span', - secondaryText: 'span', - }, - - avatarShape: tagContentAvatarShapeMap[shape], - avatarSize: tagContentAvatarSizeMap[size], - dismissible, - interactive, - shape, - - media: resolveShorthand(props.media), - icon: resolveShorthand(props.icon), - primaryText: resolveShorthand(props.primaryText, { required: true, defaultProps: { children: props.children } }), - secondaryText: resolveShorthand(props.secondaryText), - root: getNativeElementProps(interactive ? 'button' : 'div', { - ref, - ...(interactive && { type: 'button' }), - ...props, - }), - }; -}; diff --git a/packages/react-components/react-tags/src/components/TagContent/useTagContentContextValues.ts b/packages/react-components/react-tags/src/components/TagContent/useTagContentContextValues.ts deleted file mode 100644 index 14bc83936f6f39..00000000000000 --- a/packages/react-components/react-tags/src/components/TagContent/useTagContentContextValues.ts +++ /dev/null @@ -1,18 +0,0 @@ -import * as React from 'react'; -import type { TagContentContextValues, TagContentState } from './TagContent.types'; - -export function useTagContentContextValues_unstable(state: TagContentState): TagContentContextValues { - const { avatarSize, avatarShape } = state; - - const avatar = React.useMemo( - () => ({ - size: avatarSize, - shape: avatarShape, - }), - [avatarShape, avatarSize], - ); - - return { - avatar, - }; -} diff --git a/packages/react-components/react-tags/src/components/TagContent/useTagContentStyles.styles.ts b/packages/react-components/react-tags/src/components/TagContent/useTagContentStyles.styles.ts deleted file mode 100644 index a7b85d170c04bd..00000000000000 --- a/packages/react-components/react-tags/src/components/TagContent/useTagContentStyles.styles.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; -import type { TagContentSlots, TagContentState } from './TagContent.types'; -import type { SlotClassNames } from '@fluentui/react-utilities'; -import { tokens, typographyStyles } from '@fluentui/react-theme'; -import { createCustomFocusIndicatorStyle } from '@fluentui/react-tabster'; - -export const tagContentClassNames: SlotClassNames = { - root: 'fui-TagContent', - media: 'fui-TagContent__media', - icon: 'fui-TagContent__icon', - primaryText: 'fui-TagContent__primaryText', - secondaryText: 'fui-TagContent__secondaryText', -}; - -/** - * Styles for the root slot - */ -const useStyles = makeStyles({ - root: { - display: 'inline-grid', - gridTemplateRows: '1fr auto auto 1fr', - gridTemplateAreas: ` - "media . " - "media primary " - "media secondary" - "media . " - `, - paddingRight: tokens.spacingHorizontalS, - }, - textOnlyRoot: { - paddingLeft: tokens.spacingHorizontalS, - }, - nonInteractiveDismissableRoot: { - paddingRight: '2px', - }, - - media: { - ...shorthands.gridArea('media'), - alignSelf: 'center', - paddingLeft: tokens.spacingHorizontalXXS, - paddingRight: tokens.spacingHorizontalS, - }, - - icon: { - display: 'flex', - alignSelf: 'center', - ...shorthands.gridArea('media'), - paddingLeft: '6px', - paddingRight: '2px', - }, - primaryText: { - gridColumnStart: 'primary', - gridRowStart: 'primary', - gridRowEnd: 'secondary', - ...typographyStyles.body1, - paddingLeft: tokens.spacingHorizontalXXS, - paddingRight: tokens.spacingHorizontalXXS, - }, - primaryTextWithSecondaryText: { - ...shorthands.gridArea('primary'), - ...typographyStyles.caption1, - }, - secondaryText: { - ...shorthands.gridArea('secondary'), - paddingLeft: tokens.spacingHorizontalXXS, - paddingRight: tokens.spacingHorizontalXXS, - ...typographyStyles.caption2, - }, -}); - -/** - * Styles for the root slot - */ -const useInteractiveTagContentStyles = makeStyles({ - resetButton: { - boxSizing: 'content-box', - backgroundColor: 'transparent', - color: 'inherit', - fontFamily: 'inherit', - lineHeight: 'normal', - ...shorthands.overflow('visible'), - ...shorthands.padding(0), - ...shorthands.borderStyle('none'), - appearance: 'button', - textAlign: 'unset', - }, - root: { - position: 'relative', - ...createCustomFocusIndicatorStyle({ - ...shorthands.borderRadius(tokens.borderRadiusMedium), - ...shorthands.outline(tokens.strokeWidthThick, 'solid', tokens.colorStrokeFocus2), - }), - }, - circularRoot: createCustomFocusIndicatorStyle(shorthands.borderRadius(tokens.borderRadiusCircular), { - enableOutline: true, - }), - dismissableRoot: { - ...createCustomFocusIndicatorStyle({ - borderTopRightRadius: tokens.borderRadiusNone, - borderBottomRightRadius: tokens.borderRadiusNone, - }), - }, -}); - -/** - * Apply styling to the TagContent slots based on the state - */ -export const useTagContentStyles_unstable = (state: TagContentState): TagContentState => { - const styles = useStyles(); - const interactiveTagContentStyles = useInteractiveTagContentStyles(); - - state.root.className = mergeClasses( - tagContentClassNames.root, - state.interactive && interactiveTagContentStyles.resetButton, - - styles.root, - !state.media && !state.icon && styles.textOnlyRoot, - - !state.interactive && state.dismissible && styles.nonInteractiveDismissableRoot, - - state.interactive && interactiveTagContentStyles.root, - state.interactive && state.shape === 'circular' && interactiveTagContentStyles.circularRoot, - state.interactive && state.dismissible && interactiveTagContentStyles.dismissableRoot, - - state.root.className, - ); - - if (state.media) { - state.media.className = mergeClasses(tagContentClassNames.media, styles.media, state.media.className); - } - - if (state.icon) { - state.icon.className = mergeClasses(tagContentClassNames.icon, styles.icon, state.icon.className); - } - - if (state.primaryText) { - state.primaryText.className = mergeClasses( - tagContentClassNames.primaryText, - styles.primaryText, - state.secondaryText && styles.primaryTextWithSecondaryText, - state.primaryText.className, - ); - } - - if (state.secondaryText) { - state.secondaryText.className = mergeClasses( - tagContentClassNames.secondaryText, - styles.secondaryText, - state.secondaryText.className, - ); - } - return state; -}; diff --git a/packages/react-components/react-tags/src/index.ts b/packages/react-components/react-tags/src/index.ts index 7a26dabaf87916..46755dc9444566 100644 --- a/packages/react-components/react-tags/src/index.ts +++ b/packages/react-components/react-tags/src/index.ts @@ -1,10 +1,2 @@ export { Tag, renderTag_unstable, tagClassNames, useTagStyles_unstable, useTag_unstable } from './Tag'; export type { TagProps, TagSlots, TagState } from './Tag'; -export { - TagContent, - renderTagContent_unstable, - tagContentClassNames, - useTagContentStyles_unstable, - useTagContent_unstable, -} from './TagContent'; -export type { TagContentProps, TagContentSlots, TagContentState } from './TagContent'; diff --git a/packages/react-components/react-tags/stories/Tag/TagDefault.stories.tsx b/packages/react-components/react-tags/stories/Tag/TagDefault.stories.tsx index d085ec5cbb9434..468142ba43bcbc 100644 --- a/packages/react-components/react-tags/stories/Tag/TagDefault.stories.tsx +++ b/packages/react-components/react-tags/stories/Tag/TagDefault.stories.tsx @@ -1,158 +1,131 @@ import * as React from 'react'; -import { Tag, TagContent, TagProps } from '@fluentui/react-tags'; +import { Tag } from '@fluentui/react-tags'; import { Calendar3Day20Regular } from '@fluentui/react-icons'; import { Avatar } from '@fluentui/react-components'; // TODO I added many examples here for easier implementation. This story will be simplified to keep only the default example -export const Default = (props: Partial) => ( +export const Default = () => (
- - - } - secondaryText="Secondary text" - > - Primary text - + + } + secondaryText="Secondary text" + dismissible + > + Primary text - - - - } - > - Primary text - - - - - - } - > - Primary text - - - - - } secondaryText="Secondary text"> - Primary text - + + } + dismissible + > + Primary text - - - }>Primary text + + } + > + Primary text - - - }>Primary text + } secondaryText="Secondary text" dismissible> + Primary text - - - Primary text + } dismissible> + Primary text + }>Primary text + Primary text
- - - } - secondaryText="Secondary text" - > - Primary text - + + } + secondaryText="Secondary text" + dismissible + > + Primary text - - - - } - > - Primary text - + + } + dismissible + > + Primary text - - - - } - > - Primary text - + + } + > + Primary text - - - } secondaryText="Secondary text"> - Primary text - + } secondaryText="Secondary text" dismissible> + Primary text - - - }>Primary text + } dismissible> + Primary text - - - }>Primary text - - - - Primary text + }> + Primary text + Primary text
); diff --git a/packages/react-components/react-tags/stories/Tag/TagDismiss.stories.tsx b/packages/react-components/react-tags/stories/Tag/TagDismiss.stories.tsx index 688ded56632095..52cddfec88a2db 100644 --- a/packages/react-components/react-tags/stories/Tag/TagDismiss.stories.tsx +++ b/packages/react-components/react-tags/stories/Tag/TagDismiss.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Avatar, makeStyles } from '@fluentui/react-components'; import { Calendar3Day20Regular } from '@fluentui/react-icons'; -import { Tag, TagContent } from '@fluentui/react-tags'; +import { Tag } from '@fluentui/react-tags'; const useContainerStyles = makeStyles({ root: { @@ -16,16 +16,16 @@ export const Dismiss = () => { const containerStyles = useContainerStyles(); return (
- - Primary text + Primary text + }> + Primary text - - }>Primary text{' '} - - - } secondaryText="Secondary text"> - Primary text - + } + secondaryText="Secondary text" + > + Primary text
); diff --git a/packages/react-components/react-tags/stories/Tag/TagIcon.stories.tsx b/packages/react-components/react-tags/stories/Tag/TagIcon.stories.tsx index 854fa1ec9ce724..28e79a1dc3e3f4 100644 --- a/packages/react-components/react-tags/stories/Tag/TagIcon.stories.tsx +++ b/packages/react-components/react-tags/stories/Tag/TagIcon.stories.tsx @@ -1,18 +1,14 @@ import * as React from 'react'; import { Calendar3Day20Regular } from '@fluentui/react-icons'; -import { Tag, TagContent } from '@fluentui/react-tags'; +import { Tag } from '@fluentui/react-tags'; -export const Icon = () => ( - - }>Primary text - -); +export const Icon = () => }>Primary text; Icon.storyName = 'Icon'; Icon.parameters = { docs: { description: { - story: 'A TagContent can render a custom icon if provided.', + story: 'A Tag can render a custom icon if provided.', }, }, }; diff --git a/packages/react-components/react-tags/stories/Tag/TagInteractive.stories.tsx b/packages/react-components/react-tags/stories/Tag/TagInteractive.stories.tsx index 5da6be892642d2..01936fe78d9464 100644 --- a/packages/react-components/react-tags/stories/Tag/TagInteractive.stories.tsx +++ b/packages/react-components/react-tags/stories/Tag/TagInteractive.stories.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Tag, TagContent } from '@fluentui/react-tags'; +import { Tag } from '@fluentui/react-tags'; import { Calendar3Day20Regular } from '@fluentui/react-icons'; import { Avatar, makeStyles } from '@fluentui/react-components'; @@ -16,37 +16,39 @@ export const Interactive = () => { return (
- - { + { console.log('Tag with avatar clicked'); - }} - media={ - - } - secondaryText="Secondary text" - > - Primary text - + }, + }} + media={ + + } + secondaryText="Secondary text" + > + Primary text - - - { + { console.log('Tag with icon clicked'); - }} - icon={} - > - Primary text - + }, + }} + icon={} + > + Primary text
); diff --git a/packages/react-components/react-tags/stories/Tag/TagMedia.stories.tsx b/packages/react-components/react-tags/stories/Tag/TagMedia.stories.tsx index e5bf7e078353b2..ed780a9fd0ca67 100644 --- a/packages/react-components/react-tags/stories/Tag/TagMedia.stories.tsx +++ b/packages/react-components/react-tags/stories/Tag/TagMedia.stories.tsx @@ -1,19 +1,15 @@ import * as React from 'react'; import { Avatar } from '@fluentui/react-components'; -import { Tag, TagContent } from '@fluentui/react-tags'; +import { Tag } from '@fluentui/react-tags'; -export const Media = () => ( - - }>Primary text - -); +export const Media = () => }>Primary text; Media.storyName = 'Media'; Media.parameters = { docs: { description: { - story: 'A TagContent can render a media, for example an Avatar.', + story: 'A Tag can render a media, for example an Avatar.', }, }, }; diff --git a/packages/react-components/react-tags/stories/Tag/TagSecondaryText.stories.tsx b/packages/react-components/react-tags/stories/Tag/TagSecondaryText.stories.tsx index 2f903564c179a8..1f16e2b7b0815d 100644 --- a/packages/react-components/react-tags/stories/Tag/TagSecondaryText.stories.tsx +++ b/packages/react-components/react-tags/stories/Tag/TagSecondaryText.stories.tsx @@ -1,17 +1,13 @@ import * as React from 'react'; -import { Tag, TagContent } from '@fluentui/react-tags'; +import { Tag } from '@fluentui/react-tags'; -export const SecondaryText = () => ( - - Primary text - -); +export const SecondaryText = () => Primary text; SecondaryText.storyName = 'SecondaryText'; SecondaryText.parameters = { docs: { description: { - story: 'A TagContent can have a secondary text.', + story: 'A Tag can have a secondary text.', }, }, }; diff --git a/packages/react-components/react-tags/stories/Tag/TagShape.stories.tsx b/packages/react-components/react-tags/stories/Tag/TagShape.stories.tsx index 59e30bba9ab60e..af3fd6c08a7ccd 100644 --- a/packages/react-components/react-tags/stories/Tag/TagShape.stories.tsx +++ b/packages/react-components/react-tags/stories/Tag/TagShape.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Avatar, makeStyles } from '@fluentui/react-components'; import { Calendar3Day20Regular } from '@fluentui/react-icons'; -import { Tag, TagContent } from '@fluentui/react-tags'; +import { Tag } from '@fluentui/react-tags'; const useContainerStyles = makeStyles({ root: { @@ -17,22 +17,16 @@ export const Shape = () => { const containerStyles = useContainerStyles(); return (
- - }>Rounded - - - }>Circular + }>Rounded + }> + Circular - - } secondaryText="Secondary text"> - Rounded - + } secondaryText="Secondary text"> + Rounded - - } secondaryText="Secondary text"> - Circular - + } secondaryText="Secondary text"> + Circular
); From 1bebaf7d842f48fa87b3a954a97d9752131dfb58 Mon Sep 17 00:00:00 2001 From: YuanboXue-Amber Date: Tue, 9 May 2023 17:22:44 +0800 Subject: [PATCH 08/18] regen api --- .../react-tags/etc/react-tags.api.md | 48 +++++-------------- 1 file changed, 11 insertions(+), 37 deletions(-) diff --git a/packages/react-components/react-tags/etc/react-tags.api.md b/packages/react-components/react-tags/etc/react-tags.api.md index 951e2b7dbfa9ab..13303420a63824 100644 --- a/packages/react-components/react-tags/etc/react-tags.api.md +++ b/packages/react-components/react-tags/etc/react-tags.api.md @@ -18,68 +18,42 @@ import type { SlotClassNames } from '@fluentui/react-utilities'; // @public export const renderTag_unstable: (state: TagState, contextValues: TagContextValues) => JSX.Element; -// @public -export const renderTagContent_unstable: (state: TagContentState, contextValues: TagContentContextValues) => JSX.Element; - // @public export const Tag: ForwardRefComponent; // @public (undocumented) export const tagClassNames: SlotClassNames; -// @public -export const TagContent: ForwardRefComponent; - -// @public (undocumented) -export const tagContentClassNames: SlotClassNames; - -// @public -export type TagContentProps = ComponentProps>; - -// @public (undocumented) -export type TagContentSlots = { - root: Slot<'div', 'button'>; - media: Slot<'span'>; - icon: Slot<'span'>; - primaryText: Slot<'span'>; - secondaryText: Slot<'span'>; -}; - -// @public -export type TagContentState = ComponentState & Required & { - avatarSize: AvatarSize; - avatarShape: AvatarShape; -}>; - // @public export type TagProps = ComponentProps> & { - size?: 'extra-small' | 'small' | 'medium'; - shape?: 'rounded' | 'circular'; appearance?: 'filled-darker' | 'filled-lighter' | 'tint' | 'outline'; disabled?: boolean; - checked?: boolean; dismissible?: boolean; interactive?: boolean; + shape?: 'rounded' | 'circular'; + size?: 'extra-small' | 'small' | 'medium'; }; // @public (undocumented) export type TagSlots = { root: NonNullable>; + media: Slot<'span'>; + content: Slot<'div', 'button'>; + icon: Slot<'span'>; + primaryText: Slot<'span'>; + secondaryText: Slot<'span'>; dismissButton: Slot<'button'>; }; // @public -export type TagState = ComponentState & Required>; +export type TagState = ComponentState & Required & { + avatarSize: AvatarSize | undefined; + avatarShape: AvatarShape | undefined; +}>; // @public export const useTag_unstable: (props: TagProps, ref: React_2.Ref) => TagState; -// @public -export const useTagContent_unstable: (props: TagContentProps, ref: React_2.Ref) => TagContentState; - -// @public -export const useTagContentStyles_unstable: (state: TagContentState) => TagContentState; - // @public export const useTagStyles_unstable: (state: TagState) => TagState; From c2d57523059971c98642bec0cd288b9538f0877d Mon Sep 17 00:00:00 2001 From: YuanboXue-Amber Date: Tue, 9 May 2023 17:24:22 +0800 Subject: [PATCH 09/18] test --- .../react-tags/src/components/Tag/Tag.test.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/react-components/react-tags/src/components/Tag/Tag.test.tsx b/packages/react-components/react-tags/src/components/Tag/Tag.test.tsx index a56f2a235dce60..efbad544fff883 100644 --- a/packages/react-components/react-tags/src/components/Tag/Tag.test.tsx +++ b/packages/react-components/react-tags/src/components/Tag/Tag.test.tsx @@ -4,6 +4,10 @@ import { TagProps } from './Tag.types'; const requiredProps: TagProps = { dismissible: true, + icon: 'i', + media: 'media', + primaryText: 'Primary text', + secondaryText: 'Secondary text', }; describe('Tag', () => { From cc8e75c20c3efe6d1bdf15699170a1fd201a7011 Mon Sep 17 00:00:00 2001 From: YuanboXue-Amber Date: Tue, 9 May 2023 17:28:55 +0800 Subject: [PATCH 10/18] rename --- .../react-tags/etc/react-tags.api.md | 4 +-- .../src/components/Tag/Tag.test.tsx | 2 +- .../src/components/Tag/Tag.types.ts | 11 ++++---- .../src/components/Tag/renderTag.tsx | 2 +- .../react-tags/src/components/Tag/useTag.tsx | 25 +++++++++---------- .../src/components/Tag/useTagStyles.styles.ts | 4 +-- .../components/TagButton/TagButton.test.tsx | 2 +- .../components/TagButton/renderTagButton.tsx | 2 +- .../TagButton/useTagButtonStyles.styles.ts | 4 +-- .../stories/Tag/TagDefault.stories.tsx | 16 ++++++------ .../stories/Tag/TagDismiss.stories.tsx | 6 ++--- .../stories/Tag/TagShape.stories.tsx | 4 +-- .../TagButton/TagButtonDefault.stories.tsx | 16 ++++++------ .../TagButton/TagButtonDismiss.stories.tsx | 6 ++--- .../TagButton/TagButtonShape.stories.tsx | 4 +-- 15 files changed, 54 insertions(+), 54 deletions(-) diff --git a/packages/react-components/react-tags/etc/react-tags.api.md b/packages/react-components/react-tags/etc/react-tags.api.md index c8e561dc6958cd..5e2ea23ff9c983 100644 --- a/packages/react-components/react-tags/etc/react-tags.api.md +++ b/packages/react-components/react-tags/etc/react-tags.api.md @@ -49,7 +49,7 @@ export type TagProps = ComponentProps> & { appearance?: 'filled-darker' | 'filled-lighter' | 'tint' | 'outline'; disabled?: boolean; checked?: boolean; - dismissable?: boolean; + dismissible?: boolean; }; // @public (undocumented) @@ -64,7 +64,7 @@ export type TagSlots = { }; // @public -export type TagState = ComponentState & Required & { +export type TagState = ComponentState & Required & { avatarSize: AvatarSize | undefined; avatarShape: AvatarShape | undefined; }>; diff --git a/packages/react-components/react-tags/src/components/Tag/Tag.test.tsx b/packages/react-components/react-tags/src/components/Tag/Tag.test.tsx index 480342e78bece2..cc3354d9246375 100644 --- a/packages/react-components/react-tags/src/components/Tag/Tag.test.tsx +++ b/packages/react-components/react-tags/src/components/Tag/Tag.test.tsx @@ -6,7 +6,7 @@ const requiredProps = { icon: 'i', primaryText: 'Primary text', secondaryText: 'Secondary text', - dismissable: true, + dismissible: true, }; describe('Tag', () => { diff --git a/packages/react-components/react-tags/src/components/Tag/Tag.types.ts b/packages/react-components/react-tags/src/components/Tag/Tag.types.ts index 9d130328b633c0..4a63246869152f 100644 --- a/packages/react-components/react-tags/src/components/Tag/Tag.types.ts +++ b/packages/react-components/react-tags/src/components/Tag/Tag.types.ts @@ -40,12 +40,13 @@ export type TagSlots = { * Tag Props */ export type TagProps = ComponentProps> & { - size?: 'extra-small' | 'small' | 'medium'; - shape?: 'rounded' | 'circular'; appearance?: 'filled-darker' | 'filled-lighter' | 'tint' | 'outline'; + // TODO implement tag checked state + // checked?: boolean; disabled?: boolean; - checked?: boolean; - dismissable?: boolean; + dismissible?: boolean; + shape?: 'rounded' | 'circular'; + size?: 'extra-small' | 'small' | 'medium'; }; /** @@ -53,7 +54,7 @@ export type TagProps = ComponentProps> & { */ export type TagState = ComponentState & Required< - Pick & { + Pick & { avatarSize: AvatarSize | undefined; avatarShape: AvatarShape | undefined; } diff --git a/packages/react-components/react-tags/src/components/Tag/renderTag.tsx b/packages/react-components/react-tags/src/components/Tag/renderTag.tsx index 6e382ca446cb48..92e79fd3afdee1 100644 --- a/packages/react-components/react-tags/src/components/Tag/renderTag.tsx +++ b/packages/react-components/react-tags/src/components/Tag/renderTag.tsx @@ -29,7 +29,7 @@ export const renderTag_unstable = (state: TagState, contextValues: TagContextVal {slots.secondaryText && } )} - {slots.dismissButton && state.dismissable && } + {slots.dismissButton && state.dismissible && } ); }; diff --git a/packages/react-components/react-tags/src/components/Tag/useTag.tsx b/packages/react-components/react-tags/src/components/Tag/useTag.tsx index 35f804d5c4c2a5..048876b1931aef 100644 --- a/packages/react-components/react-tags/src/components/Tag/useTag.tsx +++ b/packages/react-components/react-tags/src/components/Tag/useTag.tsx @@ -28,15 +28,22 @@ const DismissIcon = bundleIcon(DismissFilled, DismissRegular); */ export const useTag_unstable = (props: TagProps, ref: React.Ref): TagState => { const { - checked = false, disabled = false, - dismissable = false, + dismissible = false, shape = 'rounded', size = 'medium', appearance = 'filled-lighter', } = props; return { + appearance, + avatarShape: tagAvatarShapeMap[shape], + avatarSize: tagAvatarSizeMap[size], + disabled, + dismissible, + shape, + size, + components: { root: 'div', content: 'div', @@ -46,27 +53,19 @@ export const useTag_unstable = (props: TagProps, ref: React.Ref): T secondaryText: 'span', dismissButton: 'button', }, - checked, - disabled, - dismissable, - shape, - size, - appearance, + root: getNativeElementProps('div', { ref, ...props, }), - avatarSize: tagAvatarSizeMap[size], - avatarShape: tagAvatarShapeMap[shape], - media: resolveShorthand(props.media), content: resolveShorthand(props.content, { required: true }), + media: resolveShorthand(props.media), icon: resolveShorthand(props.icon), primaryText: resolveShorthand(props.primaryText, { required: true }), secondaryText: resolveShorthand(props.secondaryText), - dismissButton: useARIAButtonShorthand(props.dismissButton, { - required: props.dismissable, + required: props.dismissible, defaultProps: { disabled, type: 'button', diff --git a/packages/react-components/react-tags/src/components/Tag/useTagStyles.styles.ts b/packages/react-components/react-tags/src/components/Tag/useTagStyles.styles.ts index 1edf60c2352c94..127333f259289e 100644 --- a/packages/react-components/react-tags/src/components/Tag/useTagStyles.styles.ts +++ b/packages/react-components/react-tags/src/components/Tag/useTagStyles.styles.ts @@ -113,7 +113,7 @@ export const useTagBaseStyles = makeStyles({ }); const useTagStyles = makeStyles({ - dismissableContent: { + dismissibleContent: { paddingRight: '2px', }, dismissButton: { @@ -140,7 +140,7 @@ export const useTagStyles_unstable = (state: TagState): TagState => { baseStyles.content, !state.media && !state.icon && baseStyles.textOnlyContent, - state.dismissButton && styles.dismissableContent, + state.dismissButton && styles.dismissibleContent, state.content.className, ); } diff --git a/packages/react-components/react-tags/src/components/TagButton/TagButton.test.tsx b/packages/react-components/react-tags/src/components/TagButton/TagButton.test.tsx index 74dd557f5f6efd..003e20c001ac91 100644 --- a/packages/react-components/react-tags/src/components/TagButton/TagButton.test.tsx +++ b/packages/react-components/react-tags/src/components/TagButton/TagButton.test.tsx @@ -6,7 +6,7 @@ const requiredProps = { icon: 'i', primaryText: 'Primary text', secondaryText: 'Secondary text', - dismissable: true, + dismissible: true, }; describe('TagButton', () => { diff --git a/packages/react-components/react-tags/src/components/TagButton/renderTagButton.tsx b/packages/react-components/react-tags/src/components/TagButton/renderTagButton.tsx index 1d358dcaa60b5d..d8ebda08cd235f 100644 --- a/packages/react-components/react-tags/src/components/TagButton/renderTagButton.tsx +++ b/packages/react-components/react-tags/src/components/TagButton/renderTagButton.tsx @@ -29,7 +29,7 @@ export const renderTagButton_unstable = (state: TagButtonState, contextValues: T {slots.secondaryText && } )} - {slots.dismissButton && state.dismissable && } + {slots.dismissButton && state.dismissible && } ); }; diff --git a/packages/react-components/react-tags/src/components/TagButton/useTagButtonStyles.styles.ts b/packages/react-components/react-tags/src/components/TagButton/useTagButtonStyles.styles.ts index fb28d8ef924084..ff5763f957e8dd 100644 --- a/packages/react-components/react-tags/src/components/TagButton/useTagButtonStyles.styles.ts +++ b/packages/react-components/react-tags/src/components/TagButton/useTagButtonStyles.styles.ts @@ -33,7 +33,7 @@ const useStyles = makeStyles({ circularContent: createCustomFocusIndicatorStyle(shorthands.borderRadius(tokens.borderRadiusCircular), { enableOutline: true, }), - dismissableContent: { + dismissibleContent: { ...createCustomFocusIndicatorStyle({ borderTopRightRadius: tokens.borderRadiusNone, borderBottomRightRadius: tokens.borderRadiusNone, @@ -79,7 +79,7 @@ export const useTagButtonStyles_unstable = (state: TagButtonState): TagButtonSta styles.content, state.shape === 'circular' && styles.circularContent, - state.dismissButton && styles.dismissableContent, + state.dismissButton && styles.dismissibleContent, state.content.className, ); diff --git a/packages/react-components/react-tags/stories/Tag/TagDefault.stories.tsx b/packages/react-components/react-tags/stories/Tag/TagDefault.stories.tsx index 85917d78a12326..596d6bdbfb00b6 100644 --- a/packages/react-components/react-tags/stories/Tag/TagDefault.stories.tsx +++ b/packages/react-components/react-tags/stories/Tag/TagDefault.stories.tsx @@ -20,7 +20,7 @@ export const Default = (props: Partial) => ( /> } secondaryText="Secondary text" - dismissable={true} + dismissible={true} {...props} > Primary text @@ -37,7 +37,7 @@ export const Default = (props: Partial) => ( }} /> } - dismissable={true} + dismissible={true} {...props} > Primary text @@ -58,10 +58,10 @@ export const Default = (props: Partial) => ( > Primary text
- } secondaryText="Secondary text" dismissable={true} {...props} {...props}> + } secondaryText="Secondary text" dismissible={true} {...props} {...props}> Primary text - } dismissable={true} {...props}> + } dismissible={true} {...props}> Primary text } {...props}> @@ -84,7 +84,7 @@ export const Default = (props: Partial) => ( /> } secondaryText="Secondary text" - dismissable={true} + dismissible={true} {...props} > Primary text @@ -102,7 +102,7 @@ export const Default = (props: Partial) => ( }} /> } - dismissable={true} + dismissible={true} {...props} > Primary text @@ -128,13 +128,13 @@ export const Default = (props: Partial) => ( shape="circular" icon={} secondaryText="Secondary text" - dismissable={true} + dismissible={true} {...props} {...props} > Primary text - } dismissable={true} {...props}> + } dismissible={true} {...props}> Primary text } {...props}> diff --git a/packages/react-components/react-tags/stories/Tag/TagDismiss.stories.tsx b/packages/react-components/react-tags/stories/Tag/TagDismiss.stories.tsx index c5b805116d96d4..52cddfec88a2db 100644 --- a/packages/react-components/react-tags/stories/Tag/TagDismiss.stories.tsx +++ b/packages/react-components/react-tags/stories/Tag/TagDismiss.stories.tsx @@ -16,12 +16,12 @@ export const Dismiss = () => { const containerStyles = useContainerStyles(); return (
- Primary text - }> + Primary text + }> Primary text } secondaryText="Secondary text" > diff --git a/packages/react-components/react-tags/stories/Tag/TagShape.stories.tsx b/packages/react-components/react-tags/stories/Tag/TagShape.stories.tsx index 620b4001ce5e12..af3fd6c08a7ccd 100644 --- a/packages/react-components/react-tags/stories/Tag/TagShape.stories.tsx +++ b/packages/react-components/react-tags/stories/Tag/TagShape.stories.tsx @@ -22,10 +22,10 @@ export const Shape = () => { Circular - } secondaryText="Secondary text"> + } secondaryText="Secondary text"> Rounded - } secondaryText="Secondary text"> + } secondaryText="Secondary text"> Circular
diff --git a/packages/react-components/react-tags/stories/TagButton/TagButtonDefault.stories.tsx b/packages/react-components/react-tags/stories/TagButton/TagButtonDefault.stories.tsx index a9ef63153070a5..067a7b2fb2be70 100644 --- a/packages/react-components/react-tags/stories/TagButton/TagButtonDefault.stories.tsx +++ b/packages/react-components/react-tags/stories/TagButton/TagButtonDefault.stories.tsx @@ -20,7 +20,7 @@ export const Default = (props: Partial) => ( /> } secondaryText="Secondary text" - dismissable={true} + dismissible={true} {...props} > Primary text @@ -37,7 +37,7 @@ export const Default = (props: Partial) => ( }} /> } - dismissable={true} + dismissible={true} {...props} > Primary text @@ -61,13 +61,13 @@ export const Default = (props: Partial) => ( } secondaryText="Secondary text" - dismissable={true} + dismissible={true} {...props} {...props} > Primary text - } dismissable={true} {...props}> + } dismissible={true} {...props}> Primary text } {...props}> @@ -90,7 +90,7 @@ export const Default = (props: Partial) => ( /> } secondaryText="Secondary text" - dismissable={true} + dismissible={true} {...props} > Primary text @@ -108,7 +108,7 @@ export const Default = (props: Partial) => ( }} /> } - dismissable={true} + dismissible={true} {...props} > Primary text @@ -134,13 +134,13 @@ export const Default = (props: Partial) => ( shape="circular" icon={} secondaryText="Secondary text" - dismissable={true} + dismissible={true} {...props} {...props} > Primary text - } dismissable={true} {...props}> + } dismissible={true} {...props}> Primary text } {...props}> diff --git a/packages/react-components/react-tags/stories/TagButton/TagButtonDismiss.stories.tsx b/packages/react-components/react-tags/stories/TagButton/TagButtonDismiss.stories.tsx index f10a12d9b45e1e..b0656cc1fae249 100644 --- a/packages/react-components/react-tags/stories/TagButton/TagButtonDismiss.stories.tsx +++ b/packages/react-components/react-tags/stories/TagButton/TagButtonDismiss.stories.tsx @@ -16,12 +16,12 @@ export const Dismiss = () => { const containerStyles = useContainerStyles(); return (
- Primary text - }> + Primary text + }> Primary text } secondaryText="Secondary text" > diff --git a/packages/react-components/react-tags/stories/TagButton/TagButtonShape.stories.tsx b/packages/react-components/react-tags/stories/TagButton/TagButtonShape.stories.tsx index 3f45196559c4b0..4133f1460fe00c 100644 --- a/packages/react-components/react-tags/stories/TagButton/TagButtonShape.stories.tsx +++ b/packages/react-components/react-tags/stories/TagButton/TagButtonShape.stories.tsx @@ -22,10 +22,10 @@ export const Shape = () => { Circular - } secondaryText="Secondary text"> + } secondaryText="Secondary text"> Rounded - } secondaryText="Secondary text"> + } secondaryText="Secondary text"> Circular
From 34237f80fbc5f8df6fc930c776c7542ca45328e5 Mon Sep 17 00:00:00 2001 From: YuanboXue-Amber Date: Tue, 9 May 2023 17:29:17 +0800 Subject: [PATCH 11/18] fix --- .../react-tags/src/components/Tag/Tag.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-components/react-tags/src/components/Tag/Tag.test.tsx b/packages/react-components/react-tags/src/components/Tag/Tag.test.tsx index cc3354d9246375..c061c63599909a 100644 --- a/packages/react-components/react-tags/src/components/Tag/Tag.test.tsx +++ b/packages/react-components/react-tags/src/components/Tag/Tag.test.tsx @@ -2,11 +2,11 @@ import { Tag } from './Tag'; import { isConformant } from '../../testing/isConformant'; const requiredProps = { - media: 'media', + dismissible: true, icon: 'i', + media: 'media', primaryText: 'Primary text', secondaryText: 'Secondary text', - dismissible: true, }; describe('Tag', () => { From 813cf0b4f30b4289d6f04c39827dd12891a18b5d Mon Sep 17 00:00:00 2001 From: YuanboXue-Amber Date: Tue, 9 May 2023 17:31:41 +0800 Subject: [PATCH 12/18] api --- packages/react-components/react-tags/etc/react-tags.api.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/react-components/react-tags/etc/react-tags.api.md b/packages/react-components/react-tags/etc/react-tags.api.md index 5e2ea23ff9c983..88eba16afc7998 100644 --- a/packages/react-components/react-tags/etc/react-tags.api.md +++ b/packages/react-components/react-tags/etc/react-tags.api.md @@ -44,12 +44,11 @@ export const tagClassNames: SlotClassNames; // @public export type TagProps = ComponentProps> & { - size?: 'extra-small' | 'small' | 'medium'; - shape?: 'rounded' | 'circular'; appearance?: 'filled-darker' | 'filled-lighter' | 'tint' | 'outline'; disabled?: boolean; - checked?: boolean; dismissible?: boolean; + shape?: 'rounded' | 'circular'; + size?: 'extra-small' | 'small' | 'medium'; }; // @public (undocumented) @@ -64,7 +63,7 @@ export type TagSlots = { }; // @public -export type TagState = ComponentState & Required & { +export type TagState = ComponentState & Required & { avatarSize: AvatarSize | undefined; avatarShape: AvatarShape | undefined; }>; From e2ab0730c24750612a7b50a10235f2ee736f21b8 Mon Sep 17 00:00:00 2001 From: YuanboXue-Amber Date: Tue, 9 May 2023 17:39:44 +0800 Subject: [PATCH 13/18] change dismissible={true} to just dismiss --- .../react-tags/src/components/Tag/Tag.test.tsx | 3 ++- .../stories/Tag/TagDefault.stories.tsx | 16 ++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/react-components/react-tags/src/components/Tag/Tag.test.tsx b/packages/react-components/react-tags/src/components/Tag/Tag.test.tsx index c061c63599909a..df2e9e30a5fdfa 100644 --- a/packages/react-components/react-tags/src/components/Tag/Tag.test.tsx +++ b/packages/react-components/react-tags/src/components/Tag/Tag.test.tsx @@ -1,7 +1,8 @@ import { Tag } from './Tag'; import { isConformant } from '../../testing/isConformant'; +import { TagProps } from './Tag.types'; -const requiredProps = { +const requiredProps: TagProps = { dismissible: true, icon: 'i', media: 'media', diff --git a/packages/react-components/react-tags/stories/Tag/TagDefault.stories.tsx b/packages/react-components/react-tags/stories/Tag/TagDefault.stories.tsx index 596d6bdbfb00b6..28aef463677683 100644 --- a/packages/react-components/react-tags/stories/Tag/TagDefault.stories.tsx +++ b/packages/react-components/react-tags/stories/Tag/TagDefault.stories.tsx @@ -20,7 +20,7 @@ export const Default = (props: Partial) => ( /> } secondaryText="Secondary text" - dismissible={true} + dismissible {...props} > Primary text @@ -37,7 +37,7 @@ export const Default = (props: Partial) => ( }} /> } - dismissible={true} + dismissible {...props} > Primary text @@ -58,10 +58,10 @@ export const Default = (props: Partial) => ( > Primary text
- } secondaryText="Secondary text" dismissible={true} {...props} {...props}> + } secondaryText="Secondary text" dismissible {...props} {...props}> Primary text - } dismissible={true} {...props}> + } dismissible {...props}> Primary text } {...props}> @@ -84,7 +84,7 @@ export const Default = (props: Partial) => ( /> } secondaryText="Secondary text" - dismissible={true} + dismissible {...props} > Primary text @@ -102,7 +102,7 @@ export const Default = (props: Partial) => ( }} /> } - dismissible={true} + dismissible {...props} > Primary text @@ -128,13 +128,13 @@ export const Default = (props: Partial) => ( shape="circular" icon={} secondaryText="Secondary text" - dismissible={true} + dismissible {...props} {...props} > Primary text - } dismissible={true} {...props}> + } dismissible {...props}> Primary text } {...props}> From 8a7c618eaad1623d216c253936012a718dc4456e Mon Sep 17 00:00:00 2001 From: YuanboXue-Amber Date: Tue, 9 May 2023 17:46:06 +0800 Subject: [PATCH 14/18] alphabetic sort --- .../react-components/react-tags/src/components/Tag/useTag.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-components/react-tags/src/components/Tag/useTag.tsx b/packages/react-components/react-tags/src/components/Tag/useTag.tsx index 048876b1931aef..92f9dcfb0613e2 100644 --- a/packages/react-components/react-tags/src/components/Tag/useTag.tsx +++ b/packages/react-components/react-tags/src/components/Tag/useTag.tsx @@ -28,11 +28,11 @@ const DismissIcon = bundleIcon(DismissFilled, DismissRegular); */ export const useTag_unstable = (props: TagProps, ref: React.Ref): TagState => { const { + appearance = 'filled-lighter', disabled = false, dismissible = false, shape = 'rounded', size = 'medium', - appearance = 'filled-lighter', } = props; return { From 867c0c98884d3a661b91f0c4e7a82eac3e2fc638 Mon Sep 17 00:00:00 2001 From: YuanboXue-Amber Date: Tue, 9 May 2023 17:48:32 +0800 Subject: [PATCH 15/18] remove crud --- .../react-tags/stories/Tag/TagDefault.stories.tsx | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/react-components/react-tags/stories/Tag/TagDefault.stories.tsx b/packages/react-components/react-tags/stories/Tag/TagDefault.stories.tsx index 28aef463677683..cbdcd13e85740e 100644 --- a/packages/react-components/react-tags/stories/Tag/TagDefault.stories.tsx +++ b/packages/react-components/react-tags/stories/Tag/TagDefault.stories.tsx @@ -58,7 +58,7 @@ export const Default = (props: Partial) => ( > Primary text - } secondaryText="Secondary text" dismissible {...props} {...props}> + } secondaryText="Secondary text" dismissible {...props}> Primary text } dismissible {...props}> @@ -124,14 +124,7 @@ export const Default = (props: Partial) => ( > Primary text - } - secondaryText="Secondary text" - dismissible - {...props} - {...props} - > + } secondaryText="Secondary text" dismissible {...props}> Primary text } dismissible {...props}> From 270a4dc05025a38f5c379a883203d223eff09fd0 Mon Sep 17 00:00:00 2001 From: YuanboXue-Amber Date: Tue, 9 May 2023 20:44:07 +0800 Subject: [PATCH 16/18] fix content typing --- .../react-tags/src/components/Tag/Tag.types.ts | 3 ++- .../react-tags/src/components/Tag/useTag.tsx | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/react-components/react-tags/src/components/Tag/Tag.types.ts b/packages/react-components/react-tags/src/components/Tag/Tag.types.ts index 4c892ce8b2f21a..f77fcfe51cb51b 100644 --- a/packages/react-components/react-tags/src/components/Tag/Tag.types.ts +++ b/packages/react-components/react-tags/src/components/Tag/Tag.types.ts @@ -1,3 +1,4 @@ +import { ARIAButtonSlotProps } from '@fluentui/react-aria'; import { AvatarSize, AvatarShape } from '@fluentui/react-avatar'; import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; @@ -19,7 +20,7 @@ export type TagSlots = { /** * A layout wrapper for the icon slot, the primaryText and secondaryText slots */ - content: Slot<'div', 'button'>; + content: NonNullable>>; icon: Slot<'span'>; diff --git a/packages/react-components/react-tags/src/components/Tag/useTag.tsx b/packages/react-components/react-tags/src/components/Tag/useTag.tsx index faf20d73756dac..bbfece6afd43e9 100644 --- a/packages/react-components/react-tags/src/components/Tag/useTag.tsx +++ b/packages/react-components/react-tags/src/components/Tag/useTag.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { getNativeElementProps, resolveShorthand } from '@fluentui/react-utilities'; import { DismissRegular, bundleIcon, DismissFilled } from '@fluentui/react-icons'; import type { TagProps, TagState } from './Tag.types'; -import { ARIAButtonSlotProps, useARIAButtonShorthand } from '@fluentui/react-aria'; +import { useARIAButtonShorthand } from '@fluentui/react-aria'; const tagAvatarSizeMap = { medium: 28, @@ -36,7 +36,7 @@ export const useTag_unstable = (props: TagProps, ref: React.Ref): T size = 'medium', } = props; - const contentButton = useARIAButtonShorthand(props.content as ARIAButtonSlotProps, { + const contentButton = useARIAButtonShorthand(props.content, { required: true, defaultProps: { disabled, @@ -68,7 +68,7 @@ export const useTag_unstable = (props: TagProps, ref: React.Ref): T ...props, }), - content: interactive ? (contentButton as TagState['content']) : resolveShorthand(props.content, { required: true }), + content: interactive ? contentButton : resolveShorthand(props.content, { required: true }), media: resolveShorthand(props.media), icon: resolveShorthand(props.icon), primaryText: resolveShorthand(props.primaryText, { required: true, defaultProps: { children: props.children } }), From f64cd3872615bd34e45c8b443310eca29ff4374e Mon Sep 17 00:00:00 2001 From: YuanboXue-Amber Date: Tue, 9 May 2023 21:29:22 +0800 Subject: [PATCH 17/18] improve slot typing --- .../react-tags/etc/react-tags.api.md | 13 ++++++----- .../src/components/Tag/Tag.types.ts | 10 ++++----- .../src/components/Tag/renderTag.tsx | 22 +++++++++---------- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/packages/react-components/react-tags/etc/react-tags.api.md b/packages/react-components/react-tags/etc/react-tags.api.md index 13303420a63824..a2ebc70166393a 100644 --- a/packages/react-components/react-tags/etc/react-tags.api.md +++ b/packages/react-components/react-tags/etc/react-tags.api.md @@ -6,6 +6,7 @@ /// +import { ARIAButtonSlotProps } from '@fluentui/react-aria'; import { AvatarShape } from '@fluentui/react-avatar'; import { AvatarSize } from '@fluentui/react-avatar'; import type { ComponentProps } from '@fluentui/react-utilities'; @@ -37,12 +38,12 @@ export type TagProps = ComponentProps> & { // @public (undocumented) export type TagSlots = { root: NonNullable>; - media: Slot<'span'>; - content: Slot<'div', 'button'>; - icon: Slot<'span'>; - primaryText: Slot<'span'>; - secondaryText: Slot<'span'>; - dismissButton: Slot<'button'>; + media?: Slot<'span'>; + content: NonNullable>>; + icon?: Slot<'span'>; + primaryText: NonNullable>; + secondaryText?: Slot<'span'>; + dismissButton?: Slot<'button'>; }; // @public diff --git a/packages/react-components/react-tags/src/components/Tag/Tag.types.ts b/packages/react-components/react-tags/src/components/Tag/Tag.types.ts index f77fcfe51cb51b..4a9ebe29c8d086 100644 --- a/packages/react-components/react-tags/src/components/Tag/Tag.types.ts +++ b/packages/react-components/react-tags/src/components/Tag/Tag.types.ts @@ -15,26 +15,26 @@ export type TagSlots = { /** * Slot for an icon or other visual element */ - media: Slot<'span'>; + media?: Slot<'span'>; /** * A layout wrapper for the icon slot, the primaryText and secondaryText slots */ content: NonNullable>>; - icon: Slot<'span'>; + icon?: Slot<'span'>; /** * Main text for the Tag. Children of the root slot are automatically rendered here */ - primaryText: Slot<'span'>; + primaryText: NonNullable>; /** * Secondary text that describes or complements the main text */ - secondaryText: Slot<'span'>; + secondaryText?: Slot<'span'>; - dismissButton: Slot<'button'>; + dismissButton?: Slot<'button'>; }; /** diff --git a/packages/react-components/react-tags/src/components/Tag/renderTag.tsx b/packages/react-components/react-tags/src/components/Tag/renderTag.tsx index 5bb795c5e4925d..73e5d32a4a82b1 100644 --- a/packages/react-components/react-tags/src/components/Tag/renderTag.tsx +++ b/packages/react-components/react-tags/src/components/Tag/renderTag.tsx @@ -15,18 +15,16 @@ export const renderTag_unstable = (state: TagState, contextValues: TagContextVal return ( - {slots.content && ( - - {slots.media && ( - - - - )} - {slots.icon && } - {slots.primaryText && } - {slots.secondaryText && } - - )} + + {slots.media && ( + + + + )} + {slots.icon && } + + {slots.secondaryText && } + {slots.dismissButton && state.dismissible && } ); From 51bcf58168a3cccc2aafcc30cb1e82351a8eb7f8 Mon Sep 17 00:00:00 2001 From: YuanboXue-Amber Date: Tue, 9 May 2023 22:42:47 +0800 Subject: [PATCH 18/18] use simple resolve shorthand for dismissButton instead of useARIAButtonShorthand --- .../react-components/react-tags/src/components/Tag/useTag.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-components/react-tags/src/components/Tag/useTag.tsx b/packages/react-components/react-tags/src/components/Tag/useTag.tsx index bbfece6afd43e9..b1ae4b25047d2c 100644 --- a/packages/react-components/react-tags/src/components/Tag/useTag.tsx +++ b/packages/react-components/react-tags/src/components/Tag/useTag.tsx @@ -73,7 +73,7 @@ export const useTag_unstable = (props: TagProps, ref: React.Ref): T icon: resolveShorthand(props.icon), primaryText: resolveShorthand(props.primaryText, { required: true, defaultProps: { children: props.children } }), secondaryText: resolveShorthand(props.secondaryText), - dismissButton: useARIAButtonShorthand(props.dismissButton, { + dismissButton: resolveShorthand(props.dismissButton, { required: props.dismissible, defaultProps: { disabled,