@coorpacademy/app-review 0.1.8 → 0.1.10

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 (82) hide show
  1. package/es/actions/api/fetch-skills.d.ts +8 -5
  2. package/es/actions/api/fetch-slide.d.ts +8 -5
  3. package/es/actions/api/post-progression.d.ts +8 -5
  4. package/es/actions/ui/navigation.d.ts +1 -1
  5. package/es/fixtures/qcm-slide.js +1 -1
  6. package/es/helpers/map-api-slide-to-ui.d.ts +8 -0
  7. package/es/helpers/map-api-slide-to-ui.js +175 -0
  8. package/es/helpers/test/fixtures/free-text.d.ts +4 -0
  9. package/es/helpers/test/fixtures/free-text.js +24 -0
  10. package/es/helpers/test/fixtures/qcm-drag.d.ts +4 -0
  11. package/es/helpers/test/fixtures/qcm-drag.js +106 -0
  12. package/es/helpers/test/fixtures/qcm-graphic.d.ts +4 -0
  13. package/es/helpers/test/fixtures/qcm-graphic.js +70 -0
  14. package/es/helpers/test/fixtures/qcm.d.ts +4 -0
  15. package/es/helpers/test/fixtures/qcm.js +99 -0
  16. package/es/helpers/test/fixtures/select-template.d.ts +4 -0
  17. package/es/helpers/test/fixtures/select-template.js +88 -0
  18. package/es/helpers/test/fixtures/slider.d.ts +4 -0
  19. package/es/helpers/test/fixtures/slider.js +27 -0
  20. package/es/helpers/test/fixtures/text-template.d.ts +4 -0
  21. package/es/helpers/test/fixtures/text-template.js +48 -0
  22. package/es/helpers/test/map-api-slide-to-ui.test.d.ts +1 -0
  23. package/es/helpers/test/map-api-slide-to-ui.test.js +32 -0
  24. package/es/index.d.ts +1 -1
  25. package/es/index.js +31 -16
  26. package/es/reducers/ui/navigation.d.ts +1 -1
  27. package/es/test/index.test.js +18 -15
  28. package/es/test/util/services.mock.js +2 -32
  29. package/es/types/common.d.ts +62 -5
  30. package/es/types/slides.d.ts +27 -16
  31. package/lib/actions/api/fetch-skills.d.ts +8 -5
  32. package/lib/actions/api/fetch-slide.d.ts +8 -5
  33. package/lib/actions/api/post-progression.d.ts +8 -5
  34. package/lib/actions/ui/navigation.d.ts +1 -1
  35. package/lib/fixtures/qcm-slide.js +1 -1
  36. package/lib/helpers/map-api-slide-to-ui.d.ts +8 -0
  37. package/lib/helpers/map-api-slide-to-ui.js +181 -0
  38. package/lib/helpers/test/fixtures/free-text.d.ts +4 -0
  39. package/lib/helpers/test/fixtures/free-text.js +24 -0
  40. package/lib/helpers/test/fixtures/qcm-drag.d.ts +4 -0
  41. package/lib/helpers/test/fixtures/qcm-drag.js +106 -0
  42. package/lib/helpers/test/fixtures/qcm-graphic.d.ts +4 -0
  43. package/lib/helpers/test/fixtures/qcm-graphic.js +70 -0
  44. package/lib/helpers/test/fixtures/qcm.d.ts +4 -0
  45. package/lib/helpers/test/fixtures/qcm.js +99 -0
  46. package/lib/helpers/test/fixtures/select-template.d.ts +4 -0
  47. package/lib/helpers/test/fixtures/select-template.js +88 -0
  48. package/lib/helpers/test/fixtures/slider.d.ts +4 -0
  49. package/lib/helpers/test/fixtures/slider.js +27 -0
  50. package/lib/helpers/test/fixtures/text-template.d.ts +4 -0
  51. package/lib/helpers/test/fixtures/text-template.js +48 -0
  52. package/lib/helpers/test/map-api-slide-to-ui.test.d.ts +1 -0
  53. package/lib/helpers/test/map-api-slide-to-ui.test.js +43 -0
  54. package/lib/index.d.ts +1 -1
  55. package/lib/index.js +31 -16
  56. package/lib/reducers/ui/navigation.d.ts +1 -1
  57. package/lib/test/index.test.js +19 -16
  58. package/lib/test/util/services.mock.js +13 -32
  59. package/lib/types/common.d.ts +62 -5
  60. package/lib/types/slides.d.ts +27 -16
  61. package/package.json +3 -3
  62. package/src/actions/api/fetch-skills.ts +8 -4
  63. package/src/actions/api/fetch-slide.ts +8 -4
  64. package/src/actions/api/post-progression.ts +14 -4
  65. package/src/actions/ui/navigation.ts +1 -1
  66. package/src/fixtures/qcm-slide.ts +1 -1
  67. package/src/helpers/map-api-slide-to-ui.ts +253 -0
  68. package/src/helpers/test/fixtures/free-text.ts +28 -0
  69. package/src/helpers/test/fixtures/qcm-drag.ts +110 -0
  70. package/src/helpers/test/fixtures/qcm-graphic.ts +74 -0
  71. package/src/helpers/test/fixtures/qcm.ts +108 -0
  72. package/src/helpers/test/fixtures/select-template.ts +92 -0
  73. package/src/helpers/test/fixtures/slider.ts +31 -0
  74. package/src/helpers/test/fixtures/text-template.ts +52 -0
  75. package/src/helpers/test/map-api-slide-to-ui.test.ts +55 -0
  76. package/src/index.tsx +39 -30
  77. package/src/reducers/ui/navigation.ts +1 -1
  78. package/src/test/index.test.tsx +23 -12
  79. package/src/test/util/services.mock.ts +2 -32
  80. package/src/types/common.ts +74 -5
  81. package/src/types/globals.d.ts +0 -5
  82. package/src/types/slides.ts +29 -12
