microsoft / microsoft/fluentui-react-native
Theming: Theme Snapshot Parity
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 1.4k
- Forks
- 179
- Avg merge
- 16h 17m
- Merged PRs (30d)
- 30
Description
Summary
Add full resolved Flex-token snapshots to the design package for every
supported appearance and platform, matching the coverage that the legacy
default-theme package already provides for Fluent themes. Commit the snapshot
files alongside the tests and define an explicit update policy so token-value
changes are always surfaced in PR diffs and reviewed intentionally.
The design package currently has no snapshot of the resolved FlexTokens
object for any appearance. This gap means a codegen or mapping change that
silently shifts a token value cannot be detected by CI.
Goal
Produce one committed Jest snapshot per appearance and platform that captures
the complete resolved FlexTokens object, written with toMatchSnapshot() so
the test source contains no duplicated literal values and the .snap files are
the single authoritative record. Define and document the update policy so teams
know when a snapshot update is safe and what reviewing a diff requires.
Stage
Stage 2 - Beta delivery. The snapshots are only meaningful once
Default Values Codegen generates the
per-appearance value sets they will capture.
Why it matters
- Observed. The legacy
default-themepackage has per-platform snapshot
files for the resolved Fluent theme at every appearance:
packages/theming/default-theme/src/__tests__
containsdefaultThemes.test.win32.ts,defaultThemes.test.macos.ts,
defaultThemes.test.ios.ts,defaultThemes.test.windows.ts, and
defaultThemes.test.android.ts, each callingtoMatchSnapshot()for the
light, dark, and high-contrast themes it exports. - Observed. The design package has no equivalent. Its current token tests,
flex.test.ts
and
flexTokensFromTheme.test.ts,
contain spot assertions against named literal values. They verify a small
number of tokens and cannot detect a silent shift elsewhere in the resolved
set. - Observed.
defaultTokens.tsis 473 lines and contains 249 color
literals with only 80 distinct values
(packages/agentic/design/src/tokens/defaultTokens.ts);
Default Values Codegen will replace it with
generated output. Without a before-and-after snapshot comparison, a codegen
bug that shifts a single color value in one appearance would not be caught
by any existing test. - Observed.
codegen.ctscurrently generates only one appearance per
platform (e.g.,win32/colorfulandmacos/light)
(packages/agentic/design/scripts/codegen.cts).
Once codegen is extended to emit all appearances, there is no test harness
to assert those additional sets are correct. - Inferred. Snapshots in PR diffs are the lowest-friction way to make
cross-appearance token changes visible to reviewers. A value that differs
between acolorfuland adarkgrayappearance will appear in the diff
only if both are captured.
Observed current state
- Observed. The design package's Jest platform is
macosby default
("furn": { "jestPlatform": "macos" }in
packages/agentic/design/package.json).
Tests run with@rnx-kit/jest-presetvia
scripts/configs/jest/jest.config.cjs,
which selects a React Native platform and resolves platform-suffixed modules
accordingly. - Observed. Platform-specific test files use the
.test.<platform>.ts
suffix convention, as demonstrated by the legacy package files and
recognized by the@rnx-kit/jest-presetrunner. - Observed.
AppearanceOptionsin the design package is
'light' | 'dark' | 'darkElevated' | 'highContrast'
(packages/agentic/design/src/theming/types/Theme.types.ts).
The installed@fluentui-react-native/design-tokens-macospackage ships
light,dark,hclight, andhcdarkvariants, and
@fluentui-react-native/design-tokens-win32shipscolorful,darkgray,
hc, andblack; these do not map one-to-one toAppearanceOptions. - Observed.
useFlexTokensreturnsdefaultFlexTokensunconditionally
and ignores context
(packages/agentic/design/src/tokens/useFlexTokens.ts),
so there is currently no per-appearance resolution to snapshot. - Observed.
flexTokensFromThemederives Flex token values from a Fluent
Themevia a declarative lookup table and is tested with value assertions
against a hand-constructed mock theme
(flexTokensFromTheme.test.ts).
That test covers the projection path but not the per-appearance shape of
the resolved defaults. - Observed. The design package's
package.jsonalready includes an
update-snapshotsscript (fluentui-scripts jest -u), so the runner
infrastructure is in place. - Observed. Legacy snapshot test files in
default-themeuse
toMatchSnapshot()without any embedded literal values in the test source;
all resolved values live only in the committed.snapfiles alongside each
test file.
Scope
- Add snapshot tests to the design package that call
toMatchSnapshot()on
the full resolvedFlexTokensobject for every supported appearance and
platform. Test source must not embed literal token values; the.snapfiles
are the only record of resolved values. - Follow the
.test.<platform>.tsfile naming convention used by the legacy
packages so the Jest platform runner can load the correct React Native fork
for each file. - Cover, at minimum:
win32(colorful/light, darkgray/dark, hc/high-contrast,
black/dark-alternate),macos(light, dark, hclight, hcdark),windows
(light, dark),ios(light), andandroid(light). The exact
appearance-to-AppearanceOptionsmapping is resolved by
System Appearance Handling and
Default Values Codegen; the snapshot files
must be updated to match once that mapping is stable. - Commit all
.snapfiles in the repository alongside the test files so they
appear in CI diff output when token values change. - Define and document the update policy: when an intentional token change is
made (e.g., by regenerating defaults), the author runsyarn update-snapshots
in the design package, reviews the diff to confirm only expected tokens
changed, and commits the updated.snapfiles with the same change. An
unreviewed snapshot update is a regression indicator. - Add a note to the codegen task's change protocol that a
defaultTokens
regeneration requires re-runningyarn update-snapshots.
Out of scope
- Generating the per-appearance token values; see
Default Values Codegen. - Defining which appearance is active at runtime; see
System Appearance Handling. - Snapshotting the legacy Fluent
Themeshape; the legacy packages already
own that coverage. - Snapshotting component render output; those are component-level concerns.
- Automated detection of mapping file drift; see the combined drift detection
task that covers bothflex-token-map.yamland upstream x3 pins.
Deliverables
- Per-platform snapshot test files in the design package, one per platform
(defaultFlexTokens.test.win32.ts,defaultFlexTokens.test.macos.ts,
defaultFlexTokens.test.windows.ts,defaultFlexTokens.test.ios.ts,
defaultFlexTokens.test.android.ts), each exercising all appearances
available for that platform. - Initial committed
.snapfiles generated from the current resolved
defaults plus each generated per-appearance set once
Default Values Codegen lands. - A documented update policy in the design package's
CONTRIBUTING.mdor
inline in the test source, covering when and how to run
yarn update-snapshotsand what constitutes a safe diff to commit. - A note in the Default Values Codegen task
acceptance criteria requiring that its first generation pass produces
snapshot files with no unexpected value changes. - Changesets for the design package.
Acceptance criteria
- One
.test.<platform>.tsfile exists for each of win32, macos, windows,
ios, and android, and each callstoMatchSnapshot()on the fully
resolvedFlexTokensfor every appearance that platform supports. - No test source file embeds literal token values; all resolved values are
in.snapfiles only. - All
.snapfiles are committed to the repository and appear in CI diff
output when token values change. - Running
yarn testin the design package with no code changes passes
cleanly, confirming the committed snapshots match the current resolved
values. - A
flexTokenschange that shifts a single color value in any appearance
causes exactly those snapshot entries to fail and no others. - The update procedure (
yarn update-snapshots-> diff review -> commit)
is documented and produces a reproducible result, verified by re-running
yarn testafter the snapshot update. -
yarn build,yarn lage test, andyarn lage lintpass at the
repository root, and changesets are present.
Dependencies and ordering
- Depends on Dynamic Theme Building. The
snapshot tests exercise per-appearanceFlexTokensresolution, which
requires the context-awareuseFlexTokensthat this task defines. Without
that, all appearances would snapshot identically. - Depends on Default Values Codegen. The
per-appearance token sets being snapshotted are generated by this task. The
initial snapshot files should be generated and committed as part of the same
change that lands the codegen output, so the first committed snapshots are
known-correct against the generated values. - Pairs with System Appearance Handling. The
appearance-to-AppearanceOptionsmapping determines which appearances each
platform test file must cover. The snapshot coverage list should be
finalized once that mapping is stable. - Does not block any other task in this workstream but provides regression
protection for all subsequent token-value changes.
Risks and open decisions
- Open decision. Which appearance names each platform snapshot file should
use as test-case labels. Observed: the design-tokens packages use
appearance names from the token package (e.g.,colorful,darkgray) while
AppearanceOptionsuseslight,dark,highContrast. Whether the test
labels follow the token package names, theAppearanceOptionsnames, or
both depends on the resolution in System Appearance Handling. - Open decision. Whether
darkElevatedandblack(win32) map to the
same snapshot or require distinct entries. Observed:getAliasTokens
foldsdarkElevatedinto the dark branch
(packages/theming/theme-tokens/src/getTokens.ts),
but win32blackis a distinct token set; the snapshot strategy must reflect
whatever the codegen emits. - Open decision. Whether the snapshot tests should use the context path
(injecting aThemeProviderin a test renderer) or call the default export
functions directly. Inferred: the legacy tests snapshot exported
constants, not a rendered tree; the same pattern is simpler and avoids
React-tree overhead for a token-value test. - Risk. If snapshots are introduced before Default Values Codegen
lands, they will capture the current hand-authored single-appearance defaults
and will need a full re-generation pass when codegen lands. Introducing them
in the same change as the codegen output is strongly preferred to avoid a
transitional state where snapshots exist but are known to be incomplete. - Risk. A snapshot file that is too large to review in a PR diff becomes
rubber-stamped rather than reviewed. TheFlexTokensobject has nine groups
and roughly 300 token paths; at roughly 5 bytes per value, a single-appearance
snapshot is around 1,500 characters, which is reviewable. Per-appearance
files keep each snapshot small enough to inspect. - Risk. Snapshot tests are only as stable as the Jest platform and module
resolver configuration. The design package pins its Jest platform tomacos;
runningyarn teston a developer machine with a different platform override
will pick up the wrong platform-suffixed file. The update policy must state
thatyarn update-snapshotsmust be run per platform or through CI to avoid
committing host-machine artifacts.
Evidence and references
packages/theming/default-theme/src/__tests__:
the legacy snapshot pattern this task matches, with one.test.<platform>.ts
file per platform andtoMatchSnapshot()for each appearance export.packages/theming/default-theme/src/__tests__/default-theme.test.ts:
the platform-neutral test that snapshotscreateColorAliasTokensand
createShadowAliasTokensperAppearanceOptions, analogous to what the new
tests should do forFlexTokens.packages/theming/default-theme/src/__tests__/defaultThemes.test.win32.ts:
three-appearance win32 snapshot file, illustrating the minimal test shape.packages/agentic/design/src/tokens/flex.test.ts:
current spot-assertion tests fordefaultFlexTokens, which the new snapshot
tests supplement but do not replace.packages/agentic/design/src/tokens/flexTokensFromTheme.test.ts:
projection test that remains the canonical coverage for the Fluent-to-Flex
mapping path.packages/agentic/design/src/tokens/defaultTokens.ts:
the current single-appearance hand-authored defaults that the snapshots will
initially capture.packages/agentic/design/scripts/codegen.cts:
the codegen entry point that will emit the per-appearance sets the snapshots
will cover once extended.packages/agentic/design/package.json:
theupdate-snapshotsscript andfurn.jestPlatformsetting.scripts/configs/jest/jest.config.cjs:
platform selection and.test.<platform>.tsresolution logic.packages/theming/theme-tokens/src/getTokens.ts:
AppearanceOptionsconsumer withassertNever; illustrates the current
appearance-to-token-set mapping in the legacy path.- Default Values Codegen: the task that generates
the per-appearanceFlexTokenssets these snapshots will capture. - Dynamic Theme Building: the task that makes
useFlexTokenscontext-aware and enables per-appearance resolution. - System Appearance Handling: the task that defines
the stableAppearanceOptions-to-platform-variant mapping.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading packages/agentic/design/src/tokens/flex.test.ts, flexTokensFromTheme.test.ts, useFlexTokens.ts, and the legacy default-theme snapshot tests. Check the design package scripts and platform test naming before resolving the appearance mapping with issues 4263, 4264, and 4270. Done means committed per-platform snapshots, documented update guidance, passing design and repository checks, and a changeset.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react-native, typescript
- Domain
- design, testing
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100