@pie-element/ebsr 14.2.1 → 14.2.2-next.1

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 (97) hide show
  1. package/configure.js +2 -0
  2. package/controller.js +1 -0
  3. package/dist/author/defaults.d.ts +348 -0
  4. package/dist/author/defaults.js +161 -0
  5. package/dist/author/index.d.ts +34 -0
  6. package/dist/author/index.js +78 -0
  7. package/dist/author/main.d.ts +22 -0
  8. package/dist/author/main.js +121 -0
  9. package/dist/browser/author/index.js +13940 -0
  10. package/dist/browser/author/index.js.map +1 -0
  11. package/dist/browser/browser-CdX8WGiZ.js +207 -0
  12. package/dist/browser/browser-CdX8WGiZ.js.map +1 -0
  13. package/dist/browser/controller/index.js +257 -0
  14. package/dist/browser/controller/index.js.map +1 -0
  15. package/dist/browser/delivery/index.js +133 -0
  16. package/dist/browser/delivery/index.js.map +1 -0
  17. package/dist/browser/dist-CaWRqnXk.js +62 -0
  18. package/dist/browser/dist-CaWRqnXk.js.map +1 -0
  19. package/dist/browser/dist-D9ARZhQk.js +1426 -0
  20. package/dist/browser/dist-D9ARZhQk.js.map +1 -0
  21. package/dist/browser/dist-DQkngCcw.js +36 -0
  22. package/dist/browser/dist-DQkngCcw.js.map +1 -0
  23. package/dist/browser/print/index.js +107 -0
  24. package/dist/browser/print/index.js.map +1 -0
  25. package/dist/controller/defaults.d.ts +15 -0
  26. package/dist/controller/defaults.js +26 -0
  27. package/dist/controller/index.d.ts +42 -0
  28. package/dist/controller/index.js +185 -0
  29. package/dist/controller/utils.d.ts +10 -0
  30. package/dist/controller/utils.js +8 -0
  31. package/dist/delivery/index.d.ts +26 -0
  32. package/dist/delivery/index.js +132 -0
  33. package/dist/index.d.ts +1 -0
  34. package/dist/index.iife.d.ts +8 -0
  35. package/dist/index.iife.js +165 -0
  36. package/dist/index.js +2 -0
  37. package/dist/print/index.d.ts +25 -0
  38. package/dist/print/index.js +106 -0
  39. package/dist/runtime-support.d.ts +12 -0
  40. package/dist/runtime-support.js +12 -0
  41. package/package.json +88 -19
  42. package/CHANGELOG.json +0 -1547
  43. package/CHANGELOG.md +0 -2285
  44. package/LICENSE.md +0 -5
  45. package/README.md +0 -62
  46. package/configure/CHANGELOG.json +0 -992
  47. package/configure/CHANGELOG.md +0 -2148
  48. package/configure/lib/defaults.js +0 -229
  49. package/configure/lib/defaults.js.map +0 -1
  50. package/configure/lib/index.js +0 -154
  51. package/configure/lib/index.js.map +0 -1
  52. package/configure/lib/main.js +0 -222
  53. package/configure/lib/main.js.map +0 -1
  54. package/configure/package.json +0 -23
  55. package/configure/src/__tests__/index.test.js +0 -492
  56. package/configure/src/defaults.js +0 -173
  57. package/configure/src/index.js +0 -174
  58. package/configure/src/main.jsx +0 -235
  59. package/controller/CHANGELOG.json +0 -552
  60. package/controller/CHANGELOG.md +0 -1286
  61. package/controller/lib/defaults.js +0 -31
  62. package/controller/lib/defaults.js.map +0 -1
  63. package/controller/lib/index.js +0 -448
  64. package/controller/lib/index.js.map +0 -1
  65. package/controller/lib/utils.js +0 -18
  66. package/controller/lib/utils.js.map +0 -1
  67. package/controller/package.json +0 -18
  68. package/controller/src/__tests__/index.test.js +0 -591
  69. package/controller/src/__tests__/utils.test.js +0 -48
  70. package/controller/src/defaults.js +0 -25
  71. package/controller/src/index.js +0 -442
  72. package/controller/src/utils.js +0 -18
  73. package/docs/config-schema.json +0 -5787
  74. package/docs/config-schema.json.md +0 -4278
  75. package/docs/demo/config.js +0 -8
  76. package/docs/demo/generate.js +0 -52
  77. package/docs/demo/index.html +0 -2
  78. package/docs/demo/session.js +0 -14
  79. package/docs/pie-schema.json +0 -2911
  80. package/docs/pie-schema.json.md +0 -2142
  81. package/ebsr.png +0 -0
  82. package/lib/index.js +0 -207
  83. package/lib/index.js.map +0 -1
  84. package/lib/print.js +0 -186
  85. package/lib/print.js.map +0 -1
  86. package/module/configure.js +0 -1
  87. package/module/controller.js +0 -5664
  88. package/module/demo.js +0 -77
  89. package/module/element.js +0 -1
  90. package/module/index.html +0 -21
  91. package/module/manifest.json +0 -14
  92. package/module/print-demo.js +0 -115
  93. package/module/print.html +0 -18
  94. package/module/print.js +0 -1
  95. package/src/__tests__/index.test.js +0 -129
  96. package/src/index.js +0 -222
  97. package/src/print.js +0 -207
