Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions packages/editable-html-tip-tap/src/__tests__/EditableHtml.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -266,4 +266,39 @@ describe('EditableHtml', () => {
const { container } = render(<EditableHtml {...defaultProps} disableImageAlignmentButtons={true} />);
expect(container).toBeInTheDocument();
});

it('calls editorRef callback when editor is initialized', async () => {
const editorRef = jest.fn();
render(<EditableHtml {...defaultProps} editorRef={editorRef} />);

await waitFor(() => {
expect(editorRef).toHaveBeenCalled();
});
});

it('calls editorRef with the editor instance', async () => {
const editorRef = jest.fn();
render(<EditableHtml {...defaultProps} editorRef={editorRef} />);

await waitFor(() => {
expect(editorRef).toHaveBeenCalled();
// Verify it was called with an object that has editor-like properties
const callArg = editorRef.mock.calls[0][0];
expect(callArg).toHaveProperty('getHTML');
expect(callArg).toHaveProperty('commands');
});
});

it('handles editorRef being undefined', () => {
const { container } = render(<EditableHtml {...defaultProps} editorRef={undefined} />);
expect(container).toBeInTheDocument();
});

it('applies flex display to StyledEditorContent', async () => {
const { getByTestId } = render(<EditableHtml {...defaultProps} />);
await waitFor(() => {
const editorContent = getByTestId('editor-content');
expect(editorContent).toBeInTheDocument();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ export function CharacterPicker({ editor, opts, onClose }) {
<div
ref={containerRef}
className="insert-character-dialog"
data-toolbar-for={editor.instanceId}
style={{
visibility: position.top === 0 && position.left === 0 ? 'hidden' : 'initial',
position: 'absolute',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,12 @@ export const EditableHtml = (props) => {
[props.charactersLimit],
);

useEffect(() => {
if (props.editorRef) {
props.editorRef(editor);
}
}, [props.editorRef, editor]);

useEffect(() => {
editor?.setEditable(!props.disabled);
}, [props.disabled, editor]);
Expand Down Expand Up @@ -349,8 +355,10 @@ export const EditableHtml = (props) => {
const StyledEditorContent = styled(EditorContent, {
shouldForwardProp: (prop) => !['showParagraph', 'separateParagraph'].includes(prop),
})(({ showParagraph, separateParagraph }) => ({
display: 'flex',
outline: 'none !important',
'& .ProseMirror': {
flex: 1,
padding: '5px',
maxHeight: '500px',
outline: 'none !important',
Expand Down
47 changes: 25 additions & 22 deletions packages/editable-html-tip-tap/src/components/MenuBar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,12 @@ function MenuBar({
ctx.editor?.isActive('imageUploadNode') ||
ctx.editor?.isActive('drag_in_the_blank');

const hasTextSelectionInTable = selection && selection.empty === false && ctx.editor.isActive('table');

return {
currentNode,
hideDefaultToolbar,
hasTextSelectionInTable,
isFocused: ctx.editor?.isFocused,
isBold: ctx.editor.isActive('bold') ?? false,
canBold: ctx.editor.can().chain().toggleBold().run() ?? false,
Expand Down Expand Up @@ -162,35 +165,35 @@ function MenuBar({
{
icon: <AddRow />,
onClick: (editor) => editor.chain().focus().addRowAfter().run(),
hidden: (state) => !state.isTable,
hidden: (state) => !(state.isTable && !state.hasTextSelectionInTable),
isActive: (state) => state.isTable,
isDisabled: (state) => !state.canTable,
},
{
icon: <RemoveRow />,
onClick: (editor) => editor.chain().focus().deleteRow().run(),
hidden: (state) => !state.isTable,
hidden: (state) => !(state.isTable && !state.hasTextSelectionInTable),
isActive: (state) => state.isTable,
isDisabled: (state) => !state.canTable,
},
{
icon: <AddColumn />,
onClick: (editor) => editor.chain().focus().addColumnAfter().run(),
hidden: (state) => !state.isTable,
hidden: (state) => !(state.isTable && !state.hasTextSelectionInTable),
isActive: (state) => state.isTable,
isDisabled: (state) => !state.canTable,
},
{
icon: <RemoveColumn />,
onClick: (editor) => editor.chain().focus().deleteColumn().run(),
hidden: (state) => !state.isTable,
hidden: (state) => !(state.isTable && !state.hasTextSelectionInTable),
isActive: (state) => state.isTable,
isDisabled: (state) => !state.canTable,
},
{
icon: <RemoveTable />,
onClick: (editor) => editor.chain().focus().deleteTable().run(),
hidden: (state) => !state.isTable,
hidden: (state) => !(state.isTable && !state.hasTextSelectionInTable),
isActive: (state) => state.isTable,
isDisabled: (state) => !state.canTable,
},
Expand All @@ -206,54 +209,54 @@ function MenuBar({

editor.commands.updateAttributes('table', update);
},
hidden: (state) => !state.isTable,
hidden: (state) => !(state.isTable && !state.hasTextSelectionInTable),
isActive: (state) => state.tableHasBorder,
isDisabled: (state) => !state.canTable,
},
{
icon: <Bold />,
onClick: (editor) => editor.chain().focus().toggleBold().run(),
hidden: (state) => !activePlugins?.includes('bold') || state.isTable,
hidden: () => !activePlugins?.includes('bold'),
isActive: (state) => state.isBold,
isDisabled: (state) => !state.canBold,
},
{
icon: <Italic />,
onClick: (editor) => editor.chain().focus().toggleItalic().run(),
hidden: (state) => !activePlugins?.includes('italic') || state.isTable,
hidden: () => !activePlugins?.includes('italic'),
isActive: (state) => state.isItalic,
isDisabled: (state) => !state.canItalic,
},
{
icon: <Strikethrough />,
onClick: (editor) => editor.chain().focus().toggleStrike().run(),
hidden: (state) => !activePlugins?.includes('strikethrough') || state.isTable,
hidden: () => !activePlugins?.includes('strikethrough'),
isActive: (state) => state.isStrike,
isDisabled: (state) => !state.canStrike,
},
{
icon: <Code />,
onClick: (editor) => editor.chain().focus().toggleCode().run(),
hidden: (state) => !activePlugins?.includes('code') || state.isTable,
hidden: () => !activePlugins?.includes('code'),
isActive: (state) => state.isCode,
isDisabled: (state) => !state.canCode,
},
{
icon: <Underline />,
onClick: (editor) => editor.chain().focus().toggleUnderline().run(),
hidden: (state) => !activePlugins?.includes('underline') || state.isTable,
hidden: () => !activePlugins?.includes('underline'),
isActive: (state) => state.isUnderline,
},
{
icon: <SubscriptIcon />,
onClick: (editor) => editor.chain().focus().toggleSubscript().run(),
hidden: (state) => !activePlugins?.includes('subscript') || state.isTable,
hidden: () => !activePlugins?.includes('subscript'),
isActive: (state) => state.isSubScript,
},
{
icon: <SuperscriptIcon />,
onClick: (editor) => editor.chain().focus().toggleSuperscript().run(),
hidden: (state) => !activePlugins?.includes('superscript') || state.isTable,
hidden: () => !activePlugins?.includes('superscript'),
isActive: (state) => state.isSuperScript,
},
{
Expand All @@ -263,22 +266,22 @@ function MenuBar({
},
{
icon: <TheatersIcon />,
hidden: (state) => !activePlugins?.includes('video') || state.isTable,
hidden: () => !activePlugins?.includes('video'),
onClick: (editor) => editor.chain().focus().insertMedia({ type: 'video' }).run(),
},
{
icon: <VolumeUpIcon />,
hidden: (state) => !activePlugins?.includes('audio') || state.isTable,
hidden: () => !activePlugins?.includes('audio'),
onClick: (editor) => editor.chain().focus().insertMedia({ type: 'audio', tag: 'audio' }).run(),
},
{
icon: <CSSIcon />,
hidden: (state) => !activePlugins?.includes('css') || state.isTable,
hidden: () => !activePlugins?.includes('css'),
onClick: (editor) => editor.commands.openCSSClassDialog(),
},
{
icon: <HeadingIcon />,
hidden: (state) => !activePlugins?.includes('h3') || state.isTable,
hidden: () => !activePlugins?.includes('h3'),
onClick: (editor) => editor.chain().focus().toggleHeading({ level: 3 }).run(),
isActive: (state) => state.isHeading3,
},
Expand All @@ -299,30 +302,30 @@ function MenuBar({
},
{
icon: <TextAlignIcon editor={editor} />,
hidden: (state) => !activePlugins?.includes('text-align') || state.isTable,
hidden: () => !activePlugins?.includes('text-align'),
onClick: () => {},
},
{
icon: <BulletedListIcon />,
hidden: (state) => !activePlugins?.includes('bulleted-list') || state.isTable,
hidden: () => !activePlugins?.includes('bulleted-list'),
onClick: (editor) => editor.chain().focus().toggleBulletList().run(),
isActive: (state) => state.isBulletList,
},
{
icon: <NumberedListIcon />,
hidden: (state) => !activePlugins?.includes('numbered-list') || state.isTable,
hidden: () => !activePlugins?.includes('numbered-list'),
onClick: (editor) => editor.chain().focus().toggleOrderedList().run(),
isActive: (state) => state.isOrderedList,
},
{
icon: <Undo />,
hidden: (state) => !activePlugins?.includes('undo') || state.isTable,
hidden: () => !activePlugins?.includes('undo'),
onClick: (editor) => editor.chain().focus().undo().run(),
isDisabled: (state) => !state.canUndo,
},
{
icon: <Redo />,
hidden: (state) => !activePlugins?.includes('redo') || state.isTable,
hidden: () => !activePlugins?.includes('redo'),
onClick: (editor) => editor.chain().focus().redo().run(),
isDisabled: (state) => !state.canRedo,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,4 +194,26 @@ describe('CharacterPicker', () => {
const dialog = container.querySelector('.insert-character-dialog');
expect(dialog).toHaveStyle({ position: 'absolute' });
});

it('adds data-toolbar-for attribute with editor instanceId', () => {
const editorWithInstanceId = {
...mockEditor,
instanceId: 'editor-123',
};
const opts = {
characters: [['á', 'é']],
};
const { container } = render(<CharacterPicker editor={editorWithInstanceId} opts={opts} onClose={jest.fn()} />);
const dialog = container.querySelector('.insert-character-dialog');
expect(dialog).toHaveAttribute('data-toolbar-for', 'editor-123');
});

it('renders without instanceId gracefully', () => {
const opts = {
characters: [['á', 'é']],
};
const { container } = render(<CharacterPicker editor={mockEditor} opts={opts} onClose={jest.fn()} />);
const dialog = container.querySelector('.insert-character-dialog');
expect(dialog).toBeInTheDocument();
});
});
Loading
Loading