@ksp-gonogo/ui-kit 0.1.0 → 0.2.0-rc.12

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 (133) hide show
  1. package/LICENSE +21 -596
  2. package/README.md +151 -0
  3. package/dist/chunk-G3PMV62Z.js +36 -0
  4. package/dist/chunk-G3PMV62Z.js.map +1 -0
  5. package/dist/chunk-PG6WD6AT.js +8611 -0
  6. package/dist/chunk-PG6WD6AT.js.map +1 -0
  7. package/dist/chunk-SHAX2WJJ.js +18 -0
  8. package/dist/chunk-SHAX2WJJ.js.map +1 -0
  9. package/dist/compat.json +3 -0
  10. package/dist/dist-JSEJWZKX.js +546 -0
  11. package/dist/dist-JSEJWZKX.js.map +1 -0
  12. package/dist/gridUnits.d.ts +34 -0
  13. package/dist/gridUnits.js +14 -0
  14. package/dist/gridUnits.js.map +1 -0
  15. package/dist/guards.d.ts +110 -0
  16. package/dist/guards.js +171 -0
  17. package/dist/guards.js.map +1 -0
  18. package/dist/index.d.ts +9349 -25
  19. package/dist/index.js +8098 -28
  20. package/dist/index.js.map +1 -1
  21. package/dist/magic-string.es-J7BYFTTJ.js +1309 -0
  22. package/dist/magic-string.es-J7BYFTTJ.js.map +1 -0
  23. package/dist/testing.d.ts +471 -0
  24. package/dist/testing.js +16747 -0
  25. package/dist/testing.js.map +1 -0
  26. package/dist/tokens.css +1063 -29
  27. package/dist/units-Cl-8ZdJc.d.ts +270 -0
  28. package/package.json +57 -15
  29. package/dist/ActionButton.d.ts +0 -18
  30. package/dist/ActionButton.d.ts.map +0 -1
  31. package/dist/ActionButton.js +0 -56
  32. package/dist/ActionButton.js.map +0 -1
  33. package/dist/Badge.d.ts +0 -15
  34. package/dist/Badge.d.ts.map +0 -1
  35. package/dist/Badge.js +0 -60
  36. package/dist/Badge.js.map +0 -1
  37. package/dist/Box.d.ts +0 -19
  38. package/dist/Box.d.ts.map +0 -1
  39. package/dist/Box.js +0 -27
  40. package/dist/Box.js.map +0 -1
  41. package/dist/Card.d.ts +0 -11
  42. package/dist/Card.d.ts.map +0 -1
  43. package/dist/Card.js +0 -13
  44. package/dist/Card.js.map +0 -1
  45. package/dist/Cluster.d.ts +0 -18
  46. package/dist/Cluster.d.ts.map +0 -1
  47. package/dist/Cluster.js +0 -24
  48. package/dist/Cluster.js.map +0 -1
  49. package/dist/EmptyState.d.ts +0 -16
  50. package/dist/EmptyState.d.ts.map +0 -1
  51. package/dist/EmptyState.js +0 -37
  52. package/dist/EmptyState.js.map +0 -1
  53. package/dist/GonogoTokens.d.ts +0 -16
  54. package/dist/GonogoTokens.d.ts.map +0 -1
  55. package/dist/GonogoTokens.js +0 -108
  56. package/dist/GonogoTokens.js.map +0 -1
  57. package/dist/Grid.d.ts +0 -25
  58. package/dist/Grid.d.ts.map +0 -1
  59. package/dist/Grid.js +0 -23
  60. package/dist/Grid.js.map +0 -1
  61. package/dist/Inline.d.ts +0 -20
  62. package/dist/Inline.d.ts.map +0 -1
  63. package/dist/Inline.js +0 -17
  64. package/dist/Inline.js.map +0 -1
  65. package/dist/Panel.d.ts +0 -14
  66. package/dist/Panel.d.ts.map +0 -1
  67. package/dist/Panel.js +0 -145
  68. package/dist/Panel.js.map +0 -1
  69. package/dist/ProgressBar.d.ts +0 -15
  70. package/dist/ProgressBar.d.ts.map +0 -1
  71. package/dist/ProgressBar.js +0 -31
  72. package/dist/ProgressBar.js.map +0 -1
  73. package/dist/Readout.d.ts +0 -35
  74. package/dist/Readout.d.ts.map +0 -1
  75. package/dist/Readout.js +0 -114
  76. package/dist/Readout.js.map +0 -1
  77. package/dist/Row.d.ts +0 -22
  78. package/dist/Row.d.ts.map +0 -1
  79. package/dist/Row.js +0 -32
  80. package/dist/Row.js.map +0 -1
  81. package/dist/Section.d.ts +0 -15
  82. package/dist/Section.d.ts.map +0 -1
  83. package/dist/Section.js +0 -21
  84. package/dist/Section.js.map +0 -1
  85. package/dist/Spinner.d.ts +0 -18
  86. package/dist/Spinner.d.ts.map +0 -1
  87. package/dist/Spinner.js +0 -22
  88. package/dist/Spinner.js.map +0 -1
  89. package/dist/Stack.d.ts +0 -15
  90. package/dist/Stack.d.ts.map +0 -1
  91. package/dist/Stack.js +0 -17
  92. package/dist/Stack.js.map +0 -1
  93. package/dist/StatusIndicator.d.ts +0 -27
  94. package/dist/StatusIndicator.d.ts.map +0 -1
  95. package/dist/StatusIndicator.js +0 -78
  96. package/dist/StatusIndicator.js.map +0 -1
  97. package/dist/Truncate.d.ts +0 -7
  98. package/dist/Truncate.d.ts.map +0 -1
  99. package/dist/Truncate.js +0 -14
  100. package/dist/Truncate.js.map +0 -1
  101. package/dist/Value.d.ts +0 -24
  102. package/dist/Value.d.ts.map +0 -1
  103. package/dist/Value.js +0 -42
  104. package/dist/Value.js.map +0 -1
  105. package/dist/WidgetHeader.d.ts +0 -16
  106. package/dist/WidgetHeader.d.ts.map +0 -1
  107. package/dist/WidgetHeader.js +0 -40
  108. package/dist/WidgetHeader.js.map +0 -1
  109. package/dist/defaultDarkTheme.d.ts +0 -16
  110. package/dist/defaultDarkTheme.d.ts.map +0 -1
  111. package/dist/defaultDarkTheme.js +0 -96
  112. package/dist/defaultDarkTheme.js.map +0 -1
  113. package/dist/format.d.ts +0 -11
  114. package/dist/format.d.ts.map +0 -1
  115. package/dist/format.js +0 -12
  116. package/dist/format.js.map +0 -1
  117. package/dist/index.d.ts.map +0 -1
  118. package/dist/science/ScienceExperimentRow.d.ts +0 -43
  119. package/dist/science/ScienceExperimentRow.d.ts.map +0 -1
  120. package/dist/science/ScienceExperimentRow.js +0 -61
  121. package/dist/science/ScienceExperimentRow.js.map +0 -1
  122. package/dist/test/axe.d.ts +0 -3
  123. package/dist/test/axe.d.ts.map +0 -1
  124. package/dist/test/axe.js +0 -5
  125. package/dist/test/axe.js.map +0 -1
  126. package/dist/test/setup.d.ts +0 -2
  127. package/dist/test/setup.d.ts.map +0 -1
  128. package/dist/test/setup.js +0 -12
  129. package/dist/test/setup.js.map +0 -1
  130. package/dist/theme.d.ts +0 -116
  131. package/dist/theme.d.ts.map +0 -1
  132. package/dist/theme.js +0 -15
  133. package/dist/theme.js.map +0 -1
