@explorer02/cfm-survey-sdk 0.2.1 → 0.2.2
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/cli/index.js +18 -18
- package/dist/cli/index.mjs +14 -14
- package/dist/index.d.mts +465 -580
- package/dist/index.d.ts +465 -580
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +6 -4
- package/postinstall.js +96 -69
- package/templates/AGENT.md +14 -8
- package/templates/docs/00-integration/answer-logic-and-navigation.md +50 -0
- package/templates/docs/00-integration/aws-deploy.md +281 -0
- package/templates/docs/00-integration/client-integration-guide.md +351 -0
- package/templates/docs/00-integration/component-checklist.md +45 -22
- package/templates/docs/00-integration/constraints.md +49 -4
- package/templates/docs/00-integration/custom-field-logic-and-navigation.md +174 -0
- package/templates/docs/00-integration/display-logic-and-navigation.md +49 -0
- package/templates/docs/00-integration/end-page-logic.md +64 -0
- package/templates/docs/00-integration/partial-save-and-recovery.md +74 -0
- package/templates/docs/00-integration/placeholders-and-tokens.md +116 -0
- package/templates/docs/00-integration/progress.md +61 -0
- package/templates/docs/00-integration/question-display-variants.md +65 -0
- package/templates/docs/00-integration/question-numbering.md +51 -0
- package/templates/docs/00-integration/question-type-sdk-matrix.md +242 -0
- package/templates/docs/00-integration/setup.md +84 -0
- package/templates/docs/00-integration/skip-logic-and-navigation.md +80 -0
- package/templates/docs/00-integration/survey-lifecycle-analytics.md +94 -0
- package/templates/docs/00-integration/useSurveySDK.md +151 -23
- package/templates/docs/01-components/01-survey-page.md +76 -54
- package/templates/docs/01-components/02-question.md +61 -58
- package/templates/docs/01-components/03-rating-scale.md +11 -1
- package/templates/docs/01-components/04-star-rating-scale.md +45 -0
- package/templates/docs/01-components/05-csat-matrix-scale.md +20 -11
- package/templates/docs/01-components/06-likert-matrix-scale.md +12 -3
- package/templates/docs/01-components/07-slider-matrix-scale.md +11 -2
- package/templates/docs/01-components/08-file-upload-scale.md +10 -1
- package/templates/docs/01-components/09-custom-slider-track.md +2 -2
- package/templates/docs/01-components/11-progress-bar.md +10 -4
- package/templates/docs/01-components/13-matrix-dropdown.md +3 -1
- package/templates/docs/01-components/14-intro-page.md +34 -0
- package/templates/docs/01-components/15-end-page.md +31 -0
- package/templates/docs/01-components/16-paused-page.md +27 -0
- package/templates/docs/01-components/17-heatmap-scale.md +75 -0
- package/templates/docs/01-components/18-rank-order-scale.md +100 -0
- package/templates/docs/01-components/README.md +42 -36
- package/templates/docs/02-reference/config-field-index.md +141 -139
- package/templates/docs/02-reference/exports.md +149 -0
- package/templates/docs/02-reference/question-types/01-rating.md +31 -32
- package/templates/docs/02-reference/question-types/04-csat.md +51 -42
- package/templates/docs/02-reference/question-types/05-rating-scale.md +16 -11
- package/templates/docs/02-reference/question-types/06-slider.md +2 -2
- package/templates/docs/02-reference/question-types/07-matrix-cfm.md +44 -30
- package/templates/docs/02-reference/question-types/08-matrix-csat.md +4 -26
- package/templates/docs/02-reference/question-types/09-matrix-rating.md +29 -13
- package/templates/docs/02-reference/question-types/10-slider-matrix.md +15 -4
- package/templates/docs/02-reference/question-types/11-file-upload.md +13 -2
- package/templates/docs/02-reference/question-types/12-heatmap.md +59 -0
- package/templates/docs/02-reference/question-types/13-rank-order.md +101 -0
- package/templates/docs/02-reference/question-types/README.md +86 -47
- package/templates/docs/02-reference/routing-table.md +33 -18
- package/templates/docs/02-reference/value-derivation.md +63 -0
- package/templates/docs/03-ui-specs/00-question-shell.md +19 -88
- package/templates/docs/03-ui-specs/01-rating.md +12 -54
- package/templates/docs/03-ui-specs/02-radio.md +21 -33
- package/templates/docs/03-ui-specs/03-text.md +2 -2
- package/templates/docs/03-ui-specs/04-csat.md +38 -33
- package/templates/docs/03-ui-specs/05-rating-scale.md +21 -18
- package/templates/docs/03-ui-specs/06-slider.md +3 -1
- package/templates/docs/03-ui-specs/07-matrix-cfm.md +24 -21
- package/templates/docs/03-ui-specs/08-matrix-csat-rating.md +17 -45
- package/templates/docs/03-ui-specs/09-slider-matrix.md +1 -1
- package/templates/docs/03-ui-specs/10-file-upload.md +1 -1
- package/templates/docs/03-ui-specs/11-text-and-media.md +29 -29
- package/templates/docs/03-ui-specs/12-survey-chrome.md +20 -14
- package/templates/docs/03-ui-specs/13-heatmap.md +73 -0
- package/templates/docs/03-ui-specs/14-rank-order.md +59 -0
- package/templates/docs/03-ui-specs/README.md +13 -13
- package/templates/docs/03-ui-specs/shared/custom-slider-track.md +2 -2
- package/templates/docs/03-ui-specs/shared/matrix-dropdown.md +5 -0
- package/templates/docs/MANIFEST.json +451 -69
- package/templates/docs/index.md +80 -29
- package/templates/docs/templates/Question.tsx +221 -154
- package/templates/docs/templates/deploy-to-aws.sh +114 -0
- package/templates/docs/templates/heatmapCoords.ts +58 -0
- package/templates/docs/templates/implementation_plan.md +108 -69
- package/templates/docs/templates/survey-inventory.schema.json +72 -0
- package/templates/docs/templates/verify-agent-build.sh +53 -0
- package/templates/docs/01-components/04-csat-scale.md +0 -40
- package/templates/docs/02-question-types/01-rating.md +0 -44
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
# UI Spec: CFM Matrix (`type: '
|
|
1
|
+
# UI Spec: CFM Matrix (`type: 'CFM_MATRIX'`)
|
|
2
2
|
|
|
3
3
|
> **Implement from this spec** (npm installs). Monorepo reference: `apps/client/src/components/LikertMatrixScale.tsx`
|
|
4
|
-
> **SDK type:** `
|
|
4
|
+
> **SDK type:** `CfmMatrixQuestion` — `fetchSurvey/types/cfmMatrix.ts`
|
|
5
5
|
> **Component blueprint:** `01-components/06-likert-matrix-scale.md`
|
|
6
6
|
|
|
7
7
|
## Config Inventory
|
|
8
8
|
|
|
9
9
|
| Field | Implemented | UI impact |
|
|
10
10
|
|-------|-------------|-----------|
|
|
11
|
-
| `
|
|
12
|
-
| `
|
|
13
|
-
| `
|
|
14
|
-
| `
|
|
15
|
-
| `
|
|
11
|
+
| `statementRows[]`, `scaleColumns[]` | ✅ | Grid / carousel / dropdown data |
|
|
12
|
+
| `gridLayout` | ✅ | Orchestrator routing |
|
|
13
|
+
| `selectionMode` | ✅ | `'multiple'` → checkboxes |
|
|
14
|
+
| `statementLayout: 'bipolar'` | ✅ | 25% left + 25% right columns |
|
|
15
|
+
| `scaleAnchorLabels[]` | ✅ | Anchor labels above scale |
|
|
16
16
|
| `transposeTable` | ✅ | Swap row/col iteration |
|
|
17
|
-
| `
|
|
18
|
-
| `
|
|
19
|
-
| `
|
|
17
|
+
| `repeatColumnHeaders` | ✅ | Re-render header per row |
|
|
18
|
+
| `showColumnHeaders` | ✅ | Show/hide column header row |
|
|
19
|
+
| `hasNotApplicableOption` | ✅ | N/A column with mutex |
|
|
20
20
|
|
|
21
21
|
## Config Decision Tree
|
|
22
22
|
|
|
23
23
|
```
|
|
24
24
|
CFM_MATRIX
|
|
25
|
-
├──
|
|
26
|
-
├──
|
|
25
|
+
├── gridLayout === 'dropdown' → LikertMatrixDropdownLayout (MatrixDropdown per row)
|
|
26
|
+
├── gridLayout === 'carousel' → LikertMatrixCarouselLayout (one row, prev/next, blue dots)
|
|
27
27
|
└── else → LikertMatrixGridLayout
|
|
28
28
|
├── transposeTable ? columns×rows : rows×columns
|
|
29
|
-
├──
|
|
30
|
-
├──
|
|
31
|
-
└──
|
|
29
|
+
├── repeatColumnHeaders ? header per row : single header
|
|
30
|
+
├── showColumnHeaders ? column headers : hidden
|
|
31
|
+
└── statementLayout === 'bipolar' ? 25% | scale | 25% layout
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
## Interactive Behaviors
|
|
@@ -37,8 +37,8 @@ CFM_MATRIX
|
|
|
37
37
|
|----------|-----------|--------|
|
|
38
38
|
| Click selected radio again | single-select grid/carousel | Clear row (`undefined`) |
|
|
39
39
|
| N/A selected | any | Other cols opacity 0.4, disabled |
|
|
40
|
-
| Multi N/A |
|
|
41
|
-
| Multi toggle |
|
|
40
|
+
| Multi N/A | selectionMode multiple | Sets `[null]` exclusively |
|
|
41
|
+
| Multi toggle | selectionMode multiple | Add/remove from array |
|
|
42
42
|
|
|
43
43
|
## Grid Layout
|
|
44
44
|
|
|
@@ -53,14 +53,17 @@ CFM_MATRIX
|
|
|
53
53
|
|
|
54
54
|
## Answer Mutation
|
|
55
55
|
|
|
56
|
+
Store **`column.id`** via `matrixColumnStoredValue(col)` — see `02-reference/value-derivation.md`.
|
|
57
|
+
|
|
56
58
|
```typescript
|
|
57
|
-
onSelect(
|
|
59
|
+
onSelect(MatrixRowAnswers) // Record<statementRows[].id, value | value[] | null>
|
|
60
|
+
onAction({ type: 'CHANGE', payload: { questionId, answerValue: MatrixRowAnswers } })
|
|
58
61
|
```
|
|
59
62
|
|
|
60
63
|
## Agent Checklist
|
|
61
64
|
|
|
62
|
-
- [ ] All three
|
|
63
|
-
- [ ] transposeTable +
|
|
64
|
-
- [ ] Bipolar 25/50/25 layout with
|
|
65
|
+
- [ ] All three gridLayout branches
|
|
66
|
+
- [ ] transposeTable + repeatColumnHeaders + showColumnHeaders
|
|
67
|
+
- [ ] Bipolar 25/50/25 layout with oppositeStatementText
|
|
65
68
|
- [ ] Single-select deselect on re-click
|
|
66
69
|
- [ ] N/A mutex with opacity 0.4
|
|
@@ -1,65 +1,37 @@
|
|
|
1
|
-
# UI Spec:
|
|
1
|
+
# UI Spec: Rating Matrix Grid (`type: 'RATING_MATRIX'`)
|
|
2
2
|
|
|
3
3
|
> **Implement from this spec** (npm installs). Monorepo reference: `apps/client/src/components/CsatMatrixScale.tsx`
|
|
4
|
-
> **SDK type:** `
|
|
4
|
+
> **SDK type:** `RatingMatrixQuestion` — `fetchSurvey/types/ratingMatrix.ts`
|
|
5
5
|
> **Component blueprint:** `01-components/05-csat-matrix-scale.md`
|
|
6
6
|
|
|
7
7
|
## Config Inventory
|
|
8
8
|
|
|
9
9
|
| Field | Implemented | UI impact |
|
|
10
10
|
|-------|-------------|-----------|
|
|
11
|
-
| `
|
|
12
|
-
| `
|
|
13
|
-
| `
|
|
11
|
+
| `statementRows[]`, `scaleColumns[]` | ✅ | Grid / carousel / dropdown data |
|
|
12
|
+
| `displayStyle` | ✅ | Grid vs vertical list routing |
|
|
13
|
+
| `gridLayout` | ✅ | carousel vs standard vs dropdown |
|
|
14
|
+
| `hasNotApplicableOption` | ✅ | N/A column or checkbox |
|
|
14
15
|
| `reverseScaleOrder` | ✅ | Flip emoji/star/number index |
|
|
15
|
-
| `
|
|
16
|
-
| `
|
|
16
|
+
| `scaleAnchorLabels[]` | ✅ | Anchor row above columns |
|
|
17
|
+
| `minLabel` / `maxLabel` | ✅ | Rendered in Question.tsx above scale |
|
|
18
|
+
| `scaleColumns[].accentColor` | ✅ | NPS-style badge colors when numbered |
|
|
17
19
|
|
|
18
20
|
## Config Decision Tree
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
CSAT_MATRIX | RATING_MATRIX
|
|
22
|
-
├── matrixFormat === 'carousel' → CsatMatrixCarousel
|
|
23
|
-
├── buttonType in (dropdown, selectbox, radio) → CsatMatrixVerticalList
|
|
24
|
-
│ ├── dropdown → MatrixDropdown placeholder "Select..."
|
|
25
|
-
│ ├── selectbox → full-width cards #fdf2f8 selected
|
|
26
|
-
│ └── else → card radio list
|
|
27
|
-
└── else → CsatMatrixGrid
|
|
28
|
-
├── emoji → getEmojiForIndex + hover scale + ring when selected
|
|
29
|
-
├── star → opacity 0.5 unselected; filled/empty icons
|
|
30
|
-
├── numbered → 40×40 badge; selected magenta fill
|
|
31
|
-
└── graphics → CustomSliderTrack per row (not in grid — carousel only)
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## Grid Structure
|
|
35
|
-
|
|
36
|
-
- Row label: `180px` fixed column
|
|
37
|
-
- N/A column: `56px` when `hasNotApplicable`
|
|
38
|
-
- Alternating row backgrounds
|
|
39
|
-
- Column headers OR `labels[]` anchor row with percentage positioning
|
|
40
|
-
|
|
41
|
-
## Carousel
|
|
42
|
-
|
|
43
|
-
- Pink active dot expands: `20px × 8px` vs `8px` inactive
|
|
44
|
-
- Column labels below cells
|
|
45
|
-
- Emoji tooltip via hover state (`hoveredCell`)
|
|
46
|
-
- Graphics mode: slider maps column index
|
|
47
|
-
|
|
48
|
-
## Tooltips
|
|
49
|
-
|
|
50
|
-
`title={col.label}` recommended on grid cells (emoji hover uses scale transform).
|
|
22
|
+
Same as `CSAT_MATRIX` grid — reuse `CsatMatrixScale` component. See [04-csat.md](04-csat.md) decision tree with `type: 'RATING_MATRIX'`.
|
|
51
23
|
|
|
52
24
|
## Answer Mutation
|
|
53
25
|
|
|
26
|
+
Store **`column.id`** via `matrixColumnStoredValue(col)` — see `02-reference/value-derivation.md`.
|
|
27
|
+
|
|
54
28
|
```typescript
|
|
55
|
-
onSelect(
|
|
56
|
-
|
|
29
|
+
onSelect(MatrixRowAnswers)
|
|
30
|
+
onAction({ type: 'CHANGE', payload: { questionId, answerValue: MatrixRowAnswers } })
|
|
57
31
|
```
|
|
58
32
|
|
|
59
33
|
## Agent Checklist
|
|
60
34
|
|
|
61
|
-
- [ ]
|
|
62
|
-
- [ ]
|
|
63
|
-
- [ ]
|
|
64
|
-
- [ ] Carousel pink dot indicators
|
|
65
|
-
- [ ] Vertical selectbox cards match standalone radio styling
|
|
35
|
+
- [ ] Reuse CsatMatrixScale with `RATING_MATRIX` type
|
|
36
|
+
- [ ] Apply accentColor on numbered badges when present
|
|
37
|
+
- [ ] minLabel / maxLabel above grid when set
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# UI Spec: Slider Matrix (`type: '
|
|
1
|
+
# UI Spec: Slider Matrix (`type: 'SLIDER_MATRIX'`)
|
|
2
2
|
|
|
3
3
|
> **Implement from this spec** (npm installs). Monorepo reference: `apps/client/src/components/SliderMatrixScale.tsx`
|
|
4
4
|
> **SDK type:** `SliderMatrixQuestion` — `fetchSurvey/types/sliderMatrix.ts`
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# UI Spec: File Upload (`type: '
|
|
1
|
+
# UI Spec: File Upload (`type: 'FILE_UPLOAD'`)
|
|
2
2
|
|
|
3
3
|
> **Implement from this spec** (npm installs). Monorepo reference: `apps/client/src/components/FileUploadScale.tsx`
|
|
4
4
|
> **SDK type:** `FileUploadQuestion` — `fetchSurvey/types/fileUpload.ts`
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# UI Spec: Text and Media (`type: '
|
|
1
|
+
# UI Spec: Text and Media (`type: 'TEXT_AND_MEDIA'`)
|
|
2
2
|
|
|
3
3
|
> **Reference:** `apps/client/src/components/Question.tsx` (inline)
|
|
4
4
|
> **SDK type:** `TextAndMediaQuestion` — `fetchSurvey/types/textAndMedia.ts`
|
|
@@ -8,42 +8,42 @@
|
|
|
8
8
|
|
|
9
9
|
| Field | Implemented | UI impact |
|
|
10
10
|
|-------|-------------|-----------|
|
|
11
|
-
| `
|
|
12
|
-
| `
|
|
13
|
-
| `
|
|
14
|
-
| `
|
|
15
|
-
| `
|
|
11
|
+
| `media?.url` | ✅ | Required to render media block |
|
|
12
|
+
| `media?.mimeType` | ✅ | `video/` or `VIDEO` → `<video>`; else `<img>` |
|
|
13
|
+
| `media?.title` | ✅ | Caption below media, centered gray text |
|
|
14
|
+
| `media?.alignment` | ✅ | Flex justify: CENTER / RIGHT / default left |
|
|
15
|
+
| `media?.size` | ✅ | Width `%` on media container; default 100% |
|
|
16
16
|
|
|
17
17
|
No answer input — display-only question.
|
|
18
18
|
|
|
19
|
-
##
|
|
19
|
+
## Media alignment anatomy
|
|
20
20
|
|
|
21
21
|
```
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
│
|
|
27
|
-
│
|
|
28
|
-
│
|
|
29
|
-
|
|
22
|
+
┌─────────────────────────────────────────┐
|
|
23
|
+
│ questionText (HTML) │
|
|
24
|
+
│ questionDescription (optional) │
|
|
25
|
+
├─────────────────────────────────────────┤
|
|
26
|
+
│ [ media block — flex container ] │
|
|
27
|
+
│ alignment LEFT: justify-start │
|
|
28
|
+
│ alignment CENTER: justify-center │
|
|
29
|
+
│ alignment RIGHT: justify-end │
|
|
30
|
+
│ width: media.size % (default 100) │
|
|
31
|
+
│ <img> or <video> max-w-full │
|
|
32
|
+
│ caption: media.title (centered gray) │
|
|
33
|
+
└─────────────────────────────────────────┘
|
|
30
34
|
```
|
|
31
35
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
| `mediaAlignment` | CSS |
|
|
35
|
-
|------------------|-----|
|
|
36
|
-
| `TOP_CENTER`, `BOTTOM_CENTER` | `justify-center` |
|
|
37
|
-
| `TOP_RIGHT`, `BOTTOM_RIGHT` | `justify-end` |
|
|
38
|
-
| default | `justify-start` |
|
|
39
|
-
|
|
40
|
-
## Answer Mutation
|
|
41
|
-
|
|
42
|
-
None — no `onSelect` payload for this type.
|
|
36
|
+
Container-level media (survey chrome) is separate — see `03-ui-specs/12-survey-chrome.md`.
|
|
43
37
|
|
|
44
38
|
## Agent Checklist
|
|
45
39
|
|
|
46
|
-
- [ ] Render only when `
|
|
47
|
-
- [ ] Video vs image from
|
|
40
|
+
- [ ] Render only when `question.media?.url` is present
|
|
41
|
+
- [ ] Video vs image from `media.mimeType`
|
|
48
42
|
- [ ] `max-w-full rounded-md shadow-sm` on media element
|
|
49
|
-
- [ ] Optional centered caption for `
|
|
43
|
+
- [ ] Optional centered caption for `media.title`
|
|
44
|
+
- [ ] Respect `media.alignment` (LEFT / CENTER / RIGHT) and `media.size` width %
|
|
45
|
+
|
|
46
|
+
## SDK Integration Checklist (agent)
|
|
47
|
+
|
|
48
|
+
- [ ] No CHANGE action — display-only
|
|
49
|
+
- [ ] Matrix row: [`question-type-sdk-matrix.md#text_and_media`](../00-integration/question-type-sdk-matrix.md#text_and_media)
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
# UI Spec: Survey Chrome
|
|
2
2
|
|
|
3
|
-
> **Reference:** `SurveyPage.tsx`, `Header.tsx`, `Footer.tsx`, `ProgressBar.tsx`, `LanguageSelector.tsx`
|
|
4
|
-
> **Component blueprints:** `01-components/01-survey-page.md`, `10-header-footer.md`, `11-progress-bar.md`, `12-language-selector.md`
|
|
3
|
+
> **Reference:** `SurveyPage.tsx`, `Header.tsx`, `Footer.tsx`, `ProgressBar.tsx`, `LanguageSelector.tsx`, `IntroPage.tsx`, `EndPage.tsx`, `PausedPage.tsx`
|
|
4
|
+
> **Component blueprints:** `01-components/01-survey-page.md`, `10-header-footer.md`, `11-progress-bar.md`, `12-language-selector.md`, `14-intro-page.md`, `15-end-page.md`, `16-paused-page.md`
|
|
5
5
|
|
|
6
|
-
## SurveyPage —
|
|
6
|
+
## SurveyPage — Phase Router
|
|
7
7
|
|
|
8
8
|
| State | Condition | UI |
|
|
9
9
|
|-------|-----------|-----|
|
|
10
10
|
| Loading | `surveyQueryResults.isLoading` | Centered "Loading..." |
|
|
11
11
|
| Error | `surveyQueryResults.error` | Red error message |
|
|
12
12
|
| Empty | `!survey` | "No survey data" |
|
|
13
|
-
|
|
|
14
|
-
|
|
|
13
|
+
| Intro | `state.phase.kind === 'intro'` | `IntroPage` + Start CTA |
|
|
14
|
+
| Paused | `state.phase.kind === 'paused'` | `PausedPage` |
|
|
15
|
+
| End | `state.phase.kind === 'end'` | `EndPage` |
|
|
16
|
+
| Submitted | `state.phase.kind === 'submitted'` | Terminal message |
|
|
17
|
+
| Expired | `state.phase.kind === 'expired'` | Terminal message |
|
|
18
|
+
| Quota | `state.phase.kind === 'quota_fulfilled'` | Terminal message |
|
|
19
|
+
| Active | `state.phase.kind === 'content'` | Questions + nav |
|
|
15
20
|
|
|
16
21
|
## Active Layout
|
|
17
22
|
|
|
@@ -22,7 +27,7 @@ main (max-w-4xl)
|
|
|
22
27
|
LanguageSelector (hidden if ≤1 language)
|
|
23
28
|
Questions (space-y-12)
|
|
24
29
|
Submit error (if any)
|
|
25
|
-
Nav: Back (if
|
|
30
|
+
Nav: Back (if canGoBack) + Next/Submit (primaryButtonType)
|
|
26
31
|
Footer
|
|
27
32
|
```
|
|
28
33
|
|
|
@@ -32,10 +37,10 @@ German when `survey.language.startsWith('de')`:
|
|
|
32
37
|
|
|
33
38
|
| Action | EN | DE |
|
|
34
39
|
|--------|----|----|
|
|
35
|
-
| Back |
|
|
36
|
-
| Next |
|
|
37
|
-
| Submit |
|
|
38
|
-
| Submitting |
|
|
40
|
+
| Back | Back | Zurück |
|
|
41
|
+
| Next | Next | Weiter |
|
|
42
|
+
| Submit | Submit | Absenden |
|
|
43
|
+
| Submitting | Submitting... | Wird gesendet... |
|
|
39
44
|
|
|
40
45
|
## Button Tokens
|
|
41
46
|
|
|
@@ -44,7 +49,7 @@ German when `survey.language.startsWith('de')`:
|
|
|
44
49
|
|
|
45
50
|
## ProgressBar
|
|
46
51
|
|
|
47
|
-
Pink fill bar
|
|
52
|
+
Pink fill bar. Bound to `state.progressPercentage` (path-scoped — see `00-integration/progress.md`).
|
|
48
53
|
|
|
49
54
|
## LanguageSelector
|
|
50
55
|
|
|
@@ -53,8 +58,9 @@ Pink fill bar, `300ms` width transition on `progressPercentage`.
|
|
|
53
58
|
|
|
54
59
|
## Agent Checklist
|
|
55
60
|
|
|
56
|
-
- [ ] All
|
|
57
|
-
- [ ] `onAction({ type: '
|
|
58
|
-
- [ ]
|
|
61
|
+
- [ ] All phase branches implemented
|
|
62
|
+
- [ ] `onAction({ type: 'START' })` on intro
|
|
63
|
+
- [ ] `onAction({ type: 'CHANGE' })` wired per question with `allAnswers` / `allQuestions`
|
|
64
|
+
- [ ] Back uses `state.canGoBack`; primary uses `state.primaryButtonType`
|
|
59
65
|
- [ ] ProgressBar receives `state.progressPercentage`
|
|
60
66
|
- [ ] Header/Footer on every state branch
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# UI Spec: Heatmap (`type: 'HEATMAP'`)
|
|
2
|
+
|
|
3
|
+
> **Implement from this spec** (npm installs). Monorepo reference: `apps/client/src/components/HeatmapScale.tsx`
|
|
4
|
+
> **SDK type:** `HeatmapQuestion` — `fetchSurvey/types/heatmap.ts`
|
|
5
|
+
> **Component blueprint:** `01-components/17-heatmap-scale.md`
|
|
6
|
+
|
|
7
|
+
## Config Inventory
|
|
8
|
+
|
|
9
|
+
| Field | Implemented | UI impact |
|
|
10
|
+
|-------|-------------|-----------|
|
|
11
|
+
| `questionText` | ✅ (shell) | Bold title + required asterisk |
|
|
12
|
+
| `questionDescription` | ✅ (shell) | Instruction text below title |
|
|
13
|
+
| `required` | ✅ (shell + SDK) | Asterisk + validation on NEXT |
|
|
14
|
+
| `imageUrl` | ✅ | Center-aligned interactive image |
|
|
15
|
+
| `maxClicksAllowed` | ✅ | Footer hint count; blocks new clicks at limit |
|
|
16
|
+
| `regions` | ❌ (respondent UI) | Logic only — not rendered on survey |
|
|
17
|
+
|
|
18
|
+
## Layout Anatomy
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
[Question shell: title *, description]
|
|
22
|
+
[Heatmap container — relative, inline-block, max-width 100%]
|
|
23
|
+
[<img> — object-contain, display block, crosshair cursor]
|
|
24
|
+
[Spot overlay — absolute inset-0, pointer-events-none]
|
|
25
|
+
[Orange circle markers at normalized x/y positions]
|
|
26
|
+
[Footer hint — info icon + max spots text]
|
|
27
|
+
[Validation error — from Question shell]
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Spot Marker Visual Spec
|
|
31
|
+
|
|
32
|
+
| Property | Value |
|
|
33
|
+
|----------|-------|
|
|
34
|
+
| Shape | Circle |
|
|
35
|
+
| Diameter | 16px |
|
|
36
|
+
| Fill | `#F97316` |
|
|
37
|
+
| Border | 2px solid `#FFFFFF` |
|
|
38
|
+
| Shadow | `0 1px 3px rgba(0,0,0,0.25)` |
|
|
39
|
+
| Position | `left: x * 100%`, `top: y * 100%`, `transform: translate(-50%, -50%)` |
|
|
40
|
+
|
|
41
|
+
## Interactive States
|
|
42
|
+
|
|
43
|
+
| State | Behavior |
|
|
44
|
+
|-------|----------|
|
|
45
|
+
| Image loading | Spinner overlay; clicks disabled |
|
|
46
|
+
| Ready | `cursor: crosshair` on image |
|
|
47
|
+
| At max clicks | Block new adds; allow remove |
|
|
48
|
+
| Removing last spot | `onSelect(undefined)` |
|
|
49
|
+
|
|
50
|
+
## Footer Hint Text
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
ⓘ You can select up to {maxClicksAllowed} spot(s). To change your selection, simply click on the same spot again to remove it.
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Use singular "spot" when `maxClicksAllowed === 1`.
|
|
57
|
+
|
|
58
|
+
## Answer Mutation
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
onSelect(HeatmapAnswer | undefined)
|
|
62
|
+
// HeatmapAnswer = Array<{ id: string; x: number; y: number }>
|
|
63
|
+
// x, y normalized 0–1 relative to rendered image bounds
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Agent Checklist
|
|
67
|
+
|
|
68
|
+
- [ ] Click handler on `<img>` element (not overlay) for precise coordinates
|
|
69
|
+
- [ ] `inline-block` wrapper so spots align with image bounds
|
|
70
|
+
- [ ] Toggle-remove within 14px hit radius
|
|
71
|
+
- [ ] Wait for `onLoad` before accepting clicks
|
|
72
|
+
- [ ] Percentage-positioned spots (survives resize)
|
|
73
|
+
- [ ] Do NOT render region overlays on respondent UI
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Rank Order UI Spec (`RANK_ORDER`)
|
|
2
|
+
|
|
3
|
+
> **Reference:** `02-reference/question-types/13-rank-order.md`
|
|
4
|
+
> **Blueprint:** `01-components/18-rank-order-scale.md`
|
|
5
|
+
|
|
6
|
+
## Interaction Modes
|
|
7
|
+
|
|
8
|
+
### Dropdown (`interactionMode: 'dropdown'`)
|
|
9
|
+
|
|
10
|
+
| Element | Behavior |
|
|
11
|
+
|---------|----------|
|
|
12
|
+
| Rank select | `-` (unassigned) or `1..N` per option row |
|
|
13
|
+
| Duplicate ranks | Selecting rank R on option A clears R from any other option |
|
|
14
|
+
| Layout | Rank select on left, option content on right |
|
|
15
|
+
|
|
16
|
+
### Drag and Drop (`interactionMode: 'dragAndDrop'`)
|
|
17
|
+
|
|
18
|
+
| Element | Behavior |
|
|
19
|
+
|---------|----------|
|
|
20
|
+
| Drag handle | Six-dot grip on left; pointer + keyboard sensors |
|
|
21
|
+
| Rank badge | Circular badge showing 1-based position |
|
|
22
|
+
| Reorder | List order determines rank; smooth transform during drag |
|
|
23
|
+
| Up/Down buttons | Keyboard fallback on right side of each row |
|
|
24
|
+
|
|
25
|
+
## Option Display Variants
|
|
26
|
+
|
|
27
|
+
| `optionDisplay` | Render |
|
|
28
|
+
|-----------------|--------|
|
|
29
|
+
| `textOnly` | Full-width text label only |
|
|
30
|
+
| `imageOnly` | Large square thumbnail only (no label) |
|
|
31
|
+
| `textAndImage` | Thumbnail + text label side by side |
|
|
32
|
+
|
|
33
|
+
Use `dangerouslySetInnerHTML` for option labels (HTML from builder).
|
|
34
|
+
|
|
35
|
+
## Validation UX
|
|
36
|
+
|
|
37
|
+
- Required + `requireRankAll`: every visible option must have a unique rank from 1..N
|
|
38
|
+
- Required + not `requireRankAll`: at least one visible option ranked
|
|
39
|
+
- SDK surfaces `validationError` on the question shell — show the standard error banner
|
|
40
|
+
|
|
41
|
+
## Styling
|
|
42
|
+
|
|
43
|
+
Match MCQ card styling:
|
|
44
|
+
|
|
45
|
+
- Border: `#e5e5e5` default, `#e20074` when actively ranked (optional highlight)
|
|
46
|
+
- Background hover: `hover:bg-gray-50/50`
|
|
47
|
+
- Rank badge: `#fdf2f8` background, `#e20074` text
|
|
48
|
+
|
|
49
|
+
## Accessibility
|
|
50
|
+
|
|
51
|
+
- Each rank select: `aria-label="Rank for {option label}"`
|
|
52
|
+
- Drag handle: `aria-label="Drag to reorder {option label}"`
|
|
53
|
+
- Up/Down: disabled on first/last item
|
|
54
|
+
|
|
55
|
+
## Do Not
|
|
56
|
+
|
|
57
|
+
- Store ranks in local component state without syncing to `onSelect`
|
|
58
|
+
- Submit option labels — submit rank numbers via SDK formatter only
|
|
59
|
+
- Hardcode logic operators in the client — all conditions are SDK-evaluated
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# UI Specifications — Reference Implementation Guide
|
|
2
2
|
|
|
3
|
-
> **For npm installs:** implement from these specs + [`templates/Question.tsx`](../templates/Question.tsx).
|
|
4
|
-
> **For monorepo devs:**
|
|
3
|
+
> **For npm installs:** implement from these specs + [`templates/Question.tsx`](../templates/Question.tsx).
|
|
4
|
+
> **For monorepo devs:** Reference client at `apps/client/src/components/` aligns with SDK types — trust `03-ui-specs/`, `templates/Question.tsx`, and exported types.
|
|
5
5
|
> Read the spec for **each question type present** in the fetched survey before writing React code.
|
|
6
6
|
|
|
7
7
|
## How to Read a UI Spec
|
|
@@ -38,14 +38,14 @@ Each file in this folder follows the same structure:
|
|
|
38
38
|
| Spec file | Reference component | `question.type` |
|
|
39
39
|
|-----------|---------------------|-----------------|
|
|
40
40
|
| [00-question-shell.md](00-question-shell.md) | `Question.tsx` | All (wrapper) |
|
|
41
|
-
| [01-rating.md](01-rating.md) | `RatingScale.tsx` | `
|
|
42
|
-
| [02-radio.md](02-radio.md) | `Question.tsx` inline | `
|
|
43
|
-
| [03-text.md](03-text.md) | `Question.tsx` inline | `
|
|
44
|
-
| [04-csat.md](04-csat.md) | `
|
|
45
|
-
| [05-rating-scale.md](05-rating-scale.md) |
|
|
46
|
-
| [06-slider.md](06-slider.md) |
|
|
47
|
-
| [07-matrix-cfm.md](07-matrix-cfm.md) | `LikertMatrixScale.tsx` | `
|
|
48
|
-
| [08-matrix-csat-rating.md](08-matrix-csat-rating.md) | `CsatMatrixScale.tsx` |
|
|
41
|
+
| [01-rating.md](01-rating.md) | `RatingScale.tsx` | `nps` |
|
|
42
|
+
| [02-radio.md](02-radio.md) | `Question.tsx` inline | `mcq` |
|
|
43
|
+
| [03-text.md](03-text.md) | `Question.tsx` inline | `text_input` |
|
|
44
|
+
| [04-csat.md](04-csat.md) | `CsatMatrixScale.tsx` | `csat` |
|
|
45
|
+
| [05-rating-scale.md](05-rating-scale.md) | `RatingScale.tsx` | `star_rating` |
|
|
46
|
+
| [06-slider.md](06-slider.md) | `CustomSliderTrack.tsx` | `slider` |
|
|
47
|
+
| [07-matrix-cfm.md](07-matrix-cfm.md) | `LikertMatrixScale.tsx` | `cfm_matrix` |
|
|
48
|
+
| [08-matrix-csat-rating.md](08-matrix-csat-rating.md) | `CsatMatrixScale.tsx` | `rating_matrix` |
|
|
49
49
|
| [09-slider-matrix.md](09-slider-matrix.md) | `SliderMatrixScale.tsx` | `slider_matrix` |
|
|
50
50
|
| [10-file-upload.md](10-file-upload.md) | `FileUploadScale.tsx` | `file_upload` |
|
|
51
51
|
| [11-text-and-media.md](11-text-and-media.md) | `Question.tsx` inline | `text_and_media` |
|
|
@@ -55,7 +55,7 @@ Each file in this folder follows the same structure:
|
|
|
55
55
|
|
|
56
56
|
## NPS Traffic-Light Colors
|
|
57
57
|
|
|
58
|
-
Use
|
|
58
|
+
Use accent colors from SDK options — never invent colors for NPS:
|
|
59
59
|
|
|
60
60
|
| Value | Hex |
|
|
61
61
|
|-------|-----|
|
|
@@ -73,8 +73,8 @@ Use `option.color` from SDK — never invent colors:
|
|
|
73
73
|
|
|
74
74
|
## When to Read
|
|
75
75
|
|
|
76
|
-
After Phase 3 in [`index.md`](../index.md), for each question in
|
|
76
|
+
After Phase 3 in [`index.md`](../index.md), for each question in the fetched survey:
|
|
77
77
|
|
|
78
78
|
1. Read [`02-reference/question-types/`](../02-reference/question-types/) for data shape
|
|
79
|
-
2. Read matching spec from this folder (see [`MANIFEST.json`](../MANIFEST.json) `
|
|
79
|
+
2. Read matching spec from this folder (see [`MANIFEST.json`](../MANIFEST.json) `questionTypes.{type}.uiSpec`)
|
|
80
80
|
3. Cross-check [`config-field-index.md`](../02-reference/config-field-index.md)
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
| `disabled` | Gray theme, opacity 0.5, no interaction |
|
|
24
24
|
| `displayValues` | Reserved — tooltip always shows on hover/drag in client |
|
|
25
25
|
| `hasSelectedValue` | Reserved |
|
|
26
|
-
| `sliderType: 'graphics'` | Emoji thumb via
|
|
26
|
+
| `sliderType: 'graphics'` | Emoji thumb via client-side react-icons |
|
|
27
27
|
| `ticks` | Discrete tick count + emoji index scale |
|
|
28
28
|
| `reverseScaleOrder` | Flip emoji index calculation |
|
|
29
29
|
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
```typescript
|
|
42
42
|
const idx = Math.round(((value - min) / (max - min)) * ((ticks || 10) - 1));
|
|
43
43
|
const displayIdx = reverseScaleOrder ? (ticks || 10) - 1 - idx : idx;
|
|
44
|
-
|
|
44
|
+
// Map displayIdx to react-icons emoji — proportional mapping is client-implemented (not an SDK export)
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
## Agent Checklist
|
|
@@ -30,6 +30,11 @@ if (val === null) {
|
|
|
30
30
|
// When null selected, non-null options disabled
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
+
## Props
|
|
34
|
+
|
|
35
|
+
- `options: ScaleColumn[]` — matrix scale columns (`id`, `label`)
|
|
36
|
+
- `value` / `onChange` — stored values use `matrixColumnStoredValue(col)` → column **`id`**
|
|
37
|
+
|
|
33
38
|
## Agent Checklist
|
|
34
39
|
|
|
35
40
|
- [ ] Click-outside closes panel
|