diff --git a/apps/vr-tests-react-components/src/stories/Tabs.stories.tsx b/apps/vr-tests-react-components/src/stories/Tabs.stories.tsx index a1b23b5cb64f5..c3181064c4f0b 100644 --- a/apps/vr-tests-react-components/src/stories/Tabs.stories.tsx +++ b/apps/vr-tests-react-components/src/stories/Tabs.stories.tsx @@ -207,8 +207,8 @@ export const WithIconOnlyAndVertical = () => ( WithIconOnlyAndVertical.storyName = 'With icon only and vertical'; -export const CircularSubtleAppearance = () => ( - +export const SubtleRoundedAppearance = () => ( + First Second @@ -217,12 +217,12 @@ export const CircularSubtleAppearance = () => ( ); -export const CircularSubtleAppearanceDarkMode = getStoryVariant(CircularSubtleAppearance, DARK_MODE); +export const SubtleRoundedAppearanceDarkMode = getStoryVariant(SubtleRoundedAppearance, DARK_MODE); -export const CircularSubtleAppearanceHighContrast = getStoryVariant(CircularSubtleAppearance, HIGH_CONTRAST); +export const SubtleRoundedAppearanceHighContrast = getStoryVariant(SubtleRoundedAppearance, HIGH_CONTRAST); -export const CircularFilledAppearance = () => ( - +export const FilledRoundedAppearance = () => ( + First Second @@ -231,6 +231,6 @@ export const CircularFilledAppearance = () => ( ); -export const CircularFilledAppearanceDarkMode = getStoryVariant(CircularFilledAppearance, DARK_MODE); +export const FilledRoundedAppearanceDarkMode = getStoryVariant(FilledRoundedAppearance, DARK_MODE); -export const CircularFilledAppearanceHighContrast = getStoryVariant(CircularFilledAppearance, HIGH_CONTRAST); +export const FilledRoundedAppearanceHighContrast = getStoryVariant(FilledRoundedAppearance, HIGH_CONTRAST); diff --git a/packages/react-components/react-tabs/library/src/components/Tab/Tab.types.ts b/packages/react-components/react-tabs/library/src/components/Tab/Tab.types.ts index 087bd0e99de30..acb7d16710d55 100644 --- a/packages/react-components/react-tabs/library/src/components/Tab/Tab.types.ts +++ b/packages/react-components/react-tabs/library/src/components/Tab/Tab.types.ts @@ -50,9 +50,9 @@ export type TabState = ComponentState & Pick & Required> & { /** - * A tab supports 'transparent' and 'subtle' appearance. + * A tab supports 'transparent', 'subtle', `subtle-rounded` and `filled-rounded` appearance. */ - appearance?: 'transparent' | 'subtle' | 'filled'; + appearance?: 'transparent' | 'subtle' | 'subtle-rounded' | 'filled-rounded'; /** * A tab can have only an icon slot filled and no content. @@ -73,12 +73,6 @@ export type TabState = ComponentState & * A tab can be either 'small', 'medium', or 'large' size. */ size: 'small' | 'medium' | 'large'; - - /** - * A tab can have a circular shape. Could be used only with 'filled' or 'subtle' appearance. - */ - shape?: 'circular'; - /** * A tab can arrange its content based on if the tabs in the list are arranged vertically. */ diff --git a/packages/react-components/react-tabs/library/src/components/Tab/useTab.ts b/packages/react-components/react-tabs/library/src/components/Tab/useTab.ts index bd34c49e08b0e..32ba1962d3ad3 100644 --- a/packages/react-components/react-tabs/library/src/components/Tab/useTab.ts +++ b/packages/react-components/react-tabs/library/src/components/Tab/useTab.ts @@ -33,7 +33,6 @@ export const useTab_unstable = (props: TabProps, ref: React.Ref): T const onSelect = useTabListContext_unstable(ctx => ctx.onSelect); const size = useTabListContext_unstable(ctx => ctx.size); const vertical = useTabListContext_unstable(ctx => !!ctx.vertical); - const shape = useTabListContext_unstable(ctx => ctx.shape); const disabled = listDisabled || tabDisabled; const innerRef = React.useRef(null); @@ -89,7 +88,6 @@ export const useTab_unstable = (props: TabProps, ref: React.Ref): T elementType: 'span', }), appearance, - shape, disabled, selected, size, diff --git a/packages/react-components/react-tabs/library/src/components/Tab/useTabStyles.styles.ts b/packages/react-components/react-tabs/library/src/components/Tab/useTabStyles.styles.ts index 0318140cc7269..c9b5608698ca5 100644 --- a/packages/react-components/react-tabs/library/src/components/Tab/useTabStyles.styles.ts +++ b/packages/react-components/react-tabs/library/src/components/Tab/useTabStyles.styles.ts @@ -88,55 +88,55 @@ const useRootStyles = makeStyles({ }, transparent: { backgroundColor: tokens.colorTransparentBackground, - ':hover': { + ':enabled:hover': { backgroundColor: tokens.colorTransparentBackgroundHover, }, - ':active': { + ':enabled:active': { backgroundColor: tokens.colorTransparentBackgroundPressed, }, '& .fui-Tab__icon': { color: tokens.colorNeutralForeground2, }, - ':hover .fui-Tab__icon': { + ':enabled:hover .fui-Tab__icon': { color: tokens.colorNeutralForeground2Hover, }, - ':active .fui-Tab__icon': { + ':enabled:active .fui-Tab__icon': { color: tokens.colorNeutralForeground2Pressed, }, '& .fui-Tab__content': { color: tokens.colorNeutralForeground2, }, - ':hover .fui-Tab__content': { + ':enabled:hover .fui-Tab__content': { color: tokens.colorNeutralForeground2Hover, }, - ':active .fui-Tab__content': { + ':enabled:active .fui-Tab__content': { color: tokens.colorNeutralForeground2Pressed, }, }, subtle: { backgroundColor: tokens.colorSubtleBackground, - ':hover': { + ':enabled:hover': { backgroundColor: tokens.colorSubtleBackgroundHover, }, - ':active': { + ':enabled:active': { backgroundColor: tokens.colorSubtleBackgroundPressed, }, '& .fui-Tab__icon': { color: tokens.colorNeutralForeground2, }, - ':hover .fui-Tab__icon': { + ':enabled:hover .fui-Tab__icon': { color: tokens.colorNeutralForeground2Hover, }, - ':active .fui-Tab__icon': { + ':enabled:active .fui-Tab__icon': { color: tokens.colorNeutralForeground2Pressed, }, '& .fui-Tab__content': { color: tokens.colorNeutralForeground2, }, - ':hover .fui-Tab__content': { + ':enabled:hover .fui-Tab__content': { color: tokens.colorNeutralForeground2Hover, }, - ':active .fui-Tab__content': { + ':enabled:active .fui-Tab__content': { color: tokens.colorNeutralForeground2Pressed, }, }, @@ -155,26 +155,23 @@ const useRootStyles = makeStyles({ '& .fui-Tab__icon': { color: tokens.colorCompoundBrandForeground1, }, - ':hover .fui-Tab__icon': { + ':enabled:hover .fui-Tab__icon': { color: tokens.colorCompoundBrandForeground1Hover, }, - ':active .fui-Tab__icon': { + ':enabled:active .fui-Tab__icon': { color: tokens.colorCompoundBrandForeground1Pressed, }, '& .fui-Tab__content': { color: tokens.colorNeutralForeground1, }, - ':hover .fui-Tab__content': { + ':enabled:hover .fui-Tab__content': { color: tokens.colorNeutralForeground1Hover, }, - ':active .fui-Tab__content': { + ':enabled:active .fui-Tab__content': { color: tokens.colorNeutralForeground1Pressed, }, }, -}); - -const useCircularStyles = makeStyles({ - base: { + rounded: { borderRadius: tokens.borderRadiusCircular, '& .fui-Tab__icon': { color: 'inherit', @@ -183,79 +180,75 @@ const useCircularStyles = makeStyles({ color: 'inherit', }, }, - subtle: { + subtleRounded: { backgroundColor: tokens.colorTransparentBackground, border: `solid ${tokens.strokeWidthThin} ${tokens.colorTransparentStroke}`, color: tokens.colorNeutralForeground2, - ':hover': { + ':enabled:hover': { backgroundColor: tokens.colorNeutralBackground1Hover, border: `solid ${tokens.strokeWidthThin} ${tokens.colorNeutralStroke1Hover}`, color: tokens.colorNeutralForeground2Hover, }, - ':active': { + ':enabled:active': { backgroundColor: tokens.colorNeutralBackground1Pressed, border: `solid ${tokens.strokeWidthThin} ${tokens.colorNeutralStroke1Pressed}`, color: tokens.colorNeutralForeground2Pressed, }, }, - subtleSelected: { + subtleRoundedSelected: { backgroundColor: tokens.colorBrandBackground2, border: `solid ${tokens.strokeWidthThin} ${tokens.colorCompoundBrandStroke}`, color: tokens.colorBrandForeground2, - ':hover': { + ':enabled:hover': { backgroundColor: tokens.colorBrandBackground2Hover, border: `solid ${tokens.strokeWidthThin} ${tokens.colorCompoundBrandStrokeHover}`, color: tokens.colorBrandForeground2Hover, }, - ':active': { + ':enabled:active': { backgroundColor: tokens.colorBrandBackground2Pressed, border: `solid ${tokens.strokeWidthThin} ${tokens.colorCompoundBrandStrokePressed}`, color: tokens.colorBrandForeground2Pressed, }, }, - subtleDisabled: { + subtleRoundedDisabled: { backgroundColor: tokens.colorTransparentBackground, color: tokens.colorNeutralForegroundDisabled, border: `solid ${tokens.strokeWidthThin} ${tokens.colorTransparentStroke}`, }, - subtleDisabledSelected: { - backgroundColor: tokens.colorTransparentBackground, - color: tokens.colorNeutralForegroundDisabled, + subtleRoundedDisabledSelected: { border: `solid ${tokens.strokeWidthThin} ${tokens.colorNeutralStrokeDisabled}`, }, - filled: { + filledRounded: { backgroundColor: tokens.colorNeutralBackground3, color: tokens.colorNeutralForeground2, - ':hover': { + ':enabled:hover': { backgroundColor: tokens.colorNeutralBackground3Hover, color: tokens.colorNeutralForeground2Hover, }, - ':active': { + ':enabled:active': { backgroundColor: tokens.colorNeutralBackground3Pressed, color: tokens.colorNeutralForeground2Pressed, }, }, - filledSelected: { + filledRoundedSelected: { backgroundColor: tokens.colorBrandBackground, color: tokens.colorNeutralForegroundOnBrand, - ':hover': { + ':enabled:hover': { backgroundColor: tokens.colorBrandBackgroundHover, color: tokens.colorNeutralForegroundOnBrand, }, - ':active': { + ':enabled:active': { backgroundColor: tokens.colorBrandBackgroundPressed, color: tokens.colorNeutralForegroundOnBrand, }, }, - filledDisabled: { + filledRoundedDisabled: { backgroundColor: tokens.colorNeutralBackgroundDisabled, border: `solid ${tokens.strokeWidthThin} ${tokens.colorTransparentStroke}`, color: tokens.colorNeutralForegroundDisabled, }, - filledDisabledSelected: { - backgroundColor: tokens.colorNeutralBackgroundDisabled, + filledRoundedDisabledSelected: { border: `solid ${tokens.strokeWidthThin} ${tokens.colorNeutralStrokeDisabled}`, - color: tokens.colorNeutralForegroundDisabled, }, }); @@ -377,20 +370,20 @@ const useActiveIndicatorStyles = makeStyles({ '::after': { backgroundColor: tokens.colorCompoundBrandStroke, }, - ':hover::after': { + ':enabled:hover::after': { backgroundColor: tokens.colorCompoundBrandStrokeHover, }, - ':active::after': { + ':enabled:active::after': { backgroundColor: tokens.colorCompoundBrandStrokePressed, }, '@media (forced-colors: active)': { '::after': { backgroundColor: 'ButtonText', }, - ':hover::after': { + ':enabled:hover::after': { backgroundColor: 'ButtonText', }, - ':active::after': { + ':enabled:active::after': { backgroundColor: 'ButtonText', }, }, @@ -558,12 +551,11 @@ export const useTabIndicatorStyles_unstable = (state: TabState): TabState => { const pendingIndicatorStyles = usePendingIndicatorStyles(); const activeIndicatorStyles = useActiveIndicatorStyles(); - const { disabled, selected, size, shape, vertical } = state; + const { appearance, disabled, selected, size, vertical } = state; const classes: Parameters = [tabClassNames.root, rootStyles.root]; - // Only apply indicator styles if the shape is not circular - if (shape !== 'circular') { + if (appearance !== 'subtle-rounded' && appearance !== 'filled-rounded') { classes.push( // pending indicator (before pseudo element) pendingIndicatorStyles.base, @@ -610,11 +602,26 @@ export const useTabButtonStyles_unstable = (state: TabState, slot: TabState['roo const rootStyles = useRootStyles(); const focusStyles = useFocusStyles(); - const circularStyles = useCircularStyles(); - const { appearance, shape, disabled, selected, size, vertical } = state; - - const isCircular = shape === 'circular'; + const { appearance, disabled, selected, size, vertical } = state; + + const isSubtleRounded = appearance === 'subtle-rounded'; + const isFilledRounded = appearance === 'filled-rounded'; + const isRounded = isSubtleRounded || isFilledRounded; + + const roundedAppearance = [ + rootStyles.rounded, + // subtle-rounded appearance + isSubtleRounded && rootStyles.subtleRounded, + selected && isSubtleRounded && rootStyles.subtleRoundedSelected, + disabled && isSubtleRounded && rootStyles.subtleRoundedDisabled, + selected && disabled && isSubtleRounded && rootStyles.subtleRoundedDisabledSelected, + // filled-rounded appearance + isFilledRounded && rootStyles.filledRounded, + selected && isFilledRounded && rootStyles.filledRoundedSelected, + disabled && isFilledRounded && rootStyles.filledRoundedDisabled, + selected && disabled && isFilledRounded && rootStyles.filledRoundedDisabledSelected, + ]; slot.className = mergeClasses( rootStyles.button, @@ -625,25 +632,11 @@ export const useTabButtonStyles_unstable = (state: TabState, slot: TabState['roo size === 'medium' && (vertical ? rootStyles.mediumVertical : rootStyles.mediumHorizontal), size === 'large' && (vertical ? rootStyles.largeVertical : rootStyles.largeHorizontal), focusStyles.base, - // appearance - !disabled && appearance === 'subtle' && !isCircular && rootStyles.subtle, - !disabled && appearance === 'transparent' && rootStyles.transparent, - !disabled && selected && !isCircular && rootStyles.selected, + appearance === 'subtle' && rootStyles.subtle, + appearance === 'transparent' && rootStyles.transparent, + ...(isRounded ? roundedAppearance : [selected && rootStyles.selected]), disabled && rootStyles.disabled, - // circular tabs - isCircular && circularStyles.base, - // subtle + circular - !disabled && appearance === 'subtle' && isCircular && circularStyles.subtle, - !disabled && appearance === 'subtle' && selected && isCircular && circularStyles.subtleSelected, - disabled && appearance === 'subtle' && isCircular && circularStyles.subtleDisabled, - disabled && appearance === 'subtle' && selected && isCircular && circularStyles.subtleDisabledSelected, - // filled + circular - !disabled && appearance === 'filled' && isCircular && circularStyles.filled, - !disabled && appearance === 'filled' && selected && isCircular && circularStyles.filledSelected, - disabled && appearance === 'filled' && isCircular && circularStyles.filledDisabled, - disabled && appearance === 'filled' && selected && isCircular && circularStyles.filledDisabledSelected, - slot.className, ); diff --git a/packages/react-components/react-tabs/library/src/components/TabList/TabList.types.ts b/packages/react-components/react-tabs/library/src/components/TabList/TabList.types.ts index 0d0c7a3cca964..f49a36fb60583 100644 --- a/packages/react-components/react-tabs/library/src/components/TabList/TabList.types.ts +++ b/packages/react-components/react-tabs/library/src/components/TabList/TabList.types.ts @@ -42,12 +42,13 @@ export type TabListProps = ComponentProps & { * A tab list can supports 'transparent' and 'subtle' appearance. *- 'subtle': Minimizes emphasis to blend into the background until hovered or focused. *- 'transparent': No background and border styling - *- 'filled': Adds background and border styling + *- 'subtle-rounded': Adds background and border styling + *- 'filled-rounded': Adds background styling * * The appearance affects each of the contained tabs. * @default 'transparent' */ - appearance?: 'transparent' | 'subtle' | 'filled'; + appearance?: 'transparent' | 'subtle' | 'subtle-rounded' | 'filled-rounded'; /** * Tab size may change between unselected and selected states. @@ -94,11 +95,6 @@ export type TabListProps = ComponentProps & { */ size?: 'small' | 'medium' | 'large'; - /** - * A tab list can be set to have a circular shape. Could be used with 'filled' or 'subtle' appearance. - */ - shape?: 'circular'; - /** * A tab list can arrange its tabs vertically. * @default false @@ -108,7 +104,7 @@ export type TabListProps = ComponentProps & { export type TabListContextValue = Pick< TabListProps, - 'onTabSelect' | 'selectTabOnFocus' | 'selectedValue' | 'shape' | 'reserveSelectedTabSpace' + 'onTabSelect' | 'selectTabOnFocus' | 'selectedValue' | 'reserveSelectedTabSpace' > & Required> & { /** A callback to allow a tab to register itself with the tab list. */ diff --git a/packages/react-components/react-tabs/library/src/components/TabList/TabListContext.ts b/packages/react-components/react-tabs/library/src/components/TabList/TabListContext.ts index d096b67560aaf..c183b8e6963f4 100644 --- a/packages/react-components/react-tabs/library/src/components/TabList/TabListContext.ts +++ b/packages/react-components/react-tabs/library/src/components/TabList/TabListContext.ts @@ -22,7 +22,6 @@ const tabListContextDefaultValue: TabListContextValue = { registeredTabs: {}, }; }, - shape: undefined, size: 'medium', vertical: false, }; diff --git a/packages/react-components/react-tabs/library/src/components/TabList/useTabListStyles.styles.ts b/packages/react-components/react-tabs/library/src/components/TabList/useTabListStyles.styles.ts index 275b48a06dd74..2df05038969a1 100644 --- a/packages/react-components/react-tabs/library/src/components/TabList/useTabListStyles.styles.ts +++ b/packages/react-components/react-tabs/library/src/components/TabList/useTabListStyles.styles.ts @@ -40,15 +40,17 @@ const useStyles = makeStyles({ export const useTabListStyles_unstable = (state: TabListState): TabListState => { 'use no memo'; - const { vertical, shape, size } = state; + const { appearance, vertical, size } = state; const styles = useStyles(); + const isRounded = appearance === 'subtle-rounded' || appearance === 'filled-rounded'; + state.root.className = mergeClasses( tabListClassNames.root, styles.root, vertical ? styles.vertical : styles.horizontal, - shape === 'circular' && (size === 'small' ? styles.circularSmall : styles.circular), + isRounded && (size === 'small' ? styles.circularSmall : styles.circular), state.root.className, ); diff --git a/packages/react-components/react-tabs/stories/src/Tabs/TabListAppearance.stories.tsx b/packages/react-components/react-tabs/stories/src/Tabs/TabListAppearance.stories.tsx index 1cda26d74ce74..bd2bfc74a6abf 100644 --- a/packages/react-components/react-tabs/stories/src/Tabs/TabListAppearance.stories.tsx +++ b/packages/react-components/react-tabs/stories/src/Tabs/TabListAppearance.stories.tsx @@ -19,7 +19,9 @@ export const Appearance = () => { return ( <> First Tab - Second Tab + + Second Tab + Third Tab Fourth Tab @@ -34,6 +36,12 @@ export const Appearance = () => { {renderTabs()} + + {renderTabs()} + + + {renderTabs()} + ); }; @@ -41,7 +49,8 @@ export const Appearance = () => { Appearance.parameters = { docs: { description: { - story: 'A tab list can have a `transparent` or `subtle` appearance. The default is `transparent`.', + story: + 'A tab list can have a `transparent`, `subtle`, `subtle-rounded` and `filled-rounded` appearance. The default is `transparent`.', }, }, }; diff --git a/packages/react-components/react-tabs/stories/src/Tabs/TabListCircular.stories.tsx b/packages/react-components/react-tabs/stories/src/Tabs/TabListCircular.stories.tsx deleted file mode 100644 index 326e2f6ce9995..0000000000000 --- a/packages/react-components/react-tabs/stories/src/Tabs/TabListCircular.stories.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import * as React from 'react'; -import { makeStyles, Tab, TabList } from '@fluentui/react-components'; - -const useStyles = makeStyles({ - root: { - alignItems: 'flex-start', - display: 'flex', - flexDirection: 'column', - justifyContent: 'flex-start', - padding: '50px 20px', - rowGap: '20px', - }, -}); - -export const Circular = () => { - const styles = useStyles(); - - const renderTabs = () => { - return ( - <> - First Tab - - Second Tab - - Third Tab - Fourth Tab - - ); - }; - - return ( -
- - {renderTabs()} - - - {renderTabs()} - -
- ); -}; - -Circular.parameters = { - docs: { - description: { - story: - 'A tab list can be `circular` appearance to make tabs look like pills. shape. Could be used only with `filled` or `subtle` appearance.', - }, - }, -}; diff --git a/packages/react-components/react-tabs/stories/src/Tabs/index.stories.tsx b/packages/react-components/react-tabs/stories/src/Tabs/index.stories.tsx index d0fa3f6214d2b..22ead95180811 100644 --- a/packages/react-components/react-tabs/stories/src/Tabs/index.stories.tsx +++ b/packages/react-components/react-tabs/stories/src/Tabs/index.stories.tsx @@ -7,7 +7,6 @@ export { Default } from './TabListDefault.stories'; export { Horizontal } from './TabListHorizontal.stories'; export { Vertical } from './TabListVertical.stories'; export { Appearance } from './TabListAppearance.stories'; -export { Circular } from './TabListCircular.stories'; export { Disabled } from './TabListDisabled.stories'; export { SizeSmall } from './TabListSizeSmall.stories'; export { SizeMedium } from './TabListSizeMedium.stories';