Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🕵🏾‍♀️ visual changes to review in the Visual Change Report

vr-tests-react-components/Avatar Converged 1 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/Avatar Converged.badgeMask.normal.chromium.png 5 Changed
vr-tests-react-components/Charts-DonutChart 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/Charts-DonutChart.Dynamic - RTL.default.chromium.png 33448 Changed
vr-tests-react-components/Charts-DonutChart.Dynamic.default.chromium.png 33367 Changed
vr-tests-react-components/Positioning 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/Positioning.Positioning end.chromium.png 966 Changed
vr-tests-react-components/Positioning.Positioning end.updated 2 times.chromium.png 740 Changed
vr-tests-react-components/ProgressBar converged 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness - Dark Mode.default.chromium.png 43 Changed
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness - High Contrast.default.chromium.png 61 Changed
vr-tests-react-components/TagPicker 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/TagPicker.disabled - RTL.disabled input hover.chromium.png 635 Changed
vr-tests-react-components/TagPicker.disabled - Dark Mode.chromium.png 658 Changed

There were 2 duplicate changes discarded. Check the build logs for more information.

"type": "minor",
"comment": "feat: add optional `icon` slot to Label, rendered before the label content",
"packageName": "@fluentui/react-label",
"email": "egianoglio@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export type LabelProps = Omit<ComponentProps<LabelSlots>, 'required'> & {
export type LabelSlots = {
root: Slot<'label'>;
required?: Slot<'span'>;
icon?: Slot<'span'>;
};

// @public
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ describe('Label', () => {
testOptions: {
'has-static-classnames': [
{
props: { required: 'Required Test' },
props: { required: 'Required Test', icon: 'Icon Test' },
},
],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ export type LabelProps = Omit<ComponentProps<LabelSlots>, 'required'> & {
export type LabelSlots = {
root: Slot<'label'>;
required?: Slot<'span'>;

/**
* Optional icon rendered alongside the label text, before the label content.
*/
icon?: Slot<'span'>;
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const renderLabel_unstable = (state: LabelBaseState): JSXElement => {

return (
<state.root>
{state.icon && <state.icon />}
{state.root.children}
{state.required && <state.required />}
</state.root>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,15 @@ export const useLabel_unstable = (props: LabelProps, ref: React.Ref<HTMLElement>
* @param ref - reference to root HTMLElement of Label
*/
export const useLabelBase_unstable = (props: LabelBaseProps, ref: React.Ref<HTMLLabelElement>): LabelBaseState => {
const { disabled = false, required = false, ...rest } = props;
const { disabled = false, required = false, icon, ...rest } = props;
return {
disabled,
required: slot.optional(required === true ? '*' : required || undefined, {
defaultProps: { 'aria-hidden': 'true' },
elementType: 'span',
}),
components: { root: 'label', required: 'span' },
icon: slot.optional(icon, { elementType: 'span' }),
components: { root: 'label', required: 'span', icon: 'span' },
root: slot.always(
getIntrinsicElementProps('label', {
ref: ref as React.Ref<HTMLLabelElement>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { SlotClassNames } from '@fluentui/react-utilities';
export const labelClassNames: SlotClassNames<LabelSlots> = {
root: 'fui-Label',
required: 'fui-Label__required',
icon: 'fui-Label__icon',
};

/**
Expand All @@ -31,6 +32,11 @@ const useStyles = makeStyles({
paddingLeft: tokens.spacingHorizontalXS,
},

withIcon: {
display: 'inline-flex',
alignItems: 'center',
},

small: {
fontSize: tokens.fontSizeBase200,
lineHeight: tokens.lineHeightBase200,
Expand All @@ -52,18 +58,60 @@ const useStyles = makeStyles({
},
});

/**
* Styles for the icon slot
*/
const useIconStyles = makeStyles({
base: {
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
borderRadius: tokens.borderRadiusMedium,
backgroundColor: tokens.colorNeutralBackground3,
color: tokens.colorNeutralForeground3,
marginRight: tokens.spacingHorizontalXS,
},

small: {
fontSize: tokens.fontSizeBase200,
height: tokens.fontSizeBase500,
width: tokens.fontSizeBase500,
},

smallSemibold: {
height: tokens.fontSizeBase400,
width: tokens.fontSizeBase400,
},

medium: {
fontSize: tokens.fontSizeBase400,
height: tokens.fontSizeBase500,
width: tokens.fontSizeBase500,
},

large: {
fontSize: tokens.fontSizeBase500,
height: tokens.fontSizeBase600,
width: tokens.fontSizeBase600,
borderRadius: tokens.borderRadiusLarge,
marginRight: tokens.spacingHorizontalSNudge,
},
});

/**
* Apply styling to the Label slots based on the state
*/
export const useLabelStyles_unstable = (state: LabelState): LabelState => {
const styles = useStyles();
const iconStyles = useIconStyles();
// eslint-disable-next-line react-hooks/immutability
state.root.className = mergeClasses(
labelClassNames.root,
styles.root,
state.disabled && styles.disabled,
styles[state.size],
state.weight === 'semibold' && styles.semibold,
state.icon && styles.withIcon,
state.root.className,
);

Expand All @@ -77,5 +125,16 @@ export const useLabelStyles_unstable = (state: LabelState): LabelState => {
);
}

if (state.icon) {
// eslint-disable-next-line react-hooks/immutability
state.icon.className = mergeClasses(
labelClassNames.icon,
iconStyles.base,
iconStyles[state.size],
state.size === 'small' && state.weight === 'semibold' && iconStyles.smallSemibold,
state.icon.className,
);
}

return state;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as React from 'react';
import type { JSXElement } from '@fluentui/react-components';
import { Label } from '@fluentui/react-components';
import { InfoRegular } from '@fluentui/react-icons';

export const Icon = (): JSXElement => {
return (
<Label icon={<InfoRegular />} required>
Label with icon
</Label>
);
};

Icon.parameters = {
docs: {
description: {
story: 'A Label can render an optional `icon` slot before its content.',
},
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export { Size } from './LabelSize.stories';
export { Weight } from './LabelWeight.stories';
export { Disabled } from './LabelDisabled.stories';
export { Required } from './LabelRequired.stories';
export { Icon } from './LabelIcon.stories';

const meta = {
title: 'Components/Label',
Expand Down
Loading