@coorpacademy/app-review 0.5.3 → 0.5.4

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 (78) hide show
  1. package/es/actions/api/fetch-rank.js +5 -1
  2. package/es/actions/ui/quit-popin.d.ts +8 -0
  3. package/es/actions/ui/quit-popin.js +8 -0
  4. package/es/index.d.ts +0 -1
  5. package/es/reducers/data/rank.d.ts +4 -3
  6. package/es/reducers/data/rank.js +9 -18
  7. package/es/reducers/index.d.ts +7 -1
  8. package/es/reducers/ui/index.d.ts +11 -1
  9. package/es/reducers/ui/index.js +5 -1
  10. package/es/reducers/ui/navigation.js +0 -1
  11. package/es/reducers/ui/positions.js +3 -1
  12. package/es/reducers/ui/quit-popin.d.ts +4 -0
  13. package/es/reducers/ui/quit-popin.js +16 -0
  14. package/es/reducers/ui/show-congrats.d.ts +5 -0
  15. package/es/reducers/ui/show-congrats.js +20 -0
  16. package/es/reducers/ui/slide.js +4 -1
  17. package/es/types/common.d.ts +10 -8
  18. package/es/views/slides/index.d.ts +67 -19
  19. package/es/views/slides/index.js +122 -6
  20. package/lib/actions/api/fetch-rank.js +7 -1
  21. package/lib/actions/ui/quit-popin.d.ts +8 -0
  22. package/lib/actions/ui/quit-popin.js +8 -0
  23. package/lib/index.d.ts +0 -1
  24. package/lib/reducers/data/rank.d.ts +4 -3
  25. package/lib/reducers/data/rank.js +9 -18
  26. package/lib/reducers/index.d.ts +7 -1
  27. package/lib/reducers/ui/index.d.ts +11 -1
  28. package/lib/reducers/ui/index.js +5 -1
  29. package/lib/reducers/ui/navigation.js +0 -1
  30. package/lib/reducers/ui/positions.js +3 -1
  31. package/lib/reducers/ui/quit-popin.d.ts +4 -0
  32. package/lib/reducers/ui/quit-popin.js +17 -0
  33. package/lib/reducers/ui/show-congrats.d.ts +5 -0
  34. package/lib/reducers/ui/show-congrats.js +21 -0
  35. package/lib/reducers/ui/slide.js +4 -1
  36. package/lib/types/common.d.ts +10 -8
  37. package/lib/views/slides/index.d.ts +67 -19
  38. package/lib/views/slides/index.js +124 -6
  39. package/package.json +3 -3
  40. package/src/actions/api/fetch-rank.ts +8 -1
  41. package/src/actions/api/test/fetch-correction.test.ts +4 -2
  42. package/src/actions/api/test/fetch-rank.test.ts +17 -8
  43. package/src/actions/api/test/fetch-skills.test.ts +4 -2
  44. package/src/actions/api/test/fetch-slide.test.ts +4 -2
  45. package/src/actions/api/test/post-answer.test.ts +9 -10
  46. package/src/actions/api/test/post-progression.test.ts +4 -2
  47. package/src/actions/data/test/token.test.ts +4 -2
  48. package/src/actions/ui/quit-popin.ts +10 -0
  49. package/src/actions/ui/test/answers.test.ts +4 -2
  50. package/src/actions/ui/test/next-slide.test.ts +5 -2
  51. package/src/actions/ui/test/quit-popin.test.ts +39 -0
  52. package/src/actions/ui/test/slides.test.ts +4 -2
  53. package/src/reducers/data/rank.ts +15 -31
  54. package/src/reducers/data/test/rank.test.ts +33 -52
  55. package/src/reducers/ui/index.ts +7 -1
  56. package/src/reducers/ui/navigation.ts +0 -2
  57. package/src/reducers/ui/positions.ts +3 -1
  58. package/src/reducers/ui/quit-popin.ts +22 -0
  59. package/src/reducers/ui/show-congrats.ts +26 -0
  60. package/src/reducers/ui/slide.ts +4 -2
  61. package/src/reducers/ui/test/positions.test.ts +14 -0
  62. package/src/reducers/ui/test/quit-popin.test.ts +24 -0
  63. package/src/reducers/ui/test/show-congrats.test.ts +40 -0
  64. package/src/reducers/ui/test/slide.test.ts +21 -0
  65. package/src/types/common.ts +12 -8
  66. package/src/views/skills/test/skills.test.ts +8 -4
  67. package/src/views/slides/index.ts +207 -27
  68. package/src/views/slides/test/header.on-click.test.ts +46 -0
  69. package/src/views/slides/test/index.test.ts +335 -25
  70. package/src/views/slides/test/on-quit-popin.on-click.test.ts +65 -0
  71. package/src/views/slides/test/slide.free-text.on-change.test.ts +4 -2
  72. package/src/views/slides/test/slide.next-slide.on-click.test.ts +107 -2
  73. package/src/views/slides/test/slide.qcm-drag.on-click.test.ts +4 -2
  74. package/src/views/slides/test/slide.qcm-graphic.on-click.test.ts +4 -2
  75. package/src/views/slides/test/slide.qcm.on-click.test.ts +4 -2
  76. package/src/views/slides/test/slide.slider.on-change.test.ts +4 -2
  77. package/src/views/slides/test/slide.slider.on-slider-change.test.ts +4 -2
  78. package/src/views/slides/test/slide.template.on-change.test.ts +4 -2
