@tangle-network/ui 11.51.7 → 11.51.8

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.
Files changed (36) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/package.json +1 -1
  3. package/src/editor/source-markdown-editor.test.tsx +7 -7
  4. package/src/editor/document-editor-pane.test.tsx +0 -60
  5. package/src/editor/editor-lazy.test.tsx +0 -102
  6. package/src/editor/editor-peers.test.ts +0 -250
  7. package/src/editor/editor-provider.test.tsx +0 -226
  8. package/src/editor/markdown-document-editor.test.tsx +0 -126
  9. package/src/editor/tiptap-editor.test.tsx +0 -228
  10. package/src/files/file-artifact-pane.test.tsx +0 -301
  11. package/src/files/file-card.test.tsx +0 -62
  12. package/src/files/file-list.test.tsx +0 -81
  13. package/src/files/pdf-loader.test.ts +0 -130
  14. package/src/files/pdf-viewer.test.tsx +0 -245
  15. package/src/files/rich-file-tree.test.tsx +0 -90
  16. package/src/markdown/api-presentation.test.tsx +0 -63
  17. package/src/markdown/code-block.test.tsx +0 -201
  18. package/src/markdown/markdown-table.test.tsx +0 -76
  19. package/src/markdown/markdown.test.tsx +0 -166
  20. package/src/openui/schema.test.tsx +0 -158
  21. package/src/primitives/button.test.tsx +0 -230
  22. package/src/primitives/card.test.tsx +0 -71
  23. package/src/primitives/chip.test.tsx +0 -200
  24. package/src/primitives/date-picker.test.tsx +0 -146
  25. package/src/primitives/dialog.test.tsx +0 -75
  26. package/src/primitives/input.test.tsx +0 -161
  27. package/src/primitives/interaction-states.test.tsx +0 -287
  28. package/src/primitives/pagination-alert.test.tsx +0 -180
  29. package/src/primitives/popover-collapsible-alert-dialog-context-menu-slider.test.tsx +0 -165
  30. package/src/primitives/radio-group.test.tsx +0 -26
  31. package/src/primitives/segmented-control.test.tsx +0 -451
  32. package/src/primitives/table.test.tsx +0 -72
  33. package/src/primitives/theme-toggle.test.tsx +0 -168
  34. package/src/primitives/toast.test.tsx +0 -130
  35. package/src/primitives/wizard.test.tsx +0 -106
  36. package/src/run/expandable-content.test.tsx +0 -52
