@fluentui-react-native/storybook-desktop-runtime 0.3.4 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -15,8 +15,9 @@ authenticated bridge, Metro, theme, and platform chrome.
15
15
  test reporting belong to the integration and harness packages.
16
16
  - Preserve the authenticated nonce, correlated request/run IDs, readiness,
17
17
  error signaling, and native story-root verification.
18
- - Keep one `ThemedRoot` around app content and chrome. Preserve explicit story
19
- background overrides and safe fallback behavior for opaque native colors.
18
+ - Keep one Flex `ThemedRoot` around app content and chrome; apply the selected
19
+ theme only to story content through the nested provider. Preserve explicit
20
+ story background overrides and safe fallback behavior for opaque native colors.
20
21
  - Keep platform-specific behavior in platform files. Preserve Win32's
21
22
  desktop-only chrome and keep macOS/Windows on upstream LiteUI.
22
23
  - Metro configuration has side effects: loading it can generate the runtime
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # @fluentui-react-native/storybook-desktop-runtime
2
2
 
3
+ ## 0.3.5
4
+
5
+ ### Patch Changes
6
+
7
+ - 06b458f: Keep the Storybook chrome and story canvas on the Flex theme. The Theme selector now applies Flex or Fluent only to
8
+ story content, while scheme, contrast, and tone still apply to the chrome.
9
+ - 06b458f: Read Storybook chrome colors through the design package's interactive color envelope.
10
+ - 06b458f: Replace the fixed Storybook theme buttons with Theme (Flex or Fluent), Scheme (light, dark, or system), Contrast, and Tone dropdowns. The toolbar now defaults to Flex light, standard contrast, and cool tone, independent of the host appearance.
11
+ - Updated dependencies [06b458f]
12
+ - Updated dependencies [06b458f]
13
+ - Updated dependencies [06b458f]
14
+ - Updated dependencies [ccb458e]
15
+ - Updated dependencies [06b458f]
16
+ - Updated dependencies [06b458f]
17
+ - Updated dependencies [06b458f]
18
+ - Updated dependencies [ccb458e]
19
+ - Updated dependencies [06b458f]
20
+ - @fluentui-react-native/default-theme@0.28.0
21
+ - @fluentui-react-native/design@0.6.0
22
+ - @fluentui-react-native/native-core@0.2.0
23
+
3
24
  ## 0.3.4
4
25
 
5
26
  ### Patch Changes
@@ -1,3 +1,17 @@
1
1
  import * as React from 'react';
2
+ import type { ColorTone } from '@fluentui-react-native/design';
3
+ /** The theme configuration selected in the Storybook toolbar. */
4
+ export type StorybookThemeSelection = {
5
+ /** The story theme: the package's Flex defaults or the legacy Fluent default theme. The chrome always uses Flex. */
6
+ source: 'flex' | 'fluent';
7
+ colorScheme: 'light' | 'dark' | 'system';
8
+ contrast: 'standard' | 'highContrast';
9
+ /** Flex palette and interaction tone. Legacy Fluent themes have no tone. */
10
+ colorTone: ColorTone;
11
+ };
12
+ /** A deterministic default that does not depend on the host appearance. */
13
+ export declare const defaultStorybookThemeSelection: Readonly<StorybookThemeSelection>;
2
14
  export declare function StorybookThemeHost({ children }: React.PropsWithChildren): import("react/jsx-runtime").JSX.Element;
15
+ /** Applies the selected story theme inside the Flex chrome without adding a view or a second input root. */
16
+ export declare function StorybookStoryTheme({ children }: React.PropsWithChildren): import("react/jsx-runtime").JSX.Element;
3
17
  //# sourceMappingURL=StorybookTheme.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"StorybookTheme.d.ts","sourceRoot":"","sources":["../src/StorybookTheme.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAwB/B,wBAAgB,kBAAkB,CAAC,EAAE,QAAQ,EAAE,EAAE,KAAK,CAAC,iBAAiB,2CAWvE"}