@@ -0,0 +1,471 @@
1
+ import { F as FormatQuantityOptions } from './units-Cl-8ZdJc.js';
2
+ import { RenderHookOptions, RenderHookResult, RenderOptions, RenderResult } from '@ksp-gonogo/sitrep-sdk/testing';
3
+ import { ReactElement, JSXElementConstructor, ReactNode } from 'react';
4
+ import * as react_jsx_runtime from 'react/jsx-runtime';
5
+ import { ComponentDefinition } from '@ksp-gonogo/sitrep-sdk';
6
+
7
+ /**
8
+ * A checkered-flag test for framed pictures: paint a pattern of square cells
9
+ * into a box and ask what a cell looks like on screen, with no pixels involved.
10
+ *
11
+ * Three different faults make a framed picture look wrong, and they are easy to
12
+ * mistake for each other by eye:
13
+ *
14
+ * - **distortion**: the picture is scaled by different amounts per axis, so a
15
+ * square cell renders as a rectangle
16
+ * - **cropping**: cells stay square, but some of them are painted outside the
17
+ * box and lost off its edge
18
+ * - **field of view**: cells stay square and nothing is cut off, but the source
19
+ * was rendered showing a different grid than the one intended, so more or
20
+ * fewer cells are visible
21
+ *
22
+ * jsdom lays nothing out, so this is geometry rather than measurement: the
23
+ * caller supplies the box it gave the picture, and the fit comes from the
24
+ * element (see {@link fitOf}) or is stated.
25
+ */
26
+ /**
27
+ * A pattern of square cells, `cols` wide and `rows` high. Either may be fractional.
28
+ *
29
+ * @category Testing
30
+ */
31
+ interface CheckerGrid {
32
+ cols: number;
33
+ rows: number;
34
+ }
35
+ /**
36
+ * The `object-fit` values that scale a picture to its box.
37
+ *
38
+ * @category Testing
39
+ */
40
+ type CheckerFit = "fill" | "contain" | "cover";
41
+ /**
42
+ * How a {@link CheckerGrid} lands in a box under a given fit, in CSS px.
43
+ *
44
+ * @category Testing
45
+ */
46
+ interface CheckerPaint {
47
+ /** The grid the source picture carries. */
48
+ grid: CheckerGrid;
49
+ fit: CheckerFit;
50
+ box: {
51
+ width: number;
52
+ height: number;
53
+ };
54
+ /** The painted picture's own size, which exceeds `box` on a cropped axis and falls short of it on a letterboxed one. */
55
+ painted: {
56
+ width: number;
57
+ height: number;
58
+ };
59
+ cellWidth: number;
60
+ cellHeight: number;
61
+ /** Cells visible inside the box per axis, never more than the grid holds. */
62
+ visible: CheckerGrid;
63
+ }
64
+ /**
65
+ * One of the three ways a framed picture goes wrong. `distortion` renders a
66
+ * square cell as a rectangle; `cropping` keeps cells square and loses some
67
+ * off the box's edge; `field-of-view` keeps cells square and whole but shows a
68
+ * different grid from the one intended.
69
+ *
70
+ * @category Testing
71
+ */
72
+ type FramingFault = "distortion" | "cropping" | "field-of-view";
73
+ /**
74
+ * Relative slack on every comparison, 1%. A box floored to whole pixels to fit a
75
+ * feed's aspect sits a fraction of a percent off it, which is not a fault; a
76
+ * real distortion is far larger.
77
+ *
78
+ * @category Testing
79
+ */
80
+ declare const CHECKER_TOLERANCE = 0.01;
81
+ /**
82
+ * Paint `grid` into `box` under `fit`, centred as `object-position` defaults.
83
+ * The source's pixel size never matters: every supported fit scales it to the
84
+ * box, so only its shape does, and that is the grid's.
85
+ *
86
+ * @category Testing
87
+ */
88
+ declare function paintChecker(grid: CheckerGrid, box: {
89
+ width: number;
90
+ height: number;
91
+ }, fit: CheckerFit): CheckerPaint;
92
+ /**
93
+ * Every {@link FramingFault} `paint` shows.
94
+ * `intended` is the grid the picture was meant to carry (a camera's own
95
+ * aspect, say); without it a field-of-view change cannot be told from a
96
+ * faithful picture, so it is never reported.
97
+ *
98
+ * @category Testing
99
+ */
100
+ declare function framingFaults(paint: CheckerPaint, intended?: CheckerGrid): FramingFault[];
101
+ /**
102
+ * The fit an element paints its picture with. An `<svg>` takes it from its
103
+ * `preserveAspectRatio` (`none` fills, `slice` covers, anything else
104
+ * contains); every other element from its `object-fit`, which is `fill` unless
105
+ * set, and `contain` on a `<video>`, whose user-agent style says so. Throws for
106
+ * an `<svg>` with no `viewBox`, which does not scale its content at all, and
107
+ * for `none` or `scale-down`, which depend on a pixel size the checker does not
108
+ * have.
109
+ *
110
+ * @category Testing
111
+ */
112
+ declare function fitOf(element: Element): CheckerFit;
113
+
114
+ /**
115
+ * Asserts that a component's live region is in the document, empty, before its
116
+ * first message, and that the same element is still there holding the message
117
+ * afterwards.
118
+ *
119
+ * Assistive tech announces a CHANGE to a region it already knows. A region
120
+ * inserted together with its words is often never announced, and a test that
121
+ * only looks at the final DOM cannot see that: the words are there either way.
122
+ * Call this with `container` rendered in the state that has nothing to say, and
123
+ * `reveal` doing whatever makes the component say something (a rerender, a
124
+ * store write, a click). Rendering the region only once it has content, as
125
+ * `{message && <span role="status">{message}</span>}` does, fails the first
126
+ * check; keeping the region mounted for as long as the thing it reports on is
127
+ * on screen passes.
128
+ *
129
+ * `region` narrows which live region is under test when a component carries
130
+ * several, as a selector. It defaults to every `aria-live` element and every
131
+ * `status`, `log` and `alert` role under `container`.
132
+ *
133
+ * @example
134
+ * ```tsx
135
+ * const { container, rerender } = render(<Outcome entries={[]} />);
136
+ * await expectLiveRegionPrimed(container, () =>
137
+ * rerender(<Outcome entries={[sent]} />),
138
+ * );
139
+ * ```
140
+ *
141
+ * @category Testing
142
+ */
143
+ declare function expectLiveRegionPrimed(container: HTMLElement, reveal: () => void | Promise<void>, region?: string): Promise<HTMLElement[]>;
144
+
145
+ /**
146
+ * The accessibility smoke assertion for a widget test: runs `axe` over
147
+ * `container` (an element or a selector) and fails on any violation.
148
+ *
149
+ * The whole check runs inside `act`, so a widget with a clock or a
150
+ * subscription that keeps updating while `axe` walks the DOM raises no act
151
+ * warning. Call this rather than `axe` directly. Needs `jest-axe` installed as
152
+ * a dev dependency of your own; it is loaded on first use.
153
+ *
154
+ * @example
155
+ * ```tsx
156
+ * import { expectNoA11yViolations, renderWidget } from "@ksp-gonogo/ui-kit/testing";
157
+ *
158
+ * it("has no accessibility violations", async () => {
159
+ * const { container } = renderWidget("my-gauge");
160
+ * await expectNoA11yViolations(container);
161
+ * });
162
+ * ```
163
+ *
164
+ * @category Testing
165
+ */
166
+ declare function expectNoA11yViolations(container: Element | string): Promise<void>;
167
+
168
+ /**
169
+ * Why a drawn held mark fails to tell an operator what it means:
170
+ * `silent` where nothing a screen reader hears names its grade, `no-hover`
171
+ * where the hover does not, and `no-as-of` where the hover leaves out the
172
+ * instant the spoken words give.
173
+ *
174
+ * @category Testing
175
+ */
176
+ type HeldMarkFault = "silent" | "no-hover" | "no-as-of";
177
+ /**
178
+ * One held mark that does not announce itself, with a short description of
179
+ * where it was drawn.
180
+ *
181
+ * @category Testing
182
+ */
183
+ interface UnannouncedHeldMark {
184
+ mark: Element;
185
+ fault: HeldMarkFault;
186
+ where: string;
187
+ }
188
+ /**
189
+ * Every held mark (`[data-held-mark]`) under `root` that does not say what it
190
+ * means: its grade's word to a screen reader and on hover, with the hover
191
+ * naming the same "as of" instant the spoken words do.
192
+ *
193
+ * Keyed on the mark rather than the figure, so a mark drawn by hand outside any
194
+ * kit primitive is still asked about.
195
+ *
196
+ * @category Testing
197
+ */
198
+ declare function unannouncedHeldMarks(root?: ParentNode): UnannouncedHeldMark[];
199
+
200
+ /**
201
+ * The sdk's themed `render` (from `@ksp-gonogo/sitrep-sdk/testing`), with a
202
+ * command rail mounted above it, for a component under test that dispatches
203
+ * through `useCommand` but is not a registered widget. A dev build throws on a
204
+ * dispatch with no rail mounted. A caller's own `wrapper` nests inside the
205
+ * rail. For a registered widget use {@link renderWidget}, which carries one.
206
+ *
207
+ * @category Testing
208
+ */
209
+ declare function renderWithRail(ui: ReactElement, options?: RenderOptions): RenderResult;
210
+ /**
211
+ * The sdk's `renderHook`, with the same command rail as {@link renderWithRail},
212
+ * for a hook under test that calls `useCommand`.
213
+ *
214
+ * @category Testing
215
+ */
216
+ declare function renderHookWithRail<Result, Props>(callback: (initialProps: Props) => Result, options?: RenderHookOptions<Props>): RenderHookResult<Result, Props>;
217
+
218
+ /**
219
+ * Options for {@link renderWidget}.
220
+ *
221
+ * @category Testing
222
+ */
223
+ interface RenderWidgetOptions {
224
+ /**
225
+ * The dashboard instance id, what the widget sees as
226
+ * `DashboardItemContext`'s `instanceId` and as its own `id` prop.
227
+ */
228
+ instanceId?: string;
229
+ /** Per-instance config, the widget's `config` prop. */
230
+ config?: Record<string, unknown>;
231
+ /** Grid units, the `w`/`h` props a responsive widget reads. */
232
+ w?: number;
233
+ /** Grid units, the widget's `h` prop. */
234
+ h?: number;
235
+ /** The widget's `onConfigChange`. Defaults to a no-op. */
236
+ onConfigChange?: (config: Record<string, unknown>) => void;
237
+ /**
238
+ * Mounted outside the dashboard stack, where the app mounts the equivalent:
239
+ * a stream fixture's `Provider` belongs above the widget host, because the
240
+ * host's own hooks read telemetry through it.
241
+ */
242
+ wrapper?: JSXElementConstructor<{
243
+ children: ReactNode;
244
+ }>;
245
+ }
246
+ /**
247
+ * The provider stack the dashboard puts around every widget, in the same
248
+ * order, for a registered widget id: the command rail, the panel status store,
249
+ * the widget's instance and metadata, augment settings and contributions.
250
+ * {@link renderWidget} uses it; use it directly to wrap a tree of your own.
251
+ *
252
+ * It omits three things the dashboard also wraps a widget in, because each
253
+ * would make a test quieter rather than truer:
254
+ *
255
+ * - an error boundary, which turns a throw into a fallback UI. In a test a
256
+ * throw should reach the test
257
+ * - the requires guard, which hides a widget whose `requires` domain is absent.
258
+ * A test asserting on a widget's contents wants the widget, not the gate;
259
+ * test the gate by asserting on `def.requires` directly
260
+ * - the alarm-status bridge, which folds firing alarms into the status store.
261
+ * It needs an alarm host, which is app-side, and it renders nothing
262
+ *
263
+ * @category Testing
264
+ */
265
+ declare function WidgetHost({ widgetId, instanceId, children, }: {
266
+ /** The registered widget id, which the stack is built from. */
267
+ widgetId: string;
268
+ /** Defaults to `<widgetId>-test`, matching `renderWidget`. */
269
+ instanceId?: string;
270
+ children: ReactNode;
271
+ }): react_jsx_runtime.JSX.Element;
272
+ /**
273
+ * The same stack as {@link WidgetHost}, given the component definition rather
274
+ * than an id to look one up by: for a harness previewing an augment against a
275
+ * synthetic host definition.
276
+ *
277
+ * @category Testing
278
+ */
279
+ declare function WidgetHostFor({ def, instanceId, children, }: {
280
+ def: ComponentDefinition;
281
+ instanceId: string;
282
+ children: ReactNode;
283
+ }): react_jsx_runtime.JSX.Element;
284
+ /**
285
+ * Render a widget the way the dashboard does: by its registered id, inside the
286
+ * provider stack the dashboard puts around one ({@link WidgetHost}). A bare
287
+ * `render` omits that stack, so for instance a `Panel` status badge would never
288
+ * appear. The widget must already be registered, which a widget's module does
289
+ * when it is loaded by calling the sdk's `registerComponent`, so import that
290
+ * module first; an unknown id throws and lists the registered ones.
291
+ *
292
+ * Returns the `RenderResult` of `@ksp-gonogo/sitrep-sdk/testing`'s `render`. A
293
+ * widget with unusual needs drops to `render` and builds its own scaffolding.
294
+ *
295
+ * @example
296
+ * ```tsx
297
+ * import { screen } from "@ksp-gonogo/sitrep-sdk/testing";
298
+ * import { renderWidget } from "@ksp-gonogo/ui-kit/testing";
299
+ * import { expect, it } from "vitest";
300
+ * import "./MyGauge"; // registers "my-gauge" on load
301
+ *
302
+ * it("shows its label", () => {
303
+ * renderWidget("my-gauge", { config: { label: "Apoapsis" }, w: 4, h: 3 });
304
+ * expect(screen.getByText("Apoapsis")).toBeTruthy();
305
+ * });
306
+ * ```
307
+ *
308
+ * @category Testing
309
+ */
310
+ declare function renderWidget(widgetId: string, options?: RenderWidgetOptions): RenderResult;
311
+
312
+ /**
313
+ * Testing helpers for the readouts this kit renders, published as
314
+ * `@ksp-gonogo/ui-kit/testing` (separate from the root so a runtime bundle
315
+ * never pulls testing code in). `<Unit>` splits a readout into a number, a
316
+ * symbol and a hidden spoken word, so `getByText("12.4 km")` finds nothing;
317
+ * these read it back.
318
+ *
319
+ * `renderWidget` mounts a widget inside the dashboard's provider stack, which is
320
+ * made of this package's providers. `renderWithRail`/`renderHookWithRail` are
321
+ * the bare equivalent, the sdk's themed `render`/`renderHook` with a command
322
+ * rail mounted above it, for a component or hook under test that is not a
323
+ * registered widget but still dispatches through `useCommand`. Named apart
324
+ * from the sdk's own `render`/`renderHook` rather than shadowing them, since
325
+ * both packages are published. Neither re-exports the sdk's testing entry
326
+ * wholesale: an Uplink's setup takes a host from the sdk and a provider stack
327
+ * from here.
328
+ */
329
+ /**
330
+ * What a sighted reader sees, with the screen-reader words removed:
331
+ * `textContent` of a `<Unit>` reads "12.4 km kilometres", this reads
332
+ * "12.4 km". Assert on `textContent`, or `getByText("kilometres")`, when the
333
+ * announcement is under test.
334
+ *
335
+ * Defaults to `document.body`. The thin space between a number and its symbol
336
+ * is normalised to an ordinary space.
337
+ *
338
+ * @category Testing
339
+ */
340
+ declare function visibleText(container?: HTMLElement): string;
341
+ /** The shape a matcher reports back to its test framework. */
342
+ interface MatcherResult {
343
+ pass: boolean;
344
+ message: () => string;
345
+ }
346
+ /**
347
+ * `expect(container).toShowQuantity(value("m", 12400))`.
348
+ *
349
+ * Asserts that a quantity is on screen without naming how it is spelled: it
350
+ * formats through `writeQuantity`, the same ladder `<Unit>` renders with, so it
351
+ * survives a change to where metres hand off to kilometres. For the same
352
+ * reason it cannot catch a formatting bug. To pin the exact spelling, assert
353
+ * the literal:
354
+ *
355
+ * ```ts
356
+ * expect(visibleText()).toContain("12.4 km");
357
+ * ```
358
+ *
359
+ * Register it once, in a setup file:
360
+ *
361
+ * ```ts
362
+ * import { expect } from "vitest";
363
+ * import { unitMatchers } from "@ksp-gonogo/ui-kit/testing";
364
+ * expect.extend(unitMatchers);
365
+ * ```
366
+ *
367
+ * @category Testing
368
+ */
369
+ declare const unitMatchers: {
370
+ toShowQuantity(received: HTMLElement | undefined, quantity: {
371
+ magnitude: number;
372
+ unit: string;
373
+ } | null | undefined, opts?: FormatQuantityOptions): MatcherResult;
374
+ };
375
+ /**
376
+ * Type augmentation for the matcher above, for a Vitest consumer to merge
377
+ * explicitly (importing this module never changes `expect` types on its own):
378
+ *
379
+ * ```ts
380
+ * declare module "vitest" {
381
+ * interface Assertion<T> extends UnitMatchers<T> {}
382
+ * }
383
+ * ```
384
+ *
385
+ * @category Testing
386
+ */
387
+ interface UnitMatchers<Result = unknown> {
388
+ toShowQuantity(quantity: {
389
+ magnitude: number;
390
+ unit: string;
391
+ } | null | undefined, opts?: FormatQuantityOptions): Result;
392
+ }
393
+
394
+ /**
395
+ * Every element under `container` that carries the kit's {@link Tooltip} with
396
+ * this text, a string matched whole or a pattern matched anywhere. The tip is
397
+ * the `data-tooltip` attribute, present whether or not the tip is open, so a
398
+ * test reads what an element would say without hovering it.
399
+ *
400
+ * @category Testing
401
+ */
402
+ declare function getAllByTip(container: ParentNode, text: string | RegExp): HTMLElement[];
403
+ /**
404
+ * The one element under `container` carrying this tip, or `null`; throws when
405
+ * more than one does.
406
+ *
407
+ * @category Testing
408
+ */
409
+ declare function queryByTip(container: ParentNode, text: string | RegExp): HTMLElement | null;
410
+ /**
411
+ * The one element under `container` carrying this tip; throws when there is
412
+ * none or more than one.
413
+ *
414
+ * @category Testing
415
+ */
416
+ declare function getByTip(container: ParentNode, text: string | RegExp): HTMLElement;
417
+
418
+ /**
419
+ * One observation of `target` at the given size, as a complete
420
+ * `ResizeObserverEntry` with every box filled. jsdom lays nothing out, so a
421
+ * self-sizing component otherwise renders at zero.
422
+ *
423
+ * @category Testing
424
+ */
425
+ declare function resizeObservation(target: Element, size: {
426
+ width: number;
427
+ height: number;
428
+ }): ResizeObserverEntry;
429
+ /**
430
+ * Install a `ResizeObserver` that reports one fixed size to everything observed,
431
+ * and return the uninstall. `deliver` fires the callback during `observe`
432
+ * (`"sync"`, the default), or on a macrotask for a component that must not see
433
+ * a size during its own mount.
434
+ *
435
+ * The returned function is the only way to uninstall: `vi.unstubAllGlobals()`
436
+ * does not undo it, and installing either observer while one is live throws.
437
+ * Installing once at module scope and never restoring is fine.
438
+ *
439
+ * @category Testing
440
+ */
441
+ declare function installFixedSizeResizeObserver(options: {
442
+ width: number;
443
+ height: number;
444
+ deliver?: "sync" | "macrotask";
445
+ }): () => void;
446
+ /**
447
+ * A `ResizeObserver` a test drives by hand: see {@link installDrivableResizeObserver}.
448
+ *
449
+ * @category Testing
450
+ */
451
+ interface DrivableResizeObservers {
452
+ /** Deliver one observation of `target` at `size` to every observer watching it. */
453
+ resize(target: Element, size: {
454
+ width: number;
455
+ height: number;
456
+ }): void;
457
+ /** Restore the previous `ResizeObserver`. The only way to uninstall. */
458
+ uninstall(): void;
459
+ }
460
+ /**
461
+ * Install a `ResizeObserver` whose observations a test delivers itself, for a
462
+ * component whose behaviour is the response to a size change. `resize` reaches
463
+ * only observers watching the element, so a component that never subscribed
464
+ * cannot pass. Installing either observer while one is live throws; call
465
+ * `uninstall` when done, or install once at module scope.
466
+ *
467
+ * @category Testing
468
+ */
469
+ declare function installDrivableResizeObserver(): DrivableResizeObservers;
470
+
471
+ export { CHECKER_TOLERANCE, type CheckerFit, type CheckerGrid, type CheckerPaint, type DrivableResizeObservers, type FramingFault, type HeldMarkFault, type RenderWidgetOptions, type UnannouncedHeldMark, type UnitMatchers, WidgetHost, WidgetHostFor, expectLiveRegionPrimed, expectNoA11yViolations, fitOf, framingFaults, getAllByTip, getByTip, installDrivableResizeObserver, installFixedSizeResizeObserver, paintChecker, queryByTip, renderHookWithRail, renderWidget, renderWithRail, resizeObservation, unannouncedHeldMarks, unitMatchers, visibleText };