@charcuterie/ui 4.5.0 → 4.6.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/README.md +7 -2
- package/dist/Card/Card.d.ts +5 -2
- package/dist/Card/Card.d.ts.map +1 -1
- package/dist/Card/Card.js +23 -3
- package/dist/Card/Card.js.map +1 -1
- package/dist/Overlay/OverlayPanel.d.ts.map +1 -1
- package/dist/Overlay/OverlayPanel.js +54 -2
- package/dist/Overlay/OverlayPanel.js.map +1 -1
- package/dist/Overlay/OverlayStack.d.ts +5 -0
- package/dist/Overlay/OverlayStack.d.ts.map +1 -1
- package/dist/Overlay/OverlayStack.js +2 -0
- package/dist/Overlay/OverlayStack.js.map +1 -1
- package/dist/Toast/ToastRegion.d.ts +61 -0
- package/dist/Toast/ToastRegion.d.ts.map +1 -1
- package/dist/Toast/ToastRegion.js +80 -3
- package/dist/Toast/ToastRegion.js.map +1 -1
- package/dist/Toolbar/useToolbarOverflow.d.ts.map +1 -1
- package/dist/Toolbar/useToolbarOverflow.js +39 -0
- package/dist/Toolbar/useToolbarOverflow.js.map +1 -1
- package/package.json +3 -2
- package/src/Accordion/Accordion.mdx +1 -1
- package/src/Accordion/Accordion.stories.tsx +3 -1
- package/src/Accordion/Accordion.test.tsx +4 -4
- package/src/ActionTiles/ActionTiles.mdx +1 -1
- package/src/ActionTiles/ActionTiles.stories.tsx +3 -1
- package/src/ActionTiles/ActionTiles.test.tsx +3 -3
- package/src/AdaptiveGrid/AdaptiveGrid.mdx +1 -1
- package/src/AdaptiveGrid/AdaptiveGrid.stories.tsx +3 -1
- package/src/AdaptiveGrid/AdaptiveGrid.test.tsx +5 -5
- package/src/Alert/Alert.mdx +1 -1
- package/src/Alert/Alert.stories.tsx +3 -1
- package/src/Alert/Alert.test.tsx +2 -2
- package/src/Avatar/Avatar.mdx +1 -1
- package/src/Avatar/Avatar.stories.tsx +4 -1
- package/src/Avatar/Avatar.test.tsx +2 -2
- package/src/Badge/Badge.mdx +1 -1
- package/src/Badge/Badge.stories.tsx +3 -1
- package/src/Badge/Badge.test.tsx +3 -3
- package/src/BadgeButton/BadgeButton.mdx +1 -1
- package/src/BadgeButton/BadgeButton.stories.tsx +3 -1
- package/src/BadgeButton/BadgeButton.test.tsx +2 -2
- package/src/Board/Board.mdx +1 -1
- package/src/Board/Board.stories.tsx +4 -1
- package/src/Board/Board.test.tsx +2 -2
- package/src/Button/Button.mdx +1 -1
- package/src/Button/Button.stories.tsx +3 -1
- package/src/Button/Button.test.tsx +2 -2
- package/src/ButtonLink/ButtonLink.mdx +1 -1
- package/src/ButtonLink/ButtonLink.stories.tsx +3 -1
- package/src/ButtonLink/ButtonLink.test.tsx +3 -3
- package/src/Card/Card.mdx +21 -1
- package/src/Card/Card.stories.tsx +91 -1
- package/src/Card/Card.test.tsx +60 -3
- package/src/Card/Card.tsx +24 -2
- package/src/Checkbox/Checkbox.mdx +1 -1
- package/src/Checkbox/Checkbox.stories.tsx +3 -1
- package/src/Checkbox/Checkbox.test.tsx +4 -4
- package/src/ColorSchemeSwitcher/ColorSchemeSwitcher.mdx +1 -1
- package/src/ColorSchemeSwitcher/ColorSchemeSwitcher.stories.tsx +3 -1
- package/src/ColorSchemeSwitcher/ColorSchemeSwitcher.test.tsx +5 -5
- package/src/ColorSchemeToggle/ColorSchemeToggle.mdx +1 -1
- package/src/ColorSchemeToggle/ColorSchemeToggle.stories.tsx +3 -1
- package/src/ColorSchemeToggle/ColorSchemeToggle.test.tsx +5 -5
- package/src/Combobox/Combobox.mdx +1 -1
- package/src/Combobox/Combobox.stories.tsx +3 -1
- package/src/Combobox/Combobox.test.tsx +2 -2
- package/src/CopyButton/CopyButton.mdx +1 -1
- package/src/CopyButton/CopyButton.stories.tsx +4 -1
- package/src/CopyButton/CopyButton.test.tsx +3 -3
- package/src/DataTable/DataTable.mdx +1 -1
- package/src/DataTable/DataTable.stories.tsx +3 -1
- package/src/DataTable/DataTable.test.tsx +3 -3
- package/src/DatePicker/DatePicker.mdx +1 -1
- package/src/DatePicker/DatePicker.stories.tsx +3 -1
- package/src/DatePicker/DatePicker.test.tsx +3 -3
- package/src/DatePicker/plainDate.test.ts +21 -3
- package/src/Dialog/Dialog.mdx +1 -1
- package/src/Dialog/Dialog.stories.tsx +3 -1
- package/src/Dialog/Dialog.test.tsx +2 -2
- package/src/DropRail/DropRail.mdx +1 -1
- package/src/DropRail/DropRail.stories.tsx +4 -1
- package/src/DropRail/DropRail.test.tsx +4 -4
- package/src/EmptyState/EmptyState.mdx +1 -1
- package/src/EmptyState/EmptyState.stories.tsx +3 -1
- package/src/EmptyState/EmptyState.test.tsx +2 -2
- package/src/Field/Field.mdx +1 -1
- package/src/Field/Field.stories.tsx +3 -1
- package/src/Field/Field.test.tsx +4 -4
- package/src/FileDropZone/FileDropZone.mdx +1 -1
- package/src/FileDropZone/FileDropZone.stories.tsx +3 -1
- package/src/FileDropZone/FileDropZone.test.tsx +3 -3
- package/src/Header/Header.mdx +1 -1
- package/src/Header/Header.stories.tsx +3 -1
- package/src/IconButton/IconButton.mdx +1 -1
- package/src/IconButton/IconButton.stories.tsx +3 -1
- package/src/IconButton/IconButton.test.tsx +2 -2
- package/src/Lightbox/Lightbox.mdx +1 -1
- package/src/Lightbox/Lightbox.stories.tsx +3 -1
- package/src/Lightbox/Lightbox.test.tsx +2 -2
- package/src/Listbox/Listbox.mdx +1 -1
- package/src/Listbox/Listbox.stories.tsx +3 -1
- package/src/Listbox/Listbox.test.tsx +5 -5
- package/src/LiveStatusIndicator/LiveStatusIndicator.mdx +1 -1
- package/src/LiveStatusIndicator/LiveStatusIndicator.stories.tsx +3 -1
- package/src/LiveStatusIndicator/LiveStatusIndicator.test.tsx +2 -2
- package/src/LogViewer/LogViewer.mdx +1 -1
- package/src/LogViewer/LogViewer.stories.tsx +3 -1
- package/src/LogViewer/LogViewer.test.tsx +2 -2
- package/src/Main/Main.mdx +1 -1
- package/src/Main/Main.stories.tsx +3 -1
- package/src/Main/Main.test.tsx +3 -3
- package/src/MarkdownEditor/MarkdownEditor.mdx +1 -1
- package/src/MarkdownEditor/MarkdownEditor.stories.tsx +3 -1
- package/src/MarkdownEditor/MarkdownEditor.test.tsx +4 -4
- package/src/MarkdownLine/MarkdownLine.mdx +1 -1
- package/src/MarkdownLine/MarkdownLine.stories.tsx +4 -1
- package/src/MarkdownLine/MarkdownLine.test.tsx +3 -3
- package/src/MediaTile/MediaTile.stories.tsx +3 -1
- package/src/MediaTile/MediaTile.test.tsx +2 -2
- package/src/Menu/Menu.mdx +1 -1
- package/src/Menu/Menu.stories.tsx +3 -1
- package/src/Modal/Modal.mdx +1 -1
- package/src/Modal/Modal.stories.tsx +3 -1
- package/src/Modal/Modal.test.tsx +3 -3
- package/src/Nav/Nav.mdx +1 -1
- package/src/Nav/Nav.stories.tsx +3 -1
- package/src/Nav/Nav.test.tsx +6 -6
- package/src/NavBar/NavBar.mdx +1 -1
- package/src/NavBar/NavBar.stories.tsx +3 -1
- package/src/NavBar/NavBar.test.tsx +3 -3
- package/src/Overlay/OverlayPanel.tsx +63 -2
- package/src/Overlay/OverlayStack.tsx +7 -1
- package/src/Picker/Picker.mdx +1 -1
- package/src/Picker/Picker.stories.tsx +3 -1
- package/src/Picker/Picker.test.tsx +2 -2
- package/src/Popover/Popover.mdx +1 -1
- package/src/Popover/Popover.stories.tsx +3 -1
- package/src/Popover/Popover.test.tsx +2 -2
- package/src/PortraitTiles/PortraitTiles.mdx +1 -1
- package/src/PortraitTiles/PortraitTiles.stories.tsx +3 -1
- package/src/PortraitTiles/PortraitTiles.test.tsx +5 -5
- package/src/ProgressBar/ProgressBar.mdx +1 -1
- package/src/ProgressBar/ProgressBar.stories.tsx +3 -1
- package/src/ProgressBar/ProgressBar.test.tsx +2 -2
- package/src/QueryBuilder/QueryBuilder.mdx +1 -1
- package/src/QueryBuilder/QueryBuilder.stories.tsx +3 -1
- package/src/QueryBuilder/QueryBuilder.test.tsx +2 -2
- package/src/RadioGroup/RadioGroup.mdx +1 -1
- package/src/RadioGroup/RadioGroup.stories.tsx +3 -1
- package/src/RadioGroup/RadioGroup.test.tsx +2 -2
- package/src/Rail/Rail.mdx +1 -1
- package/src/Rail/Rail.stories.tsx +3 -1
- package/src/Rail/Rail.test.tsx +2 -2
- package/src/RangeSlider/RangeSlider.mdx +1 -1
- package/src/RangeSlider/RangeSlider.stories.tsx +3 -1
- package/src/RangeSlider/RangeSlider.test.tsx +7 -3
- package/src/ReorderList/ReorderList.mdx +1 -1
- package/src/ReorderList/ReorderList.stories.tsx +3 -1
- package/src/ReorderList/ReorderList.test.tsx +7 -7
- package/src/RouterLink/UnstyledLink.stories.tsx +4 -1
- package/src/RouterLink/UnstyledLink.test.tsx +2 -2
- package/src/SearchInput/SearchInput.mdx +1 -1
- package/src/SearchInput/SearchInput.stories.tsx +3 -1
- package/src/SegmentedControl/SegmentedControl.mdx +1 -1
- package/src/SegmentedControl/SegmentedControl.stories.tsx +3 -1
- package/src/SegmentedControl/SegmentedControl.test.tsx +2 -2
- package/src/Select/Select.mdx +1 -1
- package/src/Select/Select.stories.tsx +3 -1
- package/src/Select/Select.test.tsx +4 -4
- package/src/Shell/Shell.mdx +1 -1
- package/src/Shell/Shell.stories.tsx +3 -1
- package/src/Shell/Shell.test.tsx +2 -2
- package/src/Skeleton/Skeleton.mdx +1 -1
- package/src/Skeleton/Skeleton.stories.tsx +3 -1
- package/src/Skeleton/Skeleton.test.tsx +2 -2
- package/src/Slider/Slider.mdx +1 -1
- package/src/Slider/Slider.stories.tsx +3 -1
- package/src/Slider/Slider.test.tsx +2 -2
- package/src/SortableTableHeader/SortableTableHeader.mdx +1 -1
- package/src/SortableTableHeader/SortableTableHeader.stories.tsx +3 -1
- package/src/SortableTableHeader/SortableTableHeader.test.tsx +3 -3
- package/src/Spinner/Spinner.mdx +1 -1
- package/src/Spinner/Spinner.stories.tsx +3 -1
- package/src/Spinner/Spinner.test.tsx +2 -2
- package/src/Stepper/Stepper.mdx +1 -1
- package/src/Stepper/Stepper.stories.tsx +4 -1
- package/src/Stepper/Stepper.test.tsx +7 -7
- package/src/Swatch/Swatch.mdx +1 -1
- package/src/Swatch/Swatch.stories.tsx +4 -1
- package/src/Swatch/Swatch.test.tsx +3 -3
- package/src/Switch/Switch.mdx +1 -1
- package/src/Switch/Switch.stories.tsx +3 -1
- package/src/Switch/Switch.test.tsx +2 -2
- package/src/Tabs/Tabs.mdx +1 -1
- package/src/Tabs/Tabs.stories.tsx +3 -1
- package/src/Tabs/Tabs.test.tsx +3 -3
- package/src/TextLink/TextLink.mdx +1 -1
- package/src/TextLink/TextLink.stories.tsx +3 -1
- package/src/TextLink/TextLink.test.tsx +4 -4
- package/src/TimecodeInput/TimecodeInput.mdx +1 -1
- package/src/TimecodeInput/TimecodeInput.stories.tsx +3 -1
- package/src/Toast/Toast.mdx +1 -1
- package/src/Toast/Toast.stories.tsx +157 -1
- package/src/Toast/Toast.test.tsx +2 -2
- package/src/Toast/ToastRegion.test.tsx +172 -0
- package/src/Toast/ToastRegion.tsx +120 -32
- package/src/Toolbar/Toolbar.mdx +1 -1
- package/src/Toolbar/Toolbar.stories.tsx +3 -1
- package/src/Toolbar/Toolbar.test.tsx +89 -5
- package/src/Toolbar/useToolbarOverflow.ts +43 -0
- package/src/Tooltip/Tooltip.mdx +1 -1
- package/src/Tooltip/Tooltip.stories.tsx +3 -1
- package/src/Tooltip/Tooltip.test.tsx +2 -2
- package/src/VirtualizedGrid/VirtualizedGrid.mdx +1 -1
- package/src/VirtualizedGrid/VirtualizedGrid.stories.tsx +4 -1
- package/src/VirtualizedGrid/VirtualizedGrid.test.tsx +5 -5
- package/src/VisuallyHidden/VisuallyHidden.mdx +1 -1
- package/src/VisuallyHidden/VisuallyHidden.stories.tsx +3 -1
- package/src/markdownEditorCodeMirror/MarkdownEditorCodeMirror.mdx +1 -1
- package/src/markdownEditorCodeMirror/MarkdownEditorCodeMirror.stories.tsx +3 -1
- package/src/markdownEditorCodeMirror/MarkdownEditorCodeMirror.test.tsx +3 -3
- package/src/markdownEditorCodeMirror/MarkdownView.mdx +1 -1
- package/src/markdownEditorCodeMirror/MarkdownView.stories.tsx +4 -1
- package/src/markdownEditorCodeMirror/MarkdownView.test.tsx +44 -20
- package/src/reactRouter/ReactRouterAdapter.stories.tsx +3 -1
- package/src/reactRouter/ReactRouterAdapter.test.tsx +2 -2
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useMediaQuery } from "@charcuterie/logic"
|
|
2
2
|
import { matchMediaMatcher } from "@charcuterie/logic/browser"
|
|
3
|
+
import { playgroundParameters } from "@charcuterie/storybook-config/story-parameters"
|
|
3
4
|
import type { Meta, StoryObj } from "@storybook/react"
|
|
4
5
|
import type { ReactNode } from "react"
|
|
5
6
|
import { useState } from "react"
|
|
@@ -150,7 +151,8 @@ type Story = StoryObj<typeof meta>
|
|
|
150
151
|
* collapse is not progressive: almost everything lives in the
|
|
151
152
|
* popover permanently.
|
|
152
153
|
*/
|
|
153
|
-
export const
|
|
154
|
+
export const Playground: Story = {
|
|
155
|
+
parameters: playgroundParameters,
|
|
154
156
|
render: (toolbarProps) => (
|
|
155
157
|
<Frame inlineSize="34rem">
|
|
156
158
|
<Toolbar {...toolbarProps} />
|
|
@@ -15,13 +15,13 @@ import * as stories from "./Toolbar.stories.tsx"
|
|
|
15
15
|
const {
|
|
16
16
|
AllStates,
|
|
17
17
|
AllVariants,
|
|
18
|
-
|
|
18
|
+
Playground,
|
|
19
19
|
InAppShell,
|
|
20
20
|
Interactive,
|
|
21
21
|
} = composeStories(stories)
|
|
22
22
|
|
|
23
23
|
test("the toolbar is named, and its actions are buttons", async () => {
|
|
24
|
-
const { canvas } = await mountStory(
|
|
24
|
+
const { canvas } = await mountStory(Playground)
|
|
25
25
|
|
|
26
26
|
const toolbar = expectAgentDrivable(canvas, {
|
|
27
27
|
name: "Deck actions",
|
|
@@ -43,7 +43,7 @@ test("the toolbar is named, and its actions are buttons", async () => {
|
|
|
43
43
|
* popover permanently.
|
|
44
44
|
*/
|
|
45
45
|
test("a bar with room shows no overflow trigger", async () => {
|
|
46
|
-
const { canvas } = await mountStory(
|
|
46
|
+
const { canvas } = await mountStory(Playground)
|
|
47
47
|
|
|
48
48
|
const toolbar = expectAgentDrivable(canvas, {
|
|
49
49
|
name: "Deck actions",
|
|
@@ -73,7 +73,7 @@ test("a bar with room shows no overflow trigger", async () => {
|
|
|
73
73
|
* the app's own component and most of them forward no `tabIndex`.
|
|
74
74
|
*/
|
|
75
75
|
test("the toolbar is one tab stop", async () => {
|
|
76
|
-
const { canvas } = await mountStory(
|
|
76
|
+
const { canvas } = await mountStory(Playground)
|
|
77
77
|
|
|
78
78
|
const toolbar = expectAgentDrivable(canvas, {
|
|
79
79
|
name: "Deck actions",
|
|
@@ -98,7 +98,7 @@ test("the toolbar is one tab stop", async () => {
|
|
|
98
98
|
})
|
|
99
99
|
|
|
100
100
|
test("the arrow keys move along the row", async () => {
|
|
101
|
-
const { canvas } = await mountStory(
|
|
101
|
+
const { canvas } = await mountStory(Playground)
|
|
102
102
|
|
|
103
103
|
const start = expectAgentDrivable(canvas, {
|
|
104
104
|
name: "Start rip",
|
|
@@ -284,3 +284,87 @@ test("the shell mounts exactly one toolbar", async () => {
|
|
|
284
284
|
}),
|
|
285
285
|
).toHaveLength(1)
|
|
286
286
|
})
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* The font race, which is latent rather than observed: it needs the
|
|
290
|
+
* two faces to disagree across a collapse boundary, and no story in
|
|
291
|
+
* the library crosses one today. The test builds that boundary on
|
|
292
|
+
* purpose, so the mechanism is checked rather than waited for.
|
|
293
|
+
*
|
|
294
|
+
* Every width the bar measures is a **text** width. On a cold cache
|
|
295
|
+
* the first measurement is taken in the fallback face, and when the
|
|
296
|
+
* real face swaps in the items change size — but the container does
|
|
297
|
+
* not, so the `ResizeObserver` stays quiet, and nothing re-renders,
|
|
298
|
+
* so the layout effect does not run either. The bar keeps an answer
|
|
299
|
+
* computed against a font that is no longer on screen.
|
|
300
|
+
*
|
|
301
|
+
* The stub below is that swap with the timing made explicit: the bar
|
|
302
|
+
* mounts while `document.fonts.ready` is still pending, the items
|
|
303
|
+
* are widened with no change to the container's box, and the
|
|
304
|
+
* promise is then resolved. A bar that only measures on mount and
|
|
305
|
+
* on resize sees neither event and stays wrong.
|
|
306
|
+
*/
|
|
307
|
+
test("the bar re-measures once the webfont has loaded", async () => {
|
|
308
|
+
const realFonts = document.fonts
|
|
309
|
+
|
|
310
|
+
let markFontsReady = () => {}
|
|
311
|
+
|
|
312
|
+
const fontsReady = new Promise<FontFaceSet>((resolve) => {
|
|
313
|
+
markFontsReady = () => {
|
|
314
|
+
resolve(realFonts)
|
|
315
|
+
}
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
// Layered over the real set rather than replacing it, so anything
|
|
319
|
+
// else that reads `document.fonts` during the mount still works.
|
|
320
|
+
Object.defineProperty(document, "fonts", {
|
|
321
|
+
configurable: true,
|
|
322
|
+
value: Object.create(realFonts, {
|
|
323
|
+
ready: {
|
|
324
|
+
get: () => fontsReady,
|
|
325
|
+
},
|
|
326
|
+
}),
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
// Widens every action without touching the bar's own box — the
|
|
330
|
+
// `Frame` around `Playground` is a fixed `34rem`. That is exactly
|
|
331
|
+
// what a font swap does, and it is why the `ResizeObserver` is
|
|
332
|
+
// not a substitute for this effect.
|
|
333
|
+
const widenActions = document.createElement("style")
|
|
334
|
+
|
|
335
|
+
widenActions.textContent = `[role="toolbar"] button { padding-inline: 4rem; }`
|
|
336
|
+
|
|
337
|
+
try {
|
|
338
|
+
const { canvas } = await mountStory(Playground)
|
|
339
|
+
|
|
340
|
+
const toolbar = expectAgentDrivable(canvas, {
|
|
341
|
+
name: "Deck actions",
|
|
342
|
+
role: "toolbar",
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
// It fits in the fallback face.
|
|
346
|
+
await expect(
|
|
347
|
+
within(toolbar).queryByRole("button", {
|
|
348
|
+
name: "More actions",
|
|
349
|
+
}),
|
|
350
|
+
).toBeNull()
|
|
351
|
+
|
|
352
|
+
document.head.append(widenActions)
|
|
353
|
+
|
|
354
|
+
markFontsReady()
|
|
355
|
+
|
|
356
|
+
await waitFor(() => {
|
|
357
|
+
expect(
|
|
358
|
+
within(toolbar).getByRole("button", {
|
|
359
|
+
name: "More actions",
|
|
360
|
+
}),
|
|
361
|
+
).toBeInTheDocument()
|
|
362
|
+
})
|
|
363
|
+
} finally {
|
|
364
|
+
widenActions.remove()
|
|
365
|
+
|
|
366
|
+
// Back to the prototype getter — the stub was an own property
|
|
367
|
+
// shadowing it.
|
|
368
|
+
Reflect.deleteProperty(document, "fonts")
|
|
369
|
+
}
|
|
370
|
+
})
|
|
@@ -159,6 +159,49 @@ export const useToolbarOverflow = ({
|
|
|
159
159
|
*/
|
|
160
160
|
useLayoutEffect(measure)
|
|
161
161
|
|
|
162
|
+
/**
|
|
163
|
+
* Measure again once the webfont has actually arrived.
|
|
164
|
+
*
|
|
165
|
+
* Every width above is a **text** width, so it is only as correct
|
|
166
|
+
* as the font that was loaded when it was read. On a cold cache
|
|
167
|
+
* the first measurement happens in the fallback face, the items
|
|
168
|
+
* come out a few pixels off, and `chooseVisibleCount` keeps or
|
|
169
|
+
* drops one action it should not have — then never revisits it,
|
|
170
|
+
* because swapping a font changes no box the `ResizeObserver`
|
|
171
|
+
* watches and triggers no render.
|
|
172
|
+
*
|
|
173
|
+
* The user-visible symptom is a bar that settles one button wrong
|
|
174
|
+
* and stays there. It needs the two faces to disagree across a
|
|
175
|
+
* collapse boundary, so it is latent rather than observed: no
|
|
176
|
+
* story in the library crosses one today, which is why `vrt`
|
|
177
|
+
* reports nothing either way. That makes it a gap to close, not a
|
|
178
|
+
* regression to chase — the bar is supposed to measure what is on
|
|
179
|
+
* screen, and until now it could not.
|
|
180
|
+
*
|
|
181
|
+
* `document.fonts.ready` resolves immediately when the fonts are
|
|
182
|
+
* already in, so a warm load pays one microtask and
|
|
183
|
+
* `setVisibleCount` bails on the unchanged answer.
|
|
184
|
+
*/
|
|
185
|
+
useLayoutEffect(() => {
|
|
186
|
+
// jsdom has no `document.fonts`, and neither does a very old
|
|
187
|
+
// browser. Nothing to wait for is not an error.
|
|
188
|
+
if (!document.fonts) {
|
|
189
|
+
return
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
let isActive = true
|
|
193
|
+
|
|
194
|
+
document.fonts.ready.then(() => {
|
|
195
|
+
if (isActive) {
|
|
196
|
+
measure()
|
|
197
|
+
}
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
return () => {
|
|
201
|
+
isActive = false
|
|
202
|
+
}
|
|
203
|
+
}, [measure])
|
|
204
|
+
|
|
162
205
|
useLayoutEffect(() => {
|
|
163
206
|
const container = containerRef.current
|
|
164
207
|
|
package/src/Tooltip/Tooltip.mdx
CHANGED
|
@@ -9,7 +9,7 @@ import * as TooltipStories from "./Tooltip.stories";
|
|
|
9
9
|
Replaces mux-magic's `FieldTooltip` — 130 lines of hand-rolled positioning, including a
|
|
10
10
|
`computePosition` that reimplements `flip` and `shift` against `window.innerHeight`.
|
|
11
11
|
|
|
12
|
-
<Canvas of={TooltipStories.
|
|
12
|
+
<Canvas of={TooltipStories.Playground} />
|
|
13
13
|
|
|
14
14
|
## Props
|
|
15
15
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { playgroundParameters } from "@charcuterie/storybook-config/story-parameters"
|
|
1
2
|
import type { Meta, StoryObj } from "@storybook/react"
|
|
2
3
|
|
|
3
4
|
import { placementArgType } from "../argTypes.storyHelpers.ts"
|
|
@@ -26,7 +27,8 @@ export default meta
|
|
|
26
27
|
|
|
27
28
|
type Story = StoryObj<typeof meta>
|
|
28
29
|
|
|
29
|
-
export const
|
|
30
|
+
export const Playground: Story = {
|
|
31
|
+
parameters: playgroundParameters,
|
|
30
32
|
args: {
|
|
31
33
|
children: <Button appearance="outline">Retry</Button>,
|
|
32
34
|
label: "Re-reads the disc from title 1.",
|
|
@@ -7,12 +7,12 @@ import { mountStory } from "../mountStory.testHelpers.ts"
|
|
|
7
7
|
import { expectAgentDrivable } from "../testing/index.ts"
|
|
8
8
|
import * as stories from "./Tooltip.stories.tsx"
|
|
9
9
|
|
|
10
|
-
const { AllVariants,
|
|
10
|
+
const { AllVariants, Playground, Interactive } =
|
|
11
11
|
composeStories(stories)
|
|
12
12
|
|
|
13
13
|
test("nothing is shown until something asks", async () => {
|
|
14
14
|
const { body, canvas, canvasElement } =
|
|
15
|
-
await mountStory(
|
|
15
|
+
await mountStory(Playground)
|
|
16
16
|
|
|
17
17
|
expectAgentDrivable(canvas, {
|
|
18
18
|
name: "Retry",
|
|
@@ -10,7 +10,7 @@ import * as VirtualizedGridStories from "./VirtualizedGrid.stories";
|
|
|
10
10
|
two-box structure, same content cap. The one difference is that a list of 2,000 costs
|
|
11
11
|
about as much as a list of 40.
|
|
12
12
|
|
|
13
|
-
<Canvas of={VirtualizedGridStories.
|
|
13
|
+
<Canvas of={VirtualizedGridStories.Playground} />
|
|
14
14
|
|
|
15
15
|
## Props
|
|
16
16
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { playgroundParameters } from "@charcuterie/storybook-config/story-parameters"
|
|
1
2
|
import type { Meta, StoryObj } from "@storybook/react"
|
|
2
3
|
import { useState } from "react"
|
|
3
4
|
|
|
@@ -107,7 +108,9 @@ type Story = StoryObj<typeof meta>
|
|
|
107
108
|
* Inspect the DOM and count: the row elements are the ones on
|
|
108
109
|
* screen plus `overscanRows` above and below.
|
|
109
110
|
*/
|
|
110
|
-
export const
|
|
111
|
+
export const Playground: Story = {
|
|
112
|
+
parameters: playgroundParameters,
|
|
113
|
+
}
|
|
111
114
|
|
|
112
115
|
/**
|
|
113
116
|
* The app-shell contract: `Main` is the one vertical scroll region,
|
|
@@ -38,7 +38,7 @@ import * as stories from "./VirtualizedGrid.stories.tsx"
|
|
|
38
38
|
|
|
39
39
|
const {
|
|
40
40
|
AllStates,
|
|
41
|
-
|
|
41
|
+
Playground,
|
|
42
42
|
InHiddenTabPanel,
|
|
43
43
|
InShellScrollRegion,
|
|
44
44
|
} = composeStories(stories)
|
|
@@ -84,7 +84,7 @@ const getRowIndexes = (canvasElement: HTMLElement) => [
|
|
|
84
84
|
]
|
|
85
85
|
|
|
86
86
|
test("2,000 items mount as a few dozen cells", async () => {
|
|
87
|
-
const { canvasElement } = await mountStory(
|
|
87
|
+
const { canvasElement } = await mountStory(Playground)
|
|
88
88
|
|
|
89
89
|
await waitFor(async () => {
|
|
90
90
|
await expect(
|
|
@@ -102,7 +102,7 @@ test("2,000 items mount as a few dozen cells", async () => {
|
|
|
102
102
|
})
|
|
103
103
|
|
|
104
104
|
test("the page is still as tall as the whole list", async () => {
|
|
105
|
-
const { canvasElement } = await mountStory(
|
|
105
|
+
const { canvasElement } = await mountStory(Playground)
|
|
106
106
|
|
|
107
107
|
await waitFor(async () => {
|
|
108
108
|
await expect(
|
|
@@ -123,7 +123,7 @@ test("the page is still as tall as the whole list", async () => {
|
|
|
123
123
|
})
|
|
124
124
|
|
|
125
125
|
test("scrolling changes which rows exist", async () => {
|
|
126
|
-
const { canvasElement } = await mountStory(
|
|
126
|
+
const { canvasElement } = await mountStory(Playground)
|
|
127
127
|
|
|
128
128
|
await waitFor(async () => {
|
|
129
129
|
await expect(
|
|
@@ -325,7 +325,7 @@ test("a grid inside a hidden panel mounts nothing and reveals without scrolling"
|
|
|
325
325
|
})
|
|
326
326
|
|
|
327
327
|
test("a screen reader is told the real length", async () => {
|
|
328
|
-
const { canvasElement } = await mountStory(
|
|
328
|
+
const { canvasElement } = await mountStory(Playground)
|
|
329
329
|
|
|
330
330
|
await waitFor(async () => {
|
|
331
331
|
await expect(
|
|
@@ -11,7 +11,7 @@ other component in M3 leans on: it is how a `Spinner` says "Loading…" without
|
|
|
11
11
|
how a `ProgressBar`'s label stays discoverable when the design does not show it, and how an
|
|
12
12
|
`IconButton`'s glyph gets a name.
|
|
13
13
|
|
|
14
|
-
<Canvas of={VisuallyHiddenStories.
|
|
14
|
+
<Canvas of={VisuallyHiddenStories.Playground} />
|
|
15
15
|
|
|
16
16
|
## Props
|
|
17
17
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { playgroundParameters } from "@charcuterie/storybook-config/story-parameters"
|
|
1
2
|
import type { Meta, StoryObj } from "@storybook/react"
|
|
2
3
|
|
|
3
4
|
import {
|
|
@@ -28,7 +29,8 @@ export default meta
|
|
|
28
29
|
|
|
29
30
|
type Story = StoryObj<typeof meta>
|
|
30
31
|
|
|
31
|
-
export const
|
|
32
|
+
export const Playground: Story = {
|
|
33
|
+
parameters: playgroundParameters,
|
|
32
34
|
args: { children: "Announced, never printed." },
|
|
33
35
|
render: (visuallyHiddenProps) => (
|
|
34
36
|
<StoryGrid columns={2}>
|
|
@@ -16,7 +16,7 @@ It is **opt-in**, from a subpath:
|
|
|
16
16
|
import { MarkdownEditorCodeMirror } from "@charcuterie/ui/markdown-editor-codemirror"
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
<Canvas of={MarkdownEditorCodeMirrorStories.
|
|
19
|
+
<Canvas of={MarkdownEditorCodeMirrorStories.Playground} />
|
|
20
20
|
|
|
21
21
|
## Props
|
|
22
22
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { playgroundParameters } from "@charcuterie/storybook-config/story-parameters"
|
|
1
2
|
import type { Meta, StoryObj } from "@storybook/react"
|
|
2
3
|
|
|
3
4
|
import {
|
|
@@ -91,7 +92,8 @@ type Story = StoryObj<typeof meta>
|
|
|
91
92
|
* genuinely larger, `**` is nowhere on screen, the checkboxes are
|
|
92
93
|
* checkboxes, and the bare URL is a link.
|
|
93
94
|
*/
|
|
94
|
-
export const
|
|
95
|
+
export const Playground: Story = {
|
|
96
|
+
parameters: playgroundParameters,
|
|
95
97
|
args: {
|
|
96
98
|
defaultValue: SAMPLE,
|
|
97
99
|
label: "Description",
|
|
@@ -5,7 +5,7 @@ import { test } from "vitest"
|
|
|
5
5
|
import { mountStory } from "../mountStory.testHelpers.ts"
|
|
6
6
|
import * as stories from "./MarkdownEditorCodeMirror.stories.tsx"
|
|
7
7
|
|
|
8
|
-
const { AllStates, CaretReveal,
|
|
8
|
+
const { AllStates, CaretReveal, Playground, Uploading } =
|
|
9
9
|
composeStories(stories)
|
|
10
10
|
|
|
11
11
|
type Canvas = Awaited<
|
|
@@ -169,7 +169,7 @@ test("a click low in an empty field lands in the document", async () => {
|
|
|
169
169
|
* anywhere in this stack would have quietly taken away.
|
|
170
170
|
*/
|
|
171
171
|
test("a document taller than the frame grows it", async () => {
|
|
172
|
-
const { canvas } = await mountStory(
|
|
172
|
+
const { canvas } = await mountStory(Playground)
|
|
173
173
|
|
|
174
174
|
const content = canvas.getByRole("textbox", {
|
|
175
175
|
name: "Description",
|
|
@@ -235,7 +235,7 @@ test("the toolbar offers Image when uploads are wired", async () => {
|
|
|
235
235
|
/** A button that opens a file picker and then has nowhere to send
|
|
236
236
|
* the file is worse than no button. */
|
|
237
237
|
test("the toolbar hides Image when nothing can take the file", async () => {
|
|
238
|
-
const { body, canvas } = await mountStory(
|
|
238
|
+
const { body, canvas } = await mountStory(Playground)
|
|
239
239
|
|
|
240
240
|
const overflow = canvas.queryByRole("button", {
|
|
241
241
|
name: "More actions",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { playgroundParameters } from "@charcuterie/storybook-config/story-parameters"
|
|
1
2
|
import type { Meta, StoryObj } from "@storybook/react"
|
|
2
3
|
import { useState } from "react"
|
|
3
4
|
|
|
@@ -183,7 +184,9 @@ type Story = StoryObj<typeof meta>
|
|
|
183
184
|
* `getAllByRole("toolbar")` returns nothing, because nothing in this
|
|
184
185
|
* component can render one.
|
|
185
186
|
*/
|
|
186
|
-
export const
|
|
187
|
+
export const Playground: Story = {
|
|
188
|
+
parameters: playgroundParameters,
|
|
189
|
+
}
|
|
187
190
|
|
|
188
191
|
/**
|
|
189
192
|
* The same string, in both surfaces, side by side.
|
|
@@ -9,7 +9,7 @@ import * as stories from "./MarkdownView.stories.tsx"
|
|
|
9
9
|
|
|
10
10
|
const {
|
|
11
11
|
AllStates,
|
|
12
|
-
|
|
12
|
+
Playground,
|
|
13
13
|
HostileMarkdown,
|
|
14
14
|
Interactive,
|
|
15
15
|
NestedLinkText,
|
|
@@ -26,7 +26,7 @@ const {
|
|
|
26
26
|
* there is the fix.
|
|
27
27
|
*/
|
|
28
28
|
test("there is no toolbar, not a hidden one", async () => {
|
|
29
|
-
const { canvas } = await mountStory(
|
|
29
|
+
const { canvas } = await mountStory(Playground)
|
|
30
30
|
|
|
31
31
|
await expect(canvas.queryAllByRole("toolbar")).toEqual([])
|
|
32
32
|
|
|
@@ -47,7 +47,7 @@ test("there is no toolbar, not a hidden one", async () => {
|
|
|
47
47
|
* asserted together.
|
|
48
48
|
*/
|
|
49
49
|
test("it is an article whose headings are headings", async () => {
|
|
50
|
-
const { canvas } = await mountStory(
|
|
50
|
+
const { canvas } = await mountStory(Playground)
|
|
51
51
|
|
|
52
52
|
const article = expectAgentDrivable(canvas, {
|
|
53
53
|
name: "Description",
|
|
@@ -86,12 +86,28 @@ test("it is an article whose headings are headings", async () => {
|
|
|
86
86
|
test("it renders what the editor renders", async () => {
|
|
87
87
|
const { canvas } = await mountStory(SameAsTheEditor)
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
89
|
+
// ⚠️ WAIT FOR THE COUNT, not for "a table".
|
|
90
|
+
//
|
|
91
|
+
// This test is the only one in the file that mounts a story with
|
|
92
|
+
// BOTH surfaces, so it is the only one that has to wait for
|
|
93
|
+
// CodeMirror. `MarkdownView` paints its table in the first commit;
|
|
94
|
+
// CodeMirror parses and decorates the document afterwards. A
|
|
95
|
+
// synchronous `getAllByRole("table")` therefore returned ONE table
|
|
96
|
+
// on a slow runner, `editorTable` was `undefined`, and the failure
|
|
97
|
+
// read "expected undefined to be defined" — which says nothing
|
|
98
|
+
// about the real cause (CI, 2026-09-14).
|
|
99
|
+
//
|
|
100
|
+
// ⚠️ A bare `findAllByRole` does NOT fix it. `findAllBy*` retries
|
|
101
|
+
// until the query matches AT LEAST ONE element, and one element is
|
|
102
|
+
// exactly the broken state: the view's table is there from the
|
|
103
|
+
// start. The count is the condition, so the count goes in the wait.
|
|
104
|
+
const [viewTable, editorTable] = await waitFor(() => {
|
|
105
|
+
const tables = canvas.getAllByRole("table")
|
|
106
|
+
|
|
107
|
+
expect(tables).toHaveLength(2)
|
|
108
|
+
|
|
109
|
+
return tables
|
|
110
|
+
})
|
|
95
111
|
|
|
96
112
|
const toCellText = (table: HTMLElement) =>
|
|
97
113
|
Array.from(table.querySelectorAll("th, td")).map(
|
|
@@ -103,10 +119,16 @@ test("it renders what the editor renders", async () => {
|
|
|
103
119
|
).toEqual(toCellText(editorTable as HTMLElement))
|
|
104
120
|
|
|
105
121
|
// Three task boxes in each, and the first one ticked in each.
|
|
106
|
-
|
|
107
|
-
|
|
122
|
+
// Same wait, same reason: CodeMirror's boxes arrive with its
|
|
123
|
+
// decorations, so a count taken too early sees only the view's.
|
|
124
|
+
const checkboxes = await waitFor(() => {
|
|
125
|
+
const boxes: HTMLInputElement[] =
|
|
126
|
+
canvas.getAllByRole("checkbox")
|
|
108
127
|
|
|
109
|
-
|
|
128
|
+
expect(boxes).toHaveLength(6)
|
|
129
|
+
|
|
130
|
+
return boxes
|
|
131
|
+
})
|
|
110
132
|
|
|
111
133
|
await expect(
|
|
112
134
|
checkboxes.filter((checkbox) => checkbox.checked),
|
|
@@ -114,11 +136,13 @@ test("it renders what the editor renders", async () => {
|
|
|
114
136
|
|
|
115
137
|
// The bare URL, autolinked on both sides with nothing typed
|
|
116
138
|
// around it.
|
|
117
|
-
await
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
139
|
+
await waitFor(() => {
|
|
140
|
+
expect(
|
|
141
|
+
canvas.getAllByText(
|
|
142
|
+
"https://example.invalid/product?id=1234",
|
|
143
|
+
),
|
|
144
|
+
).toHaveLength(2)
|
|
145
|
+
})
|
|
122
146
|
})
|
|
123
147
|
|
|
124
148
|
/**
|
|
@@ -265,7 +289,7 @@ test("hostile markdown renders as characters, not as behaviour", async () => {
|
|
|
265
289
|
* else noticing.
|
|
266
290
|
*/
|
|
267
291
|
test("the text can be selected", async () => {
|
|
268
|
-
const { canvas } = await mountStory(
|
|
292
|
+
const { canvas } = await mountStory(Playground)
|
|
269
293
|
|
|
270
294
|
const article = canvas.getByRole("article")
|
|
271
295
|
|
|
@@ -360,7 +384,7 @@ test.each([
|
|
|
360
384
|
* survives a change to the base font size.
|
|
361
385
|
*/
|
|
362
386
|
test("the document is set at prose leading", async () => {
|
|
363
|
-
const { canvas } = await mountStory(
|
|
387
|
+
const { canvas } = await mountStory(Playground)
|
|
364
388
|
|
|
365
389
|
const article = canvas.getByRole("article")
|
|
366
390
|
|
|
@@ -379,7 +403,7 @@ test("the document is set at prose leading", async () => {
|
|
|
379
403
|
* backticks comes out as a wall of emphasis nobody asked for.
|
|
380
404
|
*/
|
|
381
405
|
test("a code span is smaller than the prose it sits in", async () => {
|
|
382
|
-
const { canvas } = await mountStory(
|
|
406
|
+
const { canvas } = await mountStory(Playground)
|
|
383
407
|
|
|
384
408
|
const article = canvas.getByRole("article")
|
|
385
409
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { playgroundParameters } from "@charcuterie/storybook-config/story-parameters"
|
|
1
2
|
import type { Meta, StoryObj } from "@storybook/react"
|
|
2
3
|
import {
|
|
3
4
|
MemoryRouter,
|
|
@@ -92,7 +93,8 @@ const EpisodePage = () => (
|
|
|
92
93
|
* `TextLink` becomes a soft navigation and `Main` keeps the
|
|
93
94
|
* reader's place because `ReactRouterAdapter` is above them both.
|
|
94
95
|
*/
|
|
95
|
-
export const
|
|
96
|
+
export const Playground: Story = {
|
|
97
|
+
parameters: playgroundParameters,
|
|
96
98
|
render: () => (
|
|
97
99
|
<MemoryRouter initialEntries={["/library"]}>
|
|
98
100
|
<ReactRouterAdapter>
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
} from "../viewport.testHelpers.ts"
|
|
12
12
|
import * as stories from "./ReactRouterAdapter.stories.tsx"
|
|
13
13
|
|
|
14
|
-
const {
|
|
14
|
+
const { Playground } = composeStories(stories)
|
|
15
15
|
|
|
16
16
|
// The offsets are module state, so they outlive a mount. A test
|
|
17
17
|
// must not.
|
|
@@ -33,7 +33,7 @@ afterEach(() => {
|
|
|
33
33
|
test("an app renders one component and gets both seams", async () => {
|
|
34
34
|
await setViewport(DESKTOP)
|
|
35
35
|
|
|
36
|
-
const { canvas } = await mountStory(
|
|
36
|
+
const { canvas } = await mountStory(Playground)
|
|
37
37
|
|
|
38
38
|
const main = canvas.getByRole("main")
|
|
39
39
|
|