@@ -1,129 +0,0 @@
1
- import React from 'react';
2
- import { SessionChangedEvent } from '@pie-framework/pie-player-events';
3
- import { isSessionComplete } from '..';
4
-
5
- jest.mock('@pie-lib/math-rendering', () => ({ renderMath: jest.fn() }));
6
- jest.mock('@pie-element/multiple-choice', () => jest.fn());
7
-
8
- const PART_A = 'partA';
9
- const PART_B = 'partB';
10
-
11
- const defaultModel = {
12
- disabled: false,
13
- mode: 'gather',
14
- partA: {
15
- choiceMode: 'radio',
16
- choices: [
17
- { value: 'a', label: 'label a' },
18
- { value: 'b', label: 'label b' },
19
- { value: 'c', label: 'label c', correct: true, feedback: 'great' },
20
- ],
21
- choicePrefix: 'numbers',
22
- prompt: `prompt ${PART_A}`,
23
- },
24
- partB: {
25
- choiceMode: 'radio',
26
- choices: [
27
- { value: 'd', label: 'label d', correct: true, feedback: 'great' },
28
- { value: 'e', label: 'label e' },
29
- { value: 'f', label: 'label f' },
30
- ],
31
- choicePrefix: 'numbers',
32
- prompt: `prompt ${PART_B}`,
33
- },
34
- };
35
-
36
- const defaultSession = { id: 1 };
37
-
38
- describe('ebsr', () => {
39
- let Def;
40
- let el;
41
- let ebsr;
42
-
43
- beforeAll(() => {
44
- Def = require('../index').default;
45
-
46
- // Register the custom element if not already registered
47
- if (!customElements.get('ebsr-element')) {
48
- customElements.define('ebsr-element', Def);
49
- }
50
- });
51
-
52
- beforeEach(() => {
53
- el = document.createElement('ebsr-element');
54
-
55
- // Mock createElement for part elements
56
- ebsr = {
57
- partA: document.createElement('div'),
58
- partB: document.createElement('div'),
59
- };
60
- el.querySelector = jest.fn((s) => {
61
- if (s === '#part-a') {
62
- return ebsr.partA;
63
- } else {
64
- return ebsr.partB;
65
- }
66
- });
67
-
68
- // Mock _render to avoid innerHTML issues with custom elements
69
- el._render = jest.fn();
70
-
71
- // Mock dispatchEvent for testing
72
- el.dispatchEvent = jest.fn();
73
-
74
- el.connectedCallback();
75
- el.model = defaultModel;
76
- el.session = defaultSession;
77
- });
78
-
79
- describe('model', () => {
80
- it('should have set the model', () => {
81
- expect(el._model).toEqual(defaultModel);
82
- });
83
- });
84
-
85
- describe('session', () => {
86
- const dispatchesSessionEvent = (key, value) => {
87
- it(`${key} dispatches session changed event`, () => {
88
- const session = { id: key, value };
89
- el.dispatchSessionChanged(session, key);
90
-
91
- expect(el.dispatchEvent).toBeCalledWith(new SessionChangedEvent('ebsr-element', false));
92
- expect(el._session.value[key]).toEqual(session);
93
- });
94
- };
95
-
96
- it('should have set the session', () => {
97
- expect(el._session).toEqual(defaultSession);
98
- });
99
-
100
- describe('changeAnswers', () => {
101
- dispatchesSessionEvent(PART_A, 'a');
102
- dispatchesSessionEvent(PART_B, 'd');
103
- });
104
- });
105
-
106
- describe('setPartSession', () => {
107
- it('sets the session object even if missing from session.value', () => {
108
- const part = {};
109
- el.model = {};
110
- el.session = {
111
- value: {},
112
- };
113
- el.setPartSession(part, 'partA');
114
- expect(part.session).toEqual({ id: 'partA' });
115
- });
116
- });
117
- });
118
-
119
- describe('isSessionComplete', () => {
120
- const assertComplete = (s, expected) => {
121
- it(`${JSON.stringify(s)} => ${expected}`, () => {
122
- const result = isSessionComplete(s);
123
- expect(result).toEqual(expected);
124
- });
125
- };
126
-
127
- assertComplete({}, false);
128
- assertComplete({ value: { partA: { value: [1] }, partB: { value: [2] } } }, true);
129
- });
package/src/index.js DELETED
@@ -1,222 +0,0 @@
1
- import { SessionChangedEvent } from '@pie-framework/pie-player-events';
2
- import MultipleChoice from '@pie-element/multiple-choice';
3
- import { get } from 'lodash-es';
4
- import debug from 'debug';
5
-
6
- const SESSION_CHANGED = SessionChangedEvent.TYPE;
7
- const MC_TAG_NAME = 'ebsr-multiple-choice';
8
- const log = debug('pie-elements:ebsr');
9
-
10
- class EbsrMC extends MultipleChoice {}
11
-
12
- const defineMultipleChoice = () => {
13
- if (!customElements.get(MC_TAG_NAME)) {
14
- customElements.define(MC_TAG_NAME, EbsrMC);
15
- }
16
- };
17
-
18
- defineMultipleChoice();
19
-
20
- const isNonEmptyArray = (a) => Array.isArray(a) && a.length > 0;
21
-
22
- export const isSessionComplete = (session) => {
23
- const a = get(session, 'value.partA.value');
24
- const b = get(session, 'value.partB.value');
25
-
26
- return isNonEmptyArray(a) && isNonEmptyArray(b);
27
- };
28
-
29
- function getPlayerAttributes(element) {
30
- const player =
31
- element.closest('pie-player') ||
32
- element.closest('pie-item-player');
33
-
34
- if (!player) {
35
- return { baseHeadingLevel: undefined, includeSrHeading: true };
36
- }
37
-
38
- const getRaw = (camelCaseName, hyphenatedName, allLowerName) => {
39
- let raw = player[camelCaseName];
40
-
41
- // fallback in case someone sets via HTML attribute manually
42
- if (raw == null) {
43
- raw =
44
- player.getAttribute(hyphenatedName) ??
45
- player.getAttribute(allLowerName);
46
- }
47
-
48
- return raw;
49
- };
50
-
51
- const levelRaw = getRaw('baseHeadingLevel', 'base-heading-level', 'baseheadinglevel');
52
- const level = parseInt(levelRaw, 10);
53
- const baseHeadingLevel = Number.isFinite(level) && level >= 1 && level <= 6 ? level : undefined;
54
-
55
- const srRaw = getRaw('includeSrHeading', 'include-sr-heading', 'includesrheading');
56
- const includeSrHeading = srRaw == null ? true : srRaw !== false && srRaw !== 'false';
57
-
58
- return { baseHeadingLevel, includeSrHeading };
59
- }
60
-
61
- export default class Ebsr extends HTMLElement {
62
- constructor() {
63
- super();
64
- this._model = {};
65
- this._session = {};
66
- }
67
-
68
- onSessionUpdated = (e) => {
69
- if (e.target === this) {
70
- return;
71
- }
72
-
73
- e.preventDefault();
74
- e.stopImmediatePropagation();
75
-
76
- const id = e.target.getAttribute('id');
77
-
78
- if (id) {
79
- const key = `part${id.toUpperCase()}`;
80
-
81
- if (e.update) {
82
- this._model[key] = e.update;
83
- }
84
- //TODO: accessing a private property here. The session event should contain the update in future to prevent this.
85
- this.dispatchSessionChanged(e.srcElement._session, key);
86
- }
87
- };
88
-
89
- set model(m) {
90
- this._model = m;
91
-
92
- customElements.whenDefined(MC_TAG_NAME).then(() => {
93
- this.setPartModel(this.partA, 'partA');
94
- this.setPartModel(this.partB, 'partB');
95
- });
96
- }
97
-
98
- set session(s) {
99
- this._session = s;
100
-
101
- customElements.whenDefined(MC_TAG_NAME).then(() => {
102
- this.setPartSession(this.partA, 'partA');
103
- this.setPartSession(this.partB, 'partB');
104
- });
105
- }
106
-
107
- get session() {
108
- return this._session;
109
- }
110
-
111
- setPartModel(part, key) {
112
- if (this._model && this._model[key] && part) {
113
- const { mode } = this._model;
114
-
115
- part.model = {
116
- ...this._model[key],
117
- mode,
118
- keyMode: this._model[key].choicePrefix,
119
- };
120
-
121
- // Parts of an EBSR item should not render their own SR headings —
122
- // the EBSR element itself provides the item-level heading.
123
- const { includeSrHeading, baseHeadingLevel } = getPlayerAttributes(this);
124
- part.includeSrHeading = includeSrHeading;
125
- part.baseHeadingLevel = baseHeadingLevel;
126
- }
127
- }
128
-
129
- setPartSession(part, key) {
130
- if (this._session && this._model && part) {
131
- const { value } = this._session;
132
- part.session = value && value[key] ? value[key] : { id: key };
133
- }
134
- }
135
-
136
- dispatchSessionChanged(partSession, key) {
137
- this._session.value = {
138
- ...this._session.value,
139
- [key]: partSession,
140
- };
141
-
142
- log('[onSessionChanged] session: ', this._session);
143
- const complete = isSessionComplete(this._session);
144
- this.dispatchEvent(new SessionChangedEvent(this.tagName.toLowerCase(), complete));
145
- }
146
-
147
- get partA() {
148
- return this.querySelector(`${MC_TAG_NAME}#a`);
149
- }
150
-
151
- get partB() {
152
- return this.querySelector(`${MC_TAG_NAME}#b`);
153
- }
154
-
155
- connectedCallback() {
156
- this._render();
157
- this._initPlayerObserver();
158
- this.addEventListener(SESSION_CHANGED, this.onSessionUpdated);
159
- }
160
-
161
- disconnectedCallback() {
162
- this._disconnectPlayerObserver();
163
- this.removeEventListener(SESSION_CHANGED, this.onSessionUpdated);
164
- }
165
-
166
- _initPlayerObserver() {
167
- const player = this.closest('pie-player') || this.closest('pie-item-player');
168
- if (!player) return;
169
-
170
- this._playerObserver = new MutationObserver(() => {
171
- this._render();
172
- });
173
- this._playerObserver.observe(player, {
174
- attributes: true,
175
- attributeFilter: ['base-heading-level', 'baseheadinglevel', 'include-sr-heading', 'includesrheading'],
176
- });
177
- }
178
-
179
- _disconnectPlayerObserver() {
180
- if (this._playerObserver) {
181
- this._playerObserver.disconnect();
182
- this._playerObserver = null;
183
- }
184
- }
185
-
186
- _render() {
187
- this.ariaLabel = 'Two-Part Question';
188
- this.role = 'region';
189
-
190
- const { baseHeadingLevel: ebsrLevel, includeSrHeading } = getPlayerAttributes(this);
191
- const headingTag = ebsrLevel ? `h${Math.min(6, ebsrLevel)}` : 'h2';
192
- const srHeading = includeSrHeading ? `<${headingTag} class="srOnly">Two-Part Question</${headingTag}>` : '';
193
-
194
- this.innerHTML = `
195
- <style>
196
- .srOnly {
197
- position: absolute;
198
- width: 1px;
199
- height: 1px;
200
- padding: 0;
201
- margin: -1px;
202
- overflow: hidden;
203
- left: -10000px;
204
- top: auto;
205
- }
206
- ${this._model?.extraCSSRules?.rules}
207
- </style>
208
- ${srHeading}
209
- <${MC_TAG_NAME} id="a"></${MC_TAG_NAME}>
210
- <${MC_TAG_NAME} id="b"></${MC_TAG_NAME}>
211
- `;
212
-
213
- // when item is re-rendered (due to connectedCallback), if the custom element is already defined,
214
- // we need to set the model and session, otherwise the setters are not reached again
215
- if (customElements.get(MC_TAG_NAME)) {
216
- this.setPartModel(this.partA, 'partA');
217
- this.setPartModel(this.partB, 'partB');
218
- this.setPartSession(this.partA, 'partA');
219
- this.setPartSession(this.partB, 'partB');
220
- }
221
- }
222
- }
package/src/print.js DELETED
@@ -1,207 +0,0 @@
1
- import { cloneDeep, get } from 'lodash-es';
2
- import MultipleChoice from '@pie-element/multiple-choice';
3
- import debug from 'debug';
4
- import { SessionChangedEvent } from '@pie-framework/pie-player-events';
5
- const MC_TAG_NAME = 'ebsr-multiple-choice';
6
- const SESSION_CHANGED = SessionChangedEvent.TYPE;
7
- import Translator from '@pie-lib/translator';
8
-
9
- const { translator } = Translator;
10
-
11
- const log = debug('pie-element:ebsr:print');
12
-
13
- /**
14
- * Live in same package as main element - so we can access some of the shared comps!
15
- *
16
- * - update pslb to build print if src/print.js is there
17
- * - update demo el
18
- * - get configure/controller building
19
- */
20
-
21
- const preparePrintModel = (model, opts) => {
22
- const instr = opts.role === 'instructor';
23
- const printModel = cloneDeep(model);
24
-
25
- printModel.prompt = printModel.promptEnabled !== false ? printModel.prompt : undefined;
26
- printModel.teacherInstructions =
27
- instr && printModel.teacherInstructionsEnabled !== false ? printModel.teacherInstructions : undefined;
28
- printModel.showTeacherInstructions = instr;
29
- printModel.alwaysShowCorrect = instr;
30
- printModel.mode = instr ? 'evaluate' : 'gather';
31
-
32
- printModel.disabled = true;
33
- printModel.animationsDisabled = true;
34
- printModel.lockChoiceOrder = true;
35
- printModel.choicesLayout = printModel.choicesLayout || 'vertical';
36
-
37
- printModel.choices = (printModel.choices || []).map((c) => {
38
- c.rationale = instr && printModel.rationaleEnabled !== false ? c.rationale : undefined;
39
- c.hideTick = instr;
40
- c.feedback = undefined;
41
- return c;
42
- });
43
-
44
- printModel.keyMode = printModel.choicePrefix || 'letters';
45
-
46
- return printModel;
47
- };
48
-
49
- class EbsrMC extends MultipleChoice {}
50
-
51
- const defineMultipleChoice = () => {
52
- if (!customElements.get(MC_TAG_NAME)) {
53
- customElements.define(MC_TAG_NAME, EbsrMC);
54
- }
55
- };
56
-
57
- defineMultipleChoice();
58
-
59
- const isNonEmptyArray = (a) => Array.isArray(a) && a.length > 0;
60
-
61
- export const isSessionComplete = (session) => {
62
- const a = get(session, 'value.partA.value');
63
- const b = get(session, 'value.partB.value');
64
-
65
- return isNonEmptyArray(a) && isNonEmptyArray(b);
66
- };
67
-
68
- export default class Ebsr extends HTMLElement {
69
- constructor() {
70
- super();
71
- this._model = {};
72
- this._session = {};
73
- this._options = null;
74
- }
75
-
76
- onSessionUpdated = (e) => {
77
- if (e.target === this) {
78
- return;
79
- }
80
-
81
- e.preventDefault();
82
- e.stopImmediatePropagation();
83
-
84
- const id = e.target.getAttribute('id');
85
-
86
- if (id) {
87
- const key = `part${id.toUpperCase()}`;
88
-
89
- if (e.update) {
90
- this._model[key] = e.update;
91
- }
92
- //TODO: accessing a private property here. The session event should contain the update in future to prevent this.
93
- this.dispatchSessionChanged(e.srcElement._session, key);
94
- }
95
- };
96
-
97
- set model(m) {
98
- this._model = m;
99
- this._updateParts();
100
- }
101
-
102
- set session(s) {
103
- this._session = s;
104
-
105
- customElements.whenDefined(MC_TAG_NAME).then(() => {
106
- this.setPartSession(this.partA, 'partA');
107
- this.setPartSession(this.partB, 'partB');
108
- });
109
- }
110
-
111
- _updateParts() {
112
- customElements.whenDefined(MC_TAG_NAME).then(() => {
113
- this.setPartModel(this.partA, 'partA');
114
- this.setPartModel(this.partB, 'partB');
115
- });
116
- }
117
-
118
- setPartModel(part, key) {
119
- if (this._model && this._model[key] && part && this._options) {
120
- let labels = {
121
- partA: undefined,
122
- partB: undefined,
123
- };
124
-
125
- if (this._model.partLabels) {
126
- const language = this._model.language;
127
-
128
- labels = {
129
- partA: translator.t('ebsr.part', {
130
- lng: language,
131
- index: this._model.partLabelType === 'Letters' ? 'A' : '1',
132
- }),
133
- partB: translator.t('ebsr.part', {
134
- lng: language,
135
- index: this._model.partLabelType === 'Letters' ? 'B' : '2',
136
- }),
137
- };
138
- }
139
-
140
- part.model = {
141
- ...preparePrintModel(this._model[key], this._options),
142
- keyMode: this._model[key].choicePrefix,
143
- partLabel: labels[key],
144
- };
145
-
146
- // pass options to enable print mode detection in multiple-choice component
147
- part.options = this._options;
148
-
149
- if (!part._session) {
150
- // for print, "set session" is not called,
151
- // but ebsr needs sessions in order to render the elements,
152
- // so we set it here it was not set already
153
- part.session = {};
154
- }
155
- }
156
- }
157
-
158
- set options(o) {
159
- this._options = o;
160
- // re-render parts so role changes (student/instructor) propagate to each part
161
- this._updateParts();
162
- }
163
-
164
- setPartSession(part, key) {
165
- if (this._session && this._model && part) {
166
- const { value } = this._session;
167
- part.session = value && value[key] ? value[key] : { id: key };
168
- }
169
- }
170
-
171
- dispatchSessionChanged(partSession, key) {
172
- this._session.value = {
173
- ...this._session.value,
174
- [key]: partSession,
175
- };
176
-
177
- log('[onSessionChanged] session: ', this._session);
178
- const complete = isSessionComplete(this._session);
179
- this.dispatchEvent(new SessionChangedEvent(this.tagName.toLowerCase(), complete));
180
- }
181
-
182
- get partA() {
183
- return this.querySelector(`${MC_TAG_NAME}#a`);
184
- }
185
-
186
- get partB() {
187
- return this.querySelector(`${MC_TAG_NAME}#b`);
188
- }
189
-
190
- connectedCallback() {
191
- this._render();
192
- this.addEventListener(SESSION_CHANGED, this.onSessionUpdated);
193
- }
194
-
195
- disconnectedCallback() {
196
- this.removeEventListener(SESSION_CHANGED, this.onSessionUpdated);
197
- }
198
-
199
- _render() {
200
- this.innerHTML = `
201
- <div>
202
- <${MC_TAG_NAME} id="a"></${MC_TAG_NAME}>
203
- <${MC_TAG_NAME} id="b"></${MC_TAG_NAME}>
204
- </div>
205
- `;
206
- }
207
- }