diff --git a/.storybook/react-icons-font.css b/.storybook/react-icons-font.css new file mode 100644 index 0000000000000..6d1fc2257066a --- /dev/null +++ b/.storybook/react-icons-font.css @@ -0,0 +1,6 @@ +/* Prevent inherited text weight from synthetically bolding font icons. + * TODO: Remove when https://github.com/microsoft/fluentui-system-icons/issues/1235 is fixed. + */ +:where([data-fui-icon]) { + font-weight: normal; +} diff --git a/.storybook/react-icons-webpack.js b/.storybook/react-icons-webpack.js new file mode 100644 index 0000000000000..5826ae517e68c --- /dev/null +++ b/.storybook/react-icons-webpack.js @@ -0,0 +1,108 @@ +// @ts-check + +const FluentUIReactIconsFontSubsettingPlugin = require('@fluentui/react-icons-font-subsetting-webpack-plugin').default; + +const FONT_ICON_VARIANT = 'fonts'; +const iconLoader = require.resolve('@fluentui/react-icons-atomic-webpack-loader'); +const headlessBaseStyles = require.resolve('@fluentui/react-icons/headless/styles.css'); +const headlessFontStyles = require.resolve('@fluentui/react-icons/headless/fonts/styles.css'); +const fontIconStyles = require.resolve('./react-icons-font.css'); + +/** @typedef {string | string[] | import('webpack').EntryObject} ResolvedEntry */ + +/** + * @param {ResolvedEntry} entry + * @param {string[]} imports + * @returns {ResolvedEntry} + */ +function prependEntryImports(entry, imports) { + if (typeof entry === 'string') { + return [...imports, entry]; + } + + if (Array.isArray(entry)) { + return [...imports, ...entry]; + } + + if (entry && typeof entry === 'object') { + return Object.fromEntries( + Object.entries(entry).map(([name, value]) => { + if (typeof value === 'string' || Array.isArray(value)) { + return [name, prependEntryImports(value, imports)]; + } + + if (value && typeof value === 'object') { + return [name, { ...value, import: prependEntryImports(value.import ?? [], imports) }]; + } + + return [name, value]; + }), + ); + } + + return entry; +} + +/** + * Configures atomic Fluent icon imports for Storybook. + * Set FLUENTUI_ICON_VARIANT=fonts to use subsetted font icons; SVG atoms are the default. + * + * @see https://github.com/microsoft/fluentui-system-icons/blob/main/packages/react-icons-atomic-webpack-loader/README.md + * @see https://github.com/microsoft/fluentui-system-icons/tree/main/packages/react-icons-font-subsetting-webpack-plugin + * + * @param {{ config: import('webpack').Configuration; headless?: boolean }} options + */ +function configureReactIcons(options) { + const { config, headless = false } = options; + const useFontIcons = process.env.FLUENTUI_ICON_VARIANT === FONT_ICON_VARIANT; + + config.module ??= {}; + config.module.rules ??= []; + config.module.rules.push({ + test: /\.[mc]?[jt]sx?$/, + enforce: 'pre', + use: [ + { + loader: iconLoader, + options: { + iconVariant: useFontIcons ? 'fonts' : 'svg', + fallbackVariant: 'svg', + headless, + }, + }, + ], + }); + + if (useFontIcons) { + config.module.rules.push({ + test: /\.(ttf|woff2?)$/, + type: 'asset', + }); + config.plugins ??= []; + config.plugins.push(new FluentUIReactIconsFontSubsettingPlugin()); + } + + const styleImports = []; + if (headless) { + styleImports.push(headlessBaseStyles); + } + if (useFontIcons) { + if (headless) { + styleImports.push(headlessFontStyles); + } + styleImports.push(fontIconStyles); + } + + if (styleImports.length > 0) { + const originalEntry = config.entry; + + config.entry = async () => { + const entry = typeof originalEntry === 'function' ? await originalEntry() : originalEntry; + return prependEntryImports(entry ?? [], styleImports); + }; + } + + return config; +} + +module.exports = { configureReactIcons }; diff --git a/apps/public-docsite-v9-headless/.storybook/main.js b/apps/public-docsite-v9-headless/.storybook/main.js index 9c140774e3c21..565aa6fc0e5e4 100644 --- a/apps/public-docsite-v9-headless/.storybook/main.js +++ b/apps/public-docsite-v9-headless/.storybook/main.js @@ -1,5 +1,6 @@ const headlessMain = require('../../../packages/react-components/react-headless-components-preview/stories/.storybook/main'); const { registerRules, rules } = require('@fluentui/scripts-storybook'); +const { configureReactIcons } = require('../../../.storybook/react-icons-webpack'); module.exports = /** @type {Omit} */ ({ ...headlessMain, @@ -18,6 +19,7 @@ module.exports = /** @type {Omit {

