Skip to content

Commit ee64332

Browse files
authored
fix(block-tunes): enter keydown problems (#2650)
* debug enter press * fix sync set caret * fix enter keydown problems + tests addedd * Update search-input.ts * add changelog * add useful log to cypress custom comand * Update commands.ts
1 parent e9b4c30 commit ee64332

File tree

11 files changed

+170
-8
lines changed

11 files changed

+170
-8
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ dist/
1717

1818
coverage/
1919
.nyc_output/
20+
.vscode/launch.json

docs/CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
### 2.30.0
44

55
- `Fix``onChange` will be called when removing the entire text within a descendant element of a block.
6+
- `Fix` - Unexpected new line on Enter press with selected block without caret
7+
- `Fix` - Search input autofocus loosing after Block Tunes opening
8+
- `Fix` - Block removing while Enter press on Block Tunes
69

710
### 2.29.1
811

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@editorjs/editorjs",
3-
"version": "2.30.0-rc.0",
3+
"version": "2.30.0-rc.1",
44
"description": "Editor.js — Native JS, based on API and Open Source",
55
"main": "dist/editorjs.umd.js",
66
"module": "dist/editorjs.mjs",

src/components/block/index.ts

+4
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,10 @@ export default class Block extends EventsDispatcher<BlockEvents> {
738738
contentNode = $.make('div', Block.CSS.content),
739739
pluginsContent = this.toolInstance.render();
740740

741+
if (import.meta.env.MODE === 'test') {
742+
wrapper.setAttribute('data-cy', 'block-wrapper');
743+
}
744+
741745
/**
742746
* Export id to the DOM three
743747
* Useful for standalone modules development. For example, allows to identify Block by some child node. Or scroll to a particular Block by id.

src/components/constants.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/**
2+
* Debounce timeout for selection change event
3+
* {@link modules/ui.ts}
4+
*/
5+
export const selectionChangeDebounceTimeout = 180;

src/components/modules/crossBlockSelection.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,11 @@ export default class CrossBlockSelection extends Module {
4848
}
4949

5050
/**
51-
* return boolean is cross block selection started
51+
* Return boolean is cross block selection started:
52+
* there should be at least 2 selected blocks
5253
*/
5354
public get isCrossBlockSelectionStarted(): boolean {
54-
return !!this.firstSelectedBlock &&
55-
!!this.lastSelectedBlock;
55+
return !!this.firstSelectedBlock && !!this.lastSelectedBlock && this.firstSelectedBlock !== this.lastSelectedBlock;
5656
}
5757

5858
/**

src/components/modules/ui.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { mobileScreenBreakpoint } from '../utils';
1515

1616
import styles from '../../styles/main.css?inline';
1717
import { BlockHovered } from '../events/BlockHovered';
18+
import { selectionChangeDebounceTimeout } from '../constants';
1819
/**
1920
* HTML Elements used for UI
2021
*/
@@ -350,7 +351,6 @@ export default class UI extends Module<UINodes> {
350351
/**
351352
* Handle selection change to manipulate Inline Toolbar appearance
352353
*/
353-
const selectionChangeDebounceTimeout = 180;
354354
const selectionChangeDebounced = _.debounce(() => {
355355
this.selectionChanged();
356356
}, selectionChangeDebounceTimeout);
@@ -556,6 +556,11 @@ export default class UI extends Module<UINodes> {
556556
*/
557557
private enterPressed(event: KeyboardEvent): void {
558558
const { BlockManager, BlockSelection } = this.Editor;
559+
560+
if (this.someToolbarOpened) {
561+
return;
562+
}
563+
559564
const hasPointerToBlock = BlockManager.currentBlockIndex >= 0;
560565

561566
/**
@@ -591,6 +596,10 @@ export default class UI extends Module<UINodes> {
591596
*/
592597
const newBlock = this.Editor.BlockManager.insert();
593598

599+
/**
600+
* Prevent default enter behaviour to prevent adding a new line (<div><br></div>) to the inserted block
601+
*/
602+
event.preventDefault();
594603
this.Editor.Caret.setToBlock(newBlock);
595604

596605
/**

src/components/utils/popover/index.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -237,9 +237,7 @@ export default class Popover extends EventsDispatcher<PopoverEventMap> {
237237
this.flipper.activate(this.flippableElements);
238238

239239
if (this.search !== undefined) {
240-
requestAnimationFrame(() => {
241-
this.search?.focus();
242-
});
240+
this.search?.focus();
243241
}
244242

245243
if (isMobileScreen()) {

test/cypress/support/commands.ts

+27
Original file line numberDiff line numberDiff line change
@@ -234,3 +234,30 @@ Cypress.Commands.add('getLineWrapPositions', {
234234

235235
return cy.wrap(lineWraps);
236236
});
237+
238+
/**
239+
* Dispatches keydown event on subject
240+
* Uses the correct KeyboardEvent object to make it work with our code (see below)
241+
*/
242+
Cypress.Commands.add('keydown', {
243+
prevSubject: true,
244+
}, (subject, keyCode: number) => {
245+
cy.log('Dispatching KeyboardEvent with keyCode: ' + keyCode);
246+
/**
247+
* We use the "reason instanceof KeyboardEvent" statement in blockSelection.ts
248+
* but by default cypress' KeyboardEvent is not an instance of the native KeyboardEvent,
249+
* so real-world and Cypress behaviour were different.
250+
*
251+
* To make it work we need to trigger Cypress event with "eventConstructor: 'KeyboardEvent'",
252+
*
253+
* @see https://github.com/cypress-io/cypress/issues/5650
254+
* @see https://github.com/cypress-io/cypress/pull/8305/files
255+
*/
256+
subject.trigger('keydown', {
257+
eventConstructor: 'KeyboardEvent',
258+
keyCode,
259+
bubbles: false,
260+
});
261+
262+
return cy.wrap(subject);
263+
});

test/cypress/support/index.d.ts

+8
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,14 @@ declare global {
8585
* @returns number[] - array of line wrap positions
8686
*/
8787
getLineWrapPositions(): Chainable<number[]>;
88+
89+
/**
90+
* Dispatches keydown event on subject
91+
* Uses the correct KeyboardEvent object to make it work with our code (see below)
92+
*
93+
* @param keyCode - key code to dispatch
94+
*/
95+
keydown(keyCode: number): Chainable<Subject>;
8896
}
8997

9098
interface ApplicationWindow {
+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { selectionChangeDebounceTimeout } from '../../../../src/components/constants';
2+
3+
describe('BlockTunes', function () {
4+
describe('Search', () => {
5+
it('should be focused after popover opened', () => {
6+
cy.createEditor({
7+
data: {
8+
blocks: [
9+
{
10+
type: 'paragraph',
11+
data: {
12+
text: 'Some text',
13+
},
14+
},
15+
],
16+
},
17+
});
18+
19+
cy.get('[data-cy=editorjs]')
20+
.find('.ce-paragraph')
21+
.click()
22+
.type('{cmd}/')
23+
.wait(selectionChangeDebounceTimeout);
24+
25+
/**
26+
* Caret is set to the search input
27+
*/
28+
cy.window()
29+
.then((window) => {
30+
const selection = window.getSelection();
31+
32+
expect(selection.rangeCount).to.be.equal(1);
33+
34+
const range = selection.getRangeAt(0);
35+
36+
cy.get('[data-cy=editorjs]')
37+
.find('[data-cy="block-tunes"] .cdx-search-field')
38+
.should(($block) => {
39+
expect($block[0].contains(range.startContainer)).to.be.true;
40+
});
41+
});
42+
});
43+
});
44+
45+
describe('Keyboard only', function () {
46+
it('should not delete the currently selected block when Enter pressed on a search input (or any block tune)', function () {
47+
const ENTER_KEY_CODE = 13;
48+
49+
cy.createEditor({
50+
data: {
51+
blocks: [
52+
{
53+
type: 'paragraph',
54+
data: {
55+
text: 'Some text',
56+
},
57+
},
58+
],
59+
},
60+
});
61+
62+
cy.get('[data-cy=editorjs]')
63+
.find('.ce-paragraph')
64+
.click()
65+
.type('{cmd}/')
66+
.wait(selectionChangeDebounceTimeout)
67+
.keydown(ENTER_KEY_CODE);
68+
69+
/**
70+
* Block should have same text
71+
*/
72+
cy.get('[data-cy="block-wrapper"')
73+
.should('have.text', 'Some text');
74+
});
75+
76+
it('should not unselect currently selected block when Enter pressed on a block tune', function () {
77+
const ENTER_KEY_CODE = 13;
78+
79+
cy.createEditor({
80+
data: {
81+
blocks: [
82+
{
83+
type: 'paragraph',
84+
data: {
85+
text: 'Some text',
86+
},
87+
},
88+
],
89+
},
90+
});
91+
92+
cy.get('[data-cy=editorjs]')
93+
.find('.ce-paragraph')
94+
.click()
95+
.type('{cmd}/')
96+
.wait(selectionChangeDebounceTimeout)
97+
.keydown(ENTER_KEY_CODE);
98+
99+
/**
100+
* Block should not be selected
101+
*/
102+
cy.get('[data-cy="block-wrapper"')
103+
.first()
104+
.should('have.class', 'ce-block--selected');
105+
});
106+
});
107+
});

0 commit comments

Comments
 (0)