Skip to content

Commit

Permalink
fix(block-tunes): enter keydown problems (#2650)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
neSpecc authored Mar 13, 2024
1 parent e9b4c30 commit ee64332
Show file tree
Hide file tree
Showing 11 changed files with 170 additions and 8 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ dist/

coverage/
.nyc_output/
.vscode/launch.json
3 changes: 3 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
### 2.30.0

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

### 2.29.1

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@editorjs/editorjs",
"version": "2.30.0-rc.0",
"version": "2.30.0-rc.1",
"description": "Editor.js — Native JS, based on API and Open Source",
"main": "dist/editorjs.umd.js",
"module": "dist/editorjs.mjs",
Expand Down
4 changes: 4 additions & 0 deletions src/components/block/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,10 @@ export default class Block extends EventsDispatcher<BlockEvents> {
contentNode = $.make('div', Block.CSS.content),
pluginsContent = this.toolInstance.render();

if (import.meta.env.MODE === 'test') {
wrapper.setAttribute('data-cy', 'block-wrapper');
}

/**
* Export id to the DOM three
* Useful for standalone modules development. For example, allows to identify Block by some child node. Or scroll to a particular Block by id.
Expand Down
5 changes: 5 additions & 0 deletions src/components/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* Debounce timeout for selection change event
* {@link modules/ui.ts}
*/
export const selectionChangeDebounceTimeout = 180;
6 changes: 3 additions & 3 deletions src/components/modules/crossBlockSelection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ export default class CrossBlockSelection extends Module {
}

/**
* return boolean is cross block selection started
* Return boolean is cross block selection started:
* there should be at least 2 selected blocks
*/
public get isCrossBlockSelectionStarted(): boolean {
return !!this.firstSelectedBlock &&
!!this.lastSelectedBlock;
return !!this.firstSelectedBlock && !!this.lastSelectedBlock && this.firstSelectedBlock !== this.lastSelectedBlock;
}

/**
Expand Down
11 changes: 10 additions & 1 deletion src/components/modules/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { mobileScreenBreakpoint } from '../utils';

import styles from '../../styles/main.css?inline';
import { BlockHovered } from '../events/BlockHovered';
import { selectionChangeDebounceTimeout } from '../constants';
/**
* HTML Elements used for UI
*/
Expand Down Expand Up @@ -350,7 +351,6 @@ export default class UI extends Module<UINodes> {
/**
* Handle selection change to manipulate Inline Toolbar appearance
*/
const selectionChangeDebounceTimeout = 180;
const selectionChangeDebounced = _.debounce(() => {
this.selectionChanged();
}, selectionChangeDebounceTimeout);
Expand Down Expand Up @@ -556,6 +556,11 @@ export default class UI extends Module<UINodes> {
*/
private enterPressed(event: KeyboardEvent): void {
const { BlockManager, BlockSelection } = this.Editor;

if (this.someToolbarOpened) {
return;
}

const hasPointerToBlock = BlockManager.currentBlockIndex >= 0;

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

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

/**
Expand Down
4 changes: 1 addition & 3 deletions src/components/utils/popover/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,9 +237,7 @@ export default class Popover extends EventsDispatcher<PopoverEventMap> {
this.flipper.activate(this.flippableElements);

if (this.search !== undefined) {
requestAnimationFrame(() => {
this.search?.focus();
});
this.search?.focus();
}

if (isMobileScreen()) {
Expand Down
27 changes: 27 additions & 0 deletions test/cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,3 +234,30 @@ Cypress.Commands.add('getLineWrapPositions', {

return cy.wrap(lineWraps);
});

/**
* Dispatches keydown event on subject
* Uses the correct KeyboardEvent object to make it work with our code (see below)
*/
Cypress.Commands.add('keydown', {
prevSubject: true,
}, (subject, keyCode: number) => {
cy.log('Dispatching KeyboardEvent with keyCode: ' + keyCode);
/**
* We use the "reason instanceof KeyboardEvent" statement in blockSelection.ts
* but by default cypress' KeyboardEvent is not an instance of the native KeyboardEvent,
* so real-world and Cypress behaviour were different.
*
* To make it work we need to trigger Cypress event with "eventConstructor: 'KeyboardEvent'",
*
* @see https://github.com/cypress-io/cypress/issues/5650
* @see https://github.com/cypress-io/cypress/pull/8305/files
*/
subject.trigger('keydown', {
eventConstructor: 'KeyboardEvent',
keyCode,
bubbles: false,
});

return cy.wrap(subject);
});
8 changes: 8 additions & 0 deletions test/cypress/support/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ declare global {
* @returns number[] - array of line wrap positions
*/
getLineWrapPositions(): Chainable<number[]>;

/**
* Dispatches keydown event on subject
* Uses the correct KeyboardEvent object to make it work with our code (see below)
*
* @param keyCode - key code to dispatch
*/
keydown(keyCode: number): Chainable<Subject>;
}

interface ApplicationWindow {
Expand Down
107 changes: 107 additions & 0 deletions test/cypress/tests/ui/BlockTunes.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { selectionChangeDebounceTimeout } from '../../../../src/components/constants';

describe('BlockTunes', function () {
describe('Search', () => {
it('should be focused after popover opened', () => {
cy.createEditor({
data: {
blocks: [
{
type: 'paragraph',
data: {
text: 'Some text',
},
},
],
},
});

cy.get('[data-cy=editorjs]')
.find('.ce-paragraph')
.click()
.type('{cmd}/')
.wait(selectionChangeDebounceTimeout);

/**
* Caret is set to the search input
*/
cy.window()
.then((window) => {
const selection = window.getSelection();

expect(selection.rangeCount).to.be.equal(1);

const range = selection.getRangeAt(0);

cy.get('[data-cy=editorjs]')
.find('[data-cy="block-tunes"] .cdx-search-field')
.should(($block) => {
expect($block[0].contains(range.startContainer)).to.be.true;
});
});
});
});

describe('Keyboard only', function () {
it('should not delete the currently selected block when Enter pressed on a search input (or any block tune)', function () {
const ENTER_KEY_CODE = 13;

cy.createEditor({
data: {
blocks: [
{
type: 'paragraph',
data: {
text: 'Some text',
},
},
],
},
});

cy.get('[data-cy=editorjs]')
.find('.ce-paragraph')
.click()
.type('{cmd}/')
.wait(selectionChangeDebounceTimeout)
.keydown(ENTER_KEY_CODE);

/**
* Block should have same text
*/
cy.get('[data-cy="block-wrapper"')
.should('have.text', 'Some text');
});

it('should not unselect currently selected block when Enter pressed on a block tune', function () {
const ENTER_KEY_CODE = 13;

cy.createEditor({
data: {
blocks: [
{
type: 'paragraph',
data: {
text: 'Some text',
},
},
],
},
});

cy.get('[data-cy=editorjs]')
.find('.ce-paragraph')
.click()
.type('{cmd}/')
.wait(selectionChangeDebounceTimeout)
.keydown(ENTER_KEY_CODE);

/**
* Block should not be selected
*/
cy.get('[data-cy="block-wrapper"')
.first()
.should('have.class', 'ce-block--selected');
});
});
});

0 comments on commit ee64332

Please sign in to comment.