@multiplatform.one/components 7.5.0 → 7.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.
Files changed (244) hide show
  1. package/dist/cjs/CopyField/index.cjs +4 -1
  2. package/dist/cjs/CopyField/index.native.js +4 -1
  3. package/dist/cjs/CopyField/index.native.js.map +1 -1
  4. package/dist/cjs/ProgressSteps/index.cjs +5 -2
  5. package/dist/cjs/ProgressSteps/index.native.js +5 -2
  6. package/dist/cjs/ProgressSteps/index.native.js.map +1 -1
  7. package/dist/cjs/Separator/index.native.js.map +1 -1
  8. package/dist/cjs/Wizard/index.cjs +1 -0
  9. package/dist/cjs/Wizard/index.native.js +3 -12
  10. package/dist/cjs/Wizard/index.native.js.map +1 -1
  11. package/dist/cjs/code/CodeBlock/index.cjs +38 -22
  12. package/dist/cjs/code/CodeBlock/index.native.js +38 -22
  13. package/dist/cjs/code/CodeBlock/index.native.js.map +1 -1
  14. package/dist/cjs/index.cjs +1 -0
  15. package/dist/cjs/index.native.js +1 -0
  16. package/dist/cjs/index.native.js.map +1 -1
  17. package/dist/cjs/surfaces.cjs +30 -7
  18. package/dist/cjs/surfaces.native.js +30 -7
  19. package/dist/cjs/surfaces.native.js.map +1 -1
  20. package/dist/cjs/views/index.cjs +3 -0
  21. package/dist/cjs/views/index.native.js +3 -0
  22. package/dist/cjs/views/index.native.js.map +1 -1
  23. package/dist/esm/Accordion/index.native.js.map +1 -1
  24. package/dist/esm/Alert/index.native.js.map +1 -1
  25. package/dist/esm/AnimateHeight/index.native.js.map +1 -1
  26. package/dist/esm/Avatar/getInitials.native.js.map +1 -1
  27. package/dist/esm/Avatar/index.native.js.map +1 -1
  28. package/dist/esm/Badge/index.native.js.map +1 -1
  29. package/dist/esm/Button.native.js.map +1 -1
  30. package/dist/esm/Card.native.js.map +1 -1
  31. package/dist/esm/CardFooter.native.js.map +1 -1
  32. package/dist/esm/CardHeader.native.js.map +1 -1
  33. package/dist/esm/Carousel/index.native.js.map +1 -1
  34. package/dist/esm/Chip/index.native.js.map +1 -1
  35. package/dist/esm/ContextMenu/index.native.js.map +1 -1
  36. package/dist/esm/CopyField/index.mjs +4 -1
  37. package/dist/esm/CopyField/index.mjs.map +1 -1
  38. package/dist/esm/CopyField/index.native.js +4 -1
  39. package/dist/esm/CopyField/index.native.js.map +1 -1
  40. package/dist/esm/DotIndicator/index.native.js.map +1 -1
  41. package/dist/esm/DropdownMenu/index.native.js.map +1 -1
  42. package/dist/esm/ErrorBoundary/index.native.js.map +1 -1
  43. package/dist/esm/Heading.native.js.map +1 -1
  44. package/dist/esm/Meter/index.native.js.map +1 -1
  45. package/dist/esm/Meter/meter.native.js.map +1 -1
  46. package/dist/esm/Pagination.native.js.map +1 -1
  47. package/dist/esm/ProgressSteps/index.mjs +5 -2
  48. package/dist/esm/ProgressSteps/index.mjs.map +1 -1
  49. package/dist/esm/ProgressSteps/index.native.js +5 -2
  50. package/dist/esm/ProgressSteps/index.native.js.map +1 -1
  51. package/dist/esm/Quote/index.native.js.map +1 -1
  52. package/dist/esm/Separator/index.mjs.map +1 -1
  53. package/dist/esm/Separator/index.native.js.map +1 -1
  54. package/dist/esm/Skeleton/index.native.js.map +1 -1
  55. package/dist/esm/SkipLink/index.native.js.map +1 -1
  56. package/dist/esm/SortableList/SortableHandle.native.js.map +1 -1
  57. package/dist/esm/SortableList/index.native.js.map +1 -1
  58. package/dist/esm/SortableList/useSortableRows.native.js.map +1 -1
  59. package/dist/esm/Tabs/index.native.js.map +1 -1
  60. package/dist/esm/Text.native.js.map +1 -1
  61. package/dist/esm/Toast/index.native.js.map +1 -1
  62. package/dist/esm/Toast/store.native.js.map +1 -1
  63. package/dist/esm/Tooltip/nativePress.native.js.map +1 -1
  64. package/dist/esm/Tooltip/tooltipShared.native.js.map +1 -1
  65. package/dist/esm/TreeSelect/index.native.js.map +1 -1
  66. package/dist/esm/TreeSelect/treeSelectModel.native.js.map +1 -1
  67. package/dist/esm/Video/PlayerChrome.native.js.map +1 -1
  68. package/dist/esm/Video/VideoFrame.native.js.map +1 -1
  69. package/dist/esm/Video/types.native.js.map +1 -1
  70. package/dist/esm/Wheel/index.native.js.map +1 -1
  71. package/dist/esm/Wizard/index.mjs +1 -0
  72. package/dist/esm/Wizard/index.mjs.map +1 -1
  73. package/dist/esm/Wizard/index.native.js +3 -12
  74. package/dist/esm/Wizard/index.native.js.map +1 -1
  75. package/dist/esm/actions/ActionBar.native.js.map +1 -1
  76. package/dist/esm/actions/DestructiveAction.native.js.map +1 -1
  77. package/dist/esm/actions/devWarn.native.js.map +1 -1
  78. package/dist/esm/actions/index.native.js.map +1 -1
  79. package/dist/esm/actions/severity.native.js.map +1 -1
  80. package/dist/esm/charts/Axis.native.js.map +1 -1
  81. package/dist/esm/charts/BarChart.native.js.map +1 -1
  82. package/dist/esm/charts/ChartSurface.native.js.map +1 -1
  83. package/dist/esm/charts/LineChart.native.js.map +1 -1
  84. package/dist/esm/charts/PieChart.native.js.map +1 -1
  85. package/dist/esm/charts/Sparkline.native.js.map +1 -1
  86. package/dist/esm/charts/accessibility.native.js.map +1 -1
  87. package/dist/esm/charts/composeChart.native.js.map +1 -1
  88. package/dist/esm/charts/index.native.js.map +1 -1
  89. package/dist/esm/charts/math.native.js.map +1 -1
  90. package/dist/esm/charts/series.native.js.map +1 -1
  91. package/dist/esm/charts/svg.native.js.map +1 -1
  92. package/dist/esm/charts/types.native.js.map +1 -1
  93. package/dist/esm/charts/useChartTheme.native.js.map +1 -1
  94. package/dist/esm/code/Code/index.native.js.map +1 -1
  95. package/dist/esm/code/CodeBlock/index.mjs +42 -26
  96. package/dist/esm/code/CodeBlock/index.mjs.map +1 -1
  97. package/dist/esm/code/CodeBlock/index.native.js +42 -26
  98. package/dist/esm/code/CodeBlock/index.native.js.map +1 -1
  99. package/dist/esm/code/HighlightedCode/index.native.js.map +1 -1
  100. package/dist/esm/code/Highlighter/index.native.js.map +1 -1
  101. package/dist/esm/code/Pre/index.native.js.map +1 -1
  102. package/dist/esm/code/index.native.js.map +1 -1
  103. package/dist/esm/componentColors.native.js.map +1 -1
  104. package/dist/esm/feedback/ConfirmDialog.native.js.map +1 -1
  105. package/dist/esm/feedback/NotifyHost.native.js.map +1 -1
  106. package/dist/esm/feedback/copy.native.js.map +1 -1
  107. package/dist/esm/feedback/index.native.js.map +1 -1
  108. package/dist/esm/feedback/notify.native.js.map +1 -1
  109. package/dist/esm/feedback/route.native.js.map +1 -1
  110. package/dist/esm/feedback/store.native.js.map +1 -1
  111. package/dist/esm/hooks/index.native.js.map +1 -1
  112. package/dist/esm/hooks/useClipboard.native.js.map +1 -1
  113. package/dist/esm/hooks/useDocumentTitle.native.js.map +1 -1
  114. package/dist/esm/icons/CheckRegular.native.js.map +1 -1
  115. package/dist/esm/icons/MinusRegular.native.js.map +1 -1
  116. package/dist/esm/icons/index.native.js.map +1 -1
  117. package/dist/esm/icons/lucide.native.js.map +1 -1
  118. package/dist/esm/icons/themed.native.js.map +1 -1
  119. package/dist/esm/images/Image/ImageFrame.native.js.map +1 -1
  120. package/dist/esm/images/Image/shared.native.js.map +1 -1
  121. package/dist/esm/images/Image/types.native.js.map +1 -1
  122. package/dist/esm/images/index.native.js.map +1 -1
  123. package/dist/esm/index.mjs +1 -0
  124. package/dist/esm/index.mjs.map +1 -1
  125. package/dist/esm/index.native.js +1 -0
  126. package/dist/esm/index.native.js.map +1 -1
  127. package/dist/esm/knobImmuneExports.native.js.map +1 -1
  128. package/dist/esm/layouts/AsyncBoundary.native.js.map +1 -1
  129. package/dist/esm/layouts/Debug/index.native.js.map +1 -1
  130. package/dist/esm/layouts/EmptyState.native.js.map +1 -1
  131. package/dist/esm/layouts/KeyboardAvoidingWrapper/behavior.native.js.map +1 -1
  132. package/dist/esm/layouts/PageTemplates.native.js.map +1 -1
  133. package/dist/esm/layouts/PaneScaffold.native.js.map +1 -1
  134. package/dist/esm/layouts/SafeAreaWrapper/insets.native.js.map +1 -1
  135. package/dist/esm/layouts/ViewSwitcher.native.js.map +1 -1
  136. package/dist/esm/layouts/attachments.native.js.map +1 -1
  137. package/dist/esm/layouts/breadcrumbs.native.js.map +1 -1
  138. package/dist/esm/layouts/index.native.js.map +1 -1
  139. package/dist/esm/layouts/page.native.js.map +1 -1
  140. package/dist/esm/layouts/pageHeadingScope.native.js.map +1 -1
  141. package/dist/esm/layouts/sidebar.native.js.map +1 -1
  142. package/dist/esm/layouts/tags.native.js.map +1 -1
  143. package/dist/esm/layouts/timeline.native.js.map +1 -1
  144. package/dist/esm/layouts/toolbar.native.js.map +1 -1
  145. package/dist/esm/shared/actionScan.native.js.map +1 -1
  146. package/dist/esm/shared/i18n.native.js.map +1 -1
  147. package/dist/esm/shared/t.native.js.map +1 -1
  148. package/dist/esm/shared/textRidesText.native.js.map +1 -1
  149. package/dist/esm/shared/useKeyboardInset.native.js.map +1 -1
  150. package/dist/esm/shared/useReturnFocusOnClose.native.js.map +1 -1
  151. package/dist/esm/surfaces.mjs +31 -8
  152. package/dist/esm/surfaces.mjs.map +1 -1
  153. package/dist/esm/surfaces.native.js +31 -8
  154. package/dist/esm/surfaces.native.js.map +1 -1
  155. package/dist/esm/tamagui.native.js.map +1 -1
  156. package/dist/esm/testSetup.native.js.map +1 -1
  157. package/dist/esm/utils/setTinted.native.js.map +1 -1
  158. package/dist/esm/utils/unwrapText.native.js.map +1 -1
  159. package/dist/esm/views/Calendar.native.js.map +1 -1
  160. package/dist/esm/views/Dashboard.native.js.map +1 -1
  161. package/dist/esm/views/Gantt.native.js.map +1 -1
  162. package/dist/esm/views/ImageGrid.native.js.map +1 -1
  163. package/dist/esm/views/List.native.js.map +1 -1
  164. package/dist/esm/views/ReportBuilder.native.js.map +1 -1
  165. package/dist/esm/views/TreeView.native.js.map +1 -1
  166. package/dist/esm/views/calendarMath.native.js.map +1 -1
  167. package/dist/esm/views/ganttMath.native.js.map +1 -1
  168. package/dist/esm/views/index.mjs +2 -2
  169. package/dist/esm/views/index.mjs.map +1 -1
  170. package/dist/esm/views/index.native.js +2 -2
  171. package/dist/esm/views/index.native.js.map +1 -1
  172. package/dist/esm/views/kanbanDnd.native.js.map +1 -1
  173. package/dist/esm/views/treeViewDnd.native.js.map +1 -1
  174. package/dist/esm/views/treeViewLayout.native.js.map +1 -1
  175. package/dist/esm/views/treeViewMotion.native.js.map +1 -1
  176. package/dist/jsx/CopyField/index.mjs +4 -1
  177. package/dist/jsx/CopyField/index.mjs.map +1 -1
  178. package/dist/jsx/CopyField/index.native.js +4 -1
  179. package/dist/jsx/CopyField/index.native.js.map +1 -1
  180. package/dist/jsx/ProgressSteps/index.mjs +5 -2
  181. package/dist/jsx/ProgressSteps/index.mjs.map +1 -1
  182. package/dist/jsx/ProgressSteps/index.native.js +5 -2
  183. package/dist/jsx/ProgressSteps/index.native.js.map +1 -1
  184. package/dist/jsx/Separator/index.mjs.map +1 -1
  185. package/dist/jsx/Separator/index.native.js.map +1 -1
  186. package/dist/jsx/Wizard/index.mjs +1 -0
  187. package/dist/jsx/Wizard/index.mjs.map +1 -1
  188. package/dist/jsx/Wizard/index.native.js +3 -12
  189. package/dist/jsx/Wizard/index.native.js.map +1 -1
  190. package/dist/jsx/code/CodeBlock/index.mjs +42 -26
  191. package/dist/jsx/code/CodeBlock/index.mjs.map +1 -1
  192. package/dist/jsx/code/CodeBlock/index.native.js +38 -22
  193. package/dist/jsx/code/CodeBlock/index.native.js.map +1 -1
  194. package/dist/jsx/index.js +1 -0
  195. package/dist/jsx/index.js.map +1 -1
  196. package/dist/jsx/index.mjs +1 -0
  197. package/dist/jsx/index.mjs.map +1 -1
  198. package/dist/jsx/index.native.js +1 -0
  199. package/dist/jsx/index.native.js.map +1 -1
  200. package/dist/jsx/surfaces.mjs +31 -8
  201. package/dist/jsx/surfaces.mjs.map +1 -1
  202. package/dist/jsx/surfaces.native.js +30 -7
  203. package/dist/jsx/surfaces.native.js.map +1 -1
  204. package/dist/jsx/views/index.mjs +2 -2
  205. package/dist/jsx/views/index.mjs.map +1 -1
  206. package/dist/jsx/views/index.native.js +3 -0
  207. package/dist/jsx/views/index.native.js.map +1 -1
  208. package/package.json +9 -9
  209. package/src/CopyField/index.tsx +10 -15
  210. package/src/Heading.native.spec.tsx +156 -0
  211. package/src/Heading.spec.tsx +14 -79
  212. package/src/Meter/Meter.spec.tsx +9 -1
  213. package/src/ProgressSteps/index.tsx +18 -3
  214. package/src/Separator/Separator.spec.tsx +68 -32
  215. package/src/Separator/index.tsx +27 -9
  216. package/src/Text.native.spec.tsx +144 -0
  217. package/src/Text.spec.tsx +12 -70
  218. package/src/Tooltip/tooltipGapContract.spec.ts +54 -0
  219. package/src/Wizard/Wizard.spec.tsx +23 -1
  220. package/src/Wizard/index.tsx +1 -8
  221. package/src/code/CodeBlock/CodeBlock.spec.tsx +6 -6
  222. package/src/code/CodeBlock/index.tsx +44 -27
  223. package/src/index.ts +12 -6
  224. package/src/overlayElevation.spec.tsx +154 -0
  225. package/src/surfaces.spec.tsx +98 -1
  226. package/src/surfaces.tsx +28 -6
  227. package/src/tamagui.spec.ts +11 -2
  228. package/src/tamagui.ts +5 -5
  229. package/src/views/index.ts +15 -0
  230. package/types/CopyField/index.d.ts +8 -14
  231. package/types/CopyField/index.d.ts.map +1 -1
  232. package/types/ProgressSteps/index.d.ts +8 -1
  233. package/types/ProgressSteps/index.d.ts.map +1 -1
  234. package/types/Separator/index.d.ts +27 -9
  235. package/types/Separator/index.d.ts.map +1 -1
  236. package/types/Tabs/index.d.ts +1 -1
  237. package/types/Wizard/index.d.ts.map +1 -1
  238. package/types/code/CodeBlock/index.d.ts.map +1 -1
  239. package/types/index.d.ts +1 -0
  240. package/types/index.d.ts.map +1 -1
  241. package/types/surfaces.d.ts +6 -6
  242. package/types/surfaces.d.ts.map +1 -1
  243. package/types/views/index.d.ts +1 -1
  244. package/types/views/index.d.ts.map +1 -1