@@ -0,0 +1,52 @@
1
+ import noop from 'lodash/fp/noop';
2
+ import {Template} from '../../../types/slides';
3
+ import {SlideFromAPI} from '../../../types/common';
4
+
5
+ export const textTemplateSlide: SlideFromAPI = {
6
+ question: {
7
+ content: {
8
+ media: {
9
+ src: [],
10
+ posters: [],
11
+ subtitles: []
12
+ },
13
+ template: 'Un {{inp58402}} model.',
14
+ choices: [
15
+ {
16
+ media: {
17
+ src: [],
18
+ posters: [],
19
+ subtitles: []
20
+ },
21
+ items: [],
22
+ _id: '62433c2c58920f02c4950de0',
23
+ type: 'text',
24
+ name: 'inp58402',
25
+ id: '62433c2c58920f02c4950de0'
26
+ }
27
+ ]
28
+ },
29
+ type: 'template',
30
+ header: 'Comment le protégé doit-il percevoir son mentor ?',
31
+ explanation: 'Saisissez votre réponse (en anglais).'
32
+ },
33
+ klf: "Le mentoré voit dans son mentor un <i>role model</i>, c'est-à-dire un exemple inspirant qui lui permet de se projeter dans un poste à responsabilité.",
34
+ tips: "L'origine de l'expression <i>role model</i> est attribué au sociologue Robert K. Merton.",
35
+ universalRef: 'sli_VJKQonm2g',
36
+ hasClue: true,
37
+ id: 'sli_VJKQonm2g'
38
+ };
39
+
40
+ export const textTemplateUISlide: Template = {
41
+ type: 'template',
42
+ template: 'Un {{inp58402}} model.',
43
+ answers: [
44
+ {
45
+ type: 'text',
46
+ name: 'inp58402',
47
+ placeholder: 'Type here',
48
+ value: undefined,
49
+ onChange: noop
50
+ }
51
+ ]
52
+ };
@@ -0,0 +1,55 @@
1
+ import test from 'ava';
2
+ import {SlideFromAPI} from '../../types/common';
3
+ import {mapApiSlideToUi} from '../map-api-slide-to-ui';
4
+ import {
5
+ QcmDrag,
6
+ Qcm,
7
+ QcmGraphic,
8
+ FreeText,
9
+ DropDown,
10
+ QuestionRange,
11
+ Template
12
+ } from '../../types/slides';
13
+ import {qcmUISlide, qcmSlide} from './fixtures/qcm';
14
+ import {qcmDragUISlide, qcmDragSlide} from './fixtures/qcm-drag';
15
+ import {freeTextSlide, freeTextUISlide} from './fixtures/free-text';
16
+ import {qcmGraphicUISlide, qcmGraphicSlide} from './fixtures/qcm-graphic';
17
+ import {textTemplateSlide, textTemplateUISlide} from './fixtures/text-template';
18
+ import {selectTemplateSlide, selectTemplateUISlide} from './fixtures/select-template';
19
+ import {sliderSlide, sliderUISlide} from './fixtures/slider';
20
+
21
+ const _mapApiSlideToUi = mapApiSlideToUi({translate: (text: string) => text});
22
+
23
+ const macro = test.macro({
24
+ title(providedTitle) {
25
+ return `should handle ${providedTitle} conversion`.trim();
26
+ },
27
+ exec(
28
+ t,
29
+ arg: {
30
+ slide: SlideFromAPI;
31
+ expectedUiSlide: QcmDrag | Qcm | QcmGraphic | FreeText | DropDown | QuestionRange | Template;
32
+ }
33
+ ) {
34
+ t.deepEqual(
35
+ JSON.parse(JSON.stringify(_mapApiSlideToUi(arg.slide))),
36
+ JSON.parse(JSON.stringify(arg.expectedUiSlide))
37
+ );
38
+ }
39
+ });
40
+
41
+ test('qcm', macro, {slide: qcmSlide, expectedUiSlide: qcmUISlide});
42
+ test('qcm drag', macro, {slide: qcmDragSlide, expectedUiSlide: qcmDragUISlide});
43
+ test('qcm graphic', macro, {slide: qcmGraphicSlide, expectedUiSlide: qcmGraphicUISlide});
44
+ test('text template', macro, {slide: textTemplateSlide, expectedUiSlide: textTemplateUISlide});
45
+ test('select template', macro, {
46
+ slide: selectTemplateSlide,
47
+ expectedUiSlide: selectTemplateUISlide
48
+ });
49
+ test('free text', macro, {slide: freeTextSlide, expectedUiSlide: freeTextUISlide});
50
+ test('slider', macro, {slide: sliderSlide, expectedUiSlide: sliderUISlide});
51
+
52
+ test('should throw an error if the question type can not be handled', t => {
53
+ const faultySlide = {...qcmSlide, question: {type: 'lol'}};
54
+ t.throws(() => _mapApiSlideToUi(faultySlide as SlideFromAPI));
55
+ });
package/src/index.tsx CHANGED
@@ -1,9 +1,12 @@
1
1
  import React, {useEffect, useState} from 'react';
