@explorer02/cfm-survey-sdk 0.1.1 → 0.1.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/index.d.ts CHANGED
@@ -1,54 +1,614 @@
1
+ import React from 'react';
2
+
3
+ /**
4
+ * Visual display type for scale/rating input controls.
5
+ * Determines how individual answer options are rendered.
6
+ *
7
+ * - `'emoji'` — Sentiment emoji faces (😞 → 😐 → 😊)
8
+ * - `'star'` — Star icons (☆ → ★)
9
+ * - `'radio'` — Standard radio-button circles
10
+ * - `'numbered'` — Numeric badge buttons (1, 2, 3 …)
11
+ * - `'number'` — Alias for `'numbered'` (legacy value from API)
12
+ * - `'dropdown'` — Collapsed select dropdown
13
+ * - `'selectbox'` — Expanded list of selectable cards
14
+ * - `'graphics'` — Continuous slider with emoji thumb
15
+ */
16
+ type SurveyButtonType = 'emoji' | 'star' | 'radio' | 'numbered' | 'number' | 'dropdown' | 'selectbox' | 'graphics';
1
17
  /**
2
- * Types representing survey option selections (e.g., choice options in a question).
18
+ * A single selectable option used by rating, radio, CSAT, and matrix question types.
3
19
  */
4
20
  type SurveyOption = {
21
+ /**
22
+ * Unique identifier for this option, sourced from the API's `answerDetail.id`.
23
+ * Falls back to the option's index position as a string if the API omits it.
24
+ */
25
+ id: string;
26
+ /** The text label displayed to the respondent */
5
27
  label: string;
6
- value: string | number;
28
+ /** The underlying value stored in the answer payload when this option is selected */
29
+ value: string | number | null;
30
+ /** Optional color hint (e.g. NPS traffic-light hex, or HSL scale color) */
7
31
  color?: string;
8
32
  };
9
33
  /**
10
- * Types representing a processed question from the survey.
34
+ * Fields present on every question variant.
35
+ *
36
+ * All question-specific types extend this via intersection (`&`).
11
37
  */
