freemium-survey-components 0.1.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.
Files changed (57) hide show
  1. package/.eslintrc +24 -0
  2. package/.prettierrc +10 -0
  3. package/.storybook/main.js +59 -0
  4. package/.storybook/manager.js +9 -0
  5. package/.storybook/preview.js +1 -0
  6. package/README.md +3 -0
  7. package/index.ts +3 -0
  8. package/lib/index.cjs.js +1 -0
  9. package/lib/index.esm.js +1 -0
  10. package/lib/types/index.d.ts +3 -0
  11. package/lib/types/src/components/button/index.d.ts +25 -0
  12. package/lib/types/src/components/checkbox/index.d.ts +14 -0
  13. package/lib/types/src/components/index.d.ts +6 -0
  14. package/lib/types/src/components/nps/index.d.ts +23 -0
  15. package/lib/types/src/components/progressbar/index.d.ts +8 -0
  16. package/lib/types/src/components/radio-button/index.d.ts +25 -0
  17. package/lib/types/src/components/text-input/index.d.ts +52 -0
  18. package/lib/types/src/index.d.ts +1 -0
  19. package/lib/types/src/mock.d.ts +215 -0
  20. package/lib/types/src/survey/index.d.ts +4 -0
  21. package/lib/types/src/survey/question.d.ts +3 -0
  22. package/lib/types/src/survey/widget.d.ts +4 -0
  23. package/lib/types/src/utils.d.ts +3 -0
  24. package/package.json +97 -0
  25. package/postcss.config.js +4 -0
  26. package/rollup.config.ts +32 -0
  27. package/src/components/button/button.stories.tsx +23 -0
  28. package/src/components/button/index.tsx +67 -0
  29. package/src/components/button/style.css +41 -0
  30. package/src/components/checkbox/checkbox.stories.tsx +34 -0
  31. package/src/components/checkbox/index.tsx +118 -0
  32. package/src/components/checkbox/style.css +85 -0
  33. package/src/components/index.tsx +6 -0
  34. package/src/components/nps/index.tsx +69 -0
  35. package/src/components/nps/nps.stories.tsx +34 -0
  36. package/src/components/nps/style.css +154 -0
  37. package/src/components/progressbar/index.tsx +21 -0
  38. package/src/components/progressbar/progressbar.stories.tsx +22 -0
  39. package/src/components/progressbar/style.css +14 -0
  40. package/src/components/radio-button/index.tsx +66 -0
  41. package/src/components/radio-button/radio.stories.tsx +26 -0
  42. package/src/components/radio-button/style.css +60 -0
  43. package/src/components/text-input/index.tsx +155 -0
  44. package/src/components/text-input/style.css +102 -0
  45. package/src/components/text-input/text-input.stories.tsx +84 -0
  46. package/src/index.tsx +1 -0
  47. package/src/mock.ts +363 -0
  48. package/src/survey/index.tsx +269 -0
  49. package/src/survey/question.tsx +79 -0
  50. package/src/survey/style.css +58 -0
  51. package/src/survey/survey.stories.tsx +28 -0
  52. package/src/survey/widget.css +46 -0
  53. package/src/survey/widget.tsx +17 -0
  54. package/src/theme/index.css +72 -0
  55. package/src/typings/index.d.ts +1 -0
  56. package/src/utils.tsx +12 -0
  57. package/tsconfig.json +22 -0