@@ -53,6 +53,13 @@ export interface ProgressStepsProps extends Omit<GetProps<typeof StepsRoot>, "ch
53
53
  linear?: boolean;
54
54
  /** Compact markers + type (density compact override). */
55
55
  compact?: boolean;
56
+ /**
57
+ * Rail label treatment. `"hidden"` keeps markers (and a11y names) and
58
+ * drops the visible per-step label — the 390 wizard treatment; the
59
+ * current step's name rides the wizard legend as "Step n of m — X".
60
+ * @default "visible"
61
+ */
62
+ labels?: "visible" | "hidden";
56
63
  }
57
64
 
58
65
  type StepState = "completed" | "current" | "upcoming";
@@ -130,6 +137,7 @@ export function ProgressSteps({
130
137
  allowJump = true,
131
138
  linear = true,
132
139
  compact,
140
+ labels = "visible",
133
141
  ...props
134
142
  }: ProgressStepsProps) {
135
143
  const { t } = useTranslation();
@@ -155,7 +163,13 @@ export function ProgressSteps({
155
163
  const gapToken = knobProps.gap.gap;
156
164
 
157
165
  return (
158
- <StepsRoot role="list" aria-label={t("Progress")} data-density={knobProps.density} {...props}>
166
+ <StepsRoot
167
+ role="list"
168
+ aria-label={t("Progress")}
169
+ data-density={knobProps.density}
170
+ data-labels={labels}
171
+ {...props}
172
+ >
159
173
  <XStack
160
174
  flexDirection={horizontal ? "row" : "column"}
161
175
  alignItems={horizontal ? "flex-start" : "stretch"}
@@ -310,10 +324,11 @@ export function ProgressSteps({
310
324
  />
311
325
  ) : null;
312
326
 
327
+ const showLabels = labels !== "hidden";
313
328
  const body = horizontal ? (
314
329
  <YStack alignItems="center" flexShrink={0}>
315
330
  {markerTarget}
316
- {labelBlock}
331
+ {showLabels ? labelBlock : null}
317
332
  </YStack>
318
333
  ) : (
319
334
  <XStack alignItems="stretch" gap={gapToken} minHeight={MIN_PRESS_TARGET}>
@@ -321,7 +336,7 @@ export function ProgressSteps({
321
336
  {markerTarget}
322
337
  {verticalConnector}
323
338
  </YStack>
324
- {labelBlock}
339
+ {showLabels ? labelBlock : null}
325
340
  </XStack>
326
341
  );
327
342
 
@@ -3,17 +3,25 @@
3
3
  *
4
4
  * MPO-47 — Separator paints a line.
5
5
  *
6
- * The defect this locks against: 96 of 96 separators measured height 1px,
7
- * background-color rgba(0,0,0,0), border-top-width 0px — the box was there,
8
- * the PAINT was not. Asserting height and never colour is exactly why that
9
- * shipped, so this spec asserts the painted colour first.
6
+ * What this locks: the house rule paints, at 1 CSS px, in $borderColor, in
7
+ * both schemes, and cannot flex-grow (DG-OVF-07 — the raw primitive it
8
+ * replaced declares `flex: 1`). Asserting a separator's height and never its
9
+ * colour is how a transparent rule ships, so this spec asserts the painted
10
+ * colour first.
10
11
  *
11
- * jsdom cannot cascade Tamagui's class-based CSS, so paint is asserted the
12
- * house way (Button LC-18 / DotIndicator technique): read the atomic classes
13
- * the style emits. `_bg-borderColor` is the background atom bound to the
14
- * `$borderColor` theme token one token, resolved per scheme and the
15
- * both-schemes arm additionally asserts the token's light AND dark values are
16
- * real, non-transparent colours in the active config.
12
+ * (The ticket's "96 of 96 paint nothing" reading was a probe artefact — it
13
+ * read borderTopWidth on a primitive that paints borderBottomWidth. See the
14
+ * component header. The assertions below are unaffected: they are about what
15
+ * the HOUSE rule paints, not about what the raw one did.)
16
+ *
17
+ * jsdom cannot CASCADE Tamagui's class-based CSS, so `getComputedStyle` on the
18
+ * element reads nothing — but Tamagui does INJECT its atomic rules into
19
+ * `document.styleSheets`, so the declaration behind each of the element's own
20
+ * atoms is readable and is what this spec asserts. That is deliberate: the
21
+ * first version of this file matched atom NAMES (`/_o-0d0t5/`), the encoder
22
+ * emits `_o-0--5`, and the quiet test was red from the day it merged without
23
+ * anyone seeing it — CI runs `turbo run build` only (MPO-78). A declaration is
24
+ * the law; an atom name is an implementation detail of the encoder.
17
25
  */
18
26
  import { renderWithProviders } from "@multiplatform.one/test-utils";
19
27
  import { cleanup } from "@testing-library/react";
@@ -29,6 +37,39 @@ function getSeparator(container: HTMLElement): HTMLElement {
29
37
  return el as HTMLElement;
30
38
  }
31
39
 
40
+ const escapeForRegExp = (value: string) => value.replace(/[.*+?^${}()|[\]\\-]/g, "\\$&");
41
+
42
+ /**
43
+ * Every value Tamagui declared for `property` through one of THIS element's
44
+ * atomic classes. Empty means the element does not carry that property at all.
45
+ */
46
+ function declared(el: HTMLElement, property: string): string[] {
47
+ const atoms = el.className.split(/\s+/).filter(Boolean);
48
+ const values: string[] = [];
49
+ for (const sheet of Array.from(document.styleSheets)) {
50
+ let rules: CSSRuleList | undefined;
51
+ try {
52
+ rules = sheet.cssRules;
53
+ } catch {
54
+ continue; // cross-origin sheet; nothing of ours lives there
55
+ }
56
+ for (const rule of Array.from(rules ?? [])) {
57
+ const parsed = (rule.cssText ?? "").match(/^([^{]+)\{([^}]*)\}$/);
58
+ if (!parsed) continue;
59
+ const [, selector, body] = parsed;
60
+ const mine = atoms.some((atom) =>
61
+ new RegExp(`\\.${escapeForRegExp(atom)}(?![\\w-])`).test(selector),
62
+ );
63
+ if (!mine) continue;
64
+ const decl = body.match(
65
+ new RegExp(`(?:^|[;\\s])${escapeForRegExp(property)}\\s*:\\s*([^;]+)`),
66
+ );
67
+ if (decl) values.push(decl[1].trim());
68
+ }
69
+ }
70
+ return values;
71
+ }
72
+
32
73
  /** Resolve a theme colour Variable to its raw value string. */
33
74
  function themeColorVal(scheme: "light" | "dark", key: string): string {
34
75
  const themes = getConfig().themes as Record<string, Record<string, { val?: unknown }>>;
@@ -47,14 +88,10 @@ describe("Separator — painted colour (the regression this component exists for
47
88
  it("paints its 1px box with the $borderColor token, not a transparent box", () => {
48
89
  const { container } = renderWithProviders(<Separator />);
49
90
  const separator = getSeparator(container);
50
- // PAINT: the background atom is bound to the borderColor token.
51
- expect(separator.className).toMatch(/_bg-borderColo/);
52
- // Nothing re-zeroes the paint: no transparent background atom.
53
- expect(separator.className).not.toMatch(/_bg-transparent/);
91
+ // PAINT: the declared background is the borderColor token, not transparent.
92
+ expect(declared(separator, "background-color")).toEqual(["var(--borderColor)"]);
54
93
  // GEOMETRY: exactly 1 CSS px tall — never 0.5 (§7.3: "1px is the value").
55
- expect(separator.className).toMatch(/_h-1px/);
56
- expect(separator.className).not.toMatch(/_h-0d0t5px/);
57
- expect(separator.className).not.toMatch(/_mah-0d0t5px/);
94
+ expect(declared(separator, "height")).toEqual(["1px"]);
58
95
  });
59
96
 
60
97
  it("carries a visible $borderColor in BOTH schemes (token values are real colours)", () => {
@@ -69,15 +106,15 @@ describe("Separator — painted colour (the regression this component exists for
69
106
  }
70
107
  });
71
108
 
72
- it("paints the same token atom under an explicit dark theme subtree", () => {
109
+ it("paints the same token under an explicit dark theme subtree", () => {
73
110
  const { container } = renderWithProviders(
74
111
  <Theme name="dark">
75
112
  <Separator />
76
113
  </Theme>,
77
114
  );
78
115
  const separator = getSeparator(container);
79
- expect(separator.className).toMatch(/_bg-borderColo/);
80
- expect(separator.className).toMatch(/_h-1px/);
116
+ expect(declared(separator, "background-color")).toEqual(["var(--borderColor)"]);
117
+ expect(declared(separator, "height")).toEqual(["1px"]);
81
118
  });
82
119
  });
83
120
 
@@ -85,14 +122,14 @@ describe("Separator — quiet variant (§7.3: opacity 0.5, never a lighter colou
85
122
  it("dims with opacity 0.5 and keeps the exact same paint token", () => {
86
123
  const { container } = renderWithProviders(<Separator quiet />);
87
124
  const separator = getSeparator(container);
88
- expect(separator.className).toMatch(/_o-0d0t5/);
89
- // Never a lighter colour: the background atom is unchanged from default.
90
- expect(separator.className).toMatch(/_bg-borderColo/);
125
+ expect(declared(separator, "opacity")).toEqual(["0.5"]);
126
+ // Never a lighter colour: the paint token is unchanged from the default.
127
+ expect(declared(separator, "background-color")).toEqual(["var(--borderColor)"]);
91
128
  });
92
129
 
93
130
  it("default separator carries no opacity dim", () => {
94
131
  const { container } = renderWithProviders(<Separator />);
95
- expect(getSeparator(container).className).not.toMatch(/_o-0d0t5/);
132
+ expect(declared(getSeparator(container), "opacity")).toEqual([]);
96
133
  });
97
134
  });
98
135
 
@@ -100,8 +137,8 @@ describe("Separator — vertical (§7.3: vertical separators are 1px wide)", ()
100
137
  it("is 1px wide, releases the horizontal height, and declares its orientation", () => {
101
138
  const { container } = renderWithProviders(<Separator vertical />);
102
139
  const separator = getSeparator(container);
103
- expect(separator.className).toMatch(/_w-1px/);
104
- expect(separator.className).not.toMatch(/_w-0d0t5px/);
140
+ expect(declared(separator, "width")).toEqual(["1px"]);
141
+ expect(declared(separator, "height")).toEqual(["auto"]);
105
142
  expect(separator.getAttribute("aria-orientation")).toBe("vertical");
106
143
  });
107
144
 
@@ -115,10 +152,10 @@ describe("Separator — DG-OVF-07: never leftover space", () => {
115
152
  it("cannot grow or shrink (flex: none discipline on the painting edge)", () => {
116
153
  const { container } = renderWithProviders(<Separator />);
117
154
  const separator = getSeparator(container);
118
- expect(separator.className).toMatch(/_fg-0/);
119
- expect(separator.className).toMatch(/_fs-0/);
120
- // The raw primitive was a `flex: 1` item — the defect DG-OVF-07 measured.
121
- expect(separator.className).not.toMatch(/_fg-1/);
155
+ // The raw primitive it replaced declares `flex: 1` — the defect DG-OVF-07
156
+ // measures — so both ends of the flex story are pinned here.
157
+ expect(declared(separator, "flex-grow")).toEqual(["0"]);
158
+ expect(declared(separator, "flex-shrink")).toEqual(["0"]);
122
159
  });
123
160
  });
124
161
 
@@ -126,7 +163,6 @@ describe("Separator — consumer eject stays the last word (STD-EJECT-LAST)", ()
126
163
  it("an explicit backgroundColor prop wins over the house paint", () => {
127
164
  const { container } = renderWithProviders(<Separator backgroundColor="$red9" />);
128
165
  const separator = getSeparator(container);
129
- expect(separator.className).toMatch(/_bg-red9/);
130
- expect(separator.className).not.toMatch(/_bg-borderColo/);
166
+ expect(declared(separator, "background-color")).toEqual(["var(--red9)"]);
131
167
  });
132
168
  });
@@ -1,15 +1,33 @@
1
1
  /**
2
- * House Separator — a rule that actually paints (MPO-47).
2
+ * House Separator — the catalog rule (MPO-47).
3
3
  *
4
4
  * The raw tamagui Separator rode the curated re-export as a "layout primitive
5
- * with no house replacement" and painted NOTHING on every measured board:
6
- * 96 of 96 separators across the action-and-feedback gallery measured
7
- * height 1px, background-color rgba(0,0,0,0), border-top-width 0px a 1px
8
- * gap where a line should be. LC-45 / LC-70 forbid a raw primitive behind a
9
- * bare catalog name; this is the house implementation. The paint recipe is
10
- * lifted from DropdownMenu's proven private SeparatorLine (a FILLED line, not
11
- * a borderBlink floors sub-1px borders and drops 0-height bordered boxes
12
- * out of paint entirely, which is how the raw one shipped invisible).
5
+ * with no house replacement". Two measured reasons it may not:
6
+ *
7
+ * - LC-45 / LC-70: a bare name a house barrel exports is a catalog part, and
8
+ * this one resolved to a raw primitive.
9
+ * - DG-OVF-07: the raw primitive declares `flex: 1`
10
+ * (@tamagui/separator@2.7.6 Separator.tsx), so the rule is CAPABLE of
11
+ * absorbing free spacethe exact defect DG-OVF-07 names.
12
+ *
13
+ * The paint recipe is lifted from DropdownMenu's proven private
14
+ * SeparatorLine: a FILLED line rather than a border, because Blink floors
15
+ * sub-1px border widths and a filled fractional height is crisp in every
16
+ * engine.
17
+ *
18
+ * MEASUREMENT CORRECTION (2026-09-01). MPO-47's headline — "96 of 96
19
+ * separators paint nothing: height 1px, background rgba(0,0,0,0),
20
+ * border-top-width 0px" — was a PROBE ARTEFACT, not a defect. The gallery
21
+ * probe computed `paints = backgroundColor.alpha > 0 || borderTopWidth > 0`,
22
+ * while tamagui's Separator paints with borderBOTTOMWidth (horizontal) and
23
+ * borderRIGHTWidth (vertical), so the expression was structurally blind to
24
+ * the one primitive it audited. The shots that reading produced carry the
25
+ * painted 1px $borderColor rules in their own pixels, in both schemes; a
26
+ * pixel audit of all 52 action-and-feedback shots finds at least as many
27
+ * painted 1px $borderColor rules as the sidecar called blank, on every
28
+ * board. The raw rule did paint. It was still the wrong thing behind a
29
+ * catalog name, and it could still flex-grow — which is why this component
30
+ * stands.
13
31
  *
14
32
  * Measured law (tamagui-rendered-reference.md §7.3):
15
33
  * - 1 CSS px, never 0.5px. Vertical separators are 1px wide.
@@ -0,0 +1,144 @@
1
+ /**
2
+ * House Text knob wiring, NATIVE arm (MPO-20). Sibling of
3
+ * ./Heading.native.spec.tsx — see its header for WHY the arm is a separate
4
+ * file (the `vi.resetModules()` version returned a split module graph and
5
+ * proved nothing) and for what a `.native` spec can and cannot claim.
6
+ */
7
+ import { cleanup, screen } from "@testing-library/react";
8
+ import { renderWithProviders } from "@multiplatform.one/test-utils";
9
+ import { Preset } from "@multiplatform.one/theme";
10
+ import { afterEach, describe, expect, it, vi } from "vitest";
11
+ import { Paragraph, SizableText, Text } from "./Text";
12
+
13
+ vi.mock("@multiplatform.one/platform", async (importOriginal) => ({
14
+ ...(await importOriginal<typeof import("@multiplatform.one/platform")>()),
15
+ isWeb: false,
16
+ isNative: true,
17
+ }));
18
+
19
+ afterEach(cleanup);
20
+
21
+ const LABEL = "Measure the text node";
22
+
23
+ function classesByPrefix(el: Element, prefixes: string[]): string[] {
24
+ return String((el as HTMLElement).className || "")
25
+ .split(" ")
26
+ .filter((c) => prefixes.some((p) => c.startsWith(p)))
27
+ .sort();
28
+ }
29
+
30
+ /** Tamagui font-scope class on the TEXT NODE (`font_body` / `font_mono` / …). */
31
+ function fontScopeClass(el: Element): string {
32
+ return (
33
+ String((el as HTMLElement).className || "")
34
+ .split(" ")
35
+ .find((c) => c.startsWith("font_")) ?? ""
36
+ );
37
+ }
38
+
39
+ /** Render, read the TEXT NODE's prefixed classes, clean up. */
40
+ function renderTextClasses(node: React.ReactElement, prefixes: string[]): string[] {
41
+ renderWithProviders(node);
42
+ const textNode = screen.getByText(LABEL);
43
+ const classes = classesByPrefix(textNode, prefixes);
44
+ cleanup();
45
+ return classes;
46
+ }
47
+
48
+ describe("house Text — NATIVE environment (isWeb false)", () => {
49
+ it("really is running with the native platform flags", async () => {
50
+ const platform = await import("@multiplatform.one/platform");
51
+ expect(platform.isWeb).toBe(false);
52
+ expect(platform.isNative).toBe(true);
53
+ });
54
+
55
+ it("changes colour on a textAccent flip", () => {
56
+ const high = renderTextClasses(
57
+ <Preset overrides={{ textAccent: "high" }}>
58
+ <Text>{LABEL}</Text>
59
+ </Preset>,
60
+ ["_col-"],
61
+ );
62
+ const low = renderTextClasses(
63
+ <Preset overrides={{ textAccent: "low" }}>
64
+ <Text>{LABEL}</Text>
65
+ </Preset>,
66
+ ["_col-"],
67
+ );
68
+ expect(high).not.toHaveLength(0);
69
+ expect(high).not.toEqual(low);
70
+ });
71
+
72
+ it("changes weight on a fontWeight flip, pinned to the 400/700 label law", () => {
73
+ const regular = renderTextClasses(
74
+ <Preset overrides={{ fontWeight: "regular" }}>
75
+ <Text>{LABEL}</Text>
76
+ </Preset>,
77
+ ["_fow-"],
78
+ );
79
+ const bold = renderTextClasses(
80
+ <Preset overrides={{ fontWeight: "bold" }}>
81
+ <Text>{LABEL}</Text>
82
+ </Preset>,
83
+ ["_fow-"],
84
+ );
85
+ const explicit400 = renderTextClasses(<Text fontWeight="400">{LABEL}</Text>, ["_fow-"]);
86
+ const explicit700 = renderTextClasses(<Text fontWeight="700">{LABEL}</Text>, ["_fow-"]);
87
+ expect(regular).not.toHaveLength(0);
88
+ expect(regular).not.toEqual(bold);
89
+ expect(regular).toEqual(explicit400);
90
+ expect(bold).toEqual(explicit700);
91
+ });
92
+
93
+ it("changes family on a bodyFont flip — the props channel, not the CSS rescue", () => {
94
+ const scope = (node: React.ReactElement) => {
95
+ renderWithProviders(node);
96
+ const cls = fontScopeClass(screen.getByText(LABEL));
97
+ cleanup();
98
+ return cls;
99
+ };
100
+ const sans = scope(
101
+ <Preset overrides={{ bodyFont: "sans-serif" }}>
102
+ <Text>{LABEL}</Text>
103
+ </Preset>,
104
+ );
105
+ const mono = scope(
106
+ <Preset overrides={{ bodyFont: "mono" }}>
107
+ <Text>{LABEL}</Text>
108
+ </Preset>,
109
+ );
110
+ expect(sans).not.toBe("");
111
+ expect(sans).not.toBe(mono);
112
+ expect(mono).toBe("font_mono");
113
+ });
114
+
115
+ it("carries the same knobs on the sibling body voices", () => {
116
+ for (const Voice of [SizableText, Paragraph]) {
117
+ const high = renderTextClasses(
118
+ <Preset overrides={{ textAccent: "high" }}>
119
+ <Voice>{LABEL}</Voice>
120
+ </Preset>,
121
+ ["_col-"],
122
+ );
123
+ const low = renderTextClasses(
124
+ <Preset overrides={{ textAccent: "low" }}>
125
+ <Voice>{LABEL}</Voice>
126
+ </Preset>,
127
+ ["_col-"],
128
+ );
129
+ expect(high).not.toHaveLength(0);
130
+ expect(high).not.toEqual(low);
131
+ }
132
+ });
133
+
134
+ it("explicit consumer props still eject (STD-EJECT-LAST)", () => {
135
+ const ejected = renderTextClasses(
136
+ <Preset overrides={{ fontWeight: "bold" }}>
137
+ <Text fontWeight="400">{LABEL}</Text>
138
+ </Preset>,
139
+ ["_fow-"],
140
+ );
141
+ const explicit400 = renderTextClasses(<Text fontWeight="400">{LABEL}</Text>, ["_fow-"]);
142
+ expect(ejected).toEqual(explicit400);
143
+ });
144
+ });
package/src/Text.spec.tsx CHANGED
@@ -12,19 +12,18 @@
12
12
  * family. Between knob flips and against explicit-prop renders of the
13
13
  * same value. Hashed class names are compared by identity only.
14
14
  *
15
- * WEB arm: the default module graph (isWeb true).
16
- * NATIVE arm: `@multiplatform.one/platform` mocked to isWeb:false on a fresh
17
- * module graph (the resolveKnobs.spec.ts elevation-native pattern), with
18
- * Preset/render/Text all imported from that same graph. FontKnobStyles'
19
- * CSS-variable rescue is absent in BOTH arms (renderWithProviders mounts no
20
- * theme provider), so a flip that reaches the node proves the PROPS channel
21
- * — the only channel native has.
15
+ * This file is the WEB arm (the default module graph, isWeb true). The NATIVE
16
+ * arm is ./Text.native.spec.tsx, a separate FILE so that a file-scoped
17
+ * `vi.mock` gives it one coherent module graph. FontKnobStyles' CSS-variable
18
+ * rescue is absent in BOTH arms (renderWithProviders mounts no theme
19
+ * provider), so a flip that reaches the node proves the PROPS channel — the
20
+ * only channel native has.
22
21
  */
23
22
  import { cleanup, screen } from "@testing-library/react";
24
23
  import { renderWithProviders } from "@multiplatform.one/test-utils";
25
24
  import { Preset } from "@multiplatform.one/theme";
26
25
  import { XStack } from "tamagui";
27
- import { afterEach, describe, expect, it, vi } from "vitest";
26
+ import { afterEach, describe, expect, it } from "vitest";
28
27
  import { Text } from "./Text";
29
28
 
30
29
  afterEach(cleanup);
@@ -182,65 +181,8 @@ describe("house Text — fontWeight and bodyFont reach the text node", () => {
182
181
  });
183
182
  });
184
183
 
185
- describe("house Text NATIVE environment (isWeb false, fresh module graph)", () => {
186
- it("textAccent and fontWeight flips still reach the text node", async () => {
187
- vi.resetModules();
188
- vi.doMock("@multiplatform.one/platform", async (importOriginal) => ({
189
- ...(await importOriginal<typeof import("@multiplatform.one/platform")>()),
190
- isWeb: false,
191
- isNative: true,
192
- }));
193
- try {
194
- // Everything must come from the SAME fresh graph: a Preset from the
195
- // old graph would provide a context the fresh Text never reads.
196
- const [{ Text: NativeText }, { Preset: NativePreset }, testUtils, testingLibrary] =
197
- await Promise.all([
198
- import("./Text"),
199
- import("@multiplatform.one/theme"),
200
- import("@multiplatform.one/test-utils"),
201
- import("@testing-library/react"),
202
- ]);
203
-
204
- const renderNative = (node: React.ReactElement, prefixes: string[]): string[] => {
205
- testUtils.renderWithProviders(node);
206
- const el = testingLibrary.screen.getByText(LABEL);
207
- const classes = classesByPrefix(el, prefixes);
208
- testingLibrary.cleanup();
209
- return classes;
210
- };
211
-
212
- const high = renderNative(
213
- <NativePreset overrides={{ textAccent: "high" }}>
214
- <NativeText>{LABEL}</NativeText>
215
- </NativePreset>,
216
- ["_col-"],
217
- );
218
- const low = renderNative(
219
- <NativePreset overrides={{ textAccent: "low" }}>
220
- <NativeText>{LABEL}</NativeText>
221
- </NativePreset>,
222
- ["_col-"],
223
- );
224
- expect(high).not.toHaveLength(0);
225
- expect(high).not.toEqual(low);
226
-
227
- const regular = renderNative(
228
- <NativePreset overrides={{ fontWeight: "regular" }}>
229
- <NativeText>{LABEL}</NativeText>
230
- </NativePreset>,
231
- ["_fow-"],
232
- );
233
- const bold = renderNative(
234
- <NativePreset overrides={{ fontWeight: "bold" }}>
235
- <NativeText>{LABEL}</NativeText>
236
- </NativePreset>,
237
- ["_fow-"],
238
- );
239
- expect(regular).not.toHaveLength(0);
240
- expect(regular).not.toEqual(bold);
241
- } finally {
242
- vi.doUnmock("@multiplatform.one/platform");
243
- vi.resetModules();
244
- }
245
- });
246
- });
184
+ // The NATIVE arm lives in ./Text.native.spec.tsx. It used to sit here as a
185
+ // `vi.resetModules()` + dynamic-import block, but that returned a SPLIT module
186
+ // graph — a fresh `renderWithProviders` over a stale `useResolvedKnobs` — and
187
+ // threw "Missing theme." instead of measuring anything. A file-scoped
188
+ // `vi.mock` in its own spec file is one coherent graph.
@@ -0,0 +1,54 @@
1
+ /**
2
+ * MPO-38 AC-7, the source half: Tooltip owns no gap constant of its own.
3
+ *
4
+ * The runtime value is asserted where it lives — `layoutTokens.spec.ts` pins
5
+ * `OVERLAY_ANCHOR_GAP` at 4. Asserting that number again from a Tooltip spec
6
+ * proves nothing about Tooltip: it passes just as happily if this component
7
+ * goes back to a hardcoded `offset={4}`, which is exactly the drift the AC
8
+ * was written to close.
9
+ *
10
+ * So this is the source half, in the shape of `panelWidthContract.spec.ts`.
11
+ * A Tooltip is a FREE overlay — no trigger to attach to — so it is one of the
12
+ * few overlays `OVERLAY_ANCHOR_GAP` still governs after the cover ruling
13
+ * (`OVERLAY_ATTACH_GAP = 0`). It must READ that token rather than restate its
14
+ * value under a second name (LC-16 ONE-IMPLEMENTATION).
15
+ *
16
+ * Both twins are checked. The web/native split is where a duplicate constant
17
+ * survives longest: fixing one file and leaving the other still renders the
18
+ * right gap on the platform anyone happened to look at.
19
+ */
20
+ import { readFileSync } from "node:fs";
21
+ import { dirname, resolve } from "node:path";
22
+ import { fileURLToPath } from "node:url";
23
+ import { describe, expect, it } from "vitest";
24
+
25
+ const tooltipDir = dirname(fileURLToPath(import.meta.url));
26
+
27
+ /** The web twin, the native twin, and the module they share. */
28
+ const tooltipSources = ["index.tsx", "index.native.tsx", "tooltipShared.tsx"];
29
+
30
+ function read(relative: string): string {
31
+ return readFileSync(resolve(tooltipDir, relative), "utf8");
32
+ }
33
+
34
+ describe("Tooltip gap contract (MPO-38 AC-7)", () => {
35
+ it.each(tooltipSources)("%s declares no gap constant of its own", (relative) => {
36
+ // Named for the constant this AC deleted, but the assertion is the rule,
37
+ // not the name: any local OFFSET/GAP constant is the same drift again.
38
+ expect(read(relative)).not.toMatch(/\b(?:const|let|var)\s+\w*(?:OFFSET|GAP)\w*\s*=/);
39
+ });
40
+
41
+ it.each(["index.tsx", "index.native.tsx"])(
42
+ "%s positions the bubble with the shared FREE-overlay token",
43
+ (relative) => {
44
+ const source = read(relative);
45
+ expect(source).toMatch(
46
+ /import\s*\{[^}]*\bOVERLAY_ANCHOR_GAP\b[^}]*\}\s*from\s*"@multiplatform\.one\/theme"/s,
47
+ );
48
+ expect(source).toContain("offset={OVERLAY_ANCHOR_GAP}");
49
+ // A literal offset is the regression: it renders the same 4px today and
50
+ // silently stops tracking the token the moment the house rule moves.
51
+ expect(source).not.toMatch(/offset=\{\s*\d/);
52
+ },
53
+ );
54
+ });
@@ -1,8 +1,9 @@
1
1
  import { Form, Input, Switch, useFormContext } from "@multiplatform.one/forms";
2
2
  import { renderWithProviders } from "@multiplatform.one/test-utils";
3
+ import { __resetDevWarnSeen } from "@multiplatform.one/theme";
3
4
  import { act, fireEvent } from "@testing-library/react";
4
5
  import { useEffect } from "react";
5
- import { describe, expect, it } from "vitest";
6
+ import { describe, expect, it, vi } from "vitest";
6
7
  import { Wizard, useWizard, type WizardFormLike, type WizardStep } from "./index";
7
8
 
8
9
  const STEPS: WizardStep[] = [
@@ -214,6 +215,27 @@ describe("Wizard LC-63 / DG-VAL", () => {
214
215
  });
215
216
  });
216
217
 
218
+ describe("Wizard LC-57 guardrail-clean at rest", () => {
219
+ it("a ready wizard at rest emits zero [theme] guardrail warnings", async () => {
220
+ __resetDevWarnSeen();
221
+ const warn = vi.spyOn(console, "warn").mockImplementation(() => undefined);
222
+ const error = vi.spyOn(console, "error").mockImplementation(() => undefined);
223
+ try {
224
+ renderWizard();
225
+ await act(async () => {
226
+ await new Promise((resolve) => setTimeout(resolve, 0));
227
+ });
228
+ const themeWarns = [...warn.mock.calls, ...error.mock.calls]
229
+ .map((args) => String(args[0]))
230
+ .filter((msg) => msg.startsWith("[theme]"));
231
+ expect(themeWarns).toEqual([]);
232
+ } finally {
233
+ warn.mockRestore();
234
+ error.mockRestore();
235
+ }
236
+ });
237
+ });
238
+
217
239
  describe("Wizard states", () => {
218
240
  it("draws loading, empty, and error shells", () => {
219
241
  const loading = renderWithProviders(
@@ -521,17 +521,10 @@ function WizardRoot({
521
521
  {...props}
522
522
  >
523
523
  <YStack data-wizard-rail="" width="100%">
524
- {/*
525
- * MPO-81: no `labels` prop here. ProgressStepsProps does not declare
526
- * one and ProgressSteps reads no such prop, so passing it was inert
527
- * at runtime and failed the build with TS2322. `hideLabels` still
528
- * drives the per-step caption below, which is where it has always
529
- * had a real effect. Re-add the pass in the same change that gives
530
- * ProgressSteps a labels prop it actually honours.
531
- */}
532
524
  <ProgressSteps
533
525
  steps={railSteps}
534
526
  current={current}
527
+ labels={hideLabels ? "hidden" : "visible"}
535
528
  compact={compact}
536
529
  linear
537
530
  onStepChange={(index) => {
@@ -85,19 +85,19 @@ function greet() {
85
85
  describe("CodeBlock", () => {
86
86
  it("keeps the copy action reachable at the 44px press floor", () => {
87
87
  renderWithProviders(<CodeBlock>const x = 1;</CodeBlock>);
88
- const button = screen.getByLabelText("Copy code to clipboard");
88
+ const button = screen.getByLabelText("Copy to clipboard");
89
89
  expect(button).toBeTruthy();
90
90
  expect(button.getAttribute("data-press-min")).toBe(String(MIN_PRESS_TARGET));
91
+ // Default knob: the glyph-ring end-cap square itself meets the floor
92
+ // (44). Smaller size steps keep the floor by spilling hitSlop instead
93
+ // (CodeBlock.copy.spec covers the 36/52 cap geometry).
94
+ expect(Number(button.getAttribute("data-end-cap"))).toBeGreaterThanOrEqual(MIN_PRESS_TARGET);
91
95
  const style = getComputedStyle(button);
92
96
  expect(style.display).not.toBe("none");
93
- const minWidth = Number.parseFloat(style.minWidth);
94
- const minHeight = Number.parseFloat(style.minHeight);
95
- if (!Number.isNaN(minWidth)) expect(minWidth).toBeGreaterThanOrEqual(MIN_PRESS_TARGET);
96
- if (!Number.isNaN(minHeight)) expect(minHeight).toBeGreaterThanOrEqual(MIN_PRESS_TARGET);
97
97
  });
98
98
 
99
99
  it("hides the copy action only when disableCopy is set", () => {
100
100
  renderWithProviders(<CodeBlock disableCopy>const x = 1;</CodeBlock>);
101
- expect(screen.queryByLabelText("Copy code to clipboard")).toBeNull();
101
+ expect(screen.queryByLabelText("Copy to clipboard")).toBeNull();
102
102
  });
103
103
  });