fcad-core-dragon 2.0.0-beta.4 → 2.0.0-beta.5
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/CHANGELOG +377 -373
- package/package.json +61 -61
- package/src/$locales/en.json +3 -1
- package/src/$locales/fr.json +3 -1
- package/src/components/AppBase.vue +20 -17
- package/src/components/AppBaseButton.test.js +22 -0
- package/src/components/AppBaseButton.vue +11 -5
- package/src/components/AppBaseModule.vue +48 -27
- package/src/components/AppBasePage.vue +7 -44
- package/src/components/AppCompAudio.vue +31 -0
- package/src/components/AppCompBranchButtons.vue +20 -16
- package/src/components/AppCompButtonProgress.vue +4 -9
- package/src/components/AppCompCarousel.vue +120 -90
- package/src/components/AppCompInputCheckBoxNext.vue +5 -0
- package/src/components/AppCompInputDropdownNext.vue +50 -8
- package/src/components/AppCompInputTextNext.vue +21 -2
- package/src/components/AppCompInputTextTableNext.vue +1 -0
- package/src/components/AppCompInputTextToFillDropdownNext.vue +8 -0
- package/src/components/AppCompMenu.vue +428 -423
- package/src/components/AppCompNavigation.vue +41 -28
- package/src/components/AppCompNoteCall.vue +64 -38
- package/src/components/AppCompNoteCredit.vue +303 -105
- package/src/components/AppCompPlayBar.vue +4 -5
- package/src/components/AppCompPlayBarNext.vue +20 -12
- package/src/components/AppCompPopUpNext.vue +1 -4
- package/src/components/AppCompQuizNext.vue +8 -4
- package/src/components/AppCompQuizRecall.vue +44 -22
- package/src/components/AppCompTableOfContent.vue +61 -62
- package/src/components/AppCompViewDisplay.vue +6 -6
- package/src/components/BaseModule.vue +1 -18
- package/src/components/tests__/AppBaseButton.spec.js +53 -0
- package/src/main.js +3 -3
- package/src/module/stores/appStore.js +58 -5
- package/src/module/xapi/Crypto/index.js +53 -53
- package/src/module/xapi/Statement/activity.js +47 -47
- package/src/module/xapi/Statement/group.js +26 -26
- package/src/module/xapi/Statement/statementRef.js +23 -23
- package/src/module/xapi/Statement/substatement.js +22 -22
- package/src/module/xapi/Statement/verb.js +36 -36
- package/src/module/xapi/activitytypes.js +17 -17
- package/src/plugins/helper.js +53 -12
- package/src/router/index.js +6 -1
- package/src/shared/validators.js +36 -179
- package/vitest.config.js +19 -0
|
@@ -263,12 +263,12 @@ mediaMixins is used for all the methods/data shared between audio and video. In
|
|
|
263
263
|
@mouseenter="activateLabel($event.currentTarget)"
|
|
264
264
|
@mouseleave="deactivateLabel($event.currentTarget)"
|
|
265
265
|
@focus="
|
|
266
|
-
activateLabel($event.currentTarget),
|
|
267
|
-
|
|
266
|
+
(activateLabel($event.currentTarget),
|
|
267
|
+
changeFocusState('volumeSlider', true))
|
|
268
268
|
"
|
|
269
269
|
@blur="
|
|
270
|
-
deactivateLabel($event.currentTarget),
|
|
271
|
-
|
|
270
|
+
(deactivateLabel($event.currentTarget),
|
|
271
|
+
changeFocusState('volumeSlider', false))
|
|
272
272
|
"
|
|
273
273
|
/>
|
|
274
274
|
<span class="volume-label" aria-hidden="true">
|
|
@@ -292,7 +292,7 @@ mediaMixins is used for all the methods/data shared between audio and video. In
|
|
|
292
292
|
class="btn subtitleBtns"
|
|
293
293
|
:aria-label="ccLabel"
|
|
294
294
|
:data-title="`${ccLabel} (c)`"
|
|
295
|
-
:disabled="!hasSubtitle"
|
|
295
|
+
:is-disabled="!hasSubtitle"
|
|
296
296
|
:class="{
|
|
297
297
|
md_disabled: !hasSubtitle,
|
|
298
298
|
displayLabel: btnsLabelDisplay['btn-subtitles'] === true
|
|
@@ -320,11 +320,11 @@ mediaMixins is used for all the methods/data shared between audio and video. In
|
|
|
320
320
|
class="btn-transcript"
|
|
321
321
|
:aria-label="transcriptLabel"
|
|
322
322
|
:data-title="`${transcriptLabel} (t)`"
|
|
323
|
-
:disabled="transcriptBtnDisabled"
|
|
324
323
|
:class="{
|
|
325
324
|
md_disabled: transcriptBtnDisabled,
|
|
326
325
|
displayLabel: btnsLabelDisplay['btn-transcript'] === true
|
|
327
326
|
}"
|
|
327
|
+
:is-disabled="transcriptBtnDisabled"
|
|
328
328
|
@click="toggleViewTranscript($event)"
|
|
329
329
|
@mouseenter="activateLabel($event)"
|
|
330
330
|
@mouseleave="deactivateLabel($event)"
|
|
@@ -350,11 +350,11 @@ mediaMixins is used for all the methods/data shared between audio and video. In
|
|
|
350
350
|
ref="$btn-fullscreen"
|
|
351
351
|
class="fullscreenBtns"
|
|
352
352
|
:aria-label="fullscreenLabel"
|
|
353
|
-
:disabled="fullscreenBtnDisabled"
|
|
354
353
|
:class="{
|
|
355
354
|
md_disabled: fullscreenBtnDisabled,
|
|
356
355
|
displayLabel: btnsLabelDisplay['btn-fullscreen'] === true
|
|
357
356
|
}"
|
|
357
|
+
:is-disabled="fullscreenBtnDisabled"
|
|
358
358
|
:data-title="`${fullscreenLabel} (f)`"
|
|
359
359
|
@click="toggleFullScreen()"
|
|
360
360
|
@mouseenter="activateLabel($event)"
|
|
@@ -682,9 +682,8 @@ export default {
|
|
|
682
682
|
if (this.firefoxTrack) {
|
|
683
683
|
this.firefoxTrack.removeEventListener('cuechange', this.fireFoxMoveCC)
|
|
684
684
|
}
|
|
685
|
-
|
|
685
|
+
|
|
686
686
|
this.$bus.$off('transcript-hidden', this.resetTranscript)
|
|
687
|
-
return this.$bus.$off('close-sidebar', 'ctxTranscript')
|
|
688
687
|
},
|
|
689
688
|
methods: {
|
|
690
689
|
...mapActions(useAppStore, [
|
|
@@ -899,6 +898,7 @@ export default {
|
|
|
899
898
|
* @description - Toggle the state fo the sound
|
|
900
899
|
*/
|
|
901
900
|
toggleMute() {
|
|
901
|
+
if (!this.showControlsValue) this.showControls()
|
|
902
902
|
//Check first the volume of the media
|
|
903
903
|
if (this.mediaElement.volume == 0) this.muted = true
|
|
904
904
|
|
|
@@ -952,7 +952,7 @@ export default {
|
|
|
952
952
|
|
|
953
953
|
handleMediaControls(e) {
|
|
954
954
|
e = e.code
|
|
955
|
-
this.showControls()
|
|
955
|
+
if (!this.showControlsValue) this.showControls()
|
|
956
956
|
switch (e) {
|
|
957
957
|
case 'ArrowLeft':
|
|
958
958
|
//If focus is on Volume, VolDown
|
|
@@ -1334,6 +1334,7 @@ export default {
|
|
|
1334
1334
|
|
|
1335
1335
|
/** "@description -update the information of the seek tooltip*/
|
|
1336
1336
|
updateSeekTooltip(evt) {
|
|
1337
|
+
if (!this.showControlsValue) this.showControls()
|
|
1337
1338
|
//Elm progress Bar
|
|
1338
1339
|
const progressBar = this.progressBar
|
|
1339
1340
|
//progressBar position on the page
|
|
@@ -1423,6 +1424,7 @@ export default {
|
|
|
1423
1424
|
*/
|
|
1424
1425
|
toggleFullScreen() {
|
|
1425
1426
|
const fullscreenElement = this.mediaContainer
|
|
1427
|
+
|
|
1426
1428
|
if (document.fullscreenElement) {
|
|
1427
1429
|
// exitFullscreen is only available on the Document object.
|
|
1428
1430
|
document.exitFullscreen()
|
|
@@ -1435,6 +1437,7 @@ export default {
|
|
|
1435
1437
|
* @description update the value of fulls screen state
|
|
1436
1438
|
*/
|
|
1437
1439
|
updateFullScreenState() {
|
|
1440
|
+
if (!this.showControlsValue) this.showControls()
|
|
1438
1441
|
this.fullscreenOn = !this.fullscreenOn
|
|
1439
1442
|
},
|
|
1440
1443
|
|
|
@@ -1463,6 +1466,7 @@ export default {
|
|
|
1463
1466
|
this.setMediaSubtitles(false)
|
|
1464
1467
|
},
|
|
1465
1468
|
toggleViewSubtitle() {
|
|
1469
|
+
if (!this.showControlsValue) this.showControls()
|
|
1466
1470
|
if (this.subtitlesEnabled) return this.hideSubtitles()
|
|
1467
1471
|
if (!this.subtitlesEnabled) return this.showSubtitles()
|
|
1468
1472
|
},
|
|
@@ -1474,6 +1478,7 @@ export default {
|
|
|
1474
1478
|
* @fires 'close-sidebar' to AppBaseModule
|
|
1475
1479
|
*/
|
|
1476
1480
|
toggleViewTranscript(event) {
|
|
1481
|
+
if (!this.showControlsValue) this.showControls()
|
|
1477
1482
|
this.transcriptEnabled = !this.transcriptEnabled
|
|
1478
1483
|
//When the function is called after a user interaction (mouse or keyboard), emit event
|
|
1479
1484
|
if (event) {
|
|
@@ -1550,6 +1555,7 @@ export default {
|
|
|
1550
1555
|
* @description Method to reset the transcript state
|
|
1551
1556
|
*/
|
|
1552
1557
|
resetTranscript(content) {
|
|
1558
|
+
if (!this.showControlsValue) this.showControls()
|
|
1553
1559
|
this.transcriptEnabled = false
|
|
1554
1560
|
this.otherVideoTranscriptShown = false
|
|
1555
1561
|
},
|
|
@@ -1685,9 +1691,11 @@ export default {
|
|
|
1685
1691
|
|
|
1686
1692
|
right: calc(var(--progress-thumb-size) / -2);
|
|
1687
1693
|
top: calc(
|
|
1688
|
-
-1 *
|
|
1694
|
+
-1 *
|
|
1695
|
+
(
|
|
1689
1696
|
var(--progress-thumb-size) / 2 - var(--progress-bar-height) / 2
|
|
1690
|
-
) - var(--progress-bar-border-height) /
|
|
1697
|
+
) - var(--progress-bar-border-height) /
|
|
1698
|
+
2
|
|
1691
1699
|
);
|
|
1692
1700
|
display: flex;
|
|
1693
1701
|
align-items: center;
|
|
@@ -208,13 +208,12 @@
|
|
|
208
208
|
</template>
|
|
209
209
|
|
|
210
210
|
<script>
|
|
211
|
-
import AppBaseErrorDisplay from './AppBaseErrorDisplay.vue'
|
|
212
211
|
import { mapActions } from 'pinia'
|
|
213
212
|
import { useAppStore } from '../module/stores/appStore'
|
|
214
213
|
|
|
215
214
|
export default {
|
|
216
215
|
name: 'AppCompPopUp',
|
|
217
|
-
|
|
216
|
+
|
|
218
217
|
data() {
|
|
219
218
|
return {
|
|
220
219
|
pType: null,
|
|
@@ -397,8 +396,6 @@ export default {
|
|
|
397
396
|
this.pType = data.type
|
|
398
397
|
this.pName = data.name
|
|
399
398
|
|
|
400
|
-
console.log(this.pType)
|
|
401
|
-
|
|
402
399
|
if (this.pType == 'popup-endActivity')
|
|
403
400
|
this.updateEndPopUp(true) //This will handle the activation of the element in the portal
|
|
404
401
|
else this.updateEndPopUp(false) //This will handle the activation of the element in the portal
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
:id="`btn_quiz_${quizData.id}`"
|
|
78
78
|
ref="quiz"
|
|
79
79
|
class="btn-quiz btn-main"
|
|
80
|
-
:is-
|
|
80
|
+
:is-disabled="quizIsNotAnswered || attemptsReached"
|
|
81
81
|
:title="txtBtnSubmit"
|
|
82
82
|
@click="validateAnswers"
|
|
83
83
|
>
|
|
@@ -306,18 +306,21 @@ export default {
|
|
|
306
306
|
break
|
|
307
307
|
case ['dropdown', 'texte_troue_select', 'texte_tableau'].includes(
|
|
308
308
|
type_question
|
|
309
|
-
):
|
|
309
|
+
): {
|
|
310
|
+
const searchedVal = type_question == 'texte_tableau' ? '' : null
|
|
310
311
|
if (
|
|
311
312
|
this.quizInputType.length < choix_reponse.length ||
|
|
312
|
-
this.$helper.containsValue(this.quizInputType,
|
|
313
|
+
this.$helper.containsValue(this.quizInputType, searchedVal)
|
|
313
314
|
)
|
|
314
315
|
notAnswer = true
|
|
315
316
|
break
|
|
317
|
+
}
|
|
316
318
|
|
|
317
319
|
case type_question == 'texte_troue':
|
|
318
320
|
if (texte_base) {
|
|
319
321
|
const regex = /\$%\S*%\$/g // regex pattern to match exp: $%number%$
|
|
320
322
|
let matchAll = texte_base.split(regex)
|
|
323
|
+
|
|
321
324
|
if (
|
|
322
325
|
this.quizInputType.length < matchAll.length - 1 ||
|
|
323
326
|
this.$helper.containsValue(this.quizInputType, '')
|
|
@@ -2271,7 +2274,7 @@ export default {
|
|
|
2271
2274
|
const { type_question } = this.quizData
|
|
2272
2275
|
switch (true) {
|
|
2273
2276
|
case ['choix_unique', 'reponse_ouverte'].includes(type_question):
|
|
2274
|
-
if (oldValue !==
|
|
2277
|
+
if (newValue !== null && oldValue !== '') {
|
|
2275
2278
|
this.showSolution = false
|
|
2276
2279
|
}
|
|
2277
2280
|
break
|
|
@@ -2870,6 +2873,7 @@ export default {
|
|
|
2870
2873
|
const regex = /\$%\S*%\$/g // regex pattern to match exp: $%number%$
|
|
2871
2874
|
let matchAll = texte_base.split(regex)
|
|
2872
2875
|
let listInputTextFill = this.quizInputType
|
|
2876
|
+
|
|
2873
2877
|
if (
|
|
2874
2878
|
listInputTextFill.length == matchAll.length - 1 &&
|
|
2875
2879
|
!this.$helper.containsValue(listInputTextFill, null) &&
|
|
@@ -16,25 +16,33 @@
|
|
|
16
16
|
{{ quizRecall.title }}
|
|
17
17
|
</component>
|
|
18
18
|
<!--Quiz answer conditionning-->
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
19
|
+
<app-base-error-display
|
|
20
|
+
v-if="hasError.length"
|
|
21
|
+
:error-group="'component'"
|
|
22
|
+
:error-title="`ERREUR: COMPOSANT QUIZ RECALL`"
|
|
23
|
+
:errors-list="hasError"
|
|
24
|
+
></app-base-error-display>
|
|
25
|
+
<template v-else>
|
|
26
|
+
<template v-if="quizRecall.done == true">
|
|
27
|
+
<div
|
|
28
|
+
v-if="quizRecall.hypertext_done"
|
|
29
|
+
class="quizRecall-text-done"
|
|
30
|
+
v-html="quizRecall.hypertext_done"
|
|
31
|
+
></div>
|
|
32
|
+
<div class="quizRecall-ennonce" v-html="quizRecall.ennonce"></div>
|
|
33
|
+
<textarea
|
|
34
|
+
v-model="quizRecall.answer"
|
|
35
|
+
disabled
|
|
36
|
+
class="form-control"
|
|
37
|
+
></textarea>
|
|
38
|
+
</template>
|
|
39
|
+
<template v-if="quizRecall.done == false">
|
|
40
|
+
<div
|
|
41
|
+
v-if="quizRecall.hypertext_undone"
|
|
42
|
+
class="quizRecall-text-undone"
|
|
43
|
+
v-html="quizRecall.hypertext_undone"
|
|
44
|
+
></div>
|
|
45
|
+
</template>
|
|
38
46
|
</template>
|
|
39
47
|
</section>
|
|
40
48
|
</template>
|
|
@@ -62,6 +70,7 @@ export default {
|
|
|
62
70
|
'message.recall_undone'
|
|
63
71
|
)}</p>` /*String traduite par défaut*/
|
|
64
72
|
},
|
|
73
|
+
hasError: [],
|
|
65
74
|
quizData: null
|
|
66
75
|
}
|
|
67
76
|
},
|
|
@@ -98,6 +107,8 @@ export default {
|
|
|
98
107
|
*/
|
|
99
108
|
findQuizdataObject(searchObject, quizID) {
|
|
100
109
|
for (let property in searchObject) {
|
|
110
|
+
//Should only check for not null poperties
|
|
111
|
+
if (!searchObject[property]) continue
|
|
101
112
|
// Should only check for Object or Array property
|
|
102
113
|
if (
|
|
103
114
|
searchObject[property].constructor !== Array &&
|
|
@@ -124,6 +135,7 @@ export default {
|
|
|
124
135
|
getQuizData(activityId, pageId) {
|
|
125
136
|
if (pageId && activityId) {
|
|
126
137
|
let pageData = this.getPageData(activityId, pageId)
|
|
138
|
+
|
|
127
139
|
if (pageData) {
|
|
128
140
|
this.quizData = this.findQuizdataObject(
|
|
129
141
|
pageData,
|
|
@@ -132,17 +144,21 @@ export default {
|
|
|
132
144
|
|
|
133
145
|
//Warn that the specified quiz could not be found
|
|
134
146
|
if (!this.quizData) {
|
|
147
|
+
let msgErr = `Aucun quiz avec id '${this.quizRecallData.quizId}' pour la ${pageId} de ${activityId}`
|
|
135
148
|
console.warn(
|
|
136
149
|
`%c WARNING!>>> Quiz Recall: Unable to find a quiz with type_question 'reponse_ouverte' and id '${this.quizRecallData.quizId}' in ${pageId} of ${activityId}`,
|
|
137
150
|
'background: orange; color: white; display: block; margin:5px;'
|
|
138
151
|
)
|
|
152
|
+
this.hasError.push(msgErr)
|
|
139
153
|
}
|
|
140
154
|
} else {
|
|
141
155
|
this.quizRecall.done == false
|
|
156
|
+
let msgErr = `Aucun quiz avec id '${this.quizRecallData.quizId}' pour la ${pageId} de ${activityId}`
|
|
142
157
|
console.warn(
|
|
143
158
|
`%c WARNING!>>> AppCompQuizRecall : QuizData ActivityID and PageID combinaison is invalid.`,
|
|
144
159
|
'background: orange; color: white; display: block; margin:5px;'
|
|
145
160
|
)
|
|
161
|
+
this.hasError.push(msgErr)
|
|
146
162
|
}
|
|
147
163
|
} else {
|
|
148
164
|
this.quizRecall.done == false
|
|
@@ -243,17 +259,21 @@ export default {
|
|
|
243
259
|
|
|
244
260
|
//Validate if all properties in quizRecallData are valid
|
|
245
261
|
if (wrongProperties.length > 0) {
|
|
262
|
+
let msgErr = `QuizRecallData ${wrongProperties} invalid`
|
|
246
263
|
console.warn(
|
|
247
|
-
`%c WARNING!>>> AppCompQuizRecall :
|
|
264
|
+
`%c WARNING!>>> AppCompQuizRecall : ${msgErr}. Required properties: ${requiredProperties} . Optional properties: ${optionalProperties}`,
|
|
248
265
|
'background: orange; color: white; display: block; margin:5px;'
|
|
249
266
|
)
|
|
267
|
+
this.hasError.push(msgErr)
|
|
250
268
|
}
|
|
251
269
|
//Validate if all required properties are present in quizRecallData
|
|
252
270
|
if (missingRequired.length > 0) {
|
|
271
|
+
let msgErr = `QuizRecallData missing required ${missingRequired} property`
|
|
253
272
|
console.warn(
|
|
254
|
-
`%c WARNING!>>> AppCompQuizRecall :
|
|
273
|
+
`%c WARNING!>>> AppCompQuizRecall : ${msgErr}. Required properties: activityId and pageId. Optional properties: title, titletag, hypertext_done and hypertext_undone.`,
|
|
255
274
|
'background: orange; color: white; display: block; margin:5px;'
|
|
256
275
|
)
|
|
276
|
+
this.hasError.push(msgErr)
|
|
257
277
|
}
|
|
258
278
|
},
|
|
259
279
|
/*Get a title tag (string). Return true if the tag is valid and false is it's not*/
|
|
@@ -264,10 +284,12 @@ export default {
|
|
|
264
284
|
if (tags && value && tags.find((element) => element == lowerValue))
|
|
265
285
|
return true
|
|
266
286
|
else {
|
|
287
|
+
let msgErr = `Your quizRecallData titletag is not valid`
|
|
267
288
|
console.warn(
|
|
268
|
-
'%c WARNING!>>>
|
|
289
|
+
'%c WARNING!>>> AppCompQuizRecall : Your quizRecallData titletag is not valid. You can use h1,h2,h3,h4,h5,h6,p,or span',
|
|
269
290
|
'background: orange; color: white; display: block; margin:5px;'
|
|
270
291
|
)
|
|
292
|
+
this.hasError.push(msgErr)
|
|
271
293
|
return false
|
|
272
294
|
}
|
|
273
295
|
}
|
|
@@ -8,55 +8,59 @@
|
|
|
8
8
|
-->
|
|
9
9
|
|
|
10
10
|
<template>
|
|
11
|
-
<div v-if="error" id="sidebar-submenu" :class="{ isOpen:
|
|
12
|
-
<
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
</svg>
|
|
21
|
-
</app-base-button>
|
|
22
|
-
|
|
23
|
-
<p class="t-act" v-html="title"></p>
|
|
24
|
-
|
|
25
|
-
<div class="box-prog-act">
|
|
26
|
-
<p>
|
|
27
|
-
{{ $t('text.activity_progress') }}
|
|
28
|
-
<span>{{ actProgress }}</span>
|
|
29
|
-
</p>
|
|
30
|
-
</div>
|
|
31
|
-
|
|
32
|
-
<div class="box-anchor">
|
|
33
|
-
<p class="t-toc">{{ $t('text.toc') }}</p>
|
|
34
|
-
|
|
35
|
-
<a
|
|
36
|
-
v-for="(anchor, index) of anchors"
|
|
37
|
-
:key="anchor.title"
|
|
38
|
-
class="toc-item"
|
|
39
|
-
:done="sectionProgress(index)"
|
|
40
|
-
@click="closeAndNextPage(anchor.path)"
|
|
41
|
-
>
|
|
42
|
-
<div class="box-text-anchor">
|
|
43
|
-
<div class="text-anchor">
|
|
44
|
-
<p class="anchor-t" v-html="anchor.title"></p>
|
|
45
|
-
<p class="state">
|
|
46
|
-
<svg>
|
|
47
|
-
<use href="#check-toc" />
|
|
48
|
-
</svg>
|
|
49
|
-
{{ $t('text.complete') }}
|
|
50
|
-
</p>
|
|
51
|
-
</div>
|
|
11
|
+
<div v-if="error" id="sidebar-submenu" :class="{ isOpen: isOpened }">
|
|
12
|
+
<focus-trap :active="isOpened">
|
|
13
|
+
<div ref="target">
|
|
14
|
+
<app-base-button
|
|
15
|
+
id="close-toc"
|
|
16
|
+
class="btn-ghost"
|
|
17
|
+
:title="$t('button.closePopUp')"
|
|
18
|
+
@click="onCloseWidget('toc')"
|
|
19
|
+
>
|
|
52
20
|
<svg>
|
|
53
|
-
<use href="#
|
|
21
|
+
<use href="#close-square-icon" />
|
|
54
22
|
</svg>
|
|
23
|
+
</app-base-button>
|
|
24
|
+
|
|
25
|
+
<p class="t-act" v-html="title"></p>
|
|
26
|
+
|
|
27
|
+
<div class="box-prog-act">
|
|
28
|
+
<p>
|
|
29
|
+
{{ $t('text.activity_progress') }}
|
|
30
|
+
<span>{{ actProgress }}</span>
|
|
31
|
+
</p>
|
|
32
|
+
</div>
|
|
33
|
+
<div class="box-anchor">
|
|
34
|
+
<p class="t-toc">{{ $t('text.toc') }}</p>
|
|
35
|
+
|
|
36
|
+
<RouterLink
|
|
37
|
+
v-for="(anchor, index) of anchors"
|
|
38
|
+
:key="anchor.title"
|
|
39
|
+
class="toc-item"
|
|
40
|
+
:done="sectionProgress(index)"
|
|
41
|
+
:to="{ name: anchor.path }"
|
|
42
|
+
@click="closeAndNextPage(anchor.path)"
|
|
43
|
+
>
|
|
44
|
+
<div class="box-text-anchor">
|
|
45
|
+
<div class="text-anchor">
|
|
46
|
+
<p class="anchor-t" v-html="anchor.title"></p>
|
|
47
|
+
<p class="state">
|
|
48
|
+
<svg>
|
|
49
|
+
<use href="#check-toc" />
|
|
50
|
+
</svg>
|
|
51
|
+
{{ $t('text.complete') }}
|
|
52
|
+
</p>
|
|
53
|
+
</div>
|
|
54
|
+
<svg>
|
|
55
|
+
<use href="#chevronD-icon" />
|
|
56
|
+
</svg>
|
|
57
|
+
</div>
|
|
58
|
+
</RouterLink>
|
|
55
59
|
</div>
|
|
56
|
-
</
|
|
57
|
-
</
|
|
60
|
+
</div>
|
|
61
|
+
</focus-trap>
|
|
58
62
|
</div>
|
|
59
|
-
<div v-else id="sidebar-submenu" :class="{ isOpen:
|
|
63
|
+
<div v-else id="sidebar-submenu" :class="{ isOpen: isOpened }">
|
|
60
64
|
<p>
|
|
61
65
|
Cette activité n'existe pas dans menu setting, svp l'ajouter à votre menu
|
|
62
66
|
setting
|
|
@@ -73,7 +77,7 @@ export default {
|
|
|
73
77
|
title: '',
|
|
74
78
|
anchors: [],
|
|
75
79
|
activity: '',
|
|
76
|
-
|
|
80
|
+
isOpened: false,
|
|
77
81
|
menuInfo: null,
|
|
78
82
|
actProgress: null,
|
|
79
83
|
error: true
|
|
@@ -101,7 +105,7 @@ export default {
|
|
|
101
105
|
methods: {
|
|
102
106
|
...mapActions(useAppStore, ['updateWidgetOpen']),
|
|
103
107
|
onInfoActivity(data) {
|
|
104
|
-
//should be
|
|
108
|
+
//should be conditioned to avoid event firing multiple time on call
|
|
105
109
|
|
|
106
110
|
if (data) {
|
|
107
111
|
this.reset()
|
|
@@ -116,23 +120,18 @@ export default {
|
|
|
116
120
|
}
|
|
117
121
|
},
|
|
118
122
|
onCloseWidget(data) {
|
|
119
|
-
this.
|
|
123
|
+
this.isOpened = false
|
|
120
124
|
this.updateWidgetOpen(false)
|
|
121
125
|
this.reset()
|
|
122
126
|
},
|
|
123
127
|
onToggleWidget(data) {
|
|
124
|
-
if (data
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
} else {
|
|
128
|
-
this.updateWidgetOpen(false)
|
|
129
|
-
this.open = false
|
|
130
|
-
}
|
|
128
|
+
if (data.type !== 'toc') return (this.isOpened = false)
|
|
129
|
+
this.isOpened = !this.isOpened
|
|
130
|
+
this.updateWidgetOpen(this.isOpened)
|
|
131
131
|
},
|
|
132
132
|
/* @Description:Get and set the section to output
|
|
133
133
|
* @params: activity {Object}: the activity which sections need to be output
|
|
134
134
|
*/
|
|
135
|
-
//
|
|
136
135
|
getInfoSubMenu(activity) {
|
|
137
136
|
//get the anchors for this activity from the store
|
|
138
137
|
let menuInfo = this.getMenuSettings
|
|
@@ -226,12 +225,12 @@ export default {
|
|
|
226
225
|
}
|
|
227
226
|
},
|
|
228
227
|
close() {
|
|
229
|
-
this.
|
|
230
|
-
this.updateWidgetOpen(this.
|
|
228
|
+
this.isOpened = !this.isOpened
|
|
229
|
+
this.updateWidgetOpen(this.isOpened)
|
|
231
230
|
},
|
|
232
231
|
closeAndNextPage(NextPage) {
|
|
233
|
-
this.
|
|
234
|
-
this.updateWidgetOpen(this.
|
|
232
|
+
this.isOpened = !this.isOpened
|
|
233
|
+
this.updateWidgetOpen(this.isOpened)
|
|
235
234
|
this.$router.push({ name: NextPage })
|
|
236
235
|
},
|
|
237
236
|
reset() {
|
|
@@ -267,10 +266,10 @@ export default {
|
|
|
267
266
|
for (let g = 1; g == numPage; g++) {
|
|
268
267
|
tabPageSec.push(`P${g}`)
|
|
269
268
|
}
|
|
270
|
-
|
|
271
269
|
if (
|
|
270
|
+
typeof this.getAllCompleted[this.activity] !== 'undefined' &&
|
|
272
271
|
this.getAllCompleted[this.activity].length !==
|
|
273
|
-
|
|
272
|
+
this.getAllActivitiesState[this.activity].size
|
|
274
273
|
)
|
|
275
274
|
return false
|
|
276
275
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<router-view />
|
|
3
|
-
</template>
|
|
4
|
-
<script>
|
|
5
|
-
export default {}
|
|
6
|
-
</script>
|
|
1
|
+
<template>
|
|
2
|
+
<router-view />
|
|
3
|
+
</template>
|
|
4
|
+
<script>
|
|
5
|
+
export default {}
|
|
6
|
+
</script>
|
|
@@ -5,11 +5,6 @@
|
|
|
5
5
|
-->
|
|
6
6
|
<template>
|
|
7
7
|
<div v-if="mData" class="module-wrapper">
|
|
8
|
-
<!-- <div
|
|
9
|
-
class="overlay-close-widget"
|
|
10
|
-
:class="{ blockOverlay: over }"
|
|
11
|
-
@click="overlayClose()"
|
|
12
|
-
></div> -->
|
|
13
8
|
<slot name="mTitle" />
|
|
14
9
|
<slot>Module</slot>
|
|
15
10
|
</div>
|
|
@@ -32,20 +27,8 @@ export default {
|
|
|
32
27
|
}
|
|
33
28
|
},
|
|
34
29
|
|
|
35
|
-
mounted() {
|
|
36
|
-
// this.$bus.$on('toggle-widget', () => {
|
|
37
|
-
// this.over = true
|
|
38
|
-
// })
|
|
39
|
-
// this.$bus.$on('close-widget', () => {
|
|
40
|
-
// this.over = false
|
|
41
|
-
// })
|
|
42
|
-
},
|
|
43
30
|
created() {},
|
|
44
|
-
methods: {
|
|
45
|
-
// overlayClose() {
|
|
46
|
-
// this.$bus.$emit('close-widget')
|
|
47
|
-
// }
|
|
48
|
-
}
|
|
31
|
+
methods: {}
|
|
49
32
|
}
|
|
50
33
|
</script>
|
|
51
34
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils'
|
|
2
|
+
import AppBaseButton from '../AppBaseButton.vue'
|
|
3
|
+
import { describe, expect, test } from 'vitest'
|
|
4
|
+
|
|
5
|
+
describe('AppBaseButton.vue', () => {
|
|
6
|
+
test('It renders a button', () => {
|
|
7
|
+
const wrapper = mount(AppBaseButton)
|
|
8
|
+
|
|
9
|
+
expect(wrapper.text()).toContain('Button')
|
|
10
|
+
})
|
|
11
|
+
// test prop value :type
|
|
12
|
+
test('Button has the correct type', () => {
|
|
13
|
+
const wrapper = mount(AppBaseButton, {
|
|
14
|
+
props: { type: 'submit' }
|
|
15
|
+
})
|
|
16
|
+
// expect(wrapper.props().type).toBe(submit)
|
|
17
|
+
expect(wrapper.get('button').attributes('type')).toBe('submit')
|
|
18
|
+
})
|
|
19
|
+
//test prop value : isActive
|
|
20
|
+
test('Button render a correct state', () => {
|
|
21
|
+
const wrapper = mount(AppBaseButton, {
|
|
22
|
+
props: { isDisabled: true }
|
|
23
|
+
})
|
|
24
|
+
expect(wrapper.get('button').classes()).toEqual(
|
|
25
|
+
expect.arrayContaining(['md_disabled'])
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
// expect(wrapper.props().isActive).toBe(true)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
//test slot
|
|
32
|
+
test('Renders slot content properly', () => {
|
|
33
|
+
const wrapper = mount(AppBaseButton, {
|
|
34
|
+
slots: {
|
|
35
|
+
default: 'Mon bouton custome'
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
expect(wrapper.get('button').text()).toBe('Mon bouton custome')
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
//test click with event emitter
|
|
43
|
+
test('Emit event with value of the the $el when clicked', () => {
|
|
44
|
+
const wrapper = mount(AppBaseButton, {
|
|
45
|
+
slots: {
|
|
46
|
+
default: 'Mon bouton custome'
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
wrapper.get('button').trigger('click')
|
|
50
|
+
const clickEvent = wrapper.emitted('click')[0]
|
|
51
|
+
expect(clickEvent[0]).toEqual(wrapper.vm.$el)
|
|
52
|
+
})
|
|
53
|
+
})
|
package/src/main.js
CHANGED
|
@@ -143,9 +143,9 @@ export default {
|
|
|
143
143
|
if (key === 'menuSettings') {
|
|
144
144
|
const mapAct = appStore.$state.thisModule.activities
|
|
145
145
|
if (!mapAct) return
|
|
146
|
-
const keys4Lesson = ['lessonTitle', ...mapAct.keys()]
|
|
146
|
+
const keys4Lesson = ['lessonTitle', 'lessonNumber', ...mapAct.keys()]
|
|
147
147
|
const keys4ActivityOpt = ['title', 'subTitle']
|
|
148
|
-
const
|
|
148
|
+
const keys4ActivityMandatory = ['time', 'anchors']
|
|
149
149
|
const keys4Anchors = ['anchorName', 'pageRef', 'page']
|
|
150
150
|
|
|
151
151
|
const errChecked = validatefileContent(
|
|
@@ -153,7 +153,7 @@ export default {
|
|
|
153
153
|
options.menuSettings,
|
|
154
154
|
{
|
|
155
155
|
keys4Lesson,
|
|
156
|
-
|
|
156
|
+
keys4ActivityMandatory,
|
|
157
157
|
keys4ActivityOpt,
|
|
158
158
|
keys4Anchors
|
|
159
159
|
}
|