@@ -92,17 +92,21 @@ export type ProgressionAnswerItem = {
92
92
  answer: string[];
93
93
  };
94
94
 
95
+ export type SlideContent = {
96
+ type: 'slide';
97
+ ref: string;
98
+ };
99
+
100
+ export type SuccessNodeContent = {
101
+ type: 'success';
102
+ ref: 'successExitNode';
103
+ };
104
+
95
105
  export type ProgressionState = {
96
106
  allAnswers: ProgressionAnswerItem[];
97
- content?: {
98
- ref: string;
99
- type: string;
100
- };
107
+ content?: SlideContent;
101
108
  isCorrect: boolean;
102
- nextContent: {
103
- type: 'success' | 'slide';
104
- ref: 'successExitNode' | string;
105
- };
109
+ nextContent: SlideContent | SuccessNodeContent;
106
110
  pendingSlides: string[];
107
111
  slides: string[];
108
112
  step: {
@@ -12,14 +12,16 @@ test('should create initial props when there are no skills on the state', t => {
12
12
  slides: {},
13
13
  token: '1234',
14
14
  corrections: {},
15
- rank: {}
15
+ rank: {start: Number.NaN, end: Number.NaN}
16
16
  },
17
17
  ui: {
18
+ showCongrats: false,
18
19
  currentSlideRef: '',
19
20
  positions: [0, 1, 2, 3, 4],
20
21
  navigation: ['loader', 'skills'],
21
22
  answers: {},
22
- slide: {}
23
+ slide: {},
24
+ showQuitPopin: false
23
25
  }
24
26
  };
25
27
 
@@ -57,14 +59,16 @@ test('should create initial props when skills on the state', t => {
57
59
  slides: {},
58
60
  token: '1234',
59
61
  corrections: {},
60
- rank: {}
62
+ rank: {start: Number.NaN, end: Number.NaN}
61
63
  },
62
64
  ui: {
65
+ showCongrats: false,
63
66
  currentSlideRef: '',
64
67
  positions: [0, 1, 2, 3, 4],
65
68
  navigation: ['loader', 'skills'],
66
69
  answers: {},
67
- slide: {}
70
+ slide: {},
71
+ showQuitPopin: false
68
72
  }
69
73
  };
70
74
 
@@ -7,12 +7,14 @@ import set from 'lodash/fp/set';
7
7
  import toInteger from 'lodash/fp/toInteger';
8
8
  import type {Dispatch} from 'redux';
9
9
  import join from 'lodash/fp/join';
10
- import type {ProgressionAnswerItem} from '../../types/common';
10
+ import {closeQuitPopin, openQuitPopin} from '../../actions/ui/quit-popin';
11
+ import type {ProgressionAnswerItem, ProgressionFromAPI, SlideContent} from '../../types/common';
11
12
  import {getProgressionSlidesRefs, type SlideIndexes} from '../../common';
12
13
  import type {StoreState} from '../../reducers';
13
14
  import type {AnswerUI} from '../../types/slides';
14
15
  import {postAnswer} from '../../actions/api/post-answer';
15
16
  import {nextSlide} from '../../actions/ui/next-slide';
17
+ import {ProgressionState} from '../../reducers/data/progression';
16
18
  import {mapApiSlideToUi} from './map-api-slide-to-ui';
17
19
 
18
20
  const ICON_VALUES = {
@@ -62,6 +64,16 @@ type CorrectionPopinNext = {
62
64
  onClick: Function;
63
65
  };
64
66
 
67
+ type QuitPopinButton = {
68
+ label: string;
69
+ type: string;
70
+ customStyle?: {
71
+ color: string;
72
+ };
73
+ handleOnclick: Function;
74
+ ariaLabel: string;
75
+ };
76
+
65
77
  export type CorrectionPopinProps = {
66
78
  klf?: CorrectionPopinKlf;
67
79
  information: CorrectionPopinInformation;
@@ -70,6 +82,15 @@ export type CorrectionPopinProps = {
70
82
  type: 'right' | 'wrong';
71
83
  };
72
84
 
85
+ export type QuitPopinProps = {
86
+ content: string;
87
+ icon: string;
88
+ mode: string;
89
+ descriptionText: string;
90
+ firstButton: QuitPopinButton;
91
+ secondButton: QuitPopinButton;
92
+ };
93
+
73
94
  export type SlidesViewProps = {
74
95
  header: {
75
96
  mode: string;
@@ -90,29 +111,75 @@ export type SlidesViewProps = {
90
111
  endReview: boolean;
91
112
  };
92
113
  reviewBackgroundAriaLabel?: string;
93
- congrats?: {
94
- 'aria-label'?: string;
95
- 'data-name'?: string;
96
- animationLottie: unknown;
97
- title: string;
98
- cardCongratsStar: unknown;
99
- cardCongratsRank: unknown;
100
- buttonRevising: {
101
- 'aria-label'?: string;
102
- label: string;
103
- onClick: Function;
104
- type: string;
105
- };
106
- buttonRevisingSkill: {
107
- 'aria-label'?: string;
108
- label: string;
109
- onClick: Function;
110
- type: string;
111
- };
114
+ congrats?: CongratsProps;
115
+ quitPopin?: QuitPopinProps;
116
+ };
117
+
118
+ type LottieAnimationProps = {
119
+ 'aria-label': string;
120
+ 'data-name'?: string;
121
+ animationSrc: string;
122
+ loop?: boolean;
123
+ rendererSettings?: {
124
+ hideOnTransparent?: boolean;
125
+ className?: string;
112
126
  };
127
+ height?: number;
128
+ width?: number;
129
+ className?: number;
130
+ ie11ImageBackup: string;
131
+ backupImageClassName?: string;
132
+ autoplay?: boolean;
133
+ animationControl?: 'play' | 'pause' | 'stop' | 'loading';
134
+ };
135
+
136
+ export type CongratsCardProps = {
137
+ 'aria-label': string;
138
+ 'data-name': string;
139
+ animationLottie: LottieAnimationProps;
140
+ iconAriaLabel: string;
141
+ className?: string;
142
+ cardType: string;
143
+ reviewCardTitle: string;
144
+ reviewCardValue: string;
145
+ rankSuffix?: string;
146
+ timerAnimation: number;
147
+ };
148
+
149
+ export type CongratsProps = {
150
+ 'aria-label': string;
151
+ 'data-name': 'review-congrats';
152
+ animationLottie: LottieAnimationProps;
153
+ title: string;
154
+ cardCongratsStar: CongratsCardProps;
155
+ cardCongratsRank?: CongratsCardProps;
156
+ buttonRevising?: {
157
+ 'aria-label': string;
158
+ label: string;
159
+ onClick: Function;
160
+ type: string;
161
+ };
162
+ buttonRevisingSkill?: {
163
+ label: string;
164
+ 'aria-label': string;
165
+ onClick: Function;
166
+ type: string;
167
+ };
168
+ };
169
+
170
+ const confettiAnimation: LottieAnimationProps = {
171
+ 'aria-label': 'aria lottie',
172
+ 'data-name': 'default-lottie',
173
+ className: undefined,
174
+ animationSrc: 'https://static-staging.coorpacademy.com/animations/review/confetti.json',
175
+ loop: undefined,
176
+ autoplay: true,
177
+ rendererSettings: {
178
+ hideOnTransparent: false
179
+ },
180
+ ie11ImageBackup: 'https://static-staging.coorpacademy.com/animations/review/conffeti_congrats.svg'
113
181
  };
114
182
 
115
- // TODO replace this, position no more needed
116
183
  export const initialState: SlidesStack = {
117
184
  '0': {
118
185
  position: 0,
@@ -136,8 +203,18 @@ export const initialState: SlidesStack = {
136
203
  }
137
204
  };
138
205
 
206
+ const getCurrentSlideRef = (state: StoreState): string => {
207
+ const currentSlideRef = get(['ui', 'currentSlideRef'], state);
208
+ const endReview = currentSlideRef === 'successExitNode';
209
+
210
+ if (!endReview) return currentSlideRef;
211
+ const progression = state.data.progression as ProgressionFromAPI;
212
+ const content = progression.state.content as SlideContent;
213
+ return content.ref;
214
+ };
215
+
139
216
  const buildStackSlides = (state: StoreState, dispatch: Dispatch): SlidesStack => {
140
- const currentSlideRef = state.ui.currentSlideRef;
217
+ const currentSlideRef = getCurrentSlideRef(state);
141
218
  const progression = state.data.progression;
142
219
 
143
220
  if (!currentSlideRef || !progression) return initialState;
@@ -293,21 +370,123 @@ const getCorrectionPopinProps =
293
370
  };
294
371
  };
295
372
 
373
+ const buildQuitPopinProps =
374
+ (dispatch: Dispatch) =>
375
+ (onQuitClick: Function): QuitPopinProps => {
376
+ return {
377
+ content: `Tu t'en vas déjà ?`,
378
+ icon: `MoonRocket`,
379
+ mode: 'alert',
380
+ descriptionText: `Tu vas t'en sortir ! Si tu arrêtes maintenant, tu vas perdre ta progression.`,
381
+ firstButton: {
382
+ label: 'Arrêter ma session',
383
+ type: 'tertiary',
384
+ customStyle: {
385
+ color: '#ED3436'
386
+ },
387
+ handleOnclick: onQuitClick,
388
+ ariaLabel: 'Stop session'
389
+ },
390
+ secondButton: {
391
+ label: `Continuer d'apprendre`,
392
+ type: 'primary',
393
+ handleOnclick: (): void => {
394
+ dispatch(closeQuitPopin);
395
+ },
396
+ ariaLabel: 'Continue review'
397
+ }
398
+ };
399
+ };
400
+
401
+ const buildRankCard = (rank: number): CongratsCardProps => {
402
+ return {
403
+ 'aria-label': 'Review Card Congrats Container',
404
+ 'data-name': 'card-rank',
405
+ animationLottie: {
406
+ 'aria-label': 'aria lottie',
407
+ 'data-name': 'default-lottie',
408
+ animationSrc: 'https://static-staging.coorpacademy.com/animations/review/rank.json',
409
+ loop: true,
410
+ autoplay: true,
411
+ ie11ImageBackup:
412
+ 'https://static-staging.coorpacademy.com/animations/review/rank_icon_congrats.svg'
413
+ },
414
+ cardType: 'card-rank',
415
+ iconAriaLabel: 'Image without information',
416
+ className: undefined,
417
+ reviewCardTitle: 'You are now',
418
+ reviewCardValue: `${rank}`,
419
+ rankSuffix: 'th',
420
+ timerAnimation: 200
421
+ };
422
+ };
423
+
424
+ const buildCongratsProps = (state: StoreState): CongratsProps | undefined => {
425
+ if (!state.ui.showCongrats) return;
426
+
427
+ const progression = state.data.progression as ProgressionFromAPI;
428
+ const stars = progression.state.stars;
429
+ const cardCongratsStar: CongratsCardProps = {
430
+ 'aria-label': 'Review Card Congrats Container',
431
+ 'data-name': 'card-star',
432
+ animationLottie: {
433
+ 'aria-label': 'aria lottie',
434
+ 'data-name': 'default-lottie',
435
+ className: undefined,
436
+ animationSrc: 'https://static-staging.coorpacademy.com/animations/review/star.json',
437
+ loop: false,
438
+ autoplay: undefined,
439
+ rendererSettings: {
440
+ hideOnTransparent: false
441
+ },
442
+ ie11ImageBackup:
443
+ 'https://static-staging.coorpacademy.com/animations/review/stars_icon_congrats.svg'
444
+ },
445
+ iconAriaLabel: 'Image without information',
446
+ className: undefined,
447
+ cardType: 'card-star',
448
+ reviewCardTitle: 'You have won',
449
+ reviewCardValue: `${stars}`,
450
+ timerAnimation: 200
451
+ };
452
+
453
+ const {start, end} = state.data.rank;
454
+ const newRank = start - end;
455
+ const cardCongratsRank = !Number.isNaN(newRank) && newRank > 0 ? buildRankCard(end) : undefined;
456
+
457
+ return {
458
+ 'aria-label': 'Review Congratulations',
459
+ 'data-name': 'review-congrats',
460
+ animationLottie: confettiAnimation,
461
+ title: 'Congratulations!',
462
+ cardCongratsStar,
463
+ cardCongratsRank,
464
+ buttonRevising: undefined, // TODO make boutons and actions
465
+ buttonRevisingSkill: undefined // TODO make boutons and actions
466
+ };
467
+ };
468
+
469
+ const isEndOfProgression = (progression: ProgressionState): boolean => {
470
+ if (!progression) return false;
471
+ return progression.state.nextContent.ref === 'successExitNode';
472
+ };
473
+
296
474
  export const mapStateToSlidesProps = (
297
475
  state: StoreState,
298
476
  dispatch: Dispatch,
299
477
  onQuitClick: Function
300
478
  ): SlidesViewProps => {
301
- const currentSlideRef = get(['ui', 'currentSlideRef'], state);
479
+ const currentSlideRef = getCurrentSlideRef(state);
480
+ const endReview = isEndOfProgression(state.data.progression);
302
481
  const correction = get(['data', 'corrections', currentSlideRef], state);
303
482
  const isCorrect = get(['data', 'progression', 'state', 'isCorrect'], state);
304
483
  const klf = getOr('', ['data', 'slides', currentSlideRef, 'klf'], state);
305
-
484
+ const showQuitPopin = get(['ui', 'showQuitPopin'], state);
306
485
  return {
307
486
  header: {
308
487
  mode: '__revision_mode',
309
488
  skillName: '__agility',
310
- onQuitClick,
489
+ onQuitClick: () => dispatch(openQuitPopin),
311
490
  'aria-label': 'aria-header-wrapper',
312
491
  closeButtonAriaLabel: 'aria-close-button',
313
492
  steps: buildStepItems(state)
@@ -323,8 +502,9 @@ export const mapStateToSlidesProps = (
323
502
  },
324
503
  correctionPopinProps:
325
504
  correction && getCorrectionPopinProps(dispatch)(isCorrect, correction.correctAnswer, klf),
326
- endReview: false
505
+ endReview: endReview && state.ui.showCongrats
327
506
  },
328
- congrats: undefined
507
+ congrats: buildCongratsProps(state),
508
+ quitPopin: showQuitPopin ? buildQuitPopinProps(dispatch)(onQuitClick) : undefined
329
509
  };
330
510
  };
@@ -0,0 +1,46 @@
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: {start: Number.NaN, end: Number.NaN}
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
+ showCongrats: false
34
+ }
35
+ };
36
+
37
+ test('should dispatch OPEN_POPIN action after a click on close button in header', async t => {
38
+ const expectedAction = [{type: OPEN_POPIN}];
39
+ const {dispatch, getState} = createTestStore(t, state, services, expectedAction);
40
+ const props = mapStateToSlidesProps(getState(), dispatch, identity);
41
+ t.is(props.quitPopin, undefined);
42
+ await props.header.onQuitClick();
43
+ const updatedProps = mapStateToSlidesProps(getState(), dispatch, identity);
44
+ t.not(updatedProps.quitPopin, undefined);
45
+ t.pass();
46
+ });