1
+ {"version":3,"file":"StorybookTheme.d.ts","sourceRoot":"","sources":["../src/StorybookTheme.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAK/B,OAAO,KAAK,EAAE,SAAS,EAAuC,MAAM,+BAA+B,CAAC;AAIpG,iEAAiE;AACjE,MAAM,MAAM,uBAAuB,GAAG;IACpC,oHAAoH;IACpH,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC1B,WAAW,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;IACzC,QAAQ,EAAE,UAAU,GAAG,cAAc,CAAC;IACtC,4EAA4E;IAC5E,SAAS,EAAE,SAAS,CAAC;CACtB,CAAC;AAYF,2EAA2E;AAC3E,eAAO,MAAM,8BAA8B,EAAE,QAAQ,CAAC,uBAAuB,CAK3E,CAAC;AAqDH,wBAAgB,kBAAkB,CAAC,EAAE,QAAQ,EAAE,EAAE,KAAK,CAAC,iBAAiB,2CAkBvE;AAED,4GAA4G;AAC5G,wBAAgB,mBAAmB,CAAC,EAAE,QAAQ,EAAE,EAAE,KAAK,CAAC,iBAAiB,2CAUxE"}
@@ -1,30 +1,90 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import * as React from 'react';
3
3
  import { Pressable, StyleSheet, Text, View } from 'react-native';
4
4
  import { createDefaultTheme } from '@fluentui-react-native/default-theme';
5
- import { ThemedRoot, themedStyleSheetFactory, useThemeState } from '@fluentui-react-native/design';
5
+ import { FlexThemeReference, ThemedRoot, ThemeProvider, themedStyleSheetFactory, useThemeState } from '@fluentui-react-native/design';
6
6
  import { useDesktopStorybookTestID } from './DesktopStorybookConfig';
7
- const themeChoiceNames = ['none', 'light', 'dark', 'highContrast'];
8
- const themeChoices = {
9
- none: { label: 'Default Flex' },
10
- light: { label: 'Light', theme: createDefaultTheme({ appearance: 'light' }) },
11
- dark: { label: 'Dark', theme: createDefaultTheme({ appearance: 'dark' }) },
12
- highContrast: { label: 'High contrast', theme: createDefaultTheme({ appearance: 'highContrast' }) },
7
+ /** A deterministic default that does not depend on the host appearance. */
8
+ export const defaultStorybookThemeSelection = Object.freeze({
9
+ source: 'flex',
10
+ colorScheme: 'light',
11
+ contrast: 'standard',
12
+ colorTone: 'cool',
13
+ });
14
+ function themeAxis(key, id, label, options) {
15
+ return { key, id, label, options };
16
+ }
17
+ const themeAxes = [
18
+ themeAxis('source', 'source', 'Theme', [
19
+ { value: 'flex', label: 'Flex' },
20
+ { value: 'fluent', label: 'Fluent' },
21
+ ]),
22
+ themeAxis('colorScheme', 'scheme', 'Scheme', [
23
+ { value: 'light', label: 'Light' },
24
+ { value: 'dark', label: 'Dark' },
25
+ { value: 'system', label: 'System' },
26
+ ]),
27
+ themeAxis('contrast', 'contrast', 'Contrast', [
28
+ { value: 'standard', label: 'Standard' },
29
+ { value: 'highContrast', label: 'High' },
30
+ ]),
31
+ themeAxis('colorTone', 'tone', 'Tone', [
32
+ { value: 'cool', label: 'Cool' },
33
+ { value: 'warm', label: 'Warm' },
34
+ ]),
35
+ ];
36
+ // Each source resolves every appearance itself, so selection changes only the appearance request.
37
+ const themeSources = {
38
+ flex: new FlexThemeReference(),
39
+ fluent: createDefaultTheme({ appearance: 'dynamic' }),
13
40
  };
41
+ function appearanceFromSelection({ source, colorScheme, contrast, colorTone }) {
42
+ // Omitting tone for Fluent keeps the resolved appearance at its cool default.
43
+ return source === 'flex' ? { colorScheme, contrast, colorTone } : { colorScheme, contrast };
44
+ }
45
+ function isAxisDisabled(key, selection) {
46
+ return key === 'colorTone' && selection.source !== 'flex';
47
+ }
48
+ const StoryThemeContext = React.createContext(undefined);
14
49
  export function StorybookThemeHost({ children }) {
15
- const [selectedName, setSelectedName] = React.useState('none');
50
+ const [selection, setSelection] = React.useState(defaultStorybookThemeSelection);
51
+ const storyTheme = React.useMemo(() => ({ theme: themeSources[selection.source], appearance: appearanceFromSelection(selection) }), [selection]);
16
52
  const testID = useDesktopStorybookTestID('app-root');
17
- return (_jsx(ThemedRoot, { style: layoutStyles.root, testID: testID, theme: themeChoices[selectedName].theme, children: _jsx(StorybookThemeContent, { selectedName: selectedName, onSelect: setSelectedName, children: children }) }));
53
+ // The chrome always uses Flex at the selected appearance; only story content follows the selected source.
54
+ return (_jsx(ThemedRoot, { appearance: storyTheme.appearance, style: layoutStyles.root, testID: testID, theme: themeSources.flex, children: _jsx(StoryThemeContext.Provider, { value: storyTheme, children: _jsx(StorybookThemeContent, { selection: selection, onSelectionChange: setSelection, children: children }) }) }));
18
55
  }
19
- function StorybookThemeContent({ children, selectedName, onSelect, }) {
56
+ /** Applies the selected story theme inside the Flex chrome without adding a view or a second input root. */
57
+ export function StorybookStoryTheme({ children }) {
58
+ const storyTheme = React.useContext(StoryThemeContext);
59
+ if (!storyTheme) {
60
+ return _jsx(_Fragment, { children: children });
61
+ }
62
+ return (_jsx(ThemeProvider, { appearance: storyTheme.appearance, theme: storyTheme.theme, children: children }));
63
+ }
64
+ function StorybookThemeContent({ children, selection, onSelectionChange, }) {
20
65
  const styles = getStyles(useThemeState());
21
66
  const toolbarTestID = useDesktopStorybookTestID('theme-toolbar');
22
- const optionTestID = useDesktopStorybookTestID('theme');
23
- return (_jsxs(View, { style: styles.root, children: [_jsxs(View, { accessibilityRole: "toolbar", style: styles.header, testID: toolbarTestID, children: [_jsx(Text, { style: styles.label, children: "Theme" }), themeChoiceNames.map((name) => {
24
- const choice = themeChoices[name];
25
- const selected = name === selectedName;
26
- return (_jsx(Pressable, { accessibilityRole: "button", accessibilityState: { selected }, onPress: () => onSelect(name), style: ({ pressed }) => [styles.option, selected && styles.selectedOption, pressed && styles.pressedOption], testID: `${optionTestID}-${name}`, children: _jsx(Text, { style: [styles.optionText, selected && styles.selectedOptionText], children: choice.label }) }, name));
27
- })] }), _jsx(View, { style: layoutStyles.root, children: children })] }));
67
+ const axisTestID = useDesktopStorybookTestID('theme');
68
+ const [openAxis, setOpenAxis] = React.useState();
69
+ const select = (key, value) => {
70
+ onSelectionChange((current) => (current[key] === value ? current : { ...current, [key]: value }));
71
+ setOpenAxis(undefined);
72
+ };
73
+ return (_jsxs(View, { style: styles.root, children: [_jsx(View, { accessibilityRole: "toolbar", style: styles.header, testID: toolbarTestID, children: themeAxes.map((axis) => {
74
+ const disabled = isAxisDisabled(axis.key, selection);
75
+ return (_jsx(ThemeAxisDropdown, { axis: axis, disabled: disabled, open: !disabled && openAxis === axis.key, styles: styles, testID: `${axisTestID}-${axis.id}`, value: disabled ? defaultStorybookThemeSelection[axis.key] : selection[axis.key], onSelect: (value) => select(axis.key, value), onToggle: () => setOpenAxis((current) => (current === axis.key ? undefined : axis.key)) }, axis.key));
76
+ }) }), _jsx(View, { style: layoutStyles.root, children: children })] }));
77
+ }
78
+ function ThemeAxisDropdown({ axis, disabled, open, styles, testID, value, onSelect, onToggle }) {
79
+ const selectedLabel = axis.options.find((option) => option.value === value)?.label ?? value;
80
+ return (_jsxs(View, { style: styles.field, children: [_jsx(Text, { style: [styles.label, disabled && styles.disabledText], children: axis.label }), _jsxs(View, { children: [_jsxs(Pressable, { accessibilityLabel: axis.label, accessibilityRole: "combobox", accessibilityState: { disabled, expanded: open }, accessibilityValue: { text: selectedLabel }, disabled: disabled, onPress: onToggle, style: ({ pressed }) => [styles.option, styles.trigger, pressed && styles.pressedOption, disabled && styles.disabledOption], testID: testID, children: [_jsx(Text, { style: [styles.optionText, disabled && styles.disabledText], children: selectedLabel }), _jsx(Text, { style: [styles.chevron, disabled && styles.disabledText], children: open ? '\u25b4' : '\u25be' })] }), open ? (_jsx(View, { accessibilityRole: "list", style: styles.menu, testID: `${testID}-menu`, children: axis.options.map((option) => {
81
+ const selected = option.value === value;
82
+ return (_jsx(Pressable, { accessibilityRole: "button", accessibilityState: { selected }, onPress: () => onSelect(option.value), style: ({ pressed }) => [
83
+ styles.menuOption,
84
+ selected && styles.selectedOption,
85
+ pressed && (selected ? styles.pressedSelectedOption : styles.pressedMenuOption),
86
+ ], testID: `${testID}-${option.value}`, children: _jsx(Text, { style: [styles.optionText, selected && styles.selectedOptionText], children: option.label }) }, option.value));
87
+ }) })) : null] })] }));
28
88
  }
29
89
  const layoutStyles = StyleSheet.create({
30
90
  root: {
@@ -34,48 +94,91 @@ const layoutStyles = StyleSheet.create({
34
94
  const getStyles = themedStyleSheetFactory('StorybookTheme', ({ tokens }) => StyleSheet.create({
35
95
  root: {
36
96
  flex: 1,
37
- backgroundColor: tokens.color.surfaceNeutralFar,
97
+ backgroundColor: tokens.color.surfaceNeutralFar.rest,
38
98
  },
39
99
  header: {
40
- alignItems: 'center',
41
- backgroundColor: tokens.color.backgroundNeutralSubtle,
42
- borderBottomColor: tokens.color.strokeNeutralSubtle,
100
+ alignItems: 'flex-start',
101
+ backgroundColor: tokens.color.backgroundNeutralSubtle.rest,
102
+ borderBottomColor: tokens.color.strokeNeutralSubtle.rest,
43
103
  borderBottomWidth: 1,
44
104
  flexDirection: 'row',
45
105
  flexWrap: 'wrap',
46
- gap: 8,
106
+ gap: 16,
47
107
  minHeight: 44,
48
108
  paddingHorizontal: 12,
49
- paddingVertical: 6,
109
+ paddingVertical: 8,
110
+ },
111
+ field: {
112
+ alignItems: 'flex-start',
113
+ flexDirection: 'row',
114
+ gap: 6,
50
115
  },
51
116
  label: {
52
- color: tokens.color.foregroundNeutralPrimary,
117
+ color: tokens.color.foregroundNeutralPrimary.rest,
53
118
  fontSize: 14,
54
119
  fontWeight: '600',
55
- marginEnd: 4,
120
+ paddingVertical: 5,
56
121
  },
57
122
  option: {
58
- backgroundColor: tokens.color.backgroundNeutralLoud,
59
- borderColor: tokens.color.strokeNeutralSoft,
123
+ backgroundColor: tokens.color.surfaceNeutralNearer.rest,
124
+ borderColor: tokens.color.strokeNeutralLoud.rest,
60
125
  borderRadius: 4,
61
126
  borderWidth: 1,
62
127
  paddingHorizontal: 10,
63
128
  paddingVertical: 5,
64
129
  },
65
- selectedOption: {
66
- backgroundColor: tokens.color.backgroundBrandHeavy,
67
- borderColor: tokens.color.strokeBrandLoud,
130
+ trigger: {
131
+ alignItems: 'center',
132
+ flexDirection: 'row',
133
+ gap: 8,
134
+ justifyContent: 'space-between',
135
+ minWidth: 96,
68
136
  },
69
137
  pressedOption: {
70
- opacity: 0.75,
138
+ backgroundColor: tokens.color.surfaceNeutralNearer.pressed,
139
+ borderColor: tokens.color.strokeNeutralLoud.pressed,
140
+ },
141
+ disabledOption: {
142
+ backgroundColor: tokens.color.backgroundNeutralDisabled.rest,
143
+ borderColor: tokens.color.strokeNeutralDisabled.rest,
144
+ },
145
+ menu: {
146
+ backgroundColor: tokens.color.surfaceNeutralNearer.rest,
147
+ borderColor: tokens.color.strokeNeutralSubtle.rest,
148
+ borderRadius: 4,
149
+ borderWidth: 1,
150
+ gap: 2,
151
+ marginTop: 4,
152
+ padding: 2,
153
+ },
154
+ menuOption: {
155
+ backgroundColor: tokens.color.backgroundNeutralTransparent.rest,
156
+ borderRadius: 3,
157
+ paddingHorizontal: 10,
158
+ paddingVertical: 5,
159
+ },
160
+ pressedMenuOption: {
161
+ backgroundColor: tokens.color.backgroundNeutralTransparent.pressed,
162
+ },
163
+ selectedOption: {
164
+ backgroundColor: tokens.color.backgroundNeutralSoft.rest,
165
+ },
166
+ pressedSelectedOption: {
167
+ backgroundColor: tokens.color.backgroundNeutralSoft.pressed,
71
168
  },
72
169
  optionText: {
73
- color: tokens.color.foregroundNeutralPrimary,
170
+ color: tokens.color.foregroundNeutralPrimary.rest,
171
+ fontSize: 12,
172
+ },
173
+ chevron: {
174
+ color: tokens.color.foregroundNeutralSecondary.rest,
74
175
  fontSize: 12,
75
176
  },
76
177
  selectedOptionText: {
77
- color: tokens.color.foregroundBrandOnloud,
78
178
  fontWeight: '600',
79
179
  },
180
+ disabledText: {
181
+ color: tokens.color.foregroundNeutralDisabled.rest,
182
+ },
80
183
  }));
81
184
  //# sourceMappingURL=StorybookTheme.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"StorybookTheme.js","sourceRoot":"","sources":["../src/StorybookTheme.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sCAAsC,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAGnG,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAOrE,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc,CAAU,CAAC;AAG5E,MAAM,YAAY,GAAyC;IACzD,IAAI,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE;IAC/B,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,kBAAkB,CAAC,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,EAAE;IAC7E,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,EAAE;IAC1E,YAAY,EAAE,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,kBAAkB,CAAC,EAAE,UAAU,EAAE,cAAc,EAAE,CAAC,EAAE;CACpG,CAAC;AAEF,MAAM,UAAU,kBAAkB,CAAC,EAAE,QAAQ,EAA2B;IACtE,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAkB,MAAM,CAAC,CAAC;IAChF,MAAM,MAAM,GAAG,yBAAyB,CAAC,UAAU,CAAC,CAAC;IAErD,OAAO,CACL,KAAC,UAAU,IAAC,KAAK,EAAE,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,CAAC,YAAY,CAAC,CAAC,KAAK,YAC3F,KAAC,qBAAqB,IAAC,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,eAAe,YACzE,QAAQ,GACa,GACb,CACd,CAAC;AACJ,CAAC;AAED,SAAS,qBAAqB,CAAC,EAC7B,QAAQ,EACR,YAAY,EACZ,QAAQ,GAC8F;IACtG,MAAM,MAAM,GAAG,SAAS,CAAC,aAAa,EAAE,CAAC,CAAC;IAC1C,MAAM,aAAa,GAAG,yBAAyB,CAAC,eAAe,CAAC,CAAC;IACjE,MAAM,YAAY,GAAG,yBAAyB,CAAC,OAAO,CAAC,CAAC;IAExD,OAAO,CACL,MAAC,IAAI,IAAC,KAAK,EAAE,MAAM,CAAC,IAAI,aACtB,MAAC,IAAI,IAAC,iBAAiB,EAAC,SAAS,EAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,aAC3E,KAAC,IAAI,IAAC,KAAK,EAAE,MAAM,CAAC,KAAK,sBAAc,EACtC,gBAAgB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;wBAC7B,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;wBAClC,MAAM,QAAQ,GAAG,IAAI,KAAK,YAAY,CAAC;wBACvC,OAAO,CACL,KAAC,SAAS,IACR,iBAAiB,EAAC,QAAQ,EAC1B,kBAAkB,EAAE,EAAE,QAAQ,EAAE,EAEhC,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,EAC7B,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,IAAI,MAAM,CAAC,cAAc,EAAE,OAAO,IAAI,MAAM,CAAC,aAAa,CAAC,EAC3G,MAAM,EAAE,GAAG,YAAY,IAAI,IAAI,EAAE,YAEjC,KAAC,IAAI,IAAC,KAAK,EAAE,CAAC,MAAM,CAAC,UAAU,EAAE,QAAQ,IAAI,MAAM,CAAC,kBAAkB,CAAC,YAAG,MAAM,CAAC,KAAK,GAAQ,IALzF,IAAI,CAMC,CACb,CAAC;oBACJ,CAAC,CAAC,IACG,EACP,KAAC,IAAI,IAAC,KAAK,EAAE,YAAY,CAAC,IAAI,YAAG,QAAQ,GAAQ,IAC5C,CACR,CAAC;AACJ,CAAC;AAED,MAAM,YAAY,GAAG,UAAU,CAAC,MAAM,CAAC;IACrC,IAAI,EAAE;QACJ,IAAI,EAAE,CAAC;KACR;CACF,CAAC,CAAC;AAEH,MAAM,SAAS,GAAG,uBAAuB,CAAC,gBAAgB,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CACzE,UAAU,CAAC,MAAM,CAAC;IAChB,IAAI,EAAE;QACJ,IAAI,EAAE,CAAC;QACP,eAAe,EAAE,MAAM,CAAC,KAAK,CAAC,iBAAiB;KAChD;IACD,MAAM,EAAE;QACN,UAAU,EAAE,QAAQ;QACpB,eAAe,EAAE,MAAM,CAAC,KAAK,CAAC,uBAAuB;QACrD,iBAAiB,EAAE,MAAM,CAAC,KAAK,CAAC,mBAAmB;QACnD,iBAAiB,EAAE,CAAC;QACpB,aAAa,EAAE,KAAK;QACpB,QAAQ,EAAE,MAAM;QAChB,GAAG,EAAE,CAAC;QACN,SAAS,EAAE,EAAE;QACb,iBAAiB,EAAE,EAAE;QACrB,eAAe,EAAE,CAAC;KACnB;IACD,KAAK,EAAE;QACL,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,wBAAwB;QAC5C,QAAQ,EAAE,EAAE;QACZ,UAAU,EAAE,KAAK;QACjB,SAAS,EAAE,CAAC;KACb;IACD,MAAM,EAAE;QACN,eAAe,EAAE,MAAM,CAAC,KAAK,CAAC,qBAAqB;QACnD,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,iBAAiB;QAC3C,YAAY,EAAE,CAAC;QACf,WAAW,EAAE,CAAC;QACd,iBAAiB,EAAE,EAAE;QACrB,eAAe,EAAE,CAAC;KACnB;IACD,cAAc,EAAE;QACd,eAAe,EAAE,MAAM,CAAC,KAAK,CAAC,oBAAoB;QAClD,WAAW,EAAE,MAAM,CAAC,KAAK,CAAC,eAAe;KAC1C;IACD,aAAa,EAAE;QACb,OAAO,EAAE,IAAI;KACd;IACD,UAAU,EAAE;QACV,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,wBAAwB;QAC5C,QAAQ,EAAE,EAAE;KACb;IACD,kBAAkB,EAAE;QAClB,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,qBAAqB;QACzC,UAAU,EAAE,KAAK;KAClB;CACF,CAAC,CACH,CAAC"}
1
+ {"version":3,"file":"StorybookTheme.js","sourceRoot":"","sources":["../src/StorybookTheme.tsx"],"names":[],"mappings":"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"}
@@ -1,8 +1,9 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
2
  import * as React from 'react';
3
- import { StyleSheet, Text } from 'react-native';
3
+ import { Appearance, StyleSheet, Text } from 'react-native';
4
4
  import { fireEvent, render } from '@testing-library/react-native';
5
- import { useRootSettings, useThemeState } from '@fluentui-react-native/design';
5
+ import { FlexThemeReference, useRootSettings, useThemeAppearance, useThemeState } from '@fluentui-react-native/design';
6
+ import { ThemeContext } from '@fluentui-react-native/design/theming';
6
7
  import { defaultFlexTokens } from '@fluentui-react-native/design/testing';
7
8
  import { theme as lightTheme } from '@storybook/react-native-theming';
8
9
  import { createDesktopStorybookApp } from './createDesktopStorybookApp';
@@ -14,11 +15,16 @@ function createTestApp(storyBackgroundColor) {
14
15
  const roots = [];
15
16
  const bridgeRoots = [];
16
17
  const themes = [];
18
+ const chromeThemes = [];
19
+ const appearances = [];
20
+ const legacyThemes = [];
17
21
  const mounted = jest.fn();
18
22
  const unmounted = jest.fn();
19
23
  function Story() {
20
24
  roots.push(useRootSettings());
21
25
  themes.push(useThemeState());
26
+ appearances.push(useThemeAppearance());
27
+ legacyThemes.push(React.useContext(ThemeContext));
22
28
  React.useEffect(() => {
23
29
  mounted();
24
30
  return unmounted;
@@ -29,7 +35,10 @@ function createTestApp(storyBackgroundColor) {
29
35
  bridgeRoots.push(useRootSettings());
30
36
  return null;
31
37
  });
32
- jest.mocked(StorybookUIComponent).mockImplementation(({ children }) => _jsx(_Fragment, { children: children }));
38
+ jest.mocked(StorybookUIComponent).mockImplementation(({ children }) => {
39
+ chromeThemes.push(useThemeState());
40
+ return _jsx(_Fragment, { children: children });
41
+ });
33
42
  const getStorybookUI = jest.fn((params) => {
34
43
  const UI = params?.CustomUIComponent;
35
44
  if (!UI) {
@@ -38,21 +47,55 @@ function createTestApp(storyBackgroundColor) {
38
47
  return () => (_jsx(UI, { storyBackgroundColor: storyBackgroundColor, storyHash: {}, setStory: jest.fn(), storage: { getItem: () => null, setItem: () => undefined }, theme: lightTheme, children: _jsx(Story, {}) }));
39
48
  });
40
49
  const App = createDesktopStorybookApp({ getStorybookUI }, { enableWebsockets: false, testIDPrefix: 'test' });
41
- return { App, roots, bridgeRoots, themes, mounted, unmounted, getStorybookUI };
50
+ return { App, roots, bridgeRoots, themes, chromeThemes, appearances, legacyThemes, mounted, unmounted, getStorybookUI };
51
+ }
52
+ async function selectTheme(scene, axis, value) {
53
+ await fireEvent.press(scene.getByTestId(`test-theme-${axis}`));
54
+ await fireEvent.press(scene.getByTestId(`test-theme-${axis}-${value}`));
42
55
  }
56
+ function latestStorybookTheme() {
57
+ const calls = jest.mocked(StorybookUIComponent).mock.calls;
58
+ return calls[calls.length - 1][0].theme;
59
+ }
60
+ const selectionSteps = [
61
+ ['scheme', 'dark'],
62
+ ['tone', 'warm'],
63
+ ['contrast', 'highContrast'],
64
+ ['source', 'fluent'],
65
+ ['contrast', 'standard'],
66
+ ['scheme', 'light'],
67
+ ['source', 'flex'],
68
+ ['tone', 'cool'],
69
+ ];
43
70
  afterEach(() => {
44
71
  jest.clearAllMocks();
45
72
  jest.restoreAllMocks();
46
73
  });
47
74
  describe('Storybook app theme root', () => {
48
- it('places the toolbar, UI, story, and runtime bridge in the same default Flex scene', async () => {
49
- const { App, roots, bridgeRoots, themes } = createTestApp();
75
+ it('places the toolbar, UI, story, and runtime bridge in one deterministic default Flex scene', async () => {
76
+ const { App, roots, bridgeRoots, themes, chromeThemes, appearances, legacyThemes } = createTestApp();
50
77
  const scene = await render(_jsx(App, {}));
51
78
  expect(scene.getByTestId('test-app-root')).toBeOnTheScreen();
52
79
  expect(scene.getByTestId('test-theme-toolbar')).toBeOnTheScreen();
53
80
  expect(scene.getByTestId('selected-story')).toBeOnTheScreen();
54
- expect(scene.getByRole('button', { name: 'Default Flex' })).toBeOnTheScreen();
81
+ for (const [axis, label] of [
82
+ ['source', 'Flex'],
83
+ ['scheme', 'Light'],
84
+ ['contrast', 'Standard'],
85
+ ['tone', 'Cool'],
86
+ ]) {
87
+ const trigger = scene.getByTestId(`test-theme-${axis}`);
88
+ expect(trigger.props.accessibilityValue).toEqual({ text: label });
89
+ expect(trigger.props.accessibilityState).toEqual({ disabled: false, expanded: false });
90
+ }
91
+ expect(appearances[0]).toEqual({
92
+ requested: { colorScheme: 'light', contrast: 'standard', interfaceLevel: 'system', colorTone: 'cool' },
93
+ resolved: { colorScheme: 'light', contrast: 'standard', interfaceLevel: 'base', colorTone: 'cool' },
94
+ });
95
+ expect(themes[0].appearance).toBe(appearances[0].resolved);
55
96
  expect(themes[0].tokens).toEqual(defaultFlexTokens);
97
+ expect(chromeThemes[0]).toBe(themes[0]);
98
+ expect(legacyThemes[0]).toBeUndefined();
56
99
  expect(bridgeRoots.every((root) => root === roots[0])).toBe(true);
57
100
  expect(roots[0].inputModality).toBe('pointer');
58
101
  });
@@ -71,58 +114,132 @@ describe('Storybook app theme root', () => {
71
114
  expect(roots[0].inputModality).toBe('pointer');
72
115
  expect(roots).toHaveLength(renderCount);
73
116
  });
74
- it.each(['none', 'light', 'dark'])('keeps the %s canvas distinct from the Secondary Button fill', async (choice) => {
117
+ it('opens one axis menu at a time and closes it after a selection', async () => {
118
+ const { App } = createTestApp();
119
+ const scene = await render(_jsx(App, {}));
120
+ await fireEvent.press(scene.getByTestId('test-theme-scheme'));
121
+ expect(scene.getByTestId('test-theme-scheme').props.accessibilityState.expanded).toBe(true);
122
+ expect(scene.getByTestId('test-theme-scheme-light').props.accessibilityState.selected).toBe(true);
123
+ expect(scene.getByTestId('test-theme-scheme-system')).toBeOnTheScreen();
124
+ await fireEvent.press(scene.getByTestId('test-theme-source'));
125
+ expect(scene.queryByTestId('test-theme-scheme-menu')).toBeNull();
126
+ expect(scene.getByTestId('test-theme-source-menu')).toBeOnTheScreen();
127
+ await fireEvent.press(scene.getByTestId('test-theme-source'));
128
+ expect(scene.queryByTestId('test-theme-source-menu')).toBeNull();
129
+ await selectTheme(scene, 'scheme', 'dark');
130
+ expect(scene.queryByTestId('test-theme-scheme-menu')).toBeNull();
131
+ expect(scene.getByTestId('test-theme-scheme').props.accessibilityValue).toEqual({ text: 'Dark' });
132
+ });
133
+ it('applies scheme, contrast, and tone to the Flex defaults', async () => {
75
134
  const { App, themes } = createTestApp();
76
135
  const scene = await render(_jsx(App, {}));
77
- await fireEvent.press(scene.getByTestId(`test-theme-${choice}`));
78
- const state = themes[themes.length - 1];
79
- const calls = jest.mocked(StorybookUIComponent).mock.calls;
80
- const theme = calls[calls.length - 1][0].theme;
81
- expect(theme.background.content).toBe(state.tokens.color.surfaceNeutralFar);
136
+ const coolLight = themes[themes.length - 1].tokens;
137
+ await selectTheme(scene, 'tone', 'warm');
138
+ const warmLight = themes[themes.length - 1];
139
+ expect(warmLight.appearance).toEqual({ colorScheme: 'light', contrast: 'standard', interfaceLevel: 'base', colorTone: 'warm' });
140
+ expect(warmLight.tokens.color.surfaceNeutralFar.rest).not.toBe(coolLight.color.surfaceNeutralFar.rest);
141
+ await selectTheme(scene, 'scheme', 'dark');
142
+ const warmDark = themes[themes.length - 1];
143
+ expect(warmDark.appearance.colorScheme).toBe('dark');
144
+ expect(warmDark.tokens.color.surfaceNeutralFar.rest).not.toBe(warmLight.tokens.color.surfaceNeutralFar.rest);
145
+ expect(warmDark.tokens.color.backgroundBrandLoud.hover).not.toBe(warmDark.tokens.color.backgroundBrandLoud.rest);
146
+ await selectTheme(scene, 'contrast', 'highContrast');
147
+ const highContrast = themes[themes.length - 1];
148
+ expect(highContrast.appearance.contrast).toBe('highContrast');
149
+ expect(highContrast.tokens.color.backgroundBrandLoud.hover).toBe(highContrast.tokens.color.backgroundBrandLoud.rest);
150
+ expect(highContrast.tokens.color.backgroundBrandLoud.pressed).toBe(highContrast.tokens.color.backgroundBrandLoud.rest);
151
+ });
152
+ it('follows the platform scheme only when System is selected', async () => {
153
+ jest.spyOn(Appearance, 'getColorScheme').mockReturnValue('dark');
154
+ const { App, themes, appearances } = createTestApp();
155
+ const scene = await render(_jsx(App, {}));
156
+ expect(themes[themes.length - 1].appearance.colorScheme).toBe('light');
157
+ await selectTheme(scene, 'scheme', 'system');
158
+ expect(appearances[appearances.length - 1].requested.colorScheme).toBe('system');
159
+ expect(themes[themes.length - 1].appearance.colorScheme).toBe('dark');
160
+ });
161
+ it('applies the legacy Fluent default theme to stories without tone and restores the Flex tone', async () => {
162
+ const { App, themes, chromeThemes, legacyThemes } = createTestApp();
163
+ const scene = await render(_jsx(App, {}));
164
+ await selectTheme(scene, 'tone', 'warm');
165
+ await selectTheme(scene, 'source', 'fluent');
166
+ const tone = scene.getByTestId('test-theme-tone');
167
+ expect(tone.props.accessibilityState).toEqual({ disabled: true, expanded: false });
168
+ expect(tone.props.accessibilityValue).toEqual({ text: 'Cool' });
169
+ await fireEvent.press(tone);
170
+ expect(scene.queryByTestId('test-theme-tone-menu')).toBeNull();
171
+ expect(themes[themes.length - 1].appearance.colorTone).toBe('cool');
172
+ expect(themes[themes.length - 1].tokens).not.toEqual(defaultFlexTokens);
173
+ expect(chromeThemes[chromeThemes.length - 1].tokens).toEqual(defaultFlexTokens);
174
+ expect(legacyThemes[legacyThemes.length - 1]?.host.appearance).toBe('light');
175
+ await selectTheme(scene, 'scheme', 'dark');
176
+ expect(legacyThemes[legacyThemes.length - 1]?.host.appearance).toBe('dark');
177
+ await selectTheme(scene, 'contrast', 'highContrast');
178
+ expect(legacyThemes[legacyThemes.length - 1]?.host.appearance).toBe('highContrast');
179
+ await selectTheme(scene, 'source', 'flex');
180
+ expect(legacyThemes[legacyThemes.length - 1]).toBeUndefined();
181
+ expect(scene.getByTestId('test-theme-tone').props.accessibilityValue).toEqual({ text: 'Warm' });
182
+ expect(themes[themes.length - 1].appearance.colorTone).toBe('warm');
183
+ });
184
+ it.each([
185
+ ['flex', 'light'],
186
+ ['flex', 'dark'],
187
+ ['fluent', 'light'],
188
+ ['fluent', 'dark'],
189
+ ])('keeps the Flex canvas distinct from the %s %s Secondary Button fill', async (source, scheme) => {
190
+ const { App, themes, chromeThemes } = createTestApp();
191
+ const scene = await render(_jsx(App, {}));
192
+ await selectTheme(scene, 'source', source);
193
+ await selectTheme(scene, 'scheme', scheme);
194
+ const story = themes[themes.length - 1];
195
+ const chrome = chromeThemes[chromeThemes.length - 1];
196
+ const theme = latestStorybookTheme();
197
+ expect(theme.background.content).toBe(chrome.tokens.color.surfaceNeutralFar.rest);
82
198
  expect(theme.background.content).toBe(theme.background.preview);
83
- expect(theme.background.content).not.toBe(state.tokens.color.backgroundNeutralSubtle);
199
+ expect(theme.background.content).not.toBe(story.tokens.color.backgroundNeutralSubtle.rest);
84
200
  });
85
201
  it.each(['#ffeedd', 'transparent'])('preserves the explicit %s story background across theme changes', async (storyBackgroundColor) => {
86
202
  const { App } = createTestApp(storyBackgroundColor);
87
203
  const scene = await render(_jsx(App, {}));
88
- for (const choice of ['none', 'dark', 'light']) {
89
- await fireEvent.press(scene.getByTestId(`test-theme-${choice}`));
204
+ for (const [axis, value] of selectionSteps) {
205
+ await selectTheme(scene, axis, value);
90
206
  const calls = jest.mocked(StorybookUIComponent).mock.calls;
91
207
  expect(calls[calls.length - 1][0].storyBackgroundColor).toBe(storyBackgroundColor);
92
208
  }
93
209
  });
94
- it('switches chrome and story colors together without recreating Storybook or resetting the scene', async () => {
95
- const warn = jest.spyOn(console, 'warn').mockImplementation();
96
- const { App, roots, themes, mounted, unmounted, getStorybookUI } = createTestApp();
210
+ it('switches Flex chrome and selected story colors together without recreating Storybook or resetting the scene', async () => {
211
+ const flex = new FlexThemeReference();
212
+ const { App, roots, themes, chromeThemes, mounted, unmounted, getStorybookUI } = createTestApp();
97
213
  const scene = await render(_jsx(App, {}));
98
214
  const root = roots[0];
99
- const defaultBackground = themes[0].tokens.color.surfaceNeutralFar;
100
- const backgrounds = {};
215
+ const defaultBackground = themes[0].tokens.color.surfaceNeutralFar.rest;
216
+ const backgrounds = new Set([defaultBackground]);
217
+ let source = 'flex';
101
218
  await fireEvent(scene.getByTestId('test-app-root'), 'keyDownCapture', { nativeEvent: { key: 'Tab' } });
102
- for (const choice of ['dark', 'highContrast', 'light', 'none']) {
103
- await fireEvent.press(scene.getByTestId(`test-theme-${choice}`));
104
- const state = themes[themes.length - 1];
105
- const calls = jest.mocked(StorybookUIComponent).mock.calls;
106
- const theme = calls[calls.length - 1][0].theme;
107
- backgrounds[choice] = theme.background.app;
108
- expect(scene.getByTestId(`test-theme-${choice}`).props.accessibilityState.selected).toBe(true);
109
- expect(StyleSheet.flatten(scene.getByTestId('test-theme-toolbar').props.style).backgroundColor).toBe(state.tokens.color.backgroundNeutralSubtle);
110
- expect(StyleSheet.flatten(scene.getByText('Theme').props.style).color).toBe(state.tokens.color.foregroundNeutralPrimary);
111
- const foreground = theme.base === 'dark' ? state.tokens.color.fixedWhite : state.tokens.color.fixedBlack;
112
- const background = theme.base === 'dark' ? state.tokens.color.fixedBlack : state.tokens.color.fixedWhite;
113
- const literal = (value, fallback) => (typeof value === 'string' ? value : fallback);
114
- expect(theme.background.app).toBe(literal(state.tokens.color.surfaceNeutralFar, background));
115
- expect(theme.background.content).toBe(literal(state.tokens.color.surfaceNeutralFar, background));
116
- expect(theme.background.preview).toBe(literal(state.tokens.color.surfaceNeutralFar, background));
117
- expect(theme.color.defaultText).toBe(literal(state.tokens.color.foregroundNeutralPrimary, foreground));
118
- expect(theme.input.color).toBe(literal(state.tokens.color.foregroundNeutralPrimary, foreground));
119
- expect(theme.button.background).toBe(literal(state.tokens.color.backgroundNeutralLoud, background));
219
+ for (const [axis, value] of selectionSteps) {
220
+ await selectTheme(scene, axis, value);
221
+ source = axis === 'source' ? value : source;
222
+ const story = themes[themes.length - 1];
223
+ const chrome = chromeThemes[chromeThemes.length - 1];
224
+ const { color } = chrome.tokens;
225
+ const theme = latestStorybookTheme();
226
+ backgrounds.add(theme.background.app);
227
+ expect(chrome.appearance).toEqual(story.appearance);
228
+ expect(chrome.tokens).toEqual(flex.resolveFlexTokens(chrome.appearance));
229
+ expect(story === chrome).toBe(source === 'flex');
230
+ expect(StyleSheet.flatten(scene.getByTestId('test-theme-toolbar').props.style).backgroundColor).toBe(color.backgroundNeutralSubtle.rest);
231
+ expect(StyleSheet.flatten(scene.getByText('Theme').props.style).color).toBe(color.foregroundNeutralPrimary.rest);
232
+ expect(theme.background.app).toBe(color.surfaceNeutralFar.rest);
233
+ expect(theme.background.content).toBe(color.surfaceNeutralFar.rest);
234
+ expect(theme.background.preview).toBe(color.surfaceNeutralFar.rest);
235
+ expect(theme.color.defaultText).toBe(color.foregroundNeutralPrimary.rest);
236
+ expect(theme.input.color).toBe(color.foregroundNeutralPrimary.rest);
237
+ expect(theme.button.background).toBe(color.backgroundNeutralLoud.rest);
120
238
  expect(root.inputModality).toBe('keyboard');
121
239
  expect(roots.every((settings) => settings === root)).toBe(true);
122
240
  }
123
- expect(backgrounds.dark).not.toBe(backgrounds.light);
124
- expect(backgrounds.none).toBe(defaultBackground);
125
- expect(warn).toHaveBeenCalledWith(expect.stringContaining('Storybook chrome cannot resolve native system colors'));
241
+ expect(themes[themes.length - 1].tokens.color.surfaceNeutralFar.rest).toBe(defaultBackground);
242
+ expect(backgrounds.size).toBeGreaterThan(3);
126
243
  expect(getStorybookUI).toHaveBeenCalledTimes(1);
127
244
  expect(mounted).toHaveBeenCalledTimes(1);
128
245
  expect(unmounted).not.toHaveBeenCalled();