12
- type SurveyQuestion = {
38
+ type QuestionBase = {
39
+ /**
40
+ * Unique identifier for this question.
41
+ *
42
+ * ⚠️ CRITICAL: Set this as the `id` attribute on the outermost DOM element
43
+ * so the SDK's scroll-to-error logic can find it via `document.getElementById`.
44
+ */
13
45
  id: string;
46
+ /**
47
+ * Question text — may contain HTML markup.
48
+ * Always render with `dangerouslySetInnerHTML` or an HTML-safe renderer.
49
+ */
14
50
  text: string;
15
- type: 'rating' | 'radio' | 'text';
51
+ /**
52
+ * Optional description / supplementary instruction text — may contain HTML markup.
53
+ * Rendered below the main question text.
54
+ */
55
+ description?: string;
56
+ /** Whether the respondent must answer this question before proceeding */
57
+ required?: boolean;
58
+ /** Localised error message shown when a required question is left unanswered */
59
+ requiredErrorMessage?: string;
60
+ /**
61
+ * Ordinal position of this question in the survey (1-indexed).
62
+ * Useful for displaying question numbers in the UI.
63
+ */
64
+ questionNumber?: number;
65
+ /**
66
+ * URL of a media asset displayed alongside the question (container image/video).
67
+ * This field is present on ANY question type that has an attached container asset —
68
+ * not just TEXT_AND_MEDIA questions.
69
+ */
70
+ containerMediaUrl?: string;
71
+ /**
72
+ * MIME type of the container asset.
73
+ * Use this to decide between `<img>` and `<video>` rendering.
74
+ */
75
+ containerMediaMimeType?: string;
76
+ /**
77
+ * Layout alignment of the container media relative to the question text.
78
+ * Sourced from the `QUESTION_CONTAINER_MEDIA_ALIGNMENT` question config.
79
+ * Common values: `'LEFT_CENTER'`, `'RIGHT_CENTER'`, `'TOP'`, `'BOTTOM'`
80
+ */
81
+ containerMediaAlignment?: string;
82
+ /**
83
+ * Width of the container media as a percentage of the question card width.
84
+ * Sourced from the `TEXT_AND_MEDIA_SIZE` question config.
85
+ * e.g. `50` means the media takes up 50% of the width.
86
+ */
87
+ containerMediaSize?: number;
88
+ };
89
+
90
+ /**
91
+ * NPS Rating question — a 0-to-10 numeric badge scale.
92
+ *
93
+ * API source: `SCALE` answerType with `SCALE_TYPE=TEN_POINT` config (or `NPS_SCALE`).
94
+ * Answer shape: `number` — the selected badge value (e.g. `7`).
95
+ *
96
+ * The `options` array is auto-generated (0–10) if the API omits it;
97
+ * each option carries a color from the NPS traffic-light palette
98
+ * (red for 0–6, amber for 7–8, green for 9–10).
99
+ */
100
+ type RatingQuestion = QuestionBase & {
101
+ type: 'rating';
102
+ /** Selectable rating options (0–10), each with an NPS traffic-light color */
16
103
  options: SurveyOption[];
104
+ /** Label shown at the left/low end of the scale (e.g. "Not Likely") */
17
105
  minLabel?: string;
106
+ /** Label shown at the middle of the scale (e.g. "Neutral") */
18
107
  midLabel?: string;
108
+ /** Label shown at the right/high end of the scale (e.g. "Extremely Likely") */
19
109
  maxLabel?: string;
110
+ /**
111
+ * Fractional position index for the mid label within the option list.
112
+ * Used to compute the CSS `left` percentage offset of the mid-label element.
113
+ * Formula: `(midLabelIndex / (options.length - 1)) * 100`%
114
+ */
20
115
  midLabelIndex?: number;
116
+ /** Whether the scale options should be rendered in reverse order (high → low) */
117
+ reverseScaleOrder?: boolean;
118
+ /** Visual style for each option button */
119
+ buttonVariant?: 'radio' | 'numbered' | 'emoji';
120
+ };
121
+
122
+ /**
123
+ * Multiple Choice Question (MCQ) — a single-select radio list.
124
+ *
125
+ * API source: `MCQ` answerType (and any unrecognised answerType as a safe fallback).
126
+ * Answer shape: `string | number` — the `.value` of the selected option.
127
+ */
128
+ type RadioQuestion = QuestionBase & {
129
+ type: 'radio';
130
+ /** Selectable answer choices rendered as radio buttons or option cards */
131
+ options: SurveyOption[];
132
+ /**
133
+ * Whether the respondent can select more than one option.
134
+ * When `true`, the answer shape becomes `(string | number)[]`.
135
+ */
136
+ isMultiSelect?: boolean;
137
+ };
138
+
139
+ /**
140
+ * Text Field question — short or long free-text input.
141
+ *
142
+ * API source: `TEXTFIELD` answerType.
143
+ * Answer shape: `string` — the raw text typed by the respondent.
144
+ *
145
+ * Both "Short Text Field" and "Long Text Field" from the survey builder
146
+ * map to this type. UI components use `maxCharacterCount` to decide
147
+ * between a single-line `<input>` and a multi-line `<textarea>`.
148
+ */
149
+ type TextQuestion = QuestionBase & {
150
+ type: 'text';
151
+ /** Maximum number of characters allowed; should be enforced client-side */
21
152
  maxCharacterCount?: number;
22
- required?: boolean;
23
- requiredErrorMessage?: string;
153
+ /** Placeholder hint shown inside the empty input field */
24
154
  placeholder?: string;
155
+ /**
156
+ * Text input variant.
157
+ * - `'short'` — Rendered as a single-line text input.
158
+ * - `'long'` — Rendered as a multi-line textarea.
159
+ */
160
+ inputVariant?: 'short' | 'long';
161
+ };
162
+
163
+ /**
164
+ * A single row (sub-question) in a matrix question.
165
+ * Each row represents an independent statement the respondent rates
166
+ * by selecting one of the shared column options.
167
+ */
168
+ type MatrixRow = {
169
+ /** Unique row ID — used as the key in the `Record<rowId, value>` answer map */
170
+ id: string;
171
+ /** Primary statement text shown at the start of the row. May contain HTML. */
172
+ text: string;
173
+ /** Right-side statement for bipolar scales (e.g. "Agree" ↔ "Disagree") */
174
+ rightText?: string;
175
+ };
176
+ /**
177
+ * A single column header in a matrix question.
178
+ * Columns are shared across all rows; selecting a cell in a column records
179
+ * that column's `value` for the corresponding row.
180
+ */
181
+ type MatrixColumn = {
182
+ /** Unique column identifier */
183
+ id: string;
184
+ /** Display label shown in the column header (e.g. "Strongly Agree") */
185
+ label: string;
186
+ /** Value recorded in the payload when this column is selected for any given row */
187
+ value: string | number | null;
188
+ /** Optional color hint — used by RATING_MATRIX scale columns (HSL or hex) */
189
+ color?: string;
190
+ };
191
+ /**
192
+ * CFM_MATRIX / CSAT_MATRIX — a grid of rows × columns where the respondent
193
+ * selects exactly one column per row (or N/A).
194
+ *
195
+ * API sources: `CFM_MATRIX`, `CSAT_MATRIX` answerType.
196
+ * Answer shape: `Record<rowId, columnValue | null>` — one entry per row.
197
+ * - `null` means the respondent selected the N/A option for that row.
198
+ *
199
+ * Use `subType` to distinguish visual styling:
200
+ * - `'CSAT_MATRIX'` → rendered with emoji/star/numbered buttons per cell.
201
+ * - `'CFM_MATRIX'` → rendered as a Likert-style text-label grid.
202
+ * - `'RATING_MATRIX'` → rendered with color-coded NPS-style rating buttons.
203
+ */
204
+ type MatrixQuestion = QuestionBase & {
205
+ type: 'matrix';
206
+ /** Original API sub-type — use this to apply the correct visual variant */
207
+ subType: 'CFM_MATRIX' | 'CSAT_MATRIX' | 'RATING_MATRIX';
208
+ /** Ordered list of row sub-questions (statements to rate) */
209
+ rows: MatrixRow[];
210
+ /**
211
+ * Shared column header options (same columns appear for every row).
212
+ * Columns with `value === null` represent the N/A column.
213
+ */
214
+ columns: MatrixColumn[];
215
+ /** Visual display type for cell input controls (only applies to CSAT_MATRIX) */
216
+ buttonType?: SurveyButtonType;
217
+ /** Whether the last column is a Not Applicable (N/A) option */
218
+ hasNotApplicable?: boolean;
219
+ /** Whether the scale columns should be displayed in reverse order (high → low) */
220
+ reverseScaleOrder?: boolean;
221
+ /**
222
+ * Anchor labels displayed above the scale columns at evenly distributed positions.
223
+ * (e.g. `['Strongly Disagree', 'Neutral', 'Strongly Agree']`)
224
+ */
225
+ labels?: string[];
226
+ /**
227
+ * Layout format for the matrix.
228
+ * - `'standard'` — All rows displayed as a grid at once.
229
+ * - `'carousel'` — One row shown at a time with prev/next navigation.
230
+ * - `'dropdown'` — Each row's answer is selected via a dropdown menu.
231
+ */
232
+ matrixFormat?: 'standard' | 'carousel' | 'dropdown';
233
+ /**
234
+ * Scale polarity for CFM_MATRIX.
235
+ * - `'likert'` — Standard unipolar scale (e.g. Strongly Agree → Strongly Disagree).
236
+ * - `'bipolar'` — Two-sided scale with opposite statements at each end.
237
+ */
238
+ matrixType?: 'likert' | 'bipolar';
239
+ /**
240
+ * Answer selection mode for CFM_MATRIX.
241
+ * - `'single'` — One selection per row.
242
+ * - `'multiple'` — Multiple selections allowed per row.
243
+ */
244
+ answerType?: 'single' | 'multiple';
245
+ /** Whether to transpose the table (rows become columns and vice versa) */
246
+ transposeTable?: boolean;
247
+ /** Whether to repeat the column header row at regular intervals */
248
+ repeatScale?: boolean;
249
+ /** Whether to show the scale header row at all */
250
+ enableScale?: boolean;
25
251
  };
252
+
26
253
  /**
27
- * Type representing a survey page.
254
+ * RATING_MATRIX a grid of rows × NPS-style rating columns.
255
+ * Visually similar to `MatrixQuestion` (CSAT_MATRIX), but specifically
256
+ * for rating scales where columns carry traffic-light colors and endpoint labels.
257
+ *
258
+ * API source: `RATING_MATRIX` answerType.
259
+ * Answer shape: `Record<rowId, columnValue | null>` — one entry per row.
260
+ * - `null` means the respondent selected the N/A option for that row.
261
+ */
262
+ type RatingMatrixQuestion = QuestionBase & {
263
+ type: 'rating_matrix';
264
+ /** Ordered list of row sub-questions (statements to rate) */
265
+ rows: MatrixRow[];
266
+ /**
267
+ * Rating-scale column options — each may carry a `color` for visual emphasis.
268
+ * Columns with `value === null` represent the N/A column.
269
+ */
270
+ columns: MatrixColumn[];
271
+ /** Label for the lowest end of the rating scale (e.g. "Poor") */
272
+ minLabel?: string;
273
+ /** Label for the highest end of the rating scale (e.g. "Excellent") */
274
+ maxLabel?: string;
275
+ /** Visual display type for each cell's input control */
276
+ buttonType?: SurveyButtonType;
277
+ /** Whether the last column is a Not Applicable (N/A) option */
278
+ hasNotApplicable?: boolean;
279
+ /** Whether the scale columns should be displayed in reverse order (high → low) */
280
+ reverseScaleOrder?: boolean;
281
+ /**
282
+ * Anchor labels displayed above the scale columns at evenly distributed positions.
283
+ * (e.g. `['Strongly Disagree', 'Neutral', 'Strongly Agree']`)
284
+ */
285
+ labels?: string[];
286
+ };
287
+
288
+ /**
289
+ * A single row in a SLIDER_MATRIX question.
290
+ * Each row carries its own independent slider range configuration.
291
+ */
292
+ type SliderMatrixRow = MatrixRow & {
293
+ /** Minimum value of this row's slider track */
294
+ min: number;
295
+ /** Maximum value of this row's slider track */
296
+ max: number;
297
+ /** Step increment between selectable values (e.g. `1` for integers) */
298
+ step: number;
299
+ /** Optional initial position of the slider handle before interaction */
300
+ defaultValue?: number;
301
+ };
302
+ /**
303
+ * A discrete tick mark on a slider track, carrying a display label and color.
304
+ * Used by `SliderMatrixQuestion.tickValues` for rich visual scales.
305
+ */
306
+ type TickValue = {
307
+ /** Numeric position on the slider track */
308
+ value: number;
309
+ /** Display label shown at this tick (e.g. `"0"`, `"Neutral"`) */
310
+ label: string;
311
+ /** HSL or hex color for the tick / emoji at this position */
312
+ color: string;
313
+ };
314
+ /**
315
+ * SLIDER_MATRIX — a grid of rows where each row has an independent range slider.
316
+ * The respondent drags a handle to set a numeric position per row.
317
+ *
318
+ * API source: `SLIDER_MATRIX` answerType.
319
+ * Answer shape: `Record<rowId, number | null>` — one numeric value per row,
320
+ * or `null` when the respondent selects the N/A option for that row.
321
+ */
322
+ type SliderMatrixQuestion = QuestionBase & {
323
+ type: 'slider_matrix';
324
+ /** Rows — each row carries its own min/max/step slider configuration */
325
+ rows: SliderMatrixRow[];
326
+ /**
327
+ * Anchor text labels displayed above the slider tracks at evenly distributed positions.
328
+ * (e.g. `['Label 1', 'Label 3', 'Label 5']`)
329
+ */
330
+ labels?: string[];
331
+ /**
332
+ * Number of discrete tick marks rendered on the slider track.
333
+ * Defaults to `10` if not set by the survey configuration.
334
+ */
335
+ ticks?: number;
336
+ /**
337
+ * Pre-calculated tick values with display labels and HSL color mapping.
338
+ * The colors transition Red → Amber → Green from index 0 to the last tick.
339
+ */
340
+ tickValues?: TickValue[];
341
+ /** Whether to show a numeric text input box alongside the slider handle */
342
+ enableInputBox?: boolean;
343
+ /** Whether to display the current value as a tooltip on the slider thumb */
344
+ displayValues?: boolean;
345
+ /** Whether to include a Not Applicable (N/A) option per row */
346
+ enableNotApplicable?: boolean;
347
+ /**
348
+ * Whether this is a Graphics Slider (renders an emoji thumb instead of a plain handle).
349
+ * When `'graphics'`, the slider thumb shows a sentiment emoji that changes with the value.
350
+ */
351
+ sliderType?: 'graphics';
352
+ };
353
+
354
+ /**
355
+ * Text and Media — a display-only informational card.
356
+ * No answer is recorded for this type — it is purely presentational.
357
+ *
358
+ * API source: `TEXT_AND_MEDIA` answerType.
359
+ * ⚠️ Do NOT dispatch a CHANGE action for this question type.
360
+ * ⚠️ This type is excluded from validation and response progress counting.
361
+ *
362
+ * The heading text is sourced from `answerDetails` where
363
+ * `additional.textAndMediaFieldType === 'HEADER'`, not from `questionText`.
364
+ */
365
+ type TextAndMediaQuestion = QuestionBase & {
366
+ type: 'text_and_media';
367
+ /**
368
+ * Public URL of the image or video asset to display.
369
+ * Sourced from `questionContainerAssets[0].source`.
370
+ */
371
+ mediaUrl?: string;
372
+ /**
373
+ * MIME type of the media asset.
374
+ * - `'image/*'` for photos.
375
+ * - `'video/*'` for video clips.
376
+ * Use this to decide between `<img>` and `<video>` rendering.
377
+ */
378
+ mediaMimeType?: 'image/*' | 'video/*' | string;
379
+ /** Optional caption or alt text rendered beneath the media asset */
380
+ mediaTitle?: string;
381
+ /**
382
+ * Layout position of the media relative to the text content.
383
+ * Sourced from the `TEXT_AND_MEDIA_ALIGNMENT` question config.
384
+ * (e.g. `'BOTTOM_LEFT'`, `'TOP_CENTER'`, `'LEFT_CENTER'`)
385
+ */
386
+ mediaAlignment?: string;
387
+ /**
388
+ * Width of the media container as a percentage of the card width.
389
+ * Sourced from the `TEXT_AND_MEDIA_SIZE` question config.
390
+ * (e.g. `50` means the image takes up 50% of the width)
391
+ */
392
+ mediaSize?: number;
393
+ };
394
+
395
+ /**
396
+ * CSAT Rating — a short customer satisfaction scale, typically 1–5.
397
+ * Usually rendered with emoji face icons (😞 → 😐 → 😊).
398
+ *
399
+ * API source: `SCALE` answerType with `SCALE_TYPE=CSAT` config,
400
+ * or heuristically detected when the option count is ≤ 5.
401
+ * Answer shape: `string | number | null` — the selected satisfaction value,
402
+ * or `null` when the respondent selects the N/A option.
403
+ */
404
+ type CsatQuestion = QuestionBase & {
405
+ type: 'csat';
406
+ /**
407
+ * Selectable satisfaction options — usually 1 to 5.
408
+ * The UI should render these as emoji/face icons rather than numeric badges.
409
+ */
410
+ options: SurveyOption[];
411
+ /** Label for the lowest satisfaction level (e.g. "Very Dissatisfied") */
412
+ minLabel?: string;
413
+ /** Label for the highest satisfaction level (e.g. "Very Satisfied") */
414
+ maxLabel?: string;
415
+ /** Visual display type for the scale options */
416
+ buttonType?: SurveyButtonType;
417
+ /** Whether the user can select an N/A (Not Applicable) option */
418
+ hasNotApplicable?: boolean;
419
+ /** Whether the options should be displayed in reverse order (high → low) */
420
+ reverseScaleOrder?: boolean;
421
+ /**
422
+ * Where the min/max labels are positioned relative to the scale buttons.
423
+ * - `'top'` — Labels appear above the scale
424
+ * - `'bottom'` — Labels appear below the scale
425
+ * - `'hidden'` — Labels are not shown
426
+ */
427
+ labelPosition?: 'top' | 'bottom' | 'hidden';
428
+ };
429
+
430
+ /**
431
+ * Standalone Slider — a single continuous range input.
432
+ * The respondent drags a handle to set a numeric value along a track.
433
+ *
434
+ * API source: `SCALE` answerType with `INPUT_TYPE=SLIDER` questionConfig.
435
+ * Answer shape: `number` — the position of the slider handle on the track.
436
+ */
437
+ type SliderQuestion = QuestionBase & {
438
+ type: 'slider';
439
+ /** Minimum value at the left end of the slider track */
440
+ min: number;
441
+ /** Maximum value at the right end of the slider track */
442
+ max: number;
443
+ /** Step increment between selectable positions (e.g. `1` for whole numbers) */
444
+ step: number;
445
+ /** Optional initial position of the slider handle before interaction */
446
+ defaultValue?: number;
447
+ /** Label displayed at the minimum (left) end of the track */
448
+ minLabel?: string;
449
+ /** Label displayed at the maximum (right) end of the track */
450
+ maxLabel?: string;
451
+ /** Whether to show a numeric text input box alongside the slider handle */
452
+ enableInputBox?: boolean;
453
+ /** Whether to display the current value as a floating tooltip on the thumb */
454
+ displayValues?: boolean;
455
+ };
456
+
457
+ /**
458
+ * Rating Scale — a star, emoji, or icon-based rating question (e.g. 1–5 stars).
459
+ *
460
+ * Unlike the NPS `'rating'` type, this does NOT apply traffic-light colors to options
461
+ * and is visually rendered as stars, emojis, thumbs, or similar icons.
462
+ *
463
+ * API source: `SCALE` answerType with `SCALE_TYPE=STAR`, `SCALE_TYPE=EMOJI`,
464
+ * or `SCALE_TYPE=FIVE_POINT` config.
465
+ * Answer shape: `number` — the selected star/icon value.
466
+ */
467
+ type RatingScaleQuestion = QuestionBase & {
468
+ type: 'rating_scale';
469
+ /**
470
+ * Selectable rating options (typically 1–5 or 1–10).
471
+ * These carry no NPS colors — the UI should render them as stars or custom icons.
472
+ */
473
+ options: SurveyOption[];
474
+ /** Label for the lowest rating end (e.g. "Poor") */
475
+ minLabel?: string;
476
+ /** Label for the midpoint of the scale (e.g. "Average") */
477
+ midLabel?: string;
478
+ /** Label for the highest rating end (e.g. "Excellent") */
479
+ maxLabel?: string;
480
+ /**
481
+ * Fractional position index for the mid label within the option list.
482
+ * Used to compute the CSS `left` percentage offset of the mid-label element.
483
+ * Formula: `(midLabelIndex / (options.length - 1)) * 100`%
484
+ */
485
+ midLabelIndex?: number;
486
+ /** Whether the scale options should be displayed in reverse order (high → low) */
487
+ reverseScaleOrder?: boolean;
488
+ /**
489
+ * The specific icon style used for this scale.
490
+ * - `'star'` — Classic star icons
491
+ * - `'emoji'` — Sentiment emoji faces
492
+ */
493
+ scaleStyle?: 'star' | 'emoji';
494
+ };
495
+
496
+ /**
497
+ * A file uploaded by the respondent in response to a `FILE_UPLOAD` question.
498
+ * This is the shape that should be passed to `onSelect` after a successful upload.
499
+ *
500
+ * @example
501
+ * ```ts
502
+ * const handleUpload = (file: File) => {
503
+ * uploadToServer(file).then((result) => {
504
+ * onSelect([{ id: result.id, name: file.name, mediaUrl: result.url }]);
505
+ * });
506
+ * };
507
+ * ```
508
+ */
509
+ type UploadedFile = {
510
+ /** Unique asset ID returned by the upload server */
511
+ id: string;
512
+ /** Original filename of the uploaded file */
513
+ name: string;
514
+ /** Public URL to access the uploaded file */
515
+ mediaUrl: string;
516
+ /** MIME type of the uploaded file (e.g. `'image/png'`, `'application/pdf'`) */
517
+ mimeType?: string;
518
+ /** File size in bytes */
519
+ size?: number;
520
+ };
521
+ /**
522
+ * File Upload question — prompts the respondent to attach one or more files.
523
+ *
524
+ * API source: `FILE_UPLOAD` answerType.
525
+ * Answer shape: `UploadedFile[]` — an array of successfully uploaded file descriptors.
526
+ * Submit target: `quesIdVsAttachmentDetails[questionId]` (NOT `questionToAnswers`).
527
+ */
528
+ type FileUploadQuestion = QuestionBase & {
529
+ type: 'file_upload';
530
+ /**
531
+ * Custom instruction text displayed above the upload area.
532
+ * May contain HTML markup; render with `dangerouslySetInnerHTML`.
533
+ */
534
+ uploadMessage?: string;
535
+ /**
536
+ * List of accepted file extensions in uppercase without dots (e.g. `['PDF', 'PNG', 'DOCX']`).
537
+ * Use these to set the `accept` attribute on the `<input type="file">` element.
538
+ */
539
+ supportedFileFormats?: string[];
540
+ /**
541
+ * Maximum number of files the respondent can attach to this question.
542
+ * If `undefined`, no limit is enforced by the SDK.
543
+ */
544
+ maxFileCount?: number;
545
+ /**
546
+ * Maximum file size in megabytes (MB).
547
+ * Applies either per-file or in total, depending on `fileSizeLimitType`.
548
+ */
549
+ fileSizeLimit?: number;
550
+ /**
551
+ * Determines how `fileSizeLimit` is applied.
552
+ * - `'PER_FILE'` — Each individual file must not exceed `fileSizeLimit` MB.
553
+ * - `'IN_TOTAL'` — The combined size of all uploads must not exceed `fileSizeLimit` MB.
554
+ */
555
+ fileSizeLimitType?: 'PER_FILE' | 'IN_TOTAL';
556
+ };
557
+
558
+ /**
559
+ * Discriminated union of all question types surfaced by the SDK.
560
+ */
561
+ type SurveyQuestion = RatingQuestion | RadioQuestion | TextQuestion | MatrixQuestion | RatingMatrixQuestion | SliderMatrixQuestion | TextAndMediaQuestion | CsatQuestion | SliderQuestion | RatingScaleQuestion | FileUploadQuestion;
562
+ /**
563
+ * A single survey page containing an ordered list of questions.
28
564
  */
29
565
  type SurveyPageData = {
566
+ /** Unique page identifier */
30
567
  id: string;
568
+ /** Optional page title displayed as a section header */
31
569
  title?: string;
570
+ /** Ordered list of questions on this page */
32
571
  questions: SurveyQuestion[];
33
572
  };
573
+ /**
574
+ * A language option available for this survey.
575
+ */
34
576
  type SurveyLanguage = {
577
+ /** BCP-47 language code (e.g. `'en'`, `'de'`, `'fr'`) */
35
578
  code: string;
579
+ /** Human-readable language name (e.g. `'English'`, `'Deutsch'`) */
36
580
  name: string;
37
581
  };
38
582
  /**
39
- * Type representing the fully parsed and normalized Survey structure.
583
+ * The fully parsed and normalised survey structure returned by the SDK.
40
584
  */
41
585
  type Survey = {
586
+ /** Unique survey identifier */
42
587
  id: string;
588
+ /** Active language code for this response session */
43
589
  language: string;
590
+ /** All languages configured for this survey */
44
591
  languages: SurveyLanguage[];
592
+ /** Ordered list of pages that make up the survey flow */
45
593
  pages: SurveyPageData[];
46
594
  };
47
595
 
48
596
  /**
49
- * Key-value mapping of question IDs to their selected answer values (string or number).
597
+ * Answer value for matrix questions (CFM_MATRIX, CSAT_MATRIX, RATING_MATRIX, SLIDER_MATRIX).
598
+ * Maps each row sub-question ID to the selected column value or slider position.
599
+ * For multi-select grids, the value can be an array of selected column values.
600
+ */
601
+ type MatrixAnswerMap = Record<string, string | number | null | Array<string | number | null>>;
602
+ /**
603
+ * Union of all possible answer value types across all question variants.
604
+ * - `string | number | null` — standard single-value questions (rating, radio, text, csat N/A)
605
+ * - `MatrixAnswerMap` — grid questions (matrix, rating_matrix, slider_matrix)
606
+ */
607
+ type AnswerValue = string | number | null | MatrixAnswerMap | any[];
608
+ /**
609
+ * Key-value mapping of question IDs to their answer values.
50
610
  */
51
- type SubmitAnswers = Record<string, string | number>;
611
+ type SubmitAnswers = Record<string, AnswerValue>;
52
612
  /**
53
613
  * The final output result resolved after a successful submit response.
54
614
  */
@@ -80,7 +640,7 @@ type SurveyAction = {
80
640
  type: ACTIONS.CHANGE | 'CHANGE';
81
641
  payload: {
82
642
  questionId: string;
83
- value: string | number;
643
+ value: AnswerValue;
84
644
  };
85
645
  };
86
646
  /**
@@ -124,4 +684,23 @@ declare function useSurveySDK({ options }?: UseSurveySDKProps): {
124
684
  onAction: (action: SurveyAction) => void;
125
685
  };
126
686
 
127
- export { type SurveyLanguage, type SurveyQuestion, useSurveySDK };
687
+ /**
688
+ * Extended 11-level CSAT emoji set.
689
+ * Each entry is a pre-built React element with colour and sizing.
690
+ */
691
+ declare const CsatEmojiSet: Record<number, React.ReactNode>;
692
+ /**
693
+ * Maps a 0-based column index within a scale of `total` items
694
+ * to the correct professional face icon from the 11-level set.
695
+ *
696
+ * Works for any scale length. Uses specific curated subsets for 3-6 lengths.
697
+ */
698
+ declare function getEmojiForIndex(index: number, total: number): React.ReactNode;
699
+ /** Legacy 1–5 mapping kept for backward compatibility with CsatScale (standalone) */
700
+ declare const CsatEmojiMapping: Record<number, React.ReactNode>;
701
+ declare const CsatStarIcons: {
702
+ filled: React.ReactNode;
703
+ empty: React.ReactNode;
704
+ };
705
+
706
+ export { type AnswerValue, CsatEmojiMapping, CsatEmojiSet, type CsatQuestion, CsatStarIcons, type FileUploadQuestion, type MatrixAnswerMap, type MatrixColumn, type MatrixQuestion, type MatrixRow, type QuestionBase, type RadioQuestion, type RatingMatrixQuestion, type RatingQuestion, type RatingScaleQuestion, type SliderMatrixQuestion, type SliderMatrixRow, type SliderQuestion, type SurveyButtonType, type SurveyLanguage, type SurveyOption, type SurveyQuestion, type TextAndMediaQuestion, type TextQuestion, type TickValue, type UploadedFile, getEmojiForIndex, useSurveySDK };