@pie-lib/editable-html-tip-tap 1.2.0-next.2 → 1.2.0-next.21
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.md +115 -21
- package/LICENSE.md +5 -0
- package/lib/components/CharacterPicker.js +2 -1
- package/lib/components/CharacterPicker.js.map +1 -1
- package/lib/components/EditableHtml.js +34 -17
- package/lib/components/EditableHtml.js.map +1 -1
- package/lib/components/MenuBar.js +77 -45
- package/lib/components/MenuBar.js.map +1 -1
- package/lib/components/TiptapContainer.js +15 -9
- package/lib/components/TiptapContainer.js.map +1 -1
- package/lib/components/characters/characterUtils.js +1 -1
- package/lib/components/characters/custom-popper.js +1 -1
- package/lib/components/common/done-button.js +1 -1
- package/lib/components/common/toolbar-buttons.js +1 -1
- package/lib/components/icons/CssIcon.js +1 -1
- package/lib/components/icons/RespArea.js +1 -1
- package/lib/components/icons/TableIcons.js +1 -1
- package/lib/components/icons/TextAlign.js +3 -3
- package/lib/components/icons/TextAlign.js.map +1 -1
- package/lib/components/image/AltDialog.js +1 -1
- package/lib/components/image/ImageToolbar.js +1 -1
- package/lib/components/image/InsertImageHandler.js +1 -1
- package/lib/components/media/MediaDialog.js +1 -1
- package/lib/components/media/MediaToolbar.js +1 -1
- package/lib/components/media/MediaWrapper.js +1 -1
- package/lib/components/respArea/DragInTheBlank/DragInTheBlank.js +7 -2
- package/lib/components/respArea/DragInTheBlank/DragInTheBlank.js.map +1 -1
- package/lib/components/respArea/DragInTheBlank/choice.js +16 -8
- package/lib/components/respArea/DragInTheBlank/choice.js.map +1 -1
- package/lib/components/respArea/ExplicitConstructedResponse.js +2 -2
- package/lib/components/respArea/ExplicitConstructedResponse.js.map +1 -1
- package/lib/components/respArea/InlineDropdown.js +11 -4
- package/lib/components/respArea/InlineDropdown.js.map +1 -1
- package/lib/components/respArea/MathTemplated.js +130 -0
- package/lib/components/respArea/MathTemplated.js.map +1 -0
- package/lib/components/respArea/ToolbarIcon.js +1 -1
- package/lib/constants.js +1 -1
- package/lib/extensions/css.js +1 -1
- package/lib/extensions/custom-toolbar-wrapper.js +1 -1
- package/lib/extensions/div-node.js +39 -0
- package/lib/extensions/div-node.js.map +1 -0
- package/lib/extensions/extended-table.js +1 -1
- package/lib/extensions/image-component.js +1 -1
- package/lib/extensions/image-component.js.map +1 -1
- package/lib/extensions/image.js +1 -1
- package/lib/extensions/index.js +4 -2
- package/lib/extensions/index.js.map +1 -1
- package/lib/extensions/math.js +7 -4
- package/lib/extensions/math.js.map +1 -1
- package/lib/extensions/media.js +15 -12
- package/lib/extensions/media.js.map +1 -1
- package/lib/extensions/responseArea.js +13 -10
- package/lib/extensions/responseArea.js.map +1 -1
- package/lib/index.js +1 -1
- package/lib/styles/editorContainerStyles.js +6 -5
- package/lib/styles/editorContainerStyles.js.map +1 -1
- package/lib/theme.js +1 -1
- package/lib/utils/helper.js +17 -0
- package/lib/utils/helper.js.map +1 -0
- package/lib/utils/size.js +1 -1
- package/package.json +11 -11
- package/src/__tests__/EditableHtml.test.jsx +41 -1
- package/src/__tests__/index.test.jsx +4 -1
- package/src/components/CharacterPicker.jsx +1 -0
- package/src/components/EditableHtml.jsx +40 -16
- package/src/components/MenuBar.jsx +66 -25
- package/src/components/TiptapContainer.jsx +10 -7
- package/src/components/__tests__/CharacterPicker.test.jsx +22 -0
- package/src/components/__tests__/ExplicitConstructedResponse.test.jsx +1 -1
- package/src/components/__tests__/InlineDropdown.test.jsx +150 -1
- package/src/components/__tests__/MenuBar.test.jsx +32 -0
- package/src/components/icons/TextAlign.jsx +1 -1
- package/src/components/respArea/DragInTheBlank/DragInTheBlank.jsx +6 -1
- package/src/components/respArea/DragInTheBlank/choice.jsx +32 -4
- package/src/components/respArea/ExplicitConstructedResponse.jsx +1 -1
- package/src/components/respArea/InlineDropdown.jsx +12 -2
- package/src/components/respArea/MathTemplated.jsx +124 -0
- package/src/components/respArea/__tests__/MathTemplated.test.jsx +210 -0
- package/src/extensions/__tests__/divNode.test.js +87 -0
- package/src/extensions/__tests__/math.test.js +327 -0
- package/src/extensions/__tests__/media-node-view.test.jsx +296 -0
- package/src/extensions/__tests__/media.test.js +1 -0
- package/src/extensions/__tests__/responseArea.test.js +157 -0
- package/src/extensions/div-node.js +36 -0
- package/src/extensions/index.js +2 -0
- package/src/extensions/math.js +6 -3
- package/src/extensions/media.js +17 -14
- package/src/extensions/responseArea.js +4 -8
- package/src/styles/editorContainerStyles.js +5 -4
- package/src/utils/helper.js +17 -0
- package/lib/__tests__/EditableHtml.test.js +0 -377
- package/lib/__tests__/constants.test.js +0 -21
- package/lib/__tests__/extensions.test.js +0 -209
- package/lib/__tests__/index.test.js +0 -235
- package/lib/__tests__/size-utils.test.js +0 -57
- package/lib/__tests__/theme.test.js +0 -17
- package/lib/components/__tests__/AltDialog.test.js +0 -201
- package/lib/components/__tests__/CharacterPicker.test.js +0 -305
- package/lib/components/__tests__/CssIcon.test.js +0 -58
- package/lib/components/__tests__/DragInTheBlank.test.js +0 -295
- package/lib/components/__tests__/ExplicitConstructedResponse.test.js +0 -253
- package/lib/components/__tests__/ImageToolbar.test.js +0 -185
- package/lib/components/__tests__/InlineDropdown.test.js +0 -287
- package/lib/components/__tests__/InsertImageHandler.test.js +0 -162
- package/lib/components/__tests__/MediaDialog.test.js +0 -433
- package/lib/components/__tests__/MediaToolbar.test.js +0 -126
- package/lib/components/__tests__/MediaWrapper.test.js +0 -96
- package/lib/components/__tests__/MenuBar.test.js +0 -459
- package/lib/components/__tests__/RespArea.test.js +0 -171
- package/lib/components/__tests__/TableIcons.test.js +0 -153
- package/lib/components/__tests__/TextAlign.test.js +0 -209
- package/lib/components/__tests__/TiptapContainer.test.js +0 -196
- package/lib/components/__tests__/characterUtils.test.js +0 -178
- package/lib/components/__tests__/choice.test.js +0 -213
- package/lib/components/__tests__/custom-popper.test.js +0 -108
- package/lib/components/__tests__/done-button.test.js +0 -72
- package/lib/components/__tests__/toolbar-buttons.test.js +0 -277
- package/lib/extensions/__tests__/component.test.js +0 -314
- package/lib/extensions/__tests__/css.test.js +0 -214
- package/lib/extensions/__tests__/custom-toolbar-wrapper.test.js +0 -175
- package/lib/extensions/__tests__/extended-table.test.js +0 -92
- package/lib/extensions/__tests__/image-component.test.js +0 -305
- package/lib/extensions/__tests__/image.test.js +0 -164
- package/lib/extensions/__tests__/media.test.js +0 -292
- package/lib/extensions/__tests__/responseArea.test.js +0 -330
- package/lib/extensions/component.js +0 -305
|
@@ -15,9 +15,11 @@ import Functions from '@mui/icons-material/Functions';
|
|
|
15
15
|
import ImageIcon from '@mui/icons-material/Image';
|
|
16
16
|
import Redo from '@mui/icons-material/Redo';
|
|
17
17
|
import Undo from '@mui/icons-material/Undo';
|
|
18
|
+
import FormatQuote from '@mui/icons-material/FormatQuote';
|
|
18
19
|
import TheatersIcon from '@mui/icons-material/Theaters';
|
|
19
20
|
import VolumeUpIcon from '@mui/icons-material/VolumeUp';
|
|
20
21
|
import BorderAll from '@mui/icons-material/BorderAll';
|
|
22
|
+
import Delete from '@mui/icons-material/Delete';
|
|
21
23
|
|
|
22
24
|
import { useEditorState } from '@tiptap/react';
|
|
23
25
|
|
|
@@ -65,7 +67,15 @@ const HeadingIcon = () => (
|
|
|
65
67
|
</svg>
|
|
66
68
|
);
|
|
67
69
|
|
|
68
|
-
function MenuBar({
|
|
70
|
+
function MenuBar({
|
|
71
|
+
editor,
|
|
72
|
+
classes,
|
|
73
|
+
activePlugins,
|
|
74
|
+
toolbarOpts: toolOpts,
|
|
75
|
+
responseAreaProps,
|
|
76
|
+
onChange,
|
|
77
|
+
autoWidthToolbar,
|
|
78
|
+
}) {
|
|
69
79
|
const [showPicker, setShowPicker] = useState(false);
|
|
70
80
|
const toolbarOpts = toolOpts ?? {};
|
|
71
81
|
|
|
@@ -83,11 +93,15 @@ function MenuBar({ editor, classes, activePlugins, toolbarOpts: toolOpts, respon
|
|
|
83
93
|
const hideDefaultToolbar =
|
|
84
94
|
ctx.editor?.isActive('math') ||
|
|
85
95
|
ctx.editor?.isActive('explicit_constructed_response') ||
|
|
86
|
-
ctx.editor?.isActive('imageUploadNode')
|
|
96
|
+
ctx.editor?.isActive('imageUploadNode') ||
|
|
97
|
+
ctx.editor?.isActive('drag_in_the_blank');
|
|
98
|
+
|
|
99
|
+
const hasTextSelectionInTable = selection && selection.empty === false && ctx.editor.isActive('table');
|
|
87
100
|
|
|
88
101
|
return {
|
|
89
102
|
currentNode,
|
|
90
103
|
hideDefaultToolbar,
|
|
104
|
+
hasTextSelectionInTable,
|
|
91
105
|
isFocused: ctx.editor?.isFocused,
|
|
92
106
|
isBold: ctx.editor.isActive('bold') ?? false,
|
|
93
107
|
canBold: ctx.editor.can().chain().toggleBold().run() ?? false,
|
|
@@ -122,7 +136,7 @@ function MenuBar({ editor, classes, activePlugins, toolbarOpts: toolOpts, respon
|
|
|
122
136
|
});
|
|
123
137
|
|
|
124
138
|
const hasDoneButton = false;
|
|
125
|
-
const autoWidth =
|
|
139
|
+
const autoWidth = !!autoWidthToolbar;
|
|
126
140
|
|
|
127
141
|
const names = classNames(classes.toolbar, PIE_TOOLBAR__CLASS, {
|
|
128
142
|
[classes.toolbarWithNoDone]: !hasDoneButton,
|
|
@@ -152,35 +166,35 @@ function MenuBar({ editor, classes, activePlugins, toolbarOpts: toolOpts, respon
|
|
|
152
166
|
{
|
|
153
167
|
icon: <AddRow />,
|
|
154
168
|
onClick: (editor) => editor.chain().focus().addRowAfter().run(),
|
|
155
|
-
hidden: (state) => !state.isTable,
|
|
169
|
+
hidden: (state) => !(state.isTable && !state.hasTextSelectionInTable),
|
|
156
170
|
isActive: (state) => state.isTable,
|
|
157
171
|
isDisabled: (state) => !state.canTable,
|
|
158
172
|
},
|
|
159
173
|
{
|
|
160
174
|
icon: <RemoveRow />,
|
|
161
175
|
onClick: (editor) => editor.chain().focus().deleteRow().run(),
|
|
162
|
-
hidden: (state) => !state.isTable,
|
|
176
|
+
hidden: (state) => !(state.isTable && !state.hasTextSelectionInTable),
|
|
163
177
|
isActive: (state) => state.isTable,
|
|
164
178
|
isDisabled: (state) => !state.canTable,
|
|
165
179
|
},
|
|
166
180
|
{
|
|
167
181
|
icon: <AddColumn />,
|
|
168
182
|
onClick: (editor) => editor.chain().focus().addColumnAfter().run(),
|
|
169
|
-
hidden: (state) => !state.isTable,
|
|
183
|
+
hidden: (state) => !(state.isTable && !state.hasTextSelectionInTable),
|
|
170
184
|
isActive: (state) => state.isTable,
|
|
171
185
|
isDisabled: (state) => !state.canTable,
|
|
172
186
|
},
|
|
173
187
|
{
|
|
174
188
|
icon: <RemoveColumn />,
|
|
175
189
|
onClick: (editor) => editor.chain().focus().deleteColumn().run(),
|
|
176
|
-
hidden: (state) => !state.isTable,
|
|
190
|
+
hidden: (state) => !(state.isTable && !state.hasTextSelectionInTable),
|
|
177
191
|
isActive: (state) => state.isTable,
|
|
178
192
|
isDisabled: (state) => !state.canTable,
|
|
179
193
|
},
|
|
180
194
|
{
|
|
181
195
|
icon: <RemoveTable />,
|
|
182
196
|
onClick: (editor) => editor.chain().focus().deleteTable().run(),
|
|
183
|
-
hidden: (state) => !state.isTable,
|
|
197
|
+
hidden: (state) => !(state.isTable && !state.hasTextSelectionInTable),
|
|
184
198
|
isActive: (state) => state.isTable,
|
|
185
199
|
isDisabled: (state) => !state.canTable,
|
|
186
200
|
},
|
|
@@ -196,54 +210,54 @@ function MenuBar({ editor, classes, activePlugins, toolbarOpts: toolOpts, respon
|
|
|
196
210
|
|
|
197
211
|
editor.commands.updateAttributes('table', update);
|
|
198
212
|
},
|
|
199
|
-
hidden: (state) => !state.isTable,
|
|
213
|
+
hidden: (state) => !(state.isTable && !state.hasTextSelectionInTable),
|
|
200
214
|
isActive: (state) => state.tableHasBorder,
|
|
201
215
|
isDisabled: (state) => !state.canTable,
|
|
202
216
|
},
|
|
203
217
|
{
|
|
204
218
|
icon: <Bold />,
|
|
205
219
|
onClick: (editor) => editor.chain().focus().toggleBold().run(),
|
|
206
|
-
hidden: (
|
|
220
|
+
hidden: () => !activePlugins?.includes('bold'),
|
|
207
221
|
isActive: (state) => state.isBold,
|
|
208
222
|
isDisabled: (state) => !state.canBold,
|
|
209
223
|
},
|
|
210
224
|
{
|
|
211
225
|
icon: <Italic />,
|
|
212
226
|
onClick: (editor) => editor.chain().focus().toggleItalic().run(),
|
|
213
|
-
hidden: (
|
|
227
|
+
hidden: () => !activePlugins?.includes('italic'),
|
|
214
228
|
isActive: (state) => state.isItalic,
|
|
215
229
|
isDisabled: (state) => !state.canItalic,
|
|
216
230
|
},
|
|
217
231
|
{
|
|
218
232
|
icon: <Strikethrough />,
|
|
219
233
|
onClick: (editor) => editor.chain().focus().toggleStrike().run(),
|
|
220
|
-
hidden: (
|
|
234
|
+
hidden: () => !activePlugins?.includes('strikethrough'),
|
|
221
235
|
isActive: (state) => state.isStrike,
|
|
222
236
|
isDisabled: (state) => !state.canStrike,
|
|
223
237
|
},
|
|
224
238
|
{
|
|
225
239
|
icon: <Code />,
|
|
226
240
|
onClick: (editor) => editor.chain().focus().toggleCode().run(),
|
|
227
|
-
hidden: (
|
|
241
|
+
hidden: () => !activePlugins?.includes('code'),
|
|
228
242
|
isActive: (state) => state.isCode,
|
|
229
243
|
isDisabled: (state) => !state.canCode,
|
|
230
244
|
},
|
|
231
245
|
{
|
|
232
246
|
icon: <Underline />,
|
|
233
247
|
onClick: (editor) => editor.chain().focus().toggleUnderline().run(),
|
|
234
|
-
hidden: (
|
|
248
|
+
hidden: () => !activePlugins?.includes('underline'),
|
|
235
249
|
isActive: (state) => state.isUnderline,
|
|
236
250
|
},
|
|
237
251
|
{
|
|
238
252
|
icon: <SubscriptIcon />,
|
|
239
253
|
onClick: (editor) => editor.chain().focus().toggleSubscript().run(),
|
|
240
|
-
hidden: (
|
|
254
|
+
hidden: () => !activePlugins?.includes('subscript'),
|
|
241
255
|
isActive: (state) => state.isSubScript,
|
|
242
256
|
},
|
|
243
257
|
{
|
|
244
258
|
icon: <SuperscriptIcon />,
|
|
245
259
|
onClick: (editor) => editor.chain().focus().toggleSuperscript().run(),
|
|
246
|
-
hidden: (
|
|
260
|
+
hidden: () => !activePlugins?.includes('superscript'),
|
|
247
261
|
isActive: (state) => state.isSuperScript,
|
|
248
262
|
},
|
|
249
263
|
{
|
|
@@ -253,22 +267,28 @@ function MenuBar({ editor, classes, activePlugins, toolbarOpts: toolOpts, respon
|
|
|
253
267
|
},
|
|
254
268
|
{
|
|
255
269
|
icon: <TheatersIcon />,
|
|
256
|
-
hidden: (
|
|
270
|
+
hidden: () => !activePlugins?.includes('video'),
|
|
257
271
|
onClick: (editor) => editor.chain().focus().insertMedia({ type: 'video' }).run(),
|
|
258
272
|
},
|
|
259
273
|
{
|
|
260
274
|
icon: <VolumeUpIcon />,
|
|
261
|
-
hidden: (
|
|
275
|
+
hidden: () => !activePlugins?.includes('audio'),
|
|
262
276
|
onClick: (editor) => editor.chain().focus().insertMedia({ type: 'audio', tag: 'audio' }).run(),
|
|
263
277
|
},
|
|
264
278
|
{
|
|
265
279
|
icon: <CSSIcon />,
|
|
266
|
-
hidden: (
|
|
280
|
+
hidden: () => !activePlugins?.includes('css'),
|
|
267
281
|
onClick: (editor) => editor.commands.openCSSClassDialog(),
|
|
268
282
|
},
|
|
283
|
+
{
|
|
284
|
+
icon: <FormatQuote />,
|
|
285
|
+
hidden: () => !activePlugins?.includes('blockquote'),
|
|
286
|
+
onClick: (editor) => editor.chain().focus().toggleBlockquote().run(),
|
|
287
|
+
isActive: (state) => state.isBlockquote,
|
|
288
|
+
},
|
|
269
289
|
{
|
|
270
290
|
icon: <HeadingIcon />,
|
|
271
|
-
hidden: (
|
|
291
|
+
hidden: () => !activePlugins?.includes('h3'),
|
|
272
292
|
onClick: (editor) => editor.chain().focus().toggleHeading({ level: 3 }).run(),
|
|
273
293
|
isActive: (state) => state.isHeading3,
|
|
274
294
|
},
|
|
@@ -289,30 +309,30 @@ function MenuBar({ editor, classes, activePlugins, toolbarOpts: toolOpts, respon
|
|
|
289
309
|
},
|
|
290
310
|
{
|
|
291
311
|
icon: <TextAlignIcon editor={editor} />,
|
|
292
|
-
hidden: (
|
|
312
|
+
hidden: () => !activePlugins?.includes('text-align'),
|
|
293
313
|
onClick: () => {},
|
|
294
314
|
},
|
|
295
315
|
{
|
|
296
316
|
icon: <BulletedListIcon />,
|
|
297
|
-
hidden: (
|
|
317
|
+
hidden: () => !activePlugins?.includes('bulleted-list'),
|
|
298
318
|
onClick: (editor) => editor.chain().focus().toggleBulletList().run(),
|
|
299
319
|
isActive: (state) => state.isBulletList,
|
|
300
320
|
},
|
|
301
321
|
{
|
|
302
322
|
icon: <NumberedListIcon />,
|
|
303
|
-
hidden: (
|
|
323
|
+
hidden: () => !activePlugins?.includes('numbered-list'),
|
|
304
324
|
onClick: (editor) => editor.chain().focus().toggleOrderedList().run(),
|
|
305
325
|
isActive: (state) => state.isOrderedList,
|
|
306
326
|
},
|
|
307
327
|
{
|
|
308
328
|
icon: <Undo />,
|
|
309
|
-
hidden: (
|
|
329
|
+
hidden: () => !activePlugins?.includes('undo'),
|
|
310
330
|
onClick: (editor) => editor.chain().focus().undo().run(),
|
|
311
331
|
isDisabled: (state) => !state.canUndo,
|
|
312
332
|
},
|
|
313
333
|
{
|
|
314
334
|
icon: <Redo />,
|
|
315
|
-
hidden: (
|
|
335
|
+
hidden: () => !activePlugins?.includes('redo'),
|
|
316
336
|
onClick: (editor) => editor.chain().focus().redo().run(),
|
|
317
337
|
isDisabled: (state) => !state.canRedo,
|
|
318
338
|
},
|
|
@@ -320,8 +340,29 @@ function MenuBar({ editor, classes, activePlugins, toolbarOpts: toolOpts, respon
|
|
|
320
340
|
[activePlugins, editor],
|
|
321
341
|
);
|
|
322
342
|
|
|
343
|
+
const isDragInTheBlankSelected =
|
|
344
|
+
editorState.hideDefaultToolbar && editorState.currentNode?.type?.name === 'drag_in_the_blank';
|
|
345
|
+
|
|
323
346
|
return (
|
|
324
347
|
<div className={names} style={{ ...customStyles }} onMouseDown={handleMouseDown}>
|
|
348
|
+
{isDragInTheBlankSelected && (
|
|
349
|
+
<div className={classes.defaultToolbar} tabIndex="1">
|
|
350
|
+
<div className={classes.buttonsContainer}>
|
|
351
|
+
<button
|
|
352
|
+
type="button"
|
|
353
|
+
className={classes.button}
|
|
354
|
+
onClick={(e) => {
|
|
355
|
+
e.preventDefault();
|
|
356
|
+
editor.chain().focus().deleteSelection().run();
|
|
357
|
+
onChange?.(editor.getHTML());
|
|
358
|
+
}}
|
|
359
|
+
aria-label="Delete response area"
|
|
360
|
+
>
|
|
361
|
+
<Delete />
|
|
362
|
+
</button>
|
|
363
|
+
</div>
|
|
364
|
+
</div>
|
|
365
|
+
)}
|
|
325
366
|
{!editorState.hideDefaultToolbar && (
|
|
326
367
|
<div className={classes.defaultToolbar} tabIndex="1">
|
|
327
368
|
<div className={classes.buttonsContainer}>
|
|
@@ -73,9 +73,10 @@ const StyledRoot = styled('div', {
|
|
|
73
73
|
},
|
|
74
74
|
},
|
|
75
75
|
'& blockquote': {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
background: '#f9f9f9',
|
|
77
|
+
borderLeft: '5px solid #ccc',
|
|
78
|
+
margin: '1.5em 10px',
|
|
79
|
+
padding: '.5em 10px',
|
|
79
80
|
},
|
|
80
81
|
'& hr': {
|
|
81
82
|
border: 'none',
|
|
@@ -108,13 +109,13 @@ const StyledRoot = styled('div', {
|
|
|
108
109
|
}));
|
|
109
110
|
|
|
110
111
|
const StyledEditorHolder = styled('div', {
|
|
111
|
-
shouldForwardProp: (prop) =>
|
|
112
|
-
})(({ disableScrollbar }) => ({
|
|
112
|
+
shouldForwardProp: (prop) => !['disableScrollbar', 'highlightShape'].includes(prop),
|
|
113
|
+
})(({ theme, disableScrollbar, highlightShape }) => ({
|
|
113
114
|
position: 'relative',
|
|
114
115
|
padding: '0px',
|
|
115
116
|
overflowY: 'auto',
|
|
116
117
|
color: color.text(),
|
|
117
|
-
backgroundColor: color.background(),
|
|
118
|
+
backgroundColor: highlightShape ? theme.palette.action.selected : color.background(),
|
|
118
119
|
...(disableScrollbar && {
|
|
119
120
|
'&::-webkit-scrollbar': {
|
|
120
121
|
display: 'none',
|
|
@@ -148,6 +149,7 @@ function TiptapContainer(props) {
|
|
|
148
149
|
minHeight,
|
|
149
150
|
height,
|
|
150
151
|
maxHeight,
|
|
152
|
+
highlightShape,
|
|
151
153
|
ref,
|
|
152
154
|
} = props;
|
|
153
155
|
|
|
@@ -195,7 +197,7 @@ function TiptapContainer(props) {
|
|
|
195
197
|
style={{ width: sizeStyle.width, minWidth: sizeStyle.minWidth, maxWidth: sizeStyle.maxWidth }}
|
|
196
198
|
ref={rootRef}
|
|
197
199
|
>
|
|
198
|
-
<StyledEditorHolder disableScrollbar={disableScrollbar}>
|
|
200
|
+
<StyledEditorHolder disableScrollbar={disableScrollbar} highlightShape={highlightShape}>
|
|
199
201
|
<StyledChildren noPadding={toolbarOpts && toolbarOpts.noPadding}>{children}</StyledChildren>
|
|
200
202
|
</StyledEditorHolder>
|
|
201
203
|
|
|
@@ -206,6 +208,7 @@ function TiptapContainer(props) {
|
|
|
206
208
|
toolbarOpts={toolbarOpts}
|
|
207
209
|
activePlugins={activePlugins}
|
|
208
210
|
onChange={props.onChange}
|
|
211
|
+
autoWidthToolbar={props.autoWidthToolbar}
|
|
209
212
|
/>
|
|
210
213
|
)}
|
|
211
214
|
</StyledRoot>
|
|
@@ -194,4 +194,26 @@ describe('CharacterPicker', () => {
|
|
|
194
194
|
const dialog = container.querySelector('.insert-character-dialog');
|
|
195
195
|
expect(dialog).toHaveStyle({ position: 'absolute' });
|
|
196
196
|
});
|
|
197
|
+
|
|
198
|
+
it('adds data-toolbar-for attribute with editor instanceId', () => {
|
|
199
|
+
const editorWithInstanceId = {
|
|
200
|
+
...mockEditor,
|
|
201
|
+
instanceId: 'editor-123',
|
|
202
|
+
};
|
|
203
|
+
const opts = {
|
|
204
|
+
characters: [['á', 'é']],
|
|
205
|
+
};
|
|
206
|
+
const { container } = render(<CharacterPicker editor={editorWithInstanceId} opts={opts} onClose={jest.fn()} />);
|
|
207
|
+
const dialog = container.querySelector('.insert-character-dialog');
|
|
208
|
+
expect(dialog).toHaveAttribute('data-toolbar-for', 'editor-123');
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('renders without instanceId gracefully', () => {
|
|
212
|
+
const opts = {
|
|
213
|
+
characters: [['á', 'é']],
|
|
214
|
+
};
|
|
215
|
+
const { container } = render(<CharacterPicker editor={mockEditor} opts={opts} onClose={jest.fn()} />);
|
|
216
|
+
const dialog = container.querySelector('.insert-character-dialog');
|
|
217
|
+
expect(dialog).toBeInTheDocument();
|
|
218
|
+
});
|
|
197
219
|
});
|
|
@@ -124,7 +124,7 @@ describe('ExplicitConstructedResponse', () => {
|
|
|
124
124
|
|
|
125
125
|
it('calls respAreaToolbar with correct params', () => {
|
|
126
126
|
render(<ExplicitConstructedResponse {...defaultProps} />);
|
|
127
|
-
expect(mockOptions.respAreaToolbar).toHaveBeenCalledWith(mockNode, mockEditor, expect.any(Function));
|
|
127
|
+
expect(mockOptions.respAreaToolbar).toHaveBeenCalledWith([mockNode, 5], mockEditor, expect.any(Function));
|
|
128
128
|
});
|
|
129
129
|
|
|
130
130
|
it('has cursor pointer style', () => {
|
|
@@ -132,7 +132,7 @@ describe('InlineDropdown', () => {
|
|
|
132
132
|
|
|
133
133
|
it('calls respAreaToolbar with correct params', () => {
|
|
134
134
|
render(<InlineDropdown {...defaultProps} />);
|
|
135
|
-
expect(mockOptions.respAreaToolbar).toHaveBeenCalledWith(mockNode, mockEditor, expect.any(Function));
|
|
135
|
+
expect(mockOptions.respAreaToolbar).toHaveBeenCalledWith([mockNode, 5], mockEditor, expect.any(Function));
|
|
136
136
|
});
|
|
137
137
|
|
|
138
138
|
it('closes toolbar on outside click', async () => {
|
|
@@ -184,4 +184,153 @@ describe('InlineDropdown', () => {
|
|
|
184
184
|
}
|
|
185
185
|
});
|
|
186
186
|
});
|
|
187
|
+
|
|
188
|
+
it('passes editorCallback to InlineDropdownToolbar', async () => {
|
|
189
|
+
const mockToolbarComponent = jest.fn(({ editorCallback }) => {
|
|
190
|
+
editorCallback?.({ instanceId: 'test-instance' });
|
|
191
|
+
return <div data-testid="inline-dropdown-toolbar">Toolbar</div>;
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
const mockOptionsWithCallback = {
|
|
195
|
+
respAreaToolbar: jest.fn(() => mockToolbarComponent),
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const { queryByTestId } = render(
|
|
199
|
+
<InlineDropdown {...defaultProps} options={mockOptionsWithCallback} selected={true} />,
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
await waitFor(() => {
|
|
203
|
+
expect(queryByTestId('inline-dropdown-toolbar')).toBeInTheDocument();
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('stores toolbar editor instance in ref when editorCallback is called', async () => {
|
|
208
|
+
let capturedCallback;
|
|
209
|
+
const mockToolbarComponent = ({ editorCallback }) => {
|
|
210
|
+
capturedCallback = editorCallback;
|
|
211
|
+
return <div data-testid="inline-dropdown-toolbar">Toolbar</div>;
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const mockOptionsWithCallback = {
|
|
215
|
+
respAreaToolbar: jest.fn(() => mockToolbarComponent),
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const { queryByTestId } = render(
|
|
219
|
+
<InlineDropdown {...defaultProps} options={mockOptionsWithCallback} selected={true} />,
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
await waitFor(() => {
|
|
223
|
+
expect(queryByTestId('inline-dropdown-toolbar')).toBeInTheDocument();
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// Verify callback exists
|
|
227
|
+
expect(capturedCallback).toBeDefined();
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('handles click outside logic with data-toolbar-for attribute', async () => {
|
|
231
|
+
const editorWithInstanceId = {
|
|
232
|
+
...mockEditor,
|
|
233
|
+
instanceId: 'editor-123',
|
|
234
|
+
_toolbarOpened: false,
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
// Mock the toolbar callback to set the toolbar editor instance
|
|
238
|
+
let capturedCallback;
|
|
239
|
+
const mockToolbarComponent = ({ editorCallback }) => {
|
|
240
|
+
React.useEffect(() => {
|
|
241
|
+
capturedCallback = editorCallback;
|
|
242
|
+
if (editorCallback) {
|
|
243
|
+
editorCallback({ instanceId: 'editor-123' });
|
|
244
|
+
}
|
|
245
|
+
}, [editorCallback]);
|
|
246
|
+
return <div data-testid="inline-dropdown-toolbar">Toolbar</div>;
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const mockOptionsWithCallback = {
|
|
250
|
+
respAreaToolbar: jest.fn(() => mockToolbarComponent),
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
const { container, queryByTestId } = render(
|
|
254
|
+
<InlineDropdown
|
|
255
|
+
{...defaultProps}
|
|
256
|
+
editor={editorWithInstanceId}
|
|
257
|
+
options={mockOptionsWithCallback}
|
|
258
|
+
selected={true}
|
|
259
|
+
/>,
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
await waitFor(() => {
|
|
263
|
+
expect(queryByTestId('inline-dropdown-toolbar')).toBeInTheDocument();
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// Create an element with data-toolbar-for attribute
|
|
267
|
+
const otherToolbar = document.createElement('div');
|
|
268
|
+
otherToolbar.setAttribute('data-toolbar-for', 'editor-456');
|
|
269
|
+
document.body.appendChild(otherToolbar);
|
|
270
|
+
|
|
271
|
+
await waitFor(() => {
|
|
272
|
+
fireEvent.mouseDown(otherToolbar);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// Cleanup
|
|
276
|
+
document.body.removeChild(otherToolbar);
|
|
277
|
+
expect(container).toBeInTheDocument();
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it('does not close when clicking inside same editor toolbar', async () => {
|
|
281
|
+
const editorWithInstanceId = {
|
|
282
|
+
...mockEditor,
|
|
283
|
+
instanceId: 'editor-123',
|
|
284
|
+
_toolbarOpened: false,
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
let toolbarEditorInstance;
|
|
288
|
+
const mockToolbarComponent = ({ editorCallback }) => {
|
|
289
|
+
React.useEffect(() => {
|
|
290
|
+
if (editorCallback) {
|
|
291
|
+
editorCallback({ instanceId: 'editor-123' });
|
|
292
|
+
toolbarEditorInstance = { instanceId: 'editor-123' };
|
|
293
|
+
}
|
|
294
|
+
}, [editorCallback]);
|
|
295
|
+
return <div data-testid="inline-dropdown-toolbar">Toolbar</div>;
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
const mockOptionsWithCallback = {
|
|
299
|
+
respAreaToolbar: jest.fn(() => mockToolbarComponent),
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
render(
|
|
303
|
+
<InlineDropdown
|
|
304
|
+
{...defaultProps}
|
|
305
|
+
editor={editorWithInstanceId}
|
|
306
|
+
options={mockOptionsWithCallback}
|
|
307
|
+
selected={true}
|
|
308
|
+
/>,
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
await waitFor(() => {
|
|
312
|
+
expect(mockOptionsWithCallback.respAreaToolbar).toHaveBeenCalled();
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('checks editor._toolbarOpened in click outside handler', async () => {
|
|
317
|
+
const editorWithToolbarOpened = {
|
|
318
|
+
...mockEditor,
|
|
319
|
+
_toolbarOpened: true,
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
const { queryByTestId } = render(
|
|
323
|
+
<InlineDropdown {...defaultProps} editor={editorWithToolbarOpened} selected={true} />,
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
await waitFor(() => {
|
|
327
|
+
expect(queryByTestId('inline-dropdown-toolbar')).toBeInTheDocument();
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// When _toolbarOpened is true, clicking outside should not close
|
|
331
|
+
fireEvent.mouseDown(document.body);
|
|
332
|
+
|
|
333
|
+
// Toolbar should still be visible
|
|
334
|
+
expect(queryByTestId('inline-dropdown-toolbar')).toBeInTheDocument();
|
|
335
|
+
});
|
|
187
336
|
});
|
|
@@ -214,4 +214,36 @@ describe('StyledMenuBar', () => {
|
|
|
214
214
|
toolbar?.dispatchEvent(event);
|
|
215
215
|
expect(preventDefaultSpy).toHaveBeenCalled();
|
|
216
216
|
});
|
|
217
|
+
|
|
218
|
+
it('calculates hasTextSelectionInTable correctly when selection is not empty in table', () => {
|
|
219
|
+
// This test verifies the hasTextSelectionInTable state computation
|
|
220
|
+
const { container } = render(<StyledMenuBar {...defaultProps} activePlugins={['table', 'bold', 'italic']} />);
|
|
221
|
+
expect(container).toBeInTheDocument();
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('hides table manipulation buttons when text is selected in table', () => {
|
|
225
|
+
// When hasTextSelectionInTable is true, table row/column buttons should be hidden
|
|
226
|
+
const { container } = render(<StyledMenuBar {...defaultProps} activePlugins={['table']} />);
|
|
227
|
+
// The component should render but table manipulation buttons should be conditional
|
|
228
|
+
expect(container).toBeInTheDocument();
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('shows table manipulation buttons when no text is selected in table', () => {
|
|
232
|
+
// When hasTextSelectionInTable is false, table row/column buttons should be visible
|
|
233
|
+
const { container } = render(<StyledMenuBar {...defaultProps} activePlugins={['table']} />);
|
|
234
|
+
expect(container).toBeInTheDocument();
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('shows text formatting buttons regardless of table state', () => {
|
|
238
|
+
// Bold, italic, etc. should always be visible when their plugin is active
|
|
239
|
+
const { container } = render(<StyledMenuBar {...defaultProps} activePlugins={['bold', 'italic', 'underline']} />);
|
|
240
|
+
const buttons = container.querySelectorAll('button');
|
|
241
|
+
expect(buttons.length).toBeGreaterThan(0);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('does not hide text formatting buttons when in table', () => {
|
|
245
|
+
// Verify that the removal of "|| state.isTable" condition works correctly
|
|
246
|
+
const { container } = render(<StyledMenuBar {...defaultProps} activePlugins={['table', 'bold', 'italic']} />);
|
|
247
|
+
expect(container).toBeInTheDocument();
|
|
248
|
+
});
|
|
217
249
|
});
|
|
@@ -60,7 +60,7 @@ export default ({ editor, onChange }) => {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
const applyAlignment = (event) => {
|
|
63
|
-
const alignType = event.
|
|
63
|
+
const alignType = event.currentTarget?.getAttribute('value');
|
|
64
64
|
|
|
65
65
|
if (alignType) {
|
|
66
66
|
editor.commands.setTextAlign(alignType);
|
|
@@ -33,7 +33,11 @@ const DragDrop = (props) => {
|
|
|
33
33
|
|
|
34
34
|
// console.log({nodeProps.children})
|
|
35
35
|
return (
|
|
36
|
-
<NodeViewWrapper
|
|
36
|
+
<NodeViewWrapper
|
|
37
|
+
className="drag-in-the-blank"
|
|
38
|
+
data-selected={selected}
|
|
39
|
+
style={{ display: 'inline', whiteSpace: 'normal' }}
|
|
40
|
+
>
|
|
37
41
|
<span
|
|
38
42
|
{...attributes}
|
|
39
43
|
style={{
|
|
@@ -52,6 +56,7 @@ const DragDrop = (props) => {
|
|
|
52
56
|
pos={pos}
|
|
53
57
|
value={attributes}
|
|
54
58
|
duplicates={options.duplicates}
|
|
59
|
+
selected={selected}
|
|
55
60
|
onChange={(choice) => onValueChange(editor, node, pos, choice)}
|
|
56
61
|
removeResponse={(choice) => onRemoveResponse(editor, node, choice)}
|
|
57
62
|
></DragDropTile>
|
|
@@ -15,7 +15,7 @@ const StyledContent = styled('span')(({ theme }) => ({
|
|
|
15
15
|
},
|
|
16
16
|
}));
|
|
17
17
|
|
|
18
|
-
export function BlankContent({ n, children, isDragging, isOver, dragItem, value }) {
|
|
18
|
+
export function BlankContent({ n, children, isDragging, isOver, dragItem, value, selected }) {
|
|
19
19
|
const [hoveredElementSize, setHoveredElementSize] = useState(null);
|
|
20
20
|
const elementRef = useRef(null);
|
|
21
21
|
|
|
@@ -56,15 +56,22 @@ export function BlankContent({ n, children, isDragging, isOver, dragItem, value
|
|
|
56
56
|
const hasGrip = finalLabel !== '\u00A0';
|
|
57
57
|
const isPreview = dragItem && isOver;
|
|
58
58
|
|
|
59
|
+
const borderStyle = selected
|
|
60
|
+
? `2px solid ${color.primaryDark()}`
|
|
61
|
+
: isPreview
|
|
62
|
+
? `1px solid ${color.defaults.BORDER_DARK}`
|
|
63
|
+
: `1px solid ${color.defaults.BORDER_LIGHT}`;
|
|
64
|
+
|
|
59
65
|
return (
|
|
60
66
|
<div
|
|
61
67
|
ref={elementRef}
|
|
68
|
+
className={selected ? 'selected' : undefined}
|
|
62
69
|
style={{
|
|
63
70
|
display: 'inline-flex',
|
|
64
71
|
minWidth: '178px',
|
|
65
72
|
minHeight: '36px',
|
|
66
73
|
background: isPreview ? `${color.defaults.BORDER_LIGHT}` : `${color.defaults.WHITE}`,
|
|
67
|
-
border:
|
|
74
|
+
border: borderStyle,
|
|
68
75
|
boxSizing: 'border-box',
|
|
69
76
|
borderRadius: '3px',
|
|
70
77
|
overflow: 'hidden',
|
|
@@ -72,6 +79,7 @@ export function BlankContent({ n, children, isDragging, isOver, dragItem, value
|
|
|
72
79
|
padding: '8px 8px 8px 35px',
|
|
73
80
|
width: hoveredElementSize ? hoveredElementSize.width : undefined,
|
|
74
81
|
height: hoveredElementSize ? hoveredElementSize.height : undefined,
|
|
82
|
+
touchAction: 'none',
|
|
75
83
|
}}
|
|
76
84
|
data-key={n.index}
|
|
77
85
|
contentEditable={false}
|
|
@@ -104,9 +112,21 @@ BlankContent.propTypes = {
|
|
|
104
112
|
isOver: PropTypes.bool,
|
|
105
113
|
dragItem: PropTypes.object,
|
|
106
114
|
value: PropTypes.object,
|
|
115
|
+
selected: PropTypes.bool,
|
|
107
116
|
};
|
|
108
117
|
|
|
109
|
-
function DragDropChoice({
|
|
118
|
+
function DragDropChoice({
|
|
119
|
+
value,
|
|
120
|
+
disabled,
|
|
121
|
+
instanceId,
|
|
122
|
+
children,
|
|
123
|
+
n,
|
|
124
|
+
onChange,
|
|
125
|
+
removeResponse,
|
|
126
|
+
duplicates,
|
|
127
|
+
pos,
|
|
128
|
+
selected,
|
|
129
|
+
}) {
|
|
110
130
|
const {
|
|
111
131
|
attributes: dragAttributes,
|
|
112
132
|
listeners: dragListeners,
|
|
@@ -196,7 +216,14 @@ function DragDropChoice({ value, disabled, instanceId, children, n, onChange, re
|
|
|
196
216
|
};
|
|
197
217
|
|
|
198
218
|
const dragContent = (
|
|
199
|
-
<BlankContent
|
|
219
|
+
<BlankContent
|
|
220
|
+
n={n}
|
|
221
|
+
isDragging={isDragging}
|
|
222
|
+
isOver={isOver}
|
|
223
|
+
dragItem={dragItem?.data?.current}
|
|
224
|
+
value={value}
|
|
225
|
+
selected={selected}
|
|
226
|
+
>
|
|
200
227
|
{children}
|
|
201
228
|
</BlankContent>
|
|
202
229
|
);
|
|
@@ -223,6 +250,7 @@ DragDropChoice.propTypes = {
|
|
|
223
250
|
onChange: PropTypes.func.isRequired,
|
|
224
251
|
removeResponse: PropTypes.func.isRequired,
|
|
225
252
|
duplicates: PropTypes.bool,
|
|
253
|
+
selected: PropTypes.bool,
|
|
226
254
|
};
|
|
227
255
|
|
|
228
256
|
export default DragDropChoice;
|
|
@@ -9,7 +9,7 @@ const ExplicitConstructedResponse = (props) => {
|
|
|
9
9
|
const { respAreaToolbar, error: errorFn } = options;
|
|
10
10
|
const pos = getPos();
|
|
11
11
|
const [showToolbar, setShowToolbar] = useState(false);
|
|
12
|
-
const EcrToolbar = respAreaToolbar(node, editor, () => {});
|
|
12
|
+
const EcrToolbar = respAreaToolbar([node, pos], editor, () => {});
|
|
13
13
|
const toolbarRef = useRef(null);
|
|
14
14
|
|
|
15
15
|
let error;
|