@delacour/react-native-ui 0.4.1-alpha.20261021114607 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@delacour/react-native-ui",
3
- "version": "0.4.1-alpha.20261021114607",
3
+ "version": "0.4.1",
4
4
  "description": "React Native UI components — Uniwind, Reanimated, Gesture Handler",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -427,7 +427,7 @@ describe("worklet hygiene", () => {
427
427
  // parameter defaults — `max = FLIP_CARD_RUBBER_BAND` threw "Property doesn't exist"
428
428
  // on the UI thread the first time a swipe card was dragged.
429
429
  test("no worklet reads a module binding from a parameter default", () => {
430
- const source = readFileSync(join(import.meta.dir, "flip-card.variants.ts"), "utf8");
430
+ const source = readFileSync(join(import.meta.dirname, "flip-card.variants.ts"), "utf8");
431
431
  const offenders = [...source.matchAll(/export function (\w+)\(([^)]*)\)[^{]*\{\s*"worklet";/g)]
432
432
  .filter(([, , params]) => /=\s*[A-Za-z_]/.test(params ?? ""))
433
433
  .map(([, name]) => name);
@@ -1,4 +1,6 @@
1
1
  import { describe, expect, test } from "bun:test";
2
+ import { readFileSync } from "node:fs";
3
+ import { join } from "node:path";
2
4
  import { Children, createElement, isValidElement } from "react";
3
5
  import {
4
6
  GRID_ITEM_BACKGROUND_DISPLAY_NAME,
@@ -269,8 +271,8 @@ describe("orderGridItemChildren", () => {
269
271
  });
270
272
 
271
273
  describe("GRID_ITEM_BACKGROUND_DISPLAY_NAME", () => {
272
- test("is the name grid-item-background.tsx assigns", async () => {
273
- const source = await Bun.file(new URL("./grid-item-background.tsx", import.meta.url)).text();
274
- expect(source).toInclude(`GridItemBackground.displayName = "${GRID_ITEM_BACKGROUND_DISPLAY_NAME}";`);
274
+ test("is the name grid-item-background.tsx assigns", () => {
275
+ const source = readFileSync(join(import.meta.dirname, "grid-item-background.tsx"), "utf-8");
276
+ expect(source).toContain(`GridItemBackground.displayName = "${GRID_ITEM_BACKGROUND_DISPLAY_NAME}";`);
275
277
  });
276
278
  });
@@ -240,7 +240,7 @@ describe("resolveWeekStart", () => {
240
240
 
241
241
  test("auto falls back to Monday for a locale the runtime cannot read", () => {
242
242
  expect(resolveWeekStart("not a locale", "auto")).toBe(1);
243
- expect(resolveWeekStart(undefined, "auto")).toBeOneOf([0, 1, 2, 3, 4, 5, 6]);
243
+ expect([0, 1, 2, 3, 4, 5, 6]).toContain(resolveWeekStart(undefined, "auto"));
244
244
  });
245
245
  });
246
246
 
@@ -277,8 +277,15 @@ describe("msUntilNextMidnight", () => {
277
277
 
278
278
  test("follows the clock across daylight-saving nights", () => {
279
279
  // The night of 4–5 April 2026 is 25 hours long in Auckland; 26–27 September is 23.
280
- expect(msUntilNextMidnight(new Date(2026, 3, 5, 0, 0, 0))).toBe(25 * 3_600_000);
281
- expect(msUntilNextMidnight(new Date(2026, 8, 27, 0, 0, 0))).toBe(23 * 3_600_000);
280
+ // Read off the zone's offsets rather than written down: a runner that
281
+ // sandboxes `process.env` keeps the machine's zone, where those nights
282
+ // may be 24 hours long.
283
+ const night = (year: number, month: number, day: number) =>
284
+ 24 * 3_600_000 +
285
+ (new Date(year, month, day + 1).getTimezoneOffset() - new Date(year, month, day).getTimezoneOffset()) * 60_000;
286
+
287
+ expect(msUntilNextMidnight(new Date(2026, 3, 5, 0, 0, 0))).toBe(night(2026, 3, 5));
288
+ expect(msUntilNextMidnight(new Date(2026, 8, 27, 0, 0, 0))).toBe(night(2026, 8, 27));
282
289
  });
283
290
 
284
291
  test("is always positive, so a timer can never fire in a loop", () => {
@@ -196,14 +196,14 @@ describe("every slot", () => {
196
196
 
197
197
  test("resolves to a string, never undefined", () => {
198
198
  for (const [name, slot] of declared(screenVariants())) {
199
- expect(typeof slot(), `slot "${name}"`).toBe("string");
199
+ expect([name, typeof slot()]).toEqual([name, "string"]);
200
200
  }
201
201
  });
202
202
 
203
203
  test("resolves to a string at either placement", () => {
204
204
  for (const placement of SCREEN_PLACEMENTS) {
205
205
  for (const [name, slot] of declared(screenVariants({ placement }))) {
206
- expect(typeof slot(), `slot "${name}" at placement "${placement}"`).toBe("string");
206
+ expect([placement, name, typeof slot()]).toEqual([placement, name, "string"]);
207
207
  }
208
208
  }
209
209
  });
@@ -279,8 +279,10 @@ describe("boundsOf", () => {
279
279
  describe("toSVG", () => {
280
280
  test("the default is the whole pad, black ink, no background", () => {
281
281
  const svg = toSVG(VALUE);
282
- expect(svg).toStartWith('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 100" width="200" height="100">');
283
- expect(svg).toEndWith("</svg>");
282
+ expect(
283
+ svg.startsWith('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 100" width="200" height="100">')
284
+ ).toBe(true);
285
+ expect(svg.endsWith("</svg>")).toBe(true);
284
286
  expect(svg).not.toContain("<rect");
285
287
  expect(svg.match(/<path /g)).toHaveLength(2);
286
288
  expect(svg).toContain(`stroke="${SIGNATURE_EXPORT_STROKE_COLOR}"`);
@@ -1,4 +1,4 @@
1
- import { afterEach, beforeEach, describe, expect, mock, spyOn, test } from "bun:test";
1
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
2
  import { createElement, Fragment, type ReactNode } from "react";
3
3
  import {
4
4
  collectTableHeaderLabels,
@@ -22,14 +22,18 @@ import {
22
22
  tableVariants,
23
23
  } from "./table.variants";
24
24
 
25
- let warn: ReturnType<typeof spyOn>;
25
+ const consoleWarn = console.warn;
26
+ let warnings: unknown[][] = [];
26
27
 
27
28
  beforeEach(() => {
28
- warn = spyOn(console, "warn").mockImplementation(mock(() => undefined));
29
+ warnings = [];
30
+ console.warn = (...args: unknown[]) => {
31
+ warnings.push(args);
32
+ };
29
33
  });
30
34
 
31
35
  afterEach(() => {
32
- warn.mockRestore();
36
+ console.warn = consoleWarn;
33
37
  });
34
38
 
35
39
  describe("resolveTableColumn", () => {
@@ -58,23 +62,23 @@ describe("resolveTableColumn", () => {
58
62
  test("rejects a width of zero or less, warns, and falls back to flex 1", () => {
59
63
  expect(resolveTableColumn({ width: 0 })).toEqual({ align: "start", flex: 1, kind: "flex" });
60
64
  expect(resolveTableColumn({ width: -10, align: "end" })).toEqual({ align: "end", flex: 1, kind: "flex" });
61
- expect(warn).toHaveBeenCalledTimes(2);
65
+ expect(warnings).toHaveLength(2);
62
66
  });
63
67
 
64
68
  test("rejects a non-finite width", () => {
65
69
  expect(resolveTableColumn({ width: Number.NaN })).toEqual({ align: "start", flex: 1, kind: "flex" });
66
- expect(warn).toHaveBeenCalledTimes(1);
70
+ expect(warnings).toHaveLength(1);
67
71
  });
68
72
 
69
73
  test("rejects a flex of zero or less, falling back to 1", () => {
70
74
  expect(resolveTableColumn({ flex: 0 })).toEqual({ align: "start", flex: 1, kind: "flex" });
71
- expect(warn).toHaveBeenCalledTimes(1);
75
+ expect(warnings).toHaveLength(1);
72
76
  });
73
77
 
74
78
  test("does not warn on a valid column", () => {
75
79
  resolveTableColumn({ width: 1 });
76
80
  resolveTableColumn({ flex: 0.5 });
77
- expect(warn).not.toHaveBeenCalled();
81
+ expect(warnings).toHaveLength(0);
78
82
  });
79
83
  });
80
84
 
@@ -59,8 +59,8 @@ describe("the CSS readers", () => {
59
59
  describe("the geometry block", () => {
60
60
  test("restates every number tokens.css declares, with the same value", () => {
61
61
  for (const [name, value] of NATIVE) {
62
- expect(TOKENS.get(name), `tokens.css declares no ${name}`).toBeDefined();
63
- expect(TOKENS.get(name), `${name} disagrees between tokens.css and theme.css`).toBe(value);
62
+ // Paired with its name, so a failure says which token disagrees.
63
+ expect([name, TOKENS.get(name)]).toEqual([name, value]);
64
64
  }
65
65
  });
66
66
 
@@ -86,9 +86,7 @@ describe("the geometry block", () => {
86
86
  ...(["sm", "md", "lg"] as const).map((step) => `--spacing-fab-${step}`),
87
87
  ];
88
88
 
89
- for (const name of required) {
90
- expect(NATIVE.has(name), `theme.css's @variant native declares no ${name}`).toBe(true);
91
- }
89
+ expect(required.filter((name) => !NATIVE.has(name))).toEqual([]);
92
90
  });
93
91
 
94
92
  // The generic corner scale is derived from `--radius` in `tokens.css`'s
@@ -108,7 +106,7 @@ describe("the font families", () => {
108
106
  const declared = variantDeclarations(platform);
109
107
 
110
108
  for (const token of ["--font-sans", "--font-serif", "--font-mono", "--font-heading"] as const) {
111
- expect(declared.get(token), `@variant ${platform} declares no ${token}`).toBeDefined();
109
+ expect([platform, token, declared.has(token)]).toEqual([platform, token, true]);
112
110
  // React Native's `fontFamily` takes one name and no fallback
113
111
  // list, so a comma-separated web stack resolves to nothing.
114
112
  expect(declared.get(token)).not.toContain(",");