@qaiddev/quests-embed 1.1.1 → 1.2.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.
- package/dist/a11y.d.ts +93 -0
- package/dist/embed.d.ts +10 -0
- package/dist/inputs.d.ts +9 -0
- package/dist/qaid-quests.js +402 -241
- package/dist/qaid-quests.js.map +1 -1
- package/dist/qaid-quests.umd.cjs +4 -4
- package/dist/qaid-quests.umd.cjs.map +1 -1
- package/package.json +3 -1
package/dist/a11y.d.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared accessibility primitives for the qaid embeds.
|
|
3
|
+
*
|
|
4
|
+
* These helpers are intentionally framework-free and self-contained (no CSS
|
|
5
|
+
* dependency) so they can be dropped into any open shadow root. They cover the
|
|
6
|
+
* cross-cutting WCAG 2.2 AA infrastructure called for in the embed
|
|
7
|
+
* accessibility plan: live-region announcements, focus management (trap,
|
|
8
|
+
* save/restore), dialog semantics, and background isolation.
|
|
9
|
+
*
|
|
10
|
+
* The API is deliberately identical to @qaiddev/thumbs-embed's a11y module so
|
|
11
|
+
* both embeds consume one shared surface. In quests-embed specifically:
|
|
12
|
+
* - {@link announce} is a GENERAL-PURPOSE announcer for step-change,
|
|
13
|
+
* loading, and thank-you messages. It is separate from the pre-existing
|
|
14
|
+
* per-step `.qaid-q-error` region (aria-live="polite") in embed.ts — that
|
|
15
|
+
* region stays the owner of field-validation errors. Callers should route
|
|
16
|
+
* step/status text through announce()'s polite region and only use the
|
|
17
|
+
* assertive region for load failures, so the two never clobber each other.
|
|
18
|
+
*/
|
|
19
|
+
/** Host for the live regions: either a shadow root or a plain element. */
|
|
20
|
+
export type AnnounceRoot = ShadowRoot | HTMLElement;
|
|
21
|
+
/** Options accepted by {@link announce}. */
|
|
22
|
+
export interface AnnounceOptions {
|
|
23
|
+
/** Route the message through the assertive (role="alert") region. */
|
|
24
|
+
assertive?: boolean;
|
|
25
|
+
}
|
|
26
|
+
/** Options accepted by {@link applyDialog}. */
|
|
27
|
+
export interface ApplyDialogOptions {
|
|
28
|
+
/** id of the element that labels the dialog (wired via aria-labelledby). */
|
|
29
|
+
labelledbyId?: string;
|
|
30
|
+
/** id of the element that describes the dialog (wired via aria-describedby). */
|
|
31
|
+
describedbyId?: string;
|
|
32
|
+
/** Fallback accessible name when no labelling element exists. */
|
|
33
|
+
label?: string;
|
|
34
|
+
}
|
|
35
|
+
/** Handle returned by {@link createFocusTrap}. */
|
|
36
|
+
export interface FocusTrap {
|
|
37
|
+
/** Remove the trap's key handler and clean up any tabindex it added. */
|
|
38
|
+
release(): void;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Announce a message to assistive technology via a visually-hidden live region
|
|
42
|
+
* mounted inside `root`. The two regions (polite role="status" and assertive
|
|
43
|
+
* role="alert") are created lazily on first use and reused thereafter.
|
|
44
|
+
*
|
|
45
|
+
* The target region is cleared before the new text is written so that repeated
|
|
46
|
+
* identical messages are re-announced rather than coalesced.
|
|
47
|
+
*/
|
|
48
|
+
export declare function announce(root: AnnounceRoot, message: string, opts?: AnnounceOptions): void;
|
|
49
|
+
/**
|
|
50
|
+
* Collect the focusable descendants of `container` in DOM order, excluding
|
|
51
|
+
* disabled controls, elements opted out with tabindex="-1", and elements that
|
|
52
|
+
* are hidden (via the hidden attribute or an inline display/visibility rule on
|
|
53
|
+
* themselves or an ancestor up to the document root).
|
|
54
|
+
*/
|
|
55
|
+
export declare function getFocusable(container: HTMLElement): HTMLElement[];
|
|
56
|
+
/**
|
|
57
|
+
* Trap keyboard focus inside `container`: Tab / Shift+Tab cycle between the
|
|
58
|
+
* first and last focusable descendants and can never leave the container. On
|
|
59
|
+
* creation focus moves to the first focusable element, or to the container
|
|
60
|
+
* itself (made programmatically focusable) when it has no focusable children.
|
|
61
|
+
*
|
|
62
|
+
* Call {@link FocusTrap.release} to remove the handler; pair it with
|
|
63
|
+
* {@link restoreFocus} to return focus to the invoking control.
|
|
64
|
+
*/
|
|
65
|
+
export declare function createFocusTrap(container: HTMLElement): FocusTrap;
|
|
66
|
+
/**
|
|
67
|
+
* Capture the currently focused element (drilling through shadow roots) so it
|
|
68
|
+
* can be restored later with {@link restoreFocus}. Returns null when focus is
|
|
69
|
+
* on nothing focusable.
|
|
70
|
+
*/
|
|
71
|
+
export declare function saveFocus(): HTMLElement | null;
|
|
72
|
+
/**
|
|
73
|
+
* Restore focus to a previously saved element. Safe to call with null or an
|
|
74
|
+
* element that has since been detached (a missing/throwing focus is ignored).
|
|
75
|
+
*/
|
|
76
|
+
export declare function restoreFocus(el: HTMLElement | null): void;
|
|
77
|
+
/**
|
|
78
|
+
* Apply dialog semantics to `el`: role="dialog", aria-modal="true", and the
|
|
79
|
+
* naming/description wiring described by `opts`. Prefer aria-labelledby /
|
|
80
|
+
* aria-describedby pointing at visible title/subtitle elements; fall back to
|
|
81
|
+
* aria-label when no visible label element exists.
|
|
82
|
+
*/
|
|
83
|
+
export declare function applyDialog(el: HTMLElement, opts?: ApplyDialogOptions): void;
|
|
84
|
+
/**
|
|
85
|
+
* Isolate the background from assistive technology while a dialog is open by
|
|
86
|
+
* marking every top-level sibling of the dialog inert (with an aria-hidden
|
|
87
|
+
* fallback). `except` is the open dialog (or any element inside it); the
|
|
88
|
+
* top-level element that contains it is left interactive.
|
|
89
|
+
*
|
|
90
|
+
* Returns a restore function that reverts every attribute this call changed,
|
|
91
|
+
* leaving elements that were already inert/hidden untouched.
|
|
92
|
+
*/
|
|
93
|
+
export declare function setBackgroundInert(except: HTMLElement): () => void;
|
package/dist/embed.d.ts
CHANGED
|
@@ -31,6 +31,9 @@ export declare class QaidQuests {
|
|
|
31
31
|
private backdropEl;
|
|
32
32
|
private currentInput;
|
|
33
33
|
private boundKeyDown;
|
|
34
|
+
private savedOpener;
|
|
35
|
+
private focusTrap;
|
|
36
|
+
private inertRestore;
|
|
34
37
|
private cssVars;
|
|
35
38
|
private hostThemeOverrides;
|
|
36
39
|
private hostInlineVars;
|
|
@@ -73,6 +76,13 @@ export declare class QaidQuests {
|
|
|
73
76
|
private jsonHeaders;
|
|
74
77
|
private showSaving;
|
|
75
78
|
private handleKeyDown;
|
|
79
|
+
/**
|
|
80
|
+
* Modal mode only: trap Tab within the card and mark the rest of the
|
|
81
|
+
* page inert while the dialog is open. Runs once (guarded) after the
|
|
82
|
+
* first step renders. Inline mode keeps normal page tab flow and is
|
|
83
|
+
* never trapped or isolated.
|
|
84
|
+
*/
|
|
85
|
+
private setupModalA11y;
|
|
76
86
|
private close;
|
|
77
87
|
/** Destroy the embed and clean up all resources */
|
|
78
88
|
destroy(): void;
|
package/dist/inputs.d.ts
CHANGED
|
@@ -15,6 +15,15 @@ export interface QuestionInput {
|
|
|
15
15
|
focus(): void;
|
|
16
16
|
getValue(): AnswerValue;
|
|
17
17
|
isValid(): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Mark the control invalid: sets aria-invalid="true" and links the
|
|
20
|
+
* error text (identified by `errorId`) via aria-describedby so a
|
|
21
|
+
* screen reader reads the error as the field's description. Do not
|
|
22
|
+
* rely on colour alone (WCAG 3.3.1 / 1.3.1 / 4.1.2).
|
|
23
|
+
*/
|
|
24
|
+
setInvalid(errorId: string): void;
|
|
25
|
+
/** Clear aria-invalid and unlink the error text once the field is corrected. */
|
|
26
|
+
clearInvalid(): void;
|
|
18
27
|
}
|
|
19
28
|
export interface QuestionInputOptions {
|
|
20
29
|
question: Question;
|