@jsenv/navi 0.29.470 → 0.29.472

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.
@@ -141,8 +141,8 @@ start when unsure which export solves a problem.
141
141
 
142
142
  - `popup_open.md` — a popup owns its open state; `command` + `commandFor`,
143
143
  `triggerNaviCommand` as the last resort; opening ON something; a press that
144
- opens and acts is a `Picker`; Escape cancels; the close cross;
145
- `signal`/`navState`/`open`; a popup that loads data; `mount`; a closed
144
+ opens and acts is a `Picker`; Escape cancels; the close cross; its name
145
+ (`Popup.Title`); `signal`/`navState`/`open`; a popup that loads data; `mount`; a closed
146
146
  `Dialog` costs one element until asked (a row may hold the dialogs of its
147
147
  own actions). Before passing `open`, calling `triggerNaviCommand`, or
148
148
  writing a close button.
@@ -14,6 +14,7 @@ What opens a `Dialog` or a `Popover`, and who owns the fact that it is open.
14
14
  - [Reacting to open and close](#reacting-to-open-and-close)
15
15
  - [Escape cancels, the other gestures keep](#escape-cancels-the-other-gestures-keep)
16
16
  - [The close cross](#the-close-cross)
17
+ - [Its name](#its-name)
17
18
  - [When the app holds the open state](#when-the-app-holds-the-open-state)
18
19
  - [A popup that loads data](#a-popup-that-loads-data)
19
20
  - [What the popup holds while it is closed](#what-the-popup-holds-while-it-is-closed)
@@ -182,6 +183,11 @@ the element itself, so the popup element passed as the source resolves to that
182
183
  popup — the short form above. To trigger from another element, give it a
183
184
  `commandFor` pointing at the popup's `id`.
184
185
 
186
+ A picker's `<input>` carries `aria-expanded` too, for assistive tech, beside its
187
+ role (see [Its name](#its-name)). It repeats the state of the picker's root and
188
+ is not a target of its own: a command triggered from that input reaches the
189
+ picker, as one triggered from anything else in it does.
190
+
185
191
  ## The anchor
186
192
 
187
193
  A popup opens on the place the open names, on the `anchor` prop when the open
@@ -285,6 +291,10 @@ far from every press that opens it, it has to be told what it is about and has
285
291
  to answer somebody. The content can still be one component used in every picker:
286
292
  "the prompt exists once" is a question about components, not about the DOM.
287
293
 
294
+ An `aria-labelledby` on a `Picker` names its trigger, not its popup: a shared
295
+ dialog that pointed at its title that way names itself with `<Popup.Title>`
296
+ once it is a picker's popup (see [Its name](#its-name)).
297
+
288
298
  What each row costs depends on what reads the picker's value before anything
289
299
  opens. A picker told no value — no `value`, `defaultValue` or `signal` — holds
290
300
  what the control in its popup holds, so that popup is built, closed, as soon as
@@ -616,6 +626,48 @@ it and says why, and a `Picker` validates what its popup holds and runs its
616
626
  action on the way out, an invalid value keeping it open. Hiding or disabling the
617
627
  cross to hold a popup open only removes the one way out that is visible.
618
628
 
629
+ ## Its name
630
+
631
+ A screen reader announces a popup by its name, and
632
+ `getByRole("dialog", { name })` finds it by it; unnamed, it is "dialog" and
633
+ nothing else. The first of these names it:
634
+
635
+ 1. `aria-label` / `aria-labelledby` on the popup — `popupAriaLabel` /
636
+ `popupAriaLabelledBy` on a `Picker`, whose own `aria-label` names its
637
+ trigger;
638
+ 2. its title, written as `<Popup.Title>` (`<Dialog.Title>`,
639
+ `<Popover.Title>`): a heading, `h2` unless `as` says otherwise, that names
640
+ the popup it sits in with no id to wire — and, written in a picker's
641
+ children, whichever mode the popup opens in;
642
+ 3. for a `Picker`'s popup, the trigger's name: its `aria-labelledby` or
643
+ `aria-label`, the label a picker that picks nothing draws, or the `<Label>`
644
+ pointing at it. What was pressed is a fair name for what it opened.
645
+
646
+ ```jsx
647
+ <Picker
648
+ id="report_book"
649
+ picksNothing
650
+ standalone
651
+ ui="Report this book"
652
+ popupAriaDescribedBy="report_target"
653
+ >
654
+ <Popup.Title>Report</Popup.Title>
655
+ <p id="report_target">“Title”, chapter 3</p>
656
+ <ReportForm />
657
+ </Picker>
658
+ ```
659
+
660
+ A `Popover` has no role of its own, and a name on an element without one is
661
+ read by nobody: a named popover is a `dialog`, unless given another `role`. A
662
+ picker's popover always is, which is what its trigger announces
663
+ (`aria-haspopup="dialog"`, `aria-expanded`). A title inside a
664
+ `mode="callout"` picker names nothing: a callout is read as its message.
665
+
666
+ A `<label>` wrapping the picker does not name its popup: it holds the trigger
667
+ and the popup with it, so the popup would be named after its own content. A
668
+ hand-written `<label for>` names it once it has an `id`; navi's `<Label>` always
669
+ has one.
670
+
619
671
  ## When the app holds the open state
620
672
 
621
673
  A popup whose being-open is a fact about the application rather than about the
package/docs/testid.md CHANGED
@@ -90,13 +90,26 @@ found by nothing, while the host is what the user focuses and what
90
90
 
91
91
  ```html
92
92
  <div class="navi_picker" navi-control="picker" aria-expanded="false">
93
- <input navi-control-host="picker" aria-label="Aide" readonly />
93
+ <input
94
+ navi-control-host="picker"
95
+ role="combobox"
96
+ aria-haspopup="dialog"
97
+ aria-expanded="false"
98
+ aria-label="Aide"
99
+ readonly
100
+ />
94
101
  </div>
95
102
  ```
96
103
 
97
104
  Which role that host answers to is what the control really is, not what it is
98
105
  drawn like: a button's host is a `<button>`, and a picker's host is an
99
- `<input>` — a `textbox`, because it holds text the user chose.
106
+ `<input>` that answers to `combobox` — it shows a value the user does not type,
107
+ and opens what changes it. It says whether its popup is open (`aria-expanded`)
108
+ and what that popup is (`aria-haspopup="dialog"`): a `dialog` in every mode,
109
+ found with `getByRole("dialog", { name })` once open (see
110
+ [popup_open.md](./popup_open.md#its-name) for its name). A picker showing the
111
+ browser's own picker (a `type` with no children) is the `textbox` its
112
+ `<input>` is.
100
113
 
101
114
  A picker that picks nothing is the exception, and answers to `button`: its
102
115
  popup is a question (`type="confirm"`) or something to read
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/navi",
3
- "version": "0.29.470",
3
+ "version": "0.29.472",
4
4
  "type": "module",
5
5
  "description": "Library of components including navigation to create frontend applications",
6
6
  "repository": {