@apliteni/apliteni-ui 0.23.1 → 0.23.3
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 +1 -1
- package/src/components/confirm.js +27 -28
- package/src/components/drawer.js +27 -28
- package/src/components/dropdown.js +26 -23
- package/src/components/index.js +9 -13
- package/src/components/nav.js +7 -20
- package/src/components/overlay.js +28 -53
- package/src/components/shell.js +48 -76
- package/src/components/success.js +8 -17
- package/src/components/topbar.js +8 -12
- package/src/styles/base.css +4 -17
- package/src/styles/callout.css +4 -12
- package/src/styles/confirm.css +9 -12
- package/src/styles/loading.css +9 -24
- package/src/styles/motion.css +12 -20
- package/src/styles/reduced-motion.css +8 -19
- package/src/styles/table.css +13 -29
- package/src/tokens/accents.css +15 -29
- package/src/tokens/tokens.css +77 -145
package/package.json
CHANGED
|
@@ -3,18 +3,13 @@
|
|
|
3
3
|
// container.innerHTML = confirm({ title, body, confirmLabel, cancelLabel });
|
|
4
4
|
// wireConfirm(container); // scrim/Esc/answers + focus trap
|
|
5
5
|
//
|
|
6
|
-
// A page trigger opens it by id:
|
|
7
|
-
//
|
|
8
|
-
//
|
|
6
|
+
// A page trigger opens it by id: <button data-confirm-open="ID">. Answering is
|
|
7
|
+
// the caller's job — a listener on [data-confirm-accept] is where the
|
|
8
|
+
// destructive work goes — and focus opens on the SAFE answer, so a reader who
|
|
9
|
+
// hits Enter out of habit keeps what they have.
|
|
9
10
|
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// the SAFE answer, never the destructive one, so a reader who hits Enter out of
|
|
13
|
-
// habit keeps what they have.
|
|
14
|
-
//
|
|
15
|
-
// Inertness, Escape and the focus trap come from ./overlay.js — one stack for
|
|
16
|
-
// every overlay on the page, so a confirm over a drawer never has to guess which
|
|
17
|
-
// of the two the keyboard belongs to.
|
|
11
|
+
// Inertness, Escape and the focus trap come from ./overlay.js: one stack for
|
|
12
|
+
// every overlay on the page.
|
|
18
13
|
import { button, esc } from './index.js';
|
|
19
14
|
import { OVERLAY_LAYER, adoptOverlay, focusablesIn, popOverlay, pushOverlay, returnFocus, syncOverlays } from './overlay.js';
|
|
20
15
|
|
|
@@ -24,23 +19,27 @@ const cx = (...a) => a.filter(Boolean).join(' ');
|
|
|
24
19
|
let _uid = 0;
|
|
25
20
|
const nextId = (p = 'confirm') => `${p}-${++_uid}`;
|
|
26
21
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
22
|
+
/**
|
|
23
|
+
* The public factory. Returns an HTML string; wire it with wireConfirm().
|
|
24
|
+
*
|
|
25
|
+
* `specimen` renders open as a *picture* of the dialog — same markup, minus the
|
|
26
|
+
* data-confirm hook and aria-modal, so no wiring and no key handler can reach
|
|
27
|
+
* it. A documentation page shows several at once and none of them may own the
|
|
28
|
+
* page or answer Escape: an answered specimen would erase itself with nothing to
|
|
29
|
+
* bring it back, and three modal dialogs on one page trap the reader in the
|
|
30
|
+
* first. Use `open` when the dialog is real.
|
|
31
|
+
*
|
|
32
|
+
* @param {object} [o]
|
|
33
|
+
* @param {string} [o.title] the question — also the accessible name
|
|
34
|
+
* @param {string} [o.body] the consequence — the accessible description
|
|
35
|
+
* @param {string} [o.confirmLabel] the destructive answer (default 'Confirm')
|
|
36
|
+
* @param {string} [o.cancelLabel] the safe answer (default 'Cancel')
|
|
37
|
+
* @param {string} [o.variant] 'danger' (default) | 'primary'
|
|
38
|
+
* @param {boolean} [o.open] render already-open, as a real dialog
|
|
39
|
+
* @param {boolean} [o.specimen] render open as a picture of the dialog
|
|
40
|
+
* @param {string} [o.id] root id a [data-confirm-open] trigger targets
|
|
41
|
+
* @returns {string} html
|
|
42
|
+
*/
|
|
44
43
|
export function confirm({
|
|
45
44
|
title = 'Are you sure?', body = '', confirmLabel = 'Confirm', cancelLabel = 'Cancel',
|
|
46
45
|
variant = 'danger', id, open = false, specimen = false,
|
package/src/components/drawer.js
CHANGED
|
@@ -1,19 +1,15 @@
|
|
|
1
|
-
// Drawer — the kit's edge-anchored overlay panel
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// left|right, full-width for top|bottom.
|
|
1
|
+
// Drawer — the kit's edge-anchored overlay panel: a panel that slides in from
|
|
2
|
+
// any screen edge over a scrim. Header / scrollable body / footer actions, sizes
|
|
3
|
+
// sm|md|lg along the slide axis.
|
|
5
4
|
//
|
|
6
5
|
// container.innerHTML = drawer({ side: 'right', title: 'Filters', body });
|
|
7
6
|
// wireDrawer(container); // scrim/Esc/close-button + focus trap
|
|
8
7
|
//
|
|
9
|
-
// A page trigger opens it by id:
|
|
10
|
-
//
|
|
11
|
-
// open, or `specimen: true` for a picture of one on a documentation page.
|
|
12
|
-
//
|
|
13
|
-
// Inertness, Escape and the focus trap are shared with confirm(): both are
|
|
8
|
+
// A page trigger opens it by id: <button data-drawer-open="ID">. Inertness,
|
|
9
|
+
// Escape and the focus trap are shared with confirm(): both are
|
|
14
10
|
// "content over a scrim, focus-trapped, Esc-dismissable", and they push onto one
|
|
15
|
-
// stack in ./overlay.js so the two can never disagree about which of them the
|
|
16
|
-
// keyboard
|
|
11
|
+
// stack in ./overlay.js, so the two can never disagree about which of them the
|
|
12
|
+
// keyboard belongs to.
|
|
17
13
|
import { esc, icon } from './index.js';
|
|
18
14
|
import { OVERLAY_LAYER, adoptOverlay, focusablesIn, popOverlay, pushOverlay, returnFocus, syncOverlays } from './overlay.js';
|
|
19
15
|
|
|
@@ -23,23 +19,26 @@ const cx = (...a) => a.filter(Boolean).join(' ');
|
|
|
23
19
|
let _uid = 0;
|
|
24
20
|
const nextId = (p = 'drawer') => `${p}-${++_uid}`;
|
|
25
21
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
22
|
+
/**
|
|
23
|
+
* The public factory. Returns an HTML string; wire it with wireDrawer().
|
|
24
|
+
*
|
|
25
|
+
* `specimen` renders open as a *picture* of the panel — see confirm() for why a
|
|
26
|
+
* documentation page wants that. Use `open` when the drawer is real.
|
|
27
|
+
*
|
|
28
|
+
* @param {object} [o]
|
|
29
|
+
* @param {string} [o.side] 'right' (default) | 'left' | 'top' | 'bottom'
|
|
30
|
+
* @param {string} [o.size] 'sm' | 'md' (default) | 'lg', along the slide axis
|
|
31
|
+
* @param {string} [o.title] header heading, also the accessible name
|
|
32
|
+
* @param {string} [o.body] scrollable body HTML (trusted markup)
|
|
33
|
+
* @param {string} [o.footer] pinned footer actions HTML (trusted markup)
|
|
34
|
+
* @param {boolean} [o.open] render already-open, as a real panel
|
|
35
|
+
* @param {boolean} [o.specimen] render open as a picture of the panel
|
|
36
|
+
* @param {string} [o.id] root id a [data-drawer-open] trigger targets
|
|
37
|
+
* @param {string} [o.ariaLabel] accessible name when there is no visible title
|
|
38
|
+
* @param {boolean} [o.dismissible] close button + scrim/Esc dismiss (default true)
|
|
39
|
+
* @param {string} [o.closeLabel] accessible name for the close button
|
|
40
|
+
* @returns {string} html
|
|
41
|
+
*/
|
|
43
42
|
export function drawer({
|
|
44
43
|
side = 'right', size = 'md', title, body = '', footer = '',
|
|
45
44
|
open = false, specimen = false, id, ariaLabel, dismissible = true, closeLabel = 'Close',
|
|
@@ -1,20 +1,17 @@
|
|
|
1
|
-
// Dropdown — the kit's one popover-list primitive. A trigger
|
|
2
|
-
//
|
|
3
|
-
// description, optional leading icon, optional trailing badge, selected +
|
|
4
|
-
// disabled state). Two flavours share the same panel + the same open/close JS:
|
|
1
|
+
// Dropdown — the kit's one popover-list primitive. A trigger opens a panel of
|
|
2
|
+
// item rows; two flavours share the same panel and the same open/close JS:
|
|
5
3
|
//
|
|
6
4
|
// variant: 'select' → role="listbox" / role="option", value shown in trigger
|
|
7
5
|
// variant: 'menu' → role="menu" / role="menuitem", action list
|
|
8
6
|
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
// one open/close/click-outside/Esc/keyboard implementation in the kit.
|
|
7
|
+
// The name is deliberate: `menu` implies actions only and `select` implies a
|
|
8
|
+
// form control bound to a value, and this factory is the umbrella both are
|
|
9
|
+
// specialisations of. The topbar's version switcher and account menu are thin
|
|
10
|
+
// consumers of the SAME wiring, so there is one open/close/Esc/keyboard
|
|
11
|
+
// implementation in the kit.
|
|
15
12
|
//
|
|
16
13
|
// container.innerHTML = dropdown({ label: 'version:', value: '…', items });
|
|
17
|
-
// wireDropdown(container); // or let wireTopbar() do it
|
|
14
|
+
// wireDropdown(container); // or let wireTopbar() do it
|
|
18
15
|
import { esc, icon } from './index.js';
|
|
19
16
|
|
|
20
17
|
const cx = (...a) => a.filter(Boolean).join(' ');
|
|
@@ -66,18 +63,24 @@ function ddBody({ items, sections }, listbox) {
|
|
|
66
63
|
return (items || []).map((it) => ddItem(it, listbox)).join('');
|
|
67
64
|
}
|
|
68
65
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
66
|
+
/**
|
|
67
|
+
* The public factory. Returns an HTML string; wire it with wireDropdown().
|
|
68
|
+
*
|
|
69
|
+
* @param {object} [o]
|
|
70
|
+
* @param {string} [o.label] muted prefix in the trigger (e.g. "version:")
|
|
71
|
+
* @param {string} [o.value] current value shown in the trigger
|
|
72
|
+
* @param {string} [o.placeholder] shown when there is no value
|
|
73
|
+
* @param {string} [o.variant] 'select' (listbox) | 'menu' (inferred from items)
|
|
74
|
+
* @param {Array} [o.items] [{ label, value?, description?, icon?, badge?, selected?, disabled?, href?, danger? }]
|
|
75
|
+
* @param {Array} [o.sections] [{ label, items }] — grouped alternative to items
|
|
76
|
+
* @param {string} [o.header] raw HTML pinned to the top of the panel
|
|
77
|
+
* @param {string} [o.footer] raw HTML pinned to the bottom of the panel
|
|
78
|
+
* @param {string} [o.align] 'start' (default) | 'end' — the edge the panel hugs
|
|
79
|
+
* @param {boolean|number} [o.scroll] true, or a maxHeight in px, to cap and scroll
|
|
80
|
+
* @param {boolean} [o.open] render already-open (handy for screenshots)
|
|
81
|
+
* @param {string} [o.ariaLabel] accessible name for the panel and trigger
|
|
82
|
+
* @returns {string} html
|
|
83
|
+
*/
|
|
81
84
|
export function dropdown({
|
|
82
85
|
label, value, placeholder = 'Select…', variant, items, sections,
|
|
83
86
|
header = '', footer = '', triggerContent, triggerClass = '', chevron = true,
|
package/src/components/index.js
CHANGED
|
@@ -66,21 +66,17 @@ export function card({ title, sub, body = '', variant, pad, icon: ic } = {}) {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
// ---- Segmented control ---------------------------------------------------
|
|
69
|
-
// A strip of mutually exclusive toggle buttons
|
|
70
|
-
//
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
// options, Home/End to jump to the ends, and `aria-pressed` to say which is on.
|
|
74
|
-
// wireTopbar() ships that keyboard behaviour (see topbar.js); the markup here
|
|
75
|
-
// carries the state it reads.
|
|
69
|
+
// A strip of mutually exclusive toggle buttons.
|
|
70
|
+
// It is NOT a tablist: it controls no panel, so it must not announce one.
|
|
71
|
+
// `role="toolbar"` with a roving tabindex is the honest shape, and wireTopbar()
|
|
72
|
+
// ships the keyboard behaviour.
|
|
76
73
|
//
|
|
77
|
-
// When the pill drives a real change of view
|
|
78
|
-
//
|
|
74
|
+
// When the pill drives a real change of view, reach for tabs() instead —
|
|
75
|
+
// that one owns panels and earns the tab announcement.
|
|
79
76
|
//
|
|
80
|
-
// `ariaLabel` names the strip. `name` seeds data-seg
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
// switchToggle() defaults its label.
|
|
77
|
+
// `ariaLabel` names the strip. `name` seeds data-seg and is deliberately
|
|
78
|
+
// not an accessible name — it is an identifier, not prose, defaulting to
|
|
79
|
+
// "Options" so a strip is never left unlabelled.
|
|
84
80
|
export function segmented({ options = [], active = 0, size, block, name = 'seg', ariaLabel = 'Options' } = {}) {
|
|
85
81
|
const cls = cx('ui-seg', size && `ui-seg--${size}`, block && 'ui-seg--block');
|
|
86
82
|
// Exactly one option holds the Tab stop. If `active` points nowhere (nothing
|
package/src/components/nav.js
CHANGED
|
@@ -1,24 +1,11 @@
|
|
|
1
|
-
// Navigation — the kit's primary wayfinding primitives.
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// Navigation — the kit's primary wayfinding primitives. `nav({ variant })`
|
|
2
|
+
// dispatches to sidebarNav(), navTabs() and breadcrumbs(), each also exported
|
|
3
|
+
// directly: the three share tokens and classes but almost no markup, so a single
|
|
4
|
+
// options bag would mean wildly different fields per variant.
|
|
4
5
|
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
// variant: 'tabs' → navTabs() horizontal page tabs with an active
|
|
9
|
-
// underline (default) or pill.
|
|
10
|
-
// variant: 'breadcrumbs' → breadcrumbs() the `Finance / Payouts` trail.
|
|
11
|
-
//
|
|
12
|
-
// Why an umbrella AND named exports (unlike dropdown, which is one factory): the
|
|
13
|
-
// three variants don't share a body the way select/menu share a panel, so a
|
|
14
|
-
// single options bag would mean wildly different fields per variant. The named
|
|
15
|
-
// exports keep each call site honest; `nav()` stays the discoverable entry the
|
|
16
|
-
// issue asks for and the one thing to import when the variant is data-driven.
|
|
17
|
-
//
|
|
18
|
-
// These are NAVIGATION controls (links between locations), not tab panels — so
|
|
19
|
-
// tabs render as <nav> + <a aria-current="page">, not role="tablist" (that's
|
|
20
|
-
// what segmented() is for). Only the collapsible sidebar groups need JS; wire
|
|
21
|
-
// them once after mount with wireNav() (preview.js does this for Storybook).
|
|
6
|
+
// These are NAVIGATION controls, not tab panels — so tabs render as
|
|
7
|
+
// <nav> + <a aria-current="page">, not role="tablist" (that is what segmented()
|
|
8
|
+
// is for). Only the collapsible sidebar groups need JS; wire with wireNav().
|
|
22
9
|
import { esc, icon } from './index.js';
|
|
23
10
|
|
|
24
11
|
const cx = (...a) => a.filter(Boolean).join(' ');
|
|
@@ -1,19 +1,12 @@
|
|
|
1
|
-
// Overlay primitives — the page state the kit's modal surfaces share.
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// the
|
|
5
|
-
//
|
|
6
|
-
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
// Internal — not re-exported from src/index.js.
|
|
11
|
-
|
|
12
|
-
// What each overlay paints on, as its stylesheet resolves it today: a drawer at
|
|
13
|
-
// `--z-overlay` (src/styles/drawer.css) and a confirm one above it
|
|
14
|
-
// (src/styles/confirm.css). Absolute values, not ranks, so a sheet that moves and
|
|
15
|
-
// a table that did not is a failed test rather than a keyboard on the wrong layer
|
|
16
|
-
// — stories/overlay-css.test.js holds these two numbers to those two rules.
|
|
1
|
+
// Overlay primitives — the page state the kit's modal surfaces share. Drawer and Confirm
|
|
2
|
+
// are the same problem twice: content over a scrim that owns the keyboard until it is
|
|
3
|
+
// answered. What is inert, which overlay Escape talks to and where Tab may go are
|
|
4
|
+
// properties of the *page*, so they are answered here from one stack per document rather
|
|
5
|
+
// than from either component's own storage. Internal — not re-exported from src/index.js.
|
|
6
|
+
|
|
7
|
+
// What each overlay paints on today: a drawer at `--z-overlay` (styles/drawer.css) and a
|
|
8
|
+
// confirm above it (styles/confirm.css). Absolute values, not ranks, so a sheet that moves
|
|
9
|
+
// and a table that did not is a failed test — stories/overlay-css.test.js holds both.
|
|
17
10
|
export const OVERLAY_LAYER = { drawer: 100, confirm: 101 };
|
|
18
11
|
|
|
19
12
|
const FOCUSABLE = [
|
|
@@ -33,10 +26,9 @@ function pageOf(doc) {
|
|
|
33
26
|
return page;
|
|
34
27
|
}
|
|
35
28
|
|
|
36
|
-
// Focusable to the *browser*, not merely matching the selector. A control in a
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
// and Tab walks straight out of the modal.
|
|
29
|
+
// Focusable to the *browser*, not merely matching the selector. A control in a closed
|
|
30
|
+
// overlay, an inert subtree or a hidden ancestor is skipped by the real tab order, so a
|
|
31
|
+
// trap that counts it wraps at an element focus never reaches.
|
|
40
32
|
function reachable(el) {
|
|
41
33
|
for (let n = el; n && n.nodeType === 1; n = n.parentElement) {
|
|
42
34
|
if (n.inert || n.hasAttribute('inert') || n.hasAttribute('hidden')) return false;
|
|
@@ -53,10 +45,9 @@ export function focusablesIn(panel) {
|
|
|
53
45
|
return Array.from(panel.querySelectorAll(FOCUSABLE)).filter(reachable);
|
|
54
46
|
}
|
|
55
47
|
|
|
56
|
-
// Hide everything *outside* `root` from AT + the tab order: walk root→body and
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
// not — a drawer under an aria-modal alertdialog must not be tabbable.
|
|
48
|
+
// Hide everything *outside* `root` from AT + the tab order: walk root→body and mark each
|
|
49
|
+
// ancestor's other children. Scrim and panel live inside the root so they stay
|
|
50
|
+
// interactive; a drawer under an aria-modal alertdialog is outside it and must not be.
|
|
60
51
|
function mark(page, doc, root) {
|
|
61
52
|
let node = root;
|
|
62
53
|
while (node && node.parentElement && node !== doc.body) {
|
|
@@ -81,10 +72,9 @@ function unmark(page) {
|
|
|
81
72
|
page.marked = [];
|
|
82
73
|
}
|
|
83
74
|
|
|
84
|
-
// Recompute the page from the stack — never replay a snapshot taken when an
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
// what to give back outlives the node that hid it.
|
|
75
|
+
// Recompute the page from the stack — never replay a snapshot taken when an overlay
|
|
76
|
+
// opened, because by the time it closes that snapshot describes a page that has moved on.
|
|
77
|
+
// Roots torn down while open drop out here.
|
|
88
78
|
function sync(doc) {
|
|
89
79
|
const page = pageOf(doc);
|
|
90
80
|
for (let i = page.stack.length - 1; i >= 0; i--) {
|
|
@@ -128,12 +118,11 @@ function place(root, panel, dismiss, layer, where) {
|
|
|
128
118
|
sync(doc);
|
|
129
119
|
}
|
|
130
120
|
|
|
131
|
-
// The layer this root actually paints on. In a browser the live z-index is the
|
|
132
|
-
//
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
// stands. `auto` must not read as 0; anything unparseable falls through.
|
|
121
|
+
// The layer this root actually paints on. In a browser the live z-index is the truth, so
|
|
122
|
+
// a consumer who moves an overlay in their own stylesheet gets the keyboard on the layer
|
|
123
|
+
// they can see. JSDOM hands back declared text rather than a number, so under test the
|
|
124
|
+
// passed constant stands — `auto` must not read as 0, and anything unparseable falls
|
|
125
|
+
// through.
|
|
137
126
|
function paintedLayer(root, layer) {
|
|
138
127
|
const painted = root.ownerDocument.defaultView?.getComputedStyle(root)?.zIndex;
|
|
139
128
|
const live = painted ? Number(painted) : NaN;
|
|
@@ -152,25 +141,11 @@ export function pushOverlay(root, panel, dismiss, layer) {
|
|
|
152
141
|
}
|
|
153
142
|
|
|
154
143
|
/**
|
|
155
|
-
* Take on a root that arrived already open — markup rendered with `open: true`,
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
* overlay the reader is looking at is the one drawn over the rest: a confirm sits
|
|
161
|
-
* a layer above a drawer whichever component's wiring ran first, and whichever
|
|
162
|
-
* root the markup happens to put first. Document position only separates two
|
|
163
|
-
* overlays on the same layer, and there it is the right answer rather than a
|
|
164
|
-
* fallback — at equal stack levels the later root paints on top (CSS 2.2 Appendix
|
|
165
|
-
* E, steps 8 and 9). That comparison earns its keep whenever adoption order and
|
|
166
|
-
* document order disagree: a root inserted above one already on the stack and
|
|
167
|
-
* wired after it, and — in a browser only — a consumer stylesheet that lifts the
|
|
168
|
-
* drawer onto the confirm's layer, where wiring runs drawer-first however the
|
|
169
|
-
* markup is ordered. A browser refines the layer from the live z-index; JSDOM has
|
|
170
|
-
* none to give, so the tests hold OVERLAY_LAYER to the sheets instead. A root that
|
|
171
|
-
* renders closed is left alone: wiring is not an opening, and it waits for the one
|
|
172
|
-
* that is. A specimen never reaches here at all — it carries no hook for the
|
|
173
|
-
* wiring to find.
|
|
144
|
+
* Take on a root that arrived already open — markup rendered with `open: true`, which
|
|
145
|
+
* nobody called open…() for. Without this its aria-modal is a claim the page contradicts.
|
|
146
|
+
* It goes in by PAINT ORDER, because wiring has no history to order by; document position
|
|
147
|
+
* separates two overlays on the same layer, where the later root paints on top (CSS 2.2
|
|
148
|
+
* Appendix E, steps 8 and 9). A root that renders closed is left alone.
|
|
174
149
|
*/
|
|
175
150
|
export function adoptOverlay(root, panel, dismiss, layer) {
|
|
176
151
|
if (!root.classList.contains('is-open')) return;
|