@@ -1,451 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
2
- import { render, screen } from "@testing-library/react"
3
- import userEvent from "@testing-library/user-event"
4
- import { SegmentedControl, type SegmentedControlOption } from "./segmented-control"
5
-
6
- const options: SegmentedControlOption[] = [
7
- { value: "all", label: "All" },
8
- { value: "personal", label: "Personal" },
9
- { value: "team", label: "Team" },
10
- ]
11
-
12
- describe("SegmentedControl", () => {
13
- it("renders all options as radio buttons", () => {
14
- render(
15
- <SegmentedControl value="all" onValueChange={vi.fn()} options={options} />,
16
- )
17
- const radios = screen.getAllByRole("radio")
18
- expect(radios).toHaveLength(3)
19
- expect(screen.getByText("All")).toBeInTheDocument()
20
- expect(screen.getByText("Personal")).toBeInTheDocument()
21
- expect(screen.getByText("Team")).toBeInTheDocument()
22
- })
23
-
24
- it("marks the active option with aria-checked", () => {
25
- render(
26
- <SegmentedControl value="personal" onValueChange={vi.fn()} options={options} />,
27
- )
28
- const personalRadio = screen.getByRole("radio", { name: "Personal" })
29
- const allRadio = screen.getByRole("radio", { name: "All" })
30
-
31
- expect(personalRadio).toHaveAttribute("aria-checked", "true")
32
- expect(allRadio).toHaveAttribute("aria-checked", "false")
33
- })
34
-
35
- it("sets tabIndex 0 on active option and -1 on inactive options", () => {
36
- render(
37
- <SegmentedControl value="all" onValueChange={vi.fn()} options={options} />,
38
- )
39
- const radios = screen.getAllByRole("radio")
40
- expect(radios[0]).toHaveAttribute("tabindex", "0")
41
- expect(radios[1]).toHaveAttribute("tabindex", "-1")
42
- expect(radios[2]).toHaveAttribute("tabindex", "-1")
43
- })
44
-
45
- it("calls onValueChange when clicking an inactive option", async () => {
46
- const user = userEvent.setup()
47
- const onChange = vi.fn()
48
- render(
49
- <SegmentedControl value="all" onValueChange={onChange} options={options} />,
50
- )
51
-
52
- await user.click(screen.getByRole("radio", { name: "Team" }))
53
- expect(onChange).toHaveBeenCalledWith("team")
54
- })
55
-
56
- it("does not call onValueChange when clicking the already-active option", async () => {
57
- const user = userEvent.setup()
58
- const onChange = vi.fn()
59
- render(
60
- <SegmentedControl value="all" onValueChange={onChange} options={options} />,
61
- )
62
-
63
- await user.click(screen.getByRole("radio", { name: "All" }))
64
- expect(onChange).not.toHaveBeenCalled()
65
- })
66
-
67
- it("renders adornment when provided", () => {
68
- const withAdornment: SegmentedControlOption[] = [
69
- { value: "all", label: "All", adornment: <span data-testid="count">42</span> },
70
- { value: "personal", label: "Personal" },
71
- ]
72
- render(
73
- <SegmentedControl value="all" onValueChange={vi.fn()} options={withAdornment} />,
74
- )
75
- expect(screen.getByTestId("count")).toBeInTheDocument()
76
- expect(screen.getByText("42")).toBeInTheDocument()
77
- })
78
-
79
- // --- Keyboard navigation ---
80
-
81
- it("navigates to next option on ArrowRight", async () => {
82
- const user = userEvent.setup()
83
- const onChange = vi.fn()
84
- render(
85
- <SegmentedControl value="all" onValueChange={onChange} options={options} />,
86
- )
87
-
88
- screen.getByRole("radio", { name: "All" }).focus()
89
- await user.keyboard("{ArrowRight}")
90
-
91
- expect(onChange).toHaveBeenCalledWith("personal")
92
- })
93
-
94
- it("moves DOM focus to the target option on arrow key", async () => {
95
- const user = userEvent.setup()
96
- render(
97
- <SegmentedControl value="all" onValueChange={vi.fn()} options={options} />,
98
- )
99
-
100
- const personalRadio = screen.getByRole("radio", { name: "Personal" })
101
- screen.getByRole("radio", { name: "All" }).focus()
102
- await user.keyboard("{ArrowRight}")
103
-
104
- expect(document.activeElement).toBe(personalRadio)
105
- })
106
-
107
- it("navigates to previous option on ArrowLeft", async () => {
108
- const user = userEvent.setup()
109
- const onChange = vi.fn()
110
- render(
111
- <SegmentedControl value="personal" onValueChange={onChange} options={options} />,
112
- )
113
-
114
- screen.getByRole("radio", { name: "Personal" }).focus()
115
- await user.keyboard("{ArrowLeft}")
116
-
117
- expect(onChange).toHaveBeenCalledWith("all")
118
- })
119
-
120
- it("wraps around on ArrowRight from last option", async () => {
121
- const user = userEvent.setup()
122
- const onChange = vi.fn()
123
- render(
124
- <SegmentedControl value="team" onValueChange={onChange} options={options} />,
125
- )
126
-
127
- screen.getByRole("radio", { name: "Team" }).focus()
128
- await user.keyboard("{ArrowRight}")
129
-
130
- expect(onChange).toHaveBeenCalledWith("all")
131
- })
132
-
133
- it("wraps around on ArrowLeft from first option", async () => {
134
- const user = userEvent.setup()
135
- const onChange = vi.fn()
136
- render(
137
- <SegmentedControl value="all" onValueChange={onChange} options={options} />,
138
- )
139
-
140
- screen.getByRole("radio", { name: "All" }).focus()
141
- await user.keyboard("{ArrowLeft}")
142
-
143
- expect(onChange).toHaveBeenCalledWith("team")
144
- })
145
-
146
- it("navigates to first option on Home", async () => {
147
- const user = userEvent.setup()
148
- const onChange = vi.fn()
149
- render(
150
- <SegmentedControl value="team" onValueChange={onChange} options={options} />,
151
- )
152
-
153
- screen.getByRole("radio", { name: "Team" }).focus()
154
- await user.keyboard("{Home}")
155
-
156
- expect(onChange).toHaveBeenCalledWith("all")
157
- })
158
-
159
- it("navigates to last option on End", async () => {
160
- const user = userEvent.setup()
161
- const onChange = vi.fn()
162
- render(
163
- <SegmentedControl value="all" onValueChange={onChange} options={options} />,
164
- )
165
-
166
- screen.getByRole("radio", { name: "All" }).focus()
167
- await user.keyboard("{End}")
168
-
169
- expect(onChange).toHaveBeenCalledWith("team")
170
- })
171
-
172
- it("does not fire onValueChange when Home is pressed on first option", async () => {
173
- const user = userEvent.setup()
174
- const onChange = vi.fn()
175
- render(
176
- <SegmentedControl value="all" onValueChange={onChange} options={options} />,
177
- )
178
-
179
- screen.getByRole("radio", { name: "All" }).focus()
180
- await user.keyboard("{Home}")
181
-
182
- expect(onChange).not.toHaveBeenCalled()
183
- })
184
-
185
- it("does not fire onValueChange when End is pressed on last option", async () => {
186
- const user = userEvent.setup()
187
- const onChange = vi.fn()
188
- render(
189
- <SegmentedControl value="team" onValueChange={onChange} options={options} />,
190
- )
191
-
192
- screen.getByRole("radio", { name: "Team" }).focus()
193
- await user.keyboard("{End}")
194
-
195
- expect(onChange).not.toHaveBeenCalled()
196
- })
197
-
198
- // --- Edge cases ---
199
-
200
- it("does not crash on keyboard input when options array is empty", async () => {
201
- const user = userEvent.setup()
202
- const onChange = vi.fn()
203
- const { container } = render(
204
- <SegmentedControl value="all" onValueChange={onChange} options={[]} />,
205
- )
206
-
207
- const radiogroup = container.querySelector("[role='radiogroup']") as HTMLElement
208
- radiogroup.focus()
209
- await user.keyboard("{ArrowRight}")
210
- await user.keyboard("{Home}")
211
- await user.keyboard("{End}")
212
-
213
- expect(onChange).not.toHaveBeenCalled()
214
- })
215
-
216
- it("gives first option tabIndex 0 when value does not match any option", () => {
217
- render(
218
- <SegmentedControl value={"unknown" as string} onValueChange={vi.fn()} options={options} />,
219
- )
220
- const radios = screen.getAllByRole("radio")
221
- expect(radios[0]).toHaveAttribute("tabindex", "0")
222
- expect(radios[1]).toHaveAttribute("tabindex", "-1")
223
- expect(radios[2]).toHaveAttribute("tabindex", "-1")
224
- })
225
-
226
- it("navigates from first option when value does not match any option", async () => {
227
- const user = userEvent.setup()
228
- const onChange = vi.fn()
229
- render(
230
- <SegmentedControl value={"unknown" as string} onValueChange={onChange} options={options} />,
231
- )
232
-
233
- screen.getByRole("radio", { name: "All" }).focus()
234
- await user.keyboard("{ArrowRight}")
235
-
236
- expect(onChange).toHaveBeenCalledWith("personal")
237
- })
238
-
239
- it("supports ArrowDown as alias for ArrowRight", async () => {
240
- const user = userEvent.setup()
241
- const onChange = vi.fn()
242
- render(
243
- <SegmentedControl value="all" onValueChange={onChange} options={options} />,
244
- )
245
-
246
- screen.getByRole("radio", { name: "All" }).focus()
247
- await user.keyboard("{ArrowDown}")
248
-
249
- expect(onChange).toHaveBeenCalledWith("personal")
250
- })
251
-
252
- it("supports ArrowUp as alias for ArrowLeft", async () => {
253
- const user = userEvent.setup()
254
- const onChange = vi.fn()
255
- render(
256
- <SegmentedControl value="personal" onValueChange={onChange} options={options} />,
257
- )
258
-
259
- screen.getByRole("radio", { name: "Personal" }).focus()
260
- await user.keyboard("{ArrowUp}")
261
-
262
- expect(onChange).toHaveBeenCalledWith("all")
263
- })
264
-
265
- // --- Variants ---
266
-
267
- it("applies row variant classes by default", () => {
268
- render(
269
- <SegmentedControl value="all" onValueChange={vi.fn()} options={options} />,
270
- )
271
- const radiogroup = screen.getByRole("radiogroup")
272
- expect(radiogroup.className).toContain("flex-wrap")
273
- expect(radiogroup.className).toContain("rounded-lg")
274
- // The row track is one quiet well: the hairline border, not a field's strong border.
275
- expect(radiogroup.className).toContain("bg-[var(--well)]")
276
- expect(radiogroup.className).toContain("border-border")
277
- expect(radiogroup.className).not.toContain("border-[var(--field-border)]")
278
- })
279
-
280
- it("applies tabs variant classes with nowrap", () => {
281
- render(
282
- <SegmentedControl
283
- value="all"
284
- onValueChange={vi.fn()}
285
- options={options}
286
- variant="tabs"
287
- />,
288
- )
289
- const radiogroup = screen.getByRole("radiogroup")
290
- expect(radiogroup.className).toContain("flex-nowrap")
291
- expect(radiogroup.className).toContain("overflow-x-auto")
292
- expect(radiogroup.className).toContain("border-b")
293
- expect(radiogroup.className).not.toContain("bg-[var(--well)]")
294
- })
295
-
296
- it("passes aria-label to the radiogroup", () => {
297
- render(
298
- <SegmentedControl
299
- value="all"
300
- onValueChange={vi.fn()}
301
- options={options}
302
- aria-label="Scope filter"
303
- />,
304
- )
305
- expect(screen.getByRole("radiogroup")).toHaveAttribute("aria-label", "Scope filter")
306
- })
307
-
308
- it("passes aria-labelledby to the radiogroup", () => {
309
- render(
310
- <SegmentedControl
311
- value="all"
312
- onValueChange={vi.fn()}
313
- options={options}
314
- aria-labelledby="heading-id"
315
- />,
316
- )
317
- expect(screen.getByRole("radiogroup")).toHaveAttribute("aria-labelledby", "heading-id")
318
- })
319
-
320
- it("passes id to the radiogroup", () => {
321
- render(
322
- <SegmentedControl
323
- value="all"
324
- onValueChange={vi.fn()}
325
- options={options}
326
- id="scope-control"
327
- />,
328
- )
329
- expect(screen.getByRole("radiogroup")).toHaveAttribute("id", "scope-control")
330
- })
331
- })
332
-
333
- describe("SegmentedControl test ids", () => {
334
- it("renders option and group test ids for end-to-end selectors", () => {
335
- render(
336
- <SegmentedControl
337
- aria-label="Workbench view"
338
- data-testid="workbench-tabs"
339
- value="code"
340
- onValueChange={vi.fn()}
341
- options={[
342
- { value: "code", label: "Code", testId: "workbench-tab-code" },
343
- { value: "preview", label: "Preview", testId: "workbench-tab-preview" },
344
- ]}
345
- />,
346
- )
347
- expect(screen.getByTestId("workbench-tabs")).toHaveAttribute("role", "radiogroup")
348
- expect(screen.getByTestId("workbench-tab-preview")).toHaveAttribute("aria-checked", "false")
349
- expect(screen.getByTestId("workbench-tab-code")).toHaveAttribute("aria-checked", "true")
350
- })
351
- })
352
-
353
- describe("SegmentedControl tabs on a narrow strip", () => {
354
- // jsdom has no layout. Model a 250px strip holding six 100px tabs, so the
355
- // strip scrolls and some tabs sit past its edges.
356
- const STRIP = 250
357
- const TAB = 100
358
- const scroll = new WeakMap<Element, number>()
359
- const overrides: Array<[object, string, PropertyDescriptor | undefined]> = []
360
- const define = (proto: object, key: string, descriptor: PropertyDescriptor) => {
361
- overrides.push([proto, key, Object.getOwnPropertyDescriptor(proto, key)])
362
- Object.defineProperty(proto, key, { configurable: true, ...descriptor })
363
- }
364
- const isStrip = (el: Element) => el.getAttribute("role") === "radiogroup"
365
- // Options sit side by side; the edge chevrons take no room in the row.
366
- const index = (el: Element) => Array.from(el.parentElement?.querySelectorAll(':scope > [role="radio"]') ?? []).indexOf(el)
367
-
368
- beforeEach(() => {
369
- define(HTMLElement.prototype, "offsetLeft", { get(this: HTMLElement) { return isStrip(this) ? 0 : index(this) * TAB } })
370
- define(HTMLElement.prototype, "offsetWidth", { get(this: HTMLElement) { return isStrip(this) ? STRIP : TAB } })
371
- define(Element.prototype, "clientWidth", { get(this: Element) { return isStrip(this) ? STRIP : TAB } })
372
- define(Element.prototype, "scrollWidth", { get(this: Element) { return isStrip(this) ? this.querySelectorAll(':scope > [role="radio"]').length * TAB : TAB } })
373
- define(Element.prototype, "scrollLeft", {
374
- get(this: Element) { return scroll.get(this) ?? 0 },
375
- set(this: Element, v: number) { scroll.set(this, v) },
376
- })
377
- })
378
- afterEach(() => {
379
- for (const [proto, key, original] of overrides.reverse()) {
380
- if (original) Object.defineProperty(proto, key, original)
381
- else delete (proto as Record<string, unknown>)[key]
382
- }
383
- overrides.length = 0
384
- })
385
-
386
- const work: SegmentedControlOption[] = ["Today", "Messages", "Tasks", "Operations", "Payments", "People"].map(
387
- (label) => ({ value: label.toLowerCase(), label }),
388
- )
389
-
390
- it("scrolls the strip, not the page, so the selected tab is in view", () => {
391
- const { rerender } = render(
392
- <SegmentedControl aria-label="Work" variant="tabs" value="payments" onValueChange={vi.fn()} options={work} />,
393
- )
394
- const strip = screen.getByRole("radiogroup")
395
- // Payments spans 400-500px: its right edge clears the 32px chevron, plus a 4px gap, in the 250px view.
396
- expect(strip.scrollLeft).toBe(500 - STRIP + 32 + 4)
397
-
398
- rerender(<SegmentedControl aria-label="Work" variant="tabs" value="today" onValueChange={vi.fn()} options={work} />)
399
- expect(strip.scrollLeft).toBe(0)
400
- })
401
-
402
- it("shows a chevron at the edge that has more tabs past it", () => {
403
- const { rerender } = render(
404
- <SegmentedControl aria-label="Work" variant="tabs" value="today" onValueChange={vi.fn()} options={work} />,
405
- )
406
- const strip = screen.getByRole("radiogroup")
407
- expect(strip).toHaveAttribute("data-overflow-end", "true")
408
- expect(strip).not.toHaveAttribute("data-overflow-start")
409
- expect(strip.querySelector('[data-scroll-cue="end"]')).not.toBeNull()
410
- expect(strip.querySelector('[data-scroll-cue="start"]')).toBeNull()
411
-
412
- rerender(<SegmentedControl aria-label="Work" variant="tabs" value="people" onValueChange={vi.fn()} options={work} />)
413
- expect(strip).toHaveAttribute("data-overflow-start", "true")
414
- expect(strip).not.toHaveAttribute("data-overflow-end")
415
- expect(strip.querySelector('[data-scroll-cue="start"]')).not.toBeNull()
416
- expect(strip.querySelector('[data-scroll-cue="end"]')).toBeNull()
417
- })
418
-
419
- it("keeps the chevrons out of the radio group and the tab order", () => {
420
- render(<SegmentedControl aria-label="Work" variant="tabs" value="today" onValueChange={vi.fn()} options={work} />)
421
- expect(screen.getAllByRole("radio")).toHaveLength(work.length)
422
- const cue = screen.getByRole("radiogroup").querySelector('[data-scroll-cue="end"]')
423
- expect(cue).toHaveAttribute("aria-hidden", "true")
424
- expect(cue).toHaveAttribute("tabindex", "-1")
425
- })
426
-
427
- it("leaves a strip that fits without chevrons", () => {
428
- render(<SegmentedControl aria-label="Work" variant="tabs" value="all" onValueChange={vi.fn()} options={options.slice(0, 2)} />)
429
- const strip = screen.getByRole("radiogroup")
430
- expect(strip).not.toHaveAttribute("data-overflow-start")
431
- expect(strip).not.toHaveAttribute("data-overflow-end")
432
- expect(strip.querySelector("[data-scroll-cue]")).toBeNull()
433
- })
434
- })
435
-
436
- describe("SegmentedControl stable labels and touch targets", () => {
437
- it.each(["row", "tabs"] as const)("keeps one label weight whichever %s option is selected", (variant) => {
438
- render(<SegmentedControl aria-label="Scope" variant={variant} value="personal" onValueChange={vi.fn()} options={options} />)
439
- for (const radio of screen.getAllByRole("radio")) {
440
- expect(radio.className).toContain("font-medium")
441
- expect(radio.className).not.toMatch(/font-(semibold|bold)/)
442
- }
443
- })
444
-
445
- it("gives each tab a 44px hit height on a coarse pointer", () => {
446
- render(<SegmentedControl aria-label="Scope" variant="tabs" value="all" onValueChange={vi.fn()} options={options} />)
447
- for (const radio of screen.getAllByRole("radio")) {
448
- expect(radio.className).toContain("pointer-coarse:min-h-11")
449
- }
450
- })
451
- })
@@ -1,72 +0,0 @@
1
- import { createRef } from "react";
2
- import { render, screen, within } from "@testing-library/react";
3
- import userEvent from "@testing-library/user-event";
4
- import { describe, expect, it, vi } from "vitest";
5
- import { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, type TableProps } from "./table";
6
-
7
- function UsageTable(props: TableProps) {
8
- return <Table {...props}>
9
- <TableCaption>Usage</TableCaption>
10
- <TableHeader><TableRow><TableHead scope="col" id="resource">Resource</TableHead><TableHead scope="col" id="count">Count</TableHead></TableRow></TableHeader>
11
- <TableBody><TableRow><TableHead scope="row" id="builds">Builds</TableHead><TableCell headers="builds count">3</TableCell></TableRow></TableBody>
12
- <TableFooter><TableRow><TableCell colSpan={2}>One resource</TableCell></TableRow></TableFooter>
13
- </Table>;
14
- }
15
-
16
- describe("Table", () => {
17
- it("preserves the default wrapper without imposing an unnamed region or tab stop", () => {
18
- render(<UsageTable />);
19
- const table = screen.getByRole("table", { name: "Usage" });
20
- expect(table.parentElement?.tagName).toBe("DIV");
21
- expect(table.parentElement).toHaveClass("relative", "w-full", "overflow-auto");
22
- expect(table.parentElement).not.toHaveAttribute("role");
23
- expect(table.parentElement).not.toHaveAttribute("tabindex");
24
- });
25
-
26
- it("retains native caption, table sections, headers, scope and spans", () => {
27
- render(<UsageTable />);
28
- const table = screen.getByRole("table", { name: "Usage" });
29
- expect(Array.from(table.children).map((node) => node.tagName)).toEqual(["CAPTION", "THEAD", "TBODY", "TFOOT"]);
30
- expect(screen.getByRole("columnheader", { name: "Resource" })).toHaveAttribute("scope", "col");
31
- expect(screen.getByRole("rowheader", { name: "Builds" })).toHaveAttribute("scope", "row");
32
- expect(screen.getByRole("cell", { name: "3" })).toHaveAttribute("headers", "builds count");
33
- expect(screen.getByRole("cell", { name: "One resource" })).toHaveAttribute("colspan", "2");
34
- expect(screen.queryByRole("grid")).toBeNull();
35
- });
36
-
37
- it("allows a caller-named keyboard-focusable wrapper separately from table attributes", async () => {
38
- const user = userEvent.setup();
39
- render(<><h2 id="usage-title">Resource usage</h2><UsageTable id="usage-table" className="min-w-[40rem]" wrapperProps={{ role: "region", "aria-labelledby": "usage-title", tabIndex: 0, className: "max-h-80" }} /></>);
40
- const region = screen.getByRole("region", { name: "Resource usage" });
41
- const table = within(region).getByRole("table", { name: "Usage" });
42
- expect(region).toHaveClass("max-h-80", "overflow-auto");
43
- expect(table).toHaveAttribute("id", "usage-table");
44
- expect(table).not.toHaveAttribute("tabindex");
45
- expect(table).not.toHaveClass("max-h-80");
46
- await user.tab();
47
- expect(region).toHaveFocus();
48
- });
49
-
50
- it("returns a bare table for a caller-owned scroll region, without leaking wrapper props", () => {
51
- render(<div role="region" aria-label="Outer" className="overflow-auto">
52
- <UsageTable wrapper={false} wrapperProps={{ id: "unused-wrapper" }} />
53
- </div>);
54
- const outer = screen.getByRole("region", { name: "Outer" });
55
- const table = screen.getByRole("table", { name: "Usage" });
56
- expect(outer.firstElementChild).toBe(table);
57
- expect(outer.querySelectorAll(".overflow-auto")).toHaveLength(0);
58
- expect(table).not.toHaveAttribute("wrapper");
59
- expect(table).not.toHaveAttribute("wrapperProps");
60
- expect(table).not.toHaveAttribute("id", "unused-wrapper");
61
- });
62
-
63
- it("keeps the public ref and native attributes/handlers on the table", async () => {
64
- const ref = createRef<HTMLTableElement>();
65
- const onClick = vi.fn();
66
- render(<Table ref={ref} cellPadding={0} onClick={onClick} aria-label="Details"><TableBody><TableRow><TableCell>Open</TableCell></TableRow></TableBody></Table>);
67
- expect(ref.current).toBe(screen.getByRole("table", { name: "Details" }));
68
- expect(ref.current).toHaveAttribute("cellpadding", "0");
69
- await userEvent.setup().click(screen.getByRole("cell", { name: "Open" }));
70
- expect(onClick).toHaveBeenCalledTimes(1);
71
- });
72
- });