2
+ import type {} from 'redux-thunk/extend-redux'; // https://github.com/reduxjs/redux-thunk/issues/333
2
3
  import {AnyAction, Store} from 'redux';
3
4
  import {connect, Provider} from 'react-redux';
4
5
  import AppReviewTemplate from '@coorpacademy/components/es/template/app-review';
5
6
  import {TemplateContext} from '@coorpacademy/components/es/template/app-review/template-context';
6
7
 
8
+ import isEmpty from 'lodash/fp/isEmpty';
9
+ import get from 'lodash/fp/get';
7
10
  import configureStore from './configure-store';
8
11
  import {congratsProps, correctionPopinProps} from './fixtures/temp-fixture';
9
12
 
@@ -23,16 +26,12 @@ import {fetchSkills} from './actions/api/fetch-skills';
23
26
  import {postProgression} from './actions/api/post-progression';
24
27
  import {VIEWS} from './common';
25
28
 
26
- // -----------------------------------------------------------------------------
27
-
28
29
  type StaticProps = {
29
- viewName: 'skills' | 'onboarding' | 'slides';
30
+ viewName: 'skills' | 'onboarding' | 'slides' | 'loader';
30
31
  slides: SlidesViewStaticProps | null;
31
32
  skills: SkillsProps | null;
32
33
  };
