@product7/feedback-sdk 1.6.9 → 1.7.1
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/docs/survey-test-cases.md +398 -0
- package/dist/feedback-sdk.js +230 -18
- package/dist/feedback-sdk.js.map +1 -1
- package/dist/feedback-sdk.min.js +1 -1
- package/dist/feedback-sdk.min.js.map +1 -1
- package/package.json +1 -1
- package/src/api/mock-data/index.js +157 -16
- package/src/core/FeedbackSDK.js +1 -0
- package/src/widgets/SurveyWidget.js +72 -2
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
# Survey Widget — QA Test Cases
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
This document covers manual test cases for verifying the survey widget functionality across all supported survey types (NPS, CSAT, CES, Custom, and Multi-page).
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Prerequisites
|
|
9
|
+
|
|
10
|
+
- SDK is initialized with a valid `workspace` and `userContext`
|
|
11
|
+
- `init()` has been called successfully (session token received)
|
|
12
|
+
- Browser DevTools open to monitor network requests and console
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Test Environment Setup
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
const sdk = new FeedbackSDK({
|
|
20
|
+
workspace: 'your-workspace',
|
|
21
|
+
userContext: { user_id: 'qa_user_01', email: 'qa@example.com' }
|
|
22
|
+
});
|
|
23
|
+
await sdk.init();
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## TC-01: NPS Survey — Basic Display
|
|
29
|
+
|
|
30
|
+
**Objective:** Verify NPS survey renders correctly with 11-point scale.
|
|
31
|
+
|
|
32
|
+
**Steps:**
|
|
33
|
+
1. Call `sdk.showSurvey({ surveyType: 'nps', ratingScale: 11, title: 'How likely are you to recommend us?', lowLabel: 'Not likely', highLabel: 'Very likely' })`
|
|
34
|
+
2. Observe the survey widget on screen
|
|
35
|
+
|
|
36
|
+
**Expected Results:**
|
|
37
|
+
- [ ] Survey widget appears at the configured position (default: bottom-right)
|
|
38
|
+
- [ ] Title text is displayed: "How likely are you to recommend us?"
|
|
39
|
+
- [ ] 11 score buttons are shown, numbered 0 through 10
|
|
40
|
+
- [ ] "Not likely" label appears below the leftmost button
|
|
41
|
+
- [ ] "Very likely" label appears below the rightmost button
|
|
42
|
+
- [ ] A close (✕) button is visible in the top-right corner of the widget
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## TC-02: NPS Survey — Score Selection
|
|
47
|
+
|
|
48
|
+
**Objective:** Verify score buttons highlight correctly on selection.
|
|
49
|
+
|
|
50
|
+
**Steps:**
|
|
51
|
+
1. Show NPS survey (as in TC-01)
|
|
52
|
+
2. Click score button **7**
|
|
53
|
+
3. Click score button **9**
|
|
54
|
+
|
|
55
|
+
**Expected Results:**
|
|
56
|
+
- [ ] After clicking 7: button 7 is visually highlighted/selected
|
|
57
|
+
- [ ] After clicking 9: button 9 is highlighted, button 7 is no longer highlighted
|
|
58
|
+
- [ ] Only one button is selected at a time
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## TC-03: NPS Survey — Submission
|
|
63
|
+
|
|
64
|
+
**Objective:** Verify NPS survey submits the selected score.
|
|
65
|
+
|
|
66
|
+
**Steps:**
|
|
67
|
+
1. Show NPS survey with `showSubmitButton: true` and a valid `surveyId`
|
|
68
|
+
2. Click score button **8**
|
|
69
|
+
3. Click the **Submit** button
|
|
70
|
+
4. Observe network tab
|
|
71
|
+
|
|
72
|
+
**Expected Results:**
|
|
73
|
+
- [ ] A POST request is sent to `/widget/surveys/{surveyId}/responses`
|
|
74
|
+
- [ ] Request payload contains `rating: 8`
|
|
75
|
+
- [ ] Survey widget closes after submission
|
|
76
|
+
- [ ] A success notification ("Thank you for your feedback!") appears briefly
|
|
77
|
+
- [ ] `survey:submitted` event is fired (verify in console with `sdk.on('survey:submitted', console.log)`)
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## TC-04: NPS Survey — Submit Without Score
|
|
82
|
+
|
|
83
|
+
**Objective:** Verify error message when submitting with no score selected.
|
|
84
|
+
|
|
85
|
+
**Steps:**
|
|
86
|
+
1. Show NPS survey with `showSubmitButton: true`
|
|
87
|
+
2. Do NOT click any score button
|
|
88
|
+
3. Click the **Submit** button
|
|
89
|
+
|
|
90
|
+
**Expected Results:**
|
|
91
|
+
- [ ] Error message "Please select a rating" appears inside the widget
|
|
92
|
+
- [ ] Survey does NOT close
|
|
93
|
+
- [ ] No API request is made
|
|
94
|
+
- [ ] Error disappears after ~3 seconds
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## TC-05: NPS Survey — Auto-submit on Select
|
|
99
|
+
|
|
100
|
+
**Objective:** Verify survey auto-submits when `autoSubmitOnSelect: true`.
|
|
101
|
+
|
|
102
|
+
**Steps:**
|
|
103
|
+
1. Call `sdk.showSurvey({ surveyType: 'nps', autoSubmitOnSelect: true, surveyId: 'survey-id' })`
|
|
104
|
+
2. Click any score button
|
|
105
|
+
|
|
106
|
+
**Expected Results:**
|
|
107
|
+
- [ ] Survey submits immediately upon clicking a score (no submit button click needed)
|
|
108
|
+
- [ ] POST request is sent with the selected score
|
|
109
|
+
- [ ] Survey closes and success notification shows
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## TC-06: CSAT Survey — Display and Selection
|
|
114
|
+
|
|
115
|
+
**Objective:** Verify CSAT survey renders emoji buttons correctly.
|
|
116
|
+
|
|
117
|
+
**Steps:**
|
|
118
|
+
1. Call `sdk.showSurvey({ surveyType: 'csat', title: 'How satisfied are you?' })`
|
|
119
|
+
2. Click the 4th emoji (😊)
|
|
120
|
+
|
|
121
|
+
**Expected Results:**
|
|
122
|
+
- [ ] 5 emoji buttons are displayed (😞 😟 😐 🙂 😄)
|
|
123
|
+
- [ ] Clicked emoji is visually selected/highlighted
|
|
124
|
+
- [ ] Other emojis are deselected
|
|
125
|
+
- [ ] Score corresponds to position (1–5)
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## TC-07: CES Survey — Display and Selection
|
|
130
|
+
|
|
131
|
+
**Objective:** Verify CES survey renders effort scale buttons.
|
|
132
|
+
|
|
133
|
+
**Steps:**
|
|
134
|
+
1. Call `sdk.showSurvey({ surveyType: 'ces', title: 'How easy was it?' })`
|
|
135
|
+
2. Click **Easy**
|
|
136
|
+
|
|
137
|
+
**Expected Results:**
|
|
138
|
+
- [ ] 5 buttons displayed: Very Difficult / Difficult / Neutral / Easy / Very Easy
|
|
139
|
+
- [ ] Clicked button is highlighted
|
|
140
|
+
- [ ] Other buttons are deselected
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## TC-08: Survey with Feedback Text Input
|
|
145
|
+
|
|
146
|
+
**Objective:** Verify optional feedback textarea appears and value is submitted.
|
|
147
|
+
|
|
148
|
+
**Steps:**
|
|
149
|
+
1. Show any survey with `showFeedbackInput: true` and `showSubmitButton: true`
|
|
150
|
+
2. Select a score
|
|
151
|
+
3. Type "Great product!" in the feedback textarea
|
|
152
|
+
4. Click Submit
|
|
153
|
+
|
|
154
|
+
**Expected Results:**
|
|
155
|
+
- [ ] Textarea is visible below the score selection
|
|
156
|
+
- [ ] Typed text appears in the textarea
|
|
157
|
+
- [ ] Submitted payload includes `feedback: "Great product!"`
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## TC-09: Survey Dismiss — Close Button
|
|
162
|
+
|
|
163
|
+
**Objective:** Verify close button dismisses the survey.
|
|
164
|
+
|
|
165
|
+
**Steps:**
|
|
166
|
+
1. Show any survey
|
|
167
|
+
2. Click the ✕ (close) button
|
|
168
|
+
|
|
169
|
+
**Expected Results:**
|
|
170
|
+
- [ ] Survey widget fades out and is removed from the DOM
|
|
171
|
+
- [ ] `survey:dismissed` event is fired
|
|
172
|
+
- [ ] If `onDismiss` callback was provided, it is called
|
|
173
|
+
- [ ] If a valid `surveyId` was set, a POST request is sent to `/widget/surveys/{surveyId}/dismiss`
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## TC-10: Survey Dismiss — Escape Key
|
|
178
|
+
|
|
179
|
+
**Objective:** Verify pressing Escape closes the survey.
|
|
180
|
+
|
|
181
|
+
**Steps:**
|
|
182
|
+
1. Show any survey
|
|
183
|
+
2. Press the **Escape** key on the keyboard
|
|
184
|
+
|
|
185
|
+
**Expected Results:**
|
|
186
|
+
- [ ] Survey closes (same behaviour as TC-09)
|
|
187
|
+
- [ ] `survey:dismissed` event fires
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## TC-11: Survey — Disabled State
|
|
192
|
+
|
|
193
|
+
**Objective:** Verify survey is suppressed when `enabled: false`.
|
|
194
|
+
|
|
195
|
+
**Steps:**
|
|
196
|
+
1. Call `sdk.showSurvey({ surveyType: 'nps', enabled: false })`
|
|
197
|
+
|
|
198
|
+
**Expected Results:**
|
|
199
|
+
- [ ] No survey widget appears in the DOM
|
|
200
|
+
- [ ] `showSurvey()` returns `null`
|
|
201
|
+
- [ ] `survey:suppressed` event is fired with `reason: 'disabled'`
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## TC-12: Survey — Position Variants
|
|
206
|
+
|
|
207
|
+
**Objective:** Verify survey renders in each supported position.
|
|
208
|
+
|
|
209
|
+
**Steps:**
|
|
210
|
+
1. Test each position: `bottom-right`, `bottom-left`, `center`
|
|
211
|
+
2. Call `sdk.showSurvey({ surveyType: 'nps', position: '<position>' })`
|
|
212
|
+
|
|
213
|
+
**Expected Results:**
|
|
214
|
+
| Position | Expected Appearance |
|
|
215
|
+
|---|---|
|
|
216
|
+
| `bottom-right` | Widget anchored to bottom-right corner |
|
|
217
|
+
| `bottom-left` | Widget anchored to bottom-left corner |
|
|
218
|
+
| `center` | Widget centered on screen with backdrop overlay |
|
|
219
|
+
|
|
220
|
+
- [ ] For `center`: backdrop overlay is displayed behind the widget
|
|
221
|
+
- [ ] Clicking the backdrop (center mode) closes the survey
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## TC-13: Survey — Theme Variants
|
|
226
|
+
|
|
227
|
+
**Objective:** Verify light and dark themes apply correctly.
|
|
228
|
+
|
|
229
|
+
**Steps:**
|
|
230
|
+
1. Test `theme: 'light'` and `theme: 'dark'`
|
|
231
|
+
|
|
232
|
+
**Expected Results:**
|
|
233
|
+
- [ ] Light theme: white/light background, dark text
|
|
234
|
+
- [ ] Dark theme: dark background, light text
|
|
235
|
+
- [ ] Theme class is applied to the survey element
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## TC-14: Multi-page Survey — Navigation
|
|
240
|
+
|
|
241
|
+
**Objective:** Verify multi-page survey navigates between pages correctly.
|
|
242
|
+
|
|
243
|
+
**Setup:**
|
|
244
|
+
```js
|
|
245
|
+
sdk.showSurvey({
|
|
246
|
+
surveyId: 'multi-page-id',
|
|
247
|
+
pages: [
|
|
248
|
+
{ id: 'p1', type: 'rating', title: 'Rate our product', position: 0, ratingConfig: { scale: 5 } },
|
|
249
|
+
{ id: 'p2', type: 'text', title: 'Any comments?', position: 1 },
|
|
250
|
+
]
|
|
251
|
+
});
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**Steps:**
|
|
255
|
+
1. Observe page 1
|
|
256
|
+
2. Click a rating button
|
|
257
|
+
3. Click **Next**
|
|
258
|
+
4. Observe page 2
|
|
259
|
+
5. Click **Back**
|
|
260
|
+
6. Observe page 1 again
|
|
261
|
+
|
|
262
|
+
**Expected Results:**
|
|
263
|
+
- [ ] Page 1 shows "Page 1 of 2" progress indicator
|
|
264
|
+
- [ ] Action button on page 1 reads "Next"
|
|
265
|
+
- [ ] After clicking Next: page 2 loads, progress shows "Page 2 of 2"
|
|
266
|
+
- [ ] Page 2 has a **Back** button
|
|
267
|
+
- [ ] Action button on page 2 reads "Submit"
|
|
268
|
+
- [ ] After clicking Back: page 1 is shown again
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## TC-15: Multi-page Survey — Validation
|
|
273
|
+
|
|
274
|
+
**Objective:** Verify each page validates input before advancing.
|
|
275
|
+
|
|
276
|
+
**Steps (Rating page):**
|
|
277
|
+
1. Display multi-page survey on page 1 (rating type)
|
|
278
|
+
2. Click **Next** without selecting a rating
|
|
279
|
+
|
|
280
|
+
**Expected Results:**
|
|
281
|
+
- [ ] Error message "Please select a rating" appears
|
|
282
|
+
- [ ] Survey does NOT advance to page 2
|
|
283
|
+
|
|
284
|
+
**Steps (Text page):**
|
|
285
|
+
1. Advance to a text-type page
|
|
286
|
+
2. Click **Submit** without entering any text
|
|
287
|
+
|
|
288
|
+
**Expected Results:**
|
|
289
|
+
- [ ] Error message "Please enter an answer" appears
|
|
290
|
+
- [ ] Survey does NOT submit
|
|
291
|
+
|
|
292
|
+
**Steps (Multiple choice page):**
|
|
293
|
+
1. Advance to a multiple-choice page
|
|
294
|
+
2. Click **Submit** without selecting any option
|
|
295
|
+
|
|
296
|
+
**Expected Results:**
|
|
297
|
+
- [ ] Error message "Please select an option" appears
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## TC-16: Multi-page Survey — Full Submission
|
|
302
|
+
|
|
303
|
+
**Objective:** Verify complete multi-page survey submits all page answers.
|
|
304
|
+
|
|
305
|
+
**Steps:**
|
|
306
|
+
1. Display the multi-page survey (TC-14 setup)
|
|
307
|
+
2. On page 1: select a rating, click **Next**
|
|
308
|
+
3. On page 2: type a comment, click **Submit**
|
|
309
|
+
4. Check network request
|
|
310
|
+
|
|
311
|
+
**Expected Results:**
|
|
312
|
+
- [ ] POST to `/widget/surveys/{surveyId}/responses` is made
|
|
313
|
+
- [ ] Payload `answers.page_answers` contains entries for both pages
|
|
314
|
+
- [ ] Success notification appears after submission
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## TC-17: getActiveSurveys() — Fetch and Display
|
|
319
|
+
|
|
320
|
+
**Objective:** Verify active surveys can be fetched and shown programmatically.
|
|
321
|
+
|
|
322
|
+
**Steps:**
|
|
323
|
+
1. Call `const surveys = await sdk.getActiveSurveys()`
|
|
324
|
+
2. Log result to console
|
|
325
|
+
3. Call `sdk.showSurveyById(surveys[0].surveyId)`
|
|
326
|
+
|
|
327
|
+
**Expected Results:**
|
|
328
|
+
- [ ] `getActiveSurveys()` returns an array of survey objects
|
|
329
|
+
- [ ] Each survey has `surveyId`, `surveyType`, `title` fields
|
|
330
|
+
- [ ] `showSurveyById()` successfully renders the matching survey
|
|
331
|
+
- [ ] Correct survey type is shown (NPS/CSAT/CES matches the fetched type)
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## TC-18: showSurveyById() — Survey Not Found
|
|
336
|
+
|
|
337
|
+
**Objective:** Verify error handling when survey ID doesn't exist.
|
|
338
|
+
|
|
339
|
+
**Steps:**
|
|
340
|
+
1. Call `await sdk.showSurveyById('nonexistent-id')`
|
|
341
|
+
|
|
342
|
+
**Expected Results:**
|
|
343
|
+
- [ ] Promise rejects with an error message containing "not found"
|
|
344
|
+
- [ ] No survey widget appears in the DOM
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## TC-19: Survey — onSubmit Callback
|
|
349
|
+
|
|
350
|
+
**Objective:** Verify `onSubmit` callback receives the correct response object.
|
|
351
|
+
|
|
352
|
+
**Steps:**
|
|
353
|
+
1. Call:
|
|
354
|
+
```js
|
|
355
|
+
sdk.showSurvey({
|
|
356
|
+
surveyType: 'nps',
|
|
357
|
+
ratingScale: 11,
|
|
358
|
+
showSubmitButton: true,
|
|
359
|
+
onSubmit: (response) => console.log('onSubmit:', response)
|
|
360
|
+
});
|
|
361
|
+
```
|
|
362
|
+
2. Select score 9 and click Submit
|
|
363
|
+
3. Check console output
|
|
364
|
+
|
|
365
|
+
**Expected Results:**
|
|
366
|
+
- [ ] `onSubmit` is called once
|
|
367
|
+
- [ ] `response.type` is `'nps'`
|
|
368
|
+
- [ ] `response.score` is `9`
|
|
369
|
+
- [ ] `response.timestamp` is a valid ISO date string
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
## TC-20: Survey — SDK Not Initialized Guard
|
|
374
|
+
|
|
375
|
+
**Objective:** Verify SDK throws when survey methods are called before `init()`.
|
|
376
|
+
|
|
377
|
+
**Steps:**
|
|
378
|
+
1. Create SDK instance but do NOT call `init()`
|
|
379
|
+
2. Call `sdk.showSurvey({ surveyType: 'nps' })`
|
|
380
|
+
|
|
381
|
+
**Expected Results:**
|
|
382
|
+
- [ ] Error is thrown: "SDK must be initialized before showing surveys. Call init() first."
|
|
383
|
+
- [ ] No network requests are made
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## Regression Checklist
|
|
388
|
+
|
|
389
|
+
After any change to survey code, verify:
|
|
390
|
+
|
|
391
|
+
- [ ] All 4 survey types render without console errors (NPS, CSAT, CES, Custom)
|
|
392
|
+
- [ ] Score selection works for each type
|
|
393
|
+
- [ ] Submit sends correct payload to the API
|
|
394
|
+
- [ ] Dismiss/close cleans up the DOM (no lingering elements)
|
|
395
|
+
- [ ] Multi-page navigation works forward and backward
|
|
396
|
+
- [ ] `survey:shown`, `survey:submitted`, `survey:dismissed`, `survey:suppressed` events all fire at the right time
|
|
397
|
+
- [ ] `autoSubmitOnSelect: true` does not show submit button
|
|
398
|
+
- [ ] `enabled: false` prevents the survey from rendering
|
package/dist/feedback-sdk.js
CHANGED
|
@@ -179,31 +179,172 @@
|
|
|
179
179
|
const MOCK_SURVEYS = [
|
|
180
180
|
{
|
|
181
181
|
id: 'mock_nps_survey',
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
description: 'Your feedback helps us improve',
|
|
185
|
-
low_label: 'Not likely',
|
|
186
|
-
high_label: 'Very likely',
|
|
187
|
-
trigger: 'manual',
|
|
182
|
+
name: 'NPS Score',
|
|
183
|
+
slug: 'nps',
|
|
188
184
|
status: 'active',
|
|
185
|
+
trigger_type: 'manual',
|
|
186
|
+
pages: [
|
|
187
|
+
{
|
|
188
|
+
id: 'nps-page-1',
|
|
189
|
+
type: 'rating',
|
|
190
|
+
title: 'How likely are you to recommend us to a friend?',
|
|
191
|
+
description: '',
|
|
192
|
+
placeholder: '',
|
|
193
|
+
rating_config: { scale: 11, low_label: 'Very unlikely', high_label: 'Very likely', survey_type: 'nps' },
|
|
194
|
+
multiple_choice_config: null,
|
|
195
|
+
link_config: null,
|
|
196
|
+
after_this_page: { default: 'end_survey', conditions: [] },
|
|
197
|
+
required: true,
|
|
198
|
+
},
|
|
199
|
+
],
|
|
189
200
|
},
|
|
190
201
|
{
|
|
191
202
|
id: 'mock_csat_survey',
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
description: 'Rate your experience with our product',
|
|
195
|
-
trigger: 'manual',
|
|
203
|
+
name: 'CSAT Survey',
|
|
204
|
+
slug: 'csat',
|
|
196
205
|
status: 'active',
|
|
206
|
+
trigger_type: 'manual',
|
|
207
|
+
pages: [
|
|
208
|
+
{
|
|
209
|
+
id: 'csat-page-1',
|
|
210
|
+
type: 'rating',
|
|
211
|
+
title: 'How satisfied are you with our service?',
|
|
212
|
+
description: '',
|
|
213
|
+
placeholder: '',
|
|
214
|
+
rating_config: { scale: 5, low_label: 'Very dissatisfied', high_label: 'Very satisfied', survey_type: 'emoji' },
|
|
215
|
+
multiple_choice_config: null,
|
|
216
|
+
link_config: null,
|
|
217
|
+
after_this_page: { default: 'end_survey', conditions: [] },
|
|
218
|
+
required: true,
|
|
219
|
+
},
|
|
220
|
+
],
|
|
197
221
|
},
|
|
198
222
|
{
|
|
199
223
|
id: 'mock_ces_survey',
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
224
|
+
name: 'Customer Effort Score',
|
|
225
|
+
slug: 'ces',
|
|
226
|
+
status: 'active',
|
|
227
|
+
trigger_type: 'manual',
|
|
228
|
+
pages: [
|
|
229
|
+
{
|
|
230
|
+
id: 'ces-page-1',
|
|
231
|
+
type: 'rating',
|
|
232
|
+
title: 'How easy was it to use our product?',
|
|
233
|
+
description: '',
|
|
234
|
+
placeholder: '',
|
|
235
|
+
rating_config: { scale: 5, low_label: 'Very difficult', high_label: 'Very easy', survey_type: 'ces' },
|
|
236
|
+
multiple_choice_config: null,
|
|
237
|
+
link_config: null,
|
|
238
|
+
after_this_page: { default: 'end_survey', conditions: [] },
|
|
239
|
+
required: true,
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
id: 'mock_open_question_survey',
|
|
245
|
+
name: 'Open Question',
|
|
246
|
+
slug: 'open-question',
|
|
247
|
+
status: 'active',
|
|
248
|
+
trigger_type: 'manual',
|
|
249
|
+
pages: [
|
|
250
|
+
{
|
|
251
|
+
id: 'open-page-1',
|
|
252
|
+
type: 'text',
|
|
253
|
+
title: 'What could we do better?',
|
|
254
|
+
description: 'Is there anything we could do to make our product better for you?',
|
|
255
|
+
placeholder: 'Type your answer here',
|
|
256
|
+
rating_config: null,
|
|
257
|
+
multiple_choice_config: null,
|
|
258
|
+
link_config: null,
|
|
259
|
+
after_this_page: { default: 'end_survey', conditions: [] },
|
|
260
|
+
required: true,
|
|
261
|
+
},
|
|
262
|
+
],
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
id: 'mock_product_idea_poll_survey',
|
|
266
|
+
name: 'Product Idea Poll',
|
|
267
|
+
slug: 'product-idea-poll',
|
|
206
268
|
status: 'active',
|
|
269
|
+
trigger_type: 'manual',
|
|
270
|
+
pages: [
|
|
271
|
+
{
|
|
272
|
+
id: 'poll-page-1',
|
|
273
|
+
type: 'multiple_choice',
|
|
274
|
+
title: 'Which feature should we add next?',
|
|
275
|
+
description: 'Vote on the feature you would like to see next.',
|
|
276
|
+
placeholder: '',
|
|
277
|
+
rating_config: null,
|
|
278
|
+
multiple_choice_config: {
|
|
279
|
+
allow_multiple_selection: false,
|
|
280
|
+
survey_type: 'regular',
|
|
281
|
+
options: [
|
|
282
|
+
{ id: 'opt1', label: 'Better reporting' },
|
|
283
|
+
{ id: 'opt2', label: 'Mobile app' },
|
|
284
|
+
{ id: 'opt3', label: 'Integrations' },
|
|
285
|
+
{ id: 'opt4', label: 'AI features' },
|
|
286
|
+
],
|
|
287
|
+
},
|
|
288
|
+
link_config: null,
|
|
289
|
+
after_this_page: { default: 'end_survey', conditions: [] },
|
|
290
|
+
required: true,
|
|
291
|
+
},
|
|
292
|
+
],
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
id: 'mock_pmf_survey',
|
|
296
|
+
name: 'Product Market Fit (PMF)',
|
|
297
|
+
slug: 'pmf',
|
|
298
|
+
status: 'active',
|
|
299
|
+
trigger_type: 'manual',
|
|
300
|
+
pages: [
|
|
301
|
+
{
|
|
302
|
+
id: 'pmf-page-1',
|
|
303
|
+
type: 'multiple_choice',
|
|
304
|
+
title: 'How would you feel if you could no longer use our product?',
|
|
305
|
+
description: '',
|
|
306
|
+
placeholder: '',
|
|
307
|
+
rating_config: null,
|
|
308
|
+
multiple_choice_config: {
|
|
309
|
+
allow_multiple_selection: false,
|
|
310
|
+
survey_type: 'regular',
|
|
311
|
+
options: [
|
|
312
|
+
{ id: 'very_disappointed', label: 'Very disappointed' },
|
|
313
|
+
{ id: 'somewhat_disappointed', label: 'Somewhat disappointed' },
|
|
314
|
+
{ id: 'not_disappointed', label: 'Not disappointed' },
|
|
315
|
+
],
|
|
316
|
+
},
|
|
317
|
+
link_config: null,
|
|
318
|
+
after_this_page: { default: 'end_survey', conditions: [] },
|
|
319
|
+
required: true,
|
|
320
|
+
},
|
|
321
|
+
],
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
id: 'mock_user_interview_survey',
|
|
325
|
+
name: 'User Interview Request',
|
|
326
|
+
slug: 'user-interview',
|
|
327
|
+
status: 'active',
|
|
328
|
+
trigger_type: 'manual',
|
|
329
|
+
pages: [
|
|
330
|
+
{
|
|
331
|
+
id: 'interview-page-1',
|
|
332
|
+
type: 'link',
|
|
333
|
+
title: 'Would you like to hop on a quick demo?',
|
|
334
|
+
description: 'We would love to hear your thoughts and answer any questions you have.',
|
|
335
|
+
placeholder: '',
|
|
336
|
+
rating_config: null,
|
|
337
|
+
multiple_choice_config: null,
|
|
338
|
+
link_config: {
|
|
339
|
+
button_text: 'Book a demo',
|
|
340
|
+
link_text: '',
|
|
341
|
+
redirect_url: 'https://example.com',
|
|
342
|
+
open_in: 'new_tab',
|
|
343
|
+
},
|
|
344
|
+
after_this_page: { default: 'end_survey', conditions: [] },
|
|
345
|
+
required: true,
|
|
346
|
+
},
|
|
347
|
+
],
|
|
207
348
|
},
|
|
208
349
|
];
|
|
209
350
|
|
|
@@ -7368,6 +7509,8 @@
|
|
|
7368
7509
|
return this._renderMultipleChoicePage(page);
|
|
7369
7510
|
case 'text':
|
|
7370
7511
|
return this._renderTextPage(page);
|
|
7512
|
+
case 'link':
|
|
7513
|
+
return this._renderLinkPage(page);
|
|
7371
7514
|
default:
|
|
7372
7515
|
return this._renderTextPage(page);
|
|
7373
7516
|
}
|
|
@@ -7426,6 +7569,21 @@
|
|
|
7426
7569
|
`;
|
|
7427
7570
|
}
|
|
7428
7571
|
|
|
7572
|
+
if (ratingType === 'ces') {
|
|
7573
|
+
const cesLabels = ['Very Difficult', 'Difficult', 'Neutral', 'Easy', 'Very Easy'];
|
|
7574
|
+
return `
|
|
7575
|
+
<div class="feedback-survey-ces" data-page-id="${pageId}">
|
|
7576
|
+
${cesLabels
|
|
7577
|
+
.map((label, i) => {
|
|
7578
|
+
const score = i + 1;
|
|
7579
|
+
const selected = currentRating === score ? ' selected' : '';
|
|
7580
|
+
return `<button class="feedback-survey-page-rating-btn feedback-survey-ces-btn${selected}" data-page-id="${pageId}" data-score="${score}">${label}</button>`;
|
|
7581
|
+
})
|
|
7582
|
+
.join('')}
|
|
7583
|
+
</div>
|
|
7584
|
+
`;
|
|
7585
|
+
}
|
|
7586
|
+
|
|
7429
7587
|
if (ratingType === 'emoji' && scale === 5) {
|
|
7430
7588
|
const emojis = [
|
|
7431
7589
|
'\uD83D\uDE1E',
|
|
@@ -7468,7 +7626,9 @@
|
|
|
7468
7626
|
page.multipleChoiceConfig || page.multiple_choice_config || {};
|
|
7469
7627
|
const options = Array.isArray(config.options) ? config.options : [];
|
|
7470
7628
|
const allowMultiple =
|
|
7471
|
-
config.allow_multiple === true ||
|
|
7629
|
+
config.allow_multiple === true ||
|
|
7630
|
+
config.multiple === true ||
|
|
7631
|
+
config.allow_multiple_selection === true;
|
|
7472
7632
|
const pageAnswer = this.surveyState.pageAnswers[pageId] || {};
|
|
7473
7633
|
const selectedValues = Array.isArray(pageAnswer.values)
|
|
7474
7634
|
? pageAnswer.values
|
|
@@ -7495,10 +7655,36 @@
|
|
|
7495
7655
|
const pageId = page.id || `page_${this.surveyState.currentPageIndex}`;
|
|
7496
7656
|
const pageAnswer = this.surveyState.pageAnswers[pageId] || {};
|
|
7497
7657
|
const value = pageAnswer.text || '';
|
|
7658
|
+
const placeholder = page.placeholder || 'Type your answer...';
|
|
7498
7659
|
|
|
7499
7660
|
return `
|
|
7500
7661
|
<div class="feedback-survey-text-page" data-page-id="${pageId}">
|
|
7501
|
-
<textarea class="feedback-survey-page-textarea" data-page-id="${pageId}" placeholder="
|
|
7662
|
+
<textarea class="feedback-survey-page-textarea" data-page-id="${pageId}" placeholder="${placeholder}">${value}</textarea>
|
|
7663
|
+
</div>
|
|
7664
|
+
`;
|
|
7665
|
+
}
|
|
7666
|
+
|
|
7667
|
+
_renderLinkPage(page) {
|
|
7668
|
+
const pageId = page.id || `page_${this.surveyState.currentPageIndex}`;
|
|
7669
|
+
const config = page.link_config || page.linkConfig || {};
|
|
7670
|
+
const buttonText = config.button_text || config.buttonText || 'Click here';
|
|
7671
|
+
const linkText = config.link_text || config.linkText || '';
|
|
7672
|
+
const redirectUrl = config.redirect_url || config.redirectUrl || '#';
|
|
7673
|
+
const openIn = config.open_in || config.openIn || 'new_tab';
|
|
7674
|
+
const target = openIn === 'new_tab' ? '_blank' : '_self';
|
|
7675
|
+
const pageAnswer = this.surveyState.pageAnswers[pageId] || {};
|
|
7676
|
+
const clicked = pageAnswer.clicked === true;
|
|
7677
|
+
|
|
7678
|
+
return `
|
|
7679
|
+
<div class="feedback-survey-link-page" data-page-id="${pageId}">
|
|
7680
|
+
${linkText ? `<p class="feedback-survey-link-text">${linkText}</p>` : ''}
|
|
7681
|
+
<a class="feedback-survey-link-btn${clicked ? ' selected' : ''}"
|
|
7682
|
+
href="${redirectUrl}"
|
|
7683
|
+
target="${target}"
|
|
7684
|
+
rel="noopener noreferrer"
|
|
7685
|
+
data-page-id="${pageId}">
|
|
7686
|
+
${buttonText}
|
|
7687
|
+
</a>
|
|
7502
7688
|
</div>
|
|
7503
7689
|
`;
|
|
7504
7690
|
}
|
|
@@ -7747,6 +7933,24 @@
|
|
|
7747
7933
|
});
|
|
7748
7934
|
}
|
|
7749
7935
|
}
|
|
7936
|
+
|
|
7937
|
+
if (page.type === 'link') {
|
|
7938
|
+
this.surveyElement
|
|
7939
|
+
.querySelectorAll('.feedback-survey-link-btn')
|
|
7940
|
+
.forEach((btn) => {
|
|
7941
|
+
btn.addEventListener('click', () => {
|
|
7942
|
+
const pId = btn.dataset.pageId;
|
|
7943
|
+
this._setPageAnswer(pId, { clicked: true });
|
|
7944
|
+
btn.classList.add('selected');
|
|
7945
|
+
|
|
7946
|
+
const navigation = page.afterThisPage || page.after_this_page;
|
|
7947
|
+
const goesTo = navigation ? navigation.default : null;
|
|
7948
|
+
if (!goesTo || goesTo === 'end_survey') {
|
|
7949
|
+
setTimeout(() => this._handleSubmit(), 400);
|
|
7950
|
+
}
|
|
7951
|
+
});
|
|
7952
|
+
});
|
|
7953
|
+
}
|
|
7750
7954
|
}
|
|
7751
7955
|
|
|
7752
7956
|
_selectNPS(score) {
|
|
@@ -7938,6 +8142,13 @@
|
|
|
7938
8142
|
}
|
|
7939
8143
|
}
|
|
7940
8144
|
|
|
8145
|
+
if (page.type === 'link') {
|
|
8146
|
+
if (page.required && !answer.clicked) {
|
|
8147
|
+
this._showError('Please click the link to continue');
|
|
8148
|
+
return false;
|
|
8149
|
+
}
|
|
8150
|
+
}
|
|
8151
|
+
|
|
7941
8152
|
return true;
|
|
7942
8153
|
}
|
|
7943
8154
|
|
|
@@ -8667,6 +8878,7 @@
|
|
|
8667
8878
|
type: page.type || 'rating',
|
|
8668
8879
|
title: page.title || '',
|
|
8669
8880
|
description: page.description || '',
|
|
8881
|
+
placeholder: page.placeholder || '',
|
|
8670
8882
|
required: page.required !== false,
|
|
8671
8883
|
position: page.position ?? index,
|
|
8672
8884
|
ratingConfig: page.ratingConfig || page.rating_config || null,
|