From c2f6df1d1a0d6084ba28ab6e5aae4daab992bce9 Mon Sep 17 00:00:00 2001 From: Alexander Katrukhin Date: Fri, 5 Dec 2025 12:22:08 -0500 Subject: [PATCH 1/4] Generated files for Label component --- .../src/components/Label/Label.styles.ts | 17 ++++++ .../src/theme/CAPThemeProvider.tsx | 3 ++ .../components/Label/Label.stories.tsx | 54 +++++++++++++++++++ .../react-cap-theme/stories/index.stories.tsx | 1 + 4 files changed, 75 insertions(+) create mode 100644 packages/react-cap-theme/src/components/Label/Label.styles.ts create mode 100644 packages/react-cap-theme/stories/components/Label/Label.stories.tsx diff --git a/packages/react-cap-theme/src/components/Label/Label.styles.ts b/packages/react-cap-theme/src/components/Label/Label.styles.ts new file mode 100644 index 00000000..2ef9386e --- /dev/null +++ b/packages/react-cap-theme/src/components/Label/Label.styles.ts @@ -0,0 +1,17 @@ +import { + makeStyles, + mergeClasses, + type LabelState, +} from '@fluentui/react-components'; + +export const useLabelStyles = makeStyles({ + root: {}, +}); + +export function useLabelStylesHook(state: LabelState): LabelState { + const styles = useLabelStyles(); + + state.root.className = mergeClasses(state.root.className, styles.root); + + return state; +} diff --git a/packages/react-cap-theme/src/theme/CAPThemeProvider.tsx b/packages/react-cap-theme/src/theme/CAPThemeProvider.tsx index b708a23e..a413c52f 100644 --- a/packages/react-cap-theme/src/theme/CAPThemeProvider.tsx +++ b/packages/react-cap-theme/src/theme/CAPThemeProvider.tsx @@ -5,6 +5,7 @@ import { CardFooterState, CardHeaderState, CardState, + LabelState, FluentProvider, FluentProviderProps, InputState, @@ -18,6 +19,7 @@ import { useInputStylesHook } from '../components/Input/Input.styles'; import { useCardStylesHook } from '../components/Card/Card.styles'; import { useCardHeaderStylesHook } from '../components/Card/CardHeader.styles'; import { useCardFooterStylesHook } from '../components/Card/CardFooter.styles'; +import { useLabelStylesHook } from '../components/Label/Label.styles'; const customStyleHooks: NonNullable< FluentProviderProps['customStyleHooks_unstable'] @@ -31,6 +33,7 @@ const customStyleHooks: NonNullable< useCardFooterStyles_unstable: (state) => useCardFooterStylesHook(state as CardFooterState), useInputStyles_unstable: (state) => useInputStylesHook(state as InputState), + useLabelStyles_unstable: (state) => useLabelStylesHook(state as LabelState), }; type CAPThemeProviderProps = Omit< diff --git a/packages/react-cap-theme/stories/components/Label/Label.stories.tsx b/packages/react-cap-theme/stories/components/Label/Label.stories.tsx new file mode 100644 index 00000000..9541c53d --- /dev/null +++ b/packages/react-cap-theme/stories/components/Label/Label.stories.tsx @@ -0,0 +1,54 @@ +import * as React from 'react'; +import { Label, type LabelProps } from '@fluentui/react-components'; +import { CAPThemeExamplesTable } from '../../StorybookUtils'; + +export const CAPLabelStory = (props: LabelProps) => { + return ( + Label; + }, + }, + { + title: 'Required', + render() { + return ( + + ); + }, + }, + { + title: 'Disabled', + render() { + return ( + + ); + }, + }, + ]} + /> + ); +}; + +CAPLabelStory.argTypes = { + size: { + options: ['small', 'medium', 'large'], + control: { type: 'radio' }, + }, + weight: { + options: ['regular', 'semibold'], + control: { type: 'radio' }, + }, +}; + +CAPLabelStory.args = { + size: 'medium', + weight: 'regular', +}; diff --git a/packages/react-cap-theme/stories/index.stories.tsx b/packages/react-cap-theme/stories/index.stories.tsx index d4a7829d..64be10ae 100644 --- a/packages/react-cap-theme/stories/index.stories.tsx +++ b/packages/react-cap-theme/stories/index.stories.tsx @@ -4,6 +4,7 @@ import { Meta } from '@storybook/react'; export { CAPBadgeStory as Badge } from './components/Badge.stories'; export { CAPButtonStory as Button } from './components/Button.stories'; export { CAPCardStory as Card } from './components/Card.stories'; +export { CAPLabelStory as Label } from './components/Label/Label.stories'; export { CAPInputStory as Input } from './components/Input.stories'; export { CAPMenuStory as Menu } from './components/Menu.stories'; export { CAPTooltipStory as Tooltip } from './components/Tooltip.stories'; From 5f1f03e52346809aee721f0a8a19c8f95fa1403f Mon Sep 17 00:00:00 2001 From: Alexander Katrukhin Date: Fri, 5 Dec 2025 12:30:22 -0500 Subject: [PATCH 2/4] Change files --- ...act-cap-theme-2b7ce8df-bcb4-4c18-8494-634da4010923.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@fluentui-contrib-react-cap-theme-2b7ce8df-bcb4-4c18-8494-634da4010923.json diff --git a/change/@fluentui-contrib-react-cap-theme-2b7ce8df-bcb4-4c18-8494-634da4010923.json b/change/@fluentui-contrib-react-cap-theme-2b7ce8df-bcb4-4c18-8494-634da4010923.json new file mode 100644 index 00000000..5f779f81 --- /dev/null +++ b/change/@fluentui-contrib-react-cap-theme-2b7ce8df-bcb4-4c18-8494-634da4010923.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "Generated files for Label component", + "packageName": "@fluentui-contrib/react-cap-theme", + "email": "olkatruk@microsoft.com", + "dependentChangeType": "patch" +} From bdc21844125abe93567bf3ec00855ecc8f710690 Mon Sep 17 00:00:00 2001 From: Alexander Katrukhin Date: Fri, 5 Dec 2025 16:54:06 -0500 Subject: [PATCH 3/4] feat: Enhance Label component with new styles and badge integration --- .../src/components/Badge/Badge.styles.ts | 4 +- .../src/components/Label/Label.styles.ts | 17 ++++- .../src/components/Label/Label.tsx | 39 +++++++++++ .../src/components/Label/Label.types.ts | 19 ++++++ packages/react-cap-theme/src/index.ts | 2 + .../stories/components/Badge.stories.tsx | 6 ++ .../components/Label/Label.stories.tsx | 65 +++++++++++++++++-- 7 files changed, 144 insertions(+), 8 deletions(-) create mode 100644 packages/react-cap-theme/src/components/Label/Label.tsx create mode 100644 packages/react-cap-theme/src/components/Label/Label.types.ts diff --git a/packages/react-cap-theme/src/components/Badge/Badge.styles.ts b/packages/react-cap-theme/src/components/Badge/Badge.styles.ts index 09aeed2a..d4d4a1cf 100644 --- a/packages/react-cap-theme/src/components/Badge/Badge.styles.ts +++ b/packages/react-cap-theme/src/components/Badge/Badge.styles.ts @@ -121,10 +121,10 @@ export const useBadgeStyles = makeStyles({ const useBadgeIconStyles = makeStyles({ beforeTextSmall: { - marginRight: `calc(${CAP_TOKENS['cap/badge-s/gap']} + ${textPadding})`, + marginRight: `${textPadding})`, }, afterTextSmall: { - marginLeft: `calc(${CAP_TOKENS['cap/badge-s/gap-toSecondaryIcon']} + ${textPadding})`, + marginLeft: `${textPadding})`, }, }); diff --git a/packages/react-cap-theme/src/components/Label/Label.styles.ts b/packages/react-cap-theme/src/components/Label/Label.styles.ts index 2ef9386e..30e64539 100644 --- a/packages/react-cap-theme/src/components/Label/Label.styles.ts +++ b/packages/react-cap-theme/src/components/Label/Label.styles.ts @@ -1,11 +1,26 @@ import { makeStyles, mergeClasses, + tokens, type LabelState, } from '@fluentui/react-components'; export const useLabelStyles = makeStyles({ - root: {}, + root: { + display: 'inline-flex', + alignItems: 'center', + gap: tokens.spacingHorizontalXS, + }, + startSlot: { + display: 'inline-flex', + alignItems: 'center', + justifyContent: 'center', + }, + content: { + display: 'inline-flex', + alignItems: 'center', + gap: tokens.spacingHorizontalXXS, + }, }); export function useLabelStylesHook(state: LabelState): LabelState { diff --git a/packages/react-cap-theme/src/components/Label/Label.tsx b/packages/react-cap-theme/src/components/Label/Label.tsx new file mode 100644 index 00000000..5a0fa726 --- /dev/null +++ b/packages/react-cap-theme/src/components/Label/Label.tsx @@ -0,0 +1,39 @@ +import * as React from 'react'; +import type { ForwardRefComponent } from '@fluentui/react-utilities'; +import { + Badge, + Label as FluentLabel, + mergeClasses, +} from '@fluentui/react-components'; +import { CircleRegular } from '@fluentui/react-icons'; +import { useLabelStyles } from './Label.styles'; +import type { LabelProps } from './Label.types'; + +const defaultStart = ( + } /> +); + +export const Label = React.forwardRef( + ( + { badge, showBadge = false, children, className, ...rest }, + ref + ) => { + const styles = useLabelStyles(); + const badgeContent = badge ?? (showBadge ? defaultStart : null); + + return ( + + {badgeContent && ( + {badgeContent} + )} + {children} + + ); + } +) as ForwardRefComponent; + +Label.displayName = 'Label'; diff --git a/packages/react-cap-theme/src/components/Label/Label.types.ts b/packages/react-cap-theme/src/components/Label/Label.types.ts new file mode 100644 index 00000000..1bce2b2c --- /dev/null +++ b/packages/react-cap-theme/src/components/Label/Label.types.ts @@ -0,0 +1,19 @@ +import * as React from 'react'; +import type { + LabelProps as FluentLabelProps, + LabelState as FluentLabelState, +} from '@fluentui/react-components'; + +export type LabelProps = FluentLabelProps & { + /** + * Optional leading badge rendered before the label text. + */ + badge?: React.ReactNode; + /** + * Controls whether the badge is shown when `badge` is not provided. + * @default false + */ + showBadge?: boolean; +}; + +export type LabelState = FluentLabelState; diff --git a/packages/react-cap-theme/src/index.ts b/packages/react-cap-theme/src/index.ts index d4b15be2..e5039598 100644 --- a/packages/react-cap-theme/src/index.ts +++ b/packages/react-cap-theme/src/index.ts @@ -9,3 +9,5 @@ export type { ButtonProps, ButtonState, } from './components/Button/Button.types'; +export { Label } from './components/Label/Label'; +export type { LabelProps, LabelState } from './components/Label/Label.types'; diff --git a/packages/react-cap-theme/stories/components/Badge.stories.tsx b/packages/react-cap-theme/stories/components/Badge.stories.tsx index 9ae126e7..67680cf3 100644 --- a/packages/react-cap-theme/stories/components/Badge.stories.tsx +++ b/packages/react-cap-theme/stories/components/Badge.stories.tsx @@ -7,6 +7,12 @@ export const CAPBadgeStory = () => { return ( } />; + }, + }, { title: 'Tiny', render() { diff --git a/packages/react-cap-theme/stories/components/Label/Label.stories.tsx b/packages/react-cap-theme/stories/components/Label/Label.stories.tsx index 9541c53d..61cf812c 100644 --- a/packages/react-cap-theme/stories/components/Label/Label.stories.tsx +++ b/packages/react-cap-theme/stories/components/Label/Label.stories.tsx @@ -1,22 +1,53 @@ import * as React from 'react'; -import { Label, type LabelProps } from '@fluentui/react-components'; +import { Label, type LabelProps } from '@fluentui-contrib/react-cap-theme'; +import { Badge, type BadgeProps } from '@fluentui/react-components'; +import { CircleRegular } from '@fluentui/react-icons'; import { CAPThemeExamplesTable } from '../../StorybookUtils'; -export const CAPLabelStory = (props: LabelProps) => { +type LabelStoryProps = Omit & { + badgeVisible?: boolean; + badgeAppearance?: BadgeProps['appearance']; + badgeShape?: BadgeProps['shape']; + badgeSize?: BadgeProps['size']; + badgeColor?: BadgeProps['color']; +}; + +export const CAPLabelStory = ({ + badgeVisible = true, + badgeAppearance = 'filled', + badgeShape = 'rounded', + badgeSize = 'small', + badgeColor = 'informative', + ...props +}: LabelStoryProps) => { + const badgeContent = badgeVisible ? ( + } + /> + ) : undefined; + return ( Label; + return ( + + ); }, }, { title: 'Required', render() { return ( -