@product7/feedback-sdk 1.6.8 → 1.7.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@product7/feedback-sdk",
3
- "version": "1.6.8",
3
+ "version": "1.7.0",
4
4
  "description": "JavaScript SDK for integrating Product7 feedback widgets into any website",
5
5
  "main": "dist/feedback-sdk.js",
6
6
  "module": "src/index.js",
@@ -173,30 +173,171 @@ export const MOCK_HELP_COLLECTIONS = [
173
173
  export const MOCK_SURVEYS = [
174
174
  {
175
175
  id: 'mock_nps_survey',
176
- type: 'nps',
177
- title: 'How likely are you to recommend us?',
178
- description: 'Your feedback helps us improve',
179
- low_label: 'Not likely',
180
- high_label: 'Very likely',
181
- trigger: 'manual',
176
+ name: 'NPS Score',
177
+ slug: 'nps',
182
178
  status: 'active',
179
+ trigger_type: 'manual',
180
+ pages: [
181
+ {
182
+ id: 'nps-page-1',
183
+ type: 'rating',
184
+ title: 'How likely are you to recommend us to a friend?',
185
+ description: '',
186
+ placeholder: '',
187
+ rating_config: { scale: 11, low_label: 'Very unlikely', high_label: 'Very likely', survey_type: 'nps' },
188
+ multiple_choice_config: null,
189
+ link_config: null,
190
+ after_this_page: { default: 'end_survey', conditions: [] },
191
+ required: true,
192
+ },
193
+ ],
183
194
  },
184
195
  {
185
196
  id: 'mock_csat_survey',
186
- type: 'csat',
187
- title: 'How satisfied are you?',
188
- description: 'Rate your experience with our product',
189
- trigger: 'manual',
197
+ name: 'CSAT Survey',
198
+ slug: 'csat',
190
199
  status: 'active',
200
+ trigger_type: 'manual',
201
+ pages: [
202
+ {
203
+ id: 'csat-page-1',
204
+ type: 'rating',
205
+ title: 'How satisfied are you with our service?',
206
+ description: '',
207
+ placeholder: '',
208
+ rating_config: { scale: 5, low_label: 'Very dissatisfied', high_label: 'Very satisfied', survey_type: 'emoji' },
209
+ multiple_choice_config: null,
210
+ link_config: null,
211
+ after_this_page: { default: 'end_survey', conditions: [] },
212
+ required: true,
213
+ },
214
+ ],
191
215
  },
192
216
  {
193
217
  id: 'mock_ces_survey',
194
- type: 'ces',
195
- title: 'How easy was it?',
196
- description: 'Rate the ease of completing your task',
197
- low_label: 'Very difficult',
198
- high_label: 'Very easy',
199
- trigger: 'manual',
218
+ name: 'Customer Effort Score',
219
+ slug: 'ces',
220
+ status: 'active',
221
+ trigger_type: 'manual',
222
+ pages: [
223
+ {
224
+ id: 'ces-page-1',
225
+ type: 'rating',
226
+ title: 'How easy was it to use our product?',
227
+ description: '',
228
+ placeholder: '',
229
+ rating_config: { scale: 5, low_label: 'Very difficult', high_label: 'Very easy', survey_type: 'ces' },
230
+ multiple_choice_config: null,
231
+ link_config: null,
232
+ after_this_page: { default: 'end_survey', conditions: [] },
233
+ required: true,
234
+ },
235
+ ],
236
+ },
237
+ {
238
+ id: 'mock_open_question_survey',
239
+ name: 'Open Question',
240
+ slug: 'open-question',
241
+ status: 'active',
242
+ trigger_type: 'manual',
243
+ pages: [
244
+ {
245
+ id: 'open-page-1',
246
+ type: 'text',
247
+ title: 'What could we do better?',
248
+ description: 'Is there anything we could do to make our product better for you?',
249
+ placeholder: 'Type your answer here',
250
+ rating_config: null,
251
+ multiple_choice_config: null,
252
+ link_config: null,
253
+ after_this_page: { default: 'end_survey', conditions: [] },
254
+ required: true,
255
+ },
256
+ ],
257
+ },
258
+ {
259
+ id: 'mock_product_idea_poll_survey',
260
+ name: 'Product Idea Poll',
261
+ slug: 'product-idea-poll',
200
262
  status: 'active',
263
+ trigger_type: 'manual',
264
+ pages: [
265
+ {
266
+ id: 'poll-page-1',
267
+ type: 'multiple_choice',
268
+ title: 'Which feature should we add next?',
269
+ description: 'Vote on the feature you would like to see next.',
270
+ placeholder: '',
271
+ rating_config: null,
272
+ multiple_choice_config: {
273
+ allow_multiple_selection: false,
274
+ survey_type: 'regular',
275
+ options: [
276
+ { id: 'opt1', label: 'Better reporting' },
277
+ { id: 'opt2', label: 'Mobile app' },
278
+ { id: 'opt3', label: 'Integrations' },
279
+ { id: 'opt4', label: 'AI features' },
280
+ ],
281
+ },
282
+ link_config: null,
283
+ after_this_page: { default: 'end_survey', conditions: [] },
284
+ required: true,
285
+ },
286
+ ],
287
+ },
288
+ {
289
+ id: 'mock_pmf_survey',
290
+ name: 'Product Market Fit (PMF)',
291
+ slug: 'pmf',
292
+ status: 'active',
293
+ trigger_type: 'manual',
294
+ pages: [
295
+ {
296
+ id: 'pmf-page-1',
297
+ type: 'multiple_choice',
298
+ title: 'How would you feel if you could no longer use our product?',
299
+ description: '',
300
+ placeholder: '',
301
+ rating_config: null,
302
+ multiple_choice_config: {
303
+ allow_multiple_selection: false,
304
+ survey_type: 'regular',
305
+ options: [
306
+ { id: 'very_disappointed', label: 'Very disappointed' },
307
+ { id: 'somewhat_disappointed', label: 'Somewhat disappointed' },
308
+ { id: 'not_disappointed', label: 'Not disappointed' },
309
+ ],
310
+ },
311
+ link_config: null,
312
+ after_this_page: { default: 'end_survey', conditions: [] },
313
+ required: true,
314
+ },
315
+ ],
316
+ },
317
+ {
318
+ id: 'mock_user_interview_survey',
319
+ name: 'User Interview Request',
320
+ slug: 'user-interview',
321
+ status: 'active',
322
+ trigger_type: 'manual',
323
+ pages: [
324
+ {
325
+ id: 'interview-page-1',
326
+ type: 'link',
327
+ title: 'Would you like to hop on a quick demo?',
328
+ description: 'We would love to hear your thoughts and answer any questions you have.',
329
+ placeholder: '',
330
+ rating_config: null,
331
+ multiple_choice_config: null,
332
+ link_config: {
333
+ button_text: 'Book a demo',
334
+ link_text: '',
335
+ redirect_url: 'https://example.com',
336
+ open_in: 'new_tab',
337
+ },
338
+ after_this_page: { default: 'end_survey', conditions: [] },
339
+ required: true,
340
+ },
341
+ ],
201
342
  },
202
343
  ];
@@ -512,6 +512,7 @@ export class FeedbackSDK {
512
512
  triggerText: "What's New",
513
513
  showBadge: true,
514
514
  viewButtonText: 'View Update',
515
+ showBackdrop: true,
515
516
  };
516
517
 
517
518
  const configDefaults = this._getWidgetTypeConfig('changelog');
@@ -688,6 +688,7 @@ messenger.mount();
688
688
  const changelog = sdk.createWidget('changelog', {
689
689
  position: 'bottom-left',
690
690
  theme: 'light',
691
+ showBackdrop: true,
691
692
  triggerText: "What's New",
692
693
  showBadge: true,
693
694
  title: "What's New",
@@ -7,6 +7,7 @@ export class ChangelogWidget extends BaseWidget {
7
7
  this.isLoading = false;
8
8
  this.modalElement = null;
9
9
  this.sidebarElement = null;
10
+ this.listModalBackdropElement = null;
10
11
  this.currentIndex = 0;
11
12
  }
12
13
 
@@ -87,10 +88,12 @@ export class ChangelogWidget extends BaseWidget {
87
88
  }
88
89
 
89
90
  _createModal() {
90
- this.backdropElement = document.createElement('div');
91
- this.backdropElement.className = `changelog-modal-backdrop changelog-theme-${this.options.theme || 'light'}`;
92
- document.body.appendChild(this.backdropElement);
93
- this.backdropElement.addEventListener('click', () => this.closeModal());
91
+ if (this.options.showBackdrop !== false) {
92
+ this.backdropElement = document.createElement('div');
93
+ this.backdropElement.className = `changelog-modal-backdrop changelog-theme-${this.options.theme || 'light'}`;
94
+ document.body.appendChild(this.backdropElement);
95
+ this.backdropElement.addEventListener('click', () => this.closeModal());
96
+ }
94
97
 
95
98
  this.modalElement = document.createElement('div');
96
99
  this.modalElement.className = `changelog-modal changelog-theme-${this.options.theme || 'light'}`;
@@ -230,12 +233,14 @@ export class ChangelogWidget extends BaseWidget {
230
233
  }
231
234
 
232
235
  _createListModal() {
233
- this.listModalBackdropElement = document.createElement('div');
234
- this.listModalBackdropElement.className = `changelog-list-modal-backdrop changelog-theme-${this.options.theme || 'light'}`;
235
- document.body.appendChild(this.listModalBackdropElement);
236
- this.listModalBackdropElement.addEventListener('click', () =>
237
- this.closeSidebar()
238
- );
236
+ if (this.options.showBackdrop !== false) {
237
+ this.listModalBackdropElement = document.createElement('div');
238
+ this.listModalBackdropElement.className = `changelog-list-modal-backdrop changelog-theme-${this.options.theme || 'light'}`;
239
+ document.body.appendChild(this.listModalBackdropElement);
240
+ this.listModalBackdropElement.addEventListener('click', () =>
241
+ this.closeSidebar()
242
+ );
243
+ }
239
244
 
240
245
  this.listModalElement = document.createElement('div');
241
246
  this.listModalElement.className = `changelog-list-modal changelog-theme-${this.options.theme || 'light'}`;
@@ -406,6 +406,8 @@ export class SurveyWidget extends BaseWidget {
406
406
  return this._renderMultipleChoicePage(page);
407
407
  case 'text':
408
408
  return this._renderTextPage(page);
409
+ case 'link':
410
+ return this._renderLinkPage(page);
409
411
  default:
410
412
  return this._renderTextPage(page);
411
413
  }
@@ -464,6 +466,21 @@ export class SurveyWidget extends BaseWidget {
464
466
  `;
465
467
  }
466
468
 
469
+ if (ratingType === 'ces') {
470
+ const cesLabels = ['Very Difficult', 'Difficult', 'Neutral', 'Easy', 'Very Easy'];
471
+ return `
472
+ <div class="feedback-survey-ces" data-page-id="${pageId}">
473
+ ${cesLabels
474
+ .map((label, i) => {
475
+ const score = i + 1;
476
+ const selected = currentRating === score ? ' selected' : '';
477
+ return `<button class="feedback-survey-page-rating-btn feedback-survey-ces-btn${selected}" data-page-id="${pageId}" data-score="${score}">${label}</button>`;
478
+ })
479
+ .join('')}
480
+ </div>
481
+ `;
482
+ }
483
+
467
484
  if (ratingType === 'emoji' && scale === 5) {
468
485
  const emojis = [
469
486
  '\uD83D\uDE1E',
@@ -506,7 +523,9 @@ export class SurveyWidget extends BaseWidget {
506
523
  page.multipleChoiceConfig || page.multiple_choice_config || {};
507
524
  const options = Array.isArray(config.options) ? config.options : [];
508
525
  const allowMultiple =
509
- config.allow_multiple === true || config.multiple === true;
526
+ config.allow_multiple === true ||
527
+ config.multiple === true ||
528
+ config.allow_multiple_selection === true;
510
529
  const pageAnswer = this.surveyState.pageAnswers[pageId] || {};
511
530
  const selectedValues = Array.isArray(pageAnswer.values)
512
531
  ? pageAnswer.values
@@ -533,10 +552,36 @@ export class SurveyWidget extends BaseWidget {
533
552
  const pageId = page.id || `page_${this.surveyState.currentPageIndex}`;
534
553
  const pageAnswer = this.surveyState.pageAnswers[pageId] || {};
535
554
  const value = pageAnswer.text || '';
555
+ const placeholder = page.placeholder || 'Type your answer...';
536
556
 
537
557
  return `
538
558
  <div class="feedback-survey-text-page" data-page-id="${pageId}">
539
- <textarea class="feedback-survey-page-textarea" data-page-id="${pageId}" placeholder="Type your answer...">${value}</textarea>
559
+ <textarea class="feedback-survey-page-textarea" data-page-id="${pageId}" placeholder="${placeholder}">${value}</textarea>
560
+ </div>
561
+ `;
562
+ }
563
+
564
+ _renderLinkPage(page) {
565
+ const pageId = page.id || `page_${this.surveyState.currentPageIndex}`;
566
+ const config = page.link_config || page.linkConfig || {};
567
+ const buttonText = config.button_text || config.buttonText || 'Click here';
568
+ const linkText = config.link_text || config.linkText || '';
569
+ const redirectUrl = config.redirect_url || config.redirectUrl || '#';
570
+ const openIn = config.open_in || config.openIn || 'new_tab';
571
+ const target = openIn === 'new_tab' ? '_blank' : '_self';
572
+ const pageAnswer = this.surveyState.pageAnswers[pageId] || {};
573
+ const clicked = pageAnswer.clicked === true;
574
+
575
+ return `
576
+ <div class="feedback-survey-link-page" data-page-id="${pageId}">
577
+ ${linkText ? `<p class="feedback-survey-link-text">${linkText}</p>` : ''}
578
+ <a class="feedback-survey-link-btn${clicked ? ' selected' : ''}"
579
+ href="${redirectUrl}"
580
+ target="${target}"
581
+ rel="noopener noreferrer"
582
+ data-page-id="${pageId}">
583
+ ${buttonText}
584
+ </a>
540
585
  </div>
541
586
  `;
542
587
  }
@@ -785,6 +830,24 @@ export class SurveyWidget extends BaseWidget {
785
830
  });
786
831
  }
787
832
  }
833
+
834
+ if (page.type === 'link') {
835
+ this.surveyElement
836
+ .querySelectorAll('.feedback-survey-link-btn')
837
+ .forEach((btn) => {
838
+ btn.addEventListener('click', () => {
839
+ const pId = btn.dataset.pageId;
840
+ this._setPageAnswer(pId, { clicked: true });
841
+ btn.classList.add('selected');
842
+
843
+ const navigation = page.afterThisPage || page.after_this_page;
844
+ const goesTo = navigation ? navigation.default : null;
845
+ if (!goesTo || goesTo === 'end_survey') {
846
+ setTimeout(() => this._handleSubmit(), 400);
847
+ }
848
+ });
849
+ });
850
+ }
788
851
  }
789
852
 
790
853
  _selectNPS(score) {
@@ -976,6 +1039,13 @@ export class SurveyWidget extends BaseWidget {
976
1039
  }
977
1040
  }
978
1041
 
1042
+ if (page.type === 'link') {
1043
+ if (page.required && !answer.clicked) {
1044
+ this._showError('Please click the link to continue');
1045
+ return false;
1046
+ }
1047
+ }
1048
+
979
1049
  return true;
980
1050
  }
981
1051
 
package/types/index.d.ts CHANGED
@@ -233,6 +233,7 @@ declare module '@product7/feedback-sdk' {
233
233
  enabled?: boolean;
234
234
  position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
235
235
  theme?: 'light' | 'dark';
236
+ showBackdrop?: boolean;
236
237
  triggerText?: string;
237
238
  showBadge?: boolean;
238
239
  title?: string;