@qaiddev/quests-embed 1.1.1 → 1.3.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/README.md +21 -0
- package/dist/a11y.d.ts +93 -0
- package/dist/embed.d.ts +14 -0
- package/dist/inputs.d.ts +9 -0
- package/dist/qaid-quests.js +422 -243
- 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/dist/types.d.ts +23 -0
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -246,6 +246,27 @@ We offer a Free Plan that hosts both the endpoint and a dashboard for managing y
|
|
|
246
246
|
| `saveDebounceMs` | `number` | `500` | Debounce in ms for autosave on text/currency/range |
|
|
247
247
|
| `autoFocus` | `boolean` | `true` | Auto-focus the input on each step. Set `false` in preview/embedded contexts that shouldn't steal focus |
|
|
248
248
|
|
|
249
|
+
### Host integration
|
|
250
|
+
|
|
251
|
+
For programmatic embedding — e.g. launching a quest from another widget — these hooks let the host pass correlation data in and react to the quest's lifecycle. They're only useful when you construct `QaidQuests` yourself (not via the auto-init `<script>` tag).
|
|
252
|
+
|
|
253
|
+
| Option | Type | Default | Description |
|
|
254
|
+
|--------|------|---------|-------------|
|
|
255
|
+
| `metadata` | `Record<string, unknown>` | — | Extra key/value pairs sent verbatim in the create-response `POST` body. The server decides which keys it persists; unknown keys are ignored. Used, for example, by `@qaiddev/thumbs-embed` to pass `{ feedbackId }` so the QAid backend joins the quest answers to the feedback record. |
|
|
256
|
+
| `onComplete` | `(answers) => void` | — | Called once when the quest is completed and submitted (just after the thank-you screen renders), with a copy of the collected answers. Fires in both modal and inline modes. |
|
|
257
|
+
| `onClose` | `() => void` | — | Called once when the embed is torn down — visitor close, host `destroy()`, or teardown after completion. Lets a host that launched the quest drop its reference. |
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
const quest = new QaidQuests({
|
|
261
|
+
endpoint: 'https://qaid.dev/api/quests/responses',
|
|
262
|
+
configUrl: 'https://qaid.dev/api/quests/<questId>/definition',
|
|
263
|
+
apiKey: 'YOUR_API_KEY',
|
|
264
|
+
metadata: { feedbackId: 'clx…' }, // correlate this response server-side
|
|
265
|
+
onComplete: (answers) => console.log('done', answers),
|
|
266
|
+
onClose: () => { /* drop your reference */ },
|
|
267
|
+
});
|
|
268
|
+
```
|
|
269
|
+
|
|
249
270
|
### Appearance
|
|
250
271
|
|
|
251
272
|
| Option | Type | Default | Description |
|
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,12 +31,19 @@ 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;
|
|
37
40
|
private themeUrl;
|
|
38
41
|
private themeDocument;
|
|
39
42
|
private hostCss;
|
|
43
|
+
private metadata;
|
|
44
|
+
private onCompleteCb;
|
|
45
|
+
private onCloseCb;
|
|
46
|
+
private closed;
|
|
40
47
|
constructor(config: QuestsConfig);
|
|
41
48
|
private init;
|
|
42
49
|
private loadTheme;
|
|
@@ -73,6 +80,13 @@ export declare class QaidQuests {
|
|
|
73
80
|
private jsonHeaders;
|
|
74
81
|
private showSaving;
|
|
75
82
|
private handleKeyDown;
|
|
83
|
+
/**
|
|
84
|
+
* Modal mode only: trap Tab within the card and mark the rest of the
|
|
85
|
+
* page inert while the dialog is open. Runs once (guarded) after the
|
|
86
|
+
* first step renders. Inline mode keeps normal page tab flow and is
|
|
87
|
+
* never trapped or isolated.
|
|
88
|
+
*/
|
|
89
|
+
private setupModalA11y;
|
|
76
90
|
private close;
|
|
77
91
|
/** Destroy the embed and clean up all resources */
|
|
78
92
|
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;
|