@coorpacademy/app-review 0.4.5 → 0.5.1-alpha.26
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/next-slide.d.ts +4 -2
- package/es/actions/ui/next-slide.js +7 -4
- package/es/actions/ui/quit-popin.d.ts +8 -0
- package/es/actions/ui/quit-popin.js +8 -0
- package/es/actions/ui/slides.d.ts +2 -3
- package/es/common/index.d.ts +2 -0
- package/es/common/index.js +9 -0
- package/es/reducers/index.d.ts +7 -1
- package/es/reducers/ui/answers.d.ts +2 -2
- package/es/reducers/ui/current-slide-ref.d.ts +3 -2
- package/es/reducers/ui/current-slide-ref.js +3 -0
- package/es/reducers/ui/index.d.ts +11 -1
- package/es/reducers/ui/index.js +5 -1
- package/es/reducers/ui/positions.d.ts +5 -0
- package/es/reducers/ui/positions.js +25 -0
- package/es/reducers/ui/quit-popin.d.ts +4 -0
- package/es/reducers/ui/quit-popin.js +16 -0
- package/es/reducers/ui/slide.d.ts +2 -2
- package/es/views/slides/index.d.ts +19 -2
- package/es/views/slides/index.js +45 -25
- package/lib/actions/ui/next-slide.d.ts +4 -2
- package/lib/actions/ui/next-slide.js +7 -4
- package/lib/actions/ui/quit-popin.d.ts +8 -0
- package/lib/actions/ui/quit-popin.js +8 -0
- package/lib/actions/ui/slides.d.ts +2 -3
- package/lib/common/index.d.ts +2 -0
- package/lib/common/index.js +9 -0
- package/lib/reducers/index.d.ts +7 -1
- package/lib/reducers/ui/answers.d.ts +2 -2
- package/lib/reducers/ui/current-slide-ref.d.ts +3 -2
- package/lib/reducers/ui/current-slide-ref.js +3 -0
- package/lib/reducers/ui/index.d.ts +11 -1
- package/lib/reducers/ui/index.js +5 -1
- package/lib/reducers/ui/positions.d.ts +5 -0
- package/lib/reducers/ui/positions.js +26 -0
- package/lib/reducers/ui/quit-popin.d.ts +4 -0
- package/lib/reducers/ui/quit-popin.js +17 -0
- package/lib/reducers/ui/slide.d.ts +2 -2
- package/lib/views/slides/index.d.ts +19 -2
- package/lib/views/slides/index.js +47 -26
- package/package.json +10 -11
- package/src/actions/api/test/fetch-correction.test.ts +3 -1
- package/src/actions/api/test/fetch-rank.test.ts +3 -1
- package/src/actions/api/test/fetch-skills.test.ts +3 -1
- package/src/actions/api/test/fetch-slide.test.ts +3 -1
- package/src/actions/api/test/post-answer.test.ts +6 -2
- package/src/actions/api/test/post-progression.test.ts +3 -1
- package/src/actions/data/test/token.test.ts +3 -1
- package/src/actions/ui/next-slide.ts +15 -9
- package/src/actions/ui/quit-popin.ts +10 -0
- package/src/actions/ui/slides.ts +2 -4
- package/src/actions/ui/test/answers.test.ts +3 -1
- package/src/actions/ui/test/next-slide.test.ts +9 -3
- package/src/actions/ui/test/quit-popin.test.ts +38 -0
- package/src/actions/ui/test/slides.test.ts +3 -1
- package/src/common/index.ts +12 -0
- package/src/common/test/get-progression-slide-ref.test.ts +35 -0
- package/src/reducers/ui/answers.ts +2 -2
- package/src/reducers/ui/current-slide-ref.ts +5 -2
- package/src/reducers/ui/index.ts +7 -1
- package/src/reducers/ui/positions.ts +32 -0
- package/src/reducers/ui/quit-popin.ts +22 -0
- package/src/reducers/ui/slide.ts +7 -2
- package/src/reducers/ui/test/answers.test.ts +3 -1
- package/src/reducers/ui/test/current-slide-ref.test.ts +2 -2
- package/src/reducers/ui/test/positions.test.ts +68 -0
- package/src/reducers/ui/test/quit-popin.test.ts +24 -0
- package/src/reducers/ui/test/slide.test.ts +3 -1
- package/src/types/common.ts +0 -1
- package/src/views/skills/test/skills.test.ts +6 -2
- package/src/views/slides/index.ts +70 -32
- package/src/views/slides/test/header.on-click.test.ts +42 -0
- package/src/views/slides/test/index.test.ts +90 -42
- package/src/views/slides/test/on-quit-popin.on-click.test.ts +64 -0
- package/src/views/slides/test/slide.free-text.on-change.test.ts +3 -2
- package/src/views/slides/test/slide.next-slide.on-click.test.ts +10 -2
- package/src/views/slides/test/slide.qcm-drag.on-click.test.ts +3 -2
- package/src/views/slides/test/slide.qcm-graphic.on-click.test.ts +3 -2
- package/src/views/slides/test/slide.qcm.on-click.test.ts +3 -2
- package/src/views/slides/test/slide.slider.on-change.test.ts +3 -2
- package/src/views/slides/test/slide.slider.on-slider-change.test.ts +17 -4
- package/src/views/slides/test/slide.template.on-change.test.ts +11 -11
- package/es/actions/ui/is-fetching.d.ts +0 -10
- package/es/actions/ui/is-fetching.js +0 -4
- package/lib/actions/ui/is-fetching.d.ts +0 -10
- package/lib/actions/ui/is-fetching.js +0 -4
- package/src/actions/ui/is-fetching.ts +0 -13
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import {createTestStore} from '../../test/create-test-store';
|
|
3
|
+
import type {StoreState} from '../../../reducers';
|
|
4
|
+
import {closeQuitPopin, CLOSE_POPIN, openQuitPopin, OPEN_POPIN} from '../quit-popin';
|
|
5
|
+
import {services} from '../../../test/util/services.mock';
|
|
6
|
+
|
|
7
|
+
const initialState: StoreState = {
|
|
8
|
+
data: {
|
|
9
|
+
progression: null,
|
|
10
|
+
slides: {},
|
|
11
|
+
skills: [],
|
|
12
|
+
token: '1234',
|
|
13
|
+
corrections: {},
|
|
14
|
+
rank: {}
|
|
15
|
+
},
|
|
16
|
+
ui: {
|
|
17
|
+
currentSlideRef: '',
|
|
18
|
+
navigation: [],
|
|
19
|
+
answers: {},
|
|
20
|
+
slide: {},
|
|
21
|
+
positions: [],
|
|
22
|
+
showQuitPopin: false
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
test('should dispatch OPEN_POPIN when openQuitPopin action is called', async t => {
|
|
27
|
+
const expectedAction = [{type: OPEN_POPIN}];
|
|
28
|
+
|
|
29
|
+
const {dispatch} = createTestStore(t, initialState, services, expectedAction);
|
|
30
|
+
await dispatch(openQuitPopin);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('should dispatch CLOSE_POPIN when closeQuitPopin action is called', async t => {
|
|
34
|
+
const expectedAction = [{type: CLOSE_POPIN}];
|
|
35
|
+
|
|
36
|
+
const {dispatch} = createTestStore(t, initialState, services, expectedAction);
|
|
37
|
+
await dispatch(closeQuitPopin);
|
|
38
|
+
});
|
package/src/common/index.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import concat from 'lodash/fp/concat';
|
|
2
|
+
import slice from 'lodash/fp/slice';
|
|
3
|
+
import type {ProgressionFromAPI} from '../types/common';
|
|
4
|
+
|
|
1
5
|
export const VIEWS: {
|
|
2
6
|
readonly skills: 'skills';
|
|
3
7
|
readonly onboarding: 'onboarding';
|
|
@@ -11,3 +15,11 @@ export const VIEWS: {
|
|
|
11
15
|
export const slideIndexes = ['0', '1', '2', '3', '4'] as const;
|
|
12
16
|
|
|
13
17
|
export type SlideIndexes = typeof slideIndexes[number];
|
|
18
|
+
|
|
19
|
+
export const getProgressionSlidesRefs = (progression: ProgressionFromAPI): string[] => {
|
|
20
|
+
if (progression.state.step.current <= 5) {
|
|
21
|
+
const slideRef = progression.state.nextContent.ref;
|
|
22
|
+
return concat(progression.state.slides, [slideRef]);
|
|
23
|
+
}
|
|
24
|
+
return slice(0, 5, progression.state.slides);
|
|
25
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import {
|
|
3
|
+
postAnswerResponses,
|
|
4
|
+
postProgressionResponse,
|
|
5
|
+
progressionSlideWithPendingSlide
|
|
6
|
+
} from '../../test/util/services.mock';
|
|
7
|
+
import {templateSlide} from '../../views/slides/test/fixtures/template';
|
|
8
|
+
import {getProgressionSlidesRefs} from '..';
|
|
9
|
+
|
|
10
|
+
test('should return first slide for a created slide', t => {
|
|
11
|
+
const slides = getProgressionSlidesRefs(postProgressionResponse);
|
|
12
|
+
t.deepEqual(slides, ['sli_VJYjJnJhg']);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('should return all 5 slides when user is going to answer the 5th slide', t => {
|
|
16
|
+
const slides = getProgressionSlidesRefs(postAnswerResponses[templateSlide._id]);
|
|
17
|
+
t.deepEqual(slides, [
|
|
18
|
+
'sli_VJYjJnJhg',
|
|
19
|
+
'sli_VkSQroQnx',
|
|
20
|
+
'sli_N1XACJobn',
|
|
21
|
+
'sli_VkAzsCLKb',
|
|
22
|
+
'sli_N13-hG3kX'
|
|
23
|
+
]);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('should return all 5 slides when progression has a nextContent ref that is a pending slide', t => {
|
|
27
|
+
const slides = getProgressionSlidesRefs(progressionSlideWithPendingSlide);
|
|
28
|
+
t.deepEqual(slides, [
|
|
29
|
+
'sli_VJYjJnJhg',
|
|
30
|
+
'sli_VkSQroQnx',
|
|
31
|
+
'sli_N1XACJobn',
|
|
32
|
+
'sli_VkAzsCLKb',
|
|
33
|
+
'sli_N13-hG3kX'
|
|
34
|
+
]);
|
|
35
|
+
});
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
EDIT_SLIDER,
|
|
9
9
|
EDIT_TEMPLATE
|
|
10
10
|
} from '../../actions/ui/answers';
|
|
11
|
-
import {
|
|
11
|
+
import {NextSlideAction, NEXT_SLIDE} from '../../actions/ui/next-slide';
|
|
12
12
|
|
|
13
13
|
export type UISlideAnswer = string[];
|
|
14
14
|
|
|
@@ -19,7 +19,7 @@ export const initialState: UIAnswerState = {};
|
|
|
19
19
|
const reducer = (
|
|
20
20
|
// eslint-disable-next-line default-param-last
|
|
21
21
|
state: UIAnswerState = initialState,
|
|
22
|
-
action: EditAnswerAction |
|
|
22
|
+
action: EditAnswerAction | NextSlideAction
|
|
23
23
|
): UIAnswerState => {
|
|
24
24
|
switch (action.type) {
|
|
25
25
|
case EDIT_QCM:
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {NEXT_SLIDE, NextSlideAction} from '../../actions/ui/next-slide';
|
|
2
|
+
import {SET_CURRENT_SLIDE, SetCurrentSlideAction} from '../../actions/ui/slides';
|
|
2
3
|
|
|
3
4
|
export type CurrentSlideRefState = string;
|
|
4
5
|
|
|
5
6
|
const reducer = (
|
|
6
7
|
// eslint-disable-next-line default-param-last
|
|
7
8
|
state: CurrentSlideRefState = '',
|
|
8
|
-
action:
|
|
9
|
+
action: SetCurrentSlideAction | NextSlideAction
|
|
9
10
|
): CurrentSlideRefState => {
|
|
10
11
|
switch (action.type) {
|
|
12
|
+
case NEXT_SLIDE:
|
|
13
|
+
return action.payload.nextSlideRef;
|
|
11
14
|
case SET_CURRENT_SLIDE: {
|
|
12
15
|
return action.payload._id;
|
|
13
16
|
}
|
package/src/reducers/ui/index.ts
CHANGED
|
@@ -3,18 +3,24 @@ import {combineReducers} from 'redux';
|
|
|
3
3
|
import currentSlideRef, {CurrentSlideRefState} from './current-slide-ref';
|
|
4
4
|
import navigation, {NavigationState} from './navigation';
|
|
5
5
|
import answers, {UIAnswerState} from './answers';
|
|
6
|
+
import positions, {UIPositionState} from './positions';
|
|
6
7
|
import slide, {UISlideState} from './slide';
|
|
8
|
+
import showQuitPopin, {type ShowQuitPopinState} from './quit-popin';
|
|
7
9
|
|
|
8
10
|
export type UIState = {
|
|
9
11
|
currentSlideRef: CurrentSlideRefState;
|
|
10
12
|
navigation: NavigationState;
|
|
11
13
|
answers: UIAnswerState;
|
|
12
14
|
slide: UISlideState;
|
|
15
|
+
positions: UIPositionState;
|
|
16
|
+
showQuitPopin: ShowQuitPopinState;
|
|
13
17
|
};
|
|
14
18
|
|
|
15
19
|
export default combineReducers({
|
|
16
20
|
currentSlideRef,
|
|
17
21
|
navigation,
|
|
18
22
|
answers,
|
|
19
|
-
slide
|
|
23
|
+
slide,
|
|
24
|
+
positions,
|
|
25
|
+
showQuitPopin
|
|
20
26
|
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import findIndex from 'lodash/fp/findIndex';
|
|
2
|
+
import map from 'lodash/fp/map';
|
|
3
|
+
import set from 'lodash/fp/set';
|
|
4
|
+
import {ReceivedProgression, POST_PROGRESSION_SUCCESS} from '../../actions/api/post-progression';
|
|
5
|
+
import {NextSlideAction, NEXT_SLIDE} from '../../actions/ui/next-slide';
|
|
6
|
+
|
|
7
|
+
export type UIPositionState = number[];
|
|
8
|
+
const initialState: UIPositionState = [0, 1, 2, 3, 4];
|
|
9
|
+
|
|
10
|
+
const reducer = (
|
|
11
|
+
// eslint-disable-next-line default-param-last
|
|
12
|
+
state: UIPositionState = initialState,
|
|
13
|
+
action: NextSlideAction | ReceivedProgression
|
|
14
|
+
): UIPositionState => {
|
|
15
|
+
switch (action.type) {
|
|
16
|
+
case POST_PROGRESSION_SUCCESS: {
|
|
17
|
+
return initialState;
|
|
18
|
+
}
|
|
19
|
+
case NEXT_SLIDE: {
|
|
20
|
+
const {totalCorrectAnswers, answeredSlides, currentSlideRef, animationType} = action.payload;
|
|
21
|
+
|
|
22
|
+
const nextCurrentSlidePosition = animationType === 'unstack' ? -1 : 4 - totalCorrectAnswers;
|
|
23
|
+
const currentSlideIndex = findIndex(ref => ref === currentSlideRef, answeredSlides);
|
|
24
|
+
const newState = map(position => (position === -1 ? position : position - 1), state);
|
|
25
|
+
return set([`${currentSlideIndex}`], nextCurrentSlidePosition)(newState);
|
|
26
|
+
}
|
|
27
|
+
default:
|
|
28
|
+
return state;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default reducer;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {closeQuitPopin, CLOSE_POPIN, openQuitPopin, OPEN_POPIN} from '../../actions/ui/quit-popin';
|
|
2
|
+
|
|
3
|
+
export type ShowQuitPopinState = boolean;
|
|
4
|
+
|
|
5
|
+
const reducer = (
|
|
6
|
+
// eslint-disable-next-line default-param-last
|
|
7
|
+
state: ShowQuitPopinState = false,
|
|
8
|
+
action: typeof openQuitPopin | typeof closeQuitPopin
|
|
9
|
+
): ShowQuitPopinState => {
|
|
10
|
+
switch (action.type) {
|
|
11
|
+
case OPEN_POPIN: {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
case CLOSE_POPIN: {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
default:
|
|
18
|
+
return state;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default reducer;
|
package/src/reducers/ui/slide.ts
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
import {PostAnswerRequestAction, POST_ANSWER_REQUEST} from '../../actions/api/post-answer';
|
|
16
16
|
import {ReceivedCorrection, CORRECTION_FETCH_SUCCESS} from '../../actions/api/fetch-correction';
|
|
17
17
|
import {FetchSlide, SLIDE_FETCH_REQUEST} from '../../actions/api/fetch-slide';
|
|
18
|
-
import {
|
|
18
|
+
import {NextSlideAction, NEXT_SLIDE} from '../../actions/ui/next-slide';
|
|
19
19
|
import {SlideUIAnimations} from '../../types/slides';
|
|
20
20
|
|
|
21
21
|
export type UISlide = {
|
|
@@ -32,7 +32,12 @@ export const initialState: UISlideState = {};
|
|
|
32
32
|
const reducer = (
|
|
33
33
|
// eslint-disable-next-line default-param-last
|
|
34
34
|
state: UISlideState = initialState,
|
|
35
|
-
action:
|
|
35
|
+
action:
|
|
36
|
+
| FetchSlide
|
|
37
|
+
| PostAnswerRequestAction
|
|
38
|
+
| EditAnswerAction
|
|
39
|
+
| ReceivedCorrection
|
|
40
|
+
| NextSlideAction
|
|
36
41
|
): UISlideState => {
|
|
37
42
|
switch (action.type) {
|
|
38
43
|
case SLIDE_FETCH_REQUEST: {
|
|
@@ -94,7 +94,9 @@ test('should set the next slide ref with an empty array if NEXT_SLIDE action is
|
|
|
94
94
|
payload: {
|
|
95
95
|
currentSlideRef: '1234',
|
|
96
96
|
nextSlideRef: '5678',
|
|
97
|
-
animationType: 'unstack'
|
|
97
|
+
animationType: 'unstack',
|
|
98
|
+
totalCorrectAnswers: 1,
|
|
99
|
+
answeredSlides: ['1234']
|
|
98
100
|
}
|
|
99
101
|
}
|
|
100
102
|
);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import test from 'ava';
|
|
2
2
|
import reducer from '../current-slide-ref';
|
|
3
|
-
import {
|
|
3
|
+
import {SetCurrentSlideAction, SET_CURRENT_SLIDE} from '../../../actions/ui/slides';
|
|
4
4
|
import {freeTextSlide} from '../../../views/slides/test/fixtures/free-text';
|
|
5
5
|
|
|
6
6
|
test('should have initial value', t => {
|
|
7
|
-
const state = reducer(undefined, {} as
|
|
7
|
+
const state = reducer(undefined, {} as SetCurrentSlideAction);
|
|
8
8
|
t.is(state, '');
|
|
9
9
|
});
|
|
10
10
|
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import reducer from '../positions';
|
|
3
|
+
import {POST_PROGRESSION_SUCCESS, ReceivedProgression} from '../../../actions/api/post-progression';
|
|
4
|
+
import {NEXT_SLIDE} from '../../../actions/ui/next-slide';
|
|
5
|
+
|
|
6
|
+
test('should have initial value', t => {
|
|
7
|
+
const state = reducer(undefined, {} as ReceivedProgression);
|
|
8
|
+
t.deepEqual(state, [0, 1, 2, 3, 4]);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test('should reset state to initial value when POST_PROGRESSION_SUCCESS action', t => {
|
|
12
|
+
const state = reducer([-1, -1, -1, -1, -1], {
|
|
13
|
+
type: POST_PROGRESSION_SUCCESS
|
|
14
|
+
} as ReceivedProgression);
|
|
15
|
+
t.deepEqual(state, [0, 1, 2, 3, 4]);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('should set position for currentSlide according to the animationType and the position for the next slides', t => {
|
|
19
|
+
const state = reducer([-1, -1, 0, 1, 2], {
|
|
20
|
+
type: NEXT_SLIDE,
|
|
21
|
+
payload: {
|
|
22
|
+
totalCorrectAnswers: 2,
|
|
23
|
+
answeredSlides: ['sli_1', 'sli_2', 'sli_3'],
|
|
24
|
+
currentSlideRef: 'sli_3',
|
|
25
|
+
nextSlideRef: 'sli_4',
|
|
26
|
+
animationType: 'restack'
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
t.deepEqual(state, [-1, -1, 2, 0, 1]);
|
|
30
|
+
|
|
31
|
+
const _state = reducer([-1, -1, 0, 1, 2], {
|
|
32
|
+
type: NEXT_SLIDE,
|
|
33
|
+
payload: {
|
|
34
|
+
totalCorrectAnswers: 2,
|
|
35
|
+
answeredSlides: ['sli_1', 'sli_2', 'sli_3'],
|
|
36
|
+
currentSlideRef: 'sli_3',
|
|
37
|
+
nextSlideRef: 'sli_4',
|
|
38
|
+
animationType: 'unstack'
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
t.deepEqual(_state, [-1, -1, -1, 0, 1]);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('should set position for currentSlide according to the animationType and the position for the next slides when answering already bad slide', t => {
|
|
45
|
+
const state = reducer([-1, 2, -1, 0, 1], {
|
|
46
|
+
type: NEXT_SLIDE,
|
|
47
|
+
payload: {
|
|
48
|
+
totalCorrectAnswers: 2,
|
|
49
|
+
answeredSlides: ['sli_1', 'sli_2', 'sli_3', 'sli_4', 'sli_5'],
|
|
50
|
+
currentSlideRef: 'sli_4',
|
|
51
|
+
nextSlideRef: 'sli_5',
|
|
52
|
+
animationType: 'restack'
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
t.deepEqual(state, [-1, 1, -1, 2, 0]);
|
|
56
|
+
|
|
57
|
+
const _state = reducer([-1, 1, -1, 2, 0], {
|
|
58
|
+
type: NEXT_SLIDE,
|
|
59
|
+
payload: {
|
|
60
|
+
totalCorrectAnswers: 2,
|
|
61
|
+
answeredSlides: ['sli_1', 'sli_2', 'sli_3', 'sli_4', 'sli_5'],
|
|
62
|
+
currentSlideRef: 'sli_5',
|
|
63
|
+
nextSlideRef: 'sli_1',
|
|
64
|
+
animationType: 'unstack'
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
t.deepEqual(_state, [-1, 0, -1, 1, -1]);
|
|
68
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import {CLOSE_POPIN, OPEN_POPIN} from '../../../actions/ui/quit-popin';
|
|
3
|
+
import reducer from '../quit-popin';
|
|
4
|
+
|
|
5
|
+
test('should have showQuitPopin intial value to false', t => {
|
|
6
|
+
const state = reducer(undefined, {
|
|
7
|
+
type: '@@ui/CLOSE_POPIN'
|
|
8
|
+
});
|
|
9
|
+
t.is(state, false);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test('should set showQuitPopin to true if OPEN_POPIN is received', t => {
|
|
13
|
+
const state = reducer(false, {
|
|
14
|
+
type: OPEN_POPIN
|
|
15
|
+
});
|
|
16
|
+
t.is(state, true);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('should set showQuitPopin to false if CLOSE_POPIN is received', t => {
|
|
20
|
+
const state = reducer(true, {
|
|
21
|
+
type: CLOSE_POPIN
|
|
22
|
+
});
|
|
23
|
+
t.is(state, false);
|
|
24
|
+
});
|
|
@@ -66,7 +66,9 @@ test('should set animateCorrectionPopin to false and animationType to unstack or
|
|
|
66
66
|
payload: {
|
|
67
67
|
currentSlideRef: '1234',
|
|
68
68
|
nextSlideRef: '5678',
|
|
69
|
-
animationType: 'unstack'
|
|
69
|
+
animationType: 'unstack',
|
|
70
|
+
totalCorrectAnswers: 1,
|
|
71
|
+
answeredSlides: ['1234']
|
|
70
72
|
}
|
|
71
73
|
}
|
|
72
74
|
);
|
package/src/types/common.ts
CHANGED
|
@@ -16,9 +16,11 @@ test('should create initial props when there are no skills on the state', t => {
|
|
|
16
16
|
},
|
|
17
17
|
ui: {
|
|
18
18
|
currentSlideRef: '',
|
|
19
|
+
positions: [0, 1, 2, 3, 4],
|
|
19
20
|
navigation: ['loader', 'skills'],
|
|
20
21
|
answers: {},
|
|
21
|
-
slide: {}
|
|
22
|
+
slide: {},
|
|
23
|
+
showQuitPopin: false
|
|
22
24
|
}
|
|
23
25
|
};
|
|
24
26
|
|
|
@@ -60,9 +62,11 @@ test('should create initial props when skills on the state', t => {
|
|
|
60
62
|
},
|
|
61
63
|
ui: {
|
|
62
64
|
currentSlideRef: '',
|
|
65
|
+
positions: [0, 1, 2, 3, 4],
|
|
63
66
|
navigation: ['loader', 'skills'],
|
|
64
67
|
answers: {},
|
|
65
|
-
slide: {}
|
|
68
|
+
slide: {},
|
|
69
|
+
showQuitPopin: false
|
|
66
70
|
}
|
|
67
71
|
};
|
|
68
72
|
|
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import concat from 'lodash/fp/concat';
|
|
2
1
|
import findLast from 'lodash/fp/findLast';
|
|
3
2
|
import get from 'lodash/fp/get';
|
|
4
3
|
import getOr from 'lodash/fp/getOr';
|
|
5
4
|
import last from 'lodash/fp/last';
|
|
6
5
|
import reduce from 'lodash/fp/reduce';
|
|
7
6
|
import set from 'lodash/fp/set';
|
|
8
|
-
import slice from 'lodash/fp/slice';
|
|
9
7
|
import toInteger from 'lodash/fp/toInteger';
|
|
10
8
|
import type {Dispatch} from 'redux';
|
|
11
9
|
import join from 'lodash/fp/join';
|
|
12
|
-
import
|
|
13
|
-
import type {
|
|
10
|
+
import {closeQuitPopin, openQuitPopin} from '../../actions/ui/quit-popin';
|
|
11
|
+
import type {ProgressionAnswerItem} from '../../types/common';
|
|
12
|
+
import {getProgressionSlidesRefs, type SlideIndexes} from '../../common';
|
|
14
13
|
import type {StoreState} from '../../reducers';
|
|
15
14
|
import type {AnswerUI} from '../../types/slides';
|
|
16
15
|
import {postAnswer} from '../../actions/api/post-answer';
|
|
@@ -34,7 +33,6 @@ type StepItem = {
|
|
|
34
33
|
type SlideUIAnimations = 'unstack' | 'restack';
|
|
35
34
|
|
|
36
35
|
export type ReviewSlide = {
|
|
37
|
-
hidden: boolean;
|
|
38
36
|
position: number;
|
|
39
37
|
loading: boolean;
|
|
40
38
|
showCorrectionPopin?: boolean;
|
|
@@ -65,6 +63,16 @@ type CorrectionPopinNext = {
|
|
|
65
63
|
onClick: Function;
|
|
66
64
|
};
|
|
67
65
|
|
|
66
|
+
type QuitPopinButton = {
|
|
67
|
+
label: string;
|
|
68
|
+
type: string;
|
|
69
|
+
customStyle?: {
|
|
70
|
+
color: string;
|
|
71
|
+
};
|
|
72
|
+
handleOnclick: Function;
|
|
73
|
+
ariaLabel: string;
|
|
74
|
+
};
|
|
75
|
+
|
|
68
76
|
export type CorrectionPopinProps = {
|
|
69
77
|
klf?: CorrectionPopinKlf;
|
|
70
78
|
information: CorrectionPopinInformation;
|
|
@@ -73,6 +81,15 @@ export type CorrectionPopinProps = {
|
|
|
73
81
|
type: 'right' | 'wrong';
|
|
74
82
|
};
|
|
75
83
|
|
|
84
|
+
export type QuitPopinProps = {
|
|
85
|
+
content: string;
|
|
86
|
+
icon: string;
|
|
87
|
+
mode: string;
|
|
88
|
+
descriptionText: string;
|
|
89
|
+
firstButton: QuitPopinButton;
|
|
90
|
+
secondButton: QuitPopinButton;
|
|
91
|
+
};
|
|
92
|
+
|
|
76
93
|
export type SlidesViewProps = {
|
|
77
94
|
header: {
|
|
78
95
|
mode: string;
|
|
@@ -113,44 +130,33 @@ export type SlidesViewProps = {
|
|
|
113
130
|
type: string;
|
|
114
131
|
};
|
|
115
132
|
};
|
|
133
|
+
quitPopin?: QuitPopinProps;
|
|
116
134
|
};
|
|
117
135
|
|
|
136
|
+
// TODO replace this, position no more needed
|
|
118
137
|
export const initialState: SlidesStack = {
|
|
119
138
|
'0': {
|
|
120
|
-
hidden: false,
|
|
121
139
|
position: 0,
|
|
122
140
|
loading: true
|
|
123
141
|
},
|
|
124
142
|
'1': {
|
|
125
|
-
hidden: false,
|
|
126
143
|
position: 1,
|
|
127
144
|
loading: true
|
|
128
145
|
},
|
|
129
146
|
'2': {
|
|
130
|
-
hidden: false,
|
|
131
147
|
position: 2,
|
|
132
148
|
loading: true
|
|
133
149
|
},
|
|
134
150
|
'3': {
|
|
135
|
-
hidden: false,
|
|
136
151
|
position: 3,
|
|
137
152
|
loading: true
|
|
138
153
|
},
|
|
139
154
|
'4': {
|
|
140
|
-
hidden: false,
|
|
141
155
|
position: 4,
|
|
142
156
|
loading: true
|
|
143
157
|
}
|
|
144
158
|
};
|
|
145
159
|
|
|
146
|
-
const getProgressionSlidesRefs = (progression: ProgressionFromAPI): string[] => {
|
|
147
|
-
if (progression.state.step.current < 5) {
|
|
148
|
-
const slideRef = progression.state.nextContent.ref;
|
|
149
|
-
return concat(progression.state.slides, [slideRef]);
|
|
150
|
-
}
|
|
151
|
-
return slice(0, 5, progression.state.slides);
|
|
152
|
-
};
|
|
153
|
-
|
|
154
160
|
const buildStackSlides = (state: StoreState, dispatch: Dispatch): SlidesStack => {
|
|
155
161
|
const currentSlideRef = state.ui.currentSlideRef;
|
|
156
162
|
const progression = state.data.progression;
|
|
@@ -160,26 +166,30 @@ const buildStackSlides = (state: StoreState, dispatch: Dispatch): SlidesStack =>
|
|
|
160
166
|
|
|
161
167
|
// @ts-expect-error typescript does not support capped versions of lodash functions
|
|
162
168
|
const stack = reduce.convert({cap: false})(
|
|
163
|
-
(acc: SlidesStack, uiSlide: ReviewSlide,
|
|
164
|
-
const
|
|
165
|
-
|
|
169
|
+
(acc: SlidesStack, uiSlide: ReviewSlide, _index: string): SlidesStack => {
|
|
170
|
+
const index = toInteger(_index);
|
|
171
|
+
|
|
172
|
+
const positions = state.ui.positions;
|
|
173
|
+
const position = positions[index];
|
|
174
|
+
|
|
175
|
+
const slideRef = slideRefs[index];
|
|
176
|
+
if (!slideRef) return set(index, {...uiSlide, position}, acc);
|
|
166
177
|
const slideFromAPI = get(slideRef, state.data.slides);
|
|
167
|
-
if (!slideFromAPI) return set(index, uiSlide, acc);
|
|
178
|
+
if (!slideFromAPI) return set(index, {...uiSlide, position}, acc);
|
|
168
179
|
|
|
169
180
|
const answers = getOr([], ['ui', 'answers', slideRef], state);
|
|
170
181
|
const {questionText, answerUI} = mapApiSlideToUi(dispatch)(slideFromAPI, answers);
|
|
171
|
-
const parentContentTitle
|
|
172
|
-
const parentContentType = getOr('', 'parentContentTitle.type', slideFromAPI);
|
|
182
|
+
const {title: parentContentTitle, type: parentContentType} = slideFromAPI.parentContentTitle;
|
|
173
183
|
|
|
174
184
|
const isCurrentSlideRef = currentSlideRef === slideRef;
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
const showCorrectionPopin =
|
|
178
|
-
|
|
179
|
-
const animationType = get(['ui', 'slide', slideRef, 'animationType'], state);
|
|
185
|
+
const slideUI = get(['ui', 'slide', slideRef], state);
|
|
186
|
+
const animateCorrectionPopin = isCurrentSlideRef && slideUI.animateCorrectionPopin;
|
|
187
|
+
const showCorrectionPopin = isCurrentSlideRef && slideUI.showCorrectionPopin;
|
|
188
|
+
const animationType = slideUI.animationType;
|
|
180
189
|
|
|
181
190
|
const updatedUiSlide = {
|
|
182
191
|
...uiSlide,
|
|
192
|
+
position,
|
|
183
193
|
showCorrectionPopin,
|
|
184
194
|
animateCorrectionPopin,
|
|
185
195
|
loading: false,
|
|
@@ -304,6 +314,33 @@ const getCorrectionPopinProps =
|
|
|
304
314
|
};
|
|
305
315
|
};
|
|
306
316
|
|
|
317
|
+
const buildQuitPopinProps =
|
|
318
|
+
(dispatch: Dispatch) =>
|
|
319
|
+
(onQuitClick: Function): QuitPopinProps => {
|
|
320
|
+
return {
|
|
321
|
+
content: `Tu t'en vas déjà ?`,
|
|
322
|
+
icon: `MoonRocket`,
|
|
323
|
+
mode: 'alert',
|
|
324
|
+
descriptionText: `Tu vas t'en sortir ! Si tu arrêtes maintenant, tu vas perdre ta progression.`,
|
|
325
|
+
firstButton: {
|
|
326
|
+
label: 'Arrêter ma session',
|
|
327
|
+
type: 'tertiary',
|
|
328
|
+
customStyle: {
|
|
329
|
+
color: '#ED3436'
|
|
330
|
+
},
|
|
331
|
+
handleOnclick: onQuitClick,
|
|
332
|
+
ariaLabel: 'Stop session'
|
|
333
|
+
},
|
|
334
|
+
secondButton: {
|
|
335
|
+
label: `Continuer d'apprendre`,
|
|
336
|
+
type: 'primary',
|
|
337
|
+
handleOnclick: (): void => {
|
|
338
|
+
dispatch(closeQuitPopin);
|
|
339
|
+
},
|
|
340
|
+
ariaLabel: 'Continue review'
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
};
|
|
307
344
|
export const mapStateToSlidesProps = (
|
|
308
345
|
state: StoreState,
|
|
309
346
|
dispatch: Dispatch,
|
|
@@ -313,12 +350,12 @@ export const mapStateToSlidesProps = (
|
|
|
313
350
|
const correction = get(['data', 'corrections', currentSlideRef], state);
|
|
314
351
|
const isCorrect = get(['data', 'progression', 'state', 'isCorrect'], state);
|
|
315
352
|
const klf = getOr('', ['data', 'slides', currentSlideRef, 'klf'], state);
|
|
316
|
-
|
|
353
|
+
const showQuitPopin = get(['ui', 'showQuitPopin'], state);
|
|
317
354
|
return {
|
|
318
355
|
header: {
|
|
319
356
|
mode: '__revision_mode',
|
|
320
357
|
skillName: '__agility',
|
|
321
|
-
onQuitClick,
|
|
358
|
+
onQuitClick: () => dispatch(openQuitPopin),
|
|
322
359
|
'aria-label': 'aria-header-wrapper',
|
|
323
360
|
closeButtonAriaLabel: 'aria-close-button',
|
|
324
361
|
steps: buildStepItems(state)
|
|
@@ -336,6 +373,7 @@ export const mapStateToSlidesProps = (
|
|
|
336
373
|
correction && getCorrectionPopinProps(dispatch)(isCorrect, correction.correctAnswer, klf),
|
|
337
374
|
endReview: false
|
|
338
375
|
},
|
|
339
|
-
congrats: undefined
|
|
376
|
+
congrats: undefined,
|
|
377
|
+
quitPopin: showQuitPopin === true ? buildQuitPopinProps(dispatch)(onQuitClick) : undefined
|
|
340
378
|
};
|
|
341
379
|
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import identity from 'lodash/fp/identity';
|
|
3
|
+
import {createTestStore} from '../../../actions/test/create-test-store';
|
|
4
|
+
import {incorrectFreeTextPostAnswerResponse, services} from '../../../test/util/services.mock';
|
|
5
|
+
import {StoreState} from '../../../reducers';
|
|
6
|
+
import {OPEN_POPIN} from '../../../actions/ui/quit-popin';
|
|
7
|
+
import {mapStateToSlidesProps} from '..';
|
|
8
|
+
|
|
9
|
+
const state: StoreState = {
|
|
10
|
+
data: {
|
|
11
|
+
progression: incorrectFreeTextPostAnswerResponse,
|
|
12
|
+
skills: [],
|
|
13
|
+
slides: {
|
|
14
|
+
sli_N1XACJobn: null
|
|
15
|
+
},
|
|
16
|
+
token: '1234',
|
|
17
|
+
corrections: {},
|
|
18
|
+
rank: {}
|
|
19
|
+
},
|
|
20
|
+
ui: {
|
|
21
|
+
currentSlideRef: '',
|
|
22
|
+
navigation: ['loader', 'slides'],
|
|
23
|
+
answers: {},
|
|
24
|
+
positions: [0, 1, 2, 3, 4],
|
|
25
|
+
slide: {
|
|
26
|
+
sli_N1XACJobn: {
|
|
27
|
+
validateButton: false,
|
|
28
|
+
animateCorrectionPopin: false,
|
|
29
|
+
showCorrectionPopin: false
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
showQuitPopin: false
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
test('should dispatch OPEN_POPIN action after a click on close button in header', async t => {
|
|
37
|
+
const expectedAction = [{type: OPEN_POPIN}];
|
|
38
|
+
const {dispatch, getState} = createTestStore(t, state, services, expectedAction);
|
|
39
|
+
const props = mapStateToSlidesProps(getState(), dispatch, identity);
|
|
40
|
+
await props.header.onQuitClick();
|
|
41
|
+
t.pass();
|
|
42
|
+
});
|