@charcuterie/ui 3.17.0 → 3.19.1
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/dist/Combobox/Combobox.d.ts +14 -1
- package/dist/Combobox/Combobox.d.ts.map +1 -1
- package/dist/Combobox/Combobox.js +124 -56
- package/dist/Combobox/Combobox.js.map +1 -1
- package/dist/Combobox/ComboboxOption.d.ts +4 -1
- package/dist/Combobox/ComboboxOption.d.ts.map +1 -1
- package/dist/Combobox/ComboboxOption.js +8 -2
- package/dist/Combobox/ComboboxOption.js.map +1 -1
- package/dist/Listbox/Listbox.d.ts +13 -1
- package/dist/Listbox/Listbox.d.ts.map +1 -1
- package/dist/Listbox/Listbox.js +4 -2
- package/dist/Listbox/Listbox.js.map +1 -1
- package/dist/Listbox/ListboxOption.d.ts +4 -1
- package/dist/Listbox/ListboxOption.d.ts.map +1 -1
- package/dist/Listbox/ListboxOption.js +7 -2
- package/dist/Listbox/ListboxOption.js.map +1 -1
- package/dist/Menu/Menu.d.ts +15 -1
- package/dist/Menu/Menu.d.ts.map +1 -1
- package/dist/Menu/Menu.js +39 -5
- package/dist/Menu/Menu.js.map +1 -1
- package/dist/Menu/MenuAction.d.ts +4 -1
- package/dist/Menu/MenuAction.d.ts.map +1 -1
- package/dist/Menu/MenuAction.js +22 -4
- package/dist/Menu/MenuAction.js.map +1 -1
- package/dist/Overlay/useAnchoredOverlay.d.ts +25 -1
- package/dist/Overlay/useAnchoredOverlay.d.ts.map +1 -1
- package/dist/Overlay/useAnchoredOverlay.js +8 -4
- package/dist/Overlay/useAnchoredOverlay.js.map +1 -1
- package/dist/Overlay/usePanelItemSize.d.ts +3 -0
- package/dist/Overlay/usePanelItemSize.d.ts.map +1 -0
- package/dist/Overlay/usePanelItemSize.js +59 -0
- package/dist/Overlay/usePanelItemSize.js.map +1 -0
- package/dist/controlStyles.d.ts +30 -0
- package/dist/controlStyles.d.ts.map +1 -1
- package/dist/controlStyles.js +34 -0
- package/dist/controlStyles.js.map +1 -1
- package/dist/markdownEditorCodeMirror/MarkdownEditorCodeMirror.d.ts.map +1 -1
- package/dist/markdownEditorCodeMirror/MarkdownEditorCodeMirror.js +1 -1
- package/dist/markdownEditorCodeMirror/MarkdownEditorCodeMirror.js.map +1 -1
- package/dist/markdownEditorCodeMirror/MarkdownView.d.ts +24 -1
- package/dist/markdownEditorCodeMirror/MarkdownView.d.ts.map +1 -1
- package/dist/markdownEditorCodeMirror/MarkdownView.js +14 -2
- package/dist/markdownEditorCodeMirror/MarkdownView.js.map +1 -1
- package/dist/markdownEditorCodeMirror/livePreview.d.ts +7 -0
- package/dist/markdownEditorCodeMirror/livePreview.d.ts.map +1 -1
- package/dist/markdownEditorCodeMirror/livePreview.js +28 -2
- package/dist/markdownEditorCodeMirror/livePreview.js.map +1 -1
- package/dist/markdownEditorCodeMirror/livePreviewRanges.d.ts +22 -2
- package/dist/markdownEditorCodeMirror/livePreviewRanges.d.ts.map +1 -1
- package/dist/markdownEditorCodeMirror/livePreviewRanges.js +26 -7
- package/dist/markdownEditorCodeMirror/livePreviewRanges.js.map +1 -1
- package/dist/viewport.testHelpers.d.ts +18 -4
- package/dist/viewport.testHelpers.d.ts.map +1 -1
- package/dist/viewport.testHelpers.js +18 -6
- package/dist/viewport.testHelpers.js.map +1 -1
- package/package.json +2 -2
- package/src/Combobox/Combobox.mdx +26 -0
- package/src/Combobox/Combobox.stories.tsx +65 -0
- package/src/Combobox/Combobox.test.tsx +135 -0
- package/src/Combobox/Combobox.tsx +176 -83
- package/src/Combobox/ComboboxOption.tsx +11 -1
- package/src/Listbox/Listbox.mdx +24 -0
- package/src/Listbox/Listbox.stories.tsx +81 -5
- package/src/Listbox/Listbox.tsx +17 -0
- package/src/Listbox/ListboxOption.tsx +10 -1
- package/src/Menu/Menu.mdx +49 -0
- package/src/Menu/Menu.stories.tsx +113 -2
- package/src/Menu/Menu.test.tsx +129 -0
- package/src/Menu/Menu.tsx +62 -3
- package/src/Menu/MenuAction.tsx +29 -4
- package/src/Overlay/useAnchoredOverlay.ts +33 -5
- package/src/Overlay/usePanelItemSize.ts +71 -0
- package/src/controlStyles.ts +38 -0
- package/src/markdownEditorCodeMirror/MarkdownEditorCodeMirror.test.tsx +185 -0
- package/src/markdownEditorCodeMirror/MarkdownEditorCodeMirror.tsx +42 -1
- package/src/markdownEditorCodeMirror/MarkdownView.tsx +40 -1
- package/src/markdownEditorCodeMirror/livePreview.ts +40 -3
- package/src/markdownEditorCodeMirror/livePreviewRanges.test.ts +137 -0
- package/src/markdownEditorCodeMirror/livePreviewRanges.ts +50 -6
- package/src/sourceRules.test.ts +9 -0
- package/src/viewport.testHelpers.ts +21 -6
|
@@ -11,17 +11,23 @@ import { page } from "@vitest/browser/context";
|
|
|
11
11
|
* fails intermittently — which is the worst possible property for
|
|
12
12
|
* the one gate that says the shell does not scroll sideways.
|
|
13
13
|
*
|
|
14
|
-
* Polling `innerWidth` is the honest wait:
|
|
15
|
-
* the media queries and the layout resolved against, so
|
|
16
|
-
*
|
|
17
|
-
* claims.
|
|
14
|
+
* Polling `innerWidth`/`innerHeight` is the honest wait: they are the
|
|
15
|
+
* same numbers the media queries and the layout resolved against, so
|
|
16
|
+
* when they match, the thing under test really is at the size the
|
|
17
|
+
* test claims.
|
|
18
|
+
*
|
|
19
|
+
* **Height counts too**, and it did not used to. A panel row's size
|
|
20
|
+
* steps down under `(height <= 40rem)` (`usePanelItemSize`), so a test
|
|
21
|
+
* that shrinks only the height would have raced the resize with
|
|
22
|
+
* nothing to wait on and measured the tall rows about half the time.
|
|
18
23
|
*/
|
|
19
24
|
export const setViewport = async ({ height, width, }) => {
|
|
20
25
|
await page.viewport(width, height);
|
|
21
26
|
const deadline = Date.now() + 2000;
|
|
22
|
-
while (globalThis.innerWidth !== width
|
|
27
|
+
while (globalThis.innerWidth !== width ||
|
|
28
|
+
globalThis.innerHeight !== height) {
|
|
23
29
|
if (Date.now() > deadline) {
|
|
24
|
-
throw new Error(`The viewport never reached ${width}px — it is ${globalThis.innerWidth}px. Every
|
|
30
|
+
throw new Error(`The viewport never reached ${width}x${height}px — it is ${globalThis.innerWidth}x${globalThis.innerHeight}px. Every size assertion after this point would be measuring the wrong page.`);
|
|
25
31
|
}
|
|
26
32
|
await new Promise((resolve) => {
|
|
27
33
|
globalThis.requestAnimationFrame(resolve);
|
|
@@ -31,4 +37,10 @@ export const setViewport = async ({ height, width, }) => {
|
|
|
31
37
|
/** A phone in portrait — the width plex-channels scrolls at. */
|
|
32
38
|
export const PHONE = { height: 844, width: 390 };
|
|
33
39
|
export const DESKTOP = { height: 900, width: 1440 };
|
|
40
|
+
/**
|
|
41
|
+
* A window short enough to trip `usePanelItemSize`'s first step-down
|
|
42
|
+
* (`height <= 40rem`, so 640px) and wide enough that nothing else
|
|
43
|
+
* about the layout changes with it.
|
|
44
|
+
*/
|
|
45
|
+
export const SHORT_WINDOW = { height: 600, width: 1440 };
|
|
34
46
|
//# sourceMappingURL=viewport.testHelpers.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"viewport.testHelpers.js","sourceRoot":"","sources":["../src/viewport.testHelpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAA;AAE9C
|
|
1
|
+
{"version":3,"file":"viewport.testHelpers.js","sourceRoot":"","sources":["../src/viewport.testHelpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAA;AAE9C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,EAAE,EAChC,MAAM,EACN,KAAK,GAIN,EAAiB,EAAE;IAClB,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;IAElC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAA;IAElC,OACE,UAAU,CAAC,UAAU,KAAK,KAAK;QAC/B,UAAU,CAAC,WAAW,KAAK,MAAM,EACjC,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CACb,8BAA8B,KAAK,IAAI,MAAM,cAAc,UAAU,CAAC,UAAU,IAAI,UAAU,CAAC,WAAW,8EAA8E,CACzL,CAAA;QACH,CAAC;QAED,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC5B,UAAU,CAAC,qBAAqB,CAAC,OAAO,CAAC,CAAA;QAC3C,CAAC,CAAC,CAAA;IACJ,CAAC;AACH,CAAC,CAAA;AAED,gEAAgE;AAChE,MAAM,CAAC,MAAM,KAAK,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,CAAA;AAEhD,MAAM,CAAC,MAAM,OAAO,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;AAEnD;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@charcuterie/ui",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.19.1",
|
|
4
4
|
"description": "The Charcuterie components. Tokens for look, @charcuterie/logic for state, ARIA that agents can actually drive.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"typecheck": "tsc --noEmit"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@charcuterie/logic": "2.
|
|
56
|
+
"@charcuterie/logic": "2.3.0",
|
|
57
57
|
"@charcuterie/tokens": "1.6.0",
|
|
58
58
|
"@floating-ui/react": "^0.27.20",
|
|
59
59
|
"@tanstack/react-virtual": "^3.13.12",
|
|
@@ -99,3 +99,29 @@ for a 4,000-item list. Below the threshold the list is plain DOM.
|
|
|
99
99
|
<Canvas of={ComboboxStories.Virtualized} />
|
|
100
100
|
|
|
101
101
|
<Canvas of={ComboboxStories.Interactive} />
|
|
102
|
+
|
|
103
|
+
## One panel, one row size
|
|
104
|
+
|
|
105
|
+
`itemSize` drives the search field **and** the options together, from the same
|
|
106
|
+
density-aware `--control-height-*` tokens a `Button` reads. It defaults to `md`, so at the
|
|
107
|
+
default a trigger, the search field and every option are all 2.25rem.
|
|
108
|
+
|
|
109
|
+
Covering the search row is the point rather than a bonus. The report that started this was
|
|
110
|
+
that a `Combobox`'s trigger was bigger than the options under it — but the panel's own
|
|
111
|
+
field was bigger than them too, and **two sizes inside one panel** is the defect rather
|
|
112
|
+
than either number being wrong.
|
|
113
|
+
|
|
114
|
+
It also retires a hand-picked number. The search row used to carry `px-3` to line its caret
|
|
115
|
+
up with the option labels below it, which worked only while the option padding was a
|
|
116
|
+
hardcoded `px-2`; it is now wrapped in the list's own `p-1` and given the same row-size
|
|
117
|
+
class, so the two cannot drift apart again.
|
|
118
|
+
|
|
119
|
+
`lg` is the fat one — 2.75rem, the 44px WCAG 2.5.5 target. A window shorter than 40rem
|
|
120
|
+
steps each size down one step on its own.
|
|
121
|
+
|
|
122
|
+
The virtualizer's `estimateSize` tracks `itemSize` too. Every row is re-measured
|
|
123
|
+
immediately, so the estimate only decides how far the scrollbar lies before the first
|
|
124
|
+
measurement lands — but a `lg` list opening with a scrollbar a third too short and visibly
|
|
125
|
+
settling is exactly the kind of thing nobody files a bug about.
|
|
126
|
+
|
|
127
|
+
<Canvas of={ComboboxStories.ItemSizes} />
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { useVisibility } from "@charcuterie/logic"
|
|
2
|
+
import type { ControlSize } from "@charcuterie/tokens"
|
|
2
3
|
import type { Meta, StoryObj } from "@storybook/react"
|
|
3
4
|
import type { ReactNode } from "react"
|
|
4
5
|
import { useRef, useState } from "react"
|
|
5
6
|
|
|
7
|
+
import { controlSizeArgType } from "../argTypes.storyHelpers.ts"
|
|
6
8
|
import { Button } from "../Button/Button.tsx"
|
|
9
|
+
import {
|
|
10
|
+
StoryCell,
|
|
11
|
+
StoryGrid,
|
|
12
|
+
} from "../board.storyHelpers.tsx"
|
|
7
13
|
import type { ListboxItem } from "../Listbox/Listbox.tsx"
|
|
8
14
|
import { Combobox } from "./Combobox.tsx"
|
|
9
15
|
|
|
@@ -49,6 +55,8 @@ const meta = {
|
|
|
49
55
|
title: "Components/Controls/Combobox",
|
|
50
56
|
component: Combobox,
|
|
51
57
|
parameters: { layout: "padded" },
|
|
58
|
+
argTypes: { itemSize: controlSizeArgType },
|
|
59
|
+
args: { itemSize: "md" },
|
|
52
60
|
} satisfies Meta<typeof Combobox>
|
|
53
61
|
|
|
54
62
|
export default meta
|
|
@@ -65,6 +73,7 @@ const ComboboxHarness = ({
|
|
|
65
73
|
isLoading,
|
|
66
74
|
isMultiple,
|
|
67
75
|
isVirtualized,
|
|
76
|
+
itemSize,
|
|
68
77
|
options,
|
|
69
78
|
selectedValue,
|
|
70
79
|
triggerLabel,
|
|
@@ -78,6 +87,7 @@ const ComboboxHarness = ({
|
|
|
78
87
|
isLoading?: boolean
|
|
79
88
|
isMultiple?: boolean
|
|
80
89
|
isVirtualized?: boolean
|
|
90
|
+
itemSize?: ControlSize
|
|
81
91
|
options: ListboxItem[]
|
|
82
92
|
selectedValue?: readonly string[] | string
|
|
83
93
|
triggerLabel: string
|
|
@@ -105,6 +115,7 @@ const ComboboxHarness = ({
|
|
|
105
115
|
isLoading={isLoading}
|
|
106
116
|
isMultiple={isMultiple}
|
|
107
117
|
isVirtualized={isVirtualized}
|
|
118
|
+
itemSize={itemSize}
|
|
108
119
|
isVisible={isVisible}
|
|
109
120
|
onDismiss={hide}
|
|
110
121
|
onSelect={(value) => {
|
|
@@ -589,3 +600,57 @@ export const VirtualizedChosenValue: Story = {
|
|
|
589
600
|
/>
|
|
590
601
|
),
|
|
591
602
|
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* The three row sizes — and the fix the owner's screenshot asked for.
|
|
606
|
+
*
|
|
607
|
+
* The report was that a `Combobox`'s trigger was visibly bigger than
|
|
608
|
+
* the options under it. Two things were true at once: the option row
|
|
609
|
+
* was a hardcoded `px-2 py-1.5 text-sm` that no control shares, and
|
|
610
|
+
* the panel's **own** search field was bigger than the options it
|
|
611
|
+
* filters — two sizes inside one panel, which is the defect rather
|
|
612
|
+
* than either number being wrong.
|
|
613
|
+
*
|
|
614
|
+
* `itemSize` now drives the search row and the options together, from
|
|
615
|
+
* the same `--control-height-*` a `Button` reads. At the `md` default
|
|
616
|
+
* a trigger, the search field and every option are all 2.25rem.
|
|
617
|
+
*/
|
|
618
|
+
export const ItemSizes: Story = {
|
|
619
|
+
args: {
|
|
620
|
+
isVisible: false,
|
|
621
|
+
onDismiss: () => {},
|
|
622
|
+
onSelect: () => {},
|
|
623
|
+
options: LANGUAGES,
|
|
624
|
+
trigger: <Button>Search languages</Button>,
|
|
625
|
+
},
|
|
626
|
+
render: () => (
|
|
627
|
+
<StoryGrid columns={3}>
|
|
628
|
+
<StoryCell label="sm — the old option">
|
|
629
|
+
<ComboboxHarness
|
|
630
|
+
isInitiallyVisible
|
|
631
|
+
itemSize="sm"
|
|
632
|
+
options={LANGUAGES}
|
|
633
|
+
triggerLabel="Search languages"
|
|
634
|
+
/>
|
|
635
|
+
</StoryCell>
|
|
636
|
+
|
|
637
|
+
<StoryCell label="md — the default, matches the trigger">
|
|
638
|
+
<ComboboxHarness
|
|
639
|
+
isInitiallyVisible
|
|
640
|
+
itemSize="md"
|
|
641
|
+
options={LANGUAGES}
|
|
642
|
+
triggerLabel="Search languages"
|
|
643
|
+
/>
|
|
644
|
+
</StoryCell>
|
|
645
|
+
|
|
646
|
+
<StoryCell label="lg — the fat option (44px)">
|
|
647
|
+
<ComboboxHarness
|
|
648
|
+
isInitiallyVisible
|
|
649
|
+
itemSize="lg"
|
|
650
|
+
options={LANGUAGES}
|
|
651
|
+
triggerLabel="Search languages"
|
|
652
|
+
/>
|
|
653
|
+
</StoryCell>
|
|
654
|
+
</StoryGrid>
|
|
655
|
+
),
|
|
656
|
+
}
|
|
@@ -616,3 +616,138 @@ test("the chosen row is scrolled into view, not merely highlighted", async () =>
|
|
|
616
616
|
)
|
|
617
617
|
})
|
|
618
618
|
})
|
|
619
|
+
|
|
620
|
+
test("reopening without picking still lands on the chosen row", async () => {
|
|
621
|
+
const mounted = await mountStory(ChosenValueOnOpen)
|
|
622
|
+
|
|
623
|
+
const trigger = expectAgentDrivable(mounted.canvas, {
|
|
624
|
+
name: "Search languages",
|
|
625
|
+
role: "button",
|
|
626
|
+
})
|
|
627
|
+
|
|
628
|
+
const isChosenRowInView = () => {
|
|
629
|
+
const row = mounted.body.getByRole("option", {
|
|
630
|
+
name: "Item 58",
|
|
631
|
+
})
|
|
632
|
+
|
|
633
|
+
const list = row.closest(
|
|
634
|
+
"[role=listbox]",
|
|
635
|
+
) as HTMLElement
|
|
636
|
+
|
|
637
|
+
const listBox = list.getBoundingClientRect()
|
|
638
|
+
const rowBox = row.getBoundingClientRect()
|
|
639
|
+
|
|
640
|
+
return (
|
|
641
|
+
rowBox.top >= listBox.top &&
|
|
642
|
+
rowBox.bottom <= listBox.bottom
|
|
643
|
+
)
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
await userEvent.click(trigger)
|
|
647
|
+
|
|
648
|
+
await waitFor(() => {
|
|
649
|
+
expect(isChosenRowInView()).toBe(true)
|
|
650
|
+
})
|
|
651
|
+
|
|
652
|
+
// Close it the way a misclick-free dismissal does — no pick, so the
|
|
653
|
+
// chosen value and therefore the seed index are both unchanged.
|
|
654
|
+
await userEvent.keyboard("{Escape}")
|
|
655
|
+
|
|
656
|
+
await waitFor(() => {
|
|
657
|
+
expect(mounted.body.queryByRole("combobox")).toBeNull()
|
|
658
|
+
})
|
|
659
|
+
|
|
660
|
+
await userEvent.click(trigger)
|
|
661
|
+
|
|
662
|
+
// The seed must run again. It nearly did not: `activeIndex` already
|
|
663
|
+
// held the seeded index from the first open, so setting it was a no-op
|
|
664
|
+
// and the re-render the scroll rides on never happened.
|
|
665
|
+
await waitFor(() => {
|
|
666
|
+
expect(isChosenRowInView()).toBe(true)
|
|
667
|
+
})
|
|
668
|
+
})
|
|
669
|
+
|
|
670
|
+
test("a windowed list reseeds its window on every open, not just the first", async () => {
|
|
671
|
+
const mounted = await mountStory(VirtualizedChosenValue)
|
|
672
|
+
|
|
673
|
+
const trigger = expectAgentDrivable(mounted.canvas, {
|
|
674
|
+
name: "Search 500 tracks",
|
|
675
|
+
role: "button",
|
|
676
|
+
})
|
|
677
|
+
|
|
678
|
+
// The story opens already showing the window around Track 400.
|
|
679
|
+
await waitFor(() => {
|
|
680
|
+
expect(
|
|
681
|
+
mounted.body.getByRole("option", {
|
|
682
|
+
name: "Track 400",
|
|
683
|
+
}),
|
|
684
|
+
).toBeInTheDocument()
|
|
685
|
+
})
|
|
686
|
+
|
|
687
|
+
await userEvent.click(trigger)
|
|
688
|
+
|
|
689
|
+
await waitFor(() => {
|
|
690
|
+
expect(mounted.body.queryByRole("combobox")).toBeNull()
|
|
691
|
+
})
|
|
692
|
+
|
|
693
|
+
await userEvent.click(trigger)
|
|
694
|
+
|
|
695
|
+
// Rows 1–12 would be the window if the seed had run only once.
|
|
696
|
+
await waitFor(() => {
|
|
697
|
+
expect(
|
|
698
|
+
mounted.body.getByRole("option", {
|
|
699
|
+
name: "Track 400",
|
|
700
|
+
}),
|
|
701
|
+
).toBeInTheDocument()
|
|
702
|
+
})
|
|
703
|
+
|
|
704
|
+
await expect(
|
|
705
|
+
mounted.body.queryByRole("option", { name: "Track 1" }),
|
|
706
|
+
).toBeNull()
|
|
707
|
+
})
|
|
708
|
+
|
|
709
|
+
/**
|
|
710
|
+
* One panel, one row size.
|
|
711
|
+
*
|
|
712
|
+
* The owner's report was that the search field was visibly bigger
|
|
713
|
+
* than the options it filters — *"the button is huge, but the options
|
|
714
|
+
* don't match it"* — and two sizes inside one panel is the defect,
|
|
715
|
+
* not the field being wrong or the option being wrong. `itemSize`
|
|
716
|
+
* drives both, so this measures the pair rather than either number.
|
|
717
|
+
*/
|
|
718
|
+
test("the search row and an option are the same height", async () => {
|
|
719
|
+
const { body, canvas } = await mountStory(Interactive)
|
|
720
|
+
|
|
721
|
+
await userEvent.click(
|
|
722
|
+
expectAgentDrivable(canvas, {
|
|
723
|
+
name: "Search languages",
|
|
724
|
+
role: "button",
|
|
725
|
+
}),
|
|
726
|
+
)
|
|
727
|
+
|
|
728
|
+
const input = body.getByRole("combobox")
|
|
729
|
+
|
|
730
|
+
await waitFor(() => {
|
|
731
|
+
expect(input).toHaveFocus()
|
|
732
|
+
})
|
|
733
|
+
|
|
734
|
+
// The row is the input's *parent*: the input itself is a bare
|
|
735
|
+
// `flex-1` field, and the box that carries the row-size class — the
|
|
736
|
+
// height, the inline padding, the type — is the wrapper around it.
|
|
737
|
+
const searchRow = input.closest("div")
|
|
738
|
+
|
|
739
|
+
const [option] = body.getAllByRole("option")
|
|
740
|
+
|
|
741
|
+
expect(option).toBeDefined()
|
|
742
|
+
|
|
743
|
+
expect(searchRow?.getBoundingClientRect().height).toBe(
|
|
744
|
+
option?.getBoundingClientRect().height,
|
|
745
|
+
)
|
|
746
|
+
|
|
747
|
+
// And the query reads at the size of the options it is filtering,
|
|
748
|
+
// which is inherited from that wrapper rather than set on the input.
|
|
749
|
+
expect(globalThis.getComputedStyle(input).fontSize).toBe(
|
|
750
|
+
globalThis.getComputedStyle(option as HTMLElement)
|
|
751
|
+
.fontSize,
|
|
752
|
+
)
|
|
753
|
+
})
|