Skip to content

Commit 83403ea

Browse files
authored
Merge pull request #2135 from pie-framework/fix/PD-5616-PD-5582-PD-5603-PD-5604-PD-5605
fix: editable-area responsive, added handling for toolbars inside responseArea, ability to provide editor instance PD-5616-PD-5582-PD-5603-PD-5604-PD-5605
2 parents 09270ba + 02213cd commit 83403ea

12 files changed

Lines changed: 768 additions & 24 deletions

File tree

packages/editable-html-tip-tap/src/__tests__/EditableHtml.test.jsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,4 +266,39 @@ describe('EditableHtml', () => {
266266
const { container } = render(<EditableHtml {...defaultProps} disableImageAlignmentButtons={true} />);
267267
expect(container).toBeInTheDocument();
268268
});
269+
270+
it('calls editorRef callback when editor is initialized', async () => {
271+
const editorRef = jest.fn();
272+
render(<EditableHtml {...defaultProps} editorRef={editorRef} />);
273+
274+
await waitFor(() => {
275+
expect(editorRef).toHaveBeenCalled();
276+
});
277+
});
278+
279+
it('calls editorRef with the editor instance', async () => {
280+
const editorRef = jest.fn();
281+
render(<EditableHtml {...defaultProps} editorRef={editorRef} />);
282+
283+
await waitFor(() => {
284+
expect(editorRef).toHaveBeenCalled();
285+
// Verify it was called with an object that has editor-like properties
286+
const callArg = editorRef.mock.calls[0][0];
287+
expect(callArg).toHaveProperty('getHTML');
288+
expect(callArg).toHaveProperty('commands');
289+
});
290+
});
291+
292+
it('handles editorRef being undefined', () => {
293+
const { container } = render(<EditableHtml {...defaultProps} editorRef={undefined} />);
294+
expect(container).toBeInTheDocument();
295+
});
296+
297+
it('applies flex display to StyledEditorContent', async () => {
298+
const { getByTestId } = render(<EditableHtml {...defaultProps} />);
299+
await waitFor(() => {
300+
const editorContent = getByTestId('editor-content');
301+
expect(editorContent).toBeInTheDocument();
302+
});
303+
});
269304
});

