@pie-lib/editable-html-tip-tap 2.1.2-next.30 → 2.1.2-next.34

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 (276) hide show
  1. package/CHANGELOG.json +32 -0
  2. package/CHANGELOG.md +2532 -0
  3. package/LICENSE.md +5 -0
  4. package/lib/components/CharacterPicker.js +201 -0
  5. package/lib/components/CharacterPicker.js.map +1 -0
  6. package/lib/components/EditableHtml.js +376 -0
  7. package/lib/components/EditableHtml.js.map +1 -0
  8. package/lib/components/MenuBar.js +696 -0
  9. package/lib/components/MenuBar.js.map +1 -0
  10. package/lib/components/TiptapContainer.js +234 -0
  11. package/lib/components/TiptapContainer.js.map +1 -0
  12. package/lib/components/characters/characterUtils.js +378 -0
  13. package/lib/components/characters/characterUtils.js.map +1 -0
  14. package/lib/components/characters/custom-popper.js +44 -0
  15. package/lib/components/characters/custom-popper.js.map +1 -0
  16. package/lib/components/common/done-button.js +34 -0
  17. package/lib/components/common/done-button.js.map +1 -0
  18. package/lib/components/common/toolbar-buttons.js +144 -0
  19. package/lib/components/common/toolbar-buttons.js.map +1 -0
  20. package/lib/components/icons/CssIcon.js +25 -0
  21. package/lib/components/icons/CssIcon.js.map +1 -0
  22. package/lib/components/icons/RespArea.js +72 -0
  23. package/lib/components/icons/RespArea.js.map +1 -0
  24. package/lib/components/icons/TableIcons.js +53 -0
  25. package/lib/components/icons/TableIcons.js.map +1 -0
  26. package/lib/components/icons/TextAlign.js +157 -0
  27. package/lib/components/icons/TextAlign.js.map +1 -0
  28. package/lib/components/image/AltDialog.js +98 -0
  29. package/lib/components/image/AltDialog.js.map +1 -0
  30. package/lib/components/image/ImageToolbar.js +137 -0
  31. package/lib/components/image/ImageToolbar.js.map +1 -0
  32. package/lib/components/image/InsertImageHandler.js +135 -0
  33. package/lib/components/image/InsertImageHandler.js.map +1 -0
  34. package/lib/components/media/MediaDialog.js +594 -0
  35. package/lib/components/media/MediaDialog.js.map +1 -0
  36. package/lib/components/media/MediaToolbar.js +74 -0
  37. package/lib/components/media/MediaToolbar.js.map +1 -0
  38. package/lib/components/media/MediaWrapper.js +67 -0
  39. package/lib/components/media/MediaWrapper.js.map +1 -0
  40. package/lib/components/respArea/DragInTheBlank/DragInTheBlank.js +84 -0
  41. package/lib/components/respArea/DragInTheBlank/DragInTheBlank.js.map +1 -0
  42. package/lib/components/respArea/DragInTheBlank/choice.js +250 -0
  43. package/lib/components/respArea/DragInTheBlank/choice.js.map +1 -0
  44. package/lib/components/respArea/ExplicitConstructedResponse.js +136 -0
  45. package/lib/components/respArea/ExplicitConstructedResponse.js.map +1 -0
  46. package/lib/components/respArea/InlineDropdown.js +209 -0
  47. package/lib/components/respArea/InlineDropdown.js.map +1 -0
  48. package/lib/components/respArea/MathTemplated.js +130 -0
  49. package/lib/components/respArea/MathTemplated.js.map +1 -0
  50. package/lib/components/respArea/ToolbarIcon.js +81 -0
  51. package/lib/components/respArea/ToolbarIcon.js.map +1 -0
  52. package/lib/components/respArea/inlineDropdownUtils.js +67 -0
  53. package/lib/components/respArea/inlineDropdownUtils.js.map +1 -0
  54. package/lib/constants.js +11 -0
  55. package/lib/constants.js.map +1 -0
  56. package/lib/extensions/css.js +217 -0
  57. package/lib/extensions/css.js.map +1 -0
  58. package/lib/extensions/custom-toolbar-wrapper.js +92 -0
  59. package/lib/extensions/custom-toolbar-wrapper.js.map +1 -0
  60. package/lib/extensions/div-node.js +83 -0
  61. package/lib/extensions/div-node.js.map +1 -0
  62. package/lib/extensions/ensure-empty-root-div.js +48 -0
  63. package/lib/extensions/ensure-empty-root-div.js.map +1 -0
  64. package/lib/extensions/ensure-list-item-content-is-div.js +64 -0
  65. package/lib/extensions/ensure-list-item-content-is-div.js.map +1 -0
  66. package/lib/extensions/extended-list-item.js +15 -0
  67. package/lib/extensions/extended-list-item.js.map +1 -0
  68. package/lib/extensions/extended-table-cell.js +22 -0
  69. package/lib/extensions/extended-table-cell.js.map +1 -0
  70. package/lib/extensions/extended-table.js +75 -0
  71. package/lib/extensions/extended-table.js.map +1 -0
  72. package/lib/extensions/heading-paragraph.js +61 -0
  73. package/lib/extensions/heading-paragraph.js.map +1 -0
  74. package/lib/extensions/image-component.js +348 -0
  75. package/lib/extensions/image-component.js.map +1 -0
  76. package/lib/extensions/image.js +134 -0
  77. package/lib/extensions/image.js.map +1 -0
  78. package/lib/extensions/index.js +46 -0
  79. package/lib/extensions/index.js.map +1 -0
  80. package/lib/extensions/math.js +342 -0
  81. package/lib/extensions/math.js.map +1 -0
  82. package/lib/extensions/media.js +243 -0
  83. package/lib/extensions/media.js.map +1 -0
  84. package/lib/extensions/responseArea.js +446 -0
  85. package/lib/extensions/responseArea.js.map +1 -0
  86. package/lib/index.js +37 -0
  87. package/lib/index.js.map +1 -0
  88. package/lib/styles/editorContainerStyles.js +137 -0
  89. package/lib/styles/editorContainerStyles.js.map +1 -0
  90. package/lib/theme.js +8 -0
  91. package/lib/theme.js.map +1 -0
  92. package/lib/utils/helper.js +73 -0
  93. package/lib/utils/helper.js.map +1 -0
  94. package/lib/utils/size.js +26 -0
  95. package/lib/utils/size.js.map +1 -0
  96. package/package.json +24 -40
  97. package/src/__tests__/EditableHtml.test.jsx +554 -0
  98. package/src/__tests__/constants.test.js +19 -0
  99. package/src/__tests__/div-to-paragraph-conversion.test.jsx +125 -0
  100. package/src/__tests__/extensions.test.js +208 -0
  101. package/src/__tests__/index.test.jsx +154 -0
  102. package/src/__tests__/size-utils.test.js +64 -0
  103. package/src/__tests__/theme.test.js +17 -0
  104. package/src/components/CharacterPicker.jsx +207 -0
  105. package/src/components/EditableHtml.jsx +440 -0
  106. package/src/components/MenuBar.jsx +554 -0
  107. package/src/components/TiptapContainer.jsx +219 -0
  108. package/src/components/__tests__/AltDialog.test.jsx +147 -0
  109. package/src/components/__tests__/CharacterPicker.test.jsx +261 -0
  110. package/src/components/__tests__/CssIcon.test.jsx +46 -0
  111. package/src/components/__tests__/DragInTheBlank.test.jsx +255 -0
  112. package/src/components/__tests__/ExplicitConstructedResponse.test.jsx +204 -0
  113. package/src/components/__tests__/ImageToolbar.test.jsx +128 -0
  114. package/src/components/__tests__/InlineDropdown.test.jsx +388 -0
  115. package/src/components/__tests__/InsertImageHandler.test.js +161 -0
  116. package/src/components/__tests__/MediaDialog.test.jsx +293 -0
  117. package/src/components/__tests__/MediaToolbar.test.jsx +74 -0
  118. package/src/components/__tests__/MediaWrapper.test.jsx +81 -0
  119. package/src/components/__tests__/MenuBar.test.jsx +250 -0
  120. package/src/components/__tests__/RespArea.test.jsx +122 -0
  121. package/src/components/__tests__/TableIcons.test.jsx +149 -0
  122. package/src/components/__tests__/TextAlign.test.jsx +167 -0
  123. package/src/components/__tests__/TiptapContainer.test.jsx +138 -0
  124. package/src/components/__tests__/characterUtils.test.js +166 -0
  125. package/src/components/__tests__/choice.test.jsx +171 -0
  126. package/src/components/__tests__/custom-popper.test.jsx +82 -0
  127. package/src/components/__tests__/done-button.test.jsx +54 -0
  128. package/src/components/__tests__/toolbar-buttons.test.jsx +234 -0
  129. package/src/components/characters/characterUtils.js +447 -0
  130. package/src/components/characters/custom-popper.js +38 -0
  131. package/src/components/common/done-button.jsx +27 -0
  132. package/src/components/common/toolbar-buttons.jsx +122 -0
  133. package/src/components/icons/CssIcon.jsx +15 -0
  134. package/src/components/icons/RespArea.jsx +71 -0
  135. package/src/components/icons/TableIcons.jsx +52 -0
  136. package/src/components/icons/TextAlign.jsx +114 -0
  137. package/src/components/image/AltDialog.jsx +82 -0
  138. package/src/components/image/ImageToolbar.jsx +99 -0
  139. package/src/components/image/InsertImageHandler.js +107 -0
  140. package/src/components/media/MediaDialog.jsx +596 -0
  141. package/src/components/media/MediaToolbar.jsx +49 -0
  142. package/src/components/media/MediaWrapper.jsx +39 -0
  143. package/src/components/respArea/DragInTheBlank/DragInTheBlank.jsx +76 -0
  144. package/src/components/respArea/DragInTheBlank/choice.jsx +256 -0
  145. package/src/components/respArea/ExplicitConstructedResponse.jsx +135 -0
  146. package/src/components/respArea/InlineDropdown.jsx +220 -0
  147. package/src/components/respArea/MathTemplated.jsx +124 -0
  148. package/src/components/respArea/ToolbarIcon.jsx +66 -0
  149. package/src/components/respArea/__tests__/MathTemplated.test.jsx +210 -0
  150. package/src/components/respArea/inlineDropdownUtils.js +79 -0
  151. package/src/constants.js +5 -0
  152. package/src/extensions/__tests__/css.test.js +196 -0
  153. package/src/extensions/__tests__/custom-toolbar-wrapper.test.jsx +180 -0
  154. package/src/extensions/__tests__/divNode.test.js +87 -0
  155. package/src/extensions/__tests__/ensure-empty-root-div.test.js +57 -0
  156. package/src/extensions/__tests__/ensure-list-item-content-is-div.test.js +44 -0
  157. package/src/extensions/__tests__/extended-list-item.test.js +13 -0
  158. package/src/extensions/__tests__/extended-table-cell.test.js +22 -0
  159. package/src/extensions/__tests__/extended-table.test.js +183 -0
  160. package/src/extensions/__tests__/image-component.test.jsx +345 -0
  161. package/src/extensions/__tests__/image.test.js +237 -0
  162. package/src/extensions/__tests__/math.test.js +603 -0
  163. package/src/extensions/__tests__/media-node-view.test.jsx +298 -0
  164. package/src/extensions/__tests__/media.test.js +271 -0
  165. package/src/extensions/__tests__/responseArea.test.js +601 -0
  166. package/src/extensions/css.js +220 -0
  167. package/src/extensions/custom-toolbar-wrapper.jsx +78 -0
  168. package/src/extensions/div-node.js +86 -0
  169. package/src/extensions/ensure-empty-root-div.js +47 -0
  170. package/src/extensions/ensure-list-item-content-is-div.js +62 -0
  171. package/src/extensions/extended-list-item.js +10 -0
  172. package/src/extensions/extended-table-cell.js +19 -0
  173. package/src/extensions/extended-table.js +60 -0
  174. package/src/extensions/heading-paragraph.js +53 -0
  175. package/src/extensions/image-component.jsx +338 -0
  176. package/src/extensions/image.js +109 -0
  177. package/src/extensions/index.js +81 -0
  178. package/src/extensions/math.js +326 -0
  179. package/src/extensions/media.js +188 -0
  180. package/src/extensions/responseArea.js +401 -0
  181. package/src/index.jsx +5 -0
  182. package/src/styles/editorContainerStyles.js +145 -0
  183. package/src/theme.js +1 -0
  184. package/src/utils/__tests__/helper.test.js +126 -0
  185. package/src/utils/helper.js +69 -0
  186. package/src/utils/size.js +32 -0
  187. package/dist/components/CharacterPicker.d.ts +0 -31
  188. package/dist/components/CharacterPicker.js +0 -131
  189. package/dist/components/EditableHtml.d.ts +0 -11
  190. package/dist/components/EditableHtml.js +0 -291
  191. package/dist/components/MenuBar.d.ts +0 -11
  192. package/dist/components/MenuBar.js +0 -462
  193. package/dist/components/TiptapContainer.d.ts +0 -11
  194. package/dist/components/TiptapContainer.js +0 -154
  195. package/dist/components/characters/characterUtils.d.ts +0 -35
  196. package/dist/components/characters/characterUtils.js +0 -465
  197. package/dist/components/characters/custom-popper.d.ts +0 -14
  198. package/dist/components/characters/custom-popper.js +0 -32
  199. package/dist/components/common/done-button.d.ts +0 -30
  200. package/dist/components/common/done-button.js +0 -26
  201. package/dist/components/common/toolbar-buttons.d.ts +0 -38
  202. package/dist/components/common/toolbar-buttons.js +0 -91
  203. package/dist/components/icons/CssIcon.d.ts +0 -11
  204. package/dist/components/icons/CssIcon.js +0 -14
  205. package/dist/components/icons/RespArea.d.ts +0 -26
  206. package/dist/components/icons/RespArea.js +0 -42
  207. package/dist/components/icons/TableIcons.d.ts +0 -14
  208. package/dist/components/icons/TableIcons.js +0 -32
  209. package/dist/components/icons/TextAlign.d.ts +0 -18
  210. package/dist/components/icons/TextAlign.js +0 -134
  211. package/dist/components/image/AltDialog.d.ts +0 -22
  212. package/dist/components/image/AltDialog.js +0 -61
  213. package/dist/components/image/ImageToolbar.d.ts +0 -24
  214. package/dist/components/image/ImageToolbar.js +0 -80
  215. package/dist/components/image/InsertImageHandler.d.ts +0 -32
  216. package/dist/components/image/InsertImageHandler.js +0 -53
  217. package/dist/components/media/MediaDialog.d.ts +0 -43
  218. package/dist/components/media/MediaDialog.js +0 -389
  219. package/dist/components/media/MediaToolbar.d.ts +0 -19
  220. package/dist/components/media/MediaToolbar.js +0 -41
  221. package/dist/components/media/MediaWrapper.d.ts +0 -19
  222. package/dist/components/respArea/DragInTheBlank/DragInTheBlank.d.ts +0 -23
  223. package/dist/components/respArea/DragInTheBlank/DragInTheBlank.js +0 -58
  224. package/dist/components/respArea/DragInTheBlank/choice.d.ts +0 -56
  225. package/dist/components/respArea/DragInTheBlank/choice.js +0 -156
  226. package/dist/components/respArea/ExplicitConstructedResponse.d.ts +0 -20
  227. package/dist/components/respArea/ExplicitConstructedResponse.js +0 -83
  228. package/dist/components/respArea/InlineDropdown.d.ts +0 -18
  229. package/dist/components/respArea/InlineDropdown.js +0 -119
  230. package/dist/components/respArea/MathTemplated.d.ts +0 -19
  231. package/dist/components/respArea/MathTemplated.js +0 -97
  232. package/dist/components/respArea/ToolbarIcon.d.ts +0 -14
  233. package/dist/components/respArea/ToolbarIcon.js +0 -17
  234. package/dist/components/respArea/inlineDropdownUtils.d.ts +0 -15
  235. package/dist/components/respArea/inlineDropdownUtils.js +0 -15
  236. package/dist/constants.d.ts +0 -13
  237. package/dist/constants.js +0 -4
  238. package/dist/extensions/css.d.ts +0 -11
  239. package/dist/extensions/css.js +0 -115
  240. package/dist/extensions/custom-toolbar-wrapper.d.ts +0 -11
  241. package/dist/extensions/custom-toolbar-wrapper.js +0 -61
  242. package/dist/extensions/div-node.d.ts +0 -10
  243. package/dist/extensions/div-node.js +0 -42
  244. package/dist/extensions/ensure-empty-root-div.d.ts +0 -14
  245. package/dist/extensions/ensure-empty-root-div.js +0 -24
  246. package/dist/extensions/ensure-list-item-content-is-div.d.ts +0 -15
  247. package/dist/extensions/ensure-list-item-content-is-div.js +0 -31
  248. package/dist/extensions/extended-list-item.d.ts +0 -13
  249. package/dist/extensions/extended-list-item.js +0 -5
  250. package/dist/extensions/extended-table-cell.d.ts +0 -10
  251. package/dist/extensions/extended-table-cell.js +0 -6
  252. package/dist/extensions/extended-table.d.ts +0 -17
  253. package/dist/extensions/extended-table.js +0 -34
  254. package/dist/extensions/heading-paragraph.d.ts +0 -17
  255. package/dist/extensions/heading-paragraph.js +0 -30
  256. package/dist/extensions/image-component.d.ts +0 -22
  257. package/dist/extensions/image-component.js +0 -220
  258. package/dist/extensions/image.d.ts +0 -10
  259. package/dist/extensions/image.js +0 -68
  260. package/dist/extensions/index.d.ts +0 -16
  261. package/dist/extensions/index.js +0 -64
  262. package/dist/extensions/math.d.ts +0 -15
  263. package/dist/extensions/math.js +0 -158
  264. package/dist/extensions/media.d.ts +0 -19
  265. package/dist/extensions/media.js +0 -149
  266. package/dist/extensions/responseArea.d.ts +0 -27
  267. package/dist/extensions/responseArea.js +0 -259
  268. package/dist/index.d.ts +0 -13
  269. package/dist/index.js +0 -7
  270. package/dist/node_modules/.bun/clsx@2.1.1/node_modules/clsx/dist/clsx.js +0 -16
  271. package/dist/styles/editorContainerStyles.d.ts +0 -134
  272. package/dist/theme.d.ts +0 -9
  273. package/dist/utils/helper.d.ts +0 -9
  274. package/dist/utils/helper.js +0 -27
  275. package/dist/utils/size.d.ts +0 -9
  276. package/dist/utils/size.js +0 -14
