@qaiddev/quests-embed 1.4.0 → 1.5.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/README.md +56 -6
- package/dist/embed.d.ts +54 -1
- package/dist/index.d.ts +1 -1
- package/dist/qaid-quests.js +476 -323
- package/dist/qaid-quests.js.map +1 -1
- package/dist/qaid-quests.umd.cjs +5 -5
- package/dist/qaid-quests.umd.cjs.map +1 -1
- package/dist/types.d.ts +20 -6
- package/dist/visibility.d.ts +16 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -78,8 +78,8 @@ For complex configurations or inline questionnaires, use a separate JSON config
|
|
|
78
78
|
2. A response record is created on your server (`POST` to `endpoint`)
|
|
79
79
|
3. Questions render one at a time with a progress bar and Back/Next navigation
|
|
80
80
|
4. Answers autosave as the user moves between steps (`PATCH` per question). Text and currency fields debounce by `saveDebounceMs` (default 500ms)
|
|
81
|
-
5. On the final step,
|
|
82
|
-
6. A "thank you" screen appears
|
|
81
|
+
5. On the final step, the answers to the questions still visible are submitted in a single batch (`POST` to `{endpoint}/{id}/submit`). An answer to a question that `visibleIf` has since hidden is left out, and cleared on the server with a `PATCH` of `null`
|
|
82
|
+
6. A "thank you" screen appears once the server accepts the submit. If the create or the submit fails (a network error or a non-2xx reply), the embed shows "Couldn't send your answers" with a **Try again** button instead; the answers are kept, and Try again re-creates the response first if it has to
|
|
83
83
|
|
|
84
84
|
If `container` is set, the form renders inline inside that element. If not, it opens as a centered modal with a backdrop.
|
|
85
85
|
|
|
@@ -242,7 +242,7 @@ We offer a Free Plan that hosts both the endpoint and a dashboard for managing y
|
|
|
242
242
|
|
|
243
243
|
| Option | Type | Default | Description |
|
|
244
244
|
|--------|------|---------|-------------|
|
|
245
|
-
| `autoAdvance` | `boolean` | `false` | Advance automatically on
|
|
245
|
+
| `autoAdvance` | `boolean` | `false` | Advance automatically when an option is picked on a single-choice multiple-choice question. No other type auto-advances (multi-select, text, currency, range and date wait for Next / Enter) |
|
|
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
|
|
|
@@ -253,7 +253,7 @@ For programmatic embedding — e.g. launching a quest from another widget — th
|
|
|
253
253
|
| Option | Type | Default | Description |
|
|
254
254
|
|--------|------|---------|-------------|
|
|
255
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
|
|
256
|
+
| `onComplete` | `(answers) => void` | — | Called once when the quest is completed and the server accepted the submit (just after the thank-you screen renders), with a copy of the submitted answers — answers to questions hidden by `visibleIf` are left out. Not called while the submit is failing. Fires in both modal and inline modes. |
|
|
257
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
258
|
|
|
259
259
|
```typescript
|
|
@@ -348,8 +348,52 @@ const quests = new QaidQuests(config: QuestsConfig);
|
|
|
348
348
|
| Method | Description |
|
|
349
349
|
|--------|-------------|
|
|
350
350
|
| `getAnswers()` | Read-only snapshot of the answers collected so far, keyed by question id |
|
|
351
|
+
| `getCurrentQuestionId()` | Id of the question on screen, or `null` if the embed isn't on one (loading, finished, or unmounted) |
|
|
352
|
+
| `goToStep(questionId)` | Jump to a visible question. Returns `false` if it's unknown or hidden by an unmet `visibleIf`. Latched if the embed is still initializing |
|
|
353
|
+
| `update(questionnaire)` | Swap in a new questionnaire without re-mounting. Returns whether it was applied — see below |
|
|
351
354
|
| `destroy()` | Remove all DOM elements, event listeners, and injected styles. Safe to call multiple times |
|
|
352
355
|
|
|
356
|
+
#### `update(questionnaire)`
|
|
357
|
+
|
|
358
|
+
For hosts that render a questionnaire while it is being authored — a live
|
|
359
|
+
preview in an editor, say. Rebuilding the embed on every edit re-mounts the
|
|
360
|
+
shadow root, repaints the loading state, and re-runs both the theme fetch and
|
|
361
|
+
the create call; because that path awaits the network it always paints a blank
|
|
362
|
+
frame first, which is what makes a live preview flicker as the author types.
|
|
363
|
+
|
|
364
|
+
`update()` re-renders the header and the current step, synchronously. The
|
|
365
|
+
shadow root, the resolved theme, the response id and the answers so far all
|
|
366
|
+
survive, and focus is left wherever the host put it — it does not pull focus
|
|
367
|
+
into the form the way stepping through it does.
|
|
368
|
+
|
|
369
|
+
Answers are kept for questions that still exist and dropped for ones that
|
|
370
|
+
don't, so `getAnswers()` never reports an id the questionnaire has no question
|
|
371
|
+
for. The reader's place is kept the same way: if the question on screen is
|
|
372
|
+
still present and visible, the embed stays on it; otherwise the step index is
|
|
373
|
+
clamped into range.
|
|
374
|
+
|
|
375
|
+
It returns `false`, leaving the embed exactly as it was, when:
|
|
376
|
+
|
|
377
|
+
- the questionnaire has no questions,
|
|
378
|
+
- every question is gated off behind an unmet `visibleIf`, so there would be
|
|
379
|
+
nothing to render,
|
|
380
|
+
- or the reader has already completed the form — resuming a submitted response
|
|
381
|
+
isn't something the embed can decide for you, so rebuild if you want the new
|
|
382
|
+
form there.
|
|
383
|
+
|
|
384
|
+
Called before the embed has finished initializing, the update is latched and
|
|
385
|
+
applied as soon as it is ready (and reported as applied), the same way
|
|
386
|
+
`goToStep` is.
|
|
387
|
+
|
|
388
|
+
```js
|
|
389
|
+
const quests = new QaidQuests({ endpoint, questionnaire, container: "#preview" });
|
|
390
|
+
|
|
391
|
+
// later, as the author edits — no teardown, no refetch, no flicker
|
|
392
|
+
if (!quests.update(nextQuestionnaire)) {
|
|
393
|
+
// refused: rebuild if you need the new questionnaire on screen
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
353
397
|
### Server Protocol
|
|
354
398
|
|
|
355
399
|
The embed talks to your endpoint in three steps:
|
|
@@ -378,8 +422,14 @@ Your endpoint should return `{ "id": "..." }` (string or number). The form is us
|
|
|
378
422
|
```
|
|
379
423
|
|
|
380
424
|
`value` is `string | number | string[] | null` depending on the question type.
|
|
381
|
-
|
|
382
|
-
|
|
425
|
+
Just before submitting, the embed sends `"value": null` for every answered
|
|
426
|
+
question that `visibleIf` has since hidden, so a server that stores answers
|
|
427
|
+
from these PATCHes drops them too.
|
|
428
|
+
|
|
429
|
+
**3. Submit** — `POST {endpoint}/{id}/submit`. `answers` holds only the
|
|
430
|
+
questions visible at submit time. Reply with a 2xx; anything else (or a
|
|
431
|
+
network error) shows the visitor a retry screen instead of the thank-you
|
|
432
|
+
screen:
|
|
383
433
|
|
|
384
434
|
```json
|
|
385
435
|
{
|
package/dist/embed.d.ts
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
import type { Answers, QuestsConfig } from "./types";
|
|
1
|
+
import type { Answers, Questionnaire, QuestsConfig } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* A random v4 UUID.
|
|
4
|
+
*
|
|
5
|
+
* `crypto.randomUUID` exists only in secure contexts (HTTPS and localhost). On
|
|
6
|
+
* a plain http:// page it is undefined, and calling it threw while mounting, so
|
|
7
|
+
* the quest never appeared. `crypto.getRandomValues` has no such restriction;
|
|
8
|
+
* `Math.random` covers a runtime with no `crypto` at all. The id only groups
|
|
9
|
+
* one browser's responses, so it needs to be unique, not secret.
|
|
10
|
+
*/
|
|
11
|
+
export declare function randomId(): string;
|
|
2
12
|
export declare class QaidQuests {
|
|
3
13
|
private config;
|
|
4
14
|
private questionnaire;
|
|
@@ -8,10 +18,16 @@ export declare class QaidQuests {
|
|
|
8
18
|
private stepIndex;
|
|
9
19
|
private visibleQuestions;
|
|
10
20
|
private pendingGoToStep;
|
|
21
|
+
private pendingUpdate;
|
|
22
|
+
private suppressStepFocus;
|
|
11
23
|
private hasRenderedStep;
|
|
12
24
|
private answers;
|
|
13
25
|
private responseId;
|
|
14
26
|
private visitorId;
|
|
27
|
+
private createPromise;
|
|
28
|
+
private createPending;
|
|
29
|
+
private droppedSaves;
|
|
30
|
+
private submitting;
|
|
15
31
|
private pendingSaveTimer;
|
|
16
32
|
private pendingSaveQuestionId;
|
|
17
33
|
private pendingSaveValue;
|
|
@@ -70,6 +86,13 @@ export declare class QaidQuests {
|
|
|
70
86
|
private renderHeader;
|
|
71
87
|
private renderStep;
|
|
72
88
|
private renderDone;
|
|
89
|
+
/**
|
|
90
|
+
* Shown instead of the thank-you screen when the response could not be
|
|
91
|
+
* created or the submit was refused. The answers stay in memory, so
|
|
92
|
+
* "Try again" re-runs the whole submit (re-creating the response first
|
|
93
|
+
* if needed) without the visitor re-entering anything.
|
|
94
|
+
*/
|
|
95
|
+
private renderSubmitError;
|
|
73
96
|
private advance;
|
|
74
97
|
private back;
|
|
75
98
|
private recomputeVisible;
|
|
@@ -77,8 +100,11 @@ export declare class QaidQuests {
|
|
|
77
100
|
private flushPendingSave;
|
|
78
101
|
private saveAnswer;
|
|
79
102
|
private doSave;
|
|
103
|
+
private patchAnswer;
|
|
104
|
+
private startCreate;
|
|
80
105
|
private createResponse;
|
|
81
106
|
private submit;
|
|
107
|
+
private trySubmit;
|
|
82
108
|
private jsonHeaders;
|
|
83
109
|
private showSaving;
|
|
84
110
|
private handleKeyDown;
|
|
@@ -101,6 +127,33 @@ export declare class QaidQuests {
|
|
|
101
127
|
* user's place after a forced re-mount.
|
|
102
128
|
*/
|
|
103
129
|
getCurrentQuestionId(): string | null;
|
|
130
|
+
/**
|
|
131
|
+
* Swap in a new questionnaire without tearing the embed down.
|
|
132
|
+
*
|
|
133
|
+
* Built for editor previews, where the alternative — `destroy()` plus a
|
|
134
|
+
* fresh construction on every edit — re-mounts the shadow root, repaints
|
|
135
|
+
* the loading state, and re-runs both the theme fetch and the create
|
|
136
|
+
* call. Because that path awaits the network it always paints a blank
|
|
137
|
+
* frame first, which is what makes a live preview strobe while the author
|
|
138
|
+
* types. This re-renders the header and the current step and nothing
|
|
139
|
+
* else, synchronously: the shadow root, the resolved theme, the response
|
|
140
|
+
* id and the answers so far all survive.
|
|
141
|
+
*
|
|
142
|
+
* Answers are kept for questions that still exist and dropped for ones
|
|
143
|
+
* that don't, so `getAnswers()` never reports an id the questionnaire has
|
|
144
|
+
* no question for. The reader's place is kept the same way: if the
|
|
145
|
+
* question on screen is still present and visible, the embed stays on it,
|
|
146
|
+
* otherwise the step index is clamped into range.
|
|
147
|
+
*
|
|
148
|
+
* Returns whether the update was applied. It is refused, leaving the
|
|
149
|
+
* embed exactly as it was, when the questionnaire has no questions, or
|
|
150
|
+
* once the reader has completed the form — resuming a submitted response
|
|
151
|
+
* is not something this can decide on the host's behalf, so a host that
|
|
152
|
+
* wants the new form there should rebuild. Called before the embed has
|
|
153
|
+
* finished initializing, the update is latched and applied as soon as it
|
|
154
|
+
* is ready (and reported as applied), the same way `goToStep` is.
|
|
155
|
+
*/
|
|
156
|
+
update(questionnaire: Questionnaire): boolean;
|
|
104
157
|
/**
|
|
105
158
|
* Jump to the visible question with the given id. Returns true if
|
|
106
159
|
* the question is currently visible (and the embed navigated to
|
package/dist/index.d.ts
CHANGED
|
@@ -32,5 +32,5 @@
|
|
|
32
32
|
*/
|
|
33
33
|
import { QaidQuests } from "./embed";
|
|
34
34
|
export { QaidQuests };
|
|
35
|
-
export { getVisibleQuestions, evaluateRule } from "./visibility";
|
|
35
|
+
export { getVisibleQuestions, getVisibleAnswers, evaluateRule, } from "./visibility";
|
|
36
36
|
export type { QuestsConfig, ResolvedQuestsConfig, Questionnaire, Question, TextQuestion, CurrencyQuestion, RangeQuestion, DateQuestion, MultipleChoiceQuestion, MultipleChoiceOption, Answers, AnswerValue, VisibilityRule, CreateResponsePayload, CreateResponseResult, UpdateAnswerPayload, SubmitPayload, } from "./types";
|