@pie-element/image-cloze-association 10.1.2-next.2 → 10.1.2

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 (130) hide show
  1. package/CHANGELOG.json +437 -0
  2. package/CHANGELOG.md +1997 -0
  3. package/LICENSE.md +5 -0
  4. package/README.md +1 -0
  5. package/configure/CHANGELOG.json +197 -0
  6. package/configure/CHANGELOG.md +1600 -0
  7. package/configure/lib/defaults.js +86 -0
  8. package/configure/lib/defaults.js.map +1 -0
  9. package/configure/lib/index.js +99 -0
  10. package/configure/lib/index.js.map +1 -0
  11. package/configure/lib/root.js +135 -0
  12. package/configure/lib/root.js.map +1 -0
  13. package/configure/package.json +23 -0
  14. package/configure/src/__tests__/index.test.js +155 -0
  15. package/configure/src/defaults.js +59 -0
  16. package/configure/src/index.js +114 -0
  17. package/configure/src/root.jsx +116 -0
  18. package/controller/CHANGELOG.json +137 -0
  19. package/controller/CHANGELOG.md +1149 -0
  20. package/controller/lib/defaults.js +14 -0
  21. package/controller/lib/defaults.js.map +1 -0
  22. package/controller/lib/index.js +304 -0
  23. package/controller/lib/index.js.map +1 -0
  24. package/controller/lib/utils.js +70 -0
  25. package/controller/lib/utils.js.map +1 -0
  26. package/controller/package.json +19 -0
  27. package/controller/src/__tests__/index.test.js +711 -0
  28. package/controller/src/defaults.js +7 -0
  29. package/controller/src/index.js +322 -0
  30. package/controller/src/utils.js +72 -0
  31. package/docs/config-schema.json +1382 -0
  32. package/docs/config-schema.json.md +1021 -0
  33. package/docs/demo/config.js +8 -0
  34. package/docs/demo/generate.js +74 -0
  35. package/docs/demo/index.html +1 -0
  36. package/docs/demo/session.js +16 -0
  37. package/docs/pie-schema.json +1085 -0
  38. package/docs/pie-schema.json.md +810 -0
  39. package/lib/constants.js +12 -0
  40. package/lib/constants.js.map +1 -0
  41. package/lib/evaluation-icon.js +60 -0
  42. package/lib/evaluation-icon.js.map +1 -0
  43. package/lib/image-container.js +94 -0
  44. package/lib/image-container.js.map +1 -0
  45. package/lib/image-drop-target.js +130 -0
  46. package/lib/image-drop-target.js.map +1 -0
  47. package/lib/index.js +220 -0
  48. package/lib/index.js.map +1 -0
  49. package/lib/interactive-section.js +104 -0
  50. package/lib/interactive-section.js.map +1 -0
  51. package/lib/possible-response.js +161 -0
  52. package/lib/possible-response.js.map +1 -0
  53. package/lib/possible-responses.js +58 -0
  54. package/lib/possible-responses.js.map +1 -0
  55. package/lib/root.js +491 -0
  56. package/lib/root.js.map +1 -0
  57. package/lib/static-html-span.js +35 -0
  58. package/lib/static-html-span.js.map +1 -0
  59. package/lib/utils-correctness.js +89 -0
  60. package/lib/utils-correctness.js.map +1 -0
  61. package/package.json +21 -86
  62. package/src/__tests__/index.test.js +174 -0
  63. package/src/__tests__/root.test.jsx +99 -0
  64. package/src/__tests__/utils.test.js +207 -0
  65. package/src/constants.js +5 -0
  66. package/src/evaluation-icon.jsx +54 -0
  67. package/src/image-container.jsx +90 -0
  68. package/src/image-drop-target.jsx +140 -0
  69. package/src/index.js +245 -0
  70. package/src/interactive-section.jsx +94 -0
  71. package/src/possible-response.jsx +152 -0
  72. package/src/possible-responses.jsx +52 -0
  73. package/src/root.jsx +490 -0
  74. package/src/static-html-span.jsx +30 -0
  75. package/src/utils-correctness.js +95 -0
  76. package/configure.js +0 -2
  77. package/controller.js +0 -1
  78. package/dist/author/defaults.d.ts +0 -88
  79. package/dist/author/defaults.js +0 -58
  80. package/dist/author/index.d.ts +0 -34
  81. package/dist/author/index.js +0 -71
  82. package/dist/author/root.d.ts +0 -14
  83. package/dist/author/root.js +0 -80
  84. package/dist/browser/Check-DL1c-mLM.js +0 -10708
  85. package/dist/browser/Check-DL1c-mLM.js.map +0 -1
  86. package/dist/browser/author/index.js +0 -38597
  87. package/dist/browser/author/index.js.map +0 -1
  88. package/dist/browser/controller/index.js +0 -171
  89. package/dist/browser/controller/index.js.map +0 -1
  90. package/dist/browser/delivery/index.js +0 -2699
  91. package/dist/browser/delivery/index.js.map +0 -1
  92. package/dist/browser/dist-BphSS14E.js +0 -346
  93. package/dist/browser/dist-BphSS14E.js.map +0 -1
  94. package/dist/browser/humps-CZ4RCLab.js +0 -67
  95. package/dist/browser/humps-CZ4RCLab.js.map +0 -1
  96. package/dist/browser/image-cloze-association.css +0 -2
  97. package/dist/controller/defaults.d.ts +0 -16
  98. package/dist/controller/defaults.js +0 -10
  99. package/dist/controller/index.d.ts +0 -23
  100. package/dist/controller/index.js +0 -122
  101. package/dist/controller/utils.d.ts +0 -14
  102. package/dist/controller/utils.js +0 -36
  103. package/dist/delivery/constants.d.ts +0 -14
  104. package/dist/delivery/evaluation-icon.d.ts +0 -28
  105. package/dist/delivery/evaluation-icon.js +0 -38
  106. package/dist/delivery/image-container.d.ts +0 -13
  107. package/dist/delivery/image-container.js +0 -61
  108. package/dist/delivery/image-drop-target.d.ts +0 -45
  109. package/dist/delivery/image-drop-target.js +0 -90
  110. package/dist/delivery/index.d.ts +0 -20
  111. package/dist/delivery/index.js +0 -110
  112. package/dist/delivery/interactive-section.d.ts +0 -15
  113. package/dist/delivery/interactive-section.js +0 -72
  114. package/dist/delivery/possible-response.d.ts +0 -34
  115. package/dist/delivery/possible-response.js +0 -100
  116. package/dist/delivery/possible-responses.d.ts +0 -31
  117. package/dist/delivery/possible-responses.js +0 -41
  118. package/dist/delivery/root.d.ts +0 -21
  119. package/dist/delivery/root.js +0 -278
  120. package/dist/delivery/static-html-span.d.ts +0 -14
  121. package/dist/delivery/static-html-span.js +0 -22
  122. package/dist/delivery/utils-correctness.d.ts +0 -10
  123. package/dist/delivery/utils-correctness.js +0 -43
  124. package/dist/index.d.ts +0 -1
  125. package/dist/index.iife.d.ts +0 -8
  126. package/dist/index.iife.js +0 -152
  127. package/dist/index.js +0 -2
  128. package/dist/node_modules/.bun/clsx@2.1.1/node_modules/clsx/dist/clsx.js +0 -16
  129. package/dist/runtime-support.d.ts +0 -12
  130. package/dist/runtime-support.js +0 -12
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getUnansweredAnswers = exports.getAnswersCorrectness = void 0;
7
+ var _humps = require("humps");
8
+ // functions also used in controller/src/utils.js
9
+ // camelize keys is needed to convert the keys from snake_case to camelCase
10
+ // this is also done in the controller
11
+
12
+ const getAllCorrectAnswers = (answers, responses) => (answers || []).map(answer => ({
13
+ ...answer,
14
+ isCorrect: (responses[answer.containerIndex] && responses[answer.containerIndex].images || []).includes(answer.value)
15
+ }));
16
+ const getValidAnswer = (answer, response) => (response[answer.containerIndex].images || []).filter(res => res === answer.value);
17
+ const getUniqueCorrectAnswers = (answers, validResponses) => {
18
+ let finalAnswers = answers;
19
+ answers.forEach(answer1 => {
20
+ const valuesToParse = answers.filter(answer2 => answer2.value === answer1.value && answer2.containerIndex === answer1.containerIndex);
21
+ if (valuesToParse.length > 1) {
22
+ // point only to duplicates but first
23
+ valuesToParse.shift();
24
+ // mark duplicates as incorrect
25
+ valuesToParse.forEach((value, index) => {
26
+ finalAnswers = (finalAnswers || []).map(finalAnswer => {
27
+ if (finalAnswer.id === value.id) {
28
+ let valid = getValidAnswer(finalAnswer, validResponses);
29
+ return {
30
+ ...finalAnswer,
31
+ isCorrect: valid.length > index + 1
32
+ };
33
+ }
34
+ return finalAnswer;
35
+ });
36
+ });
37
+ }
38
+ });
39
+ return finalAnswers;
40
+ };
41
+ const getUnansweredAnswers = (answers, validation) => {
42
+ const camelizedValidation = (0, _humps.camelizeKeys)(validation);
43
+ const {
44
+ validResponse: {
45
+ value
46
+ } = {}
47
+ } = camelizedValidation;
48
+ return (value || []).reduce((unanswered, response, index) => {
49
+ const isAnswered = !!answers.find(answer => answer.containerIndex === index);
50
+ response.images = response.images || [];
51
+ if (!isAnswered) {
52
+ return [...unanswered, {
53
+ id: `unanswered-${index}`,
54
+ value: response.images[0] || '',
55
+ containerIndex: index,
56
+ isCorrect: !response.images.length ? undefined : false,
57
+ hidden: true
58
+ }];
59
+ }
60
+ return unanswered;
61
+ }, []);
62
+ };
63
+ exports.getUnansweredAnswers = getUnansweredAnswers;
64
+ const getAnswersCorrectness = (answers, validation) => {
65
+ const camelizedValidation = (0, _humps.camelizeKeys)(validation);
66
+ const {
67
+ validResponse: {
68
+ value
69
+ },
70
+ altResponses
71
+ } = camelizedValidation;
72
+ const allCorrect = getAllCorrectAnswers(answers, value);
73
+ const uniqueAnswers = getUniqueCorrectAnswers(allCorrect, value);
74
+ const noOfCorrect = uniqueAnswers.filter(answer => answer.isCorrect).length;
75
+
76
+ // Look for alternate correct responses if there are incorrect responses.
77
+ if (noOfCorrect < uniqueAnswers.length && altResponses && altResponses.length) {
78
+ const altUniqueStack = (altResponses || []).map(altResponse => {
79
+ const altValue = altResponse.value;
80
+ const altAllCorrect = getAllCorrectAnswers(answers, altValue);
81
+ return getUniqueCorrectAnswers(altAllCorrect, altValue);
82
+ });
83
+ // Return the one with most correct answers.
84
+ return altUniqueStack.sort((a, b) => b.filter(c => c.isCorrect).length - a.filter(c => c.isCorrect).length)[0];
85
+ }
86
+ return uniqueAnswers;
87
+ };
88
+ exports.getAnswersCorrectness = getAnswersCorrectness;
89
+ //# sourceMappingURL=utils-correctness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils-correctness.js","names":["_humps","require","getAllCorrectAnswers","answers","responses","map","answer","isCorrect","containerIndex","images","includes","value","getValidAnswer","response","filter","res","getUniqueCorrectAnswers","validResponses","finalAnswers","forEach","answer1","valuesToParse","answer2","length","shift","index","finalAnswer","id","valid","getUnansweredAnswers","validation","camelizedValidation","camelizeKeys","validResponse","reduce","unanswered","isAnswered","find","undefined","hidden","exports","getAnswersCorrectness","altResponses","allCorrect","uniqueAnswers","noOfCorrect","altUniqueStack","altResponse","altValue","altAllCorrect","sort","a","b","c"],"sources":["../src/utils-correctness.js"],"sourcesContent":["// functions also used in controller/src/utils.js\n// camelize keys is needed to convert the keys from snake_case to camelCase\n// this is also done in the controller\nimport { camelizeKeys } from 'humps';\n\nconst getAllCorrectAnswers = (answers, responses) =>\n (answers || []).map((answer) => ({\n ...answer,\n isCorrect: ((responses[answer.containerIndex] && responses[answer.containerIndex].images) || []).includes(\n answer.value,\n ),\n }));\n\nconst getValidAnswer = (answer, response) =>\n (response[answer.containerIndex].images || []).filter((res) => res === answer.value);\n\nconst getUniqueCorrectAnswers = (answers, validResponses) => {\n let finalAnswers = answers;\n\n answers.forEach((answer1) => {\n const valuesToParse = answers.filter(\n (answer2) => answer2.value === answer1.value && answer2.containerIndex === answer1.containerIndex,\n );\n\n if (valuesToParse.length > 1) {\n // point only to duplicates but first\n valuesToParse.shift();\n // mark duplicates as incorrect\n valuesToParse.forEach((value, index) => {\n finalAnswers = (finalAnswers || []).map((finalAnswer) => {\n if (finalAnswer.id === value.id) {\n let valid = getValidAnswer(finalAnswer, validResponses);\n\n return {\n ...finalAnswer,\n isCorrect: valid.length > index + 1,\n };\n }\n return finalAnswer;\n });\n });\n }\n });\n return finalAnswers;\n};\n\nexport const getUnansweredAnswers = (answers, validation) => {\n const camelizedValidation = camelizeKeys(validation);\n const { validResponse: { value } = {} } = camelizedValidation;\n\n return (value || []).reduce((unanswered, response, index) => {\n const isAnswered = !!answers.find((answer) => answer.containerIndex === index);\n response.images = response.images || [];\n\n if (!isAnswered) {\n return [\n ...unanswered,\n {\n id: `unanswered-${index}`,\n value: response.images[0] || '',\n containerIndex: index,\n isCorrect: !response.images.length ? undefined : false,\n hidden: true,\n },\n ];\n }\n\n return unanswered;\n }, []);\n};\n\nexport const getAnswersCorrectness = (answers, validation) => {\n const camelizedValidation = camelizeKeys(validation);\n const {\n validResponse: { value },\n altResponses,\n } = camelizedValidation;\n\n const allCorrect = getAllCorrectAnswers(answers, value);\n const uniqueAnswers = getUniqueCorrectAnswers(allCorrect, value);\n const noOfCorrect = uniqueAnswers.filter((answer) => answer.isCorrect).length;\n\n // Look for alternate correct responses if there are incorrect responses.\n if (noOfCorrect < uniqueAnswers.length && altResponses && altResponses.length) {\n const altUniqueStack = (altResponses || []).map((altResponse) => {\n const altValue = altResponse.value;\n\n const altAllCorrect = getAllCorrectAnswers(answers, altValue);\n return getUniqueCorrectAnswers(altAllCorrect, altValue);\n });\n // Return the one with most correct answers.\n return altUniqueStack.sort((a, b) => b.filter((c) => c.isCorrect).length - a.filter((c) => c.isCorrect).length)[0];\n }\n return uniqueAnswers;\n};\n"],"mappings":"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","ignoreList":[]}
package/package.json CHANGED
@@ -1,101 +1,36 @@
1
1
  {
2
2
  "name": "@pie-element/image-cloze-association",
3
- "version": "10.1.2-next.2",
3
+ "repository": "pie-framework/pie-elements",
4
+ "version": "10.1.2",
4
5
  "description": "",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
5
9
  "dependencies": {
6
10
  "@dnd-kit/core": "6.3.1",
7
11
  "@emotion/react": "^11.14.0",
8
12
  "@emotion/style": "^0.8.0",
9
13
  "@mui/icons-material": "^7.3.4",
10
14
  "@mui/material": "^7.3.4",
11
- "@pie-lib/correct-answer-toggle": "4.0.3-next.37",
12
- "@pie-lib/drag": "4.0.3-next.37",
13
- "@pie-lib/render-ui": "6.1.1-next.37",
14
- "@pie-lib/translator": "4.0.3-next.0",
15
+ "@pie-framework/pie-player-events": "^0.1.0",
16
+ "@pie-lib/correct-answer-toggle": "4.0.3",
17
+ "@pie-lib/drag": "4.0.3",
18
+ "@pie-lib/math-rendering": "5.0.2",
19
+ "@pie-lib/render-ui": "6.1.1",
20
+ "@pie-lib/translator": "4.0.2",
21
+ "classnames": "^2.2.6",
15
22
  "humps": "^2.0.1",
16
23
  "prop-types": "^15.8.1",
17
- "react-transition-group": "^4.4.5",
18
- "@pie-element/shared-configure-events": "0.1.0",
19
- "debug": "^4.4.3",
20
- "@pie-lib/config-ui": "13.0.4-next.30",
21
- "@pie-lib/editable-html-tip-tap": "2.1.2-next.30",
22
- "@pie-element/shared-controller-utils": "0.1.1-next.1",
23
- "@pie-element/shared-lodash": "0.1.1-next.0",
24
- "clsx": "^2.1.1",
25
- "@pie-element/shared-math-rendering-mathjax": "0.1.1-next.0",
26
- "@pie-element/shared-player-events": "0.1.0"
27
- },
28
- "peerDependencies": {
29
- "react": "^18.0.0",
30
- "react-dom": "^18.0.0"
31
- },
32
- "exports": {
33
- ".": {
34
- "types": "./dist/index.d.ts",
35
- "default": "./dist/index.js"
36
- },
37
- "./delivery": {
38
- "types": "./dist/delivery/index.d.ts",
39
- "default": "./dist/delivery/index.js"
40
- },
41
- "./browser/delivery": {
42
- "default": "./dist/browser/delivery/index.js"
43
- },
44
- "./author": {
45
- "types": "./dist/author/index.d.ts",
46
- "default": "./dist/author/index.js"
47
- },
48
- "./browser/author": {
49
- "default": "./dist/browser/author/index.js"
50
- },
51
- "./configure": {
52
- "types": "./dist/author/index.d.ts",
53
- "default": "./dist/author/index.js"
54
- },
55
- "./controller": {
56
- "types": "./dist/controller/index.d.ts",
57
- "default": "./dist/controller/index.js"
58
- },
59
- "./controller.js": {
60
- "types": "./dist/controller/index.d.ts",
61
- "default": "./dist/controller/index.js"
62
- },
63
- "./browser/controller": {
64
- "default": "./dist/browser/controller/index.js"
65
- },
66
- "./runtime-support": {
67
- "types": "./dist/runtime-support.d.ts",
68
- "default": "./dist/runtime-support.js"
69
- }
70
- },
71
- "type": "module",
72
- "main": "./dist/index.js",
73
- "types": "./dist/index.d.ts",
74
- "files": [
75
- "configure.js",
76
- "controller.js",
77
- "dist"
78
- ],
79
- "sideEffects": false,
80
- "devDependencies": {
81
- "vite": "^8.0.1",
82
- "typescript": "^5.9.3",
83
- "@vitejs/plugin-react": "^6.0.1",
84
- "@types/react": "^18.2.0",
85
- "@types/react-dom": "^18.2.0"
24
+ "react": "18.3.1",
25
+ "react-dom": "18.3.1",
26
+ "react-transition-group": "^4.4.5"
86
27
  },
28
+ "author": "pie framework developers",
29
+ "license": "ISC",
30
+ "gitHead": "206ff17a94e8e197744d059f97ad2b9a1bc9145d",
87
31
  "scripts": {
88
- "build": "bun x vite build && bun x vite build --config ../../../tools/vite/element-browser.config.ts && bun x vite build --config vite.config.iife.ts && bun x tsc --emitDeclarationOnly",
89
- "dev": "bun x vite",
90
- "demo": "bun x vite --mode demo",
91
- "test": "bun x vitest run"
32
+ "postpublish": "../../scripts/postpublish"
92
33
  },
93
- "pie": {
94
- "controller": "@pie-element/image-cloze-association/controller",
95
- "configure": "@pie-element/image-cloze-association/configure",
96
- "browserSharedDependencies": {
97
- "react": "18.2.0",
98
- "react-dom": "18.2.0"
99
- }
100
- }
34
+ "main": "lib/index.js",
35
+ "module": "src/index.js"
101
36
  }
