react-luna-form 0.0.92 → 0.0.94

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.
@@ -46,7 +46,7 @@ Props are applied in this order:
46
46
  <Component
47
47
  {...dataAttributes} // advanced.data, as data-* attributes
48
48
  {...commonProps} // id, name, placeholder, disabled, required, and what the field family adds
49
- {...ariaAttributes} // advanced.aria, and aria-invalid, aria-errormessage
49
+ {...ariaAttributes} // advanced.aria, aria-labelledby, aria-invalid, aria-errormessage
50
50
  {...inputProps} // the prepared value or defaultValue
51
51
  onBlur={onBlur}
52
52
  onChange={onChange}
@@ -98,6 +98,34 @@ function PeoplePicker({ id, ...props }: React.ComponentProps<'input'>) {
98
98
  The same applies when you build on a component library: whatever primitive
99
99
  renders the focusable element is where the `id` belongs.
100
100
 
101
+ ## A group of controls takes its name from `aria-labelledby`
102
+
103
+ A `<label for>` names only an element a label can name: an `input`, a `select`,
104
+ a `textarea`, a `button`. A component drawn as several controls, such as a row
105
+ of chips, has no one element the label belongs to, and putting the `id` on one
106
+ of its buttons would rename that button after the field.
107
+
108
+ So a field that renders a label also hands its component `aria-labelledby`,
109
+ pointing at that label, whose `id` is the field's `name` followed by `-label`.
110
+ Put it, with the `id`, on the element that holds the controls, and give that
111
+ element a role that can carry a name:
112
+
113
+ ```tsx
114
+ function Chips({ id, 'aria-labelledby': labelledBy, ...props }: ChipsProps) {
115
+ return (
116
+ <div aria-labelledby={labelledBy} id={id} role="group">
117
+ {/* one button per option */}
118
+ </div>
119
+ )
120
+ }
121
+ ```
122
+
123
+ A component that forwards its props to a single input gets it for free, and the
124
+ input is named exactly as its `for` already named it. A field without a `label`
125
+ or a `name` renders no label, and gets no `aria-labelledby`. An `aria.label` or
126
+ `aria.labelledby` in `advanced` is the author's name for the field, and the form
127
+ then sends that instead.
128
+
101
129
  ## Clearing a value on a select-family type
102
130
 
103
131
  If you register a custom input under a `select` variant, be aware that an
@@ -274,6 +302,8 @@ Before shipping a custom input:
274
302
 
275
303
  - Its `type` string matches `defineCustomInput` exactly, character for character
276
304
  - The `id` it receives reaches the focusable element
305
+ - If it renders several controls, the `aria-labelledby` it receives reaches
306
+ the element that holds them, which has a role such as `group`
277
307
  - It forwards or calls the `onChange` and `onBlur` it receives
278
308
  - If it is a select variant, it does not rely on `''` to clear
279
309
  - If it renders `input/date`, it reads and emits `yyyy-MM-dd`, or `[from, to]`
@@ -20,6 +20,7 @@ The `advanced` property customizes structural interaction rules and layout rende
20
20
 
21
21
  - **`title`** _(string)_: Names one item of the collection. The header of the second row then reads `Contact 2` and its controls announce `Collapse Contact 2` and `Remove Contact 2`.
22
22
  It falls back to the list's `label`, and then to its `name`, so a list called `contacts` with neither captions its rows `contacts 1`. A [`preview.label`](#collapsed-previews-preview-object) replaces the caption where it resolves; this stays the fallback for the rows where it does not.
23
+ A row with a `preview.label` is also named after it, so its controls announce `Collapse Ana (Contact 2)` and `Remove Ana (Contact item 2)`: the name holds the text the row shows, and two rows in different lists that share a title stay apart. The name follows the label as the user edits it. A row whose label comes out empty shows the caption instead, and its name repeats it: `Collapse Contact 2 (Contact 2)`.
23
24
  Like `action`, it is copy the form declares, so it is translated through the dictionary — see [Localization](#localization).
24
25
  - **`action`** _(string)_: A custom label applied strictly to the generic "Add item" button rendering at the end of the collection (e.g. `"Add email address"`).
25
26
  A custom label has to be translated by the form; the built-in `Add item` default is already translated by the library — see [Localization](#localization).
@@ -116,6 +117,7 @@ Every piece of copy a list renders goes through the `translations` dictionary, k
116
117
 
117
118
  - `Add item`, the add-button label when no `action` is declared.
118
119
  - `Expand {label} {index}`, `Collapse {label} {index}` and `Remove {label} item {index}`, the accessible names of the item controls.
120
+ - `Expand {name} ({label} {index})`, `Collapse {name} ({label} {index})` and `Remove {name} ({label} item {index})`, the same names for a row that has a [`preview.label`](#collapsed-previews-preview-object); `{name}` is the text that label shows.
119
121
 
120
122
  ```json
121
123
  {
@@ -176,6 +178,8 @@ The accessible names are single keys carrying `{label}` and `{index}` placeholde
176
178
 
177
179
  Left untranslated, the template fills in to the same English text it replaced.
178
180
 
181
+ `{name}` may sit anywhere in a translation, the same as the other placeholders. A translation of a `{name}` key that leaves `{name}` out is not used: the row is named with the matching key without it, `Expand {label} {index}` and so on, translated as that key is.
182
+
179
183
  ---
180
184
 
181
185
  ## Complete Example Reference
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-luna-form",
3
- "version": "0.0.92",
3
+ "version": "0.0.94",
4
4
  "description": "A React library for building forms based on JSON",
5
5
  "main": "./dist/client/cjs/index.js",
6
6
  "module": "./dist/client/esm/index.js",
@@ -29,7 +29,7 @@
29
29
  "zod": "^4.0.0"
30
30
  },
31
31
  "devDependencies": {
32
- "@luna-form/core": "0.0.92",
32
+ "@luna-form/core": "0.0.94",
33
33
  "@types/react": "19.3.0",
34
34
  "@types/react-dom": "19.3.0",
35
35
  "eslint-plugin-react": "7.37.5",