33
34
 
34
- // -----------------------------------------------------------------------------
35
-
36
35
  const mapDispatchToProps: Dispatchers = {
37
36
  navigateTo,
38
37
  navigateBack,
@@ -48,8 +47,6 @@ const mapDispatchToProps: Dispatchers = {
48
47
  const getCurrentViewName = (storeState: StoreState): ViewPath =>
49
48
  storeState.ui.navigation[storeState.ui.navigation.length - 1];
50
49
 
51
- // -----------------------------------------------------------------------------
52
-
53
50
  const mapStateToSkillsProps = (state: StoreState): SkillsProps | null => {
54
51
  if (!state.data.skills) {
55
52
  return null;
@@ -77,8 +74,6 @@ const mapStateToSkillsProps = (state: StoreState): SkillsProps | null => {
77
74
  };
78
75
  };
79
76
 
80
- // -----------------------------------------------------------------------------
81
-
82
77
  const mapStateToSlidesProps = (state: StoreState): SlidesViewStaticProps | null => {
83
78
  if (!state.data.slides) {
84
79
  return null;
@@ -108,8 +103,6 @@ const mapStateToSlidesProps = (state: StoreState): SlidesViewStaticProps | null
108
103
  };
109
104
  };
110
105
 
111
- // -----------------------------------------------------------------------------
112
-
113
106
  const mapStateToProps = (state: StoreState): StaticProps => {
114
107
  return {
115
108
  viewName: getCurrentViewName(state),
@@ -118,14 +111,11 @@ const mapStateToProps = (state: StoreState): StaticProps => {
118
111
  };
119
112
  };
120
113
 
121
- // Props = StaticProps && Dispatchers
122
-
123
114
  const App = connect(mapStateToProps, mapDispatchToProps)(AppReviewTemplate);
124
115
 
125
- // -----------------------------------------------------------------------------
126
-
127
116
  const AppReview = ({options}: {options: AppOptions}): JSX.Element | null => {
128
117
  const [store, setStore] = useState<Store<StoreState, AnyAction> | null>(null);
118
+ const [isSlideFetched, setIsSlideFetched] = useState(false);
129
119
 
130
120
  useEffect(() => {
131
121
  if (store) return;
@@ -135,29 +125,52 @@ const AppReview = ({options}: {options: AppOptions}): JSX.Element | null => {
135
125
  }, [options, store]);
136
126
 
137
127
  useEffect(() => {
138
- if (store === null) return;
128
+ if (!store || isSlideFetched) return;
139
129
 
140
- const {skillRef} = options;
141
- const initialView: ViewPath = skillRef ? VIEWS.slides : VIEWS.skills;
142
- store.dispatch(navigateTo(initialView));
143
- }, [options, store]);
130
+ return store.subscribe(() => {
131
+ const isSlidePresent = !isEmpty(store.getState().data.slides[0].questionText);
132
+ return isSlidePresent && setIsSlideFetched(isSlidePresent);
133
+ }); // using the first slide questionText for now, should only check for a slide's presence
134
+ // when the refactor is done (slides will be feed one by one on api's side)
135
+ }, [isSlideFetched, options, store]);
144
136
 
145
137
  useEffect(() => {
146
- if (store === null) return;
147
-
148
- const {token} = options;
138
+ const token = get('token', options);
139
+ if (store === null || isEmpty(token)) return;
149
140
  store.dispatch(storeToken(token));
150
141
  }, [options, store]);
151
142
 
152
143
  useEffect(() => {
153
- if (store === null) return;
144
+ const token = get('token', options);
145
+ if (store === null || isEmpty(token)) return;
146
+
147
+ const skillRef = get('skillRef', options);
154
148
 
155
- const {skillRef, token} = options;
149
+ // ThunkAction is not assignable to parameter of type 'AnyAction'
150
+ // ts problem describre here = https://github.com/reduxjs/redux-thunk/issues/333
156
151
  skillRef
157
152
  ? store.dispatch(postProgression(skillRef, token))
158
153
  : store.dispatch(fetchSkills(token));
159
154
  }, [options, store]);
160
155
 
156
+ useEffect(() => {
157
+ if (store === null) return;
158
+
159
+ const {skillRef} = options;
160
+
161
+ if (skillRef && !isSlideFetched) {
162
+ store.dispatch(navigateTo('loader')); // use loader while posting progression
163
+ return;
164
+ }
165
+
166
+ // TODO: when slides state is refactored (data + mapping),
167
+ // use loader in case a skillRef is found && no slides from the API
168
+ // are found
169
+
170
+ const initialView: ViewPath = skillRef ? VIEWS.slides : VIEWS.skills;
171
+ store.dispatch(navigateTo(initialView));
172
+ }, [isSlideFetched, options, store]);
173
+
161
174
  if (!store) return null;
162
175
 
163
176
  const {templateContext: values} = options;
@@ -171,15 +184,11 @@ const AppReview = ({options}: {options: AppOptions}): JSX.Element | null => {
171
184
  );
172
185
  };
173
186
 
174
- // -----------------------------------------------------------------------------
175
-
176
187
  declare global {
177
188
  // eslint-disable-next-line no-shadow
178
189
  interface Window {
179
- __REDUX_DEVTOOLS_EXTENSION__?: boolean;
190
+ __REDUX_DEVTOOLS_EXTENSION_COMPOSE__?: Function;
180
191
  }
181
192
  }
182
193
 
183
- // -----------------------------------------------------------------------------
184
-
185
194
  export default AppReview;
@@ -5,7 +5,7 @@ import {
5
5
  NAVIGATE_TO
6
6
  } from '../../actions/ui/navigation';
7
7
 
8
- export type NavigationState = Array<'skills' | 'onboarding' | 'slides'>;
8
+ export type NavigationState = Array<'skills' | 'onboarding' | 'slides' | 'loader'>;
9
9
 
10
10
  const reducer = (
11
11
  // eslint-disable-next-line default-param-last
@@ -65,24 +65,35 @@ const appOptions: AppOptions = {
65
65
  services
66
66
  };
67
67
 
68
- test('should validate all the slides (all correct scenario)', async t => {
69
- // t.plan(13);
68
+ test('should show the loader while the app is fetching the data', async t => {
70
69
  t.plan(2);
71
- const {container} = render(<AppReview options={appOptions} key={0} />);
70
+ const {container} = render(<AppReview options={appOptions} />);
71
+
72
72
  await waitForChanges(1000);
73
73
 
74
- const wrapper = container.querySelector('[data-name="loader"]');
75
- t.truthy(wrapper);
76
- /*
77
- const wrapper = container.querySelector('[data-name="slides-revision-container"]');
78
- t.truthy(wrapper);
74
+ const loader = container.querySelector('[data-name="loader"]');
75
+ t.truthy(loader);
76
+
77
+ // await waitFor(async () => {
78
+ // // await sleep(500);
79
+ // const result = await t.try(tt => {
80
+ // // rerender(<AppReview options={appOptions} />);
81
+
82
+ // const slidesContainer = container.querySelector('[data-name="slides-revision-container"]');
83
+
84
+ // tt.truthy(slidesContainer);
85
+ // });
86
+
87
+ // if (result.passed) return result.commit();
88
+ // result.discard();
89
+ // throw result.errors;
90
+ // });
79
91
 
80
- const stackedSlidesContainer = wrapper?.querySelector('[data-name="stacked-slides-container"]');
92
+ // const stackedSlidesContainer = container.querySelector('[data-name="stacked-slides-container"]');
81
93
 
82
- t.truthy(stackedSlidesContainer);
94
+ // t.truthy(stackedSlidesContainer);
83
95
 
84
- await clickAllSlides(t, container);
85
- */
96
+ // await clickAllSlides(t, container);
86
97
 
87
98
  t.pass();
88
99
  });
@@ -1,3 +1,4 @@
1
+ import {qcmSlide} from '../../helpers/test/fixtures/qcm';
1
2
  import {Services} from '../../types/common';
2
3
 
3
4
  export const services: Services = {
@@ -18,38 +19,7 @@ export const services: Services = {
18
19
  ]);
19
20
  },
20
21
  fetchSlide: ref => {
21
- // return Promise.resolve(qcm); // why qcm does not look like a slide returned from mooc ?
22
- return Promise.resolve({
23
- question: {
24
- content: {
25
- choices: [
26
- {
27
- selected: false,
28
- title: 'choix1',
29
- help: '(help choix1)',
30
- ariaLabel: '__ariaLabel1',
31
- onClick: (): void => undefined
32
- },
33
- {
34
- selected: false,
35
- title: 'choix2',
36
- help: '(help choix2)',
37
- ariaLabel: '__ariaLabel2',
38
- onClick: (): void => undefined
39
- },
40
- {
41
- selected: false,
42
- title: 'choix3',
43
- help: '(help choix3)',
44
- ariaLabel: '__ariaLabel3',
45
- onClick: (): void => undefined
46
- }
47
- ]
48
- }
49
- },
50
- klf: '___klf',
51
- universalRef: ref
52
- });
22
+ return Promise.resolve({...qcmSlide, universalRef: ref, id: ref});
53
23
  },
54
24
  postProgression: () => {
55
25
  return Promise.resolve({
@@ -7,14 +7,83 @@ type TemplateContextValues = {
7
7
  translations?: unknown; // TODO type Translations
8
8
  };
9
9
 
10
- export type SlideFromAPI = {
11
- question: {
12
- content: {
13
- choices: Record<string, unknown>[];
14
- };
10
+ // TODO: EDIT_CHOICES -> specify typing
11
+ export type ChoiceFromAPI = {
12
+ _id: string;
13
+ id?: string;
14
+ value?: string;
15
+ name?: string;
16
+ type?: 'text' | 'select';
17
+ label?: string;
18
+ items: {text: string; value: string; _id: string}[];
19
+ media?: unknown;
20
+ };
21
+
22
+ type BaseContent = {
23
+ media?: unknown;
24
+ choices: ChoiceFromAPI[];
25
+ };
26
+
27
+ type Question = {
28
+ header?: string;
29
+ content: BaseContent;
30
+ explanation?: string;
31
+ };
32
+
33
+ export type QcmQuestion = Question & {
34
+ type: 'qcm';
35
+ };
36
+
37
+ export type QcmGraphicQuestion = Question & {
38
+ type: 'qcmGraphic';
39
+ };
40
+
41
+ export type QcmDragQuestion = Question & {
42
+ type: 'qcmDrag';
43
+ };
44
+
45
+ export type BasicQuestion = Omit<Question, 'content'> & {
46
+ type: 'basic';
47
+ content: {
48
+ media?: unknown;
49
+ label?: string;
50
+ placeholder?: string;
51
+ id: string;
52
+ };
53
+ };
54
+
55
+ export type SliderQuestion = Omit<Question, 'content'> & {
56
+ content: {
57
+ media?: unknown;
58
+ unitLabel: string;
59
+ min: number;
60
+ max: number;
61
+ step?: number;
15
62
  };
63
+ type: 'slider';
64
+ };
65
+
66
+ export type TemplateQuestion = Omit<Question, 'content'> & {
67
+ content: BaseContent & {
68
+ template: string;
69
+ };
70
+ type: 'template';
71
+ };
72
+
73
+ export type SlideFromAPI = {
74
+ question:
75
+ | QcmQuestion
76
+ | QcmGraphicQuestion
77
+ | QcmDragQuestion
78
+ | BasicQuestion
79
+ | SliderQuestion
80
+ | TemplateQuestion;
16
81
  klf: string;
17
82
  universalRef: string;
83
+ tips?: string;
84
+ clue?: string;
85
+ hasClue?: boolean;
86
+ id?: string;
18
87
  };
19
88
 
20
89
  export type ProgressionFromAPI = {
@@ -3,8 +3,3 @@ declare module 'browser-env';
3
3
  declare module '@coorpacademy/components/es/template/app-review';
4
4
  declare module '@coorpacademy/components/es/template/app-review/template-context';
5
5
  declare module '@coorpacademy/redux-task';
6
-
7
- // eslint-disable-next-line no-shadow
8
- declare interface Window {
9
- __REDUX_DEVTOOLS_EXTENSION_COMPOSE__?: Function;
10
- }
@@ -1,9 +1,10 @@
1
1
  export type Choice = {
2
2
  order?: number;
3
3
  selected: boolean;
4
- title: string;
5
- help: string;
4
+ title?: string;
5
+ help?: string;
6
6
  onClick: Function;
7
+ ariaLabel?: string;
7
8
  };
8
9
 
9
10
  export type QcmDrag = {
@@ -14,20 +15,20 @@ export type QcmDrag = {
14
15
 
15
16
  export type Qcm = {
16
17
  type: 'qcm';
17
- choices: (Choice & {ariaLabel: string})[];
18
+ answers: Choice[];
18
19
  };
19
20
 
20
21
  export type QcmGraphic = {
21
22
  type: 'qcmGraphic';
22
- choices: (Choice & {image: string})[];
23
+ answers: (Choice & {image: string})[];
23
24
  };
24
25
 
25
26
  export type FreeText = {
26
27
  type: 'freeText';
27
28
  placeholder: string;
28
- value: string;
29
+ value?: string;
29
30
  onChange: Function;
30
- ariaLabel: string;
31
+ ariaLabel?: string;
31
32
  };
32
33
 
33
34
  export type DropDownOption = {
@@ -39,9 +40,9 @@ export type DropDownOption = {
39
40
 
40
41
  export type DropDown = {
41
42
  type: 'dropDown';
42
- onChange: Function;
43
+ onChange?: Function;
43
44
  options: DropDownOption[];
44
- theme:
45
+ theme?:
45
46
  | 'filter'
46
47
  | 'invalid'
47
48
  | 'header'
@@ -56,19 +57,35 @@ export type DropDown = {
56
57
 
57
58
  export type QuestionRange = {
58
59
  type: 'slider';
59
- onChange: Function;
60
- onChangeEnd: Function;
61
- multi: boolean;
60
+ placeholder?: string;
61
+ onChange?: Function;
62
+ onChangeEnd?: Function;
63
+ multi?: boolean;
62
64
  value: number | number[];
63
65
  title: string;
64
66
  minLabel: string;
65
67
  maxLabel: string;
66
68
  };
67
69
 
70
+ export type TextTemplate = {
71
+ type: 'text';
72
+ name: string;
73
+ placeholder: string;
74
+ value?: string;
75
+ onChange: Function;
76
+ };
77
+
78
+ export type SelectionTemplate = {
79
+ type: 'select';
80
+ name: string;
81
+ onChange?: Function;
82
+ options?: DropDownOption[];
83
+ };
84
+
68
85
  export type Template = {
69
86
  type: 'template';
70
87
  template: string;
71
- choices: (DropDown & {type?: string})[];
88
+ answers: (TextTemplate | SelectionTemplate)[];
72
89
  };
73
90
 
74
91
  export type AnswerUI = {