@@ -0,0 +1,174 @@
1
+ import React from 'react';
2
+ import { render } from '@testing-library/react';
3
+ import { createRoot } from 'react-dom/client';
4
+ import { ModelSetEvent, SessionChangedEvent } from '@pie-framework/pie-player-events';
5
+ import { ImageClozeAssociationComponent } from '../root';
6
+
7
+ jest.mock('@pie-lib/math-rendering', () => ({ renderMath: jest.fn() }));
8
+
9
+ jest.mock('@dnd-kit/core', () => ({
10
+ DragOverlay: ({ children }) => <div>{children}</div>,
11
+ useDraggable: () => ({
12
+ setNodeRef: jest.fn(),
13
+ attributes: {},
14
+ listeners: {},
15
+ }),
16
+ useDroppable: () => ({
17
+ setNodeRef: jest.fn(),
18
+ isOver: false,
19
+ }),
20
+ }));
21
+
22
+ jest.mock('@pie-lib/drag', () => ({
23
+ DragProvider: ({ children }) => <div>{children}</div>,
24
+ ICADroppablePlaceholder: ({ children }) => <div>{children}</div>,
25
+ }));
26
+
27
+ const mockRender = jest.fn();
28
+ const mockUnmount = jest.fn();
29
+ const mockCreateRoot = jest.fn(() => ({
30
+ render: mockRender,
31
+ unmount: mockUnmount,
32
+ }));
33
+
34
+ jest.mock('react-dom/client', () => ({
35
+ createRoot: (...args) => mockCreateRoot(...args),
36
+ }));
37
+
38
+ jest.mock('../index', () => {
39
+ const { ModelSetEvent, SessionChangedEvent } = require('@pie-framework/pie-player-events');
40
+
41
+ class MockHTMLElement {
42
+ constructor() {
43
+ this._root = null;
44
+ this._model = null;
45
+ this._session = null;
46
+ this.dispatchEvent = jest.fn();
47
+ this.audioComplete = false;
48
+ this.tagName = 'image-cloze-association';
49
+ }
50
+
51
+ querySelector() {
52
+ return null;
53
+ }
54
+ }
55
+
56
+ return {
57
+ __esModule: true,
58
+ default: class ImageClozeAssociation extends MockHTMLElement {
59
+ constructor() {
60
+ super();
61
+ }
62
+
63
+ set model(m) {
64
+ this._model = m;
65
+ this.dispatchEvent(new ModelSetEvent(this.tagName.toLowerCase(), this.isComplete(), !!this._model));
66
+ this._render();
67
+ }
68
+
69
+ set session(s) {
70
+ this._session = s;
71
+ this._render();
72
+ }
73
+
74
+ updateAnswer = (answers) => {
75
+ this._session = { ...this._session, answers };
76
+ this.dispatchEvent(new SessionChangedEvent(this.tagName.toLowerCase(), this.isComplete()));
77
+ };
78
+
79
+ isComplete() {
80
+ const { responseAreasToBeFilled } = this._model || {};
81
+ if (!this._session || !this._session.answers) {
82
+ return false;
83
+ }
84
+ const { answers } = this._session;
85
+ if (!Array.isArray(answers)) {
86
+ return false;
87
+ }
88
+ const filledResponseAreas = [...new Map(answers.map((item) => [item.containerIndex, item])).values()].length;
89
+ return filledResponseAreas >= responseAreasToBeFilled;
90
+ }
91
+
92
+ _render() {
93
+ if (!this._root) {
94
+ this._root = mockCreateRoot(global.document.createElement('div'));
95
+ }
96
+ this._root.render(null);
97
+ }
98
+ },
99
+ };
100
+ });
101
+
102
+ const ImageClozeAssociation = require('../index').default;
103
+
104
+ describe('image-cloze-association', () => {
105
+ beforeEach(() => {
106
+ mockRender.mockClear();
107
+ mockCreateRoot.mockClear();
108
+ });
109
+
110
+ describe('events', () => {
111
+ describe('model', () => {
112
+ it('dispatches model set event', () => {
113
+ const el = new ImageClozeAssociation();
114
+ el.tagName = 'ica-el';
115
+ el.model = {};
116
+ expect(el.dispatchEvent).toBeCalledWith(new ModelSetEvent('ica-el', false, true));
117
+ });
118
+
119
+ it('calls render', () => {
120
+ const el = new ImageClozeAssociation();
121
+ el.model = {};
122
+ const rootInstance = mockCreateRoot.mock.results[0].value;
123
+ expect(rootInstance.render).toHaveBeenCalled();
124
+ });
125
+ });
126
+
127
+ describe('updateAnswer', () => {
128
+ it('dispatches session changed event - add answer', () => {
129
+ const el = new ImageClozeAssociation();
130
+ el.tagName = 'ica-el';
131
+ el.model = {
132
+ responseAreasToBeFilled: 1,
133
+ };
134
+ el.session = { answers: [] };
135
+ el.updateAnswer([{ id: '1', containerIndex: 0, value: '' }]);
136
+ expect(el.dispatchEvent).toBeCalledWith(new SessionChangedEvent('ica-el', true));
137
+ });
138
+
139
+ it('dispatches session changed event - remove answer', () => {
140
+ const el = new ImageClozeAssociation();
141
+ el.tagName = 'ica-el';
142
+ el.model = {
143
+ responseAreasToBeFilled: 1,
144
+ };
145
+ el.session = { answers: [{ id: '1', containerIndex: 0, value: '' }] };
146
+ el.updateAnswer([]);
147
+ expect(el.dispatchEvent).toBeCalledWith(new SessionChangedEvent('ica-el', false));
148
+ });
149
+
150
+ it('dispatches session changed event - add/remove answer', () => {
151
+ const el = new ImageClozeAssociation();
152
+ el.tagName = 'ica-el';
153
+ el.model = {
154
+ responseAreasToBeFilled: 2,
155
+ };
156
+ el.session = { answers: [] };
157
+ el.updateAnswer([{ id: '1', containerIndex: 0, value: '' }]);
158
+ expect(el.dispatchEvent).toBeCalledWith(new SessionChangedEvent('ica-el', false));
159
+
160
+ el.updateAnswer([
161
+ { id: '1', containerIndex: 0, value: '' },
162
+ { id: '2', containerIndex: 1, value: '' },
163
+ ]);
164
+ expect(el.dispatchEvent).toBeCalledWith(new SessionChangedEvent('ica-el', true));
165
+
166
+ el.updateAnswer([{ id: '2', containerIndex: 1, value: '' }]);
167
+ expect(el.dispatchEvent).toBeCalledWith(new SessionChangedEvent('ica-el', false));
168
+
169
+ el.updateAnswer([]);
170
+ expect(el.dispatchEvent).toBeCalledWith(new SessionChangedEvent('ica-el', false));
171
+ });
172
+ });
173
+ });
174
+ });
@@ -0,0 +1,99 @@
1
+ import React from 'react';
2
+ import { render } from '@testing-library/react';
3
+ import { ImageClozeAssociationComponent as Root } from '../root';
4
+
5
+ jest.mock('@dnd-kit/core', () => ({
6
+ DragOverlay: ({ children }) => <div>{children}</div>,
7
+ useDraggable: () => ({
8
+ setNodeRef: jest.fn(),
9
+ attributes: {},
10
+ listeners: {},
11
+ }),
12
+ useDroppable: () => ({
13
+ setNodeRef: jest.fn(),
14
+ isOver: false,
15
+ }),
16
+ }));
17
+
18
+ jest.mock('@pie-lib/drag', () => ({
19
+ DragProvider: ({ children }) => <div>{children}</div>,
20
+ ICADroppablePlaceholder: ({ children }) => <div>{children}</div>,
21
+ }));
22
+
23
+ const model = {
24
+ possibleResponses: ['firstImage', 'secondImage'],
25
+ responseContainers: [
26
+ { index: 0, x: 0, y: 0, width: '10%', height: '10%' },
27
+ { index: 1, x: 20, y: 20, width: '10%', height: '10%' },
28
+ ],
29
+ duplicateResponses: false,
30
+ maxResponsePerZone: 1,
31
+ image: { src: 'test.jpg', width: 100, height: 100 },
32
+ };
33
+
34
+ describe('Root', () => {
35
+ const updateAnswer = jest.fn();
36
+
37
+ const mkWrapper = (opts = {}) => {
38
+ const props = {
39
+ model,
40
+ session: { answers: [] },
41
+ updateAnswer,
42
+ ...opts,
43
+ };
44
+ return render(<Root {...props} />);
45
+ };
46
+
47
+ const createInstance = (opts = {}) => {
48
+ const props = {
49
+ model,
50
+ session: { answers: [] },
51
+ updateAnswer,
52
+ ...opts,
53
+ };
54
+ const instance = new Root(props);
55
+
56
+ // Mock setState to execute updates immediately for testing
57
+ instance.setState = jest.fn((state) => {
58
+ Object.assign(instance.state, typeof state === 'function' ? state(instance.state) : state);
59
+ });
60
+
61
+ return instance;
62
+ };
63
+
64
+ describe('initialization', () => {
65
+ it('initializes with correct possible responses', () => {
66
+ const instance = createInstance();
67
+ expect(instance.state.possibleResponses).toEqual([
68
+ { value: 'firstImage', id: '0' },
69
+ { value: 'secondImage', id: '1' },
70
+ ]);
71
+ });
72
+ });
73
+
74
+ describe('handleOnAnswerSelect', () => {
75
+ it('removes response from possibleResponses on answer select', () => {
76
+ const instance = createInstance();
77
+ instance.handleOnAnswerSelect({ value: 'firstImage', id: '0' }, 0);
78
+ expect(instance.state.possibleResponses).toEqual([{ value: 'secondImage', id: '1' }]);
79
+ });
80
+
81
+ it('adds response back to possibleResponses on answer remove', () => {
82
+ const instance = createInstance();
83
+ instance.handleOnAnswerSelect({ value: 'firstImage', id: '0' }, 0);
84
+ instance.handleOnAnswerRemove({ value: 'firstImage', id: '0', containerIndex: 0 });
85
+ expect(instance.state.possibleResponses).toEqual([
86
+ { value: 'secondImage', id: '1' },
87
+ { value: 'firstImage', id: '0' },
88
+ ]);
89
+ });
90
+
91
+ it('preserves id when adding back to possibleResponses', () => {
92
+ const instance = createInstance();
93
+ instance.handleOnAnswerSelect({ value: 'firstImage', id: '0' }, 0);
94
+ instance.handleOnAnswerRemove({ value: 'firstImage', id: '0', containerIndex: 0 });
95
+ expect(instance.state.possibleResponses[1].id).toBe('0');
96
+ });
97
+ });
98
+
99
+ });