@multiplatform.one/components 7.6.2 → 7.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/Badge/index.cjs +4 -3
- package/dist/cjs/Badge/index.native.js +7 -6
- package/dist/cjs/Badge/index.native.js.map +1 -1
- package/dist/cjs/Card.cjs +7 -53
- package/dist/cjs/Card.native.js +8 -53
- package/dist/cjs/Card.native.js.map +1 -1
- package/dist/cjs/CardFooter.cjs +1 -9
- package/dist/cjs/CardFooter.native.js +2 -7
- package/dist/cjs/CardFooter.native.js.map +1 -1
- package/dist/cjs/CardHeader.cjs +1 -9
- package/dist/cjs/CardHeader.native.js +2 -7
- package/dist/cjs/CardHeader.native.js.map +1 -1
- package/dist/cjs/ProgressSteps/index.cjs +13 -6
- package/dist/cjs/ProgressSteps/index.native.js +14 -6
- package/dist/cjs/ProgressSteps/index.native.js.map +1 -1
- package/dist/cjs/SheetModal/detents.cjs +46 -0
- package/dist/cjs/SheetModal/detents.native.js +54 -0
- package/dist/cjs/SheetModal/detents.native.js.map +1 -0
- package/dist/cjs/SheetModal/index.native.js +8 -11
- package/dist/cjs/SheetModal/index.native.js.map +1 -1
- package/dist/cjs/SortableList/index.cjs +1 -1
- package/dist/cjs/SortableList/index.native.js +1 -1
- package/dist/cjs/SortableList/index.native.js.map +1 -1
- package/dist/cjs/code/Code/index.cjs +2 -15
- package/dist/cjs/code/Code/index.native.js +3 -13
- package/dist/cjs/code/Code/index.native.js.map +1 -1
- package/dist/cjs/code/HighlightedCode/index.cjs +0 -5
- package/dist/cjs/code/HighlightedCode/index.native.js +0 -5
- package/dist/cjs/code/HighlightedCode/index.native.js.map +1 -1
- package/dist/cjs/knobImmuneExports.cjs +8 -0
- package/dist/cjs/knobImmuneExports.native.js +8 -0
- package/dist/cjs/knobImmuneExports.native.js.map +1 -1
- package/dist/cjs/layouts/breadcrumbs.cjs +3 -1
- package/dist/cjs/layouts/breadcrumbs.native.js +3 -1
- package/dist/cjs/layouts/breadcrumbs.native.js.map +1 -1
- package/dist/cjs/surfaces.native.js.map +1 -1
- package/dist/cjs/tamagui.cjs +5 -0
- package/dist/cjs/tamagui.native.js +5 -0
- package/dist/cjs/tamagui.native.js.map +1 -1
- package/dist/cjs/views/Dashboard.cjs +3 -3
- package/dist/cjs/views/Dashboard.native.js +3 -3
- package/dist/cjs/views/Dashboard.native.js.map +1 -1
- package/dist/cjs/views/List.cjs +4 -5
- package/dist/cjs/views/List.native.js +4 -5
- package/dist/cjs/views/List.native.js.map +1 -1
- package/dist/cjs/views/TreeView.cjs +4 -1
- package/dist/cjs/views/TreeView.native.js +3 -0
- package/dist/cjs/views/TreeView.native.js.map +1 -1
- package/dist/esm/Badge/index.mjs +4 -3
- package/dist/esm/Badge/index.mjs.map +1 -1
- package/dist/esm/Badge/index.native.js +7 -6
- package/dist/esm/Badge/index.native.js.map +1 -1
- package/dist/esm/Card.mjs +5 -51
- package/dist/esm/Card.mjs.map +1 -1
- package/dist/esm/Card.native.js +9 -54
- package/dist/esm/Card.native.js.map +1 -1
- package/dist/esm/CardFooter.mjs +3 -11
- package/dist/esm/CardFooter.mjs.map +1 -1
- package/dist/esm/CardFooter.native.js +6 -11
- package/dist/esm/CardFooter.native.js.map +1 -1
- package/dist/esm/CardHeader.mjs +3 -11
- package/dist/esm/CardHeader.mjs.map +1 -1
- package/dist/esm/CardHeader.native.js +6 -11
- package/dist/esm/CardHeader.native.js.map +1 -1
- package/dist/esm/ProgressSteps/index.mjs +14 -7
- package/dist/esm/ProgressSteps/index.mjs.map +1 -1
- package/dist/esm/ProgressSteps/index.native.js +15 -7
- package/dist/esm/ProgressSteps/index.native.js.map +1 -1
- package/dist/esm/SheetModal/detents.mjs +19 -0
- package/dist/esm/SheetModal/detents.mjs.map +1 -0
- package/dist/esm/SheetModal/detents.native.js +24 -0
- package/dist/esm/SheetModal/detents.native.js.map +1 -0
- package/dist/esm/SheetModal/index.mjs.map +1 -1
- package/dist/esm/SheetModal/index.native.js +7 -10
- package/dist/esm/SheetModal/index.native.js.map +1 -1
- package/dist/esm/SortableList/index.mjs +1 -1
- package/dist/esm/SortableList/index.mjs.map +1 -1
- package/dist/esm/SortableList/index.native.js +1 -1
- package/dist/esm/SortableList/index.native.js.map +1 -1
- package/dist/esm/code/Code/index.mjs +2 -15
- package/dist/esm/code/Code/index.mjs.map +1 -1
- package/dist/esm/code/Code/index.native.js +6 -16
- package/dist/esm/code/Code/index.native.js.map +1 -1
- package/dist/esm/code/HighlightedCode/index.mjs +0 -5
- package/dist/esm/code/HighlightedCode/index.mjs.map +1 -1
- package/dist/esm/code/HighlightedCode/index.native.js +0 -5
- package/dist/esm/code/HighlightedCode/index.native.js.map +1 -1
- package/dist/esm/knobImmuneExports.mjs +8 -0
- package/dist/esm/knobImmuneExports.mjs.map +1 -1
- package/dist/esm/knobImmuneExports.native.js +8 -0
- package/dist/esm/knobImmuneExports.native.js.map +1 -1
- package/dist/esm/layouts/breadcrumbs.mjs +3 -1
- package/dist/esm/layouts/breadcrumbs.mjs.map +1 -1
- package/dist/esm/layouts/breadcrumbs.native.js +3 -1
- package/dist/esm/layouts/breadcrumbs.native.js.map +1 -1
- package/dist/esm/surfaces.mjs.map +1 -1
- package/dist/esm/surfaces.native.js.map +1 -1
- package/dist/esm/tamagui.mjs +3 -2
- package/dist/esm/tamagui.mjs.map +1 -1
- package/dist/esm/tamagui.native.js +3 -2
- package/dist/esm/tamagui.native.js.map +1 -1
- package/dist/esm/views/Dashboard.mjs +3 -3
- package/dist/esm/views/Dashboard.mjs.map +1 -1
- package/dist/esm/views/Dashboard.native.js +3 -3
- package/dist/esm/views/Dashboard.native.js.map +1 -1
- package/dist/esm/views/List.mjs +4 -5
- package/dist/esm/views/List.mjs.map +1 -1
- package/dist/esm/views/List.native.js +4 -5
- package/dist/esm/views/List.native.js.map +1 -1
- package/dist/esm/views/TreeView.mjs +4 -1
- package/dist/esm/views/TreeView.mjs.map +1 -1
- package/dist/esm/views/TreeView.native.js +3 -0
- package/dist/esm/views/TreeView.native.js.map +1 -1
- package/dist/jsx/Badge/index.mjs +4 -3
- package/dist/jsx/Badge/index.mjs.map +1 -1
- package/dist/jsx/Badge/index.native.js +7 -6
- package/dist/jsx/Badge/index.native.js.map +1 -1
- package/dist/jsx/Card.mjs +5 -51
- package/dist/jsx/Card.mjs.map +1 -1
- package/dist/jsx/Card.native.js +8 -53
- package/dist/jsx/Card.native.js.map +1 -1
- package/dist/jsx/CardFooter.mjs +3 -11
- package/dist/jsx/CardFooter.mjs.map +1 -1
- package/dist/jsx/CardFooter.native.js +2 -7
- package/dist/jsx/CardFooter.native.js.map +1 -1
- package/dist/jsx/CardHeader.mjs +3 -11
- package/dist/jsx/CardHeader.mjs.map +1 -1
- package/dist/jsx/CardHeader.native.js +2 -7
- package/dist/jsx/CardHeader.native.js.map +1 -1
- package/dist/jsx/ProgressSteps/index.mjs +14 -7
- package/dist/jsx/ProgressSteps/index.mjs.map +1 -1
- package/dist/jsx/ProgressSteps/index.native.js +14 -6
- package/dist/jsx/ProgressSteps/index.native.js.map +1 -1
- package/dist/jsx/SheetModal/detents.mjs +19 -0
- package/dist/jsx/SheetModal/detents.mjs.map +1 -0
- package/dist/jsx/SheetModal/detents.native.js +54 -0
- package/dist/jsx/SheetModal/detents.native.js.map +1 -0
- package/dist/jsx/SheetModal/index.mjs.map +1 -1
- package/dist/jsx/SheetModal/index.native.js +8 -11
- package/dist/jsx/SheetModal/index.native.js.map +1 -1
- package/dist/jsx/SortableList/index.mjs +1 -1
- package/dist/jsx/SortableList/index.mjs.map +1 -1
- package/dist/jsx/SortableList/index.native.js +1 -1
- package/dist/jsx/SortableList/index.native.js.map +1 -1
- package/dist/jsx/code/Code/index.mjs +2 -15
- package/dist/jsx/code/Code/index.mjs.map +1 -1
- package/dist/jsx/code/Code/index.native.js +3 -13
- package/dist/jsx/code/Code/index.native.js.map +1 -1
- package/dist/jsx/code/CodeBlock/index.native.js +1 -1
- package/dist/jsx/code/HighlightedCode/index.mjs +0 -5
- package/dist/jsx/code/HighlightedCode/index.mjs.map +1 -1
- package/dist/jsx/code/HighlightedCode/index.native.js +0 -5
- package/dist/jsx/code/HighlightedCode/index.native.js.map +1 -1
- package/dist/jsx/code/index.native.js +1 -1
- package/dist/jsx/knobImmuneExports.mjs +8 -0
- package/dist/jsx/knobImmuneExports.mjs.map +1 -1
- package/dist/jsx/knobImmuneExports.native.js +8 -0
- package/dist/jsx/knobImmuneExports.native.js.map +1 -1
- package/dist/jsx/layouts/breadcrumbs.mjs +3 -1
- package/dist/jsx/layouts/breadcrumbs.mjs.map +1 -1
- package/dist/jsx/layouts/breadcrumbs.native.js +3 -1
- package/dist/jsx/layouts/breadcrumbs.native.js.map +1 -1
- package/dist/jsx/surfaces.mjs.map +1 -1
- package/dist/jsx/surfaces.native.js.map +1 -1
- package/dist/jsx/tamagui.mjs +3 -2
- package/dist/jsx/tamagui.mjs.map +1 -1
- package/dist/jsx/tamagui.native.js +5 -0
- package/dist/jsx/tamagui.native.js.map +1 -1
- package/dist/jsx/views/Dashboard.mjs +3 -3
- package/dist/jsx/views/Dashboard.mjs.map +1 -1
- package/dist/jsx/views/Dashboard.native.js +3 -3
- package/dist/jsx/views/Dashboard.native.js.map +1 -1
- package/dist/jsx/views/List.mjs +4 -5
- package/dist/jsx/views/List.mjs.map +1 -1
- package/dist/jsx/views/List.native.js +4 -5
- package/dist/jsx/views/List.native.js.map +1 -1
- package/dist/jsx/views/TreeView.mjs +4 -1
- package/dist/jsx/views/TreeView.mjs.map +1 -1
- package/dist/jsx/views/TreeView.native.js +3 -0
- package/dist/jsx/views/TreeView.native.js.map +1 -1
- package/package.json +9 -9
- package/src/Badge/Badge.spec.tsx +10 -11
- package/src/Badge/index.tsx +7 -6
- package/src/Card.spec.tsx +7 -2
- package/src/Card.tsx +22 -37
- package/src/CardFooter.spec.tsx +1 -1
- package/src/CardFooter.tsx +6 -3
- package/src/CardHeader.spec.tsx +1 -1
- package/src/CardHeader.tsx +6 -3
- package/src/Chip/Chip.spec.tsx +1 -1
- package/src/Pagination.spec.tsx +1 -1
- package/src/ProgressSteps/ProgressSteps.spec.tsx +29 -2
- package/src/ProgressSteps/index.tsx +21 -7
- package/src/SheetModal/SheetModal.spec.tsx +88 -0
- package/src/SheetModal/detents.ts +55 -0
- package/src/SheetModal/index.native.tsx +13 -12
- package/src/SheetModal/index.tsx +5 -2
- package/src/Skeleton/Skeleton.spec.tsx +1 -1
- package/src/SortableList/SortableList.spec.tsx +41 -0
- package/src/SortableList/SortableList.stories.tsx +35 -2
- package/src/SortableList/index.tsx +1 -1
- package/src/TreeSelect/TreeSelect.stories.tsx +76 -1
- package/src/Wheel/Wheel.spec.tsx +1 -1
- package/src/code/Code/code.spec.tsx +27 -1
- package/src/code/Code/index.tsx +9 -2
- package/src/code/HighlightedCode/index.tsx +0 -1
- package/src/code/highlightedCode.spec.tsx +10 -1
- package/src/knobImmuneExports.ts +11 -0
- package/src/layouts/AsyncBoundary.spec.tsx +1 -1
- package/src/layouts/breadcrumbs.spec.tsx +17 -0
- package/src/layouts/breadcrumbs.tsx +19 -2
- package/src/layouts/tags.r7.spec.tsx +3 -1
- package/src/surfaces.tsx +2 -1
- package/src/tamagui.ts +8 -0
- package/src/views/Dashboard.tsx +3 -3
- package/src/views/List.spec.tsx +42 -1
- package/src/views/List.tsx +6 -5
- package/src/views/TreeView.spec.tsx +26 -0
- package/src/views/TreeView.tsx +3 -0
- package/types/Badge/index.d.ts.map +1 -1
- package/types/Card.d.ts +6 -11
- package/types/Card.d.ts.map +1 -1
- package/types/CardFooter.d.ts.map +1 -1
- package/types/CardHeader.d.ts.map +1 -1
- package/types/ProgressSteps/index.d.ts.map +1 -1
- package/types/SheetModal/detents.d.ts +38 -0
- package/types/SheetModal/detents.d.ts.map +1 -0
- package/types/SheetModal/index.d.ts +1 -1
- package/types/SheetModal/index.d.ts.map +1 -1
- package/types/SheetModal/index.native.d.ts +1 -1
- package/types/SheetModal/index.native.d.ts.map +1 -1
- package/types/code/Code/index.d.ts.map +1 -1
- package/types/code/HighlightedCode/index.d.ts.map +1 -1
- package/types/knobImmuneExports.d.ts.map +1 -1
- package/types/layouts/breadcrumbs.d.ts +7 -1
- package/types/layouts/breadcrumbs.d.ts.map +1 -1
- package/types/surfaces.d.ts.map +1 -1
- package/types/tamagui.d.ts +3 -1
- package/types/tamagui.d.ts.map +1 -1
- package/types/views/List.d.ts.map +1 -1
- package/types/views/TreeView.d.ts.map +1 -1
|
@@ -3,6 +3,8 @@ import {
|
|
|
3
3
|
MIN_PRESS_TARGET,
|
|
4
4
|
ensureKeyboardModalityTracking,
|
|
5
5
|
pressTargetHitSlop,
|
|
6
|
+
radiusClassProps,
|
|
7
|
+
useAaSolidFill,
|
|
6
8
|
useReadableTextOn,
|
|
7
9
|
useResolvedKnobs,
|
|
8
10
|
wasKeyboardFocus,
|
|
@@ -76,8 +78,11 @@ const Marker = styled(View, {
|
|
|
76
78
|
name: "ProgressStepMarker",
|
|
77
79
|
alignItems: "center",
|
|
78
80
|
justifyContent: "center",
|
|
79
|
-
borderWidth: 2,
|
|
80
81
|
flexShrink: 0,
|
|
82
|
+
// R-PILL (D-09 / LC-60): the numbered disc is a circle at every radius
|
|
83
|
+
// stop — the knob moves size, never shape. Not K1 (Progress/Meter rails).
|
|
84
|
+
...radiusClassProps("R-PILL", "ProgressSteps marker"),
|
|
85
|
+
borderRadius: 1000,
|
|
81
86
|
});
|
|
82
87
|
|
|
83
88
|
const StepButton = styled(View, {
|
|
@@ -148,14 +153,18 @@ export function ProgressSteps({
|
|
|
148
153
|
);
|
|
149
154
|
if (isWeb) ensureKeyboardModalityTracking();
|
|
150
155
|
const [kbFocusIndex, setKbFocusIndex] = useState<number | null>(null);
|
|
151
|
-
// LC-05: current marker rides
|
|
152
|
-
// paper/ink from luminance rather than an assumed white glyph.
|
|
153
|
-
|
|
156
|
+
// LC-05: current marker rides an accent solid; completed/error fills pick
|
|
157
|
+
// paper/ink from luminance rather than an assumed white glyph. The stock
|
|
158
|
+
// dark `$accentBackground` tops out at 4.02:1 against either scheme
|
|
159
|
+
// anchor, under the in-repo 4.5 text floor, so the current fill walks to
|
|
160
|
+
// the nearest accent solid that can carry the numeral.
|
|
161
|
+
const currentFill = useAaSolidFill(componentColors.indicator.selected);
|
|
162
|
+
const onSelected = useReadableTextOn(currentFill);
|
|
154
163
|
const onCompleted = useReadableTextOn(componentColors.indicator.completed);
|
|
155
164
|
const onError = useReadableTextOn("$red9");
|
|
156
165
|
const { px: markerPx, hitSlop, box: nestedBox } = splitNestedControl(knobProps.nestedControl);
|
|
157
166
|
const markerIconSize = Math.max(12, Math.round(markerPx * 0.45));
|
|
158
|
-
const
|
|
167
|
+
const ringWidth = knobProps.borderRadius.borderWidth ?? 0;
|
|
159
168
|
const connectorThickness = 2;
|
|
160
169
|
const pressOutset = Math.max(0, Math.ceil((MIN_PRESS_TARGET - markerPx) / 2));
|
|
161
170
|
const interactive = !!onStepChange && allowJump;
|
|
@@ -192,7 +201,7 @@ export function ProgressSteps({
|
|
|
192
201
|
: kind === "completed"
|
|
193
202
|
? componentColors.indicator.completed
|
|
194
203
|
: kind === "current"
|
|
195
|
-
?
|
|
204
|
+
? currentFill
|
|
196
205
|
: "transparent";
|
|
197
206
|
const markerBorder = kind === "upcoming" ? componentColors.indicator.track : markerBg;
|
|
198
207
|
const markerFg =
|
|
@@ -207,15 +216,20 @@ export function ProgressSteps({
|
|
|
207
216
|
step.description ?? (step.optional && kind !== "error" ? t("Optional") : null);
|
|
208
217
|
const connectorFill =
|
|
209
218
|
index < current ? componentColors.indicator.completed : componentColors.indicator.track;
|
|
219
|
+
// Upcoming is outlined-by-state: chromeless `borderWidth:none` would
|
|
220
|
+
// erase the empty disc, so it keeps the OUTLINED-HAIRLINE floor.
|
|
221
|
+
const markerRing = kind === "upcoming" ? Math.max(ringWidth, 0.5) : ringWidth;
|
|
210
222
|
|
|
211
223
|
const marker = (
|
|
212
224
|
<Marker
|
|
213
225
|
{...nestedBox}
|
|
214
|
-
borderRadius={
|
|
226
|
+
borderRadius={1000}
|
|
227
|
+
borderWidth={markerRing}
|
|
215
228
|
backgroundColor={markerBg}
|
|
216
229
|
borderColor={markerBorder}
|
|
217
230
|
data-nested-px={markerPx}
|
|
218
231
|
data-step-state={kind}
|
|
232
|
+
data-marker-ring={markerRing}
|
|
219
233
|
transition={knobProps.transition}
|
|
220
234
|
{...(state === "current"
|
|
221
235
|
? ({ "aria-current": "step" } as Record<string, unknown>)
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { mediumDetent, minSheetHeight, resolveSheetDetents } from "./detents";
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* MPO-68 AC-4 — "SheetModal's snapPoint prop either drives a detent or is
|
|
6
|
+
* removed". It drives one. These lock that in, because it has already been
|
|
7
|
+
* lost once: 184746910 (MPO-71) rewrote the detents block from a pre-MPO-68
|
|
8
|
+
* copy and dropped the snapPoint push, leaving the prop declared, documented
|
|
9
|
+
* and passed by four callers while doing nothing on device.
|
|
10
|
+
*
|
|
11
|
+
* Geometry under test is a pure height list, largest first. The component's
|
|
12
|
+
* PanResponder turns each height h into a resting translate of `travel - h`.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const travel = 600;
|
|
16
|
+
|
|
17
|
+
describe("resolveSheetDetents", () => {
|
|
18
|
+
it("a plain sheet has exactly one rest: fully expanded", () => {
|
|
19
|
+
expect(resolveSheetDetents({ travel })).toEqual([600]);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("REGRESSION: snapPoint creates a detent instead of being ignored", () => {
|
|
23
|
+
// The bug this spec exists for: main returned [600] here.
|
|
24
|
+
expect(resolveSheetDetents({ travel, snapPoint: 85 })).toEqual([600, 510]);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("reads snapPoint as a percent of the full height, matching the web Sheet", () => {
|
|
28
|
+
expect(resolveSheetDetents({ travel, snapPoint: 50 })).toEqual([600, 300]);
|
|
29
|
+
expect(resolveSheetDetents({ travel, snapPoint: 60 })).toEqual([600, 360]);
|
|
30
|
+
expect(resolveSheetDetents({ travel, snapPoint: 92 })).toEqual([600, 552]);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("honours the four heights real callers pass", () => {
|
|
34
|
+
// CommandPalette 92, list-tools-sheet 85, ListThemePlayground 85,
|
|
35
|
+
// Toast.stories 70 — every one of these was a no-op on device.
|
|
36
|
+
for (const snapPoint of [92, 85, 70]) {
|
|
37
|
+
const detents = resolveSheetDetents({ travel, snapPoint });
|
|
38
|
+
expect(detents).toContain(Math.round(travel * (snapPoint / 100)));
|
|
39
|
+
expect(detents.length).toBeGreaterThan(1);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("a scrollable sheet keeps its half-open detent", () => {
|
|
44
|
+
expect(resolveSheetDetents({ travel, scrollable: true })).toEqual([
|
|
45
|
+
600,
|
|
46
|
+
Math.round(travel * mediumDetent),
|
|
47
|
+
]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("scrollable and snapPoint stack, largest first, no duplicates", () => {
|
|
51
|
+
// 600 expanded, 330 medium, 510 from snapPoint.
|
|
52
|
+
expect(resolveSheetDetents({ travel, scrollable: true, snapPoint: 85 })).toEqual([
|
|
53
|
+
600, 510, 330,
|
|
54
|
+
]);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("dedupes a snapPoint that lands on a detent already present", () => {
|
|
58
|
+
expect(resolveSheetDetents({ travel, snapPoint: 100 })).toEqual([600]);
|
|
59
|
+
expect(resolveSheetDetents({ travel, scrollable: true, snapPoint: 55 })).toEqual([600, 330]);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("clamps a snapPoint above 100 to fully expanded", () => {
|
|
63
|
+
expect(resolveSheetDetents({ travel, snapPoint: 140 })).toEqual([600]);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("drops a detent shorter than the floor rather than resting near-dismissed", () => {
|
|
67
|
+
// 600 * 0.10 = 60, well under minSheetHeight.
|
|
68
|
+
expect(resolveSheetDetents({ travel, snapPoint: 10 })).toEqual([600]);
|
|
69
|
+
expect(minSheetHeight).toBe(180);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("a sheet shorter than the floor still keeps its own full height", () => {
|
|
73
|
+
expect(resolveSheetDetents({ travel: 120, scrollable: true, snapPoint: 85 })).toEqual([120]);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("ignores a snapPoint that is not a usable number", () => {
|
|
77
|
+
for (const snapPoint of [0, -20, Number.NaN, Number.POSITIVE_INFINITY]) {
|
|
78
|
+
expect(resolveSheetDetents({ travel, snapPoint })).toEqual([600]);
|
|
79
|
+
}
|
|
80
|
+
expect(resolveSheetDetents({ travel, snapPoint: undefined })).toEqual([600]);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("always returns descending heights, so the pan release can scan in order", () => {
|
|
84
|
+
const detents = resolveSheetDetents({ travel, scrollable: true, snapPoint: 40 });
|
|
85
|
+
expect(detents).toEqual([...detents].sort((a, b) => b - a));
|
|
86
|
+
expect(new Set(detents).size).toBe(detents.length);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SheetModal detent geometry — the pure half, split out so it can be specced
|
|
3
|
+
* without standing up a React Native renderer.
|
|
4
|
+
*
|
|
5
|
+
* A detent is a VISIBLE HEIGHT in points. The native sheet's `translateY`
|
|
6
|
+
* measures DOWN from fully-expanded, so a detent of `h` rests at `travel - h`,
|
|
7
|
+
* and `travel` itself is the fully-expanded sheet.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** Smallest height a native sheet is ever laid out at. */
|
|
11
|
+
export const minSheetHeight = 180;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Half-open detent for a sheet that owns a scrollport, as a fraction of its
|
|
15
|
+
* full height. Mirrors FloatingPanel so both native sheets drag alike.
|
|
16
|
+
*/
|
|
17
|
+
export const mediumDetent = 0.55;
|
|
18
|
+
|
|
19
|
+
export interface ResolveSheetDetentsOptions {
|
|
20
|
+
/** Full (expanded) height of the sheet, in points. */
|
|
21
|
+
travel: number;
|
|
22
|
+
/** The sheet owns a scrollport, so a half-open rest is meaningful. */
|
|
23
|
+
scrollable?: boolean;
|
|
24
|
+
/** Percent detent from the caller — the same prop the web Sheet takes. */
|
|
25
|
+
snapPoint?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The heights the sheet is allowed to rest at, largest first.
|
|
30
|
+
*
|
|
31
|
+
* MPO-68: `snapPoint` is a percent on the web Sheet (`snapPoints={[snapPoint]}`).
|
|
32
|
+
* Native honours the same prop by turning that percent into a detent, so a
|
|
33
|
+
* caller that writes `snapPoint={85}` gets an 85%-height rest on both twins
|
|
34
|
+
* rather than the prop being silently dropped on device.
|
|
35
|
+
*
|
|
36
|
+
* Anything shorter than `minSheetHeight` is discarded, because a sheet that
|
|
37
|
+
* rests below that is indistinguishable from a dismissed one. A sheet whose
|
|
38
|
+
* full height is already under the floor keeps its own height, otherwise it
|
|
39
|
+
* would have no detents at all.
|
|
40
|
+
*/
|
|
41
|
+
export function resolveSheetDetents({
|
|
42
|
+
travel,
|
|
43
|
+
scrollable,
|
|
44
|
+
snapPoint,
|
|
45
|
+
}: ResolveSheetDetentsOptions): number[] {
|
|
46
|
+
const points = [travel];
|
|
47
|
+
if (scrollable) {
|
|
48
|
+
points.push(Math.round(travel * mediumDetent));
|
|
49
|
+
}
|
|
50
|
+
if (typeof snapPoint === "number" && Number.isFinite(snapPoint) && snapPoint > 0) {
|
|
51
|
+
points.push(Math.round(travel * (Math.min(snapPoint, 100) / 100)));
|
|
52
|
+
}
|
|
53
|
+
const floor = Math.min(minSheetHeight, travel);
|
|
54
|
+
return Array.from(new Set(points.filter((h) => h >= floor))).sort((a, b) => b - a);
|
|
55
|
+
}
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
useWindowDimensions,
|
|
25
25
|
} from "react-native";
|
|
26
26
|
import { useResolvedKnobs } from "@multiplatform.one/theme";
|
|
27
|
+
import { minSheetHeight, resolveSheetDetents } from "./detents";
|
|
27
28
|
import { useTranslation } from "../shared/i18n";
|
|
28
29
|
import { useKeyboardInset } from "../shared/useKeyboardInset";
|
|
29
30
|
import { View, YStack } from "tamagui";
|
|
@@ -36,10 +37,6 @@ const sheetMaxHeightRatio = 0.85;
|
|
|
36
37
|
const bottomInset = Platform.OS === "ios" ? 24 : 16;
|
|
37
38
|
// Backdrop left visible above the sheet, so the scrim stays tappable.
|
|
38
39
|
const backdropStrip = 48;
|
|
39
|
-
const minSheetHeight = 180;
|
|
40
|
-
// Half-open detent for a sheet that owns a scrollport, as a fraction of its
|
|
41
|
-
// full height. Mirrors FloatingPanel so both native sheets drag alike.
|
|
42
|
-
const mediumDetent = 0.55;
|
|
43
40
|
// Released more than this far below the smallest detent, the sheet dismisses.
|
|
44
41
|
const dismissThreshold = 56;
|
|
45
42
|
const IN_DIALOG_REGION = { inDialogRegion: true } as const;
|
|
@@ -49,7 +46,7 @@ export function SheetModal({
|
|
|
49
46
|
onOpenChange,
|
|
50
47
|
children,
|
|
51
48
|
header,
|
|
52
|
-
snapPoint
|
|
49
|
+
snapPoint,
|
|
53
50
|
scrollable,
|
|
54
51
|
}: SheetModalProps) {
|
|
55
52
|
const { t } = useTranslation();
|
|
@@ -95,16 +92,20 @@ export function SheetModal({
|
|
|
95
92
|
*
|
|
96
93
|
* translateY measures DOWN from fully-expanded: 0 is the sheet at full
|
|
97
94
|
* height, `travel` is fully off-screen.
|
|
95
|
+
*
|
|
96
|
+
* The detent heights themselves live in ./detents.ts, pure, so the geometry
|
|
97
|
+
* is specced without a native renderer. 184746910 (MPO-71) rewrote this
|
|
98
|
+
* block from a pre-MPO-68 copy and silently dropped the `snapPoint` push,
|
|
99
|
+
* leaving the prop declared, documented and passed by four callers while
|
|
100
|
+
* doing nothing on device. Keep the math in one place so a rewrite of the
|
|
101
|
+
* animation cannot take the geometry with it again.
|
|
98
102
|
*/
|
|
99
103
|
const [measuredHeight, setMeasuredHeight] = useState(0);
|
|
100
104
|
const travel = measuredHeight || maxHeight;
|
|
101
|
-
const detents = useMemo(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
);
|
|
106
|
-
return Array.from(new Set(points)).sort((a, b) => b - a);
|
|
107
|
-
}, [scrollable, travel]);
|
|
105
|
+
const detents = useMemo(
|
|
106
|
+
() => resolveSheetDetents({ travel, scrollable, snapPoint }),
|
|
107
|
+
[travel, scrollable, snapPoint],
|
|
108
|
+
);
|
|
108
109
|
|
|
109
110
|
/*
|
|
110
111
|
* MPO-71 — park the sheet OFF-SCREEN before the modal ever presents.
|
package/src/SheetModal/index.tsx
CHANGED
|
@@ -21,7 +21,7 @@ export interface SheetModalProps {
|
|
|
21
21
|
children?: ReactNode;
|
|
22
22
|
/** Fixed content above the (scrollable) body, e.g. the sheet title. */
|
|
23
23
|
header?: ReactNode;
|
|
24
|
-
/** Percent snap point
|
|
24
|
+
/** Percent snap point. Web: the Sheet's snap point. Native: a drag detent. */
|
|
25
25
|
snapPoint?: number;
|
|
26
26
|
/** Wrap children in the sheet's scroll container. */
|
|
27
27
|
scrollable?: boolean;
|
|
@@ -59,7 +59,10 @@ function useSheetFrameFocus(open: boolean, frameRef: RefObject<HTMLElement | nul
|
|
|
59
59
|
if (!target || !target.isConnected) return;
|
|
60
60
|
const ae = document.activeElement;
|
|
61
61
|
const dropped =
|
|
62
|
-
!ae ||
|
|
62
|
+
!ae ||
|
|
63
|
+
ae === document.body ||
|
|
64
|
+
ae === document.documentElement ||
|
|
65
|
+
!!frameRef.current?.contains(ae);
|
|
63
66
|
if (dropped) {
|
|
64
67
|
target.focus({
|
|
65
68
|
preventScroll: true,
|
|
@@ -22,7 +22,7 @@ function radiusClasses(el: Element): string[] {
|
|
|
22
22
|
|
|
23
23
|
describe("Skeleton named exports", () => {
|
|
24
24
|
it("serves named exports only (no default) and keeps the namespace statics", () => {
|
|
25
|
-
expect(SkeletonExports
|
|
25
|
+
expect("default" in SkeletonExports).toBe(false);
|
|
26
26
|
expect(SkeletonExports.Skeleton).toBe(Skeleton);
|
|
27
27
|
expect(SkeletonExports.SkeletonText).toBe(SkeletonText);
|
|
28
28
|
expect(SkeletonExports.SkeletonCircle).toBe(SkeletonCircle);
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { fireEvent } from "@testing-library/react";
|
|
2
2
|
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
3
6
|
import { useState } from "react";
|
|
4
7
|
import { describe, expect, it, vi } from "vitest";
|
|
5
8
|
import { SortableList } from "./index";
|
|
6
9
|
|
|
10
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
|
|
7
12
|
const ITEMS = [
|
|
8
13
|
{ id: "mon", label: "Monday standup" },
|
|
9
14
|
{ id: "tue", label: "Tuesday review" },
|
|
@@ -14,10 +19,14 @@ const ITEMS = [
|
|
|
14
19
|
function Harness({
|
|
15
20
|
onChange = vi.fn(),
|
|
16
21
|
readOnly,
|
|
22
|
+
disabled,
|
|
23
|
+
compact,
|
|
17
24
|
canReorder,
|
|
18
25
|
}: {
|
|
19
26
|
onChange?: (next: typeof ITEMS) => void;
|
|
20
27
|
readOnly?: boolean;
|
|
28
|
+
disabled?: boolean;
|
|
29
|
+
compact?: boolean;
|
|
21
30
|
canReorder?: (item: (typeof ITEMS)[number], index: number) => boolean;
|
|
22
31
|
}) {
|
|
23
32
|
const [items, setItems] = useState(ITEMS);
|
|
@@ -28,6 +37,8 @@ function Harness({
|
|
|
28
37
|
getLabel={(item) => item.label}
|
|
29
38
|
canReorder={canReorder}
|
|
30
39
|
readOnly={readOnly}
|
|
40
|
+
disabled={disabled}
|
|
41
|
+
compact={compact}
|
|
31
42
|
onChange={(next) => {
|
|
32
43
|
setItems(next);
|
|
33
44
|
onChange(next);
|
|
@@ -119,4 +130,34 @@ describe("SortableList", () => {
|
|
|
119
130
|
const { container } = renderWithProviders(<Harness readOnly />);
|
|
120
131
|
expect(container.querySelectorAll("[data-sortable-handle]")).toHaveLength(0);
|
|
121
132
|
});
|
|
133
|
+
|
|
134
|
+
it("renders no handles when disabled (board 04)", () => {
|
|
135
|
+
const { container } = renderWithProviders(<Harness disabled />);
|
|
136
|
+
expect(container.querySelectorAll("[data-sortable-handle]")).toHaveLength(0);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("renders emptyMessage when there are no rows (board 04)", () => {
|
|
140
|
+
const { container } = renderWithProviders(
|
|
141
|
+
<SortableList
|
|
142
|
+
items={[]}
|
|
143
|
+
getId={(item: { id: string }) => item.id}
|
|
144
|
+
getLabel={(item: { label: string }) => item.label}
|
|
145
|
+
emptyMessage="No rows yet"
|
|
146
|
+
/>,
|
|
147
|
+
);
|
|
148
|
+
expect(container.textContent).toContain("No rows yet");
|
|
149
|
+
expect(container.querySelectorAll("[data-sortable-row]")).toHaveLength(0);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it("compact still renders grab handles (LC-76: density does not drop controls)", () => {
|
|
153
|
+
const { container } = renderWithProviders(<Harness compact />);
|
|
154
|
+
expect(container.querySelectorAll("[data-sortable-handle]")).toHaveLength(4);
|
|
155
|
+
expect(container.querySelector('[role="list"]')).toBeTruthy();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("spreads the outer radius fragment on the list frame, not the nested alias", () => {
|
|
159
|
+
const src = readFileSync(join(here, "index.tsx"), "utf8");
|
|
160
|
+
expect(src).toContain("{...knobProps.borderRadius}");
|
|
161
|
+
expect(src).not.toContain("knobProps.borderRadiusOuter");
|
|
162
|
+
});
|
|
122
163
|
});
|
|
@@ -21,13 +21,27 @@ const meta: Meta<typeof SortableList> = {
|
|
|
21
21
|
},
|
|
22
22
|
},
|
|
23
23
|
},
|
|
24
|
+
argTypes: {
|
|
25
|
+
disabled: { control: "boolean" },
|
|
26
|
+
readOnly: { control: "boolean" },
|
|
27
|
+
compact: { control: "boolean" },
|
|
28
|
+
disabledStyle: { control: "select", options: ["keepLabel", "dimWhole"] },
|
|
29
|
+
emptyMessage: { control: "text" },
|
|
30
|
+
},
|
|
24
31
|
};
|
|
25
32
|
export default meta;
|
|
26
33
|
|
|
27
34
|
type Story = StoryObj<typeof SortableList>;
|
|
28
35
|
|
|
29
|
-
function Demo(props: {
|
|
30
|
-
|
|
36
|
+
function Demo(props: {
|
|
37
|
+
readOnly?: boolean;
|
|
38
|
+
locked?: string;
|
|
39
|
+
disabled?: boolean;
|
|
40
|
+
compact?: boolean;
|
|
41
|
+
empty?: boolean;
|
|
42
|
+
disabledStyle?: "keepLabel" | "dimWhole";
|
|
43
|
+
}) {
|
|
44
|
+
const [items, setItems] = useState(props.empty ? [] : SEED);
|
|
31
45
|
return (
|
|
32
46
|
<SortableList
|
|
33
47
|
items={items}
|
|
@@ -35,6 +49,10 @@ function Demo(props: { readOnly?: boolean; locked?: string }) {
|
|
|
35
49
|
getLabel={(item) => item.label}
|
|
36
50
|
canReorder={props.locked ? (item) => item.id !== props.locked : undefined}
|
|
37
51
|
readOnly={props.readOnly}
|
|
52
|
+
disabled={props.disabled}
|
|
53
|
+
compact={props.compact}
|
|
54
|
+
disabledStyle={props.disabledStyle}
|
|
55
|
+
emptyMessage="No rows yet"
|
|
38
56
|
onChange={setItems}
|
|
39
57
|
aria-label="Sprint rituals"
|
|
40
58
|
/>
|
|
@@ -53,3 +71,18 @@ export const ReadOnly: Story = {
|
|
|
53
71
|
export const LockedRow: Story = {
|
|
54
72
|
render: () => <Demo locked="tue" />,
|
|
55
73
|
};
|
|
74
|
+
|
|
75
|
+
/** Board 04 — empty is a real state, not an error. */
|
|
76
|
+
export const Empty: Story = {
|
|
77
|
+
render: () => <Demo empty />,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/** Board 04 — disabled list keeps labels, drops the handles. */
|
|
81
|
+
export const Disabled: Story = {
|
|
82
|
+
render: () => <Demo disabled />,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/** Board 04 — compact nested well. */
|
|
86
|
+
export const Compact: Story = {
|
|
87
|
+
render: () => <Demo compact />,
|
|
88
|
+
};
|
|
@@ -47,7 +47,7 @@ export default meta;
|
|
|
47
47
|
|
|
48
48
|
type Story = StoryObj<typeof TreeSelect>;
|
|
49
49
|
|
|
50
|
-
export const
|
|
50
|
+
export const Default: Story = {
|
|
51
51
|
render: function Render() {
|
|
52
52
|
const [value, setValue] = useState("hdfc");
|
|
53
53
|
return (
|
|
@@ -67,6 +67,31 @@ export const Account: Story = {
|
|
|
67
67
|
},
|
|
68
68
|
};
|
|
69
69
|
|
|
70
|
+
export const Open: Story = {
|
|
71
|
+
render: function Render() {
|
|
72
|
+
const [value, setValue] = useState("hdfc");
|
|
73
|
+
const [open, setOpen] = useState(true);
|
|
74
|
+
return (
|
|
75
|
+
<YStack width={280} padding="$4">
|
|
76
|
+
<TreeSelect
|
|
77
|
+
label="Account"
|
|
78
|
+
nodes={accounts}
|
|
79
|
+
value={value}
|
|
80
|
+
onChange={setValue}
|
|
81
|
+
open={open}
|
|
82
|
+
onOpenChange={setOpen}
|
|
83
|
+
placeholder="Select account"
|
|
84
|
+
searchPlaceholder="Filter accounts"
|
|
85
|
+
emptyMessage="No matching accounts"
|
|
86
|
+
defaultExpandedIds={["assets", "current", "bank"]}
|
|
87
|
+
/>
|
|
88
|
+
</YStack>
|
|
89
|
+
);
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export const Account: Story = Default;
|
|
94
|
+
|
|
70
95
|
export const Empty: Story = {
|
|
71
96
|
render: () => (
|
|
72
97
|
<YStack width={280} padding="$4">
|
|
@@ -74,3 +99,53 @@ export const Empty: Story = {
|
|
|
74
99
|
</YStack>
|
|
75
100
|
),
|
|
76
101
|
};
|
|
102
|
+
|
|
103
|
+
export const Disabled: Story = {
|
|
104
|
+
render: () => (
|
|
105
|
+
<YStack width={280} padding="$4">
|
|
106
|
+
<TreeSelect
|
|
107
|
+
label="Account"
|
|
108
|
+
nodes={accounts}
|
|
109
|
+
value="hdfc"
|
|
110
|
+
disabled
|
|
111
|
+
placeholder="Select account"
|
|
112
|
+
/>
|
|
113
|
+
</YStack>
|
|
114
|
+
),
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export const WithError: Story = {
|
|
118
|
+
render: () => (
|
|
119
|
+
<YStack width={280} padding="$4">
|
|
120
|
+
<TreeSelect
|
|
121
|
+
label="Account"
|
|
122
|
+
nodes={accounts}
|
|
123
|
+
required
|
|
124
|
+
error="An account is required"
|
|
125
|
+
placeholder="Select account"
|
|
126
|
+
/>
|
|
127
|
+
</YStack>
|
|
128
|
+
),
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export const ReadOnly: Story = {
|
|
132
|
+
render: () => (
|
|
133
|
+
<YStack width={280} padding="$4">
|
|
134
|
+
<TreeSelect
|
|
135
|
+
label="Account"
|
|
136
|
+
nodes={accounts}
|
|
137
|
+
value="hdfc"
|
|
138
|
+
readOnly
|
|
139
|
+
placeholder="Select account"
|
|
140
|
+
/>
|
|
141
|
+
</YStack>
|
|
142
|
+
),
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
export const Skeleton: Story = {
|
|
146
|
+
render: () => (
|
|
147
|
+
<YStack width={280} padding="$4">
|
|
148
|
+
<TreeSelect label="Account" nodes={accounts} skeleton placeholder="Select account" />
|
|
149
|
+
</YStack>
|
|
150
|
+
),
|
|
151
|
+
};
|
package/src/Wheel/Wheel.spec.tsx
CHANGED
|
@@ -272,7 +272,7 @@ describe("Wheel design-law (MPO-60)", () => {
|
|
|
272
272
|
it("exposes named exports and no default", async () => {
|
|
273
273
|
const mod = await import("./index");
|
|
274
274
|
expect(mod.Wheel).toBe(Wheel);
|
|
275
|
-
expect(mod
|
|
275
|
+
expect("default" in mod).toBe(false);
|
|
276
276
|
});
|
|
277
277
|
|
|
278
278
|
it("selected row type stays on body weight 400, never 600 (R7 / design-audit)", () => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { renderWithProviders } from "@multiplatform.one/test-utils";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
3
|
import { Code, CodeInline } from "./index";
|
|
4
4
|
|
|
5
5
|
describe("Code", () => {
|
|
@@ -21,6 +21,15 @@ describe("Code", () => {
|
|
|
21
21
|
const codeElement = container.querySelector("code");
|
|
22
22
|
expect(codeElement).toBeDefined();
|
|
23
23
|
});
|
|
24
|
+
|
|
25
|
+
it("keeps data-code-font and does not emit a dataset attribute", () => {
|
|
26
|
+
const { container } = renderWithProviders(<Code>const test = "hello"</Code>);
|
|
27
|
+
const codeElement = container.querySelector("code");
|
|
28
|
+
expect(codeElement).toBeTruthy();
|
|
29
|
+
expect(codeElement!.getAttribute("data-code-font")).toBe("mono");
|
|
30
|
+
// React lowercases an unknown dataSet prop to dataset="[object Object]".
|
|
31
|
+
expect(codeElement!.hasAttribute("dataset")).toBe(false);
|
|
32
|
+
});
|
|
24
33
|
});
|
|
25
34
|
|
|
26
35
|
describe("CodeInline", () => {
|
|
@@ -42,4 +51,21 @@ describe("CodeInline", () => {
|
|
|
42
51
|
const codeElement = container.querySelector("code");
|
|
43
52
|
expect(codeElement).toBeDefined();
|
|
44
53
|
});
|
|
54
|
+
|
|
55
|
+
it("emits data-code-font on web without a dataSet host-prop leak", () => {
|
|
56
|
+
const leak = /does not recognize the `dataSet` prop on a DOM element/;
|
|
57
|
+
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
58
|
+
try {
|
|
59
|
+
const { container } = renderWithProviders(<CodeInline>npm install</CodeInline>);
|
|
60
|
+
const codeElement = container.querySelector("code");
|
|
61
|
+
expect(codeElement).toBeDefined();
|
|
62
|
+
expect(codeElement?.getAttribute("data-code-font")).toBe("mono");
|
|
63
|
+
const leaks = errorSpy.mock.calls
|
|
64
|
+
.map((args) => args.map(String).join(" "))
|
|
65
|
+
.filter((message) => leak.test(message));
|
|
66
|
+
expect(leaks).toEqual([]);
|
|
67
|
+
} finally {
|
|
68
|
+
errorSpy.mockRestore();
|
|
69
|
+
}
|
|
70
|
+
});
|
|
45
71
|
});
|
package/src/code/Code/index.tsx
CHANGED
|
@@ -40,7 +40,6 @@ export const Code = CodeFrame.styleable((props, ref) => {
|
|
|
40
40
|
fontFamily="$mono"
|
|
41
41
|
letterSpacing={0}
|
|
42
42
|
data-code-font="mono"
|
|
43
|
-
{...({ dataSet: { codeFont: "mono" } } as object)}
|
|
44
43
|
/>
|
|
45
44
|
);
|
|
46
45
|
});
|
|
@@ -57,6 +56,9 @@ const CodeInlineFrame = styled(Paragraph, {
|
|
|
57
56
|
backgroundColor: componentColors.code.background,
|
|
58
57
|
cursor: "inherit",
|
|
59
58
|
borderRadius: "$3",
|
|
59
|
+
// Pin (dial: surrounding prose): 0.9× size / 0.7× leading. Size reaches
|
|
60
|
+
// the chip through context, not sizeToken. Lettered in spec § Code and
|
|
61
|
+
// CodeInline; these ratios are the house metrics already shipping.
|
|
60
62
|
fontSize: "90%" as CSSProperties["fontSize"],
|
|
61
63
|
lineHeight: "70%" as CSSProperties["lineHeight"],
|
|
62
64
|
paddingHorizontal: "0.6%",
|
|
@@ -79,7 +81,12 @@ export const CodeInline = CodeInlineFrame.styleable((props, ref) => {
|
|
|
79
81
|
fontFamily="$mono"
|
|
80
82
|
letterSpacing={0}
|
|
81
83
|
data-code-font="mono"
|
|
82
|
-
|
|
84
|
+
// react-native-web reads dataSet; React-DOM warns on the camel host
|
|
85
|
+
// prop. Keep the kebab attribute on every target; spread dataSet
|
|
86
|
+
// only when TAMAGUI_TARGET is native.
|
|
87
|
+
{...(process.env.TAMAGUI_TARGET === "native"
|
|
88
|
+
? ({ dataSet: { codeFont: "mono" } } as object)
|
|
89
|
+
: {})}
|
|
83
90
|
/>
|
|
84
91
|
);
|
|
85
92
|
});
|
|
@@ -115,9 +115,18 @@ describe("HighlightedCode", () => {
|
|
|
115
115
|
<HighlightedCode tokens={tokens} />
|
|
116
116
|
</Preset>,
|
|
117
117
|
);
|
|
118
|
-
|
|
118
|
+
// querySelector("span") used to land on renderWithProviders' Theme
|
|
119
|
+
// wrapper (`t_light _dsp_contents`), never the code line, so this guard
|
|
120
|
+
// asserted null and had been red on main. Select the line itself.
|
|
121
|
+
const line = container.querySelector("[data-code-font]") as HTMLElement | null;
|
|
119
122
|
expect(line).toBeTruthy();
|
|
120
123
|
expect(container.textContent).toContain("const");
|
|
121
124
|
expect(line!.getAttribute("data-code-font")).toBe("mono");
|
|
125
|
+
// The actual LC-07 pin: bodyFont serif / fontWeight bold must not reach
|
|
126
|
+
// code text, so the line keeps the mono family class.
|
|
127
|
+
expect(line!.className).toContain("font_mono");
|
|
128
|
+
// React does not recognise `dataSet`; leaving it on the line logged an
|
|
129
|
+
// unknown-prop error and wrote dataset="[object Object]" on every node.
|
|
130
|
+
expect(container.querySelectorAll("[dataset]")).toHaveLength(0);
|
|
122
131
|
});
|
|
123
132
|
});
|