packages/editable-html-tip-tap/src/components/CharacterPicker.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ export function CharacterPicker({ editor, opts, onClose }) {
121121
<div
122122
ref={containerRef}
123123
className="insert-character-dialog"
124+
data-toolbar-for={editor.instanceId}
124125
style={{
125126
visibility: position.top === 0 && position.left === 0 ? 'hidden' : 'initial',
126127
position: 'absolute',

packages/editable-html-tip-tap/src/components/EditableHtml.jsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,12 @@ export const EditableHtml = (props) => {
280280
[props.charactersLimit],
281281
);
282282

283+
useEffect(() => {
284+
if (props.editorRef) {
285+
props.editorRef(editor);
286+
}
287+
}, [props.editorRef, editor]);
288+
283289
useEffect(() => {
284290
editor?.setEditable(!props.disabled);
285291
}, [props.disabled, editor]);
@@ -349,8 +355,10 @@ export const EditableHtml = (props) => {
349355
const StyledEditorContent = styled(EditorContent, {
350356
shouldForwardProp: (prop) => !['showParagraph', 'separateParagraph'].includes(prop),
351357
})(({ showParagraph, separateParagraph }) => ({
358+
display: 'flex',
352359
outline: 'none !important',
353360
'& .ProseMirror': {
361+
flex: 1,
354362
padding: '5px',
355363
maxHeight: '500px',
356364
outline: 'none !important',

packages/editable-html-tip-tap/src/components/MenuBar.jsx

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,12 @@ function MenuBar({
9595
ctx.editor?.isActive('imageUploadNode') ||
9696
ctx.editor?.isActive('drag_in_the_blank');
9797

98+
const hasTextSelectionInTable = selection && selection.empty === false && ctx.editor.isActive('table');
99+
98100
return {
99101
currentNode,
100102
hideDefaultToolbar,
103+
hasTextSelectionInTable,
101104
isFocused: ctx.editor?.isFocused,
102105
isBold: ctx.editor.isActive('bold') ?? false,
103106
canBold: ctx.editor.can().chain().toggleBold().run() ?? false,
@@ -162,35 +165,35 @@ function MenuBar({
162165
{
163166
icon: <AddRow />,
164167
onClick: (editor) => editor.chain().focus().addRowAfter().run(),
165-
hidden: (state) => !state.isTable,
168+
hidden: (state) => !(state.isTable && !state.hasTextSelectionInTable),
166169
isActive: (state) => state.isTable,
167170
isDisabled: (state) => !state.canTable,
168171
},
169172
{
170173
icon: <RemoveRow />,
171174
onClick: (editor) => editor.chain().focus().deleteRow().run(),
172-
hidden: (state) => !state.isTable,
175+
hidden: (state) => !(state.isTable && !state.hasTextSelectionInTable),
173176
isActive: (state) => state.isTable,
174177
isDisabled: (state) => !state.canTable,
175178
},
176179
{
177180
icon: <AddColumn />,
178181
onClick: (editor) => editor.chain().focus().addColumnAfter().run(),
179-
hidden: (state) => !state.isTable,
182+
hidden: (state) => !(state.isTable && !state.hasTextSelectionInTable),
180183
isActive: (state) => state.isTable,
181184
isDisabled: (state) => !state.canTable,
182185
},
183186
{
184187
icon: <RemoveColumn />,
185188
onClick: (editor) => editor.chain().focus().deleteColumn().run(),
186-
hidden: (state) => !state.isTable,
189+
hidden: (state) => !(state.isTable && !state.hasTextSelectionInTable),
187190
isActive: (state) => state.isTable,
188191
isDisabled: (state) => !state.canTable,
189192
},
190193
{
191194
icon: <RemoveTable />,
192195
onClick: (editor) => editor.chain().focus().deleteTable().run(),
193-
hidden: (state) => !state.isTable,
196+
hidden: (state) => !(state.isTable && !state.hasTextSelectionInTable),
194197
isActive: (state) => state.isTable,
195198
isDisabled: (state) => !state.canTable,
196199
},
@@ -206,54 +209,54 @@ function MenuBar({
206209

207210
editor.commands.updateAttributes('table', update);
208211
},
209-
hidden: (state) => !state.isTable,
212+
hidden: (state) => !(state.isTable && !state.hasTextSelectionInTable),
210213
isActive: (state) => state.tableHasBorder,
211214
isDisabled: (state) => !state.canTable,
212215
},
213216
{
214217
icon: <Bold />,
215218
onClick: (editor) => editor.chain().focus().toggleBold().run(),
216-
hidden: (state) => !activePlugins?.includes('bold') || state.isTable,
219+
hidden: () => !activePlugins?.includes('bold'),
217220
isActive: (state) => state.isBold,
218221
isDisabled: (state) => !state.canBold,
219222
},
220223
{
221224
icon: <Italic />,
222225
onClick: (editor) => editor.chain().focus().toggleItalic().run(),
223-
hidden: (state) => !activePlugins?.includes('italic') || state.isTable,
226+
hidden: () => !activePlugins?.includes('italic'),
224227
isActive: (state) => state.isItalic,
225228
isDisabled: (state) => !state.canItalic,
226229
},
227230
{
228231
icon: <Strikethrough />,
229232
onClick: (editor) => editor.chain().focus().toggleStrike().run(),
230-
hidden: (state) => !activePlugins?.includes('strikethrough') || state.isTable,
233+
hidden: () => !activePlugins?.includes('strikethrough'),
231234
isActive: (state) => state.isStrike,
232235
isDisabled: (state) => !state.canStrike,
233236
},
234237
{
235238
icon: <Code />,
236239
onClick: (editor) => editor.chain().focus().toggleCode().run(),
237-
hidden: (state) => !activePlugins?.includes('code') || state.isTable,
240+
hidden: () => !activePlugins?.includes('code'),
238241
isActive: (state) => state.isCode,
239242
isDisabled: (state) => !state.canCode,
240243
},
241244
{
242245
icon: <Underline />,
243246
onClick: (editor) => editor.chain().focus().toggleUnderline().run(),
244-
hidden: (state) => !activePlugins?.includes('underline') || state.isTable,
247+
hidden: () => !activePlugins?.includes('underline'),
245248
isActive: (state) => state.isUnderline,
246249
},
247250
{
248251
icon: <SubscriptIcon />,
249252
onClick: (editor) => editor.chain().focus().toggleSubscript().run(),
250-
hidden: (state) => !activePlugins?.includes('subscript') || state.isTable,
253+
hidden: () => !activePlugins?.includes('subscript'),
251254
isActive: (state) => state.isSubScript,
252255
},
253256
{
254257
icon: <SuperscriptIcon />,
255258
onClick: (editor) => editor.chain().focus().toggleSuperscript().run(),
256-
hidden: (state) => !activePlugins?.includes('superscript') || state.isTable,
259+
hidden: () => !activePlugins?.includes('superscript'),
257260
isActive: (state) => state.isSuperScript,
258261
},
259262
{
@@ -263,22 +266,22 @@ function MenuBar({
263266
},
264267
{
265268
icon: <TheatersIcon />,
266-
hidden: (state) => !activePlugins?.includes('video') || state.isTable,
269+
hidden: () => !activePlugins?.includes('video'),
267270
onClick: (editor) => editor.chain().focus().insertMedia({ type: 'video' }).run(),
268271
},
269272
{
270273
icon: <VolumeUpIcon />,
271-
hidden: (state) => !activePlugins?.includes('audio') || state.isTable,
274+
hidden: () => !activePlugins?.includes('audio'),
272275
onClick: (editor) => editor.chain().focus().insertMedia({ type: 'audio', tag: 'audio' }).run(),
273276
},
274277
{
275278
icon: <CSSIcon />,
276-
hidden: (state) => !activePlugins?.includes('css') || state.isTable,
279+
hidden: () => !activePlugins?.includes('css'),
277280
onClick: (editor) => editor.commands.openCSSClassDialog(),
278281
},
279282
{
280283
icon: <HeadingIcon />,
281-
hidden: (state) => !activePlugins?.includes('h3') || state.isTable,
284+
hidden: () => !activePlugins?.includes('h3'),
282285
onClick: (editor) => editor.chain().focus().toggleHeading({ level: 3 }).run(),
283286
isActive: (state) => state.isHeading3,
284287
},
@@ -299,30 +302,30 @@ function MenuBar({
299302
},
300303
{
301304
icon: <TextAlignIcon editor={editor} />,
302-
hidden: (state) => !activePlugins?.includes('text-align') || state.isTable,
305+
hidden: () => !activePlugins?.includes('text-align'),
303306
onClick: () => {},
304307
},
305308
{
306309
icon: <BulletedListIcon />,
307-
hidden: (state) => !activePlugins?.includes('bulleted-list') || state.isTable,
310+
hidden: () => !activePlugins?.includes('bulleted-list'),
308311
onClick: (editor) => editor.chain().focus().toggleBulletList().run(),
309312
isActive: (state) => state.isBulletList,
310313
},
311314
{
312315
icon: <NumberedListIcon />,
313-
hidden: (state) => !activePlugins?.includes('numbered-list') || state.isTable,
316+
hidden: () => !activePlugins?.includes('numbered-list'),
314317
onClick: (editor) => editor.chain().focus().toggleOrderedList().run(),
315318
isActive: (state) => state.isOrderedList,
316319
},
317320
{
318321
icon: <Undo />,
319-
hidden: (state) => !activePlugins?.includes('undo') || state.isTable,
322+
hidden: () => !activePlugins?.includes('undo'),
320323
onClick: (editor) => editor.chain().focus().undo().run(),
321324
isDisabled: (state) => !state.canUndo,
322325
},
323326
{
324327
icon: <Redo />,
325-
hidden: (state) => !activePlugins?.includes('redo') || state.isTable,
328+
hidden: () => !activePlugins?.includes('redo'),
326329
onClick: (editor) => editor.chain().focus().redo().run(),
327330
isDisabled: (state) => !state.canRedo,
328331
},

packages/editable-html-tip-tap/src/components/__tests__/CharacterPicker.test.jsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,4 +194,26 @@ describe('CharacterPicker', () => {
194194
const dialog = container.querySelector('.insert-character-dialog');
195195
expect(dialog).toHaveStyle({ position: 'absolute' });
196196
});
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+
});
197219
});

0 commit comments

Comments
 (0)