@recursica/adapter-mantine-v8 1.3.5 → 1.3.6

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @recursica/adapter-mantine-v8
2
2
 
3
+ ## 1.3.6
4
+
5
+ ### Patch Changes
6
+
7
+ - f21301b: Added tests and notes confirming Checkbox, Radio and Switch put their accessible name on the native input. No behavior change.
8
+
3
9
  ## 1.3.5
4
10
 
5
11
  ### Patch Changes
package/package.json CHANGED
@@ -12,7 +12,7 @@
12
12
  "type": "git",
13
13
  "url": "git+https://github.com/borderux/recursica-adapter-mantine-v8.git"
14
14
  },
15
- "version": "1.3.5",
15
+ "version": "1.3.6",
16
16
  "publishConfig": {
17
17
  "access": "public"
18
18
  },
@@ -23,3 +23,7 @@ This calculation ensures that the optical center of the `.inner` checkmark vecto
23
23
  ### Checkbox.Group Overrides
24
24
 
25
25
  To decouple `<CheckboxGroup>` away from `<Input.Wrapper>`, we explicitly extract the raw array execution mapped correctly against our identical `RecursicaFormControlWrapperProps` schema structurally!
26
+
27
+ ### Accessible name lands on the native `<input>` (verified 2026-10-15)
28
+
29
+ `RequireAccessibleLabel` enforces a name at compile time (`label`, `aria-label` or `aria-labelledby`). Mantine forwards `aria-*` to the native `<input>` and auto-generates the `id` that links `<label for>`, so no adapter code is needed. This is the behavior the other adapters must match. Covered by `src/components/NativeInputA11y.dom.test.tsx`, which asserts it for Checkbox, Radio and Switch.
@@ -0,0 +1,60 @@
1
+ import React from "react";
2
+ import { describe, it, expect } from "vitest";
3
+ import { createRoot } from "react-dom/client";
4
+ import { flushSync } from "react-dom";
5
+ import { Checkbox } from "./Checkbox/Checkbox";
6
+ import { Radio } from "./Radio/Radio";
7
+ import { Switch } from "./Switch/Switch";
8
+ import { MantineProvider } from "@mantine/core";
9
+ const wrap = (n: React.ReactElement) => <MantineProvider>{n}</MantineProvider>;
10
+
11
+ // Where the naming attributes land matters, not just that they render: a screen reader reads
12
+ // the native <input>, so aria-* on a wrapper leaves the control unnamed.
13
+ function render(node: React.ReactElement): HTMLElement {
14
+ const container = document.createElement("div");
15
+ document.body.appendChild(container);
16
+ flushSync(() => createRoot(container).render(wrap(node)));
17
+ return container;
18
+ }
19
+
20
+ const controls = [
21
+ ["Checkbox", Checkbox],
22
+ ["Radio", Radio],
23
+ ["Switch", Switch],
24
+ ] as const;
25
+
26
+ describe.each(controls)("%s accessible name", (_name, Control) => {
27
+ // The union type under test is already enforced at compile time; cast to exercise each branch.
28
+ const C = Control as unknown as React.ComponentType<Record<string, unknown>>;
29
+
30
+ it("puts aria-label on the native input only", () => {
31
+ const c = render(<C aria-label="Select all" />);
32
+ expect(c.querySelector("input")!.getAttribute("aria-label")).toBe(
33
+ "Select all",
34
+ );
35
+ expect(c.querySelectorAll("[aria-label]")).toHaveLength(1);
36
+ });
37
+
38
+ it("puts aria-labelledby and aria-describedby on the native input only", () => {
39
+ const c = render(<C aria-labelledby="a" aria-describedby="b" />);
40
+ const input = c.querySelector("input")!;
41
+ expect(input.getAttribute("aria-labelledby")).toBe("a");
42
+ expect(input.getAttribute("aria-describedby")).toBe("b");
43
+ expect(c.querySelectorAll("[aria-labelledby]")).toHaveLength(1);
44
+ expect(c.querySelectorAll("[aria-describedby]")).toHaveLength(1);
45
+ });
46
+
47
+ it("links a visible label to the input without a caller-supplied id", () => {
48
+ const c = render(<C label="Hello" />);
49
+ const input = c.querySelector("input")!;
50
+ const label = c.querySelector("label")!;
51
+ expect(input.id).not.toBe("");
52
+ expect(label.getAttribute("for")).toBe(input.id);
53
+ });
54
+
55
+ it("keeps a caller-supplied id", () => {
56
+ const c = render(<C label="Hello" id="mine" />);
57
+ expect(c.querySelector("input")!.id).toBe("mine");
58
+ expect(c.querySelector("label")!.getAttribute("for")).toBe("mine");
59
+ });
60
+ });
@@ -0,0 +1,5 @@
1
+ # Radio Implementation Notes
2
+
3
+ ### Accessible name lands on the native `<input>` (verified 2026-10-15)
4
+
5
+ `RequireAccessibleLabel` enforces a name at compile time (`label`, `aria-label` or `aria-labelledby`). Mantine forwards `aria-*` to the native `<input>` and auto-generates the `id` that links `<label for>`, so no adapter code is needed. This is the behavior the other adapters must match. Covered by `src/components/NativeInputA11y.dom.test.tsx`, which asserts it for Checkbox, Radio and Switch.
@@ -62,3 +62,7 @@ mui-only bug: same code pattern, same result, here. Fixed by not capping the gro
62
62
  width at all — each switch's own label already wraps at 200px via `.labelWrapper`, so no
63
63
  group-level cap is needed. Verified live in both adapters: side-by-side now shows the label
64
64
  column at left with the switches beside it; stacked layout unaffected.
65
+
66
+ ### Accessible name lands on the native `<input>` (verified 2026-10-15)
67
+
68
+ `RequireAccessibleLabel` enforces a name at compile time (`label`, `aria-label` or `aria-labelledby`). Mantine forwards `aria-*` to the native `<input>` and auto-generates the `id` that links `<label for>`, so no adapter code is needed. This is the behavior the other adapters must match. Covered by `src/components/NativeInputA11y.dom.test.tsx`, which asserts it for Checkbox, Radio and Switch.