@@ -0,0 +1,601 @@
1
+ import {
2
+ DragInTheBlankNode,
3
+ ExplicitConstructedResponseNode,
4
+ InlineDropdownNode,
5
+ ResponseAreaExtension,
6
+ } from '../responseArea';
7
+
8
+ jest.mock('@tiptap/core', () => ({
9
+ Extension: { create: jest.fn((config) => config) },
10
+ Node: { create: jest.fn((config) => config) },
11
+ }));
12
+
13
+ jest.mock('@tiptap/react', () => ({
14
+ Node: { create: jest.fn((config) => config) },
15
+ ReactNodeViewRenderer: jest.fn((component) => component),
16
+ }));
17
+
18
+ jest.mock('prosemirror-state', () => ({
19
+ Plugin: jest.fn(function (config) {
20
+ return config;
21
+ }),
22
+ PluginKey: jest.fn(function (key) {
23
+ this.key = key;
24
+ }),
25
+ TextSelection: {
26
+ near: jest.fn((pos, dir) => ({ type: 'text', pos, dir })),
27
+ create: jest.fn((doc, pos) => ({ type: 'text', pos })),
28
+ },
29
+ NodeSelection: {
30
+ create: jest.fn((doc, pos) => ({ type: 'node', pos })),
31
+ },
32
+ }));
33
+
34
+ jest.mock('../../components/respArea/ExplicitConstructedResponse', () => ({
35
+ __esModule: true,
36
+ default: jest.fn(() => <div data-testid="explicit-constructed-response" />),
37
+ }));
38
+
39
+ jest.mock('../../components/respArea/DragInTheBlank/DragInTheBlank', () => ({
40
+ __esModule: true,
41
+ default: jest.fn(() => <div data-testid="drag-in-the-blank" />),
42
+ }));
43
+
44
+ jest.mock('../../components/respArea/InlineDropdown', () => ({
45
+ __esModule: true,
46
+ default: jest.fn(() => <div data-testid="inline-dropdown" />),
47
+ }));
48
+
49
+ describe('ResponseAreaExtension', () => {
50
+ describe('configuration', () => {
51
+ it('has correct name', () => {
52
+ expect(ResponseAreaExtension.name).toBe('responseArea');
53
+ });
54
+ });
55
+
56
+ describe('addOptions', () => {
57
+ it('returns default options', () => {
58
+ const options = ResponseAreaExtension.addOptions();
59
+
60
+ expect(options).toHaveProperty('maxResponseAreas', null);
61
+ expect(options).toHaveProperty('error', null);
62
+ expect(options).toHaveProperty('options', null);
63
+ expect(options).toHaveProperty('respAreaToolbar', null);
64
+ expect(options).toHaveProperty('onHandleAreaChange', null);
65
+ });
66
+ });
67
+
68
+ describe('addProseMirrorPlugins', () => {
69
+ it('returns empty array when no type specified', () => {
70
+ const context = {
71
+ options: {},
72
+ };
73
+
74
+ const plugins = ResponseAreaExtension.addProseMirrorPlugins.call(context);
75
+
76
+ expect(Array.isArray(plugins)).toBe(true);
77
+ expect(plugins).toHaveLength(0);
78
+ });
79
+
80
+ it('returns plugin array when type is specified', () => {
81
+ const context = {
82
+ options: {
83
+ type: 'explicit-constructed-response',
84
+ maxResponseAreas: 5,
85
+ },
86
+ };
87
+
88
+ const plugins = ResponseAreaExtension.addProseMirrorPlugins.call(context);
89
+
90
+ expect(Array.isArray(plugins)).toBe(true);
91
+ expect(plugins).toHaveLength(1);
92
+ expect(plugins[0]).toHaveProperty('key');
93
+ expect(plugins[0]).toHaveProperty('view');
94
+ });
95
+ });
96
+
97
+ describe('addCommands', () => {
98
+ it('returns insertResponseArea command', () => {
99
+ const commands = ResponseAreaExtension.addCommands();
100
+
101
+ expect(commands).toHaveProperty('insertResponseArea');
102
+ expect(typeof commands.insertResponseArea).toBe('function');
103
+ });
104
+
105
+ it('returns refreshResponseArea command', () => {
106
+ const commands = ResponseAreaExtension.addCommands();
107
+
108
+ expect(commands).toHaveProperty('refreshResponseArea');
109
+ expect(typeof commands.refreshResponseArea).toBe('function');
110
+ });
111
+
112
+ it('refreshResponseArea handles node with attrs safely', () => {
113
+ const context = {
114
+ options: {
115
+ type: 'explicit-constructed-response',
116
+ maxResponseAreas: 5,
117
+ },
118
+ };
119
+
120
+ const commands = ResponseAreaExtension.addCommands.call(context);
121
+ const refreshCommand = commands.refreshResponseArea();
122
+
123
+ // Mock transaction and state
124
+ const mockNode = {
125
+ attrs: {
126
+ index: '0',
127
+ value: 'test',
128
+ },
129
+ };
130
+
131
+ const mockTr = {
132
+ setNodeMarkup: jest.fn(),
133
+ setSelection: jest.fn(),
134
+ };
135
+
136
+ const mockState = {
137
+ selection: {
138
+ from: 0,
139
+ $from: {
140
+ nodeAfter: mockNode,
141
+ },
142
+ },
143
+ tr: mockTr,
144
+ };
145
+
146
+ const mockCommands = {
147
+ focus: jest.fn(),
148
+ };
149
+
150
+ const mockDispatch = jest.fn();
151
+
152
+ refreshCommand({
153
+ tr: mockTr,
154
+ state: mockState,
155
+ commands: mockCommands,
156
+ dispatch: mockDispatch,
157
+ });
158
+
159
+ expect(mockTr.setNodeMarkup).toHaveBeenCalled();
160
+ });
161
+
162
+ it('refreshResponseArea handles node without attrs safely (optional chaining)', () => {
163
+ const context = {
164
+ options: {
165
+ type: 'explicit-constructed-response',
166
+ maxResponseAreas: 5,
167
+ },
168
+ };
169
+
170
+ const commands = ResponseAreaExtension.addCommands.call(context);
171
+ const refreshCommand = commands.refreshResponseArea();
172
+
173
+ // Mock transaction and state with node that has no attrs
174
+ const mockNode = null;
175
+
176
+ const mockTr = {
177
+ setNodeMarkup: jest.fn(),
178
+ setSelection: jest.fn(),
179
+ };
180
+
181
+ const mockState = {
182
+ selection: {
183
+ from: 0,
184
+ $from: {
185
+ nodeAfter: mockNode,
186
+ },
187
+ },
188
+ tr: mockTr,
189
+ };
190
+
191
+ const mockCommands = {
192
+ focus: jest.fn(),
193
+ };
194
+
195
+ const mockDispatch = jest.fn();
196
+
197
+ // This should not throw an error due to optional chaining on node?.attrs
198
+ expect(() => {
199
+ refreshCommand({
200
+ tr: mockTr,
201
+ state: mockState,
202
+ commands: mockCommands,
203
+ dispatch: mockDispatch,
204
+ });
205
+ }).not.toThrow();
206
+ });
207
+
208
+ it('refreshResponseArea updates timestamp in node attributes', () => {
209
+ const context = {
210
+ options: {
211
+ type: 'explicit-constructed-response',
212
+ maxResponseAreas: 5,
213
+ },
214
+ };
215
+
216
+ const commands = ResponseAreaExtension.addCommands.call(context);
217
+ const refreshCommand = commands.refreshResponseArea();
218
+
219
+ const mockNode = {
220
+ attrs: {
221
+ index: '0',
222
+ value: 'test',
223
+ updated: '1234567890',
224
+ },
225
+ };
226
+
227
+ const mockTr = {
228
+ setNodeMarkup: jest.fn((pos, type, attrs) => {
229
+ // Verify that updated timestamp is being set
230
+ expect(attrs.updated).toBeDefined();
231
+ expect(attrs.updated).not.toBe('1234567890');
232
+ }),
233
+ setSelection: jest.fn(),
234
+ };
235
+
236
+ const mockState = {
237
+ selection: {
238
+ from: 0,
239
+ $from: {
240
+ nodeAfter: mockNode,
241
+ },
242
+ },
243
+ tr: mockTr,
244
+ };
245
+
246
+ const mockCommands = {
247
+ focus: jest.fn(),
248
+ };
249
+
250
+ const mockDispatch = jest.fn();
251
+
252
+ refreshCommand({
253
+ tr: mockTr,
254
+ state: mockState,
255
+ commands: mockCommands,
256
+ dispatch: mockDispatch,
257
+ });
258
+
259
+ expect(mockTr.setNodeMarkup).toHaveBeenCalled();
260
+ });
261
+
262
+ describe('insertResponseArea', () => {
263
+ beforeEach(() => {
264
+ jest.resetModules();
265
+ });
266
+
267
+ const buildInsertCommand = () => {
268
+ const { ResponseAreaExtension } = require('../responseArea');
269
+ const context = {
270
+ options: {
271
+ type: 'inline-dropdown',
272
+ maxResponseAreas: 5,
273
+ },
274
+ };
275
+ const commands = ResponseAreaExtension.addCommands.call(context);
276
+ return commands.insertResponseArea('inline-dropdown');
277
+ };
278
+
279
+ const createDoc = (existingCount, typeName = 'inline_dropdown') => ({
280
+ descendants: jest.fn((callback) => {
281
+ for (let i = 0; i < existingCount; i += 1) {
282
+ callback({ type: { name: typeName } }, i);
283
+ }
284
+ }),
285
+ content: { size: 50 },
286
+ });
287
+
288
+ it('assigns index 1 and id 1 on the first insert', () => {
289
+ const insert = buildInsertCommand();
290
+ const mockInlineNode = { nodeSize: 1 };
291
+ const create = jest.fn(() => mockInlineNode);
292
+ const mockDoc = createDoc(0);
293
+ const mockTr = {
294
+ insert: jest.fn(),
295
+ doc: mockDoc,
296
+ setSelection: jest.fn(),
297
+ };
298
+ const state = {
299
+ schema: {
300
+ nodes: {
301
+ inline_dropdown: { create },
302
+ },
303
+ },
304
+ doc: mockDoc,
305
+ selection: { from: 5 },
306
+ };
307
+ const mockDispatch = jest.fn();
308
+ const mockCommands = { focus: jest.fn() };
309
+
310
+ const result = insert({
311
+ tr: mockTr,
312
+ state,
313
+ dispatch: mockDispatch,
314
+ commands: mockCommands,
315
+ });
316
+
317
+ expect(result).toBe(true);
318
+ expect(create).toHaveBeenCalledWith({
319
+ index: '1',
320
+ id: '1',
321
+ value: '',
322
+ });
323
+ expect(mockTr.insert).toHaveBeenCalledWith(5, mockInlineNode);
324
+ expect(mockDispatch).toHaveBeenCalled();
325
+ });
326
+
327
+ it('assigns consecutive indices on repeated inserts', () => {
328
+ const insert = buildInsertCommand();
329
+ const mockInlineNode = { nodeSize: 1 };
330
+ const create = jest.fn(() => mockInlineNode);
331
+ const mockDoc = createDoc(0);
332
+
333
+ const runOnce = () => {
334
+ const mockTr = {
335
+ insert: jest.fn(),
336
+ doc: mockDoc,
337
+ setSelection: jest.fn(),
338
+ };
339
+ const state = {
340
+ schema: {
341
+ nodes: {
342
+ inline_dropdown: { create },
343
+ },
344
+ },
345
+ doc: mockDoc,
346
+ selection: { from: 1 },
347
+ };
348
+ insert({
349
+ tr: mockTr,
350
+ state,
351
+ dispatch: jest.fn(),
352
+ commands: { focus: jest.fn() },
353
+ });
354
+ };
355
+
356
+ runOnce();
357
+ runOnce();
358
+
359
+ expect(create.mock.calls[0][0]).toEqual({ index: '1', id: '1', value: '' });
360
+ expect(create.mock.calls[1][0]).toEqual({ index: '2', id: '2', value: '' });
361
+ });
362
+
363
+ it('returns false when maxResponseAreas is reached', () => {
364
+ const insert = buildInsertCommand();
365
+ const mockInlineNode = { nodeSize: 1 };
366
+ const create = jest.fn(() => mockInlineNode);
367
+ const mockDoc = createDoc(5);
368
+ const mockTr = {
369
+ insert: jest.fn(),
370
+ doc: mockDoc,
371
+ setSelection: jest.fn(),
372
+ };
373
+ const state = {
374
+ schema: {
375
+ nodes: {
376
+ inline_dropdown: { create },
377
+ },
378
+ },
379
+ doc: mockDoc,
380
+ selection: { from: 5 },
381
+ };
382
+
383
+ const result = insert({
384
+ tr: mockTr,
385
+ state,
386
+ dispatch: jest.fn(),
387
+ commands: { focus: jest.fn() },
388
+ });
389
+
390
+ expect(result).toBe(false);
391
+ expect(create).not.toHaveBeenCalled();
392
+ expect(mockTr.insert).not.toHaveBeenCalled();
393
+ });
394
+ });
395
+ });
396
+ });
397
+
398
+ describe('ExplicitConstructedResponseNode', () => {
399
+ describe('configuration', () => {
400
+ it('has correct name', () => {
401
+ expect(ExplicitConstructedResponseNode.name).toBe('explicit_constructed_response');
402
+ });
403
+
404
+ it('is inline', () => {
405
+ expect(ExplicitConstructedResponseNode.inline).toBe(true);
406
+ });
407
+
408
+ it('is in inline group', () => {
409
+ expect(ExplicitConstructedResponseNode.group).toBe('inline');
410
+ });
411
+
412
+ it('is atomic', () => {
413
+ expect(ExplicitConstructedResponseNode.atom).toBe(true);
414
+ });
415
+ });
416
+
417
+ describe('addAttributes', () => {
418
+ it('returns required attributes', () => {
419
+ const attributes = ExplicitConstructedResponseNode.addAttributes();
420
+
421
+ expect(attributes).toHaveProperty('index');
422
+ expect(attributes).toHaveProperty('value');
423
+ expect(attributes).toHaveProperty('updated');
424
+
425
+ expect(attributes.index).toEqual({ default: null });
426
+ expect(attributes.value).toEqual({ default: '' });
427
+ expect(attributes.updated).toEqual({ default: '' });
428
+ });
429
+ });
430
+
431
+ describe('parseHTML', () => {
432
+ it('returns parsing rules', () => {
433
+ const rules = ExplicitConstructedResponseNode.parseHTML();
434
+
435
+ expect(Array.isArray(rules)).toBe(true);
436
+ expect(rules).toHaveLength(1);
437
+ expect(rules[0]).toHaveProperty('tag');
438
+ });
439
+ });
440
+
441
+ describe('renderHTML', () => {
442
+ it('renders span with attributes', () => {
443
+ const result = ExplicitConstructedResponseNode.renderHTML({
444
+ HTMLAttributes: {
445
+ index: '1',
446
+ id: '1',
447
+ value: 'test',
448
+ },
449
+ });
450
+
451
+ expect(result[0]).toBe('span');
452
+ expect(result[1]).toHaveProperty('data-type', 'explicit_constructed_response');
453
+ });
454
+ });
455
+
456
+ describe('addNodeView', () => {
457
+ it('returns ReactNodeViewRenderer result', () => {
458
+ const result = ExplicitConstructedResponseNode.addNodeView();
459
+
460
+ expect(result).toBeDefined();
461
+ });
462
+ });
463
+ });
464
+
465
+ describe('DragInTheBlankNode', () => {
466
+ describe('configuration', () => {
467
+ it('has correct name', () => {
468
+ expect(DragInTheBlankNode.name).toBe('drag_in_the_blank');
469
+ });
470
+
471
+ it('is inline', () => {
472
+ expect(DragInTheBlankNode.inline).toBe(true);
473
+ });
474
+
475
+ it('is in inline group', () => {
476
+ expect(DragInTheBlankNode.group).toBe('inline');
477
+ });
478
+
479
+ it('is atomic', () => {
480
+ expect(DragInTheBlankNode.atom).toBe(true);
481
+ });
482
+ });
483
+
484
+ describe('addAttributes', () => {
485
+ it('returns required attributes', () => {
486
+ const attributes = DragInTheBlankNode.addAttributes();
487
+
488
+ expect(attributes).toHaveProperty('index');
489
+ expect(attributes).toHaveProperty('id');
490
+ expect(attributes).toHaveProperty('value');
491
+ expect(attributes).toHaveProperty('inTable');
492
+ expect(attributes).toHaveProperty('updated');
493
+
494
+ expect(attributes.index).toEqual({ default: null });
495
+ expect(attributes.id).toEqual({ default: null });
496
+ expect(attributes.value).toEqual({ default: '' });
497
+ expect(attributes.inTable).toEqual({ default: null });
498
+ expect(attributes.updated).toEqual({ default: '' });
499
+ });
500
+ });
501
+
502
+ describe('parseHTML', () => {
503
+ it('returns parsing rules', () => {
504
+ const rules = DragInTheBlankNode.parseHTML();
505
+
506
+ expect(Array.isArray(rules)).toBe(true);
507
+ expect(rules).toHaveLength(1);
508
+ expect(rules[0]).toHaveProperty('tag');
509
+ });
510
+ });
511
+
512
+ describe('renderHTML', () => {
513
+ it('renders span with attributes', () => {
514
+ const result = DragInTheBlankNode.renderHTML({
515
+ HTMLAttributes: {
516
+ index: '1',
517
+ id: '1',
518
+ value: 'test',
519
+ },
520
+ });
521
+
522
+ expect(result[0]).toBe('span');
523
+ expect(result[1]).toHaveProperty('data-type', 'drag_in_the_blank');
524
+ });
525
+ });
526
+
527
+ describe('addNodeView', () => {
528
+ it('returns ReactNodeViewRenderer result', () => {
529
+ const result = DragInTheBlankNode.addNodeView();
530
+
531
+ expect(result).toBeDefined();
532
+ });
533
+ });
534
+ });
535
+
536
+ describe('InlineDropdownNode', () => {
537
+ describe('configuration', () => {
538
+ it('has correct name', () => {
539
+ expect(InlineDropdownNode.name).toBe('inline_dropdown');
540
+ });
541
+
542
+ it('is inline', () => {
543
+ expect(InlineDropdownNode.inline).toBe(true);
544
+ });
545
+
546
+ it('is in inline group', () => {
547
+ expect(InlineDropdownNode.group).toBe('inline');
548
+ });
549
+
550
+ it('is atomic', () => {
551
+ expect(InlineDropdownNode.atom).toBe(true);
552
+ });
553
+ });
554
+
555
+ describe('addAttributes', () => {
556
+ it('returns required attributes', () => {
557
+ const attributes = InlineDropdownNode.addAttributes();
558
+
559
+ expect(attributes).toHaveProperty('index');
560
+ expect(attributes).toHaveProperty('value');
561
+ expect(attributes).toHaveProperty('updated');
562
+
563
+ expect(attributes.index).toEqual({ default: null });
564
+ expect(attributes.value).toEqual({ default: '' });
565
+ expect(attributes.updated).toEqual({ default: '' });
566
+ });
567
+ });
568
+
569
+ describe('parseHTML', () => {
570
+ it('returns parsing rules', () => {
571
+ const rules = InlineDropdownNode.parseHTML();
572
+
573
+ expect(Array.isArray(rules)).toBe(true);
574
+ expect(rules).toHaveLength(1);
575
+ expect(rules[0]).toHaveProperty('tag');
576
+ });
577
+ });
578
+
579
+ describe('renderHTML', () => {
580
+ it('renders span with attributes', () => {
581
+ const result = InlineDropdownNode.renderHTML({
582
+ HTMLAttributes: {
583
+ index: '1',
584
+ id: '1',
585
+ value: 'test',
586
+ },
587
+ });
588
+
589
+ expect(result[0]).toBe('span');
590
+ expect(result[1]).toHaveProperty('data-type', 'inline_dropdown');
591
+ });
592
+ });
593
+
594
+ describe('addNodeView', () => {
595
+ it('returns ReactNodeViewRenderer result', () => {
596
+ const result = InlineDropdownNode.addNodeView();
597
+
598
+ expect(result).toBeDefined();
599
+ });
600
+ });
601
+ });