@pie-lib/editable-html-tip-tap 1.2.0-next.11 → 1.2.0-next.13

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 (32) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/lib/components/CharacterPicker.js +1 -0
  3. package/lib/components/CharacterPicker.js.map +1 -1
  4. package/lib/components/EditableHtml.js +9 -1
  5. package/lib/components/EditableHtml.js.map +1 -1
  6. package/lib/components/MenuBar.js +61 -42
  7. package/lib/components/MenuBar.js.map +1 -1
  8. package/lib/components/respArea/DragInTheBlank/DragInTheBlank.js +6 -1
  9. package/lib/components/respArea/DragInTheBlank/DragInTheBlank.js.map +1 -1
  10. package/lib/components/respArea/DragInTheBlank/choice.js +13 -6
  11. package/lib/components/respArea/DragInTheBlank/choice.js.map +1 -1
  12. package/lib/components/respArea/InlineDropdown.js +8 -2
  13. package/lib/components/respArea/InlineDropdown.js.map +1 -1
  14. package/lib/extensions/math.js +1 -0
  15. package/lib/extensions/math.js.map +1 -1
  16. package/lib/extensions/responseArea.js +2 -3
  17. package/lib/extensions/responseArea.js.map +1 -1
  18. package/package.json +4 -4
  19. package/src/__tests__/EditableHtml.test.jsx +35 -0
  20. package/src/components/CharacterPicker.jsx +1 -0
  21. package/src/components/EditableHtml.jsx +10 -1
  22. package/src/components/MenuBar.jsx +49 -23
  23. package/src/components/__tests__/CharacterPicker.test.jsx +22 -0
  24. package/src/components/__tests__/InlineDropdown.test.jsx +149 -0
  25. package/src/components/__tests__/MenuBar.test.jsx +32 -0
  26. package/src/components/respArea/DragInTheBlank/DragInTheBlank.jsx +6 -1
  27. package/src/components/respArea/DragInTheBlank/choice.jsx +31 -4
  28. package/src/components/respArea/InlineDropdown.jsx +10 -1
  29. package/src/extensions/__tests__/math.test.js +327 -0
  30. package/src/extensions/__tests__/responseArea.test.js +157 -0
  31. package/src/extensions/math.js +1 -0
  32. package/src/extensions/responseArea.js +2 -7
@@ -101,6 +101,163 @@ describe('ResponseAreaExtension', () => {
101
101
  expect(commands).toHaveProperty('insertResponseArea');
102
102
  expect(typeof commands.insertResponseArea).toBe('function');
103
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
+ });
104
261
  });
105
262
  });
106
263
 
@@ -253,6 +253,7 @@ export const MathNodeView = (props) => {
253
253
  ReactDOM.createPortal(
254
254
  <div
255
255
  ref={toolbarRef}
256
+ data-toolbar-for={editor.instanceId}
256
257
  style={{
257
258
  position: 'absolute',
258
259
  top: `${position.top}px`,
@@ -188,14 +188,9 @@ export const ResponseAreaExtension = Extension.create({
188
188
  // tr.setSelection(NodeSelection.create(tr.doc, usedPos))
189
189
 
190
190
  // --- Cursor move behavior for certain types (Slate: moveFocusTo next text) ---
191
- if (
192
- ['math_templated', 'inline_dropdown', 'drag_in_the_blank', 'explicit_constructed_response'].includes(
193
- typeName,
194
- )
195
- ) {
191
+ if (['math_templated', 'inline_dropdown', 'explicit_constructed_response'].includes(typeName)) {
196
192
  tr.setSelection(NodeSelection.create(tr.doc, usedPos));
197
193
  } else {
198
- // Default: put cursor after inserted node
199
194
  const after = usedPos + newInline.nodeSize;
200
195
  tr.setSelection(selectionAfterPos(tr.doc, after));
201
196
  }
@@ -214,7 +209,7 @@ export const ResponseAreaExtension = Extension.create({
214
209
  const node = selection.$from.nodeAfter;
215
210
  const nodePos = selection.from;
216
211
 
217
- tr.setNodeMarkup(nodePos, undefined, { ...node.attrs, updated: `${Date.now()}` });
212
+ tr.setNodeMarkup(nodePos, undefined, { ...node?.attrs, updated: `${Date.now()}` });
218
213
  tr.setSelection(NodeSelection.create(tr.doc, nodePos));
219
214
 
220
215
  if (dispatch) {