Tooltip variants

Tooltips for text formatting icon-only buttons

- - diff --git a/apps/vr-tests-react-components/src/stories/Dialog/Dialog.stories.tsx b/apps/vr-tests-react-components/src/stories/Dialog/Dialog.stories.tsx index bf269121e4c8d..66800b39fc09f 100644 --- a/apps/vr-tests-react-components/src/stories/Dialog/Dialog.stories.tsx +++ b/apps/vr-tests-react-components/src/stories/Dialog/Dialog.stories.tsx @@ -11,7 +11,7 @@ import { import { OverlayDrawer, DrawerBody, DrawerHeader, DrawerHeaderTitle } from '@fluentui/react-drawer'; import { Button } from '@fluentui/react-button'; import { Combobox, Option } from '@fluentui/react-combobox'; -import { Rocket24Regular, Dismiss24Regular } from '@fluentui/react-icons'; +import { RocketRegular, DismissRegular } from '@fluentui/react-icons'; import type { Meta } from '@storybook/react-webpack5'; import { getStoryVariant, DARK_MODE, HIGH_CONTRAST, RTL } from '../../utilities'; @@ -260,7 +260,7 @@ export const TitleCustomAction = () => ( - } />}> + } />}> Dialog title @@ -533,7 +533,7 @@ export const IntegrationComboboxInline = () => { export const DialogInsideDrawerDefault = () => ( - } />}> + } />}> Drawer with Dialog @@ -568,7 +568,7 @@ export const DialogInsideDrawerDefaultRTL = getStoryVariant(DialogInsideDrawerDe export const DialogInsideDrawerDimmed = () => ( - } />}> + } />}> Drawer with Dialog @@ -603,7 +603,7 @@ export const DialogInsideDrawerDimmedRTL = getStoryVariant(DialogInsideDrawerDim export const DialogInsideDrawerTransparent = () => ( - } />}> + } />}> Drawer with Dialog diff --git a/apps/vr-tests-react-components/src/stories/Drawer/Drawer.stories.tsx b/apps/vr-tests-react-components/src/stories/Drawer/Drawer.stories.tsx index 45bea6f492457..7ae7101f02eb8 100644 --- a/apps/vr-tests-react-components/src/stories/Drawer/Drawer.stories.tsx +++ b/apps/vr-tests-react-components/src/stories/Drawer/Drawer.stories.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import type { Meta } from '@storybook/react-webpack5'; +import type { OverlayDrawerProps, InlineDrawerProps } from '@fluentui/react-drawer'; import { Drawer, DrawerHeader, @@ -8,19 +9,12 @@ import { DrawerBody, DrawerHeaderNavigation, OverlayDrawer, - OverlayDrawerProps, InlineDrawer, - InlineDrawerProps, DrawerFooter, } from '@fluentui/react-drawer'; import { Toolbar, ToolbarButton, ToolbarGroup } from '@fluentui/react-toolbar'; import { Button } from '@fluentui/react-button'; -import { - ArrowLeft24Regular, - ArrowClockwise24Regular, - Settings24Regular, - Dismiss24Regular, -} from '@fluentui/react-icons'; +import { ArrowLeftRegular, ArrowClockwiseRegular, SettingsRegular, DismissRegular } from '@fluentui/react-icons'; import { getStoryVariant, DARK_MODE, HIGH_CONTRAST, RTL } from '../../utilities'; @@ -32,12 +26,12 @@ const ExampleDrawerHeader = () => ( - } /> + } /> - } /> - } /> - } /> + } /> + } /> + } /> diff --git a/apps/vr-tests-react-components/src/stories/Field.stories.tsx b/apps/vr-tests-react-components/src/stories/Field.stories.tsx index 20554f0007014..f58a8288a11ea 100644 --- a/apps/vr-tests-react-components/src/stories/Field.stories.tsx +++ b/apps/vr-tests-react-components/src/stories/Field.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Checkbox } from '@fluentui/react-checkbox'; import { Combobox, Dropdown } from '@fluentui/react-combobox'; import { Field } from '@fluentui/react-field'; -import { Dismiss12Filled } from '@fluentui/react-icons'; +import { DismissFilled } from '@fluentui/react-icons'; import { Input } from '@fluentui/react-input'; import { ProgressBar } from '@fluentui/react-progress'; import { Radio, RadioGroup } from '@fluentui/react-radio'; @@ -98,7 +98,7 @@ export const ValidationMessageIcon = () => ( } + validationMessageIcon={} > diff --git a/apps/vr-tests-react-components/src/stories/Toolbar/Toolbar.stories.tsx b/apps/vr-tests-react-components/src/stories/Toolbar/Toolbar.stories.tsx index 2268efe28acf1..bf32f634e7dea 100644 --- a/apps/vr-tests-react-components/src/stories/Toolbar/Toolbar.stories.tsx +++ b/apps/vr-tests-react-components/src/stories/Toolbar/Toolbar.stories.tsx @@ -1,19 +1,13 @@ import * as React from 'react'; import { Steps, type StoryParameters } from 'storywright'; import { makeStyles } from '@griffel/react'; +import type { ToolbarProps } from '@fluentui/react-toolbar'; +import { Toolbar, ToolbarButton, ToolbarDivider, ToolbarToggleButton, ToolbarGroup } from '@fluentui/react-toolbar'; import { - Toolbar, - ToolbarProps, - ToolbarButton, - ToolbarDivider, - ToolbarToggleButton, - ToolbarGroup, -} from '@fluentui/react-toolbar'; -import { - TextBold24Regular, - TextItalic24Regular, - TextUnderline24Regular, - AlertSnooze24Regular, + TextBoldRegular, + TextItalicRegular, + TextUnderlineRegular, + AlertSnoozeRegular, FontIncreaseRegular, FontDecreaseRegular, TextFontRegular, @@ -45,16 +39,16 @@ export default { export const Default = (props: Partial) => ( - } /> - } /> - } /> + } /> + } /> + } /> } + icon={} /> ); @@ -65,10 +59,10 @@ export const Transparent = (props: Partial) => ( id="bold-button" aria-label="Text option - Bold" appearance="transparent" - icon={} + icon={} /> - } /> - } /> + } /> + } /> ) => ( name="toggle" value="toggle" appearance="transparent" - icon={} + icon={} /> ); export const Vertical = (props: Partial) => ( - } /> - } /> - } /> + } /> + } /> + } /> } + icon={} /> ); export const Small = (props: Partial) => ( - } /> - } /> - } /> + } /> + } /> + } /> } + icon={} /> ); export const Large = (props: Partial) => ( - } /> - } /> - } /> + } /> + } /> + } /> } + icon={} /> ); @@ -136,7 +130,7 @@ export const VerticalButton = (props: Partial) => ( id="bold-button" aria-label="Text option - Bold" appearance="primary" - icon={} + icon={} /> }> Increase @@ -165,7 +159,7 @@ export const FarGroup = (props: Partial) => { id="bold-button" aria-label="Text option - Bold" appearance="primary" - icon={} + icon={} /> } /> } /> @@ -183,7 +177,7 @@ export const FarGroup = (props: Partial) => { aria-label="Snooze Alert Option" name="toggle" value="toggle" - icon={} + icon={} /> diff --git a/apps/vr-tests-react-components/src/stories/Tree.stories.tsx b/apps/vr-tests-react-components/src/stories/Tree.stories.tsx index 991c3d69eee40..cad91f792e73a 100644 --- a/apps/vr-tests-react-components/src/stories/Tree.stories.tsx +++ b/apps/vr-tests-react-components/src/stories/Tree.stories.tsx @@ -1,14 +1,16 @@ import * as React from 'react'; +import type { + HeadlessFlatTreeItemProps, + TreeItemValue, + TreeOpenChangeData, + TreeOpenChangeEvent, +} from '@fluentui/react-tree'; import { FlatTree, - HeadlessFlatTreeItemProps, Tree, TreeItem, TreeItemLayout, TreeItemPersonaLayout, - TreeItemValue, - TreeOpenChangeData, - TreeOpenChangeEvent, useHeadlessFlatTree_unstable, } from '@fluentui/react-tree'; import { tokens } from '@fluentui/react-theme'; @@ -20,12 +22,12 @@ import { Steps, type StoryParameters } from 'storywright'; import { CaretDownRegular, CaretRightRegular, - Edit20Regular, - Image20Regular, - Important16Regular, - LockClosed20Regular, - MoreHorizontal20Regular, - SquareMultiple20Regular, + EditRegular, + ImageRegular, + ImportantRegular, + LockClosedRegular, + MoreHorizontalRegular, + SquareMultipleRegular, } from '@fluentui/react-icons'; import { CounterBadge } from '@fluentui/react-badge'; import { makeStyles, shorthands } from '@griffel/react'; @@ -360,10 +362,10 @@ export const ExpandIconRTL = getStoryVariant(ExpandIcon, RTL); const ActionsExample = () => { return ( <> - - + + diff --git a/packages/react-components/react-card/stories/src/Card/CardAction.stories.tsx b/packages/react-components/react-card/stories/src/Card/CardAction.stories.tsx index c48beaf5cc3e8..c6036b65ae383 100644 --- a/packages/react-components/react-card/stories/src/Card/CardAction.stories.tsx +++ b/packages/react-components/react-card/stories/src/Card/CardAction.stories.tsx @@ -14,7 +14,7 @@ import { Text, tokens, } from '@fluentui/react-components'; -import { MoreHorizontal20Regular, Open16Regular } from '@fluentui/react-icons'; +import { MoreHorizontalRegular, OpenRegular } from '@fluentui/react-icons'; const resolveAsset = (asset: string) => { const ASSET_URL = @@ -118,7 +118,7 @@ export const WithAction = (): JSXElement => { } description={Developer} - action={ @@ -154,7 +154,7 @@ export const WithAction = (): JSXElement => { } description={Developer} - action={ - + ); diff --git a/packages/react-components/react-card/stories/src/Card/CardOrientation.stories.tsx b/packages/react-components/react-card/stories/src/Card/CardOrientation.stories.tsx index ba5db2b27a1df..6bb447a64d3f9 100644 --- a/packages/react-components/react-card/stories/src/Card/CardOrientation.stories.tsx +++ b/packages/react-components/react-card/stories/src/Card/CardOrientation.stories.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import type { JSXElement } from '@fluentui/react-components'; import { makeStyles, Button, Caption1, Text, tokens, Subtitle1 } from '@fluentui/react-components'; -import { MoreHorizontal20Regular } from '@fluentui/react-icons'; +import { MoreHorizontalRegular } from '@fluentui/react-icons'; import { Card, CardHeader, CardPreview } from '@fluentui/react-components'; const resolveAsset = (asset: string) => { @@ -77,7 +77,7 @@ export const Orientation = (): JSXElement => { } description={Developer} - action={ - + + ); }; diff --git a/packages/react-components/react-card/stories/src/CardHeader/CardHeaderDefault.stories.tsx b/packages/react-components/react-card/stories/src/CardHeader/CardHeaderDefault.stories.tsx index 2beef43801ea7..830fc5b1fa574 100644 --- a/packages/react-components/react-card/stories/src/CardHeader/CardHeaderDefault.stories.tsx +++ b/packages/react-components/react-card/stories/src/CardHeader/CardHeaderDefault.stories.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import type { JSXElement } from '@fluentui/react-components'; import { CardHeader } from '@fluentui/react-components'; import { makeStyles, Button, Body1, Caption1 } from '@fluentui/react-components'; -import { MoreHorizontal20Regular } from '@fluentui/react-icons'; +import { MoreHorizontalRegular } from '@fluentui/react-icons'; const useStyles = makeStyles({ container: { @@ -39,7 +39,7 @@ export const Default = (): JSXElement => { } description={Developer} - action={ ), diff --git a/packages/react-components/react-dialog/stories/src/Dialog/DialogBackdropAppearance.stories.tsx b/packages/react-components/react-dialog/stories/src/Dialog/DialogBackdropAppearance.stories.tsx index 85b9ec74ba823..b3a2492463048 100644 --- a/packages/react-components/react-dialog/stories/src/Dialog/DialogBackdropAppearance.stories.tsx +++ b/packages/react-components/react-dialog/stories/src/Dialog/DialogBackdropAppearance.stories.tsx @@ -21,7 +21,7 @@ import { makeStyles, useRestoreFocusTarget, } from '@fluentui/react-components'; -import { Dismiss24Regular } from '@fluentui/react-icons'; +import { DismissRegular } from '@fluentui/react-icons'; import story from './DialogBackdropAppearance.md'; const useStyles = makeStyles({ @@ -57,7 +57,7 @@ export const BackdropAppearance = (): JSXElement => {