package/src/mock.ts ADDED
@@ -0,0 +1,363 @@
1
+ export default {
2
+ name: 'new survey',
3
+ desc: 'description here',
4
+ type: 'NPS',
5
+ header_message:
6
+ "Thank you for your time. This shouldn't take more than 2 mins for you to complete the survey!",
7
+ gratitude_message: 'Thank you for the feedback',
8
+ ui_branding: {
9
+ logo_url:
10
+ 'https://upload.wikimedia.org/wikipedia/commons/thumb/b/b9/Slack_Technologies_Logo.svg/1280px-Slack_Technologies_Logo.svg.png',
11
+ footer_html: 'We look forward to acting on your feedback!',
12
+ },
13
+ meta: {
14
+ blocks: [
15
+ {
16
+ name: 'NPS',
17
+ title: 'NPS',
18
+ order: 1,
19
+ question_names: ['Q_1'],
20
+ type: 'RANGE',
21
+ required: true,
22
+ is_based_on_score: false,
23
+ is_same_for_all: true,
24
+ jump_logic: null,
25
+ },
26
+ {
27
+ name: 'B_1',
28
+ order: 2,
29
+ type: 'CHECKBOX',
30
+ title: 'Follow up 1',
31
+ question_names: ['Q_2', 'Q_21', 'Q_22'],
32
+ required: false,
33
+ is_based_on_score: true,
34
+ is_same_for_all: false,
35
+ jump_logic: null,
36
+ branchOption: 'specific',
37
+ nextBlock: 'B_3',
38
+ },
39
+ {
40
+ name: 'B_2',
41
+ order: 3,
42
+ type: 'CHECKBOX',
43
+ title: 'Follow up 2',
44
+ question_names: ['Q_5', 'Q_51', 'Q_52'],
45
+ position: 5,
46
+ required: true,
47
+ is_based_on_score: true,
48
+ is_same_for_all: false,
49
+ jump_logic: null,
50
+ },
51
+ {
52
+ name: 'B_3',
53
+ order: 4,
54
+ type: 'input',
55
+ title: 'Follow up 3',
56
+ question_names: ['Q_8'],
57
+ position: 9,
58
+ required: true,
59
+ is_based_on_score: false,
60
+ is_same_for_all: true,
61
+ jump_logic: null,
62
+ },
63
+ ],
64
+ },
65
+ question_details: {
66
+ questions: [
67
+ {
68
+ text: 'How likely are you to recommend bookmyshow to your friend or colleague?',
69
+ name: 'Q_1',
70
+ position: 0,
71
+ type_info: {
72
+ question_type: 'RANGE',
73
+ validation: {
74
+ min: 1,
75
+ max: 10,
76
+ },
77
+ choices: [
78
+ {
79
+ value: 0,
80
+ position: 0,
81
+ dependent_question_names: ['Q_2', 'Q_52'],
82
+ },
83
+ {
84
+ value: 1,
85
+ position: 1,
86
+ dependent_question_names: ['Q_2', 'Q_52'],
87
+ },
88
+ {
89
+ value: 2,
90
+ position: 2,
91
+ dependent_question_names: ['Q_2', 'Q_52'],
92
+ },
93
+ {
94
+ value: 3,
95
+ position: 3,
96
+ dependent_question_names: ['Q_21', 'Q_52'],
97
+ },
98
+ {
99
+ value: 4,
100
+ position: 4,
101
+ dependent_question_names: ['Q_21', 'Q_52'],
102
+ },
103
+ {
104
+ value: 5,
105
+ position: 5,
106
+ dependent_question_names: ['Q_21', 'Q_52'],
107
+ },
108
+ {
109
+ value: 6,
110
+ position: 6,
111
+ dependent_question_names: ['Q_21', 'Q_52'],
112
+ },
113
+ {
114
+ value: 7,
115
+ position: 7,
116
+ dependent_question_names: ['Q_22', 'Q_52'],
117
+ },
118
+ {
119
+ value: 8,
120
+ position: 8,
121
+ dependent_question_names: ['Q_22', 'Q_52'],
122
+ },
123
+ {
124
+ value: 9,
125
+ position: 9,
126
+ dependent_question_names: ['Q_22', 'Q_52'],
127
+ },
128
+ ],
129
+ linear_scale: {
130
+ button_style: 'standard',
131
+ button_shape: 'curved',
132
+ },
133
+ score_presets: {
134
+ start: 'Not Likely',
135
+ end: 'Very Likely',
136
+ },
137
+ footer_text: 'We look forward to acting on your feedback!',
138
+ },
139
+ },
140
+ {
141
+ text: 'How well do you recomment our product to others ONE',
142
+ name: 'Q_2',
143
+ position: 1,
144
+ type_info: {
145
+ question_type: 'MESSAGE',
146
+ choices: [
147
+ {
148
+ id: 'opt-1',
149
+ label: 'Option 1',
150
+ },
151
+ {
152
+ id: 'opt-2',
153
+ label: 'Option 2',
154
+ },
155
+ {
156
+ id: 'opt-3',
157
+ label: 'Option 3',
158
+ },
159
+ ],
160
+ meta: {
161
+ value: 'Thank you for the feedback',
162
+ },
163
+ },
164
+ question_type: 'CHECKBOX',
165
+ },
166
+ {
167
+ text: 'How well do you recomment our product to others TWO',
168
+ name: 'Q_21',
169
+ position: 3,
170
+ type_info: {
171
+ question_type: 'PARAGRAPH',
172
+ choices: [
173
+ {
174
+ id: 'opt-1',
175
+ label: 'Option 1',
176
+ },
177
+ {
178
+ id: 'opt-2',
179
+ label: 'Option 2',
180
+ },
181
+ {
182
+ id: 'opt-3',
183
+ label: 'Option 3',
184
+ },
185
+ ],
186
+ meta: {
187
+ value: 'Thank you for the feedback',
188
+ },
189
+ },
190
+ question_type: 'CHECKBOX',
191
+ },
192
+ {
193
+ text: 'How well do you recomment our product to others THREE',
194
+ name: 'Q_22',
195
+ position: 4,
196
+ type_info: {
197
+ question_type: 'CHECKBOX',
198
+ choices: [
199
+ {
200
+ id: 'opt-1',
201
+ label: 'Option 1',
202
+ },
203
+ {
204
+ id: 'opt-2',
205
+ label: 'Option 2',
206
+ },
207
+ {
208
+ id: 'opt-3',
209
+ label: 'Option 3',
210
+ },
211
+ ],
212
+ meta: {
213
+ value: 'Thank you for the feedback',
214
+ },
215
+ },
216
+ question_type: 'CHECKBOX',
217
+ choices: [
218
+ {
219
+ id: 'opt-1',
220
+ label: 'Option 1',
221
+ },
222
+ {
223
+ id: 'opt-2',
224
+ label: 'Option 2',
225
+ },
226
+ {
227
+ id: 'opt-3',
228
+ label: 'Option 3',
229
+ },
230
+ ],
231
+ },
232
+ {
233
+ name: 'Q_5',
234
+ position: 5,
235
+ text: 'Which feature you like the most?',
236
+ type_info: {
237
+ question_type: 'CHECKBOX',
238
+ choices: [
239
+ {
240
+ id: 'opt-1',
241
+ label: 'Option 1',
242
+ },
243
+ {
244
+ id: 'opt-2',
245
+ label: 'Option 2',
246
+ },
247
+ {
248
+ id: 'opt-3',
249
+ label: 'Option 3',
250
+ },
251
+ ],
252
+ meta: {},
253
+ },
254
+ question_type: 'CHECKBOX',
255
+ },
256
+ {
257
+ name: 'Q_51',
258
+ position: 7,
259
+ text: 'Which feature you like the most Q_51?',
260
+ type_info: {
261
+ question_type: 'CHECKBOX',
262
+ choices: [
263
+ {
264
+ id: 'opt-1',
265
+ label: 'Option 1',
266
+ },
267
+ {
268
+ id: 'opt-2',
269
+ label: 'Option 2',
270
+ },
271
+ {
272
+ id: 'opt-3',
273
+ label: 'Option 3',
274
+ },
275
+ ],
276
+ meta: {},
277
+ },
278
+ question_type: 'CHECKBOX',
279
+ choices: [
280
+ {
281
+ id: 'opt-1',
282
+ label: 'Option 1',
283
+ },
284
+ {
285
+ id: 'opt-2',
286
+ label: 'Option 2',
287
+ },
288
+ ],
289
+ },
290
+ {
291
+ name: 'Q_52',
292
+ position: 8,
293
+ text: 'Which feature you like the most?',
294
+ type_info: {
295
+ question_type: 'CHECKBOX',
296
+ choices: [
297
+ {
298
+ id: 'opt-1',
299
+ label: 'Option 1',
300
+ },
301
+ {
302
+ id: 'opt-2',
303
+ label: 'Option 2',
304
+ },
305
+ {
306
+ id: 'opt-3',
307
+ label: 'Option 3',
308
+ },
309
+ ],
310
+ meta: {},
311
+ },
312
+ question_type: 'CHECKBOX',
313
+ },
314
+ {
315
+ name: 'Q_8',
316
+ position: 8,
317
+ text: 'Which feature you like the most? Q_8 last block',
318
+ type_info: {
319
+ question_type: 'CHECKBOX',
320
+ choices: [
321
+ {
322
+ id: 'opt-1',
323
+ label: 'Option 1',
324
+ },
325
+ {
326
+ id: 'opt-2',
327
+ label: 'Option 2',
328
+ },
329
+ {
330
+ id: 'opt-3',
331
+ label: 'Option 3',
332
+ },
333
+ ],
334
+ meta: {},
335
+ },
336
+ },
337
+ ],
338
+ },
339
+ state: 'DRAFT',
340
+ created_on: '2021-11-01T13:27:11.600Z',
341
+ updated_on: '2021-11-01T13:27:11.600Z',
342
+ schedule_info: {
343
+ start_date: '2021-11-31',
344
+ end_date: '2021-12-05',
345
+ preferred_time: {
346
+ hour: 15,
347
+ minute: 15,
348
+ },
349
+ remind_in: [30],
350
+ next_survey_when_responded: 30,
351
+ next_survey_when_not_responded: 30,
352
+ },
353
+ throttle_info: {
354
+ limit_per_day: 100,
355
+ },
356
+ ui_theme: {
357
+ body_background_color: '#000',
358
+ primary_font: '#FFF',
359
+ secondary_font: '#423233',
360
+ primary_button_color: '#123249',
361
+ secondary_button_color: '#981237',
362
+ },
363
+ }
@@ -0,0 +1,269 @@
1
+ import React, {useState, useRef, useEffect, useMemo} from 'react'
2
+ import {ProgressBar, Button} from '../components'
3
+ import Question from './question'
4
+ import './style.css'
5
+
6
+ // these fields do not need next button to proceed
7
+ const AUTO_COMMIT_FIELD_TYPES = ['RANGE', 'NPS', 'RADIO']
8
+ const SurveyEndCard = () => {
9
+ return (
10
+ <div className="question-container" style={{gap: '16px'}}>
11
+ <h4>Thank you for taking our survey!.</h4>
12
+ <p> Your response is very important to us.</p>
13
+ </div>
14
+ )
15
+ }
16
+ const Survey = ({survey, onSubmit}: any) => {
17
+ const {
18
+ meta: {blocks},
19
+ question_details: {questions},
20
+ } = survey
21
+ const [formValues, setFormValues] = useState<any>({})
22
+ const [isSurveyCompleted, setIsSurveyCompleted] = useState<boolean>(false)
23
+ const [blocksWithQns, setBlocksWithQns] = useState([] as any)
24
+ const currentBlock = useRef<any>(null)
25
+ const currentQuestion = useRef<any>(null)
26
+ const blockElementRef = useRef<HTMLDivElement | null>(null) // to focus on the newly added block
27
+ const currentBlockIndex = useRef(0) // to keep track of the index of the dirty block
28
+ const isValuesCommitted = useRef(false) // in case of non-auto commit fields, to check if the user commits the values or just modifying it
29
+ const tempBlocksWithQns = useRef([] as any)
30
+
31
+ useEffect(() => {
32
+ if (!blocks) {
33
+ return
34
+ }
35
+ if (!currentBlock.current && blocks.length > 0) {
36
+ handleBlockProgression(blocks[0])
37
+ return
38
+ }
39
+ }, [blocks])
40
+
41
+ useEffect(() => {
42
+ blockElementRef.current?.scrollIntoView({
43
+ block: 'center',
44
+ behavior: 'smooth',
45
+ })
46
+ tempBlocksWithQns.current = blocksWithQns
47
+ }, [blocksWithQns])
48
+
49
+ useEffect(() => {
50
+ //if not the initial render
51
+ if (Object.keys(formValues).length > 0) {
52
+ // move to next block if current qn is a auto commit qn
53
+ // if current qn is committed
54
+ // and if the qn is not the final qn since for the final qn, submit should end the survey
55
+ if (
56
+ currentQuestion.current &&
57
+ (AUTO_COMMIT_FIELD_TYPES.includes(
58
+ currentQuestion.current.type_info.question_type,
59
+ ) ||
60
+ isValuesCommitted.current) &&
61
+ !isCurrentBlockFinal()
62
+ ) {
63
+ moveToNextBlock()
64
+ }
65
+ }
66
+ }, [formValues])
67
+
68
+ const isCurrentBlockFinal = () => {
69
+ return (
70
+ currentBlockIndex.current === blocks.length - 1 ||
71
+ currentBlock.current.branchOption === 'end'
72
+ )
73
+ }
74
+ //if the already filled block is modified flash the state associated with all the blocks below the block being edited
75
+ const resetFormBelowBlock = (block: any) => {
76
+ const blockIndex = blocksWithQns.findIndex(
77
+ (eachBlock) => eachBlock.name === block.name,
78
+ )
79
+ const modifiedBlocksWithQns = blocksWithQns.slice(0, blockIndex + 1)
80
+ const modifiedFormValues = modifiedBlocksWithQns.reduce(
81
+ (result, current) => {
82
+ result[current.question.name] = formValues[current.question.name]
83
+ return result
84
+ },
85
+ {},
86
+ )
87
+ currentBlockIndex.current = blocks.findIndex(
88
+ (eachBlock) => eachBlock.name === block.name,
89
+ )
90
+ return [modifiedBlocksWithQns, modifiedFormValues]
91
+ }
92
+
93
+ const handleBlockProgression = (block) => {
94
+ let question: any = {}
95
+ if (block.is_based_on_score) {
96
+ const possibleNextQuestions =
97
+ questions[0].type_info.choices[formValues?.[questions[0].name]]
98
+ ?.dependent_question_names
99
+ const nextQuestionName = block.question_names.find((eachQuestion: any) =>
100
+ possibleNextQuestions?.includes(eachQuestion),
101
+ )
102
+ question = questions.find(
103
+ (eachQuestion: any) => eachQuestion.name === nextQuestionName,
104
+ )
105
+ } else {
106
+ question = questions.find(
107
+ (eachQuestion: any) => eachQuestion.name === block.question_names[0],
108
+ )
109
+ }
110
+ currentBlock.current = block
111
+ currentQuestion.current = question
112
+ const isPresent = tempBlocksWithQns.current.find(
113
+ (eachBlock) => eachBlock.question.name === question.name,
114
+ )
115
+ if (!isPresent)
116
+ setBlocksWithQns([
117
+ ...tempBlocksWithQns.current,
118
+ {...block, question: question},
119
+ ])
120
+ }
121
+
122
+ const handleFormValues = (block, value, isCommitting) => {
123
+ if (isCommitting) {
124
+ if (
125
+ formValues[block.question.name] != null &&
126
+ block.name !== blocksWithQns[blocksWithQns.length - 1].name
127
+ ) {
128
+ const [modifiedBlocksWithQns, modifiedFormValues]: any =
129
+ resetFormBelowBlock(block)
130
+ setFormValues({
131
+ ...modifiedFormValues,
132
+ ...(value != null && {[block.question.name]: value}),
133
+ })
134
+ //setting the state of the blocksWithQns will trigger the useEffect unnecessarily which will scroll the view based on the temp block state
135
+ tempBlocksWithQns.current = modifiedBlocksWithQns
136
+ } else {
137
+ setFormValues({
138
+ ...formValues,
139
+ ...(value != null && {[block.question.name]: value}),
140
+ })
141
+ }
142
+ } else {
143
+ setFormValues({
144
+ ...formValues,
145
+ ...(value != null && {[block.question.name]: value}),
146
+ })
147
+ }
148
+ }
149
+
150
+ const moveToNextBlock = () => {
151
+ if (isCurrentBlockFinal()) {
152
+ setIsSurveyCompleted(true)
153
+ } else {
154
+ if (currentBlock.current.nextBlock) {
155
+ currentBlockIndex.current = blocks.findIndex(
156
+ (block) => block.name === currentBlock.current.nextBlock,
157
+ )
158
+ } else {
159
+ currentBlockIndex.current += 1
160
+ }
161
+ handleBlockProgression(blocks[currentBlockIndex.current])
162
+ }
163
+ }
164
+ const skipBlock = (block) => {
165
+ const blockIndex = blocksWithQns.findIndex(
166
+ (eachBlock) => eachBlock.name === block.name,
167
+ )
168
+ const modifiedBlocksWithQns = [...blocksWithQns]
169
+ modifiedBlocksWithQns.splice(blockIndex, 1)
170
+ //setting the state of the blocksWithQns will trigger the useEffect unnecessarily which will scroll the view based on the temporary block state
171
+ tempBlocksWithQns.current = modifiedBlocksWithQns
172
+ moveToNextBlock()
173
+ }
174
+ // do display next button, if the question is the current question being edited
175
+ // if the question is of type range, nps or radio
176
+ // if the block is not the final block by index and by End survey logic
177
+ const doDisplayNextButton = (block, question) => {
178
+ if (
179
+ question.name === currentQuestion.current.name &&
180
+ !AUTO_COMMIT_FIELD_TYPES.includes(question.type_info.question_type) &&
181
+ !isCurrentBlockFinal()
182
+ ) {
183
+ return true
184
+ }
185
+ return false
186
+ }
187
+ const doDisplaySkipButton = (block, question) => {
188
+ if (!block.required && question.name === currentQuestion.current.name) {
189
+ return true
190
+ }
191
+ return false
192
+ }
193
+
194
+ if (!currentBlock.current || !currentQuestion.current) {
195
+ return null
196
+ }
197
+ if (isSurveyCompleted) {
198
+ return <SurveyEndCard />
199
+ }
200
+ return (
201
+ <>
202
+ <ProgressBar
203
+ totalSteps={blocks.length}
204
+ completedSteps={currentBlockIndex.current}
205
+ />
206
+ {blocksWithQns.map((block: any, index: number) => (
207
+ <div
208
+ className="question-container"
209
+ ref={blockElementRef}
210
+ id={block.title}
211
+ key={block.title}
212
+ >
213
+ <div className="question-text">{block.question.text}</div>
214
+ <Question
215
+ question={block.question}
216
+ formValues={formValues}
217
+ onChangeHandler={(value: any) => {
218
+ isValuesCommitted.current = false
219
+ currentBlockIndex.current = blocks.findIndex(
220
+ (eachBlock) => eachBlock.name === block.name,
221
+ )
222
+ currentBlock.current = block
223
+ currentQuestion.current = block.question
224
+ handleFormValues(
225
+ block,
226
+ value,
227
+ AUTO_COMMIT_FIELD_TYPES.includes(
228
+ block.question.type_info.question_type,
229
+ ) && !isCurrentBlockFinal(),
230
+ )
231
+ }}
232
+ />
233
+ <div className="action-buttons">
234
+ {doDisplayNextButton(block, block.question) && (
235
+ <Button
236
+ onClick={() => {
237
+ isValuesCommitted.current = true
238
+ handleFormValues(block, null, true)
239
+ }}
240
+ className="next-button"
241
+ >
242
+ Next
243
+ </Button>
244
+ )}
245
+ {doDisplaySkipButton(block, block.question) && (
246
+ <button className="skip-button" onClick={skipBlock}>
247
+ Skip
248
+ </button>
249
+ )}
250
+ </div>
251
+ </div>
252
+ ))}
253
+ {isCurrentBlockFinal() && (
254
+ <div className="submit">
255
+ <Button
256
+ onClick={() => {
257
+ setIsSurveyCompleted(true)
258
+ onSubmit(formValues)
259
+ }}
260
+ >
261
+ Submit Survey
262
+ </Button>
263
+ </div>
264
+ )}
265
+ </>
266
+ )
267
+ }
268
+
269
+ export default Survey
@@ -0,0 +1,79 @@
1
+ import React, {useEffect} from 'react'
2
+ import {
3
+ NPS,
4
+ CheckboxGroup,
5
+ Radio,
6
+ RadioGroup,
7
+ Input,
8
+ TextArea,
9
+ } from '../components'
10
+
11
+ const Question = ({question, formValues, onChangeHandler}: any) => {
12
+ switch (question.type_info.question_type) {
13
+ case 'RANGE':
14
+ return (
15
+ <NPS
16
+ type_info={question.type_info}
17
+ onChangeHandler={onChangeHandler}
18
+ npsValue={formValues[question.name]}
19
+ />
20
+ )
21
+ case 'CHECKBOX':
22
+ return (
23
+ <CheckboxGroup
24
+ values={formValues[question.name]}
25
+ options={question.type_info.choices}
26
+ onChangeHandler={onChangeHandler}
27
+ />
28
+ )
29
+ case 'CHECKBOX':
30
+ return (
31
+ <RadioGroup
32
+ name={question.name}
33
+ onChangeHandler={onChangeHandler}
34
+ options={question.type_info.choices}
35
+ value={formValues[question.name]}
36
+ />
37
+ )
38
+ case 'MESSAGE':
39
+ return (
40
+ //in case of mobile device, we would render the textarea instead of input since we cannot line wrap the input field.
41
+ <div className="responsive-text-field">
42
+ <Input
43
+ autoFocus={true}
44
+ label=""
45
+ maxLength={300}
46
+ showCount={true}
47
+ isRequired={question.is_required}
48
+ value={formValues[question.name] || ''}
49
+ onChange={(e: any) => onChangeHandler(e.target.value)}
50
+ />
51
+ <TextArea
52
+ autoFocus={true}
53
+ label=""
54
+ maxLength={300}
55
+ showCount={true}
56
+ isRequired={question.is_required}
57
+ value={formValues[question.name] || ''}
58
+ onChange={(e: any) => onChangeHandler(e.target.value)}
59
+ />
60
+ </div>
61
+ )
62
+ case 'PARAGRAPH':
63
+ return (
64
+ <TextArea
65
+ autoFocus={true}
66
+ label=""
67
+ isRequired={question.is_required}
68
+ value={formValues[question.name] || ''}
69
+ onChange={(e: any) => onChangeHandler(e.target.value)}
70
+ />
71
+ )
72
+ default:
73
+ console.error(
74
+ `Question type ${question.type_info.question_type} not found`,
75
+ )
76
+ return null
77
+ }
78
+ }
79
+ export default Question