@coorpacademy/app-review 0.1.9 → 0.1.11
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/es/actions/ui/navigation.d.ts +1 -1
- package/es/fixtures/qcm-slide.js +1 -1
- package/es/helpers/map-api-slide-to-ui.d.ts +8 -0
- package/es/helpers/map-api-slide-to-ui.js +175 -0
- package/es/helpers/test/fixtures/free-text.d.ts +4 -0
- package/es/helpers/test/fixtures/free-text.js +24 -0
- package/es/helpers/test/fixtures/qcm-drag.d.ts +4 -0
- package/es/helpers/test/fixtures/qcm-drag.js +106 -0
- package/es/helpers/test/fixtures/qcm-graphic.d.ts +4 -0
- package/es/helpers/test/fixtures/qcm-graphic.js +70 -0
- package/es/helpers/test/fixtures/qcm.d.ts +4 -0
- package/es/helpers/test/fixtures/qcm.js +99 -0
- package/es/helpers/test/fixtures/select-template.d.ts +4 -0
- package/es/helpers/test/fixtures/select-template.js +88 -0
- package/es/helpers/test/fixtures/slider.d.ts +4 -0
- package/es/helpers/test/fixtures/slider.js +27 -0
- package/es/helpers/test/fixtures/text-template.d.ts +4 -0
- package/es/helpers/test/fixtures/text-template.js +48 -0
- package/es/helpers/test/map-api-slide-to-ui.test.d.ts +1 -0
- package/es/helpers/test/map-api-slide-to-ui.test.js +32 -0
- package/es/index.js +29 -9
- package/es/reducers/ui/navigation.d.ts +1 -1
- package/es/test/index.test.js +18 -15
- package/es/test/util/services.mock.js +2 -32
- package/es/types/common.d.ts +62 -5
- package/es/types/slides.d.ts +27 -16
- package/lib/actions/ui/navigation.d.ts +1 -1
- package/lib/fixtures/qcm-slide.js +1 -1
- package/lib/helpers/map-api-slide-to-ui.d.ts +8 -0
- package/lib/helpers/map-api-slide-to-ui.js +181 -0
- package/lib/helpers/test/fixtures/free-text.d.ts +4 -0
- package/lib/helpers/test/fixtures/free-text.js +24 -0
- package/lib/helpers/test/fixtures/qcm-drag.d.ts +4 -0
- package/lib/helpers/test/fixtures/qcm-drag.js +106 -0
- package/lib/helpers/test/fixtures/qcm-graphic.d.ts +4 -0
- package/lib/helpers/test/fixtures/qcm-graphic.js +70 -0
- package/lib/helpers/test/fixtures/qcm.d.ts +4 -0
- package/lib/helpers/test/fixtures/qcm.js +99 -0
- package/lib/helpers/test/fixtures/select-template.d.ts +4 -0
- package/lib/helpers/test/fixtures/select-template.js +88 -0
- package/lib/helpers/test/fixtures/slider.d.ts +4 -0
- package/lib/helpers/test/fixtures/slider.js +27 -0
- package/lib/helpers/test/fixtures/text-template.d.ts +4 -0
- package/lib/helpers/test/fixtures/text-template.js +48 -0
- package/lib/helpers/test/map-api-slide-to-ui.test.d.ts +1 -0
- package/lib/helpers/test/map-api-slide-to-ui.test.js +43 -0
- package/lib/index.js +29 -9
- package/lib/reducers/ui/navigation.d.ts +1 -1
- package/lib/test/index.test.js +19 -16
- package/lib/test/util/services.mock.js +13 -32
- package/lib/types/common.d.ts +62 -5
- package/lib/types/slides.d.ts +27 -16
- package/package.json +4 -3
- package/src/actions/ui/navigation.ts +1 -1
- package/src/fixtures/qcm-slide.ts +1 -1
- package/src/helpers/map-api-slide-to-ui.ts +253 -0
- package/src/helpers/test/fixtures/free-text.ts +28 -0
- package/src/helpers/test/fixtures/qcm-drag.ts +110 -0
- package/src/helpers/test/fixtures/qcm-graphic.ts +74 -0
- package/src/helpers/test/fixtures/qcm.ts +108 -0
- package/src/helpers/test/fixtures/select-template.ts +92 -0
- package/src/helpers/test/fixtures/slider.ts +31 -0
- package/src/helpers/test/fixtures/text-template.ts +52 -0
- package/src/helpers/test/map-api-slide-to-ui.test.ts +55 -0
- package/src/index.tsx +35 -11
- package/src/reducers/ui/navigation.ts +1 -1
- package/src/test/index.test.tsx +23 -12
- package/src/test/util/services.mock.ts +2 -32
- package/src/types/common.ts +74 -5
- package/src/types/slides.ts +29 -12
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import noop from 'lodash/fp/noop';
|
|
2
|
+
export const textTemplateSlide = {
|
|
3
|
+
question: {
|
|
4
|
+
content: {
|
|
5
|
+
media: {
|
|
6
|
+
src: [],
|
|
7
|
+
posters: [],
|
|
8
|
+
subtitles: []
|
|
9
|
+
},
|
|
10
|
+
template: 'Un {{inp58402}} model.',
|
|
11
|
+
choices: [
|
|
12
|
+
{
|
|
13
|
+
media: {
|
|
14
|
+
src: [],
|
|
15
|
+
posters: [],
|
|
16
|
+
subtitles: []
|
|
17
|
+
},
|
|
18
|
+
items: [],
|
|
19
|
+
_id: '62433c2c58920f02c4950de0',
|
|
20
|
+
type: 'text',
|
|
21
|
+
name: 'inp58402',
|
|
22
|
+
id: '62433c2c58920f02c4950de0'
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
type: 'template',
|
|
27
|
+
header: 'Comment le protégé doit-il percevoir son mentor ?',
|
|
28
|
+
explanation: 'Saisissez votre réponse (en anglais).'
|
|
29
|
+
},
|
|
30
|
+
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é.",
|
|
31
|
+
tips: "L'origine de l'expression <i>role model</i> est attribué au sociologue Robert K. Merton.",
|
|
32
|
+
universalRef: 'sli_VJKQonm2g',
|
|
33
|
+
hasClue: true,
|
|
34
|
+
id: 'sli_VJKQonm2g'
|
|
35
|
+
};
|
|
36
|
+
export const textTemplateUISlide = {
|
|
37
|
+
type: 'template',
|
|
38
|
+
template: 'Un {{inp58402}} model.',
|
|
39
|
+
answers: [
|
|
40
|
+
{
|
|
41
|
+
type: 'text',
|
|
42
|
+
name: 'inp58402',
|
|
43
|
+
placeholder: 'Type here',
|
|
44
|
+
value: undefined,
|
|
45
|
+
onChange: noop
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import { mapApiSlideToUi } from '../map-api-slide-to-ui';
|
|
3
|
+
import { qcmUISlide, qcmSlide } from './fixtures/qcm';
|
|
4
|
+
import { qcmDragUISlide, qcmDragSlide } from './fixtures/qcm-drag';
|
|
5
|
+
import { freeTextSlide, freeTextUISlide } from './fixtures/free-text';
|
|
6
|
+
import { qcmGraphicUISlide, qcmGraphicSlide } from './fixtures/qcm-graphic';
|
|
7
|
+
import { textTemplateSlide, textTemplateUISlide } from './fixtures/text-template';
|
|
8
|
+
import { selectTemplateSlide, selectTemplateUISlide } from './fixtures/select-template';
|
|
9
|
+
import { sliderSlide, sliderUISlide } from './fixtures/slider';
|
|
10
|
+
const _mapApiSlideToUi = mapApiSlideToUi({ translate: (text) => text });
|
|
11
|
+
const macro = test.macro({
|
|
12
|
+
title(providedTitle) {
|
|
13
|
+
return `should handle ${providedTitle} conversion`.trim();
|
|
14
|
+
},
|
|
15
|
+
exec(t, arg) {
|
|
16
|
+
t.deepEqual(JSON.parse(JSON.stringify(_mapApiSlideToUi(arg.slide))), JSON.parse(JSON.stringify(arg.expectedUiSlide)));
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
test('qcm', macro, { slide: qcmSlide, expectedUiSlide: qcmUISlide });
|
|
20
|
+
test('qcm drag', macro, { slide: qcmDragSlide, expectedUiSlide: qcmDragUISlide });
|
|
21
|
+
test('qcm graphic', macro, { slide: qcmGraphicSlide, expectedUiSlide: qcmGraphicUISlide });
|
|
22
|
+
test('text template', macro, { slide: textTemplateSlide, expectedUiSlide: textTemplateUISlide });
|
|
23
|
+
test('select template', macro, {
|
|
24
|
+
slide: selectTemplateSlide,
|
|
25
|
+
expectedUiSlide: selectTemplateUISlide
|
|
26
|
+
});
|
|
27
|
+
test('free text', macro, { slide: freeTextSlide, expectedUiSlide: freeTextUISlide });
|
|
28
|
+
test('slider', macro, { slide: sliderSlide, expectedUiSlide: sliderUISlide });
|
|
29
|
+
test('should throw an error if the question type can not be handled', t => {
|
|
30
|
+
const faultySlide = { ...qcmSlide, question: { type: 'lol' } };
|
|
31
|
+
t.throws(() => _mapApiSlideToUi(faultySlide));
|
|
32
|
+
});
|
package/es/index.js
CHANGED
|
@@ -2,6 +2,8 @@ import React, { useEffect, useState } from 'react';
|
|
|
2
2
|
import { connect, Provider } from 'react-redux';
|
|
3
3
|
import AppReviewTemplate from '@coorpacademy/components/es/template/app-review';
|
|
4
4
|
import { TemplateContext } from '@coorpacademy/components/es/template/app-review/template-context';
|
|
5
|
+
import isEmpty from 'lodash/fp/isEmpty';
|
|
6
|
+
import get from 'lodash/fp/get';
|
|
5
7
|
import configureStore from './configure-store';
|
|
6
8
|
import { congratsProps, correctionPopinProps } from './fixtures/temp-fixture';
|
|
7
9
|
import { updateSlidesOnNext, updateSlidesOnValidation, validateSlide } from './actions/data/slides';
|
|
@@ -87,6 +89,7 @@ const mapStateToProps = (state) => {
|
|
|
87
89
|
const App = connect(mapStateToProps, mapDispatchToProps)(AppReviewTemplate);
|
|
88
90
|
const AppReview = ({ options }) => {
|
|
89
91
|
const [store, setStore] = useState(null);
|
|
92
|
+
const [isSlideFetched, setIsSlideFetched] = useState(false);
|
|
90
93
|
useEffect(() => {
|
|
91
94
|
if (store)
|
|
92
95
|
return;
|
|
@@ -94,28 +97,45 @@ const AppReview = ({ options }) => {
|
|
|
94
97
|
setStore(newStore);
|
|
95
98
|
}, [options, store]);
|
|
96
99
|
useEffect(() => {
|
|
97
|
-
if (store
|
|
100
|
+
if (!store || isSlideFetched)
|
|
98
101
|
return;
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
102
|
+
return store.subscribe(() => {
|
|
103
|
+
const isSlidePresent = !isEmpty(store.getState().data.slides[0].questionText);
|
|
104
|
+
return isSlidePresent && setIsSlideFetched(isSlidePresent);
|
|
105
|
+
}); // using the first slide questionText for now, should only check for a slide's presence
|
|
106
|
+
// when the refactor is done (slides will be feed one by one on api's side)
|
|
107
|
+
}, [isSlideFetched, options, store]);
|
|
103
108
|
useEffect(() => {
|
|
104
|
-
|
|
109
|
+
const token = get('token', options);
|
|
110
|
+
if (store === null || isEmpty(token))
|
|
105
111
|
return;
|
|
106
|
-
const { token } = options;
|
|
107
112
|
store.dispatch(storeToken(token));
|
|
108
113
|
}, [options, store]);
|
|
109
114
|
useEffect(() => {
|
|
110
|
-
|
|
115
|
+
const token = get('token', options);
|
|
116
|
+
if (store === null || isEmpty(token))
|
|
111
117
|
return;
|
|
112
|
-
const
|
|
118
|
+
const skillRef = get('skillRef', options);
|
|
113
119
|
// ThunkAction is not assignable to parameter of type 'AnyAction'
|
|
114
120
|
// ts problem describre here = https://github.com/reduxjs/redux-thunk/issues/333
|
|
115
121
|
skillRef
|
|
116
122
|
? store.dispatch(postProgression(skillRef, token))
|
|
117
123
|
: store.dispatch(fetchSkills(token));
|
|
118
124
|
}, [options, store]);
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
if (store === null)
|
|
127
|
+
return;
|
|
128
|
+
const { skillRef } = options;
|
|
129
|
+
if (skillRef && !isSlideFetched) {
|
|
130
|
+
store.dispatch(navigateTo('loader')); // use loader while posting progression
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
// TODO: when slides state is refactored (data + mapping),
|
|
134
|
+
// use loader in case a skillRef is found && no slides from the API
|
|
135
|
+
// are found
|
|
136
|
+
const initialView = skillRef ? VIEWS.slides : VIEWS.skills;
|
|
137
|
+
store.dispatch(navigateTo(initialView));
|
|
138
|
+
}, [isSlideFetched, options, store]);
|
|
119
139
|
if (!store)
|
|
120
140
|
return null;
|
|
121
141
|
const { templateContext: values } = options;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { type NavigateTo, type NavigateBack } from '../../actions/ui/navigation';
|
|
2
|
-
export declare type NavigationState = Array<'skills' | 'onboarding' | 'slides'>;
|
|
2
|
+
export declare type NavigationState = Array<'skills' | 'onboarding' | 'slides' | 'loader'>;
|
|
3
3
|
declare const reducer: (state: NavigationState | undefined, action: NavigateTo | NavigateBack) => NavigationState;
|
|
4
4
|
export default reducer;
|
package/es/test/index.test.js
CHANGED
|
@@ -42,22 +42,25 @@ const appOptions = {
|
|
|
42
42
|
skillRef: 'skill_NJC0jFKoH',
|
|
43
43
|
services
|
|
44
44
|
};
|
|
45
|
-
test('should
|
|
46
|
-
// t.plan(13);
|
|
45
|
+
test('should show the loader while the app is fetching the data', async (t) => {
|
|
47
46
|
t.plan(2);
|
|
48
|
-
const { container } = render(React.createElement(AppReview, { options: appOptions
|
|
47
|
+
const { container } = render(React.createElement(AppReview, { options: appOptions }));
|
|
49
48
|
await waitForChanges(1000);
|
|
50
|
-
const
|
|
51
|
-
t.truthy(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
t.
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
49
|
+
const loader = container.querySelector('[data-name="loader"]');
|
|
50
|
+
t.truthy(loader);
|
|
51
|
+
// await waitFor(async () => {
|
|
52
|
+
// // await sleep(500);
|
|
53
|
+
// const result = await t.try(tt => {
|
|
54
|
+
// // rerender(<AppReview options={appOptions} />);
|
|
55
|
+
// const slidesContainer = container.querySelector('[data-name="slides-revision-container"]');
|
|
56
|
+
// tt.truthy(slidesContainer);
|
|
57
|
+
// });
|
|
58
|
+
// if (result.passed) return result.commit();
|
|
59
|
+
// result.discard();
|
|
60
|
+
// throw result.errors;
|
|
61
|
+
// });
|
|
62
|
+
// const stackedSlidesContainer = container.querySelector('[data-name="stacked-slides-container"]');
|
|
63
|
+
// t.truthy(stackedSlidesContainer);
|
|
64
|
+
// await clickAllSlides(t, container);
|
|
62
65
|
t.pass();
|
|
63
66
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { qcmSlide } from '../../helpers/test/fixtures/qcm';
|
|
1
2
|
export const services = {
|
|
2
3
|
fetchSkills: () => {
|
|
3
4
|
return Promise.resolve([
|
|
@@ -16,38 +17,7 @@ export const services = {
|
|
|
16
17
|
]);
|
|
17
18
|
},
|
|
18
19
|
fetchSlide: ref => {
|
|
19
|
-
|
|
20
|
-
return Promise.resolve({
|
|
21
|
-
question: {
|
|
22
|
-
content: {
|
|
23
|
-
choices: [
|
|
24
|
-
{
|
|
25
|
-
selected: false,
|
|
26
|
-
title: 'choix1',
|
|
27
|
-
help: '(help choix1)',
|
|
28
|
-
ariaLabel: '__ariaLabel1',
|
|
29
|
-
onClick: () => undefined
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
selected: false,
|
|
33
|
-
title: 'choix2',
|
|
34
|
-
help: '(help choix2)',
|
|
35
|
-
ariaLabel: '__ariaLabel2',
|
|
36
|
-
onClick: () => undefined
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
selected: false,
|
|
40
|
-
title: 'choix3',
|
|
41
|
-
help: '(help choix3)',
|
|
42
|
-
ariaLabel: '__ariaLabel3',
|
|
43
|
-
onClick: () => undefined
|
|
44
|
-
}
|
|
45
|
-
]
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
klf: '___klf',
|
|
49
|
-
universalRef: ref
|
|
50
|
-
});
|
|
20
|
+
return Promise.resolve({ ...qcmSlide, universalRef: ref, id: ref });
|
|
51
21
|
},
|
|
52
22
|
postProgression: () => {
|
|
53
23
|
return Promise.resolve({
|
package/es/types/common.d.ts
CHANGED
|
@@ -4,14 +4,71 @@ declare type TemplateContextValues = {
|
|
|
4
4
|
theme?: unknown;
|
|
5
5
|
translations?: unknown;
|
|
6
6
|
};
|
|
7
|
-
export declare type
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
export declare type ChoiceFromAPI = {
|
|
8
|
+
_id: string;
|
|
9
|
+
id?: string;
|
|
10
|
+
value?: string;
|
|
11
|
+
name?: string;
|
|
12
|
+
type?: 'text' | 'select';
|
|
13
|
+
label?: string;
|
|
14
|
+
items: {
|
|
15
|
+
text: string;
|
|
16
|
+
value: string;
|
|
17
|
+
_id: string;
|
|
18
|
+
}[];
|
|
19
|
+
media?: unknown;
|
|
20
|
+
};
|
|
21
|
+
declare type BaseContent = {
|
|
22
|
+
media?: unknown;
|
|
23
|
+
choices: ChoiceFromAPI[];
|
|
24
|
+
};
|
|
25
|
+
declare type Question = {
|
|
26
|
+
header?: string;
|
|
27
|
+
content: BaseContent;
|
|
28
|
+
explanation?: string;
|
|
29
|
+
};
|
|
30
|
+
export declare type QcmQuestion = Question & {
|
|
31
|
+
type: 'qcm';
|
|
32
|
+
};
|
|
33
|
+
export declare type QcmGraphicQuestion = Question & {
|
|
34
|
+
type: 'qcmGraphic';
|
|
35
|
+
};
|
|
36
|
+
export declare type QcmDragQuestion = Question & {
|
|
37
|
+
type: 'qcmDrag';
|
|
38
|
+
};
|
|
39
|
+
export declare type BasicQuestion = Omit<Question, 'content'> & {
|
|
40
|
+
type: 'basic';
|
|
41
|
+
content: {
|
|
42
|
+
media?: unknown;
|
|
43
|
+
label?: string;
|
|
44
|
+
placeholder?: string;
|
|
45
|
+
id: string;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
export declare type SliderQuestion = Omit<Question, 'content'> & {
|
|
49
|
+
content: {
|
|
50
|
+
media?: unknown;
|
|
51
|
+
unitLabel: string;
|
|
52
|
+
min: number;
|
|
53
|
+
max: number;
|
|
54
|
+
step?: number;
|
|
12
55
|
};
|
|
56
|
+
type: 'slider';
|
|
57
|
+
};
|
|
58
|
+
export declare type TemplateQuestion = Omit<Question, 'content'> & {
|
|
59
|
+
content: BaseContent & {
|
|
60
|
+
template: string;
|
|
61
|
+
};
|
|
62
|
+
type: 'template';
|
|
63
|
+
};
|
|
64
|
+
export declare type SlideFromAPI = {
|
|
65
|
+
question: QcmQuestion | QcmGraphicQuestion | QcmDragQuestion | BasicQuestion | SliderQuestion | TemplateQuestion;
|
|
13
66
|
klf: string;
|
|
14
67
|
universalRef: string;
|
|
68
|
+
tips?: string;
|
|
69
|
+
clue?: string;
|
|
70
|
+
hasClue?: boolean;
|
|
71
|
+
id?: string;
|
|
15
72
|
};
|
|
16
73
|
export declare type ProgressionFromAPI = {
|
|
17
74
|
_id: string;
|
package/es/types/slides.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export declare type Choice = {
|
|
2
2
|
order?: number;
|
|
3
3
|
selected: boolean;
|
|
4
|
-
title
|
|
5
|
-
help
|
|
4
|
+
title?: string;
|
|
5
|
+
help?: string;
|
|
6
6
|
onClick: Function;
|
|
7
|
+
ariaLabel?: string;
|
|
7
8
|
};
|
|
8
9
|
export declare type QcmDrag = {
|
|
9
10
|
type: 'qcmDrag';
|
|
@@ -12,22 +13,20 @@ export declare type QcmDrag = {
|
|
|
12
13
|
};
|
|
13
14
|
export declare type Qcm = {
|
|
14
15
|
type: 'qcm';
|
|
15
|
-
|
|
16
|
-
ariaLabel: string;
|
|
17
|
-
})[];
|
|
16
|
+
answers: Choice[];
|
|
18
17
|
};
|
|
19
18
|
export declare type QcmGraphic = {
|
|
20
19
|
type: 'qcmGraphic';
|
|
21
|
-
|
|
20
|
+
answers: (Choice & {
|
|
22
21
|
image: string;
|
|
23
22
|
})[];
|
|
24
23
|
};
|
|
25
24
|
export declare type FreeText = {
|
|
26
25
|
type: 'freeText';
|
|
27
26
|
placeholder: string;
|
|
28
|
-
value
|
|
27
|
+
value?: string;
|
|
29
28
|
onChange: Function;
|
|
30
|
-
ariaLabel
|
|
29
|
+
ariaLabel?: string;
|
|
31
30
|
};
|
|
32
31
|
export declare type DropDownOption = {
|
|
33
32
|
name: string;
|
|
@@ -37,26 +36,38 @@ export declare type DropDownOption = {
|
|
|
37
36
|
};
|
|
38
37
|
export declare type DropDown = {
|
|
39
38
|
type: 'dropDown';
|
|
40
|
-
onChange
|
|
39
|
+
onChange?: Function;
|
|
41
40
|
options: DropDownOption[];
|
|
42
|
-
theme
|
|
41
|
+
theme?: 'filter' | 'invalid' | 'header' | 'mooc' | 'question' | 'sort' | 'thematiques' | 'player' | 'template' | 'coorpmanager';
|
|
43
42
|
};
|
|
44
43
|
export declare type QuestionRange = {
|
|
45
44
|
type: 'slider';
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
placeholder?: string;
|
|
46
|
+
onChange?: Function;
|
|
47
|
+
onChangeEnd?: Function;
|
|
48
|
+
multi?: boolean;
|
|
49
49
|
value: number | number[];
|
|
50
50
|
title: string;
|
|
51
51
|
minLabel: string;
|
|
52
52
|
maxLabel: string;
|
|
53
53
|
};
|
|
54
|
+
export declare type TextTemplate = {
|
|
55
|
+
type: 'text';
|
|
56
|
+
name: string;
|
|
57
|
+
placeholder: string;
|
|
58
|
+
value?: string;
|
|
59
|
+
onChange: Function;
|
|
60
|
+
};
|
|
61
|
+
export declare type SelectionTemplate = {
|
|
62
|
+
type: 'select';
|
|
63
|
+
name: string;
|
|
64
|
+
onChange?: Function;
|
|
65
|
+
options?: DropDownOption[];
|
|
66
|
+
};
|
|
54
67
|
export declare type Template = {
|
|
55
68
|
type: 'template';
|
|
56
69
|
template: string;
|
|
57
|
-
|
|
58
|
-
type?: string;
|
|
59
|
-
})[];
|
|
70
|
+
answers: (TextTemplate | SelectionTemplate)[];
|
|
60
71
|
};
|
|
61
72
|
export declare type AnswerUI = {
|
|
62
73
|
model: QcmDrag | Qcm | QcmGraphic | FreeText | DropDown | QuestionRange | Template;
|
|
@@ -2,7 +2,7 @@ import { AppOptions } from '../../types/common';
|
|
|
2
2
|
export declare const NAVIGATE_TO = "@@navigation/NAVIGATE_TO";
|
|
3
3
|
export declare const NAVIGATE_BACK = "@@navigation/NAVIGATE_BACK";
|
|
4
4
|
export declare const START_APP = "@@navigation/START_APP";
|
|
5
|
-
export declare type ViewPath = 'skills' | 'onboarding' | 'slides';
|
|
5
|
+
export declare type ViewPath = 'skills' | 'onboarding' | 'slides' | 'loader';
|
|
6
6
|
export declare type NavigateTo = {
|
|
7
7
|
type: '@@navigation/NAVIGATE_TO';
|
|
8
8
|
payload: ViewPath;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { AnswerUI } from '../types/slides';
|
|
2
|
+
import { SlideFromAPI } from '../types/common';
|
|
3
|
+
declare type UiUtils = {
|
|
4
|
+
translate: (text: string) => string;
|
|
5
|
+
};
|
|
6
|
+
export declare const getQuestionType: (slide: SlideFromAPI) => SlideFromAPI['question']['type'];
|
|
7
|
+
export declare const mapApiSlideToUi: (uiUtils: UiUtils) => (slide: SlideFromAPI) => AnswerUI['model'] | void;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { concat, divide, get, getOr, head, includes, indexOf, isEmpty, map, noop, pipe, rangeStep, size, toInteger } from 'lodash/fp';
|
|
2
|
+
var qcmProps = function (question) {
|
|
3
|
+
// TODO: EDIT_CHOICES -> getAnswerValues
|
|
4
|
+
var answers = [];
|
|
5
|
+
return {
|
|
6
|
+
type: 'qcm',
|
|
7
|
+
answers: map(function (choice) { return ({
|
|
8
|
+
title: choice.label,
|
|
9
|
+
selected: includes(choice.label, answers),
|
|
10
|
+
// TODO: EDIT_CHOICES
|
|
11
|
+
onClick: noop,
|
|
12
|
+
ariaLabel: choice.label
|
|
13
|
+
}); },
|
|
14
|
+
// TODO: EDIT_CHOICES -> getChoices
|
|
15
|
+
question.content.choices)
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
var qcmDragProps = function (question) {
|
|
19
|
+
// mock the answers
|
|
20
|
+
// TODO: EDIT_CHOICES -> getAnswerValues
|
|
21
|
+
var answers = [];
|
|
22
|
+
return {
|
|
23
|
+
type: 'qcmDrag',
|
|
24
|
+
answers: map(function (choice) {
|
|
25
|
+
var indexInAnswer = indexOf(choice.label, answers);
|
|
26
|
+
return {
|
|
27
|
+
title: choice.label,
|
|
28
|
+
selected: indexInAnswer !== -1,
|
|
29
|
+
order: indexInAnswer,
|
|
30
|
+
// TODO: EDIT_CHOICES
|
|
31
|
+
onClick: noop
|
|
32
|
+
};
|
|
33
|
+
// TODO: EDIT_CHOICES -> getChoices
|
|
34
|
+
}, question.content.choices)
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
var qcmGraphicProps = function (question) {
|
|
38
|
+
// mock the answers
|
|
39
|
+
// TODO: EDIT_CHOICES -> getAnswerValues
|
|
40
|
+
var answers = [];
|
|
41
|
+
return {
|
|
42
|
+
type: 'qcmGraphic',
|
|
43
|
+
answers: map(function (choice) { return ({
|
|
44
|
+
title: choice.label,
|
|
45
|
+
image: get('media.src.0.url', choice),
|
|
46
|
+
selected: includes(choice.label, answers),
|
|
47
|
+
// TODO: EDIT_CHOICES
|
|
48
|
+
onClick: noop
|
|
49
|
+
}); },
|
|
50
|
+
// TODO: EDIT_CHOICES -> getChoices
|
|
51
|
+
question.content.choices)
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
var templateTextProps = function (uiUtils) {
|
|
55
|
+
return function (choice, index) {
|
|
56
|
+
var translate = uiUtils.translate;
|
|
57
|
+
// TODO: EDIT_CHOICES -> getAnswerValues
|
|
58
|
+
var answers = [];
|
|
59
|
+
return {
|
|
60
|
+
type: 'text',
|
|
61
|
+
name: getOr('', 'name', choice),
|
|
62
|
+
placeholder: translate('Type here'),
|
|
63
|
+
value: get(index, answers),
|
|
64
|
+
// TODO: EDIT_CHOICES
|
|
65
|
+
onChange: noop
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
// const getItemText = (item: ChoiceFromAPI['items'][0]): item['text'] is string =>
|
|
70
|
+
var templateSelectProps = function (uiUtils) {
|
|
71
|
+
return function (choice, index) {
|
|
72
|
+
var translate = uiUtils.translate;
|
|
73
|
+
// TODO: EDIT_CHOICES -> getAnswerValues
|
|
74
|
+
var answers = [];
|
|
75
|
+
var answer = get(index, answers);
|
|
76
|
+
var temporaryOption = {
|
|
77
|
+
name: translate('Select an answer'),
|
|
78
|
+
value: '',
|
|
79
|
+
validOption: false,
|
|
80
|
+
selected: true
|
|
81
|
+
};
|
|
82
|
+
var selectOptions = choice.items.map(function (item) {
|
|
83
|
+
var itemText = getOr('', 'text', item);
|
|
84
|
+
return {
|
|
85
|
+
name: itemText,
|
|
86
|
+
value: itemText,
|
|
87
|
+
validOption: true,
|
|
88
|
+
selected: itemText === answer
|
|
89
|
+
};
|
|
90
|
+
});
|
|
91
|
+
return {
|
|
92
|
+
type: 'select',
|
|
93
|
+
name: getOr('', 'name', choice),
|
|
94
|
+
// TODO: EDIT_CHOICES
|
|
95
|
+
onChange: noop,
|
|
96
|
+
options: isEmpty(answer) ? concat([temporaryOption], selectOptions) : selectOptions
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
var templateProps = function (uiUtils) {
|
|
101
|
+
var templateTextProps_ = templateTextProps(uiUtils);
|
|
102
|
+
var templateSelectProps_ = templateSelectProps(uiUtils);
|
|
103
|
+
return function (question) {
|
|
104
|
+
// TODO: EDIT_CHOICES -> getChoices
|
|
105
|
+
var choices = question.content.choices;
|
|
106
|
+
return {
|
|
107
|
+
type: 'template',
|
|
108
|
+
template: get('content.template', question),
|
|
109
|
+
answers: choices.map(function (choice, index) {
|
|
110
|
+
return choice.type === 'text'
|
|
111
|
+
? templateTextProps_(choice, index)
|
|
112
|
+
: templateSelectProps_(choice, index);
|
|
113
|
+
})
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
var basicProps = function (uiUtils) {
|
|
118
|
+
var translate = uiUtils.translate;
|
|
119
|
+
// TODO: EDIT_CHOICES -> getAnswerValues
|
|
120
|
+
var answers = [];
|
|
121
|
+
return function () {
|
|
122
|
+
return {
|
|
123
|
+
type: 'freeText',
|
|
124
|
+
placeholder: translate('Type here'),
|
|
125
|
+
value: head(answers),
|
|
126
|
+
// TODO: EDIT_CHOICES
|
|
127
|
+
onChange: noop
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
var sliderProps = function (question) {
|
|
132
|
+
var values = rangeStep(getOr(1, 'content.step', question), getOr(Number.NaN, 'content.min', question), getOr(Number.NaN, 'content.max', question) + 1
|
|
133
|
+
// Lodash doesn't infere the type very well here
|
|
134
|
+
);
|
|
135
|
+
// TODO: EDIT_CHOICES -> getAnswerValues
|
|
136
|
+
var answers = [];
|
|
137
|
+
var stateValue = head(answers);
|
|
138
|
+
var currentValue = toInteger(stateValue);
|
|
139
|
+
var indexValue = indexOf(currentValue, values);
|
|
140
|
+
var sliderPosition = divide(indexValue, size(values) - 1);
|
|
141
|
+
var unitLabel = get('question.content.unitLabel', question);
|
|
142
|
+
return {
|
|
143
|
+
type: 'slider',
|
|
144
|
+
placeholder: question.explanation,
|
|
145
|
+
minLabel: "".concat(question.content.min, " ").concat(unitLabel),
|
|
146
|
+
maxLabel: "".concat(question.content.max, " ").concat(unitLabel),
|
|
147
|
+
title: "".concat(currentValue, " ").concat(question.content.unitLabel),
|
|
148
|
+
value: sliderPosition,
|
|
149
|
+
// TODO: EDIT_CHOICES
|
|
150
|
+
onChange: noop
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
export var getQuestionType = function (slide) {
|
|
154
|
+
return slide.question.type;
|
|
155
|
+
};
|
|
156
|
+
export var mapApiSlideToUi = function (uiUtils) {
|
|
157
|
+
var basicProps_ = basicProps(uiUtils);
|
|
158
|
+
var templateProps_ = templateProps(uiUtils);
|
|
159
|
+
return function (slide) {
|
|
160
|
+
if (!slide) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
var type = getQuestionType(slide);
|
|
164
|
+
switch (type) {
|
|
165
|
+
case 'qcm':
|
|
166
|
+
return pipe(get('question'), qcmProps)(slide);
|
|
167
|
+
case 'qcmGraphic':
|
|
168
|
+
return pipe(get('question'), qcmGraphicProps)(slide);
|
|
169
|
+
case 'qcmDrag':
|
|
170
|
+
return pipe(get('question'), qcmDragProps)(slide);
|
|
171
|
+
case 'basic':
|
|
172
|
+
return basicProps_();
|
|
173
|
+
case 'template':
|
|
174
|
+
return pipe(get('question'), templateProps_)(slide);
|
|
175
|
+
case 'slider':
|
|
176
|
+
return pipe(get('question'), sliderProps)(slide);
|
|
177
|
+
default:
|
|
178
|
+
throw new Error("".concat(type, " is not an handled question.type"));
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import noop from 'lodash/fp/noop';
|
|
2
|
+
export var freeTextSlide = {
|
|
3
|
+
id: 'sli_E1Pcl-XAYbi',
|
|
4
|
+
universalRef: 'sli_E1Pcl-XAYbi',
|
|
5
|
+
question: {
|
|
6
|
+
type: 'basic',
|
|
7
|
+
header: 'sli_E1Pcl-XAYbi.header',
|
|
8
|
+
explanation: 'sli_E1Pcl-XAYbi.explanation',
|
|
9
|
+
content: {
|
|
10
|
+
label: 'sli_E1Pcl-XAYbi.label',
|
|
11
|
+
placeholder: 'sli_E1Pcl-XAYbi.placeholder',
|
|
12
|
+
id: 'sli_E1Pcl-XAYbi.choice_1'
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
clue: 'sli_E1Pcl-XAYbi.clue',
|
|
16
|
+
klf: 'sli_E1Pcl-XAYbi.klf',
|
|
17
|
+
tips: 'sli_E1Pcl-XAYbi.tips'
|
|
18
|
+
};
|
|
19
|
+
export var freeTextUISlide = {
|
|
20
|
+
type: 'freeText',
|
|
21
|
+
placeholder: 'Type here',
|
|
22
|
+
value: undefined,
|
|
23
|
+
onChange: noop
|
|
24
|
+
};
|