From 31a2d7fff00d3f478624abde0b26293bad9415c0 Mon Sep 17 00:00:00 2001 From: David Zukowski Date: Tue, 11 Nov 2025 11:16:58 -0800 Subject: [PATCH 01/14] scaffold visual refresh storybook for CAP theme --- .github/CODEOWNERS | 4 + .../src/components/Button/Button.types.ts | 2 +- .../Button/useButtonStyles.styles.ts | 3 +- .../library/.babelrc.json | 4 + .../library/.eslintrc.json | 4 + .../library/.swcrc | 30 ++++++++ .../library/LICENSE | 15 ++++ .../library/README.md | 5 ++ .../library/config/api-extractor.json | 5 ++ .../library/config/tests.js | 1 + .../library/docs/Spec.md | 63 ++++++++++++++++ .../etc/react-visual-refresh-preview.api.md | 0 .../library/jest.config.js | 34 +++++++++ .../library/package.json | 56 ++++++++++++++ .../library/project.json | 8 ++ .../library/src/CAPTheme.tsx | 66 +++++++++++++++++ .../library/src/CAPThemeProvider.tsx | 43 +++++++++++ .../library/src/components/CAPButton.tsx | 45 +++++++++++ .../library/src/index.ts | 2 + .../library/src/testing/isConformant.ts | 15 ++++ .../library/tsconfig.json | 22 ++++++ .../library/tsconfig.lib.json | 22 ++++++ .../library/tsconfig.spec.json | 17 +++++ .../stories/.eslintrc.json | 12 +++ .../stories/.storybook/main.js | 14 ++++ .../stories/.storybook/preview.js | 9 +++ .../stories/.storybook/tsconfig.json | 10 +++ .../stories/README.md | 17 +++++ .../stories/package.json | 11 +++ .../stories/project.json | 8 ++ .../stories/src/CAPBadge.stories.tsx | 18 +++++ .../stories/src/CAPButton.stories.tsx | 74 +++++++++++++++++++ .../stories/src/CAPCard.stories.tsx | 6 ++ .../stories/src/CAPInput.stories.tsx | 6 ++ .../stories/src/CAPMenu.stories.tsx | 6 ++ .../stories/src/CAPTooltip.stories.tsx | 6 ++ .../stories/src/StorybookUtil.tsx | 70 ++++++++++++++++++ .../stories/src/index.stories.tsx | 17 +++++ .../stories/tsconfig.json | 22 ++++++ .../stories/tsconfig.lib.json | 10 +++ tsconfig.base.all.json | 12 ++- tsconfig.base.json | 10 +++ 42 files changed, 801 insertions(+), 3 deletions(-) create mode 100644 packages/react-components/react-visual-refresh-preview/library/.babelrc.json create mode 100644 packages/react-components/react-visual-refresh-preview/library/.eslintrc.json create mode 100644 packages/react-components/react-visual-refresh-preview/library/.swcrc create mode 100644 packages/react-components/react-visual-refresh-preview/library/LICENSE create mode 100644 packages/react-components/react-visual-refresh-preview/library/README.md create mode 100644 packages/react-components/react-visual-refresh-preview/library/config/api-extractor.json create mode 100644 packages/react-components/react-visual-refresh-preview/library/config/tests.js create mode 100644 packages/react-components/react-visual-refresh-preview/library/docs/Spec.md create mode 100644 packages/react-components/react-visual-refresh-preview/library/etc/react-visual-refresh-preview.api.md create mode 100644 packages/react-components/react-visual-refresh-preview/library/jest.config.js create mode 100644 packages/react-components/react-visual-refresh-preview/library/package.json create mode 100644 packages/react-components/react-visual-refresh-preview/library/project.json create mode 100644 packages/react-components/react-visual-refresh-preview/library/src/CAPTheme.tsx create mode 100644 packages/react-components/react-visual-refresh-preview/library/src/CAPThemeProvider.tsx create mode 100644 packages/react-components/react-visual-refresh-preview/library/src/components/CAPButton.tsx create mode 100644 packages/react-components/react-visual-refresh-preview/library/src/index.ts create mode 100644 packages/react-components/react-visual-refresh-preview/library/src/testing/isConformant.ts create mode 100644 packages/react-components/react-visual-refresh-preview/library/tsconfig.json create mode 100644 packages/react-components/react-visual-refresh-preview/library/tsconfig.lib.json create mode 100644 packages/react-components/react-visual-refresh-preview/library/tsconfig.spec.json create mode 100644 packages/react-components/react-visual-refresh-preview/stories/.eslintrc.json create mode 100644 packages/react-components/react-visual-refresh-preview/stories/.storybook/main.js create mode 100644 packages/react-components/react-visual-refresh-preview/stories/.storybook/preview.js create mode 100644 packages/react-components/react-visual-refresh-preview/stories/.storybook/tsconfig.json create mode 100644 packages/react-components/react-visual-refresh-preview/stories/README.md create mode 100644 packages/react-components/react-visual-refresh-preview/stories/package.json create mode 100644 packages/react-components/react-visual-refresh-preview/stories/project.json create mode 100644 packages/react-components/react-visual-refresh-preview/stories/src/CAPBadge.stories.tsx create mode 100644 packages/react-components/react-visual-refresh-preview/stories/src/CAPButton.stories.tsx create mode 100644 packages/react-components/react-visual-refresh-preview/stories/src/CAPCard.stories.tsx create mode 100644 packages/react-components/react-visual-refresh-preview/stories/src/CAPInput.stories.tsx create mode 100644 packages/react-components/react-visual-refresh-preview/stories/src/CAPMenu.stories.tsx create mode 100644 packages/react-components/react-visual-refresh-preview/stories/src/CAPTooltip.stories.tsx create mode 100644 packages/react-components/react-visual-refresh-preview/stories/src/StorybookUtil.tsx create mode 100644 packages/react-components/react-visual-refresh-preview/stories/src/index.stories.tsx create mode 100644 packages/react-components/react-visual-refresh-preview/stories/tsconfig.json create mode 100644 packages/react-components/react-visual-refresh-preview/stories/tsconfig.lib.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0b47f04896ae2f..3f8e0cdbc10504 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -331,6 +331,10 @@ packages/react-components/component-selector-preview/library @microsoft/teams-pr packages/react-components/component-selector-preview/stories @microsoft/teams-prg packages/react-components/react-menu-grid-preview/library @microsoft/teams-prg packages/react-components/react-menu-grid-preview/stories @microsoft/teams-prg +packages/react-components/visual-refresh-preview/library @microsoft/teams-prg +packages/react-components/visual-refresh-preview/stories @microsoft/teams-prg +packages/react-components/react-visual-refresh-preview/library @microsoft/teams-prg +packages/react-components/react-visual-refresh-preview/stories @microsoft/teams-prg # <%= NX-CODEOWNER-PLACEHOLDER %> # Deprecated v9 packages - exposed as part of `/unstable` api diff --git a/packages/react-components/react-button/library/src/components/Button/Button.types.ts b/packages/react-components/react-button/library/src/components/Button/Button.types.ts index 201f264913be06..f28686ea5f6dc4 100644 --- a/packages/react-components/react-button/library/src/components/Button/Button.types.ts +++ b/packages/react-components/react-button/library/src/components/Button/Button.types.ts @@ -29,7 +29,7 @@ export type ButtonProps = ComponentProps & { * * @default 'secondary' */ - appearance?: 'secondary' | 'primary' | 'outline' | 'subtle' | 'transparent'; + appearance?: 'secondary' | 'primary' | 'outline' | 'subtle' | 'transparent' | 'tint'; /** * When set, allows the button to be focusable even when it has been disabled. This is used in scenarios where it diff --git a/packages/react-components/react-button/library/src/components/Button/useButtonStyles.styles.ts b/packages/react-components/react-button/library/src/components/Button/useButtonStyles.styles.ts index 186d0f7a86d58e..1221c9f082615b 100644 --- a/packages/react-components/react-button/library/src/components/Button/useButtonStyles.styles.ts +++ b/packages/react-components/react-button/library/src/components/Button/useButtonStyles.styles.ts @@ -562,7 +562,8 @@ export const useButtonStyles_unstable = (state: ButtonState): ButtonState => { const rootIconOnlyStyles = useRootIconOnlyStyles(); const iconStyles = useIconStyles(); - const { appearance, disabled, disabledFocusable, icon, iconOnly, iconPosition, shape, size } = state; + const { disabled, disabledFocusable, icon, iconOnly, iconPosition, shape, size } = state; + const appearance = state.appearance === 'tint' ? 'secondary' : state.appearance; state.root.className = mergeClasses( buttonClassNames.root, diff --git a/packages/react-components/react-visual-refresh-preview/library/.babelrc.json b/packages/react-components/react-visual-refresh-preview/library/.babelrc.json new file mode 100644 index 00000000000000..630deaf765c49f --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/library/.babelrc.json @@ -0,0 +1,4 @@ +{ + "extends": "../../../../.babelrc-v9.json", + "plugins": ["annotate-pure-calls", "@babel/transform-react-pure-annotations"] +} diff --git a/packages/react-components/react-visual-refresh-preview/library/.eslintrc.json b/packages/react-components/react-visual-refresh-preview/library/.eslintrc.json new file mode 100644 index 00000000000000..ceea884c70dccc --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/library/.eslintrc.json @@ -0,0 +1,4 @@ +{ + "extends": ["plugin:@fluentui/eslint-plugin/react"], + "root": true +} diff --git a/packages/react-components/react-visual-refresh-preview/library/.swcrc b/packages/react-components/react-visual-refresh-preview/library/.swcrc new file mode 100644 index 00000000000000..b4ffa86dee3067 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/library/.swcrc @@ -0,0 +1,30 @@ +{ + "$schema": "https://json.schemastore.org/swcrc", + "exclude": [ + "/testing", + "/**/*.cy.ts", + "/**/*.cy.tsx", + "/**/*.spec.ts", + "/**/*.spec.tsx", + "/**/*.test.ts", + "/**/*.test.tsx" + ], + "jsc": { + "parser": { + "syntax": "typescript", + "tsx": true, + "decorators": false, + "dynamicImport": false + }, + "externalHelpers": true, + "transform": { + "react": { + "runtime": "classic", + "useSpread": true + } + }, + "target": "es2019" + }, + "minify": false, + "sourceMaps": true +} diff --git a/packages/react-components/react-visual-refresh-preview/library/LICENSE b/packages/react-components/react-visual-refresh-preview/library/LICENSE new file mode 100644 index 00000000000000..a7976671d10b9d --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/library/LICENSE @@ -0,0 +1,15 @@ +@fluentui/react-visual-refresh-preview + +Copyright (c) Microsoft Corporation + +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Note: Usage of the fonts and icons referenced in Fluent UI React is subject to the terms listed at https://aka.ms/fluentui-assets-license diff --git a/packages/react-components/react-visual-refresh-preview/library/README.md b/packages/react-components/react-visual-refresh-preview/library/README.md new file mode 100644 index 00000000000000..384e11d6926d90 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/library/README.md @@ -0,0 +1,5 @@ +# @fluentui/react-visual-refresh-preview + +**React Visual Refresh components for [Fluent UI React](https://react.fluentui.dev/)** + +These are not production-ready components and **should never be used in product**. This space is useful for testing new components whose APIs might change before final release. diff --git a/packages/react-components/react-visual-refresh-preview/library/config/api-extractor.json b/packages/react-components/react-visual-refresh-preview/library/config/api-extractor.json new file mode 100644 index 00000000000000..8d482156d10d53 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/library/config/api-extractor.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "@fluentui/scripts-api-extractor/api-extractor.common.v-next.json", + "mainEntryPointFilePath": "/../../../../../../dist/out-tsc/types/packages/react-components//library/src/index.d.ts" +} diff --git a/packages/react-components/react-visual-refresh-preview/library/config/tests.js b/packages/react-components/react-visual-refresh-preview/library/config/tests.js new file mode 100644 index 00000000000000..2e211ae9e21420 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/library/config/tests.js @@ -0,0 +1 @@ +/** Jest test setup file. */ diff --git a/packages/react-components/react-visual-refresh-preview/library/docs/Spec.md b/packages/react-components/react-visual-refresh-preview/library/docs/Spec.md new file mode 100644 index 00000000000000..0df0b1a3c85dc4 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/library/docs/Spec.md @@ -0,0 +1,63 @@ +# @fluentui/react-visual-refresh-preview Spec + +## Background + +_Description and use cases of this component_ + +## Prior Art + +_Include background research done for this component_ + +- _Link to Open UI research_ +- _Link to comparison of v7 and v0_ +- _Link to GitHub epic issue for the converged component_ + +## Sample Code + +_Provide some representative example code that uses the proposed API for the component_ + +## Variants + +_Describe visual or functional variants of this control, if applicable. For example, a slider could have a 2D variant._ + +## API + +_List the **Props** and **Slots** proposed for the component. Ideally this would just be a link to the component's `.types.ts` file_ + +## Structure + +- _**Public**_ +- _**Internal**_ +- _**DOM** - how the component will be rendered as HTML elements_ + +## Migration + +_Describe what will need to be done to upgrade from the existing implementations:_ + +- _Migration from v8_ +- _Migration from v0_ + +## Behaviors + +_Explain how the component will behave in use, including:_ + +- _Component States_ +- _Interaction_ + - _Keyboard_ + - _Cursor_ + - _Touch_ + - _Screen readers_ + +## Accessibility + +Base accessibility information is included in the design document. After the spec is filled and review, outcomes from it need to be communicated to design and incorporated in the design document. + +- Decide whether to use **native element** or folow **ARIA** and provide reasons +- Identify the **[ARIA](https://www.w3.org/TR/wai-aria-practices-1.2/) pattern** and, if the component is listed there, follow its specification as possible. +- Identify accessibility **variants**, the `role` ([ARIA roles](https://www.w3.org/TR/wai-aria-1.1/#role_definitions)) of the component, its `slots` and `aria-*` props. +- Describe the **keyboard navigation**: Tab Oder and Arrow Key Navigation. Describe any other keyboard **shortcuts** used +- Specify texts for **state change announcements** - [ARIA live regions + ](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions) (number of available items in dropdown, error messages, confirmations, ...) +- Identify UI parts that appear on **hover or focus** and specify keyboard and screen reader interaction with them +- List cases when **focus** needs to be **trapped** in sections of the UI (for dialogs and popups or for hierarchical navigation) +- List cases when **focus** needs to be **moved programatically** (if parts of the UI are appearing/disappearing or other cases) diff --git a/packages/react-components/react-visual-refresh-preview/library/etc/react-visual-refresh-preview.api.md b/packages/react-components/react-visual-refresh-preview/library/etc/react-visual-refresh-preview.api.md new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/packages/react-components/react-visual-refresh-preview/library/jest.config.js b/packages/react-components/react-visual-refresh-preview/library/jest.config.js new file mode 100644 index 00000000000000..78741cc466bdd2 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/library/jest.config.js @@ -0,0 +1,34 @@ +// @ts-check +/* eslint-disable */ + +const { readFileSync } = require('node:fs'); +const { join } = require('node:path'); + +// Reading the SWC compilation config and remove the "exclude" +// for the test files to be compiled by SWC +const { exclude: _, ...swcJestConfig } = JSON.parse(readFileSync(join(__dirname, '.swcrc'), 'utf-8')); + +// disable .swcrc look-up by SWC core because we're passing in swcJestConfig ourselves. +// If we do not disable this, SWC Core will read .swcrc and won't transform our test files due to "exclude" +if (swcJestConfig.swcrc === undefined) { + swcJestConfig.swcrc = false; +} + +// Uncomment if using global setup/teardown files being transformed via swc +// https://nx.dev/packages/jest/documents/overview#global-setup/teardown-with-nx-libraries +// jest needs EsModule Interop to find the default exported setup/teardown functions +// swcJestConfig.module.noInterop = false; + +/** + * @type {import('@jest/types').Config.InitialOptions} + */ +module.exports = { + displayName: 'react-visual-refresh-preview', + preset: '../../../../jest.preset.js', + transform: { + '^.+\\.tsx?$': ['@swc/jest', swcJestConfig], + }, + coverageDirectory: './coverage', + setupFilesAfterEnv: ['./config/tests.js'], + snapshotSerializers: ['@griffel/jest-serializer'], +}; diff --git a/packages/react-components/react-visual-refresh-preview/library/package.json b/packages/react-components/react-visual-refresh-preview/library/package.json new file mode 100644 index 00000000000000..9aa5de59647f82 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/library/package.json @@ -0,0 +1,56 @@ +{ + "name": "@fluentui/react-visual-refresh-preview", + "version": "0.0.0", + "private": true, + "description": "New fluentui react package", + "main": "lib-commonjs/index.js", + "module": "lib/index.js", + "typings": "./dist/index.d.ts", + "sideEffects": false, + "files": [ + "*.md", + "dist/*.d.ts", + "lib", + "lib-commonjs" + ], + "repository": { + "type": "git", + "url": "https://github.com/microsoft/fluentui" + }, + "license": "MIT", + "devDependencies": { + "@fluentui/eslint-plugin": "*", + "@fluentui/react-conformance": "*", + "@fluentui/react-conformance-griffel": "*", + "@fluentui/scripts-api-extractor": "*" + }, + "dependencies": { + "@fluentui/react-jsx-runtime": "^9.3.2", + "@fluentui/react-shared-contexts": "^9.25.2", + "@fluentui/react-theme": "^9.2.0", + "@fluentui/react-utilities": "^9.25.3", + "@griffel/react": "^1.5.32", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.9.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib/index.js", + "require": "./lib-commonjs/index.js" + }, + "./package.json": "./package.json" + }, + "beachball": { + "disallowedChangeTypes": [ + "major", + "prerelease" + ] + } +} diff --git a/packages/react-components/react-visual-refresh-preview/library/project.json b/packages/react-components/react-visual-refresh-preview/library/project.json new file mode 100644 index 00000000000000..0c6dddfabfa7e4 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/library/project.json @@ -0,0 +1,8 @@ +{ + "name": "react-visual-refresh-preview", + "$schema": "../../../../node_modules/nx/schemas/project-schema.json", + "projectType": "library", + "sourceRoot": "packages/react-components/react-visual-refresh-preview/library/src", + "tags": ["platform:web", "vNext"], + "implicitDependencies": [] +} diff --git a/packages/react-components/react-visual-refresh-preview/library/src/CAPTheme.tsx b/packages/react-components/react-visual-refresh-preview/library/src/CAPTheme.tsx new file mode 100644 index 00000000000000..0b4eeff9729ff3 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/library/src/CAPTheme.tsx @@ -0,0 +1,66 @@ +import { tokens } from '@fluentui/react-components'; + +const TOKEN_TYPES = ['color', 'length'] as const; + +type TokenType = (typeof TOKEN_TYPES)[number]; + +export interface Token { + type: TokenType; +} + +export interface ControlSchema { + name: string; + tokens: Record; +} + +const ButtonTokens = { + buttonPrimaryBackgroundColor: { type: 'color' }, + buttonPrimaryBackgroundColorHover: { type: 'color' }, + buttonSecondaryBackgroundColor: { type: 'color' }, + buttonSecondaryBackgroundColorHover: { type: 'color' }, + buttonSubtleBackgroundColor: { type: 'color' }, + buttonSubtleBackgroundColorHover: { type: 'color' }, + buttonOutlineBackgroundColor: { type: 'color' }, + buttonOutlineBackgroundColorHover: { type: 'color' }, + buttonTintBackgroundColor: { type: 'color' }, + buttonTintBackgroundColorHover: { type: 'color' }, +} as const satisfies Record; + +export const CAPTokensSchema = { + ...ButtonTokens, +} as const satisfies { [key: string]: Token }; + +export const CAPTokens = { + ...(Object.keys(CAPTokensSchema).reduce((acc: any, key) => { + return { ...acc, [key]: `var(--cap-${key})` }; + }) as any), +} as Record; + +export type CAPTheme = { + [k in keyof typeof CAPTokens]: string; +}; + +export const CAP_THEME = { + buttonPrimaryBackgroundColor: tokens.colorBrandBackground, + buttonPrimaryBackgroundColorHover: tokens.colorBrandBackgroundHover, + buttonSecondaryBackgroundColor: '#FAFAFA', + buttonSecondaryBackgroundColorHover: '#F0F0F0', // NeutralBackground3.Hover + buttonSubtleBackgroundColor: tokens.colorBrandBackground, + buttonSubtleBackgroundColorHover: tokens.colorBrandBackground, + buttonOutlineBackgroundColor: tokens.colorTransparentBackground, + buttonOutlineBackgroundColorHover: tokens.colorTransparentBackground, + buttonTintBackgroundColor: '', + buttonTintBackgroundColorHover: '', +} as const satisfies CAPTheme; + +export const CAP_THEME_TEAMS = { + ...CAP_THEME, +} as const satisfies CAPTheme; + +export const CAP_THEME_ONE_DRIVE = { + ...CAP_THEME, +} as const satisfies CAPTheme; + +export const CAP_THEME_SHAREPOINT = { + ...CAP_THEME, +} as const satisfies CAPTheme; diff --git a/packages/react-components/react-visual-refresh-preview/library/src/CAPThemeProvider.tsx b/packages/react-components/react-visual-refresh-preview/library/src/CAPThemeProvider.tsx new file mode 100644 index 00000000000000..a676cdd6e5ee64 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/library/src/CAPThemeProvider.tsx @@ -0,0 +1,43 @@ +import * as React from 'react'; +import { FluentProvider, Theme } from '@fluentui/react-components'; +import { useCAPButtonStylesHook } from './components/CAPButton'; +import { CustomStyleHooksContextValue } from '../../../react-shared-contexts/library/src/CustomStyleHooksContext'; +import { CAPTheme } from './CAPTheme'; + +const CAPThemeContext = React.createContext<{ theme: Partial & Partial }>({ + theme: {}, +}); + +export const CAPThemeProvider = ({ + children, + theme, +}: { + children: React.ReactElement; + theme: Partial & Partial; +}) => { + const capThemeContext = React.useMemo(() => { + return { theme }; + }, [theme]); + const customStyleHooks = React.useMemo(() => { + return { useButtonStyles_unstable: useCAPButtonStylesHook }; + }, []); + const styles = React.useMemo(() => { + const styles: React.CSSProperties = {}; + for (const [tokenName, tokenValue] of Object.entries(theme)) { + (styles as any)[`--cap-${tokenName}`] = tokenValue; + } + return styles; + }, [theme]); + + return ( + + + {children} + + + ); +}; diff --git a/packages/react-components/react-visual-refresh-preview/library/src/components/CAPButton.tsx b/packages/react-components/react-visual-refresh-preview/library/src/components/CAPButton.tsx new file mode 100644 index 00000000000000..2a77354de98c53 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/library/src/components/CAPButton.tsx @@ -0,0 +1,45 @@ +import { makeStyles, mergeClasses } from '@griffel/react'; +import { ButtonState } from '@fluentui/react-components'; +import { CAPTokens } from '../CAPTheme'; + +const useCAPButtonStyles = makeStyles({ + root: { + borderRadius: '12px', + }, + primary: { + backgroundColor: CAPTokens.buttonPrimaryBackgroundColor, + + ':hover': { + backgroundColor: CAPTokens.buttonPrimaryBackgroundColorHover, + }, + }, + secondary: { + backgroundColor: CAPTokens.buttonSecondaryBackgroundColor, + + ':hover': { + backgroundColor: CAPTokens.buttonSecondaryBackgroundColorHover, + }, + }, + outline: { + backgroundColor: CAPTokens.buttonOutlineBackgroundColor, + + ':hover': { + backgroundColor: CAPTokens.buttonOutlineBackgroundColorHover, + }, + }, + subtle: {}, + tint: { + backgroundColor: CAPTokens.buttonTintBackgroundColor, + + ':hover': { + backgroundColor: CAPTokens.buttonTintBackgroundColorHover, + }, + }, + transparent: {}, +}); + +export function useCAPButtonStylesHook(state: ButtonState) { + const styles = useCAPButtonStyles(); + state.root.className = mergeClasses(state.root.className, styles.root, state.appearance && styles[state.appearance]); + return state; +} diff --git a/packages/react-components/react-visual-refresh-preview/library/src/index.ts b/packages/react-components/react-visual-refresh-preview/library/src/index.ts new file mode 100644 index 00000000000000..3a8fc17ad04890 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/library/src/index.ts @@ -0,0 +1,2 @@ +export { CAPThemeProvider } from './CAPThemeProvider'; +export { CAP_THEME_ONE_DRIVE, CAP_THEME_SHAREPOINT, CAP_THEME_TEAMS } from './CAPTheme'; diff --git a/packages/react-components/react-visual-refresh-preview/library/src/testing/isConformant.ts b/packages/react-components/react-visual-refresh-preview/library/src/testing/isConformant.ts new file mode 100644 index 00000000000000..8ed2da0f925135 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/library/src/testing/isConformant.ts @@ -0,0 +1,15 @@ +import { isConformant as baseIsConformant } from '@fluentui/react-conformance'; +import type { IsConformantOptions, TestObject } from '@fluentui/react-conformance'; +import griffelTests from '@fluentui/react-conformance-griffel'; + +export function isConformant( + testInfo: Omit, 'componentPath'> & { componentPath?: string }, +): void { + const defaultOptions: Partial> = { + tsConfig: { configName: 'tsconfig.spec.json' }, + componentPath: require.main?.filename.replace('.test', ''), + extraTests: griffelTests as TestObject, + }; + + baseIsConformant(defaultOptions, testInfo); +} diff --git a/packages/react-components/react-visual-refresh-preview/library/tsconfig.json b/packages/react-components/react-visual-refresh-preview/library/tsconfig.json new file mode 100644 index 00000000000000..32bdbdf1ac26f0 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/library/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "target": "ES2019", + "noEmit": true, + "isolatedModules": true, + "importHelpers": true, + "jsx": "react", + "noUnusedLocals": true, + "preserveConstEnums": true + }, + "include": [], + "files": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/packages/react-components/react-visual-refresh-preview/library/tsconfig.lib.json b/packages/react-components/react-visual-refresh-preview/library/tsconfig.lib.json new file mode 100644 index 00000000000000..53066fdd11fff0 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/library/tsconfig.lib.json @@ -0,0 +1,22 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": false, + "lib": ["ES2019", "dom"], + "declaration": true, + "declarationDir": "../../../../dist/out-tsc/types", + "outDir": "../../../../dist/out-tsc", + "inlineSources": true, + "types": ["static-assets", "environment"] + }, + "exclude": [ + "./src/testing/**", + "**/*.spec.ts", + "**/*.spec.tsx", + "**/*.test.ts", + "**/*.test.tsx", + "**/*.stories.ts", + "**/*.stories.tsx" + ], + "include": ["./src/**/*.ts", "./src/**/*.tsx"] +} diff --git a/packages/react-components/react-visual-refresh-preview/library/tsconfig.spec.json b/packages/react-components/react-visual-refresh-preview/library/tsconfig.spec.json new file mode 100644 index 00000000000000..911456fe4b4d91 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/library/tsconfig.spec.json @@ -0,0 +1,17 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "CommonJS", + "outDir": "dist", + "types": ["jest", "node"] + }, + "include": [ + "**/*.spec.ts", + "**/*.spec.tsx", + "**/*.test.ts", + "**/*.test.tsx", + "**/*.d.ts", + "./src/testing/**/*.ts", + "./src/testing/**/*.tsx" + ] +} diff --git a/packages/react-components/react-visual-refresh-preview/stories/.eslintrc.json b/packages/react-components/react-visual-refresh-preview/stories/.eslintrc.json new file mode 100644 index 00000000000000..a41120835dcc92 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/stories/.eslintrc.json @@ -0,0 +1,12 @@ +{ + "extends": ["plugin:@fluentui/eslint-plugin/react"], + "root": true, + "rules": { + "import/no-extraneous-dependencies": [ + "error", + { + "packageDir": [".", "../../../../"] + } + ] + } +} diff --git a/packages/react-components/react-visual-refresh-preview/stories/.storybook/main.js b/packages/react-components/react-visual-refresh-preview/stories/.storybook/main.js new file mode 100644 index 00000000000000..67905c6bfe15f2 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/stories/.storybook/main.js @@ -0,0 +1,14 @@ +const rootMain = require('../../../../../.storybook/main'); + +module.exports = /** @type {Omit} */ ({ + ...rootMain, + stories: [...rootMain.stories, '../src/**/*.mdx', '../src/**/index.stories.@(ts|tsx)'], + addons: [...rootMain.addons], + webpackFinal: (config, options) => { + const localConfig = { ...rootMain.webpackFinal(config, options) }; + + // add your own webpack tweaks if needed + + return localConfig; + }, +}); diff --git a/packages/react-components/react-visual-refresh-preview/stories/.storybook/preview.js b/packages/react-components/react-visual-refresh-preview/stories/.storybook/preview.js new file mode 100644 index 00000000000000..98274ed0b8095f --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/stories/.storybook/preview.js @@ -0,0 +1,9 @@ +import * as rootPreview from '../../../../../.storybook/preview'; + +/** @type {typeof rootPreview.decorators} */ +export const decorators = [...rootPreview.decorators]; + +/** @type {typeof rootPreview.parameters} */ +export const parameters = { ...rootPreview.parameters }; + +export const tags = ['autodocs']; diff --git a/packages/react-components/react-visual-refresh-preview/stories/.storybook/tsconfig.json b/packages/react-components/react-visual-refresh-preview/stories/.storybook/tsconfig.json new file mode 100644 index 00000000000000..4cdd1ce9d006f1 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/stories/.storybook/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "", + "allowJs": true, + "checkJs": true, + "types": ["static-assets", "environment"] + }, + "include": ["*.js"] +} diff --git a/packages/react-components/react-visual-refresh-preview/stories/README.md b/packages/react-components/react-visual-refresh-preview/stories/README.md new file mode 100644 index 00000000000000..46787c7ead4fa8 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/stories/README.md @@ -0,0 +1,17 @@ +# @fluentui/react-visual-refresh-preview-stories + +Storybook stories for packages/react-components/react-visual-refresh-preview + +## Usage + +To include within storybook specify stories globs: + +\`\`\`js +module.exports = { +stories: ['../packages/react-components/react-visual-refresh-preview/stories/src/**/*.mdx', '../packages/react-components/react-visual-refresh-preview/stories/src/**/index.stories.@(ts|tsx)'], +} +\`\`\` + +## API + +no public API available diff --git a/packages/react-components/react-visual-refresh-preview/stories/package.json b/packages/react-components/react-visual-refresh-preview/stories/package.json new file mode 100644 index 00000000000000..85e2b373256d13 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/stories/package.json @@ -0,0 +1,11 @@ +{ + "name": "@fluentui/react-visual-refresh-preview-stories", + "version": "0.0.0", + "private": true, + "devDependencies": { + "@fluentui/react-storybook-addon": "*", + "@fluentui/react-storybook-addon-export-to-sandbox": "*", + "@fluentui/scripts-storybook": "*", + "@fluentui/eslint-plugin": "*" + } +} diff --git a/packages/react-components/react-visual-refresh-preview/stories/project.json b/packages/react-components/react-visual-refresh-preview/stories/project.json new file mode 100644 index 00000000000000..8ce9ed29eae34a --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/stories/project.json @@ -0,0 +1,8 @@ +{ + "name": "react-visual-refresh-preview-stories", + "$schema": "../../../../node_modules/nx/schemas/project-schema.json", + "projectType": "library", + "sourceRoot": "packages/react-components/react-visual-refresh-preview/stories/src", + "tags": ["vNext", "platform:web", "type:stories"], + "implicitDependencies": [] +} diff --git a/packages/react-components/react-visual-refresh-preview/stories/src/CAPBadge.stories.tsx b/packages/react-components/react-visual-refresh-preview/stories/src/CAPBadge.stories.tsx new file mode 100644 index 00000000000000..37a95d46ed0625 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/stories/src/CAPBadge.stories.tsx @@ -0,0 +1,18 @@ +import * as React from 'react'; +import { Badge } from '@fluentui/react-components'; +import { VisualRefreshExamples } from './StorybookUtil'; + +export const CAPBadgeStory = () => { + return ( + ; + }, + }, + ]} + /> + ); +}; diff --git a/packages/react-components/react-visual-refresh-preview/stories/src/CAPButton.stories.tsx b/packages/react-components/react-visual-refresh-preview/stories/src/CAPButton.stories.tsx new file mode 100644 index 00000000000000..2b70c887fd9e72 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/stories/src/CAPButton.stories.tsx @@ -0,0 +1,74 @@ +import * as React from 'react'; +import { Button } from '@fluentui/react-components'; +import { VisualRefreshExamples } from './StorybookUtil'; +import { bundleIcon, CalendarMonthFilled, CalendarMonthRegular } from '@fluentui/react-icons'; + +const CalendarMonth = bundleIcon(CalendarMonthFilled, CalendarMonthRegular); + +export const CAPButtonStory = () => { + return ( + }>Default; + }, + }, + { + title: 'Primary', + render() { + return ( + + ); + }, + }, + { + title: 'Outline', + render() { + return ( + + ); + }, + }, + { + title: 'Subtle', + render() { + return ( + + ); + }, + }, + { + title: 'Transparent', + render() { + return ( + + ); + }, + }, + { + title: 'Tint', + render(variant) { + if (variant === 'v9') { + return 'NOT_IMPLEMENTED'; + } + return ( + + ); + }, + }, + ]} + /> + ); +}; diff --git a/packages/react-components/react-visual-refresh-preview/stories/src/CAPCard.stories.tsx b/packages/react-components/react-visual-refresh-preview/stories/src/CAPCard.stories.tsx new file mode 100644 index 00000000000000..f0a0f4553b9b6a --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/stories/src/CAPCard.stories.tsx @@ -0,0 +1,6 @@ +import * as React from 'react'; +import { VisualRefreshExamples } from './StorybookUtil'; + +export const CAPCardStory = () => { + return ; +}; diff --git a/packages/react-components/react-visual-refresh-preview/stories/src/CAPInput.stories.tsx b/packages/react-components/react-visual-refresh-preview/stories/src/CAPInput.stories.tsx new file mode 100644 index 00000000000000..c1a2ed23f685d1 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/stories/src/CAPInput.stories.tsx @@ -0,0 +1,6 @@ +import * as React from 'react'; +import { VisualRefreshExamples } from './StorybookUtil'; + +export const CAPInputStory = () => { + return ; +}; diff --git a/packages/react-components/react-visual-refresh-preview/stories/src/CAPMenu.stories.tsx b/packages/react-components/react-visual-refresh-preview/stories/src/CAPMenu.stories.tsx new file mode 100644 index 00000000000000..8159e057993baf --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/stories/src/CAPMenu.stories.tsx @@ -0,0 +1,6 @@ +import * as React from 'react'; +import { VisualRefreshExamples } from './StorybookUtil'; + +export const CAPMenuStory = () => { + return ; +}; diff --git a/packages/react-components/react-visual-refresh-preview/stories/src/CAPTooltip.stories.tsx b/packages/react-components/react-visual-refresh-preview/stories/src/CAPTooltip.stories.tsx new file mode 100644 index 00000000000000..7b547ab04cbb42 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/stories/src/CAPTooltip.stories.tsx @@ -0,0 +1,6 @@ +import * as React from 'react'; +import { VisualRefreshExamples } from './StorybookUtil'; + +export const CAPTooltipStory = () => { + return ; +}; diff --git a/packages/react-components/react-visual-refresh-preview/stories/src/StorybookUtil.tsx b/packages/react-components/react-visual-refresh-preview/stories/src/StorybookUtil.tsx new file mode 100644 index 00000000000000..2d03c88645b636 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/stories/src/StorybookUtil.tsx @@ -0,0 +1,70 @@ +import * as React from 'react'; +import { FluentProvider, makeStyles, teamsLightV21Theme } from '@fluentui/react-components'; +import { + CAPThemeProvider, + CAP_THEME_ONE_DRIVE, + CAP_THEME_SHAREPOINT, + CAP_THEME_TEAMS, +} from '@fluentui/react-visual-refresh-preview'; + +export interface VisualRefreshExample { + title: string; + render(variant: 'v9' | 'cap'): React.ReactElement | 'NOT_IMPLEMENTED'; +} + +export const VisualRefreshExamples = ({ examples }: { examples: VisualRefreshExample[] }) => { + const styles = useVisualRefreshExamplesStyles(); + return ( + +
+
+
Current
+
Visual Refresh (Teams)
+
Visual Refresh (OneDrive)
+
Visual Refresh (SharePoint)
+
+ {examples.map(example => { + return ( +
+
{renderExample(example, 'v9')}
+
+ + {renderExample(example, 'cap')} + +
+
+ {renderExample(example, 'cap')} +
+
+ {renderExample(example, 'cap')} +
+
+ ); + })} +
+
+ ); +}; + +function renderExample(example: VisualRefreshExample, variant: 'v9' | 'cap') { + const result = example.render(variant); + if (result === 'NOT_IMPLEMENTED') { + return NOT IMPLEMENTED; + } + return result; +} + +const useVisualRefreshExamplesStyles = makeStyles({ + table: { + display: 'flex', + flexDirection: 'column', + }, + row: { + display: 'flex', + '& > div': { + flex: 1, + padding: '16px', + border: '1px solid #ddd', + }, + }, +}); diff --git a/packages/react-components/react-visual-refresh-preview/stories/src/index.stories.tsx b/packages/react-components/react-visual-refresh-preview/stories/src/index.stories.tsx new file mode 100644 index 00000000000000..3446d94d8af3fc --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/stories/src/index.stories.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; +import { Meta } from '@storybook/react'; + +export { CAPBadgeStory as Badge } from './CAPBadge.stories'; +export { CAPButtonStory as Button } from './CAPButton.stories'; +export { CAPCardStory as Card } from './CAPCard.stories'; +export { CAPInputStory as Input } from './CAPInput.stories'; +export { CAPMenuStory as Menu } from './CAPMenu.stories'; +export { CAPTooltipStory as Tooltip } from './CAPTooltip.stories'; + +const VisualRefreshStory = () =>
; + +export default { + title: 'Visual Refresh', + component: VisualRefreshStory, + parameters: {}, +} as Meta; diff --git a/packages/react-components/react-visual-refresh-preview/stories/tsconfig.json b/packages/react-components/react-visual-refresh-preview/stories/tsconfig.json new file mode 100644 index 00000000000000..efc50169d1df18 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/stories/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "target": "ES2019", + "noEmit": true, + "isolatedModules": true, + "importHelpers": true, + "jsx": "react", + "noUnusedLocals": true, + "preserveConstEnums": true + }, + "include": [], + "files": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./.storybook/tsconfig.json" + } + ] +} diff --git a/packages/react-components/react-visual-refresh-preview/stories/tsconfig.lib.json b/packages/react-components/react-visual-refresh-preview/stories/tsconfig.lib.json new file mode 100644 index 00000000000000..9486b224643d9f --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/stories/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "lib": ["ES2019", "dom"], + "outDir": "../../../../dist/out-tsc", + "inlineSources": true, + "types": ["static-assets", "environment"] + }, + "include": ["./src/**/*.ts", "./src/**/*.tsx"] +} diff --git a/tsconfig.base.all.json b/tsconfig.base.all.json index 9b9756df7a1a05..1328da016a97c5 100644 --- a/tsconfig.base.all.json +++ b/tsconfig.base.all.json @@ -248,13 +248,23 @@ "@fluentui/react-utilities-compat-stories": [ "packages/react-components/react-utilities-compat/stories/src/index.ts" ], + "@fluentui/react-visual-refresh-preview": [ + "packages/react-components/react-visual-refresh-preview/library/src/index.ts" + ], "@fluentui/recipes": ["packages/react-components/recipes/src/index.ts"], "@fluentui/storybook-llms-extractor": ["tools/storybook-llms-extractor/src/index.ts"], "@fluentui/theme-designer": ["packages/react-components/theme-designer/src/index.ts"], "@fluentui/tokens": ["packages/tokens/src/index.ts"], + "@fluentui/visual-refresh-preview": ["packages/react-components/visual-refresh-preview/library/src/index.ts"], + "@fluentui/visual-refresh-preview-stories": [ + "packages/react-components/visual-refresh-preview/stories/src/index.ts" + ], "@fluentui/visual-regression-assert": ["tools/visual-regression-assert/src/index.ts"], "@fluentui/visual-regression-utilities": ["tools/visual-regression-utilities/src/index.ts"], - "@fluentui/workspace-plugin": ["tools/workspace-plugin/src/index.ts"] + "@fluentui/workspace-plugin": ["tools/workspace-plugin/src/index.ts"], + "@fluentui/react-visual-refresh-preview-stories": [ + "packages/react-components/react-visual-refresh-preview/stories/src/index.ts" + ] } } } diff --git a/tsconfig.base.json b/tsconfig.base.json index ded6aaaa1f2528..c76de2485b72e7 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -204,10 +204,20 @@ "@fluentui/react-utilities-compat-stories": [ "packages/react-components/react-utilities-compat/stories/src/index.ts" ], + "@fluentui/react-visual-refresh-preview": [ + "packages/react-components/react-visual-refresh-preview/library/src/index.ts" + ], + "@fluentui/react-visual-refresh-preview-stories": [ + "packages/react-components/react-visual-refresh-preview/stories/src/index.ts" + ], "@fluentui/recipes": ["packages/react-components/recipes/src/index.ts"], "@fluentui/storybook-llms-extractor": ["tools/storybook-llms-extractor/src/index.ts"], "@fluentui/theme-designer": ["packages/react-components/theme-designer/src/index.ts"], "@fluentui/tokens": ["packages/tokens/src/index.ts"], + "@fluentui/visual-refresh-preview": ["packages/react-components/visual-refresh-preview/library/src/index.ts"], + "@fluentui/visual-refresh-preview-stories": [ + "packages/react-components/visual-refresh-preview/stories/src/index.ts" + ], "@fluentui/visual-regression-assert": ["tools/visual-regression-assert/src/index.ts"], "@fluentui/visual-regression-utilities": ["tools/visual-regression-utilities/src/index.ts"], "@fluentui/workspace-plugin": ["tools/workspace-plugin/src/index.ts"] From 08ae0a10e493b4b8702d850090899f00b60a245c Mon Sep 17 00:00:00 2001 From: David Zukowski Date: Tue, 11 Nov 2025 11:21:57 -0800 Subject: [PATCH 02/14] naming --- .../library/src/CAPTheme.tsx | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/react-components/react-visual-refresh-preview/library/src/CAPTheme.tsx b/packages/react-components/react-visual-refresh-preview/library/src/CAPTheme.tsx index 0b4eeff9729ff3..6f58edc1095093 100644 --- a/packages/react-components/react-visual-refresh-preview/library/src/CAPTheme.tsx +++ b/packages/react-components/react-visual-refresh-preview/library/src/CAPTheme.tsx @@ -4,15 +4,10 @@ const TOKEN_TYPES = ['color', 'length'] as const; type TokenType = (typeof TOKEN_TYPES)[number]; -export interface Token { +export interface TokenSchema { type: TokenType; } -export interface ControlSchema { - name: string; - tokens: Record; -} - const ButtonTokens = { buttonPrimaryBackgroundColor: { type: 'color' }, buttonPrimaryBackgroundColorHover: { type: 'color' }, @@ -24,11 +19,11 @@ const ButtonTokens = { buttonOutlineBackgroundColorHover: { type: 'color' }, buttonTintBackgroundColor: { type: 'color' }, buttonTintBackgroundColorHover: { type: 'color' }, -} as const satisfies Record; +} as const satisfies Record; export const CAPTokensSchema = { ...ButtonTokens, -} as const satisfies { [key: string]: Token }; +} as const satisfies { [key: string]: TokenSchema }; export const CAPTokens = { ...(Object.keys(CAPTokensSchema).reduce((acc: any, key) => { From 2005c1c9f3f5e04614ce488ef1ecabb24ce31c69 Mon Sep 17 00:00:00 2001 From: David Zukowski Date: Tue, 11 Nov 2025 11:43:28 -0800 Subject: [PATCH 03/14] more scaffolding --- .../library/src/CAPTheme.tsx | 40 ++++++++++++----- .../library/src/CAPThemeProvider.tsx | 44 ++++++++----------- .../library/src/components/CAPBadge.tsx | 12 +++++ .../library/src/components/CAPCard.tsx | 12 +++++ .../library/src/components/CAPInput.tsx | 12 +++++ .../library/src/components/CAPMenu.tsx | 5 +++ .../stories/src/CAPInput.stories.tsx | 14 +++++- .../stories/src/StorybookUtil.tsx | 2 + 8 files changed, 104 insertions(+), 37 deletions(-) create mode 100644 packages/react-components/react-visual-refresh-preview/library/src/components/CAPBadge.tsx create mode 100644 packages/react-components/react-visual-refresh-preview/library/src/components/CAPCard.tsx create mode 100644 packages/react-components/react-visual-refresh-preview/library/src/components/CAPInput.tsx create mode 100644 packages/react-components/react-visual-refresh-preview/library/src/components/CAPMenu.tsx diff --git a/packages/react-components/react-visual-refresh-preview/library/src/CAPTheme.tsx b/packages/react-components/react-visual-refresh-preview/library/src/CAPTheme.tsx index 6f58edc1095093..acf367164f7318 100644 --- a/packages/react-components/react-visual-refresh-preview/library/src/CAPTheme.tsx +++ b/packages/react-components/react-visual-refresh-preview/library/src/CAPTheme.tsx @@ -8,21 +8,41 @@ export interface TokenSchema { type: TokenType; } +const ColorTokenSchema = { type: 'color' } as const satisfies TokenSchema; + +const BadgeTokens = {} as const satisfies Record; + const ButtonTokens = { - buttonPrimaryBackgroundColor: { type: 'color' }, - buttonPrimaryBackgroundColorHover: { type: 'color' }, - buttonSecondaryBackgroundColor: { type: 'color' }, - buttonSecondaryBackgroundColorHover: { type: 'color' }, - buttonSubtleBackgroundColor: { type: 'color' }, - buttonSubtleBackgroundColorHover: { type: 'color' }, - buttonOutlineBackgroundColor: { type: 'color' }, - buttonOutlineBackgroundColorHover: { type: 'color' }, - buttonTintBackgroundColor: { type: 'color' }, - buttonTintBackgroundColorHover: { type: 'color' }, + buttonPrimaryBackgroundColor: ColorTokenSchema, + buttonPrimaryBackgroundColorHover: ColorTokenSchema, + buttonSecondaryBackgroundColor: ColorTokenSchema, + buttonSecondaryBackgroundColorHover: ColorTokenSchema, + buttonSubtleBackgroundColor: ColorTokenSchema, + buttonSubtleBackgroundColorHover: ColorTokenSchema, + buttonOutlineBackgroundColor: ColorTokenSchema, + buttonOutlineBackgroundColorHover: ColorTokenSchema, + buttonTintBackgroundColor: ColorTokenSchema, + buttonTintBackgroundColorHover: ColorTokenSchema, } as const satisfies Record; +const CardTokens = {} as const satisfies Record; + +const DialogTokens = {} as const satisfies Record; + +const InputTokens = {} as const satisfies Record; + +const MenuTokens = {} as const satisfies Record; + +const TooltipTokens = {} as const satisfies Record; + export const CAPTokensSchema = { + ...BadgeTokens, ...ButtonTokens, + ...CardTokens, + ...DialogTokens, + ...InputTokens, + ...MenuTokens, + ...TooltipTokens, } as const satisfies { [key: string]: TokenSchema }; export const CAPTokens = { diff --git a/packages/react-components/react-visual-refresh-preview/library/src/CAPThemeProvider.tsx b/packages/react-components/react-visual-refresh-preview/library/src/CAPThemeProvider.tsx index a676cdd6e5ee64..a384ddd7e1f267 100644 --- a/packages/react-components/react-visual-refresh-preview/library/src/CAPThemeProvider.tsx +++ b/packages/react-components/react-visual-refresh-preview/library/src/CAPThemeProvider.tsx @@ -1,12 +1,11 @@ import * as React from 'react'; -import { FluentProvider, Theme } from '@fluentui/react-components'; +import { BadgeState, ButtonState, CardState, FluentProvider, InputState, Theme } from '@fluentui/react-components'; import { useCAPButtonStylesHook } from './components/CAPButton'; import { CustomStyleHooksContextValue } from '../../../react-shared-contexts/library/src/CustomStyleHooksContext'; import { CAPTheme } from './CAPTheme'; - -const CAPThemeContext = React.createContext<{ theme: Partial & Partial }>({ - theme: {}, -}); +import { useCAPBadgeStylesHook } from './components/CAPBadge'; +import { useCAPInputStylesHook } from './components/CAPInput'; +import { useCAPCardStylesHook } from './components/CAPCard'; export const CAPThemeProvider = ({ children, @@ -15,29 +14,22 @@ export const CAPThemeProvider = ({ children: React.ReactElement; theme: Partial & Partial; }) => { - const capThemeContext = React.useMemo(() => { - return { theme }; - }, [theme]); - const customStyleHooks = React.useMemo(() => { - return { useButtonStyles_unstable: useCAPButtonStylesHook }; + const customStyleHooks = React.useMemo((): CustomStyleHooksContextValue => { + return { + useBadgeStyles_unstable: state => useCAPBadgeStylesHook(state as BadgeState), + useButtonStyles_unstable: state => useCAPButtonStylesHook(state as ButtonState), + useCardStyles_unstable: state => useCAPCardStylesHook(state as CardState), + useInputStyles_unstable: state => useCAPInputStylesHook(state as InputState), + }; }, []); - const styles = React.useMemo(() => { - const styles: React.CSSProperties = {}; - for (const [tokenName, tokenValue] of Object.entries(theme)) { - (styles as any)[`--cap-${tokenName}`] = tokenValue; - } - return styles; - }, [theme]); + const styles: React.CSSProperties = {}; + for (const [tokenName, tokenValue] of Object.entries(theme)) { + (styles as any)[`--cap-${tokenName}`] = tokenValue; + } return ( - - - {children} - - + + {children} + ); }; diff --git a/packages/react-components/react-visual-refresh-preview/library/src/components/CAPBadge.tsx b/packages/react-components/react-visual-refresh-preview/library/src/components/CAPBadge.tsx new file mode 100644 index 00000000000000..415170a7c0cf8d --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/library/src/components/CAPBadge.tsx @@ -0,0 +1,12 @@ +import { makeStyles, mergeClasses } from '@griffel/react'; +import { BadgeState } from '@fluentui/react-components'; + +const useCAPBadgeStyles = makeStyles({ + root: {}, +}); + +export function useCAPBadgeStylesHook(state: BadgeState) { + const styles = useCAPBadgeStyles(); + state.root.className = mergeClasses(state.root.className, styles.root); + return state; +} diff --git a/packages/react-components/react-visual-refresh-preview/library/src/components/CAPCard.tsx b/packages/react-components/react-visual-refresh-preview/library/src/components/CAPCard.tsx new file mode 100644 index 00000000000000..f88a12463b2ec8 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/library/src/components/CAPCard.tsx @@ -0,0 +1,12 @@ +import { makeStyles, mergeClasses } from '@griffel/react'; +import { CardState } from '@fluentui/react-components'; + +const useCAPCardStyles = makeStyles({ + root: {}, +}); + +export function useCAPCardStylesHook(state: CardState) { + const styles = useCAPCardStyles(); + state.root.className = mergeClasses(state.root.className, styles.root); + return state; +} diff --git a/packages/react-components/react-visual-refresh-preview/library/src/components/CAPInput.tsx b/packages/react-components/react-visual-refresh-preview/library/src/components/CAPInput.tsx new file mode 100644 index 00000000000000..ee50d5f766e763 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/library/src/components/CAPInput.tsx @@ -0,0 +1,12 @@ +import { makeStyles, mergeClasses } from '@griffel/react'; +import { InputState } from '@fluentui/react-components'; + +const useCAPInputStyles = makeStyles({ + root: {}, +}); + +export function useCAPInputStylesHook(state: InputState) { + const styles = useCAPInputStyles(); + state.root.className = mergeClasses(state.root.className, styles.root); + return state; +} diff --git a/packages/react-components/react-visual-refresh-preview/library/src/components/CAPMenu.tsx b/packages/react-components/react-visual-refresh-preview/library/src/components/CAPMenu.tsx new file mode 100644 index 00000000000000..0c8bbedf8cb039 --- /dev/null +++ b/packages/react-components/react-visual-refresh-preview/library/src/components/CAPMenu.tsx @@ -0,0 +1,5 @@ +import { MenuState } from '@fluentui/react-components'; + +export function useCAPMenuStylesHook(state: MenuState) { + return state; +} diff --git a/packages/react-components/react-visual-refresh-preview/stories/src/CAPInput.stories.tsx b/packages/react-components/react-visual-refresh-preview/stories/src/CAPInput.stories.tsx index c1a2ed23f685d1..5e67c2c2a54f4f 100644 --- a/packages/react-components/react-visual-refresh-preview/stories/src/CAPInput.stories.tsx +++ b/packages/react-components/react-visual-refresh-preview/stories/src/CAPInput.stories.tsx @@ -1,6 +1,18 @@ import * as React from 'react'; +import { Input } from '@fluentui/react-components'; import { VisualRefreshExamples } from './StorybookUtil'; export const CAPInputStory = () => { - return ; + return ( + ; + }, + }, + ]} + /> + ); }; diff --git a/packages/react-components/react-visual-refresh-preview/stories/src/StorybookUtil.tsx b/packages/react-components/react-visual-refresh-preview/stories/src/StorybookUtil.tsx index 2d03c88645b636..99d7204faef0f2 100644 --- a/packages/react-components/react-visual-refresh-preview/stories/src/StorybookUtil.tsx +++ b/packages/react-components/react-visual-refresh-preview/stories/src/StorybookUtil.tsx @@ -18,6 +18,7 @@ export const VisualRefreshExamples = ({ examples }: { examples: VisualRefreshExa
+
Example
Current
Visual Refresh (Teams)
Visual Refresh (OneDrive)
@@ -26,6 +27,7 @@ export const VisualRefreshExamples = ({ examples }: { examples: VisualRefreshExa {examples.map(example => { return (
+
{example.title}
{renderExample(example, 'v9')}
From b3923d2008aff31d53564e29b0ebcfdfcb4abe1f Mon Sep 17 00:00:00 2001 From: Yi Zhang Date: Tue, 11 Nov 2025 16:23:37 -0800 Subject: [PATCH 04/14] add visual update toggle to storybook --- .../src/Button/ButtonAppearance.stories.tsx | 3 ++ .../src/components/VisualUpdateToggle.tsx | 28 +++++++++++++++++++ .../react-storybook-addon/src/constants.ts | 1 + .../src/decorators/withVisualUpdate.tsx | 27 ++++++++++++++++++ .../react-storybook-addon/src/hooks.ts | 5 ++-- .../react-storybook-addon/src/index.ts | 2 +- .../src/preset/manager.ts | 10 ++++++- .../src/preset/preview.ts | 17 +++++++++-- .../library/src/CAPTheme.tsx | 2 +- 9 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 packages/react-components/react-storybook-addon/src/components/VisualUpdateToggle.tsx create mode 100644 packages/react-components/react-storybook-addon/src/decorators/withVisualUpdate.tsx diff --git a/packages/react-components/react-button/stories/src/Button/ButtonAppearance.stories.tsx b/packages/react-components/react-button/stories/src/Button/ButtonAppearance.stories.tsx index 2f37e55d501842..439821a3448da9 100644 --- a/packages/react-components/react-button/stories/src/Button/ButtonAppearance.stories.tsx +++ b/packages/react-components/react-button/stories/src/Button/ButtonAppearance.stories.tsx @@ -30,6 +30,9 @@ export const Appearance = (): JSXElement => { +
); }; diff --git a/packages/react-components/react-storybook-addon/src/components/VisualUpdateToggle.tsx b/packages/react-components/react-storybook-addon/src/components/VisualUpdateToggle.tsx new file mode 100644 index 00000000000000..99b77ccaaf41b5 --- /dev/null +++ b/packages/react-components/react-storybook-addon/src/components/VisualUpdateToggle.tsx @@ -0,0 +1,28 @@ +import * as React from 'react'; +import { IconButton } from '@storybook/components'; +import { EyeIcon } from '@storybook/icons'; + +import type { JSXElement } from '@fluentui/react-utilities'; +import { VISUAL_UPDATE_ID } from '../constants'; +import { useGlobals } from '../hooks'; + +export const VisualUpdateToggle = (): JSXElement => { + const [globals, updateGlobals] = useGlobals(); + + const isActive = globals[VISUAL_UPDATE_ID] ?? false; + + const toggleVisualUpdate = React.useCallback( + () => + updateGlobals({ + [VISUAL_UPDATE_ID]: !isActive, + }), + [isActive, updateGlobals], + ); + + return ( + + + Visual update + + ); +}; \ No newline at end of file diff --git a/packages/react-components/react-storybook-addon/src/constants.ts b/packages/react-components/react-storybook-addon/src/constants.ts index 277499ae0c1693..51e0a29fa38add 100644 --- a/packages/react-components/react-storybook-addon/src/constants.ts +++ b/packages/react-components/react-storybook-addon/src/constants.ts @@ -3,3 +3,4 @@ export const ADDON_ID = 'storybook_fluentui-react-addon'; export const DIR_ID = `${ADDON_ID}_dir` as const; export const STRICT_MODE_ID = `${ADDON_ID}_strict-mode` as const; export const THEME_ID = `${ADDON_ID}_theme` as const; +export const VISUAL_UPDATE_ID = `${ADDON_ID}_visual-update` as const; diff --git a/packages/react-components/react-storybook-addon/src/decorators/withVisualUpdate.tsx b/packages/react-components/react-storybook-addon/src/decorators/withVisualUpdate.tsx new file mode 100644 index 00000000000000..630975d20681e6 --- /dev/null +++ b/packages/react-components/react-storybook-addon/src/decorators/withVisualUpdate.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; + +import type { JSXElement } from '@fluentui/react-utilities'; +import { CAPThemeProvider, CAP_THEME_TEAMS } from '@fluentui/react-visual-refresh-preview'; +import { VISUAL_UPDATE_ID } from '../constants'; +import { FluentStoryContext } from '../hooks'; +import { isDecoratorDisabled } from '../utils/isDecoratorDisabled'; + +export const withVisualUpdate = (StoryFn: () => JSXElement, context: FluentStoryContext): JSXElement => { + const { globals } = context; + + if (isDecoratorDisabled(context, 'VisualUpdate')) { + return StoryFn(); + } + + const isVisualUpdateEnabled = globals[VISUAL_UPDATE_ID] ?? false; + + if (isVisualUpdateEnabled) { + return ( + + {StoryFn()} + + ); + } + + return StoryFn(); +}; \ No newline at end of file diff --git a/packages/react-components/react-storybook-addon/src/hooks.ts b/packages/react-components/react-storybook-addon/src/hooks.ts index 6634e91f6779c2..29ed7280259a41 100644 --- a/packages/react-components/react-storybook-addon/src/hooks.ts +++ b/packages/react-components/react-storybook-addon/src/hooks.ts @@ -1,7 +1,7 @@ import { useGlobals as useStorybookGlobals } from '@storybook/manager-api'; import { Args as StorybookArgs, StoryContext as StorybookContext, Parameters } from '@storybook/react'; -import { DIR_ID, STRICT_MODE_ID, THEME_ID } from './constants'; +import { DIR_ID, STRICT_MODE_ID, THEME_ID, VISUAL_UPDATE_ID } from './constants'; import type { ThemeIds } from './theme'; export interface FluentStoryContext extends StorybookContext { @@ -16,6 +16,7 @@ export interface FluentGlobals extends StorybookArgs { [DIR_ID]?: 'ltr' | 'rtl'; [THEME_ID]?: ThemeIds; [STRICT_MODE_ID]?: boolean; + [VISUAL_UPDATE_ID]?: boolean; } /** @@ -26,7 +27,7 @@ export interface FluentParameters extends Parameters { fluentTheme?: ThemeIds; mode?: 'default' | 'vr-test'; reactStorybookAddon?: { - disabledDecorators?: ['AriaLive' | 'FluentProvider' | 'ReactStrictMode']; + disabledDecorators?: ['AriaLive' | 'FluentProvider' | 'ReactStrictMode' | 'VisualUpdate']; docs?: FluentDocsConfig; }; } diff --git a/packages/react-components/react-storybook-addon/src/index.ts b/packages/react-components/react-storybook-addon/src/index.ts index b1c71cf2823b0c..dc13225e481c50 100644 --- a/packages/react-components/react-storybook-addon/src/index.ts +++ b/packages/react-components/react-storybook-addon/src/index.ts @@ -1,6 +1,6 @@ export type { FluentGlobals, FluentParameters, FluentStoryContext } from './hooks'; export type { ThemeIds } from './theme'; export { themes } from './theme'; -export { DIR_ID, THEME_ID } from './constants'; +export { DIR_ID, THEME_ID, VISUAL_UPDATE_ID } from './constants'; export { parameters } from './hooks'; export { FluentCanvas, FluentStory } from './docs'; diff --git a/packages/react-components/react-storybook-addon/src/preset/manager.ts b/packages/react-components/react-storybook-addon/src/preset/manager.ts index 2f65b398ce7c2c..c6fcf3496e14d5 100644 --- a/packages/react-components/react-storybook-addon/src/preset/manager.ts +++ b/packages/react-components/react-storybook-addon/src/preset/manager.ts @@ -1,9 +1,10 @@ import { addons, types } from '@storybook/manager-api'; -import { ADDON_ID, DIR_ID, STRICT_MODE_ID, THEME_ID } from '../constants'; +import { ADDON_ID, DIR_ID, STRICT_MODE_ID, THEME_ID, VISUAL_UPDATE_ID } from '../constants'; import { ThemePicker } from '../components/ThemePicker'; import { ReactStrictMode } from '../components/ReactStrictMode'; import { DirectionSwitch } from '../components/DirectionSwitch'; +import { VisualUpdateToggle } from '../components/VisualUpdateToggle'; addons.register(ADDON_ID, () => { addons.add(THEME_ID, { @@ -13,6 +14,13 @@ addons.register(ADDON_ID, () => { match: ({ viewMode }) => !!(viewMode && viewMode.match(/^(story|docs)$/)), render: ThemePicker, }); + addons.add(VISUAL_UPDATE_ID, { + title: 'Visual Update Toggle', + + type: types.TOOL, + match: ({ viewMode }) => !!(viewMode && viewMode.match(/^(story|docs)$/)), + render: VisualUpdateToggle, + }); addons.add(DIR_ID, { title: 'Direction Switch', diff --git a/packages/react-components/react-storybook-addon/src/preset/preview.ts b/packages/react-components/react-storybook-addon/src/preset/preview.ts index cfac98475f71b4..bcb5ed3c5db090 100644 --- a/packages/react-components/react-storybook-addon/src/preset/preview.ts +++ b/packages/react-components/react-storybook-addon/src/preset/preview.ts @@ -3,13 +3,24 @@ import type { Preview } from '@storybook/react'; import { withFluentProvider } from '../decorators/withFluentProvider'; import { withReactStrictMode } from '../decorators/withReactStrictMode'; import { withAriaLive } from '../decorators/withAriaLive'; +import { withVisualUpdate } from '../decorators/withVisualUpdate'; import { FluentDocsContainer, FluentDocsPage } from '../docs'; -import { DIR_ID, STRICT_MODE_ID, THEME_ID } from '../constants'; +import { DIR_ID, STRICT_MODE_ID, THEME_ID, VISUAL_UPDATE_ID } from '../constants'; -export const decorators = [withFluentProvider, withAriaLive, withReactStrictMode] as Preview['decorators']; +export const decorators = [ + withFluentProvider, + withAriaLive, + withReactStrictMode, + withVisualUpdate, +] as Preview['decorators']; -export const initialGlobals = { [THEME_ID]: undefined, [DIR_ID]: undefined, [STRICT_MODE_ID]: undefined }; // allow theme to be set by URL query param +export const initialGlobals = { + [THEME_ID]: undefined, + [DIR_ID]: undefined, + [STRICT_MODE_ID]: undefined, + [VISUAL_UPDATE_ID]: undefined, +}; // allow theme to be set by URL query param const preview: Preview = { decorators, diff --git a/packages/react-components/react-visual-refresh-preview/library/src/CAPTheme.tsx b/packages/react-components/react-visual-refresh-preview/library/src/CAPTheme.tsx index acf367164f7318..be4294a2abdfed 100644 --- a/packages/react-components/react-visual-refresh-preview/library/src/CAPTheme.tsx +++ b/packages/react-components/react-visual-refresh-preview/library/src/CAPTheme.tsx @@ -64,7 +64,7 @@ export const CAP_THEME = { buttonSubtleBackgroundColorHover: tokens.colorBrandBackground, buttonOutlineBackgroundColor: tokens.colorTransparentBackground, buttonOutlineBackgroundColorHover: tokens.colorTransparentBackground, - buttonTintBackgroundColor: '', + buttonTintBackgroundColor: 'red', buttonTintBackgroundColorHover: '', } as const satisfies CAPTheme; From eb35aeafd0fbc22395a719a6637ec66438b0d8d5 Mon Sep 17 00:00:00 2001 From: Yi Zhang Date: Tue, 18 Nov 2025 17:21:56 -0800 Subject: [PATCH 05/14] update horizontal padding and gap for badges --- .../library/src/components/CAPBadge.tsx | 57 ++++++++++++++++- .../stories/src/CAPBadge.stories.tsx | 61 ++++++++++++++++++- 2 files changed, 113 insertions(+), 5 deletions(-) diff --git a/packages/react-components/react-visual-refresh-preview/library/src/components/CAPBadge.tsx b/packages/react-components/react-visual-refresh-preview/library/src/components/CAPBadge.tsx index 415170a7c0cf8d..94ce46f5b13d6a 100644 --- a/packages/react-components/react-visual-refresh-preview/library/src/components/CAPBadge.tsx +++ b/packages/react-components/react-visual-refresh-preview/library/src/components/CAPBadge.tsx @@ -1,12 +1,63 @@ import { makeStyles, mergeClasses } from '@griffel/react'; -import { BadgeState } from '@fluentui/react-components'; +import { BadgeState, useBadgeStyles_unstable } from '@fluentui/react-components'; +import { tokens } from '@fluentui/react-theme'; + +const textPadding = tokens.spacingHorizontalXXS; const useCAPBadgeStyles = makeStyles({ - root: {}, + root: { + padding: `0 calc(${tokens.spacingHorizontalSNudge} + ${textPadding})`, + }, + + tiny: { + padding: 'unset', + }, + + 'extra-small': { + padding: 'unset', + }, + + small: { + padding: `0 calc(${tokens.spacingHorizontalXS} + ${textPadding})`, + }, + + medium: { + // Set by root + }, + + large: { + padding: `0 calc(${tokens.spacingHorizontalSNudge} + ${textPadding})`, + }, + + 'extra-large': { + padding: `0 calc(${tokens.spacingHorizontalS} + ${textPadding})`, + }, +}); + +const useCAPBadgeIconStyles = makeStyles({ + beforeTextSmall: { + marginRight: textPadding, + }, + afterTextSmall: { + marginLeft: textPadding, + }, }); export function useCAPBadgeStylesHook(state: BadgeState) { + // Apply base Badge styles first + useBadgeStyles_unstable(state); + + // Then override with CAP styles const styles = useCAPBadgeStyles(); - state.root.className = mergeClasses(state.root.className, styles.root); + const iconStyles = useCAPBadgeIconStyles(); + + state.root.className = mergeClasses(state.root.className, styles.root, styles[state.size]); + + // Override icon spacing for small size + if (state.icon && state.size === 'small') { + const iconPositionClass = state.iconPosition === 'after' ? iconStyles.afterTextSmall : iconStyles.beforeTextSmall; + state.icon.className = mergeClasses(state.icon.className, iconPositionClass); + } + return state; } diff --git a/packages/react-components/react-visual-refresh-preview/stories/src/CAPBadge.stories.tsx b/packages/react-components/react-visual-refresh-preview/stories/src/CAPBadge.stories.tsx index 37a95d46ed0625..d4ee72121e3cca 100644 --- a/packages/react-components/react-visual-refresh-preview/stories/src/CAPBadge.stories.tsx +++ b/packages/react-components/react-visual-refresh-preview/stories/src/CAPBadge.stories.tsx @@ -1,15 +1,72 @@ import * as React from 'react'; import { Badge } from '@fluentui/react-components'; import { VisualRefreshExamples } from './StorybookUtil'; +import { CircleRegular } from '@fluentui/react-icons'; export const CAPBadgeStory = () => { return ( ; + return ( + }> + Badge + + ); + }, + }, + { + title: 'Extra Small', + render() { + return ( + }> + Badge + + ); + }, + }, + { + title: 'Small', + render() { + return ( + }> + Badge + + ); + }, + }, + { + title: 'Default/Medium', + render() { + return }> Badge; + }, + }, + { + title: 'Medium No Icon', + render() { + return Badge; + }, + }, + { + title: 'Large', + render() { + return ( + }> + Badge + + ); + }, + }, + { + title: 'Extra Large', + render() { + return ( + }> + Badge + + ); }, }, ]} From 041db328464aa8937b7d66c2ee61fea5782318b1 Mon Sep 17 00:00:00 2001 From: Yi Zhang Date: Tue, 18 Nov 2025 17:48:37 -0800 Subject: [PATCH 06/14] border radius for rounded --- .../library/src/components/CAPBadge.tsx | 15 +- .../stories/src/CAPBadge.stories.tsx | 130 ++++++++++++++++-- 2 files changed, 134 insertions(+), 11 deletions(-) diff --git a/packages/react-components/react-visual-refresh-preview/library/src/components/CAPBadge.tsx b/packages/react-components/react-visual-refresh-preview/library/src/components/CAPBadge.tsx index 94ce46f5b13d6a..4724098ff1a18b 100644 --- a/packages/react-components/react-visual-refresh-preview/library/src/components/CAPBadge.tsx +++ b/packages/react-components/react-visual-refresh-preview/library/src/components/CAPBadge.tsx @@ -32,6 +32,14 @@ const useCAPBadgeStyles = makeStyles({ 'extra-large': { padding: `0 calc(${tokens.spacingHorizontalS} + ${textPadding})`, }, + + // shape + 'rounded-extra-large': { borderRadius: tokens.borderRadiusXLarge }, + 'rounded-large': { borderRadius: tokens.borderRadiusLarge }, + 'rounded-medium': { borderRadius: tokens.borderRadiusMedium }, + 'rounded-small': { borderRadius: tokens.borderRadiusMedium }, + 'rounded-extra-small': { borderRadius: tokens.borderRadiusSmall }, + 'rounded-tiny': { borderRadius: tokens.borderRadiusSmall }, }); const useCAPBadgeIconStyles = makeStyles({ @@ -51,7 +59,12 @@ export function useCAPBadgeStylesHook(state: BadgeState) { const styles = useCAPBadgeStyles(); const iconStyles = useCAPBadgeIconStyles(); - state.root.className = mergeClasses(state.root.className, styles.root, styles[state.size]); + state.root.className = mergeClasses( + state.root.className, + styles.root, + styles[state.size], + state.shape === 'rounded' && styles[`rounded-${state.size}`], + ); // Override icon spacing for small size if (state.icon && state.size === 'small') { diff --git a/packages/react-components/react-visual-refresh-preview/stories/src/CAPBadge.stories.tsx b/packages/react-components/react-visual-refresh-preview/stories/src/CAPBadge.stories.tsx index d4ee72121e3cca..701d2513ebfdee 100644 --- a/packages/react-components/react-visual-refresh-preview/stories/src/CAPBadge.stories.tsx +++ b/packages/react-components/react-visual-refresh-preview/stories/src/CAPBadge.stories.tsx @@ -10,21 +10,13 @@ export const CAPBadgeStory = () => { { title: 'Tiny', render() { - return ( - }> - Badge - - ); + return } />; }, }, { title: 'Extra Small', render() { - return ( - }> - Badge - - ); + return } />; }, }, { @@ -69,6 +61,124 @@ export const CAPBadgeStory = () => { ); }, }, + { + title: 'Tiny Square', + render() { + return } shape="square" />; + }, + }, + { + title: 'Extra Small Square', + render() { + return } shape="square" />; + }, + }, + { + title: 'Small Square', + render() { + return ( + } shape="square"> + Badge + + ); + }, + }, + { + title: 'Default/Medium Square', + render() { + return ( + } shape="square"> + {' '} + Badge + + ); + }, + }, + { + title: 'Medium No Icon Square', + render() { + return Badge; + }, + }, + { + title: 'Large Square', + render() { + return ( + } shape="square"> + Badge + + ); + }, + }, + { + title: 'Extra Large Square', + render() { + return ( + } shape="square"> + Badge + + ); + }, + }, + { + title: 'Tiny Rounded', + render() { + return } shape="rounded" />; + }, + }, + { + title: 'Extra Small Rounded ', + render() { + return } shape="rounded" />; + }, + }, + { + title: 'Small Rounded', + render() { + return ( + } shape="rounded"> + Badge + + ); + }, + }, + { + title: 'Default/Medium Rounded', + render() { + return ( + } shape="rounded"> + {' '} + Badge + + ); + }, + }, + { + title: 'Medium No Icon Rounded', + render() { + return Badge; + }, + }, + { + title: 'Large Rounded', + render() { + return ( + } shape="rounded"> + Badge + + ); + }, + }, + { + title: 'Extra Large Rounded', + render() { + return ( + } shape="rounded"> + Badge + + ); + }, + }, ]} /> ); From f4765ffffba840d386e5f782bef4058eea71a11e Mon Sep 17 00:00:00 2001 From: Yi Zhang Date: Wed, 19 Nov 2025 14:58:37 -0800 Subject: [PATCH 07/14] add outline color changes --- .../library/src/components/CAPBadge.tsx | 28 ++++++- .../stories/src/CAPBadge.stories.tsx | 80 +++++++++++++++++++ 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/packages/react-components/react-visual-refresh-preview/library/src/components/CAPBadge.tsx b/packages/react-components/react-visual-refresh-preview/library/src/components/CAPBadge.tsx index 4724098ff1a18b..407b06b77d2995 100644 --- a/packages/react-components/react-visual-refresh-preview/library/src/components/CAPBadge.tsx +++ b/packages/react-components/react-visual-refresh-preview/library/src/components/CAPBadge.tsx @@ -1,4 +1,4 @@ -import { makeStyles, mergeClasses } from '@griffel/react'; +import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; import { BadgeState, useBadgeStyles_unstable } from '@fluentui/react-components'; import { tokens } from '@fluentui/react-theme'; @@ -40,6 +40,31 @@ const useCAPBadgeStyles = makeStyles({ 'rounded-small': { borderRadius: tokens.borderRadiusMedium }, 'rounded-extra-small': { borderRadius: tokens.borderRadiusSmall }, 'rounded-tiny': { borderRadius: tokens.borderRadiusSmall }, + + 'outline-brand': { + ...shorthands.borderColor(tokens.colorBrandStroke2), + }, + 'outline-warning': { + ...shorthands.borderColor(tokens.colorStatusWarningBorder1), + }, + 'outline-important': { + ...shorthands.borderColor(tokens.colorNeutralStroke1), + }, + 'outline-danger': { + ...shorthands.borderColor(tokens.colorStatusDangerBorder1), + }, + 'outline-success': { + ...shorthands.borderColor(tokens.colorStatusSuccessBorder1), + }, + 'outline-informative': { + ...shorthands.borderColor(tokens.colorNeutralStroke1), + }, + 'outline-subtle': { + ...shorthands.borderColor(tokens.colorNeutralForegroundOnBrand), + }, + 'outline-severe': { + ...shorthands.borderColor(tokens.colorPaletteDarkOrangeBorder2), + }, }); const useCAPBadgeIconStyles = makeStyles({ @@ -64,6 +89,7 @@ export function useCAPBadgeStylesHook(state: BadgeState) { styles.root, styles[state.size], state.shape === 'rounded' && styles[`rounded-${state.size}`], + state.appearance === 'outline' && styles[`outline-${state.color}`], ); // Override icon spacing for small size diff --git a/packages/react-components/react-visual-refresh-preview/stories/src/CAPBadge.stories.tsx b/packages/react-components/react-visual-refresh-preview/stories/src/CAPBadge.stories.tsx index 701d2513ebfdee..39d35a14eb3236 100644 --- a/packages/react-components/react-visual-refresh-preview/stories/src/CAPBadge.stories.tsx +++ b/packages/react-components/react-visual-refresh-preview/stories/src/CAPBadge.stories.tsx @@ -179,6 +179,86 @@ export const CAPBadgeStory = () => { ); }, }, + { + title: 'Brand / Outline / Important', + render() { + return ( + } color="important" appearance="outline"> + Badge + + ); + }, + }, + { + title: 'Brand / Outline / Brand', + render() { + return ( + } color="brand" appearance="outline"> + Badge + + ); + }, + }, + { + title: 'Brand / Outline / Danger', + render() { + return ( + } color="danger" appearance="outline"> + Badge + + ); + }, + }, + { + title: 'Brand / Outline / Warning', + render() { + return ( + } color="warning" appearance="outline"> + Badge + + ); + }, + }, + { + title: 'Brand / Outline / Success', + render() { + return ( + } color="success" appearance="outline"> + Badge + + ); + }, + }, + { + title: 'Brand / Outline / Informative', + render() { + return ( + } color="informative" appearance="outline"> + Badge + + ); + }, + }, + { + title: 'Brand / Outline / Subtle', + render() { + return ( + } color="subtle" appearance="outline"> + Badge + + ); + }, + }, + { + title: 'Brand / Outline / Severe', + render() { + return ( + } color="severe" appearance="outline"> + Badge + + ); + }, + }, ]} /> ); From 0d9ce03eb24a6ea5d27f1f7a8b202543c3c86679 Mon Sep 17 00:00:00 2001 From: Yi Zhang Date: Wed, 19 Nov 2025 17:38:15 -0800 Subject: [PATCH 08/14] first pass of all color changes --- .../library/src/CAPTheme.tsx | 6 +- .../library/src/components/CAPBadge.tsx | 43 ++++++++- .../stories/src/CAPBadge.stories.tsx | 96 +++++++++++++++++-- 3 files changed, 133 insertions(+), 12 deletions(-) diff --git a/packages/react-components/react-visual-refresh-preview/library/src/CAPTheme.tsx b/packages/react-components/react-visual-refresh-preview/library/src/CAPTheme.tsx index be4294a2abdfed..fbe6f512f0f8d1 100644 --- a/packages/react-components/react-visual-refresh-preview/library/src/CAPTheme.tsx +++ b/packages/react-components/react-visual-refresh-preview/library/src/CAPTheme.tsx @@ -10,7 +10,9 @@ export interface TokenSchema { const ColorTokenSchema = { type: 'color' } as const satisfies TokenSchema; -const BadgeTokens = {} as const satisfies Record; +const BadgeTokens = { + colorBrandForegroundCompound: ColorTokenSchema, +} as const satisfies Record; const ButtonTokens = { buttonPrimaryBackgroundColor: ColorTokenSchema, @@ -66,6 +68,8 @@ export const CAP_THEME = { buttonOutlineBackgroundColorHover: tokens.colorTransparentBackground, buttonTintBackgroundColor: 'red', buttonTintBackgroundColorHover: '', + // TODO: switch to BrandForegroundCompound when available + colorBrandForegroundCompound: tokens.colorBrandForeground1, } as const satisfies CAPTheme; export const CAP_THEME_TEAMS = { diff --git a/packages/react-components/react-visual-refresh-preview/library/src/components/CAPBadge.tsx b/packages/react-components/react-visual-refresh-preview/library/src/components/CAPBadge.tsx index 407b06b77d2995..8c89c7184390ab 100644 --- a/packages/react-components/react-visual-refresh-preview/library/src/components/CAPBadge.tsx +++ b/packages/react-components/react-visual-refresh-preview/library/src/components/CAPBadge.tsx @@ -1,6 +1,7 @@ import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; import { BadgeState, useBadgeStyles_unstable } from '@fluentui/react-components'; import { tokens } from '@fluentui/react-theme'; +import { CAP_THEME } from '../CAPTheme'; const textPadding = tokens.spacingHorizontalXXS; @@ -62,8 +63,43 @@ const useCAPBadgeStyles = makeStyles({ 'outline-subtle': { ...shorthands.borderColor(tokens.colorNeutralForegroundOnBrand), }, - 'outline-severe': { - ...shorthands.borderColor(tokens.colorPaletteDarkOrangeBorder2), + + 'tint-brand': { + color: CAP_THEME.colorBrandForegroundCompound, + }, + + 'ghost-brand': { + color: CAP_THEME.colorBrandForegroundCompound, + }, + + 'filled-warning': { + color: tokens.colorNeutralForegroundOnBrand, + backgroundColor: tokens.colorStatusWarningBackground3, + }, + + 'tint-informative': { + backgroundColor: tokens.colorNeutralBackground5, + ...shorthands.borderColor(tokens.colorNeutralStroke1), + }, + + 'filled-important': { + backgroundColor: tokens.colorNeutralBackgroundInverted, + color: tokens.colorNeutralForegroundOnBrand, + }, + + 'tint-important': { + backgroundColor: tokens.colorNeutralBackground5, + color: tokens.colorNeutralForeground3, + ...shorthands.borderColor(tokens.colorNeutralStroke1), + }, + + 'filled-subtle': { + color: tokens.colorNeutralForeground3, + backgroundColor: tokens.colorNeutralBackground5, + }, + + 'tint-subtle': { + ...shorthands.borderColor(tokens.colorNeutralStroke1), }, }); @@ -89,7 +125,8 @@ export function useCAPBadgeStylesHook(state: BadgeState) { styles.root, styles[state.size], state.shape === 'rounded' && styles[`rounded-${state.size}`], - state.appearance === 'outline' && styles[`outline-${state.color}`], + `${state.appearance}-${state.color}` in styles && + styles[`${state.appearance}-${state.color}` as keyof typeof styles], ); // Override icon spacing for small size diff --git a/packages/react-components/react-visual-refresh-preview/stories/src/CAPBadge.stories.tsx b/packages/react-components/react-visual-refresh-preview/stories/src/CAPBadge.stories.tsx index 39d35a14eb3236..47704b0bb85cfd 100644 --- a/packages/react-components/react-visual-refresh-preview/stories/src/CAPBadge.stories.tsx +++ b/packages/react-components/react-visual-refresh-preview/stories/src/CAPBadge.stories.tsx @@ -180,7 +180,7 @@ export const CAPBadgeStory = () => { }, }, { - title: 'Brand / Outline / Important', + title: 'Outline / Important', render() { return ( } color="important" appearance="outline"> @@ -190,7 +190,7 @@ export const CAPBadgeStory = () => { }, }, { - title: 'Brand / Outline / Brand', + title: 'Outline / Brand', render() { return ( } color="brand" appearance="outline"> @@ -200,7 +200,7 @@ export const CAPBadgeStory = () => { }, }, { - title: 'Brand / Outline / Danger', + title: 'Outline / Danger', render() { return ( } color="danger" appearance="outline"> @@ -210,7 +210,7 @@ export const CAPBadgeStory = () => { }, }, { - title: 'Brand / Outline / Warning', + title: 'Outline / Warning', render() { return ( } color="warning" appearance="outline"> @@ -220,7 +220,7 @@ export const CAPBadgeStory = () => { }, }, { - title: 'Brand / Outline / Success', + title: 'Outline / Success', render() { return ( } color="success" appearance="outline"> @@ -230,7 +230,7 @@ export const CAPBadgeStory = () => { }, }, { - title: 'Brand / Outline / Informative', + title: 'Outline / Informative', render() { return ( } color="informative" appearance="outline"> @@ -240,7 +240,7 @@ export const CAPBadgeStory = () => { }, }, { - title: 'Brand / Outline / Subtle', + title: 'Outline / Subtle', render() { return ( } color="subtle" appearance="outline"> @@ -250,7 +250,7 @@ export const CAPBadgeStory = () => { }, }, { - title: 'Brand / Outline / Severe', + title: 'Outline / Severe', render() { return ( } color="severe" appearance="outline"> @@ -259,6 +259,86 @@ export const CAPBadgeStory = () => { ); }, }, + { + title: 'Brand / Tint', + render() { + return ( + } color="brand" appearance="tint"> + Badge + + ); + }, + }, + { + title: 'Brand / Ghost', + render() { + return ( + } color="brand" appearance="ghost"> + Badge + + ); + }, + }, + { + title: 'Warning / Filled', + render() { + return ( + } color="warning" appearance="filled"> + Badge + + ); + }, + }, + { + title: 'Informative / Tint', + render() { + return ( + } color="informative" appearance="tint"> + Badge + + ); + }, + }, + { + title: 'Important / Filled', + render() { + return ( + } color="important" appearance="filled"> + Badge + + ); + }, + }, + { + title: 'Important / Tint', + render() { + return ( + } color="important" appearance="tint"> + Badge + + ); + }, + }, + { + title: 'Subtle / Filled', + render() { + return ( + } color="subtle" appearance="filled"> + Badge + + ); + }, + }, + { + title: 'Subtle / Tint', + render() { + return ( + } color="subtle" appearance="tint"> + Badge + + ); + }, + }, ]} /> ); From af0caff412ac8b1b635ef3186c9af349b896fd8c Mon Sep 17 00:00:00 2001 From: David Zukowski Date: Thu, 20 Nov 2025 09:25:46 -0500 Subject: [PATCH 09/14] Move @fluentui/react-visual-refresh-preview to @fluentui/react-provider See https://teams.microsoft.com/l/message/19:bd5660a820c140c7aa6a1b932029e4e6@thread.v2/1763635469070?context=%7B%22contextType%22%3A%22chat%22%7D --- .github/CODEOWNERS | 4 -- .../components/CAPThemeProvider}/CAPTheme.tsx | 0 .../CAPThemeProvider}/CAPThemeProvider.tsx | 4 +- .../CAPThemeProvider}/components/CAPBadge.tsx | 0 .../components/CAPButton.tsx | 0 .../CAPThemeProvider}/components/CAPCard.tsx | 0 .../CAPThemeProvider}/components/CAPInput.tsx | 0 .../CAPThemeProvider}/components/CAPMenu.tsx | 0 .../react-provider/library/src/index.ts | 2 + .../CAPThemeProvider}/CAPBadge.stories.tsx | 4 +- .../CAPThemeProvider}/CAPButton.stories.tsx | 4 +- .../src/CAPThemeProvider/CAPCard.stories.tsx | 6 ++ .../CAPThemeProvider}/CAPInput.stories.tsx | 4 +- .../src/CAPThemeProvider/CAPMenu.stories.tsx | 6 ++ .../CAPThemeProvider/CAPStorybookUtil.tsx} | 17 ++--- .../CAPThemeProvider/CAPTooltip.stories.tsx | 6 ++ .../src/CAPThemeProvider}/index.stories.tsx | 6 +- .../src/decorators/withVisualUpdate.tsx | 10 +-- .../library/.babelrc.json | 4 -- .../library/.eslintrc.json | 4 -- .../library/.swcrc | 30 --------- .../library/LICENSE | 15 ----- .../library/README.md | 5 -- .../library/config/api-extractor.json | 5 -- .../library/config/tests.js | 1 - .../library/docs/Spec.md | 63 ------------------- .../etc/react-visual-refresh-preview.api.md | 0 .../library/jest.config.js | 34 ---------- .../library/package.json | 56 ----------------- .../library/project.json | 8 --- .../library/src/index.ts | 2 - .../library/src/testing/isConformant.ts | 15 ----- .../library/tsconfig.json | 22 ------- .../library/tsconfig.lib.json | 22 ------- .../library/tsconfig.spec.json | 17 ----- .../stories/.eslintrc.json | 12 ---- .../stories/.storybook/main.js | 14 ----- .../stories/.storybook/preview.js | 9 --- .../stories/.storybook/tsconfig.json | 10 --- .../stories/README.md | 17 ----- .../stories/package.json | 11 ---- .../stories/project.json | 8 --- .../stories/src/CAPCard.stories.tsx | 6 -- .../stories/src/CAPMenu.stories.tsx | 6 -- .../stories/src/CAPTooltip.stories.tsx | 6 -- .../stories/tsconfig.json | 22 ------- .../stories/tsconfig.lib.json | 10 --- tsconfig.base.all.json | 12 +--- tsconfig.base.json | 10 --- 49 files changed, 41 insertions(+), 488 deletions(-) rename packages/react-components/{react-visual-refresh-preview/library/src => react-provider/library/src/components/CAPThemeProvider}/CAPTheme.tsx (100%) rename packages/react-components/{react-visual-refresh-preview/library/src => react-provider/library/src/components/CAPThemeProvider}/CAPThemeProvider.tsx (86%) rename packages/react-components/{react-visual-refresh-preview/library/src => react-provider/library/src/components/CAPThemeProvider}/components/CAPBadge.tsx (100%) rename packages/react-components/{react-visual-refresh-preview/library/src => react-provider/library/src/components/CAPThemeProvider}/components/CAPButton.tsx (100%) rename packages/react-components/{react-visual-refresh-preview/library/src => react-provider/library/src/components/CAPThemeProvider}/components/CAPCard.tsx (100%) rename packages/react-components/{react-visual-refresh-preview/library/src => react-provider/library/src/components/CAPThemeProvider}/components/CAPInput.tsx (100%) rename packages/react-components/{react-visual-refresh-preview/library/src => react-provider/library/src/components/CAPThemeProvider}/components/CAPMenu.tsx (100%) rename packages/react-components/{react-visual-refresh-preview/stories/src => react-provider/stories/src/CAPThemeProvider}/CAPBadge.stories.tsx (99%) rename packages/react-components/{react-visual-refresh-preview/stories/src => react-provider/stories/src/CAPThemeProvider}/CAPButton.stories.tsx (95%) create mode 100644 packages/react-components/react-provider/stories/src/CAPThemeProvider/CAPCard.stories.tsx rename packages/react-components/{react-visual-refresh-preview/stories/src => react-provider/stories/src/CAPThemeProvider}/CAPInput.stories.tsx (79%) create mode 100644 packages/react-components/react-provider/stories/src/CAPThemeProvider/CAPMenu.stories.tsx rename packages/react-components/{react-visual-refresh-preview/stories/src/StorybookUtil.tsx => react-provider/stories/src/CAPThemeProvider/CAPStorybookUtil.tsx} (80%) create mode 100644 packages/react-components/react-provider/stories/src/CAPThemeProvider/CAPTooltip.stories.tsx rename packages/react-components/{react-visual-refresh-preview/stories/src => react-provider/stories/src/CAPThemeProvider}/index.stories.tsx (82%) delete mode 100644 packages/react-components/react-visual-refresh-preview/library/.babelrc.json delete mode 100644 packages/react-components/react-visual-refresh-preview/library/.eslintrc.json delete mode 100644 packages/react-components/react-visual-refresh-preview/library/.swcrc delete mode 100644 packages/react-components/react-visual-refresh-preview/library/LICENSE delete mode 100644 packages/react-components/react-visual-refresh-preview/library/README.md delete mode 100644 packages/react-components/react-visual-refresh-preview/library/config/api-extractor.json delete mode 100644 packages/react-components/react-visual-refresh-preview/library/config/tests.js delete mode 100644 packages/react-components/react-visual-refresh-preview/library/docs/Spec.md delete mode 100644 packages/react-components/react-visual-refresh-preview/library/etc/react-visual-refresh-preview.api.md delete mode 100644 packages/react-components/react-visual-refresh-preview/library/jest.config.js delete mode 100644 packages/react-components/react-visual-refresh-preview/library/package.json delete mode 100644 packages/react-components/react-visual-refresh-preview/library/project.json delete mode 100644 packages/react-components/react-visual-refresh-preview/library/src/index.ts delete mode 100644 packages/react-components/react-visual-refresh-preview/library/src/testing/isConformant.ts delete mode 100644 packages/react-components/react-visual-refresh-preview/library/tsconfig.json delete mode 100644 packages/react-components/react-visual-refresh-preview/library/tsconfig.lib.json delete mode 100644 packages/react-components/react-visual-refresh-preview/library/tsconfig.spec.json delete mode 100644 packages/react-components/react-visual-refresh-preview/stories/.eslintrc.json delete mode 100644 packages/react-components/react-visual-refresh-preview/stories/.storybook/main.js delete mode 100644 packages/react-components/react-visual-refresh-preview/stories/.storybook/preview.js delete mode 100644 packages/react-components/react-visual-refresh-preview/stories/.storybook/tsconfig.json delete mode 100644 packages/react-components/react-visual-refresh-preview/stories/README.md delete mode 100644 packages/react-components/react-visual-refresh-preview/stories/package.json delete mode 100644 packages/react-components/react-visual-refresh-preview/stories/project.json delete mode 100644 packages/react-components/react-visual-refresh-preview/stories/src/CAPCard.stories.tsx delete mode 100644 packages/react-components/react-visual-refresh-preview/stories/src/CAPMenu.stories.tsx delete mode 100644 packages/react-components/react-visual-refresh-preview/stories/src/CAPTooltip.stories.tsx delete mode 100644 packages/react-components/react-visual-refresh-preview/stories/tsconfig.json delete mode 100644 packages/react-components/react-visual-refresh-preview/stories/tsconfig.lib.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3f8e0cdbc10504..0b47f04896ae2f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -331,10 +331,6 @@ packages/react-components/component-selector-preview/library @microsoft/teams-pr packages/react-components/component-selector-preview/stories @microsoft/teams-prg packages/react-components/react-menu-grid-preview/library @microsoft/teams-prg packages/react-components/react-menu-grid-preview/stories @microsoft/teams-prg -packages/react-components/visual-refresh-preview/library @microsoft/teams-prg -packages/react-components/visual-refresh-preview/stories @microsoft/teams-prg -packages/react-components/react-visual-refresh-preview/library @microsoft/teams-prg -packages/react-components/react-visual-refresh-preview/stories @microsoft/teams-prg # <%= NX-CODEOWNER-PLACEHOLDER %> # Deprecated v9 packages - exposed as part of `/unstable` api diff --git a/packages/react-components/react-visual-refresh-preview/library/src/CAPTheme.tsx b/packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPTheme.tsx similarity index 100% rename from packages/react-components/react-visual-refresh-preview/library/src/CAPTheme.tsx rename to packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPTheme.tsx diff --git a/packages/react-components/react-visual-refresh-preview/library/src/CAPThemeProvider.tsx b/packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPThemeProvider.tsx similarity index 86% rename from packages/react-components/react-visual-refresh-preview/library/src/CAPThemeProvider.tsx rename to packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPThemeProvider.tsx index a384ddd7e1f267..8607f81b582341 100644 --- a/packages/react-components/react-visual-refresh-preview/library/src/CAPThemeProvider.tsx +++ b/packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPThemeProvider.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { BadgeState, ButtonState, CardState, FluentProvider, InputState, Theme } from '@fluentui/react-components'; import { useCAPButtonStylesHook } from './components/CAPButton'; -import { CustomStyleHooksContextValue } from '../../../react-shared-contexts/library/src/CustomStyleHooksContext'; +import { CustomStyleHooksContext_unstable } from '@fluentui/react-shared-contexts'; import { CAPTheme } from './CAPTheme'; import { useCAPBadgeStylesHook } from './components/CAPBadge'; import { useCAPInputStylesHook } from './components/CAPInput'; @@ -14,7 +14,7 @@ export const CAPThemeProvider = ({ children: React.ReactElement; theme: Partial & Partial; }) => { - const customStyleHooks = React.useMemo((): CustomStyleHooksContextValue => { + const customStyleHooks = React.useMemo((): React.ContextType => { return { useBadgeStyles_unstable: state => useCAPBadgeStylesHook(state as BadgeState), useButtonStyles_unstable: state => useCAPButtonStylesHook(state as ButtonState), diff --git a/packages/react-components/react-visual-refresh-preview/library/src/components/CAPBadge.tsx b/packages/react-components/react-provider/library/src/components/CAPThemeProvider/components/CAPBadge.tsx similarity index 100% rename from packages/react-components/react-visual-refresh-preview/library/src/components/CAPBadge.tsx rename to packages/react-components/react-provider/library/src/components/CAPThemeProvider/components/CAPBadge.tsx diff --git a/packages/react-components/react-visual-refresh-preview/library/src/components/CAPButton.tsx b/packages/react-components/react-provider/library/src/components/CAPThemeProvider/components/CAPButton.tsx similarity index 100% rename from packages/react-components/react-visual-refresh-preview/library/src/components/CAPButton.tsx rename to packages/react-components/react-provider/library/src/components/CAPThemeProvider/components/CAPButton.tsx diff --git a/packages/react-components/react-visual-refresh-preview/library/src/components/CAPCard.tsx b/packages/react-components/react-provider/library/src/components/CAPThemeProvider/components/CAPCard.tsx similarity index 100% rename from packages/react-components/react-visual-refresh-preview/library/src/components/CAPCard.tsx rename to packages/react-components/react-provider/library/src/components/CAPThemeProvider/components/CAPCard.tsx diff --git a/packages/react-components/react-visual-refresh-preview/library/src/components/CAPInput.tsx b/packages/react-components/react-provider/library/src/components/CAPThemeProvider/components/CAPInput.tsx similarity index 100% rename from packages/react-components/react-visual-refresh-preview/library/src/components/CAPInput.tsx rename to packages/react-components/react-provider/library/src/components/CAPThemeProvider/components/CAPInput.tsx diff --git a/packages/react-components/react-visual-refresh-preview/library/src/components/CAPMenu.tsx b/packages/react-components/react-provider/library/src/components/CAPThemeProvider/components/CAPMenu.tsx similarity index 100% rename from packages/react-components/react-visual-refresh-preview/library/src/components/CAPMenu.tsx rename to packages/react-components/react-provider/library/src/components/CAPThemeProvider/components/CAPMenu.tsx diff --git a/packages/react-components/react-provider/library/src/index.ts b/packages/react-components/react-provider/library/src/index.ts index 5062bf63333163..070338fc47802c 100644 --- a/packages/react-components/react-provider/library/src/index.ts +++ b/packages/react-components/react-provider/library/src/index.ts @@ -15,3 +15,5 @@ export type { FluentProviderState, FluentProviderSlots, } from './FluentProvider'; +export { CAPThemeProvider } from './components/CAPThemeProvider/CAPThemeProvider'; +export { CAP_THEME_ONE_DRIVE, CAP_THEME_SHAREPOINT, CAP_THEME_TEAMS } from './components/CAPThemeProvider/CAPTheme'; diff --git a/packages/react-components/react-visual-refresh-preview/stories/src/CAPBadge.stories.tsx b/packages/react-components/react-provider/stories/src/CAPThemeProvider/CAPBadge.stories.tsx similarity index 99% rename from packages/react-components/react-visual-refresh-preview/stories/src/CAPBadge.stories.tsx rename to packages/react-components/react-provider/stories/src/CAPThemeProvider/CAPBadge.stories.tsx index 47704b0bb85cfd..e2f786d5bd313b 100644 --- a/packages/react-components/react-visual-refresh-preview/stories/src/CAPBadge.stories.tsx +++ b/packages/react-components/react-provider/stories/src/CAPThemeProvider/CAPBadge.stories.tsx @@ -1,11 +1,11 @@ import * as React from 'react'; import { Badge } from '@fluentui/react-components'; -import { VisualRefreshExamples } from './StorybookUtil'; +import { CAPThemeExamples } from './CAPStorybookUtil'; import { CircleRegular } from '@fluentui/react-icons'; export const CAPBadgeStory = () => { return ( - { return ( - { + return ; +}; diff --git a/packages/react-components/react-visual-refresh-preview/stories/src/CAPInput.stories.tsx b/packages/react-components/react-provider/stories/src/CAPThemeProvider/CAPInput.stories.tsx similarity index 79% rename from packages/react-components/react-visual-refresh-preview/stories/src/CAPInput.stories.tsx rename to packages/react-components/react-provider/stories/src/CAPThemeProvider/CAPInput.stories.tsx index 5e67c2c2a54f4f..57267a166ed79d 100644 --- a/packages/react-components/react-visual-refresh-preview/stories/src/CAPInput.stories.tsx +++ b/packages/react-components/react-provider/stories/src/CAPThemeProvider/CAPInput.stories.tsx @@ -1,10 +1,10 @@ import * as React from 'react'; import { Input } from '@fluentui/react-components'; -import { VisualRefreshExamples } from './StorybookUtil'; +import { CAPThemeExamples } from './CAPStorybookUtil'; export const CAPInputStory = () => { return ( - { + return ; +}; diff --git a/packages/react-components/react-visual-refresh-preview/stories/src/StorybookUtil.tsx b/packages/react-components/react-provider/stories/src/CAPThemeProvider/CAPStorybookUtil.tsx similarity index 80% rename from packages/react-components/react-visual-refresh-preview/stories/src/StorybookUtil.tsx rename to packages/react-components/react-provider/stories/src/CAPThemeProvider/CAPStorybookUtil.tsx index 99d7204faef0f2..4e62aaea20423c 100644 --- a/packages/react-components/react-visual-refresh-preview/stories/src/StorybookUtil.tsx +++ b/packages/react-components/react-provider/stories/src/CAPThemeProvider/CAPStorybookUtil.tsx @@ -1,19 +1,14 @@ import * as React from 'react'; import { FluentProvider, makeStyles, teamsLightV21Theme } from '@fluentui/react-components'; -import { - CAPThemeProvider, - CAP_THEME_ONE_DRIVE, - CAP_THEME_SHAREPOINT, - CAP_THEME_TEAMS, -} from '@fluentui/react-visual-refresh-preview'; +import { CAPThemeProvider, CAP_THEME_ONE_DRIVE, CAP_THEME_SHAREPOINT, CAP_THEME_TEAMS } from '@fluentui/react-provider'; -export interface VisualRefreshExample { +export interface CAPThemeExample { title: string; render(variant: 'v9' | 'cap'): React.ReactElement | 'NOT_IMPLEMENTED'; } -export const VisualRefreshExamples = ({ examples }: { examples: VisualRefreshExample[] }) => { - const styles = useVisualRefreshExamplesStyles(); +export const CAPThemeExamples = ({ examples }: { examples: CAPThemeExample[] }) => { + const styles = useCAPThemeExamplesStyles(); return (
@@ -48,7 +43,7 @@ export const VisualRefreshExamples = ({ examples }: { examples: VisualRefreshExa ); }; -function renderExample(example: VisualRefreshExample, variant: 'v9' | 'cap') { +function renderExample(example: CAPThemeExample, variant: 'v9' | 'cap') { const result = example.render(variant); if (result === 'NOT_IMPLEMENTED') { return NOT IMPLEMENTED; @@ -56,7 +51,7 @@ function renderExample(example: VisualRefreshExample, variant: 'v9' | 'cap') { return result; } -const useVisualRefreshExamplesStyles = makeStyles({ +const useCAPThemeExamplesStyles = makeStyles({ table: { display: 'flex', flexDirection: 'column', diff --git a/packages/react-components/react-provider/stories/src/CAPThemeProvider/CAPTooltip.stories.tsx b/packages/react-components/react-provider/stories/src/CAPThemeProvider/CAPTooltip.stories.tsx new file mode 100644 index 00000000000000..ebcdeb4e92b4ae --- /dev/null +++ b/packages/react-components/react-provider/stories/src/CAPThemeProvider/CAPTooltip.stories.tsx @@ -0,0 +1,6 @@ +import * as React from 'react'; +import { CAPThemeExamples } from './CAPStorybookUtil'; + +export const CAPTooltipStory = () => { + return ; +}; diff --git a/packages/react-components/react-visual-refresh-preview/stories/src/index.stories.tsx b/packages/react-components/react-provider/stories/src/CAPThemeProvider/index.stories.tsx similarity index 82% rename from packages/react-components/react-visual-refresh-preview/stories/src/index.stories.tsx rename to packages/react-components/react-provider/stories/src/CAPThemeProvider/index.stories.tsx index 3446d94d8af3fc..dce16e9208c237 100644 --- a/packages/react-components/react-visual-refresh-preview/stories/src/index.stories.tsx +++ b/packages/react-components/react-provider/stories/src/CAPThemeProvider/index.stories.tsx @@ -8,10 +8,10 @@ export { CAPInputStory as Input } from './CAPInput.stories'; export { CAPMenuStory as Menu } from './CAPMenu.stories'; export { CAPTooltipStory as Tooltip } from './CAPTooltip.stories'; -const VisualRefreshStory = () =>
; +const CAPThemStory = () =>
; export default { - title: 'Visual Refresh', - component: VisualRefreshStory, + title: 'CAP Theme', + component: CAPThemStory, parameters: {}, } as Meta; diff --git a/packages/react-components/react-storybook-addon/src/decorators/withVisualUpdate.tsx b/packages/react-components/react-storybook-addon/src/decorators/withVisualUpdate.tsx index 630975d20681e6..a2007f42a1faf4 100644 --- a/packages/react-components/react-storybook-addon/src/decorators/withVisualUpdate.tsx +++ b/packages/react-components/react-storybook-addon/src/decorators/withVisualUpdate.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import type { JSXElement } from '@fluentui/react-utilities'; -import { CAPThemeProvider, CAP_THEME_TEAMS } from '@fluentui/react-visual-refresh-preview'; +import { CAPThemeProvider, CAP_THEME_TEAMS } from '@fluentui/react-provider'; import { VISUAL_UPDATE_ID } from '../constants'; import { FluentStoryContext } from '../hooks'; import { isDecoratorDisabled } from '../utils/isDecoratorDisabled'; @@ -16,12 +16,8 @@ export const withVisualUpdate = (StoryFn: () => JSXElement, context: FluentStory const isVisualUpdateEnabled = globals[VISUAL_UPDATE_ID] ?? false; if (isVisualUpdateEnabled) { - return ( - - {StoryFn()} - - ); + return {StoryFn()}; } return StoryFn(); -}; \ No newline at end of file +}; diff --git a/packages/react-components/react-visual-refresh-preview/library/.babelrc.json b/packages/react-components/react-visual-refresh-preview/library/.babelrc.json deleted file mode 100644 index 630deaf765c49f..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/library/.babelrc.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "../../../../.babelrc-v9.json", - "plugins": ["annotate-pure-calls", "@babel/transform-react-pure-annotations"] -} diff --git a/packages/react-components/react-visual-refresh-preview/library/.eslintrc.json b/packages/react-components/react-visual-refresh-preview/library/.eslintrc.json deleted file mode 100644 index ceea884c70dccc..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/library/.eslintrc.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": ["plugin:@fluentui/eslint-plugin/react"], - "root": true -} diff --git a/packages/react-components/react-visual-refresh-preview/library/.swcrc b/packages/react-components/react-visual-refresh-preview/library/.swcrc deleted file mode 100644 index b4ffa86dee3067..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/library/.swcrc +++ /dev/null @@ -1,30 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/swcrc", - "exclude": [ - "/testing", - "/**/*.cy.ts", - "/**/*.cy.tsx", - "/**/*.spec.ts", - "/**/*.spec.tsx", - "/**/*.test.ts", - "/**/*.test.tsx" - ], - "jsc": { - "parser": { - "syntax": "typescript", - "tsx": true, - "decorators": false, - "dynamicImport": false - }, - "externalHelpers": true, - "transform": { - "react": { - "runtime": "classic", - "useSpread": true - } - }, - "target": "es2019" - }, - "minify": false, - "sourceMaps": true -} diff --git a/packages/react-components/react-visual-refresh-preview/library/LICENSE b/packages/react-components/react-visual-refresh-preview/library/LICENSE deleted file mode 100644 index a7976671d10b9d..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/library/LICENSE +++ /dev/null @@ -1,15 +0,0 @@ -@fluentui/react-visual-refresh-preview - -Copyright (c) Microsoft Corporation - -All rights reserved. - -MIT License - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Note: Usage of the fonts and icons referenced in Fluent UI React is subject to the terms listed at https://aka.ms/fluentui-assets-license diff --git a/packages/react-components/react-visual-refresh-preview/library/README.md b/packages/react-components/react-visual-refresh-preview/library/README.md deleted file mode 100644 index 384e11d6926d90..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/library/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# @fluentui/react-visual-refresh-preview - -**React Visual Refresh components for [Fluent UI React](https://react.fluentui.dev/)** - -These are not production-ready components and **should never be used in product**. This space is useful for testing new components whose APIs might change before final release. diff --git a/packages/react-components/react-visual-refresh-preview/library/config/api-extractor.json b/packages/react-components/react-visual-refresh-preview/library/config/api-extractor.json deleted file mode 100644 index 8d482156d10d53..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/library/config/api-extractor.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", - "extends": "@fluentui/scripts-api-extractor/api-extractor.common.v-next.json", - "mainEntryPointFilePath": "/../../../../../../dist/out-tsc/types/packages/react-components//library/src/index.d.ts" -} diff --git a/packages/react-components/react-visual-refresh-preview/library/config/tests.js b/packages/react-components/react-visual-refresh-preview/library/config/tests.js deleted file mode 100644 index 2e211ae9e21420..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/library/config/tests.js +++ /dev/null @@ -1 +0,0 @@ -/** Jest test setup file. */ diff --git a/packages/react-components/react-visual-refresh-preview/library/docs/Spec.md b/packages/react-components/react-visual-refresh-preview/library/docs/Spec.md deleted file mode 100644 index 0df0b1a3c85dc4..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/library/docs/Spec.md +++ /dev/null @@ -1,63 +0,0 @@ -# @fluentui/react-visual-refresh-preview Spec - -## Background - -_Description and use cases of this component_ - -## Prior Art - -_Include background research done for this component_ - -- _Link to Open UI research_ -- _Link to comparison of v7 and v0_ -- _Link to GitHub epic issue for the converged component_ - -## Sample Code - -_Provide some representative example code that uses the proposed API for the component_ - -## Variants - -_Describe visual or functional variants of this control, if applicable. For example, a slider could have a 2D variant._ - -## API - -_List the **Props** and **Slots** proposed for the component. Ideally this would just be a link to the component's `.types.ts` file_ - -## Structure - -- _**Public**_ -- _**Internal**_ -- _**DOM** - how the component will be rendered as HTML elements_ - -## Migration - -_Describe what will need to be done to upgrade from the existing implementations:_ - -- _Migration from v8_ -- _Migration from v0_ - -## Behaviors - -_Explain how the component will behave in use, including:_ - -- _Component States_ -- _Interaction_ - - _Keyboard_ - - _Cursor_ - - _Touch_ - - _Screen readers_ - -## Accessibility - -Base accessibility information is included in the design document. After the spec is filled and review, outcomes from it need to be communicated to design and incorporated in the design document. - -- Decide whether to use **native element** or folow **ARIA** and provide reasons -- Identify the **[ARIA](https://www.w3.org/TR/wai-aria-practices-1.2/) pattern** and, if the component is listed there, follow its specification as possible. -- Identify accessibility **variants**, the `role` ([ARIA roles](https://www.w3.org/TR/wai-aria-1.1/#role_definitions)) of the component, its `slots` and `aria-*` props. -- Describe the **keyboard navigation**: Tab Oder and Arrow Key Navigation. Describe any other keyboard **shortcuts** used -- Specify texts for **state change announcements** - [ARIA live regions - ](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions) (number of available items in dropdown, error messages, confirmations, ...) -- Identify UI parts that appear on **hover or focus** and specify keyboard and screen reader interaction with them -- List cases when **focus** needs to be **trapped** in sections of the UI (for dialogs and popups or for hierarchical navigation) -- List cases when **focus** needs to be **moved programatically** (if parts of the UI are appearing/disappearing or other cases) diff --git a/packages/react-components/react-visual-refresh-preview/library/etc/react-visual-refresh-preview.api.md b/packages/react-components/react-visual-refresh-preview/library/etc/react-visual-refresh-preview.api.md deleted file mode 100644 index e69de29bb2d1d6..00000000000000 diff --git a/packages/react-components/react-visual-refresh-preview/library/jest.config.js b/packages/react-components/react-visual-refresh-preview/library/jest.config.js deleted file mode 100644 index 78741cc466bdd2..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/library/jest.config.js +++ /dev/null @@ -1,34 +0,0 @@ -// @ts-check -/* eslint-disable */ - -const { readFileSync } = require('node:fs'); -const { join } = require('node:path'); - -// Reading the SWC compilation config and remove the "exclude" -// for the test files to be compiled by SWC -const { exclude: _, ...swcJestConfig } = JSON.parse(readFileSync(join(__dirname, '.swcrc'), 'utf-8')); - -// disable .swcrc look-up by SWC core because we're passing in swcJestConfig ourselves. -// If we do not disable this, SWC Core will read .swcrc and won't transform our test files due to "exclude" -if (swcJestConfig.swcrc === undefined) { - swcJestConfig.swcrc = false; -} - -// Uncomment if using global setup/teardown files being transformed via swc -// https://nx.dev/packages/jest/documents/overview#global-setup/teardown-with-nx-libraries -// jest needs EsModule Interop to find the default exported setup/teardown functions -// swcJestConfig.module.noInterop = false; - -/** - * @type {import('@jest/types').Config.InitialOptions} - */ -module.exports = { - displayName: 'react-visual-refresh-preview', - preset: '../../../../jest.preset.js', - transform: { - '^.+\\.tsx?$': ['@swc/jest', swcJestConfig], - }, - coverageDirectory: './coverage', - setupFilesAfterEnv: ['./config/tests.js'], - snapshotSerializers: ['@griffel/jest-serializer'], -}; diff --git a/packages/react-components/react-visual-refresh-preview/library/package.json b/packages/react-components/react-visual-refresh-preview/library/package.json deleted file mode 100644 index 9aa5de59647f82..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/library/package.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "name": "@fluentui/react-visual-refresh-preview", - "version": "0.0.0", - "private": true, - "description": "New fluentui react package", - "main": "lib-commonjs/index.js", - "module": "lib/index.js", - "typings": "./dist/index.d.ts", - "sideEffects": false, - "files": [ - "*.md", - "dist/*.d.ts", - "lib", - "lib-commonjs" - ], - "repository": { - "type": "git", - "url": "https://github.com/microsoft/fluentui" - }, - "license": "MIT", - "devDependencies": { - "@fluentui/eslint-plugin": "*", - "@fluentui/react-conformance": "*", - "@fluentui/react-conformance-griffel": "*", - "@fluentui/scripts-api-extractor": "*" - }, - "dependencies": { - "@fluentui/react-jsx-runtime": "^9.3.2", - "@fluentui/react-shared-contexts": "^9.25.2", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.3", - "@griffel/react": "^1.5.32", - "@swc/helpers": "^0.5.1" - }, - "peerDependencies": { - "@types/react": ">=16.14.0 <19.0.0", - "@types/react-dom": ">=16.9.0 <19.0.0", - "react": ">=16.14.0 <19.0.0", - "react-dom": ">=16.14.0 <19.0.0" - }, - "exports": { - ".": { - "types": "./dist/index.d.ts", - "node": "./lib-commonjs/index.js", - "import": "./lib/index.js", - "require": "./lib-commonjs/index.js" - }, - "./package.json": "./package.json" - }, - "beachball": { - "disallowedChangeTypes": [ - "major", - "prerelease" - ] - } -} diff --git a/packages/react-components/react-visual-refresh-preview/library/project.json b/packages/react-components/react-visual-refresh-preview/library/project.json deleted file mode 100644 index 0c6dddfabfa7e4..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/library/project.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "react-visual-refresh-preview", - "$schema": "../../../../node_modules/nx/schemas/project-schema.json", - "projectType": "library", - "sourceRoot": "packages/react-components/react-visual-refresh-preview/library/src", - "tags": ["platform:web", "vNext"], - "implicitDependencies": [] -} diff --git a/packages/react-components/react-visual-refresh-preview/library/src/index.ts b/packages/react-components/react-visual-refresh-preview/library/src/index.ts deleted file mode 100644 index 3a8fc17ad04890..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/library/src/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { CAPThemeProvider } from './CAPThemeProvider'; -export { CAP_THEME_ONE_DRIVE, CAP_THEME_SHAREPOINT, CAP_THEME_TEAMS } from './CAPTheme'; diff --git a/packages/react-components/react-visual-refresh-preview/library/src/testing/isConformant.ts b/packages/react-components/react-visual-refresh-preview/library/src/testing/isConformant.ts deleted file mode 100644 index 8ed2da0f925135..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/library/src/testing/isConformant.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { isConformant as baseIsConformant } from '@fluentui/react-conformance'; -import type { IsConformantOptions, TestObject } from '@fluentui/react-conformance'; -import griffelTests from '@fluentui/react-conformance-griffel'; - -export function isConformant( - testInfo: Omit, 'componentPath'> & { componentPath?: string }, -): void { - const defaultOptions: Partial> = { - tsConfig: { configName: 'tsconfig.spec.json' }, - componentPath: require.main?.filename.replace('.test', ''), - extraTests: griffelTests as TestObject, - }; - - baseIsConformant(defaultOptions, testInfo); -} diff --git a/packages/react-components/react-visual-refresh-preview/library/tsconfig.json b/packages/react-components/react-visual-refresh-preview/library/tsconfig.json deleted file mode 100644 index 32bdbdf1ac26f0..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/library/tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "extends": "../../../../tsconfig.base.json", - "compilerOptions": { - "target": "ES2019", - "noEmit": true, - "isolatedModules": true, - "importHelpers": true, - "jsx": "react", - "noUnusedLocals": true, - "preserveConstEnums": true - }, - "include": [], - "files": [], - "references": [ - { - "path": "./tsconfig.lib.json" - }, - { - "path": "./tsconfig.spec.json" - } - ] -} diff --git a/packages/react-components/react-visual-refresh-preview/library/tsconfig.lib.json b/packages/react-components/react-visual-refresh-preview/library/tsconfig.lib.json deleted file mode 100644 index 53066fdd11fff0..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/library/tsconfig.lib.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "noEmit": false, - "lib": ["ES2019", "dom"], - "declaration": true, - "declarationDir": "../../../../dist/out-tsc/types", - "outDir": "../../../../dist/out-tsc", - "inlineSources": true, - "types": ["static-assets", "environment"] - }, - "exclude": [ - "./src/testing/**", - "**/*.spec.ts", - "**/*.spec.tsx", - "**/*.test.ts", - "**/*.test.tsx", - "**/*.stories.ts", - "**/*.stories.tsx" - ], - "include": ["./src/**/*.ts", "./src/**/*.tsx"] -} diff --git a/packages/react-components/react-visual-refresh-preview/library/tsconfig.spec.json b/packages/react-components/react-visual-refresh-preview/library/tsconfig.spec.json deleted file mode 100644 index 911456fe4b4d91..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/library/tsconfig.spec.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "module": "CommonJS", - "outDir": "dist", - "types": ["jest", "node"] - }, - "include": [ - "**/*.spec.ts", - "**/*.spec.tsx", - "**/*.test.ts", - "**/*.test.tsx", - "**/*.d.ts", - "./src/testing/**/*.ts", - "./src/testing/**/*.tsx" - ] -} diff --git a/packages/react-components/react-visual-refresh-preview/stories/.eslintrc.json b/packages/react-components/react-visual-refresh-preview/stories/.eslintrc.json deleted file mode 100644 index a41120835dcc92..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/stories/.eslintrc.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": ["plugin:@fluentui/eslint-plugin/react"], - "root": true, - "rules": { - "import/no-extraneous-dependencies": [ - "error", - { - "packageDir": [".", "../../../../"] - } - ] - } -} diff --git a/packages/react-components/react-visual-refresh-preview/stories/.storybook/main.js b/packages/react-components/react-visual-refresh-preview/stories/.storybook/main.js deleted file mode 100644 index 67905c6bfe15f2..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/stories/.storybook/main.js +++ /dev/null @@ -1,14 +0,0 @@ -const rootMain = require('../../../../../.storybook/main'); - -module.exports = /** @type {Omit} */ ({ - ...rootMain, - stories: [...rootMain.stories, '../src/**/*.mdx', '../src/**/index.stories.@(ts|tsx)'], - addons: [...rootMain.addons], - webpackFinal: (config, options) => { - const localConfig = { ...rootMain.webpackFinal(config, options) }; - - // add your own webpack tweaks if needed - - return localConfig; - }, -}); diff --git a/packages/react-components/react-visual-refresh-preview/stories/.storybook/preview.js b/packages/react-components/react-visual-refresh-preview/stories/.storybook/preview.js deleted file mode 100644 index 98274ed0b8095f..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/stories/.storybook/preview.js +++ /dev/null @@ -1,9 +0,0 @@ -import * as rootPreview from '../../../../../.storybook/preview'; - -/** @type {typeof rootPreview.decorators} */ -export const decorators = [...rootPreview.decorators]; - -/** @type {typeof rootPreview.parameters} */ -export const parameters = { ...rootPreview.parameters }; - -export const tags = ['autodocs']; diff --git a/packages/react-components/react-visual-refresh-preview/stories/.storybook/tsconfig.json b/packages/react-components/react-visual-refresh-preview/stories/.storybook/tsconfig.json deleted file mode 100644 index 4cdd1ce9d006f1..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/stories/.storybook/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "outDir": "", - "allowJs": true, - "checkJs": true, - "types": ["static-assets", "environment"] - }, - "include": ["*.js"] -} diff --git a/packages/react-components/react-visual-refresh-preview/stories/README.md b/packages/react-components/react-visual-refresh-preview/stories/README.md deleted file mode 100644 index 46787c7ead4fa8..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/stories/README.md +++ /dev/null @@ -1,17 +0,0 @@ -# @fluentui/react-visual-refresh-preview-stories - -Storybook stories for packages/react-components/react-visual-refresh-preview - -## Usage - -To include within storybook specify stories globs: - -\`\`\`js -module.exports = { -stories: ['../packages/react-components/react-visual-refresh-preview/stories/src/**/*.mdx', '../packages/react-components/react-visual-refresh-preview/stories/src/**/index.stories.@(ts|tsx)'], -} -\`\`\` - -## API - -no public API available diff --git a/packages/react-components/react-visual-refresh-preview/stories/package.json b/packages/react-components/react-visual-refresh-preview/stories/package.json deleted file mode 100644 index 85e2b373256d13..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/stories/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "@fluentui/react-visual-refresh-preview-stories", - "version": "0.0.0", - "private": true, - "devDependencies": { - "@fluentui/react-storybook-addon": "*", - "@fluentui/react-storybook-addon-export-to-sandbox": "*", - "@fluentui/scripts-storybook": "*", - "@fluentui/eslint-plugin": "*" - } -} diff --git a/packages/react-components/react-visual-refresh-preview/stories/project.json b/packages/react-components/react-visual-refresh-preview/stories/project.json deleted file mode 100644 index 8ce9ed29eae34a..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/stories/project.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "react-visual-refresh-preview-stories", - "$schema": "../../../../node_modules/nx/schemas/project-schema.json", - "projectType": "library", - "sourceRoot": "packages/react-components/react-visual-refresh-preview/stories/src", - "tags": ["vNext", "platform:web", "type:stories"], - "implicitDependencies": [] -} diff --git a/packages/react-components/react-visual-refresh-preview/stories/src/CAPCard.stories.tsx b/packages/react-components/react-visual-refresh-preview/stories/src/CAPCard.stories.tsx deleted file mode 100644 index f0a0f4553b9b6a..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/stories/src/CAPCard.stories.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import * as React from 'react'; -import { VisualRefreshExamples } from './StorybookUtil'; - -export const CAPCardStory = () => { - return ; -}; diff --git a/packages/react-components/react-visual-refresh-preview/stories/src/CAPMenu.stories.tsx b/packages/react-components/react-visual-refresh-preview/stories/src/CAPMenu.stories.tsx deleted file mode 100644 index 8159e057993baf..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/stories/src/CAPMenu.stories.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import * as React from 'react'; -import { VisualRefreshExamples } from './StorybookUtil'; - -export const CAPMenuStory = () => { - return ; -}; diff --git a/packages/react-components/react-visual-refresh-preview/stories/src/CAPTooltip.stories.tsx b/packages/react-components/react-visual-refresh-preview/stories/src/CAPTooltip.stories.tsx deleted file mode 100644 index 7b547ab04cbb42..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/stories/src/CAPTooltip.stories.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import * as React from 'react'; -import { VisualRefreshExamples } from './StorybookUtil'; - -export const CAPTooltipStory = () => { - return ; -}; diff --git a/packages/react-components/react-visual-refresh-preview/stories/tsconfig.json b/packages/react-components/react-visual-refresh-preview/stories/tsconfig.json deleted file mode 100644 index efc50169d1df18..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/stories/tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "extends": "../../../../tsconfig.base.json", - "compilerOptions": { - "target": "ES2019", - "noEmit": true, - "isolatedModules": true, - "importHelpers": true, - "jsx": "react", - "noUnusedLocals": true, - "preserveConstEnums": true - }, - "include": [], - "files": [], - "references": [ - { - "path": "./tsconfig.lib.json" - }, - { - "path": "./.storybook/tsconfig.json" - } - ] -} diff --git a/packages/react-components/react-visual-refresh-preview/stories/tsconfig.lib.json b/packages/react-components/react-visual-refresh-preview/stories/tsconfig.lib.json deleted file mode 100644 index 9486b224643d9f..00000000000000 --- a/packages/react-components/react-visual-refresh-preview/stories/tsconfig.lib.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "./tsconfig.json", - "compilerOptions": { - "lib": ["ES2019", "dom"], - "outDir": "../../../../dist/out-tsc", - "inlineSources": true, - "types": ["static-assets", "environment"] - }, - "include": ["./src/**/*.ts", "./src/**/*.tsx"] -} diff --git a/tsconfig.base.all.json b/tsconfig.base.all.json index 1328da016a97c5..9b9756df7a1a05 100644 --- a/tsconfig.base.all.json +++ b/tsconfig.base.all.json @@ -248,23 +248,13 @@ "@fluentui/react-utilities-compat-stories": [ "packages/react-components/react-utilities-compat/stories/src/index.ts" ], - "@fluentui/react-visual-refresh-preview": [ - "packages/react-components/react-visual-refresh-preview/library/src/index.ts" - ], "@fluentui/recipes": ["packages/react-components/recipes/src/index.ts"], "@fluentui/storybook-llms-extractor": ["tools/storybook-llms-extractor/src/index.ts"], "@fluentui/theme-designer": ["packages/react-components/theme-designer/src/index.ts"], "@fluentui/tokens": ["packages/tokens/src/index.ts"], - "@fluentui/visual-refresh-preview": ["packages/react-components/visual-refresh-preview/library/src/index.ts"], - "@fluentui/visual-refresh-preview-stories": [ - "packages/react-components/visual-refresh-preview/stories/src/index.ts" - ], "@fluentui/visual-regression-assert": ["tools/visual-regression-assert/src/index.ts"], "@fluentui/visual-regression-utilities": ["tools/visual-regression-utilities/src/index.ts"], - "@fluentui/workspace-plugin": ["tools/workspace-plugin/src/index.ts"], - "@fluentui/react-visual-refresh-preview-stories": [ - "packages/react-components/react-visual-refresh-preview/stories/src/index.ts" - ] + "@fluentui/workspace-plugin": ["tools/workspace-plugin/src/index.ts"] } } } diff --git a/tsconfig.base.json b/tsconfig.base.json index c76de2485b72e7..ded6aaaa1f2528 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -204,20 +204,10 @@ "@fluentui/react-utilities-compat-stories": [ "packages/react-components/react-utilities-compat/stories/src/index.ts" ], - "@fluentui/react-visual-refresh-preview": [ - "packages/react-components/react-visual-refresh-preview/library/src/index.ts" - ], - "@fluentui/react-visual-refresh-preview-stories": [ - "packages/react-components/react-visual-refresh-preview/stories/src/index.ts" - ], "@fluentui/recipes": ["packages/react-components/recipes/src/index.ts"], "@fluentui/storybook-llms-extractor": ["tools/storybook-llms-extractor/src/index.ts"], "@fluentui/theme-designer": ["packages/react-components/theme-designer/src/index.ts"], "@fluentui/tokens": ["packages/tokens/src/index.ts"], - "@fluentui/visual-refresh-preview": ["packages/react-components/visual-refresh-preview/library/src/index.ts"], - "@fluentui/visual-refresh-preview-stories": [ - "packages/react-components/visual-refresh-preview/stories/src/index.ts" - ], "@fluentui/visual-regression-assert": ["tools/visual-regression-assert/src/index.ts"], "@fluentui/visual-regression-utilities": ["tools/visual-regression-utilities/src/index.ts"], "@fluentui/workspace-plugin": ["tools/workspace-plugin/src/index.ts"] From 5e770187201a318ccb3faaab8d8a31d3be448db3 Mon Sep 17 00:00:00 2001 From: David Zukowski Date: Thu, 20 Nov 2025 09:42:33 -0500 Subject: [PATCH 10/14] Provide CAP tokens via FluentProvider --- .../library/src/components/CAPThemeProvider/CAPTheme.tsx | 2 +- .../src/components/CAPThemeProvider/CAPThemeProvider.tsx | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPTheme.tsx b/packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPTheme.tsx index fbe6f512f0f8d1..f6616810d400ba 100644 --- a/packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPTheme.tsx +++ b/packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPTheme.tsx @@ -49,7 +49,7 @@ export const CAPTokensSchema = { export const CAPTokens = { ...(Object.keys(CAPTokensSchema).reduce((acc: any, key) => { - return { ...acc, [key]: `var(--cap-${key})` }; + return { ...acc, [key]: `var(--${key})` }; }) as any), } as Record; diff --git a/packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPThemeProvider.tsx b/packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPThemeProvider.tsx index 8607f81b582341..eacc6f2d41c0c1 100644 --- a/packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPThemeProvider.tsx +++ b/packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPThemeProvider.tsx @@ -22,13 +22,8 @@ export const CAPThemeProvider = ({ useInputStyles_unstable: state => useCAPInputStylesHook(state as InputState), }; }, []); - - const styles: React.CSSProperties = {}; - for (const [tokenName, tokenValue] of Object.entries(theme)) { - (styles as any)[`--cap-${tokenName}`] = tokenValue; - } return ( - + {children} ); From b6fef13239c2335cb99e819d78453b2cb03cc8a3 Mon Sep 17 00:00:00 2001 From: David Zukowski Date: Thu, 20 Nov 2025 09:55:58 -0500 Subject: [PATCH 11/14] Update token schemas to document semantic token and fluent v9 token references --- .../components/CAPThemeProvider/CAPTheme.tsx | 81 ++++++++++++++++--- 1 file changed, 70 insertions(+), 11 deletions(-) diff --git a/packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPTheme.tsx b/packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPTheme.tsx index f6616810d400ba..93b96f5a5883a1 100644 --- a/packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPTheme.tsx +++ b/packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPTheme.tsx @@ -57,19 +57,78 @@ export type CAPTheme = { [k in keyof typeof CAPTokens]: string; }; +function token(t: { + semanticToken: string | null; + fluentV9Token: keyof typeof tokens | null; + fallbackValue?: string; +}): string { + const parts = [ + t.semanticToken ? `var(--${t.semanticToken})` : undefined, + t.fluentV9Token ? tokens[t.fluentV9Token] : undefined, + t.fallbackValue, + ]; + let cssVar = ''; + for (const part of parts) { + if (!part) continue; + if (cssVar.startsWith('var(')) { + cssVar = cssVar.replace(/\)$/, `, ${part})`); + } else { + cssVar += part; + } + } + return cssVar; +} + export const CAP_THEME = { - buttonPrimaryBackgroundColor: tokens.colorBrandBackground, - buttonPrimaryBackgroundColorHover: tokens.colorBrandBackgroundHover, - buttonSecondaryBackgroundColor: '#FAFAFA', - buttonSecondaryBackgroundColorHover: '#F0F0F0', // NeutralBackground3.Hover - buttonSubtleBackgroundColor: tokens.colorBrandBackground, - buttonSubtleBackgroundColorHover: tokens.colorBrandBackground, - buttonOutlineBackgroundColor: tokens.colorTransparentBackground, - buttonOutlineBackgroundColorHover: tokens.colorTransparentBackground, - buttonTintBackgroundColor: 'red', - buttonTintBackgroundColorHover: '', + buttonPrimaryBackgroundColor: token({ + semanticToken: 'foobar', + fluentV9Token: 'colorBrandBackground', + }), + buttonPrimaryBackgroundColorHover: token({ + semanticToken: null, + fluentV9Token: 'colorBrandBackgroundHover', + }), + buttonSecondaryBackgroundColor: token({ + semanticToken: null, + fluentV9Token: null, + fallbackValue: '#FAFAFA', + }), + buttonSecondaryBackgroundColorHover: token({ + semanticToken: null, + fluentV9Token: null, + fallbackValue: '#F0F0F0', // NeutralBackground3 + }), + buttonSubtleBackgroundColor: token({ + semanticToken: null, + fluentV9Token: 'colorBrandBackground', + }), + buttonSubtleBackgroundColorHover: token({ + semanticToken: null, + fluentV9Token: 'colorBrandBackground', + }), + buttonOutlineBackgroundColor: token({ + semanticToken: null, + fluentV9Token: 'colorTransparentBackground', + }), + buttonOutlineBackgroundColorHover: token({ + semanticToken: null, + fluentV9Token: 'colorTransparentBackground', + }), + buttonTintBackgroundColor: token({ + semanticToken: null, + fluentV9Token: null, + fallbackValue: 'red', + }), + buttonTintBackgroundColorHover: token({ + semanticToken: null, + fluentV9Token: null, + fallbackValue: '', + }), // TODO: switch to BrandForegroundCompound when available - colorBrandForegroundCompound: tokens.colorBrandForeground1, + colorBrandForegroundCompound: token({ + semanticToken: null, + fluentV9Token: 'colorBrandForeground1', + }), } as const satisfies CAPTheme; export const CAP_THEME_TEAMS = { From cb4badb01f2cd253a64585e58eb50512d9135367 Mon Sep 17 00:00:00 2001 From: David Zukowski Date: Thu, 20 Nov 2025 10:21:24 -0500 Subject: [PATCH 12/14] Adjust TokenSchema to associate a CAP Token with a Semantic Token --- .../components/CAPThemeProvider/CAPTheme.tsx | 117 +++++------------- 1 file changed, 32 insertions(+), 85 deletions(-) diff --git a/packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPTheme.tsx b/packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPTheme.tsx index 93b96f5a5883a1..ed9307905b619a 100644 --- a/packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPTheme.tsx +++ b/packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPTheme.tsx @@ -1,30 +1,36 @@ import { tokens } from '@fluentui/react-components'; -const TOKEN_TYPES = ['color', 'length'] as const; +const EXPECTED_SEMANTIC_TOKENS = [] as const; +type ExpectedSemanticToken = (typeof EXPECTED_SEMANTIC_TOKENS)[number]; + +const TOKEN_TYPES = ['color', 'dimension'] as const; type TokenType = (typeof TOKEN_TYPES)[number]; export interface TokenSchema { type: TokenType; + /** + * What's the name of the semantic token that we expect to exist? When semantic tokens v2 + * is released, our token should be replaced by this one. + */ + semanticToken: ExpectedSemanticToken | null; } -const ColorTokenSchema = { type: 'color' } as const satisfies TokenSchema; - const BadgeTokens = { - colorBrandForegroundCompound: ColorTokenSchema, + colorBrandForegroundCompound: { type: 'color', semanticToken: null }, } as const satisfies Record; const ButtonTokens = { - buttonPrimaryBackgroundColor: ColorTokenSchema, - buttonPrimaryBackgroundColorHover: ColorTokenSchema, - buttonSecondaryBackgroundColor: ColorTokenSchema, - buttonSecondaryBackgroundColorHover: ColorTokenSchema, - buttonSubtleBackgroundColor: ColorTokenSchema, - buttonSubtleBackgroundColorHover: ColorTokenSchema, - buttonOutlineBackgroundColor: ColorTokenSchema, - buttonOutlineBackgroundColorHover: ColorTokenSchema, - buttonTintBackgroundColor: ColorTokenSchema, - buttonTintBackgroundColorHover: ColorTokenSchema, + buttonPrimaryBackgroundColor: { type: 'color', semanticToken: null }, + buttonPrimaryBackgroundColorHover: { type: 'color', semanticToken: null }, + buttonSecondaryBackgroundColor: { type: 'color', semanticToken: null }, + buttonSecondaryBackgroundColorHover: { type: 'color', semanticToken: null }, + buttonSubtleBackgroundColor: { type: 'color', semanticToken: null }, + buttonSubtleBackgroundColorHover: { type: 'color', semanticToken: null }, + buttonOutlineBackgroundColor: { type: 'color', semanticToken: null }, + buttonOutlineBackgroundColorHover: { type: 'color', semanticToken: null }, + buttonTintBackgroundColor: { type: 'color', semanticToken: null }, + buttonTintBackgroundColorHover: { type: 'color', semanticToken: null }, } as const satisfies Record; const CardTokens = {} as const satisfies Record; @@ -54,81 +60,22 @@ export const CAPTokens = { } as Record; export type CAPTheme = { - [k in keyof typeof CAPTokens]: string; + [k in keyof typeof CAPTokens]: string | null; }; -function token(t: { - semanticToken: string | null; - fluentV9Token: keyof typeof tokens | null; - fallbackValue?: string; -}): string { - const parts = [ - t.semanticToken ? `var(--${t.semanticToken})` : undefined, - t.fluentV9Token ? tokens[t.fluentV9Token] : undefined, - t.fallbackValue, - ]; - let cssVar = ''; - for (const part of parts) { - if (!part) continue; - if (cssVar.startsWith('var(')) { - cssVar = cssVar.replace(/\)$/, `, ${part})`); - } else { - cssVar += part; - } - } - return cssVar; -} - export const CAP_THEME = { - buttonPrimaryBackgroundColor: token({ - semanticToken: 'foobar', - fluentV9Token: 'colorBrandBackground', - }), - buttonPrimaryBackgroundColorHover: token({ - semanticToken: null, - fluentV9Token: 'colorBrandBackgroundHover', - }), - buttonSecondaryBackgroundColor: token({ - semanticToken: null, - fluentV9Token: null, - fallbackValue: '#FAFAFA', - }), - buttonSecondaryBackgroundColorHover: token({ - semanticToken: null, - fluentV9Token: null, - fallbackValue: '#F0F0F0', // NeutralBackground3 - }), - buttonSubtleBackgroundColor: token({ - semanticToken: null, - fluentV9Token: 'colorBrandBackground', - }), - buttonSubtleBackgroundColorHover: token({ - semanticToken: null, - fluentV9Token: 'colorBrandBackground', - }), - buttonOutlineBackgroundColor: token({ - semanticToken: null, - fluentV9Token: 'colorTransparentBackground', - }), - buttonOutlineBackgroundColorHover: token({ - semanticToken: null, - fluentV9Token: 'colorTransparentBackground', - }), - buttonTintBackgroundColor: token({ - semanticToken: null, - fluentV9Token: null, - fallbackValue: 'red', - }), - buttonTintBackgroundColorHover: token({ - semanticToken: null, - fluentV9Token: null, - fallbackValue: '', - }), + buttonPrimaryBackgroundColor: tokens.colorBrandBackground, + buttonPrimaryBackgroundColorHover: tokens.colorBrandBackgroundHover, + buttonSecondaryBackgroundColor: null, + buttonSecondaryBackgroundColorHover: null, + buttonSubtleBackgroundColor: tokens.colorBrandBackground, + buttonSubtleBackgroundColorHover: tokens.colorBrandBackground, + buttonOutlineBackgroundColor: tokens.colorTransparentBackground, + buttonOutlineBackgroundColorHover: tokens.colorTransparentBackground, + buttonTintBackgroundColor: 'red', + buttonTintBackgroundColorHover: null, // TODO: switch to BrandForegroundCompound when available - colorBrandForegroundCompound: token({ - semanticToken: null, - fluentV9Token: 'colorBrandForeground1', - }), + colorBrandForegroundCompound: tokens.colorBrandForeground1, } as const satisfies CAPTheme; export const CAP_THEME_TEAMS = { From 3f45d6fe5dcb949c4197911f24fe257454027e71 Mon Sep 17 00:00:00 2001 From: David Zukowski Date: Thu, 20 Nov 2025 10:22:52 -0500 Subject: [PATCH 13/14] Fix storybook hierarchy --- .../stories/src/CAPThemeProvider/index.stories.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-components/react-provider/stories/src/CAPThemeProvider/index.stories.tsx b/packages/react-components/react-provider/stories/src/CAPThemeProvider/index.stories.tsx index dce16e9208c237..50ec2d3b608990 100644 --- a/packages/react-components/react-provider/stories/src/CAPThemeProvider/index.stories.tsx +++ b/packages/react-components/react-provider/stories/src/CAPThemeProvider/index.stories.tsx @@ -8,10 +8,10 @@ export { CAPInputStory as Input } from './CAPInput.stories'; export { CAPMenuStory as Menu } from './CAPMenu.stories'; export { CAPTooltipStory as Tooltip } from './CAPTooltip.stories'; -const CAPThemStory = () =>
; +const VisualRefreshStory = () =>
; export default { - title: 'CAP Theme', - component: CAPThemStory, + title: 'Components/Visual Refresh', + component: VisualRefreshStory, parameters: {}, } as Meta; From 9822e6b1fef444d404ba07efccd4441cbf98ab56 Mon Sep 17 00:00:00 2001 From: David Zukowski Date: Thu, 20 Nov 2025 12:51:53 -0500 Subject: [PATCH 14/14] scaffold storybook and tokens for Card --- .../components/CAPThemeProvider/CAPTheme.tsx | 39 ++++++++++++++++++- .../CAPThemeProvider/CAPThemeProvider.tsx | 15 ++++++- .../CAPThemeProvider/components/CAPCard.tsx | 27 ++++++++++++- .../src/CAPThemeProvider/CAPCard.stories.tsx | 33 +++++++++++++++- 4 files changed, 107 insertions(+), 7 deletions(-) diff --git a/packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPTheme.tsx b/packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPTheme.tsx index ed9307905b619a..cd8067dfea3899 100644 --- a/packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPTheme.tsx +++ b/packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPTheme.tsx @@ -1,6 +1,12 @@ import { tokens } from '@fluentui/react-components'; -const EXPECTED_SEMANTIC_TOKENS = [] as const; +const EXPECTED_SEMANTIC_TOKENS = [ + // card + 'stroke/card/onPrimary/default/rest', + 'cornerRadius/ctrl/card', + 'padding/card/header/body/default/inside', + 'padding/card/header/body/default/outside', +] as const; type ExpectedSemanticToken = (typeof EXPECTED_SEMANTIC_TOKENS)[number]; const TOKEN_TYPES = ['color', 'dimension'] as const; @@ -33,7 +39,20 @@ const ButtonTokens = { buttonTintBackgroundColorHover: { type: 'color', semanticToken: null }, } as const satisfies Record; -const CardTokens = {} as const satisfies Record; +const CardTokens = { + cardBackgroundColor: { type: 'color', semanticToken: null }, + cardForegroundColor: { type: 'color', semanticToken: null }, + cardBackgroundColorHover: { type: 'color', semanticToken: null }, + cardForegroundColorHover: { type: 'color', semanticToken: null }, + cardBackgroundColorPressed: { type: 'color', semanticToken: null }, + cardForegroundColorPressed: { type: 'color', semanticToken: null }, + cardBackgroundColorDisabled: { type: 'color', semanticToken: null }, + cardForegroundColorDisabled: { type: 'color', semanticToken: null }, + cardCornerRadius: { type: 'dimension', semanticToken: 'cornerRadius/ctrl/card' }, + cardHeaderPaddingOutside: { type: 'dimension', semanticToken: 'padding/card/header/body/default/outside' }, + cardHeaderPaddingInside: { type: 'dimension', semanticToken: 'padding/card/header/body/default/inside' }, + cardFooterHorizontalGap: { type: 'dimension', semanticToken: null }, +} as const satisfies Record; const DialogTokens = {} as const satisfies Record; @@ -64,6 +83,7 @@ export type CAPTheme = { }; export const CAP_THEME = { + // button buttonPrimaryBackgroundColor: tokens.colorBrandBackground, buttonPrimaryBackgroundColorHover: tokens.colorBrandBackgroundHover, buttonSecondaryBackgroundColor: null, @@ -74,6 +94,21 @@ export const CAP_THEME = { buttonOutlineBackgroundColorHover: tokens.colorTransparentBackground, buttonTintBackgroundColor: 'red', buttonTintBackgroundColorHover: null, + + // card + cardCornerRadius: tokens.borderRadiusXLarge, // 8px + cardBackgroundColor: tokens.colorNeutralBackground1, + cardForegroundColor: tokens.colorNeutralBackground1, + cardBackgroundColorHover: '', + cardForegroundColorHover: '', + cardBackgroundColorPressed: '', + cardForegroundColorPressed: '', + cardBackgroundColorDisabled: '', + cardForegroundColorDisabled: '', + cardHeaderPaddingOutside: tokens.spacingVerticalM, + cardHeaderPaddingInside: tokens.spacingVerticalS, + cardFooterHorizontalGap: tokens.spacingHorizontalS, + // TODO: switch to BrandForegroundCompound when available colorBrandForegroundCompound: tokens.colorBrandForeground1, } as const satisfies CAPTheme; diff --git a/packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPThemeProvider.tsx b/packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPThemeProvider.tsx index eacc6f2d41c0c1..4da13f4eba7855 100644 --- a/packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPThemeProvider.tsx +++ b/packages/react-components/react-provider/library/src/components/CAPThemeProvider/CAPThemeProvider.tsx @@ -1,11 +1,20 @@ import * as React from 'react'; -import { BadgeState, ButtonState, CardState, FluentProvider, InputState, Theme } from '@fluentui/react-components'; +import { + BadgeState, + ButtonState, + CardFooterState, + CardHeaderState, + CardState, + FluentProvider, + InputState, + Theme, +} from '@fluentui/react-components'; import { useCAPButtonStylesHook } from './components/CAPButton'; import { CustomStyleHooksContext_unstable } from '@fluentui/react-shared-contexts'; import { CAPTheme } from './CAPTheme'; import { useCAPBadgeStylesHook } from './components/CAPBadge'; import { useCAPInputStylesHook } from './components/CAPInput'; -import { useCAPCardStylesHook } from './components/CAPCard'; +import { useCAPCardFooterStylesHook, useCAPCardHeaderStylesHook, useCAPCardStylesHook } from './components/CAPCard'; export const CAPThemeProvider = ({ children, @@ -19,6 +28,8 @@ export const CAPThemeProvider = ({ useBadgeStyles_unstable: state => useCAPBadgeStylesHook(state as BadgeState), useButtonStyles_unstable: state => useCAPButtonStylesHook(state as ButtonState), useCardStyles_unstable: state => useCAPCardStylesHook(state as CardState), + useCardHeaderStyles_unstable: state => useCAPCardHeaderStylesHook(state as CardHeaderState), + useCardFooterStyles_unstable: state => useCAPCardFooterStylesHook(state as CardFooterState), useInputStyles_unstable: state => useCAPInputStylesHook(state as InputState), }; }, []); diff --git a/packages/react-components/react-provider/library/src/components/CAPThemeProvider/components/CAPCard.tsx b/packages/react-components/react-provider/library/src/components/CAPThemeProvider/components/CAPCard.tsx index f88a12463b2ec8..4814683b4bc7de 100644 --- a/packages/react-components/react-provider/library/src/components/CAPThemeProvider/components/CAPCard.tsx +++ b/packages/react-components/react-provider/library/src/components/CAPThemeProvider/components/CAPCard.tsx @@ -1,8 +1,11 @@ import { makeStyles, mergeClasses } from '@griffel/react'; -import { CardState } from '@fluentui/react-components'; +import { CardFooterState, CardHeaderState, CardState } from '@fluentui/react-components'; +import { CAPTokens } from '../CAPTheme'; const useCAPCardStyles = makeStyles({ - root: {}, + root: { + borderRadius: CAPTokens.cardCornerRadius, + }, }); export function useCAPCardStylesHook(state: CardState) { @@ -10,3 +13,23 @@ export function useCAPCardStylesHook(state: CardState) { state.root.className = mergeClasses(state.root.className, styles.root); return state; } + +const useCAPCardHeaderStyles = makeStyles({ + root: {}, +}); + +export function useCAPCardHeaderStylesHook(state: CardHeaderState) { + const styles = useCAPCardHeaderStyles(); + state.root.className = mergeClasses(state.root.className, styles.root); + return state; +} + +const useCAPCardFooterStyles = makeStyles({ + root: {}, +}); + +export function useCAPCardFooterStylesHook(state: CardFooterState) { + const styles = useCAPCardFooterStyles(); + state.root.className = mergeClasses(state.root.className, styles.root); + return state; +} diff --git a/packages/react-components/react-provider/stories/src/CAPThemeProvider/CAPCard.stories.tsx b/packages/react-components/react-provider/stories/src/CAPThemeProvider/CAPCard.stories.tsx index 23fad6a8e4f13a..c5f2d73f635b75 100644 --- a/packages/react-components/react-provider/stories/src/CAPThemeProvider/CAPCard.stories.tsx +++ b/packages/react-components/react-provider/stories/src/CAPThemeProvider/CAPCard.stories.tsx @@ -1,6 +1,37 @@ import * as React from 'react'; +import { Button, Caption1, Card, CardHeader, Text } from '@fluentui/react-components'; import { CAPThemeExamples } from './CAPStorybookUtil'; +import { MoreHorizontal20Regular } from '@fluentui/react-icons'; export const CAPCardStory = () => { - return ; + return ( + + + App Name + + } + description={Developer} + action={ +