@explorer02/cfm-survey-sdk 0.2.3 → 0.2.4
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 +140 -33
- package/dist/cli/index.mjs +140 -33
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +8 -1
- package/postinstall.js +8 -5
- package/templates/AGENT.md +60 -19
- package/templates/deploy-wizard-to-s3.sh +52 -0
- package/templates/docs/00-integration/agent-execution-flow.md +281 -0
- package/templates/docs/00-integration/agent-operating-contract.md +260 -0
- package/templates/docs/00-integration/apply-ui-config.md +365 -0
- package/templates/docs/00-integration/canonical-survey-fixtures.md +94 -0
- package/templates/docs/00-integration/client-integration-guide.md +9 -7
- package/templates/docs/00-integration/client-lib-folder.md +115 -0
- package/templates/docs/00-integration/component-checklist.md +88 -62
- package/templates/docs/00-integration/constraints.md +13 -5
- package/templates/docs/00-integration/custom-field-logic-and-navigation.md +56 -0
- package/templates/docs/00-integration/progress.md +4 -2
- package/templates/docs/00-integration/setup.md +12 -0
- package/templates/docs/00-integration/ui-customization-wizard.md +144 -0
- package/templates/docs/00-integration/useSurveySDK.md +35 -1
- package/templates/docs/00-integration/wizard-api.md +134 -0
- package/templates/docs/00-integration/wizard-config-handoff.md +246 -0
- package/templates/docs/00-integration/wizard-preview-build-guide.md +400 -0
- package/templates/docs/00-integration/wizard-question-type-styling.md +195 -0
- package/templates/docs/01-components/01-survey-page.md +18 -8
- package/templates/docs/01-components/02-question.md +2 -2
- package/templates/docs/01-components/03-rating-scale.md +12 -18
- package/templates/docs/01-components/05-csat-matrix-scale.md +18 -26
- package/templates/docs/01-components/06-likert-matrix-scale.md +19 -39
- package/templates/docs/01-components/07-slider-matrix-scale.md +48 -33
- package/templates/docs/01-components/09-custom-slider-track.md +15 -25
- package/templates/docs/01-components/10-header-footer.md +23 -4
- package/templates/docs/01-components/11-progress-bar.md +14 -6
- package/templates/docs/01-components/19-survey-sticky-chrome.md +79 -0
- package/templates/docs/01-components/README.md +2 -2
- package/templates/docs/02-reference/question-types/10-slider-matrix.md +38 -26
- package/templates/docs/02-reference/routing-table.md +1 -0
- package/templates/docs/02-reference/value-derivation.md +1 -1
- package/templates/docs/03-ui-specs/00-question-shell.md +10 -0
- package/templates/docs/03-ui-specs/01-rating.md +18 -0
- package/templates/docs/03-ui-specs/02-radio.md +16 -1
- package/templates/docs/03-ui-specs/03-text.md +12 -1
- package/templates/docs/03-ui-specs/04-csat.md +84 -50
- package/templates/docs/03-ui-specs/06-slider.md +3 -1
- package/templates/docs/03-ui-specs/07-matrix-cfm.md +71 -48
- package/templates/docs/03-ui-specs/08-matrix-csat-rating.md +48 -18
- package/templates/docs/03-ui-specs/09-slider-matrix.md +59 -32
- package/templates/docs/03-ui-specs/10-file-upload.md +11 -0
- package/templates/docs/03-ui-specs/11-text-and-media.md +4 -0
- package/templates/docs/03-ui-specs/12-survey-chrome.md +54 -5
- package/templates/docs/03-ui-specs/13-heatmap.md +12 -1
- package/templates/docs/03-ui-specs/14-rank-order.md +11 -0
- package/templates/docs/03-ui-specs/README.md +4 -2
- package/templates/docs/03-ui-specs/shared/matrix-dropdown.md +1 -1
- package/templates/docs/MANIFEST.json +162 -6
- package/templates/docs/index.md +116 -117
- package/templates/docs/templates/CsatMatrixScale.tsx +637 -0
- package/templates/docs/templates/FileUploadScale.tsx +262 -0
- package/templates/docs/templates/HeatmapScale.tsx +114 -0
- package/templates/docs/templates/LikertMatrixScale.tsx +414 -0
- package/templates/docs/templates/Question.tsx +3 -0
- package/templates/docs/templates/RatingScale.tsx +129 -0
- package/templates/docs/templates/SliderMatrixScale.tsx +248 -0
- package/templates/docs/templates/SurveyStickyChrome.tsx +24 -0
- package/templates/docs/templates/customFieldValues.ts +36 -0
- package/templates/docs/templates/implementation_plan.md +56 -9
- package/templates/docs/templates/labelStyles.ts +33 -0
- package/templates/docs/templates/selectionStyles.ts +100 -0
- package/templates/docs/templates/surveyUiIcons.tsx +11 -0
- package/templates/docs/templates/surveyUiScaleUtils.ts +51 -0
- package/templates/docs/templates/verify-agent-build.sh +119 -0
- package/templates/preview-harness/preview-bridge.inline.js +201 -0
- package/templates/preview-harness/previewPages.js +108 -0
- package/templates/preview-harness/previewPages.ts +298 -0
- package/templates/preview-harness/vite-app/src/PreviewConfigContext.tsx +67 -0
- package/templates/preview-harness/vite-app/src/QuestionPreview.tsx +122 -0
- package/templates/preview-harness/vite-app/src/SurveyPagePreview.tsx +75 -0
- package/templates/preview-harness/vite-app/src/fixtures/questions.ts +311 -0
- package/templates/preview-harness/vite-app/src/globals.css +16 -0
- package/templates/preview-harness/vite-app/src/mount.tsx +9 -0
- package/templates/preview-harness/vite-app/src/preview-live-overrides.css +101 -0
- package/templates/preview-harness/vite-app/stubs/next-dynamic.tsx +25 -0
- package/templates/preview-harness/vite-app/stubs/next-image.tsx +29 -0
- package/templates/previewBridge.ts +153 -0
- package/templates/survey-theme.css +295 -0
- package/templates/survey-ui-config.schema.json +213 -0
- package/templates/wizard-dist/assets/PreviewMock-DgHfrVeb.js +1 -0
- package/templates/wizard-dist/assets/TypePanel-CFVC3Ptn.js +1 -0
- package/templates/wizard-dist/assets/index-BhWM50Yu.css +1 -0
- package/templates/wizard-dist/assets/index-DYK3X1e5.js +34 -0
- package/templates/wizard-dist/assets/vendor-BwkXDkd3.js +17 -0
- package/templates/wizard-dist/index.html +20 -0
- package/templates/wizard.html +13 -1129
|
@@ -48,5 +48,6 @@ import { QUESTION_TYPE } from '@explorer02/cfm-survey-sdk';
|
|
|
48
48
|
| `CustomSliderTrack` | `01-components/09-custom-slider-track.md` |
|
|
49
49
|
| `Header` / `Footer` | `01-components/10-header-footer.md` |
|
|
50
50
|
| `ProgressBar` | `01-components/11-progress-bar.md` |
|
|
51
|
+
| `SurveyStickyChrome` | `01-components/19-survey-sticky-chrome.md` |
|
|
51
52
|
| `LanguageSelector` | `01-components/12-language-selector.md` |
|
|
52
53
|
| `MatrixDropdown` | `01-components/13-matrix-dropdown.md` |
|
|
@@ -24,7 +24,7 @@ type ScaleColumn = { id: string; label: string };
|
|
|
24
24
|
- Filter N/A from scale pickers: `scaleColumns.filter(col => col.id !== 'na')`
|
|
25
25
|
- Slider matrix rows still store numeric slider positions (unchanged)
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
Portable template: [`templates/surveyUiScaleUtils.ts`](../templates/surveyUiScaleUtils.ts) exports `matrixColumnStoredValue` (same contract). Copy to `src/lib/` — see [`client-lib-folder.md`](../00-integration/client-lib-folder.md).
|
|
28
28
|
|
|
29
29
|
## NPS / star rating options
|
|
30
30
|
|
|
@@ -31,3 +31,13 @@ Never use a `default` fallback that shows `"not yet implemented"`.
|
|
|
31
31
|
|
|
32
32
|
- Answers: `state.answers` (not `state.values`)
|
|
33
33
|
- Change action: `{ type: 'CHANGE', payload: { questionId, answerValue } }`
|
|
34
|
+
|
|
35
|
+
## Wizard customization (Phase 5 — required)
|
|
36
|
+
|
|
37
|
+
Question shell inherits global theme tokens — no per-shell hex overrides:
|
|
38
|
+
|
|
39
|
+
- Validation banner: `--cfm-validation-border`, `--cfm-validation-bg`
|
|
40
|
+
- Question title: `--cfm-question-title-color`
|
|
41
|
+
- Required asterisk: `--cfm-required-color`
|
|
42
|
+
|
|
43
|
+
See [`wizard-preview-build-guide.md`](../00-integration/wizard-preview-build-guide.md) for `data-cfm-*` on wrapper-adjacent chrome only.
|
|
@@ -44,3 +44,21 @@ See [`02-reference/value-derivation.md`](../02-reference/value-derivation.md).
|
|
|
44
44
|
- [ ] Two-row grid with `gridTemplateColumns: repeat(options.length, 1fr)`
|
|
45
45
|
- [ ] Only track row is clickable
|
|
46
46
|
- [ ] min/mid/max labels in Question wrapper with midLabelIndex formula
|
|
47
|
+
|
|
48
|
+
## Wizard customization (Phase 5 — required)
|
|
49
|
+
|
|
50
|
+
> Reference hex in this spec are defaults. Wire `var(--cfm-*)` per [`wizard-question-type-styling.md`](../00-integration/wizard-question-type-styling.md). Copy [`selectionStyles.ts`](../templates/selectionStyles.ts) and [`labelStyles.ts`](../templates/labelStyles.ts).
|
|
51
|
+
|
|
52
|
+
| Control | CSS vars | Apply in |
|
|
53
|
+
|---------|----------|----------|
|
|
54
|
+
| Track bar / highlight | `--cfm-nps-track-bar`, `--cfm-nps-track-highlight`, `--cfm-nps-selected-fill` | `RatingScale.tsx` track row |
|
|
55
|
+
| Hint labels (min/max) | `--cfm-hint-label-color`, `--cfm-hint-label-bg` | `hintLabelStyle()` on anchor row |
|
|
56
|
+
| Number labels | `--cfm-number-label-mode`, `--cfm-number-mono-color`, `--cfm-number-color-{n}` | Badge row — unselected only |
|
|
57
|
+
| Selection | `--cfm-matrix-selected`, `--cfm-input-focus-ring` | `scaleRadioRingStyle` / `scaleRadioDotStyle` |
|
|
58
|
+
|
|
59
|
+
### Wizard checklist
|
|
60
|
+
|
|
61
|
+
- [ ] `selectionStyles.ts` imported in `RatingScale.tsx`
|
|
62
|
+
- [ ] Selected radio: outer border + dot = focus ring; annulus = cell selected
|
|
63
|
+
- [ ] Selected numbered badge: border = focus ring, fill = cell selected, text white
|
|
64
|
+
- [ ] Unselected badges keep traffic-light / per-number `--cfm-number-*` colors
|
|
@@ -92,9 +92,24 @@ onAction({ type: 'CHANGE', payload: { questionId, answerValue: string[] } })
|
|
|
92
92
|
|
|
93
93
|
- [ ] Use `selectionMode`, not `isMultiSelect`
|
|
94
94
|
- [ ] Full-width card options; custom circular radio / checkbox indicators
|
|
95
|
-
- [ ] Selected:
|
|
95
|
+
- [ ] Selected: use `var(--cfm-input-focus-ring)` border + `var(--cfm-mcq-selected-bg)` / `var(--cfm-matrix-selected)` — not hardcoded `#e20074` / `#fdf2f8`
|
|
96
96
|
- [ ] Multi-select: `string[]` answer; block at `maxSelections`
|
|
97
97
|
- [ ] Defaults: bind to `state.answers`; pre-selected visual on load when `defaultOptionIds` set
|
|
98
98
|
- [ ] `getVisibleMcqOptionsForAnswers` with optional `customFieldValues` 4th arg
|
|
99
99
|
- [ ] Store `option.id` — never label text
|
|
100
100
|
- [ ] Matrix row: [`question-type-sdk-matrix.md#mcq`](../00-integration/question-type-sdk-matrix.md#mcq)
|
|
101
|
+
|
|
102
|
+
## Wizard customization (Phase 5 — required)
|
|
103
|
+
|
|
104
|
+
> Wire MCQ tokens per [`wizard-question-type-styling.md`](../00-integration/wizard-question-type-styling.md). Copy [`selectionStyles.ts`](../templates/selectionStyles.ts).
|
|
105
|
+
|
|
106
|
+
| Control | CSS vars | Apply in |
|
|
107
|
+
|---------|----------|----------|
|
|
108
|
+
| Option gap / radius / padding | `--cfm-mcq-option-gap`, `--cfm-mcq-border-radius`, `--cfm-mcq-card-padding` | MCQ option cards |
|
|
109
|
+
| Selection (radio) | `--cfm-input-focus-ring`, `--cfm-matrix-selected`, `--cfm-mcq-selected-bg` | Outer ring border + annulus + inner dot |
|
|
110
|
+
| Selection (checkbox) | `--cfm-input-focus-ring`, `--cfm-mcq-selected-bg` | Checkbox border + fill |
|
|
111
|
+
|
|
112
|
+
### Wizard checklist
|
|
113
|
+
|
|
114
|
+
- [ ] Single-select radio uses ring + annulus + dot pattern from `selectionStyles.ts`
|
|
115
|
+
- [ ] `data-cfm-mcq-style` on `:root` when `questionTypes.MCQ.optionStyle` set
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# UI Spec: Text Field (`type: 'TEXTFIELD'`)
|
|
2
2
|
|
|
3
|
-
> **Reference:** `
|
|
3
|
+
> **Reference:** portable `docs/templates/Question.tsx` (inline TEXTFIELD branch)
|
|
4
4
|
> **SDK type:** `TextInputQuestion` — `fetchSurvey/types/text.ts`
|
|
5
5
|
|
|
6
6
|
## Config Inventory
|
|
@@ -49,3 +49,14 @@ onSelect(string) // onChange from textarea
|
|
|
49
49
|
- [ ] Character counter bottom-right
|
|
50
50
|
- [ ] Magenta focus ring
|
|
51
51
|
- [ ] Implement `inputVariant: 'short'` as single-line input when present
|
|
52
|
+
|
|
53
|
+
## Wizard customization (Phase 5 — required)
|
|
54
|
+
|
|
55
|
+
| Control | CSS vars | Apply in |
|
|
56
|
+
|---------|----------|----------|
|
|
57
|
+
| Input radius / border / placeholder | `--cfm-input-radius`, `--cfm-input-border`, `--cfm-input-placeholder` | `textarea` / `input` |
|
|
58
|
+
| Focus outline | `--cfm-input-focus-ring` | `:focus-visible` ring |
|
|
59
|
+
|
|
60
|
+
### Wizard checklist
|
|
61
|
+
|
|
62
|
+
- [ ] No hardcoded magenta focus hex — use `var(--cfm-input-focus-ring)`
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# UI Spec: CSAT Matrix Grid (`type: 'CSAT_MATRIX'`)
|
|
2
2
|
|
|
3
|
-
> **
|
|
3
|
+
> **COPY TEMPLATE FIRST:** [`templates/CsatMatrixScale.tsx`](../templates/CsatMatrixScale.tsx) + [`surveyUiIcons.tsx`](../templates/surveyUiIcons.tsx) + [`surveyUiScaleUtils.ts`](../templates/surveyUiScaleUtils.ts) + [`CustomSliderTrack.tsx`](../templates/CustomSliderTrack.tsx) + [`MatrixDropdown.tsx`](../templates/MatrixDropdown.tsx)
|
|
4
|
+
>
|
|
4
5
|
> **SDK type:** `CsatQuestion` — `fetchSurvey/types/csat.ts`
|
|
5
6
|
> **Component blueprint:** `01-components/05-csat-matrix-scale.md`
|
|
6
7
|
|
|
@@ -9,75 +10,89 @@
|
|
|
9
10
|
| Field | Implemented | UI impact |
|
|
10
11
|
|-------|-------------|-----------|
|
|
11
12
|
| `statementRows[]`, `scaleColumns[]` | ✅ | Grid / carousel / dropdown data |
|
|
12
|
-
| `displayStyle` | ✅ |
|
|
13
|
+
| `displayStyle` | ✅ | emoji / star / numbered / graphics / standard |
|
|
13
14
|
| `gridLayout` | ✅ | carousel vs standard vs dropdown |
|
|
14
|
-
| `hasNotApplicableOption` | ✅ |
|
|
15
|
+
| `hasNotApplicableOption` | ✅ | N/A column (56px) or vertical checkbox |
|
|
15
16
|
| `reverseScaleOrder` | ✅ | Flip emoji/star/number index |
|
|
16
|
-
| `scaleAnchorLabels[]` | ✅ | Anchor row above columns |
|
|
17
|
+
| `scaleAnchorLabels[]` | ✅ | Anchor row above columns (replaces column headers when set) |
|
|
17
18
|
|
|
18
|
-
##
|
|
19
|
+
## Statement & label visibility (CRITICAL)
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
21
|
+
Agents often render inputs with **no row labels** and **no column headers**. Every matrix grid MUST include:
|
|
22
|
+
|
|
23
|
+
### 1. Row labels — `statementRows[].statementText`
|
|
24
|
+
|
|
25
|
+
Fixed **180px** left column on every data row:
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
<div style={{ width: '180px', flexShrink: 0, padding: '0 16px' }}>
|
|
29
|
+
<span
|
|
30
|
+
style={{ fontSize: '14px', fontWeight: 500, color: '#111827' }}
|
|
31
|
+
dangerouslySetInnerHTML={{ __html: row.statementText }}
|
|
32
|
+
/>
|
|
33
|
+
</div>
|
|
32
34
|
```
|
|
33
35
|
|
|
34
|
-
|
|
36
|
+
### 2. Column labels — two modes (mutually exclusive header row)
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
| Condition | What to render above cells |
|
|
39
|
+
|-----------|---------------------------|
|
|
40
|
+
| `scaleAnchorLabels.length > 0` | Anchor label row positioned over columns (see formula below) |
|
|
41
|
+
| No anchor labels | Column header row from `scaleColumns[].label` |
|
|
40
42
|
|
|
41
|
-
|
|
43
|
+
**When `scaleAnchorLabels` is set**, still render anchor row even if column text labels are long (emoji mode Page 9 fixture):
|
|
42
44
|
|
|
43
|
-
|
|
45
|
+
```typescript
|
|
46
|
+
const m = scaleColumns.length;
|
|
47
|
+
const n = scaleAnchorLabels.length;
|
|
48
|
+
const percent = n === 1 ? 50 : (0.5 / m + (i / (n - 1)) * ((m - 1) / m)) * 100;
|
|
49
|
+
// position label i at left: `${percent}%`, transform: translateX(-50%)
|
|
50
|
+
```
|
|
44
51
|
|
|
45
|
-
|
|
52
|
+
**When no anchor labels**, render column headers:
|
|
46
53
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
54
|
+
```tsx
|
|
55
|
+
{scaleColumns.map(col => (
|
|
56
|
+
<span dangerouslySetInnerHTML={{ __html: col.label }} />
|
|
57
|
+
))}
|
|
58
|
+
```
|
|
51
59
|
|
|
52
|
-
|
|
60
|
+
### 3. N/A column header
|
|
53
61
|
|
|
54
|
-
|
|
62
|
+
When `hasNotApplicableOption`, reserve **56px** right column with "N/A" header text.
|
|
55
63
|
|
|
56
|
-
|
|
64
|
+
## Config Decision Tree
|
|
57
65
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
66
|
+
```
|
|
67
|
+
CSAT_MATRIX
|
|
68
|
+
├── gridLayout === 'carousel' → CsatMatrixCarousel
|
|
69
|
+
├── displayStyle in (dropdown, selectbox) → CsatMatrixVerticalList
|
|
70
|
+
└── else → CsatMatrixGrid
|
|
71
|
+
├── displayStyle === 'emoji' → getEmojiForIndex(colIdx, m) — NOT radio circles
|
|
72
|
+
├── displayStyle === 'star' → CsatStarIcons filled/empty
|
|
73
|
+
├── displayStyle === 'numbered' → 40×40 magenta badges (colIdx+1)
|
|
74
|
+
├── displayStyle === 'graphics' → CustomSliderTrack per row
|
|
75
|
+
└── displayStyle === 'standard' → custom radio circles #e20074
|
|
76
|
+
```
|
|
65
77
|
|
|
66
|
-
|
|
78
|
+
## Emoji + anchor labels (Page 9 fixture)
|
|
67
79
|
|
|
68
|
-
|
|
80
|
+
- 5 `scaleAnchorLabels` over 6 columns (5 emoji + N/A)
|
|
81
|
+
- Emoji cells: pink ring when selected; tooltip shows `col.label` on hover
|
|
82
|
+
- Do NOT replace emojis with plain radio buttons
|
|
69
83
|
|
|
70
|
-
|
|
84
|
+
## Star mode (Page 14 fixture)
|
|
71
85
|
|
|
72
|
-
|
|
86
|
+
- `CsatStarIcons` from `surveyUiIcons.tsx`
|
|
87
|
+
- Unselected opacity 0.5; selected filled star `#e20074`
|
|
73
88
|
|
|
74
|
-
##
|
|
89
|
+
## Graphics / numbered (RATING_MATRIX reuse)
|
|
75
90
|
|
|
76
|
-
|
|
91
|
+
See [`08-matrix-csat-rating.md`](08-matrix-csat-rating.md) — same `CsatMatrixScale` component.
|
|
77
92
|
|
|
78
93
|
## Answer Mutation
|
|
79
94
|
|
|
80
|
-
Store **`column.id`** via `matrixColumnStoredValue(col)
|
|
95
|
+
Store **`column.id`** via `matrixColumnStoredValue(col)`:
|
|
81
96
|
|
|
82
97
|
```typescript
|
|
83
98
|
onSelect(MatrixRowAnswers)
|
|
@@ -86,7 +101,26 @@ onAction({ type: 'CHANGE', payload: { questionId, answerValue: MatrixRowAnswers
|
|
|
86
101
|
|
|
87
102
|
## Agent Checklist
|
|
88
103
|
|
|
89
|
-
- [ ]
|
|
90
|
-
- [ ]
|
|
91
|
-
- [ ]
|
|
92
|
-
- [ ]
|
|
104
|
+
- [ ] Copied `CsatMatrixScale.tsx` + dependencies
|
|
105
|
+
- [ ] **180px statement column visible** on every row
|
|
106
|
+
- [ ] Column headers OR `scaleAnchorLabels` row visible (never both missing)
|
|
107
|
+
- [ ] `displayStyle` branch matches survey (emoji ≠ star ≠ numbered ≠ radio)
|
|
108
|
+
- [ ] N/A column when `hasNotApplicableOption`
|
|
109
|
+
- [ ] `react-icons` in package.json
|
|
110
|
+
|
|
111
|
+
## Wizard customization (Phase 5 — required)
|
|
112
|
+
|
|
113
|
+
> Per-format controls in [`wizard-question-type-styling.md`](../00-integration/wizard-question-type-styling.md). Copy [`selectionStyles.ts`](../templates/selectionStyles.ts) + [`labelStyles.ts`](../templates/labelStyles.ts).
|
|
114
|
+
|
|
115
|
+
| Format | Key tokens | Pattern |
|
|
116
|
+
|--------|------------|---------|
|
|
117
|
+
| emoji / star | `--cfm-input-focus-ring`, `cellSelectedVar` | Ring on select; star fill = cell selected |
|
|
118
|
+
| numbered | `--cfm-number-*`, focus ring + cell selected | Selected cell border + fill |
|
|
119
|
+
| radio grid | `--cfm-csat-column-label-*`, matrix radio styles | `csatColumnLabelStyle()` + `matrixRadioRingStyle` |
|
|
120
|
+
| graphics | `--cfm-slider-tick-label-*` | `CustomSliderTrack` thumb ring + annulus + dot |
|
|
121
|
+
| carousel | `--cfm-matrix-selected` | Active dot = cell selected |
|
|
122
|
+
|
|
123
|
+
### Wizard checklist
|
|
124
|
+
|
|
125
|
+
- [ ] Column headers use `csatColumnLabelStyle()` — not hint labels
|
|
126
|
+
- [ ] No per-type accent hex overrides — use Question card tokens only
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# UI Spec: Standalone Slider (`type: 'slider'`)
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> **⛔ DEPRECATED — DO NOT IMPLEMENT.** API SLIDER maps to `SLIDER_MATRIX` (`09-slider-matrix.md`) or RATING/CSAT `displayStyle: 'graphics'` (`08-matrix-csat-rating.md`).
|
|
4
|
+
>
|
|
5
|
+
> If you see slider-like UI, copy [`templates/SliderMatrixScale.tsx`](../templates/SliderMatrixScale.tsx) or use `CsatMatrixScale` with `CustomSliderTrack` — not this spec.
|
|
4
6
|
>
|
|
5
7
|
> **Reference:** Standalone slider is not routed in current `Question.tsx`
|
|
6
8
|
> **SDK type:** `SliderQuestion` — `fetchSurvey/types/slider.ts`
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# UI Spec: CFM Matrix (`type: 'CFM_MATRIX'`)
|
|
2
2
|
|
|
3
|
-
> **
|
|
3
|
+
> **COPY TEMPLATE FIRST:** [`templates/LikertMatrixScale.tsx`](../templates/LikertMatrixScale.tsx) + [`MatrixDropdown.tsx`](../templates/MatrixDropdown.tsx)
|
|
4
|
+
>
|
|
4
5
|
> **SDK type:** `CfmMatrixQuestion` — `fetchSurvey/types/cfmMatrix.ts`
|
|
5
6
|
> **Component blueprint:** `01-components/06-likert-matrix-scale.md`
|
|
6
7
|
|
|
@@ -18,68 +19,90 @@
|
|
|
18
19
|
| `showColumnHeaders` | ✅ | Show/hide column header row |
|
|
19
20
|
| `hasNotApplicableOption` | ✅ | N/A column with mutex |
|
|
20
21
|
|
|
21
|
-
##
|
|
22
|
+
## Statement & label visibility (CRITICAL)
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
├── showColumnHeaders ? column headers : hidden
|
|
31
|
-
└── statementLayout === 'bipolar' ? 25% | scale | 25% layout
|
|
32
|
-
```
|
|
24
|
+
### Row labels
|
|
25
|
+
|
|
26
|
+
| Layout | Left column content | Width |
|
|
27
|
+
|--------|---------------------|-------|
|
|
28
|
+
| `statementLayout: 'likert'` | `statementRows[].statementText` | 180px |
|
|
29
|
+
| `statementLayout: 'bipolar'` | `statementRows[].statementText` | 25% |
|
|
30
|
+
| `transposeTable: true` | `scaleColumns[].label` (rows become scale items) | 180px |
|
|
33
31
|
|
|
34
|
-
|
|
32
|
+
Always use `dangerouslySetInnerHTML` for HTML statement text.
|
|
35
33
|
|
|
36
|
-
|
|
37
|
-
|----------|-----------|--------|
|
|
38
|
-
| Click selected radio again | single-select grid/carousel | Clear row (`undefined`) |
|
|
39
|
-
| N/A selected | any | Other cols opacity 0.4, disabled |
|
|
40
|
-
| Multi N/A | selectionMode multiple | Sets `[null]` exclusively |
|
|
41
|
-
| Multi toggle | selectionMode multiple | Add/remove from array |
|
|
34
|
+
### Bipolar right column
|
|
42
35
|
|
|
43
|
-
|
|
36
|
+
When `statementLayout === 'bipolar'`, render **`oppositeStatementText`** in 25% right column (unless transposed):
|
|
44
37
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
38
|
+
```tsx
|
|
39
|
+
<span dangerouslySetInnerHTML={{ __html: row.oppositeStatementText ?? '' }} />
|
|
40
|
+
```
|
|
48
41
|
|
|
49
|
-
|
|
42
|
+
### Column / anchor labels
|
|
50
43
|
|
|
51
|
-
|
|
52
|
-
|------|----------|
|
|
53
|
-
| Prev button | Disabled when `currentRowIndex === 0` |
|
|
54
|
-
| Next button | Disabled when on last visible row |
|
|
55
|
-
| Dot indicators | Click dot → jump to that row index |
|
|
56
|
-
| On answer | **Do not** auto-advance to next row |
|
|
57
|
-
| Keyboard | Optional Left/Right for prev/next |
|
|
58
|
-
| Active dot (Likert) | `#2563eb`, expanded pill `20×8px` vs `8×8px` inactive |
|
|
44
|
+
Header renders when `scaleAnchorLabels` OR `showColumnHeaders`:
|
|
59
45
|
|
|
60
|
-
|
|
46
|
+
**Bipolar + anchor labels (Page 7 fixture)** — `showColumnHeaders: false`:
|
|
61
47
|
|
|
62
|
-
|
|
48
|
+
```
|
|
49
|
+
[25% spacer] [Label1 at col2] [Label2 at col6] [Label3 at col10] [25% spacer]
|
|
50
|
+
[25% Left Stmt 1] [10 radios] [25% Right Stmt 1]
|
|
51
|
+
```
|
|
63
52
|
|
|
64
|
-
|
|
65
|
-
|--------|--------|
|
|
66
|
-
| Select N/A | Clear other column values for row; other inputs opacity 0.4 + disabled |
|
|
67
|
-
| Select column after N/A | Clear N/A first |
|
|
68
|
-
| Multi-select + N/A | N/A sets `[null]` exclusively |
|
|
53
|
+
Anchor positioning:
|
|
69
54
|
|
|
70
|
-
|
|
55
|
+
```typescript
|
|
56
|
+
const numCols = gridCols.length;
|
|
57
|
+
if (idx === 0) left = `${100 / (2 * numCols)}%`;
|
|
58
|
+
else if (idx === labels.length - 1) left = `${100 - 100 / (2 * numCols)}%`;
|
|
59
|
+
else if (labels.length === 3 && idx === 1) left = '50%';
|
|
60
|
+
```
|
|
71
61
|
|
|
72
|
-
|
|
62
|
+
**Transposed + column headers (Page 10 fixture)** — `transposeTable: true`, `showColumnHeaders: true`:
|
|
73
63
|
|
|
74
64
|
```typescript
|
|
75
|
-
|
|
76
|
-
|
|
65
|
+
gridRows = scaleColumns; // row labels = Scale Item 1, 2, 3
|
|
66
|
+
gridCols = statementRows; // column headers = Statement Item 1, 2, 3
|
|
67
|
+
// Header cell HTML: col.statementText
|
|
68
|
+
// Row label HTML: row.label
|
|
69
|
+
input type = selectionMode === 'multiple' ? 'checkbox' : 'radio';
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Answer keys **always** use `statementRows[].id` as row id (`actualRowId` logic in template).
|
|
73
|
+
|
|
74
|
+
## Config Decision Tree
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
CFM_MATRIX
|
|
78
|
+
├── gridLayout === 'dropdown' → MatrixDropdown per statement row
|
|
79
|
+
├── gridLayout === 'carousel' → one row visible + blue dot nav
|
|
80
|
+
└── else → LikertMatrixGridLayout
|
|
81
|
+
├── transposeTable swaps gridRows/gridCols
|
|
82
|
+
├── showColumnHeaders controls column label row
|
|
83
|
+
└── statementLayout bipolar adds oppositeStatementText column
|
|
77
84
|
```
|
|
78
85
|
|
|
79
86
|
## Agent Checklist
|
|
80
87
|
|
|
81
|
-
- [ ]
|
|
82
|
-
- [ ]
|
|
83
|
-
- [ ] Bipolar
|
|
84
|
-
- [ ]
|
|
85
|
-
- [ ]
|
|
88
|
+
- [ ] Copied `LikertMatrixScale.tsx` + `MatrixDropdown.tsx`
|
|
89
|
+
- [ ] Row labels visible (statementText or transposed scale label)
|
|
90
|
+
- [ ] Bipolar: oppositeStatementText on right when not transposed
|
|
91
|
+
- [ ] Anchor labels OR column headers — never both missing when configured
|
|
92
|
+
- [ ] `selectionMode: multiple` uses checkboxes, not radios
|
|
93
|
+
- [ ] `transposeTable` swaps rows/cols correctly
|
|
94
|
+
|
|
95
|
+
## Wizard customization (Phase 5 — required)
|
|
96
|
+
|
|
97
|
+
> CFM column headers are **label items** only. See [`wizard-question-type-styling.md`](../00-integration/wizard-question-type-styling.md).
|
|
98
|
+
|
|
99
|
+
| Control | CSS vars | Apply in |
|
|
100
|
+
|---------|----------|----------|
|
|
101
|
+
| Row / bipolar width | `--cfm-matrix-row-width`, `--cfm-matrix-bipolar-width` | Statement columns |
|
|
102
|
+
| Label items | `--cfm-matrix-column-label-color`, `--cfm-matrix-column-label-bg` | Column header row — `matrixColumnLabelStyle()` |
|
|
103
|
+
| Selection | `--cfm-matrix-selected`, `--cfm-input-focus-ring` | `matrixRadioRingStyle` / dropdown option ring |
|
|
104
|
+
|
|
105
|
+
### Wizard checklist
|
|
106
|
+
|
|
107
|
+
- [ ] `labelStyles.ts` imported; column headers never use hint-label vars
|
|
108
|
+
- [ ] No CFM-specific accent overrides — Question card tokens only
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# UI Spec: Rating Matrix Grid (`type: 'RATING_MATRIX'`)
|
|
2
2
|
|
|
3
|
-
> **
|
|
3
|
+
> **COPY TEMPLATE FIRST:** [`templates/CsatMatrixScale.tsx`](../templates/CsatMatrixScale.tsx) — **same component as CSAT_MATRIX**
|
|
4
|
+
>
|
|
4
5
|
> **SDK type:** `RatingMatrixQuestion` — `fetchSurvey/types/ratingMatrix.ts`
|
|
5
6
|
> **Component blueprint:** `01-components/05-csat-matrix-scale.md`
|
|
6
7
|
|
|
@@ -8,34 +9,63 @@
|
|
|
8
9
|
|
|
9
10
|
| Field | Implemented | UI impact |
|
|
10
11
|
|-------|-------------|-----------|
|
|
11
|
-
| `statementRows[]`, `scaleColumns[]` | ✅ | Grid
|
|
12
|
-
| `displayStyle` | ✅ |
|
|
12
|
+
| `statementRows[]`, `scaleColumns[]` | ✅ | Grid data |
|
|
13
|
+
| `displayStyle` | ✅ | **numbered / graphics / standard** — NOT generic radio grid |
|
|
13
14
|
| `gridLayout` | ✅ | carousel vs standard vs dropdown |
|
|
14
|
-
| `hasNotApplicableOption` | ✅ | N/A column or checkbox |
|
|
15
|
-
| `reverseScaleOrder` | ✅ | Flip emoji/star/number index |
|
|
16
15
|
| `scaleAnchorLabels[]` | ✅ | Anchor row above columns |
|
|
17
|
-
| `minLabel` / `maxLabel` | ✅ | Rendered in Question.tsx above scale |
|
|
18
|
-
| `
|
|
16
|
+
| `minLabel` / `maxLabel` | ✅ | Rendered in `Question.tsx` above scale |
|
|
17
|
+
| `hasNotApplicableOption` | ✅ | N/A column |
|
|
18
|
+
| `reverseScaleOrder` | ✅ | Flip column index |
|
|
19
19
|
|
|
20
|
-
##
|
|
20
|
+
## Statement & label visibility (CRITICAL)
|
|
21
21
|
|
|
22
|
-
Same as `
|
|
22
|
+
Same rules as [`04-csat.md`](04-csat.md):
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
1. **180px row column** — `statementRows[].statementText` via `dangerouslySetInnerHTML`
|
|
25
|
+
2. **Header row** — `scaleAnchorLabels` OR `scaleColumns[].label` (never both missing)
|
|
26
|
+
3. **Page 14 numbered fixture** — 10 column headers "1"–"10" above radio/badge row
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
## displayStyle branches (do NOT collapse to generic radios)
|
|
27
29
|
|
|
28
|
-
|
|
30
|
+
| displayStyle | UI | Page fixture |
|
|
31
|
+
|--------------|-----|--------------|
|
|
32
|
+
| `numbered` | 40×40 magenta badges showing `colIdx+1`; tooltip = `col.label` | Page 14 Q8 |
|
|
33
|
+
| `graphics` | One `CustomSliderTrack` per row; `min=0`, `max=cols-1`, `tickLabels=scaleColumns.map(c=>c.label)` | Page 11 |
|
|
34
|
+
| `standard` | Custom 18px radio circles, `#e20074` accent | — |
|
|
35
|
+
| `emoji` / `star` | Same as CSAT — see [`04-csat.md`](04-csat.md) | — |
|
|
36
|
+
|
|
37
|
+
**graphics mode is NOT `SliderMatrixScale`** — it uses `CsatMatrixScale` with `CustomSliderTrack` per statement row.
|
|
38
|
+
|
|
39
|
+
## minLabel / maxLabel (Page 11)
|
|
29
40
|
|
|
30
|
-
|
|
41
|
+
`Question.tsx` template renders these above `CsatMatrixScale` when set on the question.
|
|
42
|
+
|
|
43
|
+
## Answer Mutation
|
|
31
44
|
|
|
32
45
|
```typescript
|
|
33
|
-
onSelect(MatrixRowAnswers)
|
|
34
|
-
|
|
46
|
+
onSelect(MatrixRowAnswers) // keys = statementRows[].id, values = scaleColumns[].id
|
|
47
|
+
matrixColumnStoredValue(col) // returns col.id or null for N/A
|
|
35
48
|
```
|
|
36
49
|
|
|
37
50
|
## Agent Checklist
|
|
38
51
|
|
|
39
|
-
- [ ] Reuse
|
|
40
|
-
- [ ]
|
|
41
|
-
- [ ]
|
|
52
|
+
- [ ] Reuse copied `CsatMatrixScale` — do not create separate RatingMatrixScale
|
|
53
|
+
- [ ] Statement text visible in 180px column
|
|
54
|
+
- [ ] Column headers or anchor labels visible
|
|
55
|
+
- [ ] `displayStyle: numbered` uses badges, not native radios
|
|
56
|
+
- [ ] `displayStyle: graphics` uses CustomSliderTrack per row
|
|
57
|
+
|
|
58
|
+
## Wizard customization (Phase 5 — required)
|
|
59
|
+
|
|
60
|
+
> Reuses `CsatMatrixScale` patterns — see [`wizard-question-type-styling.md`](../00-integration/wizard-question-type-styling.md) § RATING_MATRIX.
|
|
61
|
+
|
|
62
|
+
| Format | Key tokens |
|
|
63
|
+
|--------|------------|
|
|
64
|
+
| radio | Horizontal grid + `matrixRadioRingStyle` |
|
|
65
|
+
| star / emoji / numbered | Same as CSAT formats |
|
|
66
|
+
| graphics / slider | `CustomSliderTrack` thumb ring + annulus + dot |
|
|
67
|
+
|
|
68
|
+
### Wizard checklist
|
|
69
|
+
|
|
70
|
+
- [ ] Radio format is horizontal grid (not vertical list)
|
|
71
|
+
- [ ] `csatColumnLabelStyle()` on column headers
|