@jonmilner/ui 0.3.3 → 0.4.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.
@@ -1,5 +1,5 @@
1
1
  import "./index.mjs";
2
- import { AriaAttributes, ComponentPropsWithRef } from "react";
2
+ import { ComponentPropsWithRef } from "react";
3
3
  //#region src/SearchField/client.d.ts
4
- export type SearchFieldInputProps = Pick<ComponentPropsWithRef<"input">, "ref" | "id" | "autoFocus" | "autoComplete" | "spellCheck" | "enterKeyHint" | "maxLength" | "onKeyDown" | Exclude<keyof AriaAttributes, "aria-label" | "aria-labelledby" | "aria-invalid" | "aria-describedby" | "aria-errormessage" | "aria-disabled" | "aria-required" | "aria-readonly" | "aria-hidden">>;
4
+ export type SearchFieldInputProps = Pick<ComponentPropsWithRef<"input">, "ref" | "id" | "autoFocus" | "autoComplete" | "spellCheck" | "enterKeyHint" | "maxLength" | "onKeyDown" | "aria-atomic" | "aria-busy" | "aria-controls" | "aria-current" | "aria-details" | "aria-flowto" | "aria-keyshortcuts" | "aria-live" | "aria-owns" | "aria-relevant" | "aria-roledescription" | "aria-activedescendant" | "aria-autocomplete" | "aria-placeholder">;
5
5
  //#endregion
@@ -25,7 +25,7 @@ A form field for a search query: an `<input type="search">` (role `searchbox`) w
25
25
  - `disabled` uses the native `disabled`: the input leaves the tab order and does not submit, with a dashed border. `required` sets the native `required`.
26
26
  - `name` submits the query with its form. An uncontrolled field does not follow a native form reset; control it if the form resets.
27
27
  - Style props, `className` and `style` go to the `Field` around the label, input and messages, not to the input.
28
- - `inputProps` reach the input: `ref`, `id`, `autoFocus`, `autoComplete`, `spellCheck`, `enterKeyHint`, `maxLength`, `onKeyDown` and `aria-*` such as `aria-controls` (ADR 0093). `label` owns the name, Field owns `aria-invalid`, `aria-describedby` and `aria-errormessage`, and `disabled` and `required` own their state, so those, `aria-readonly` and `aria-hidden` are not among them.
28
+ - `inputProps` reach the input: `ref`, `id`, `autoFocus`, `autoComplete`, `spellCheck`, `enterKeyHint`, `maxLength`, `onKeyDown`, and only the `aria-*` valid on a search box (ADR 0093, 0108): `aria-controls`, `aria-activedescendant`, `aria-autocomplete`, `aria-placeholder`, `aria-details`, `aria-keyshortcuts`, `aria-owns`, `aria-flowto`, `aria-current`, `aria-roledescription`, `aria-busy`, `aria-live`, `aria-atomic` and `aria-relevant`. `label` owns the name, Field owns `aria-invalid`, `aria-describedby` and `aria-errormessage`, and `disabled` and `required` own their state, so those, `aria-readonly`, `aria-hidden` and the single-line input's `aria-multiline` are not among them. `aria-expanded` and `aria-haspopup` are not either: a search box with a popup list is a Combobox. The type closes the set, so an object literal with another `aria-*` is a type error; nothing strips one at runtime. Hint with `placeholder`, not `aria-placeholder`: the input has a native placeholder.
29
29
  - A consumer `onKeyDown` runs before the field's own. Escape still clears unless that handler called `event.preventDefault()`. A prevented Escape also propagates, so a surrounding Dialog closes on it.
30
30
  - A consumer `ref` gets the input, and the clear button still returns focus to it. A consumer `id` takes over from Field's generated one at hydration, and the label follows it.
31
31
  - Wrapping the field in a search landmark (`<search>` or `role="search"`, with the form around it) is the page's job, not the field's.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "./manifest.schema.json",
3
3
  "package": "@jonmilner/ui",
4
- "version": "0.3.3",
4
+ "version": "0.4.0",
5
5
  "components": [
6
6
  {
7
7
  "name": "AlertDialog",
@@ -0,0 +1,26 @@
1
+ # 0.4.0
2
+
3
+ `SearchFieldInputProps` accepts only the `aria-*` valid on role `searchbox`, so an `inputProps` that passes any other `aria-*` to `SearchField` no longer type-checks (decision 0108). Most of them already failed axe's `aria-allowed-attr` on the input; `aria-haspopup` (deprecated as a global in ARIA 1.2) and the ARIA 1.3 draft's `aria-description`, `aria-braillelabel` and `aria-brailleroledescription` passed axe but are outside ARIA 1.2's `searchbox`.
4
+
5
+ Still accepted: `aria-controls`, `aria-activedescendant`, `aria-autocomplete`, `aria-placeholder`, `aria-details`, `aria-keyshortcuts`, `aria-owns`, `aria-flowto`, `aria-current`, `aria-roledescription`, `aria-busy`, `aria-live`, `aria-atomic` and `aria-relevant`. Removed: `aria-braillelabel`, `aria-brailleroledescription`, `aria-checked`, `aria-colcount`, `aria-colindex`, `aria-colindextext`, `aria-colspan`, `aria-description`, `aria-dropeffect`, `aria-expanded`, `aria-grabbed`, `aria-haspopup`, `aria-level`, `aria-modal`, `aria-multiline`, `aria-multiselectable`, `aria-orientation`, `aria-posinset`, `aria-pressed`, `aria-rowcount`, `aria-rowindex`, `aria-rowindextext`, `aria-rowspan`, `aria-selected`, `aria-setsize`, `aria-sort`, `aria-valuemax`, `aria-valuemin`, `aria-valuenow` and `aria-valuetext`.
6
+
7
+ ## Find
8
+
9
+ In the app's source (`*.tsx`), every `inputProps` on a `SearchField`, and every object typed `SearchFieldInputProps`: regex `<SearchField\b|SearchFieldInputProps`. In each, and in any variable or helper that builds the `inputProps` object, look for an `aria-*` key outside the accepted list above. The typecheck names one in an inline object literal (`Object literal may only specify known properties, and '"aria-expanded"' does not exist`), but not one in an object built elsewhere and passed by name, so read those by hand.
10
+
11
+ ## Replace
12
+
13
+ Delete the key. A search box that opens a list of suggestions with `aria-expanded` and `aria-haspopup` is a combobox: keep it a plain `SearchField` that points at its results with `aria-controls`, or build it on a combobox.
14
+
15
+ ```tsx
16
+ // before
17
+ <SearchField label="Search" inputProps={{ "aria-controls": "results", "aria-expanded": open }} />
18
+ // after
19
+ <SearchField label="Search" inputProps={{ "aria-controls": "results" }} />
20
+ ```
21
+
22
+ `aria-description` has no replacement in `inputProps`. Pass the text as `description`, which joins the input's `aria-describedby`; it shows below the field, so wrap it in `VisuallyHidden` to keep it for assistive tech only: `description={<VisuallyHidden>Searches titles and numbers</VisuallyHidden>}`.
23
+
24
+ ## Verify
25
+
26
+ Run the app's typecheck (`pnpm typecheck`); it passes with no error naming an `aria-*` key on `inputProps`, and the by-hand read above found none passed by name. Then run the app's axe check on a page with the field; `aria-allowed-attr` reports nothing on it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jonmilner/ui",
3
- "version": "0.3.3",
3
+ "version": "0.4.0",
4
4
  "files": [
5
5
  "dist",
6
6
  "THIRD_PARTY_NOTICES.md"