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 88eba16afc7998..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'; @@ -18,27 +19,9 @@ import type { SlotClassNames } from '@fluentui/react-utilities'; // @public export const renderTag_unstable: (state: TagState, contextValues: TagContextValues) => JSX.Element; -// @public -export const renderTagButton_unstable: (state: TagButtonState, contextValues: TagButtonContextValues) => JSX.Element; - // @public export const Tag: ForwardRefComponent; -// @public -export const TagButton: ForwardRefComponent; - -// @public (undocumented) -export const tagButtonClassNames: SlotClassNames; - -// @public -export type TagButtonProps = TagProps; - -// @public (undocumented) -export type TagButtonSlots = TagSlots; - -// @public -export type TagButtonState = TagState; - // @public (undocumented) export const tagClassNames: SlotClassNames; @@ -47,6 +30,7 @@ export type TagProps = ComponentProps> & { appearance?: 'filled-darker' | 'filled-lighter' | 'tint' | 'outline'; disabled?: boolean; dismissible?: boolean; + interactive?: boolean; shape?: 'rounded' | 'circular'; size?: 'extra-small' | 'small' | 'medium'; }; @@ -54,16 +38,16 @@ export type TagProps = ComponentProps> & { // @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'>; + media?: Slot<'span'>; + content: NonNullable>>; + icon?: Slot<'span'>; + primaryText: NonNullable>; + secondaryText?: Slot<'span'>; + dismissButton?: Slot<'button'>; }; // @public -export type TagState = ComponentState & Required & { +export type TagState = ComponentState & Required & { avatarSize: AvatarSize | undefined; avatarShape: AvatarShape | undefined; }>; @@ -71,12 +55,6 @@ export type TagState = ComponentState & Required) => TagState; -// @public -export const useTagButton_unstable: (props: TagButtonProps, ref: React_2.Ref) => TagButtonState; - -// @public -export const useTagButtonStyles_unstable: (state: TagButtonState) => TagButtonState; - // @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..df38951b5f032f 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": "*", 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/components/Tag/Tag.test.tsx b/packages/react-components/react-tags/src/components/Tag/Tag.test.tsx index df2e9e30a5fdfa..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 @@ -16,4 +16,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 4a63246869152f..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 @@ -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'; @@ -14,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: Slot<'div'>; + 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'>; }; /** @@ -45,6 +46,7 @@ export type TagProps = ComponentProps> & { // checked?: boolean; disabled?: boolean; dismissible?: boolean; + interactive?: boolean; shape?: 'rounded' | 'circular'; size?: 'extra-small' | 'small' | 'medium'; }; @@ -54,7 +56,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 92e79fd3afdee1..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,20 +15,16 @@ export const renderTag_unstable = (state: TagState, contextValues: TagContextVal return ( - {slots.content && ( - - {slots.media && ( - - - - )} - {slots.icon && } - {slots.primaryText && ( - {slotProps.root.children} - )} - {slots.secondaryText && } - - )} + + {slots.media && ( + + + + )} + {slots.icon && } + + {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 92f9dcfb0613e2..b1ae4b25047d2c 100644 --- a/packages/react-components/react-tags/src/components/Tag/useTag.tsx +++ b/packages/react-components/react-tags/src/components/Tag/useTag.tsx @@ -31,40 +31,49 @@ export const useTag_unstable = (props: TagProps, ref: React.Ref): T appearance = 'filled-lighter', disabled = false, dismissible = false, + interactive = false, shape = 'rounded', size = 'medium', } = props; + const contentButton = useARIAButtonShorthand(props.content, { + required: true, + defaultProps: { + disabled, + type: 'button', + }, + }); + return { appearance, avatarShape: tagAvatarShapeMap[shape], avatarSize: tagAvatarSizeMap[size], disabled, dismissible, + interactive, shape, size, components: { root: 'div', - content: 'div', + content: interactive ? 'button' : 'div', media: 'span', icon: 'span', primaryText: 'span', secondaryText: 'span', dismissButton: 'button', }, - root: getNativeElementProps('div', { ref, ...props, }), - 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 }), + 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, diff --git a/packages/react-components/react-tags/src/utils/useTagContextValues.ts b/packages/react-components/react-tags/src/components/Tag/useTagContextValues.ts similarity index 82% rename from packages/react-components/react-tags/src/utils/useTagContextValues.ts rename to packages/react-components/react-tags/src/components/Tag/useTagContextValues.ts index d394a39d56c331..cc2150595acd55 100644 --- a/packages/react-components/react-tags/src/utils/useTagContextValues.ts +++ b/packages/react-components/react-tags/src/components/Tag/useTagContextValues.ts @@ -1,5 +1,5 @@ import * as React from 'react'; -import { TagContextValues, TagState } from '../components/Tag/index'; +import { TagState, TagContextValues } from './Tag.types'; export function useTagContextValues_unstable(state: TagState): TagContextValues { const { avatarSize, avatarShape } = state; 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 127333f259289e..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 @@ -7,17 +7,27 @@ import { createCustomFocusIndicatorStyle } from '@fluentui/react-tabster'; export const tagClassNames: SlotClassNames = { root: 'fui-Tag', content: 'fui-Tag__content', - media: 'fui-Tag__media', + dismissButton: 'fui-Tag__dismissButton', icon: 'fui-Tag__icon', + media: 'fui-Tag__media', primaryText: 'fui-Tag__primaryText', secondaryText: 'fui-Tag__secondaryText', - dismissButton: 'fui-Tag__dismissButton', }; -/** - * 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', @@ -33,13 +43,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', @@ -54,7 +57,16 @@ export const useTagBaseStyles = makeStyles({ textOnlyContent: { paddingLeft: tokens.spacingHorizontalS, }, + nonInteractiveDismissibleContent: { + paddingRight: '2px', + }, + media: { + ...shorthands.gridArea('media'), + alignSelf: 'center', + paddingLeft: tokens.spacingHorizontalXXS, + paddingRight: tokens.spacingHorizontalS, + }, icon: { display: 'flex', alignSelf: 'center', @@ -81,17 +93,6 @@ export const useTagBaseStyles = makeStyles({ ...typographyStyles.caption2, }, - resetButton: { - boxSizing: 'content-box', - color: 'inherit', - fontFamily: 'inherit', - lineHeight: 'normal', - ...shorthands.overflow('visible'), - ...shorthands.padding(0), - ...shorthands.borderStyle('none'), - appearance: 'button', - textAlign: 'unset', - }, dismissButton: { ...shorthands.padding('0px'), backgroundColor: 'transparent', @@ -108,71 +109,115 @@ export const useTagBaseStyles = makeStyles({ { enableOutline: true }, ), }, + nonInteractiveDismissButton: { + marginRight: '6px', + }, // TODO add additional classes for fill/outline appearance, different sizes, and state }); -const useTagStyles = makeStyles({ +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: { - paddingRight: '2px', + ...createCustomFocusIndicatorStyle({ + borderTopRightRadius: tokens.borderRadiusNone, + borderBottomRightRadius: tokens.borderRadiusNone, + }), }, + dismissButton: { - marginRight: '6px', + ...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 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, - baseStyles.content, - !state.media && !state.icon && baseStyles.textOnlyContent, + 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.dismissButton && styles.dismissibleContent, state.content.className, ); } if (state.media) { - state.media.className = mergeClasses(tagClassNames.media, baseStyles.media, state.media.className); + state.media.className = mergeClasses(tagClassNames.media, styles.media, state.media.className); } + if (state.icon) { - state.icon.className = mergeClasses(tagClassNames.icon, baseStyles.icon, state.icon.className); + state.icon.className = mergeClasses(tagClassNames.icon, styles.icon, state.icon.className); } + if (state.primaryText) { state.primaryText.className = mergeClasses( tagClassNames.primaryText, - baseStyles.primaryText, - state.secondaryText && baseStyles.primaryTextWithSecondaryText, + styles.primaryText, + state.secondaryText && styles.primaryTextWithSecondaryText, state.primaryText.className, ); } + if (state.secondaryText) { state.secondaryText.className = mergeClasses( tagClassNames.secondaryText, - baseStyles.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 && styles.nonInteractiveDismissButton, + + 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 003e20c001ac91..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', - dismissible: 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 d8ebda08cd235f..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.dismissible && } - - ); -}; 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 ff5763f957e8dd..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, - }), - dismissibleContent: { - ...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.dismissibleContent, - - 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/index.ts b/packages/react-components/react-tags/src/index.ts index 4d1d8029598994..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 { - TagButton, - renderTagButton_unstable, - tagButtonClassNames, - useTagButtonStyles_unstable, - useTagButton_unstable, -} from './TagButton'; -export type { TagButtonProps, TagButtonSlots, TagButtonState } from './TagButton'; 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..01936fe78d9464 --- /dev/null +++ b/packages/react-components/react-tags/stories/Tag/TagInteractive.stories.tsx @@ -0,0 +1,64 @@ +import * as React from 'react'; +import { Tag } 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..ed780a9fd0ca67 100644 --- a/packages/react-components/react-tags/stories/Tag/TagMedia.stories.tsx +++ b/packages/react-components/react-tags/stories/Tag/TagMedia.stories.tsx @@ -9,7 +9,7 @@ Media.storyName = 'Media'; Media.parameters = { docs: { description: { - story: 'A tag 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/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 067a7b2fb2be70..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" - dismissible={true} - {...props} - > - Primary text - - - } - dismissible={true} - {...props} - > - Primary text - - - } - {...props} - > - Primary text - - } - secondaryText="Secondary text" - dismissible={true} - {...props} - {...props} - > - Primary text - - } dismissible={true} {...props}> - Primary text - - } {...props}> - Primary text - - Primary text -
-
- - } - secondaryText="Secondary text" - dismissible={true} - {...props} - > - Primary text - - - } - dismissible={true} - {...props} - > - Primary text - - - } - {...props} - > - Primary text - - } - secondaryText="Secondary text" - dismissible={true} - {...props} - {...props} - > - Primary text - - } dismissible={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 b0656cc1fae249..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 4133f1460fe00c..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'), - }, - }, - }, -};