@fibery/ui-kit 1.40.3 → 1.41.0
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/CHANGELOG.md +14 -0
- package/eslint.config.mjs +16 -0
- package/package.json +11 -13
- package/src/a11y-color.test.ts +162 -0
- package/src/a11y-color.ts +13 -20
- package/src/actions-menu/actions-menu-item.tsx +9 -12
- package/src/actions-menu/context-actions-menu.tsx +8 -3
- package/src/ai/model.tsx +0 -1
- package/src/ai/temperature.tsx +0 -1
- package/src/animated-height-container.tsx +3 -3
- package/src/antd/ant-modal.tsx +10 -5
- package/src/antd/index.tsx +3 -0
- package/src/antd/input.tsx +0 -1
- package/src/antd/styles.ts +15 -15
- package/src/app-icon-with-fallback.tsx +6 -6
- package/src/app-icon-wrapper.tsx +2 -3
- package/src/app-icon.tsx +4 -4
- package/src/avatar.tsx +3 -3
- package/src/button/base-button.tsx +0 -1
- package/src/button/button.tsx +1 -0
- package/src/button/make-button-colors.ts +5 -13
- package/src/checkbox.tsx +2 -2
- package/src/collapsible-section.tsx +11 -1
- package/src/color-picker/ColorPickerOrLoader.js +2 -2
- package/src/color-utils.test.ts +307 -0
- package/src/color-utils.ts +215 -0
- package/src/command-menu/index.tsx +1 -0
- package/src/comment.tsx +3 -2
- package/src/context-menu/index.tsx +12 -7
- package/src/date-picker/contexts.ts +2 -2
- package/src/date-picker/date-range-picker.tsx +3 -4
- package/src/date-picker/single-date-picker.tsx +147 -119
- package/src/date-picker/types.ts +4 -3
- package/src/day-select/iso-week-day-select.tsx +2 -2
- package/src/day-select/week-day-select.tsx +2 -2
- package/src/delayed.tsx +0 -1
- package/src/design-system/alpha.test.ts +59 -0
- package/src/design-system/alpha.ts +41 -0
- package/src/design-system/animation.ts +10 -0
- package/src/design-system/colors-css.test.ts +39 -0
- package/src/design-system/colors-css.ts +25 -0
- package/src/design-system/colors-js.test.ts +232 -0
- package/src/design-system/colors-js.ts +107 -0
- package/src/design-system/colors.test.ts +74 -0
- package/src/design-system/colors.ts +759 -0
- package/src/design-system/date.ts +3 -0
- package/src/design-system/layout.ts +106 -0
- package/src/design-system/theme.test.ts +94 -0
- package/src/design-system/theme.ts +93 -0
- package/src/design-system/typography.ts +179 -0
- package/src/design-system/vars.test.ts +1679 -0
- package/src/design-system/vars.ts +52 -0
- package/src/design-system.test.ts +45 -22
- package/src/design-system.ts +25 -1339
- package/src/dropdown-menu/index.tsx +21 -16
- package/src/emoji-picker/app-icon-picker.tsx +5 -5
- package/src/emoji-picker/emoji-picker-content-with-color.tsx +4 -4
- package/src/emoji-picker/icon-emoji-picker.tsx +3 -3
- package/src/favorites-icon.tsx +1 -1
- package/src/fibermoji-placeholder.tsx +3 -3
- package/src/field-container.tsx +11 -2
- package/src/file-item/file-icon.tsx +169 -0
- package/src/file-item/file-menu-items.tsx +68 -0
- package/src/file-item/file-preview-actions.tsx +38 -0
- package/src/file-item/file-title.tsx +48 -0
- package/src/file-item/types.ts +27 -0
- package/src/file-item/use-register-in-image-gallery.tsx +70 -0
- package/src/file-item-2.tsx +32 -348
- package/src/file-item.tsx +6 -3
- package/src/hue-shift.test.ts +91 -0
- package/src/icons/ast/ChatBubble.ts +8 -0
- package/src/icons/ast/ChatFloat.ts +8 -0
- package/src/icons/ast/ChatSidebar.ts +8 -0
- package/src/icons/ast/FileCounter.ts +8 -0
- package/src/icons/ast/FileMultiple.ts +8 -0
- package/src/icons/ast/FileOther.ts +8 -0
- package/src/icons/ast/NoBorder.ts +8 -0
- package/src/icons/ast/NoFill.ts +8 -0
- package/src/icons/ast/ValueEdit.ts +8 -0
- package/src/icons/ast/index.tsx +9 -0
- package/src/icons/react/ChatBubble.tsx +13 -0
- package/src/icons/react/ChatFloat.tsx +13 -0
- package/src/icons/react/ChatSidebar.tsx +13 -0
- package/src/icons/react/FileCounter.tsx +13 -0
- package/src/icons/react/FileMultiple.tsx +13 -0
- package/src/icons/react/FileOther.tsx +13 -0
- package/src/icons/react/NoBorder.tsx +13 -0
- package/src/icons/react/NoFill.tsx +13 -0
- package/src/icons/react/ValueEdit.tsx +13 -0
- package/src/icons/react/index.tsx +9 -0
- package/src/icons/svg/chat-bubble.svg +4 -0
- package/src/icons/svg/chat-float.svg +4 -0
- package/src/icons/svg/chat-sidebar.svg +4 -0
- package/src/icons/svg/file-counter.svg +3 -0
- package/src/icons/svg/file-multiple.svg +3 -0
- package/src/icons/svg/file-other.svg +3 -0
- package/src/icons/svg/no-border.svg +17 -0
- package/src/icons/svg/no-fill.svg +4 -0
- package/src/icons/svg/value-edit.svg +3 -0
- package/src/images-gallery/images-gallery.tsx +8 -6
- package/src/images-gallery/slide-buttons.tsx +4 -11
- package/src/is-iOS.ts +0 -1
- package/src/is-in-popup.ts +2 -1
- package/src/lists/actions-menu-row-surface.tsx +7 -7
- package/src/loading-sausage.tsx +2 -2
- package/src/media-query-utils.ts +1 -2
- package/src/mobile-keyboard-aware-popup.tsx +6 -5
- package/src/modal-menu/modal-menu-content.tsx +2 -2
- package/src/number-input/decimal.js +9 -7
- package/src/number-input/{number-inline-input-with-autosize.tsx → number-input-inline-with-autosize.tsx} +20 -41
- package/src/number-input/number-input-inline.tsx +118 -0
- package/src/number-input/{index.test.js → number-input.test.tsx} +38 -43
- package/src/number-input/number-input.tsx +63 -121
- package/src/number-input/types.ts +19 -0
- package/src/number-input/utils.ts +61 -0
- package/src/online-users.tsx +2 -2
- package/src/palette-generator.test.ts +309 -0
- package/src/palette-generator.ts +160 -0
- package/src/palettes/_.ts +72 -0
- package/src/palettes/slate-arch.colors-snapshot.test.ts +1689 -0
- package/src/palettes/slate-arch.ts +188 -0
- package/src/palettes/slate-user.colors-snapshot.test.ts +1689 -0
- package/src/palettes/slate-user.ts +187 -0
- package/src/palettes/warm-arch.colors-snapshot.test.ts +1689 -0
- package/src/palettes/warm-arch.ts +222 -0
- package/src/palettes/warm-user.colors-snapshot.test.ts +1689 -0
- package/src/palettes/warm-user.ts +222 -0
- package/src/platform.ts +0 -3
- package/src/popover/get-element-ref.ts +28 -0
- package/src/popover/index.tsx +236 -339
- package/src/popover/{mobile-popup-context.tsx → mobile-popover-context.tsx} +7 -7
- package/src/popover/mobile-popover.tsx +169 -0
- package/src/popover/modifiers.tsx +2 -2
- package/src/popover/popup-stack-context.tsx +8 -9
- package/src/progress.tsx +2 -2
- package/src/reactions/reaction-button.tsx +12 -6
- package/src/root-theme-provider.test.tsx +411 -0
- package/src/scale-generator.ts +356 -0
- package/src/select/components/menu-list-virtualized.tsx +12 -25
- package/src/select/components/menu.tsx +12 -2
- package/src/select/index.tsx +6 -5
- package/src/select/select.tsx +38 -39
- package/src/select/styles.ts +0 -1
- package/src/select/util.ts +1 -1
- package/src/static-palettes.ts +356 -0
- package/src/thematic-color-picker.tsx +266 -0
- package/src/thematic-constants.tsx +27 -0
- package/src/thematic-controls.tsx +144 -0
- package/src/thematic-scales.tsx +122 -0
- package/src/thematic-state.ts +370 -0
- package/src/thematic.tsx +386 -0
- package/src/theme-provider.test.tsx +820 -0
- package/src/theme-provider.tsx +158 -92
- package/src/theme-settings.ts +67 -12
- package/src/theme-styles.ts +58 -6
- package/src/toast/toast-action.tsx +1 -1
- package/src/toast/toast.tsx +1 -2
- package/src/toggle-on-off.tsx +2 -2
- package/src/toggle.tsx +7 -8
- package/src/tooltip.tsx +14 -10
- package/src/type-badge.tsx +7 -14
- package/src/unit/styles.ts +2 -25
- package/src/unit/unit-with-tooltip.tsx +3 -2
- package/src/use-is-phone.tsx +7 -2
- package/src/use-long-press.tsx +2 -2
- package/src/use-on-screen-keyboard-data.tsx +2 -2
- package/src/with-data.tsx +4 -3
- package/src/workflow-progress-icon.tsx +2 -2
- package/.eslintignore +0 -3
- package/.eslintrc +0 -14
- package/src/__mocks__/createInlineTheme.js +0 -3
- package/src/create-inline-theme.ts +0 -66
- package/src/number-input/index.js +0 -191
- package/src/palette.ts +0 -237
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
import {act, render, screen} from "@testing-library/react";
|
|
2
|
+
import {
|
|
3
|
+
RootThemeProvider,
|
|
4
|
+
ThemeProvider,
|
|
5
|
+
useThematic,
|
|
6
|
+
useTheme,
|
|
7
|
+
useThemeMenuPreference,
|
|
8
|
+
useThemeMode,
|
|
9
|
+
useThemePreference,
|
|
10
|
+
} from "./theme-provider";
|
|
11
|
+
import type {ThemePalette} from "./palette-generator";
|
|
12
|
+
import {defaultDarkTheme, defaultLightTheme, defaultPalette} from "./theme-styles";
|
|
13
|
+
import {createInlineStyles, getThemeColors} from "./design-system";
|
|
14
|
+
import {storeWarmPreference} from "./theme-settings";
|
|
15
|
+
|
|
16
|
+
function ThemeDisplay({testId = "theme-display"}: {testId?: string}) {
|
|
17
|
+
const theme = useTheme();
|
|
18
|
+
const mode = useThemeMode();
|
|
19
|
+
return (
|
|
20
|
+
<div data-testid={testId} data-mode={mode} data-primary={theme.primary}>
|
|
21
|
+
{theme.key}
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function PreferenceDisplay() {
|
|
27
|
+
const preference = useThemePreference();
|
|
28
|
+
const menuPreference = useThemeMenuPreference();
|
|
29
|
+
return (
|
|
30
|
+
<div data-testid="preferences" data-preference={preference} data-menu-preference={menuPreference}>
|
|
31
|
+
{preference}/{menuPreference}
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function ThematicDisplay() {
|
|
37
|
+
const {palette, setPalette} = useThematic();
|
|
38
|
+
return (
|
|
39
|
+
<div data-testid="thematic" data-has-palette={Boolean(palette)} data-has-setter={typeof setPalette === "function"}>
|
|
40
|
+
thematic
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
describe("RootThemeProvider", () => {
|
|
46
|
+
beforeEach(() => {
|
|
47
|
+
document.documentElement.className = "";
|
|
48
|
+
document.documentElement.style.colorScheme = "";
|
|
49
|
+
localStorage.clear();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe("context provision", () => {
|
|
53
|
+
it("provides theme via useTheme", () => {
|
|
54
|
+
render(
|
|
55
|
+
<RootThemeProvider initialThemeMode="light" initialPreference="light" initialMenuPreference="auto">
|
|
56
|
+
<ThemeDisplay />
|
|
57
|
+
</RootThemeProvider>
|
|
58
|
+
);
|
|
59
|
+
expect(screen.getByTestId("theme-display")).toHaveAttribute("data-mode", "light");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("provides dark theme when initialThemeMode is dark", () => {
|
|
63
|
+
render(
|
|
64
|
+
<RootThemeProvider initialThemeMode="dark" initialPreference="dark" initialMenuPreference="dark">
|
|
65
|
+
<ThemeDisplay />
|
|
66
|
+
</RootThemeProvider>
|
|
67
|
+
);
|
|
68
|
+
expect(screen.getByTestId("theme-display")).toHaveAttribute("data-mode", "dark");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("provides themePreference via useThemePreference", () => {
|
|
72
|
+
render(
|
|
73
|
+
<RootThemeProvider initialThemeMode="light" initialPreference="auto" initialMenuPreference="auto">
|
|
74
|
+
<PreferenceDisplay />
|
|
75
|
+
</RootThemeProvider>
|
|
76
|
+
);
|
|
77
|
+
expect(screen.getByTestId("preferences")).toHaveAttribute("data-preference", "auto");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("provides themeMenuPreference via useThemeMenuPreference", () => {
|
|
81
|
+
render(
|
|
82
|
+
<RootThemeProvider initialThemeMode="light" initialPreference="light" initialMenuPreference="dark">
|
|
83
|
+
<PreferenceDisplay />
|
|
84
|
+
</RootThemeProvider>
|
|
85
|
+
);
|
|
86
|
+
expect(screen.getByTestId("preferences")).toHaveAttribute("data-menu-preference", "dark");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("provides thematic context with palette and setPalette", () => {
|
|
90
|
+
render(
|
|
91
|
+
<RootThemeProvider initialThemeMode="light" initialPreference="light" initialMenuPreference="auto">
|
|
92
|
+
<ThematicDisplay />
|
|
93
|
+
</RootThemeProvider>
|
|
94
|
+
);
|
|
95
|
+
const el = screen.getByTestId("thematic");
|
|
96
|
+
expect(el).toHaveAttribute("data-has-palette", "true");
|
|
97
|
+
expect(el).toHaveAttribute("data-has-setter", "true");
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe("base context for diff optimization", () => {
|
|
102
|
+
it("sets light base context in light mode", () => {
|
|
103
|
+
const {container} = render(
|
|
104
|
+
<RootThemeProvider initialThemeMode="light" initialPreference="light" initialMenuPreference="auto">
|
|
105
|
+
<ThemeProvider theme={defaultLightTheme}>
|
|
106
|
+
<span data-testid="inner">inner</span>
|
|
107
|
+
</ThemeProvider>
|
|
108
|
+
</RootThemeProvider>
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// Inner ThemeProvider with same light theme should have empty diff
|
|
112
|
+
const divs = Array.from(container.querySelectorAll("div")).filter((d) => d.hasAttribute("style"));
|
|
113
|
+
// The innermost div (from nested ThemeProvider) should have no styles
|
|
114
|
+
const innerMostDiv = divs[divs.length - 1];
|
|
115
|
+
const style = innerMostDiv?.getAttribute("style") || "";
|
|
116
|
+
expect(style).toBe("");
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("sets dark base context in dark mode", () => {
|
|
120
|
+
const {container} = render(
|
|
121
|
+
<RootThemeProvider initialThemeMode="dark" initialPreference="dark" initialMenuPreference="dark">
|
|
122
|
+
<ThemeProvider theme={defaultDarkTheme}>
|
|
123
|
+
<span data-testid="inner">inner</span>
|
|
124
|
+
</ThemeProvider>
|
|
125
|
+
</RootThemeProvider>
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// Inner ThemeProvider with same dark theme should have empty diff
|
|
129
|
+
const divs = Array.from(container.querySelectorAll("div")).filter((d) => d.hasAttribute("style"));
|
|
130
|
+
const innerMostDiv = divs[divs.length - 1];
|
|
131
|
+
const style = innerMostDiv?.getAttribute("style") || "";
|
|
132
|
+
expect(style).toBe("");
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("nested light ThemeProvider in dark RootThemeProvider produces diff", () => {
|
|
136
|
+
const {container} = render(
|
|
137
|
+
<RootThemeProvider initialThemeMode="dark" initialPreference="dark" initialMenuPreference="dark">
|
|
138
|
+
<ThemeProvider theme={defaultLightTheme}>
|
|
139
|
+
<span data-testid="inner">inner</span>
|
|
140
|
+
</ThemeProvider>
|
|
141
|
+
</RootThemeProvider>
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
// Inner ThemeProvider with light theme should have diff (dark->light)
|
|
145
|
+
const divs = Array.from(container.querySelectorAll("div")).filter((d) => d.hasAttribute("style"));
|
|
146
|
+
const innerMostDiv = divs[divs.length - 1];
|
|
147
|
+
const style = innerMostDiv?.getAttribute("style") || "";
|
|
148
|
+
expect(style.length).toBeGreaterThan(0);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe("document element classes", () => {
|
|
153
|
+
it("adds dark-theme class in dark mode", () => {
|
|
154
|
+
render(
|
|
155
|
+
<RootThemeProvider initialThemeMode="dark" initialPreference="dark" initialMenuPreference="dark">
|
|
156
|
+
<div>content</div>
|
|
157
|
+
</RootThemeProvider>
|
|
158
|
+
);
|
|
159
|
+
expect(document.documentElement.classList.contains("dark-theme")).toBe(true);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("does not add dark-theme class in light mode", () => {
|
|
163
|
+
render(
|
|
164
|
+
<RootThemeProvider initialThemeMode="light" initialPreference="light" initialMenuPreference="auto">
|
|
165
|
+
<div>content</div>
|
|
166
|
+
</RootThemeProvider>
|
|
167
|
+
);
|
|
168
|
+
expect(document.documentElement.classList.contains("dark-theme")).toBe(false);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("adds light-theme-and-dark-menu class in light2 mode", () => {
|
|
172
|
+
render(
|
|
173
|
+
<RootThemeProvider initialThemeMode="light2" initialPreference="light" initialMenuPreference="dark">
|
|
174
|
+
<div>content</div>
|
|
175
|
+
</RootThemeProvider>
|
|
176
|
+
);
|
|
177
|
+
expect(document.documentElement.classList.contains("light-theme-and-dark-menu")).toBe(true);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it("sets colorScheme to dark in dark mode", () => {
|
|
181
|
+
render(
|
|
182
|
+
<RootThemeProvider initialThemeMode="dark" initialPreference="dark" initialMenuPreference="dark">
|
|
183
|
+
<div>content</div>
|
|
184
|
+
</RootThemeProvider>
|
|
185
|
+
);
|
|
186
|
+
expect(document.documentElement.style.colorScheme).toBe("dark");
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("sets colorScheme to light in light mode", () => {
|
|
190
|
+
render(
|
|
191
|
+
<RootThemeProvider initialThemeMode="light" initialPreference="light" initialMenuPreference="auto">
|
|
192
|
+
<div>content</div>
|
|
193
|
+
</RootThemeProvider>
|
|
194
|
+
);
|
|
195
|
+
expect(document.documentElement.style.colorScheme).toBe("light");
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
describe("diff optimization integrity", () => {
|
|
200
|
+
it("RootThemeProvider internal ThemeProvider only sets diff from base", () => {
|
|
201
|
+
const lightStyles = createInlineStyles(defaultLightTheme);
|
|
202
|
+
const lightTheme = getThemeColors(null, "light", defaultPalette);
|
|
203
|
+
const actualStyles = createInlineStyles(lightTheme);
|
|
204
|
+
|
|
205
|
+
// Count expected diff (theme from getThemeColors vs defaultLightTheme)
|
|
206
|
+
let expectedDiffCount = 0;
|
|
207
|
+
for (const key in actualStyles) {
|
|
208
|
+
if (actualStyles[key] !== lightStyles[key]) {
|
|
209
|
+
expectedDiffCount++;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const {container} = render(
|
|
214
|
+
<RootThemeProvider initialThemeMode="light" initialPreference="light" initialMenuPreference="auto">
|
|
215
|
+
<span>content</span>
|
|
216
|
+
</RootThemeProvider>
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
// Get the ThemeProvider's div style
|
|
220
|
+
const divs = Array.from(container.querySelectorAll("div")).filter((d) => d.hasAttribute("style"));
|
|
221
|
+
if (divs.length > 0) {
|
|
222
|
+
const style = divs[0].getAttribute("style") || "";
|
|
223
|
+
const actualCount = style.split(";").filter((s) => s.includes(":")).length;
|
|
224
|
+
// Actual diff should match expected diff count (or be close)
|
|
225
|
+
expect(actualCount).toBeLessThanOrEqual(expectedDiffCount + 5);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it("deeply nested ThemeProviders accumulate correct diffs", () => {
|
|
230
|
+
const {container} = render(
|
|
231
|
+
<RootThemeProvider initialThemeMode="light" initialPreference="light" initialMenuPreference="auto">
|
|
232
|
+
<ThemeProvider mode="dark">
|
|
233
|
+
<ThemeProvider mode="light">
|
|
234
|
+
<span data-testid="deepest">deep</span>
|
|
235
|
+
</ThemeProvider>
|
|
236
|
+
</ThemeProvider>
|
|
237
|
+
</RootThemeProvider>
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
// Each transition (light->dark, dark->light) should produce diffs
|
|
241
|
+
const divs = Array.from(container.querySelectorAll("div")).filter((d) => d.hasAttribute("style"));
|
|
242
|
+
// Should have multiple styled divs (root's ThemeProvider + nested ones)
|
|
243
|
+
expect(divs.length).toBeGreaterThanOrEqual(2);
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
describe("setPalette", () => {
|
|
248
|
+
it("setPalette updates theme colors", () => {
|
|
249
|
+
let capturedSetPalette: (p: ThemePalette | null) => void = () => {};
|
|
250
|
+
|
|
251
|
+
function ThemeCapture() {
|
|
252
|
+
const {setPalette} = useThematic();
|
|
253
|
+
const theme = useTheme();
|
|
254
|
+
capturedSetPalette = setPalette;
|
|
255
|
+
return <div data-testid="capture" data-card-bg={theme.cardBg} />;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
render(
|
|
259
|
+
<RootThemeProvider initialThemeMode="light" initialPreference="light" initialMenuPreference="auto">
|
|
260
|
+
<ThemeCapture />
|
|
261
|
+
</RootThemeProvider>
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
const initialCardBg = screen.getByTestId("capture").getAttribute("data-card-bg");
|
|
265
|
+
|
|
266
|
+
// cardBg in light mode = white, so modify white
|
|
267
|
+
const newPalette = {...defaultPalette, key: "test-palette", white: "oklch(0.9 0.05 200)"};
|
|
268
|
+
|
|
269
|
+
act(() => capturedSetPalette(newPalette));
|
|
270
|
+
|
|
271
|
+
const updatedCardBg = screen.getByTestId("capture").getAttribute("data-card-bg");
|
|
272
|
+
expect(updatedCardBg).toBe("oklch(0.9 0.05 200)");
|
|
273
|
+
expect(updatedCardBg).not.toBe(initialCardBg);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it("nested ThemeProvider inherits updated palette", () => {
|
|
277
|
+
let capturedSetPalette: (p: ThemePalette | null) => void = () => {};
|
|
278
|
+
|
|
279
|
+
function PaletteControl() {
|
|
280
|
+
const {setPalette} = useThematic();
|
|
281
|
+
capturedSetPalette = setPalette;
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function NestedTheme() {
|
|
286
|
+
const theme = useTheme();
|
|
287
|
+
return <div data-testid="nested" data-card-bg={theme.cardBg} />;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
render(
|
|
291
|
+
<RootThemeProvider initialThemeMode="light" initialPreference="light" initialMenuPreference="auto">
|
|
292
|
+
<PaletteControl />
|
|
293
|
+
<ThemeProvider mode="light">
|
|
294
|
+
<NestedTheme />
|
|
295
|
+
</ThemeProvider>
|
|
296
|
+
</RootThemeProvider>
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
const initialCardBg = screen.getByTestId("nested").getAttribute("data-card-bg");
|
|
300
|
+
|
|
301
|
+
const newPalette = {...defaultPalette, key: "test-palette-2", white: "oklch(0.8 0.02 100)"};
|
|
302
|
+
|
|
303
|
+
act(() => capturedSetPalette(newPalette));
|
|
304
|
+
|
|
305
|
+
const updatedCardBg = screen.getByTestId("nested").getAttribute("data-card-bg");
|
|
306
|
+
expect(updatedCardBg).toBe("oklch(0.8 0.02 100)");
|
|
307
|
+
expect(updatedCardBg).not.toBe(initialCardBg);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it("setPalette(null) resets to default palette", () => {
|
|
311
|
+
let capturedSetPalette: (p: ThemePalette | null) => void = () => {};
|
|
312
|
+
|
|
313
|
+
function ThemeCapture() {
|
|
314
|
+
const {setPalette} = useThematic();
|
|
315
|
+
const theme = useTheme();
|
|
316
|
+
capturedSetPalette = setPalette;
|
|
317
|
+
return <div data-testid="capture" data-card-bg={theme.cardBg} />;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
render(
|
|
321
|
+
<RootThemeProvider initialThemeMode="light" initialPreference="light" initialMenuPreference="auto">
|
|
322
|
+
<ThemeCapture />
|
|
323
|
+
</RootThemeProvider>
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
const initialCardBg = screen.getByTestId("capture").getAttribute("data-card-bg");
|
|
327
|
+
|
|
328
|
+
const newPalette = {...defaultPalette, key: "test-override", white: "oklch(0.9 0.05 200)"};
|
|
329
|
+
act(() => capturedSetPalette(newPalette));
|
|
330
|
+
expect(screen.getByTestId("capture").getAttribute("data-card-bg")).toBe("oklch(0.9 0.05 200)");
|
|
331
|
+
|
|
332
|
+
act(() => capturedSetPalette(null));
|
|
333
|
+
expect(screen.getByTestId("capture").getAttribute("data-card-bg")).toBe(initialCardBg);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it("override palette takes precedence over warm preference", () => {
|
|
337
|
+
let capturedSetPalette: (p: ThemePalette | null) => void = () => {};
|
|
338
|
+
|
|
339
|
+
function PaletteKeyDisplay() {
|
|
340
|
+
const {palette, setPalette} = useThematic();
|
|
341
|
+
capturedSetPalette = setPalette;
|
|
342
|
+
return <div data-testid="palette-key" data-key={palette.key} />;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
render(
|
|
346
|
+
<RootThemeProvider initialThemeMode="light" initialPreference="light" initialMenuPreference="auto">
|
|
347
|
+
<PaletteKeyDisplay />
|
|
348
|
+
</RootThemeProvider>
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
const overridePalette = {...defaultPalette, key: "custom-override"};
|
|
352
|
+
act(() => capturedSetPalette(overridePalette));
|
|
353
|
+
expect(screen.getByTestId("palette-key").getAttribute("data-key")).toBe("custom-override");
|
|
354
|
+
|
|
355
|
+
// Changing warm preference doesn't affect override
|
|
356
|
+
act(() => storeWarmPreference(true));
|
|
357
|
+
expect(screen.getByTestId("palette-key").getAttribute("data-key")).toBe("custom-override");
|
|
358
|
+
|
|
359
|
+
// Clearing override picks up current warm preference
|
|
360
|
+
act(() => capturedSetPalette(null));
|
|
361
|
+
expect(screen.getByTestId("palette-key").getAttribute("data-key")).toBe("warm-user");
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
describe("warm preference / isArchitect", () => {
|
|
366
|
+
function PaletteKeyDisplay() {
|
|
367
|
+
const {palette} = useThematic();
|
|
368
|
+
return <div data-testid="palette-key" data-key={palette.key} />;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
it("uses default palette when warm is off and not architect", () => {
|
|
372
|
+
render(
|
|
373
|
+
<RootThemeProvider initialThemeMode="light" initialPreference="light" initialMenuPreference="auto">
|
|
374
|
+
<PaletteKeyDisplay />
|
|
375
|
+
</RootThemeProvider>
|
|
376
|
+
);
|
|
377
|
+
expect(screen.getByTestId("palette-key").getAttribute("data-key")).toBe("default");
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it("uses warm palette when warm preference is stored", () => {
|
|
381
|
+
storeWarmPreference(true);
|
|
382
|
+
render(
|
|
383
|
+
<RootThemeProvider initialThemeMode="light" initialPreference="light" initialMenuPreference="auto">
|
|
384
|
+
<PaletteKeyDisplay />
|
|
385
|
+
</RootThemeProvider>
|
|
386
|
+
);
|
|
387
|
+
expect(screen.getByTestId("palette-key").getAttribute("data-key")).toBe("warm-user");
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it("uses architect palette when isArchitect is true", () => {
|
|
391
|
+
render(
|
|
392
|
+
<RootThemeProvider initialThemeMode="light" initialPreference="light" initialMenuPreference="auto" isArchitect>
|
|
393
|
+
<PaletteKeyDisplay />
|
|
394
|
+
</RootThemeProvider>
|
|
395
|
+
);
|
|
396
|
+
expect(screen.getByTestId("palette-key").getAttribute("data-key")).toBe("slate-arch");
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it("reacts to warm preference change at runtime", () => {
|
|
400
|
+
render(
|
|
401
|
+
<RootThemeProvider initialThemeMode="light" initialPreference="light" initialMenuPreference="auto" isArchitect>
|
|
402
|
+
<PaletteKeyDisplay />
|
|
403
|
+
</RootThemeProvider>
|
|
404
|
+
);
|
|
405
|
+
expect(screen.getByTestId("palette-key").getAttribute("data-key")).toBe("slate-arch");
|
|
406
|
+
|
|
407
|
+
act(() => storeWarmPreference(true));
|
|
408
|
+
expect(screen.getByTestId("palette-key").getAttribute("data-key")).toBe("warm-arch");
|
|
409
|
+
});
|
|
410
|
+
});
|
|
411
|
+
});
|