Skip to content

Commit b6ba44d

Browse files
gorenburgneSpecc
andauthored
fix: prevent inline toolbar from closing in nested instance of editor (codex-team#2780)
* fix: prevent inline toolbar from closing in nested instance of editor * docs: updated changelog.md with fix description * fix: fix import to use `type` --------- Co-authored-by: Peter <[email protected]>
1 parent 2f9696a commit b6ba44d

File tree

4 files changed

+89
-2
lines changed

4 files changed

+89
-2
lines changed

docs/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
`Fix` – Fix the display of ‘Convert To’ near blocks that do not have the ‘conversionConfig.export’ rule specified
66
`Fix` – The Plus button does not appear when the editor is loaded in an iframe in Chrome
7+
- `Fix` - Prevent inline toolbar from closing in nested instance of editor
78

89
### 2.30.5
910

src/components/modules/ui.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -847,9 +847,11 @@ export default class UI extends Module<UINodes> {
847847

848848
/**
849849
* Event can be fired on clicks at non-block-content elements,
850-
* for example, at the Inline Toolbar or some Block Tune element
850+
* for example, at the Inline Toolbar or some Block Tune element.
851+
* We also make sure that the closest block belongs to the current editor and not a parent
851852
*/
852-
const clickedOutsideBlockContent = focusedElement.closest(`.${Block.CSS.content}`) === null;
853+
const closestBlock = focusedElement.closest(`.${Block.CSS.content}`);
854+
const clickedOutsideBlockContent = closestBlock === null || (closestBlock.closest(`.${Selection.CSS.editorWrapper}`) !== this.nodes.wrapper);
853855

854856
if (clickedOutsideBlockContent) {
855857
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type { BlockTool, BlockToolConstructorOptions } from '../../../../types';
2+
import { createEditorWithTextBlocks } from './createEditorWithTextBlocks';
3+
4+
export const NESTED_EDITOR_ID = 'nested-editor';
5+
6+
/**
7+
* Creates nested Editor instance with paragraph block
8+
*/
9+
export default class NestedEditor implements BlockTool {
10+
private data: { text: string };
11+
12+
constructor(value: BlockToolConstructorOptions) {
13+
this.data = value.data;
14+
}
15+
16+
public render(): HTMLDivElement {
17+
const editorEl = Object.assign(document.createElement('div'), {
18+
id: NESTED_EDITOR_ID,
19+
});
20+
21+
editorEl.setAttribute('data-cy', NESTED_EDITOR_ID);
22+
23+
createEditorWithTextBlocks([ this.data.text ], { holder: NESTED_EDITOR_ID });
24+
25+
return editorEl;
26+
}
27+
28+
public save(): string {
29+
return this.data.text;
30+
}
31+
}

test/cypress/tests/modules/InlineToolbar.cy.ts

+53
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Header from '@editorjs/header';
2+
import NestedEditor, { NESTED_EDITOR_ID } from '../../support/utils/nestedEditorInstance';
23

34
describe('Inline Toolbar', () => {
45
it('should appear aligned with left coord of selection rect', () => {
@@ -164,4 +165,56 @@ describe('Inline Toolbar', () => {
164165
});
165166
});
166167
});
168+
169+
describe('Nested Editor instance inline toolbar', () => {
170+
it('should not close inline toolbar of the nested Editor instance when clicking within that toolbar', () => {
171+
cy.createEditor({
172+
tools: {
173+
nestedEditor: {
174+
class: NestedEditor,
175+
},
176+
},
177+
data: {
178+
blocks: [
179+
{
180+
type: 'paragraph',
181+
data: {
182+
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
183+
},
184+
},
185+
{
186+
type: 'nestedEditor',
187+
data: {
188+
text: 'Nunc pellentesque, tortor nec luctus venenatis',
189+
},
190+
},
191+
],
192+
},
193+
});
194+
195+
cy.get(`[data-cy=${NESTED_EDITOR_ID}]`)
196+
.find('.ce-paragraph')
197+
.selectText('tortor nec luctus');
198+
199+
cy.get(`[data-cy=${NESTED_EDITOR_ID}]`)
200+
.find('[data-item-name=link]')
201+
.click();
202+
203+
// `wait()` function below is required. without it the test will always pass
204+
// because cypress types the text in the field without delay, while we need some delay (just like user)
205+
// to test the actual case that nested editor inline toolbar is still visible and not closed
206+
207+
cy.get(`[data-cy=${NESTED_EDITOR_ID}]`)
208+
.find('.ce-inline-tool-input')
209+
.click()
210+
.wait(100)
211+
.type('https://editorjs.io');
212+
213+
cy.get(`[data-cy=${NESTED_EDITOR_ID}]`)
214+
.find('.ce-popover__container')
215+
.then(($toolbar) => {
216+
expect($toolbar).to.be.visible;
217+
});
218+
});
219+
});
167220
});

0 commit comments

Comments
 (0)