From 08da64b407e95683946b34cf6ebca5d52ac28227 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ella=20van=C2=A0Durpe?= Date: Wed, 13 Mar 2019 13:31:27 +0100 Subject: [PATCH] RichText: try alternative list shortcuts (to tab) (#14343) * RichText: try alternative list shortcuts * Try tooltips * Change tooltips to use text * Add inline comments * Add e2e test * Rebase --- .../src/components/rich-text/index.js | 79 +++++++++++++++---- .../src/components/rich-text/list-edit.js | 4 +- .../blocks/__snapshots__/list.test.js.snap | 38 +++++++++ packages/e2e-tests/specs/blocks/list.test.js | 42 ++++++++++ 4 files changed, 145 insertions(+), 18 deletions(-) diff --git a/packages/block-editor/src/components/rich-text/index.js b/packages/block-editor/src/components/rich-text/index.js index 1d714ba9844d3..92afb3799eb90 100644 --- a/packages/block-editor/src/components/rich-text/index.js +++ b/packages/block-editor/src/components/rich-text/index.js @@ -19,7 +19,7 @@ import memize from 'memize'; import { Component, Fragment, RawHTML } from '@wordpress/element'; import { isHorizontalEdge } from '@wordpress/dom'; import { createBlobURL } from '@wordpress/blob'; -import { BACKSPACE, DELETE, ENTER, LEFT, RIGHT } from '@wordpress/keycodes'; +import { BACKSPACE, DELETE, ENTER, LEFT, RIGHT, SPACE } from '@wordpress/keycodes'; import { withDispatch, withSelect } from '@wordpress/data'; import { pasteHandler, children, getBlockTransforms, findTransform } from '@wordpress/blocks'; import { withInstanceId, withSafeTimeout, compose } from '@wordpress/compose'; @@ -37,13 +37,11 @@ import { insertLineSeparator, isEmptyLine, unstableToDom, - getSelectionStart, - getSelectionEnd, remove, removeFormat, isCollapsed, LINE_SEPARATOR, - charAt, + indentListItems, } from '@wordpress/rich-text'; import { decodeEntities } from '@wordpress/html-entities'; import { withFilters, IsolatedEventContainer } from '@wordpress/components'; @@ -599,10 +597,26 @@ export class RichText extends Component { this.handleHorizontalNavigation( event ); } + // Use the space key in list items (at the start of an item) to indent + // the list item. + if ( keyCode === SPACE && this.multilineTag === 'li' ) { + const value = this.createRecord(); + + if ( isCollapsed( value ) ) { + const { text, start } = value; + const characterBefore = text[ start - 1 ]; + + // The caret must be at the start of a line. + if ( ! characterBefore || characterBefore === LINE_SEPARATOR ) { + this.onChange( indentListItems( value, { type: this.props.tagName } ) ); + event.preventDefault(); + } + } + } + if ( keyCode === DELETE || keyCode === BACKSPACE ) { const value = this.createRecord(); - const start = getSelectionStart( value ); - const end = getSelectionEnd( value ); + const { replacements, text, start, end } = value; // Always handle full content deletion ourselves. if ( start === 0 && end !== 0 && end === value.text.length ) { @@ -615,22 +629,53 @@ export class RichText extends Component { let newValue; if ( keyCode === BACKSPACE ) { - if ( charAt( value, start - 1 ) === LINE_SEPARATOR ) { + const index = start - 1; + + if ( text[ index ] === LINE_SEPARATOR ) { + const collapsed = isCollapsed( value ); + + // If the line separator that is about te be removed + // contains wrappers, remove the wrappers first. + if ( collapsed && replacements[ index ] && replacements[ index ].length ) { + const newReplacements = replacements.slice(); + + newReplacements[ index ] = replacements[ index ].slice( 0, -1 ); + newValue = { + ...value, + replacements: newReplacements, + }; + } else { + newValue = remove( + value, + // Only remove the line if the selection is + // collapsed, otherwise remove the selection. + collapsed ? start - 1 : start, + end + ); + } + } + } else if ( text[ end ] === LINE_SEPARATOR ) { + const collapsed = isCollapsed( value ); + + // If the line separator that is about te be removed + // contains wrappers, remove the wrappers first. + if ( collapsed && replacements[ end ] && replacements[ end ].length ) { + const newReplacements = replacements.slice(); + + newReplacements[ end ] = replacements[ end ].slice( 0, -1 ); + newValue = { + ...value, + replacements: newReplacements, + }; + } else { newValue = remove( value, + start, // Only remove the line if the selection is - // collapsed. - isCollapsed( value ) ? start - 1 : start, - end + // collapsed, otherwise remove the selection. + collapsed ? end + 1 : end, ); } - } else if ( charAt( value, end ) === LINE_SEPARATOR ) { - newValue = remove( - value, - start, - // Only remove the line if the selection is collapsed. - isCollapsed( value ) ? end + 1 : end, - ); } if ( newValue ) { diff --git a/packages/block-editor/src/components/rich-text/list-edit.js b/packages/block-editor/src/components/rich-text/list-edit.js index 81de868f62719..aee85983258df 100644 --- a/packages/block-editor/src/components/rich-text/list-edit.js +++ b/packages/block-editor/src/components/rich-text/list-edit.js @@ -3,7 +3,7 @@ */ import { Toolbar } from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; +import { __, _x } from '@wordpress/i18n'; import { Fragment } from '@wordpress/element'; import { indentListItems, @@ -149,6 +149,7 @@ export const ListEdit = ( { { icon: 'editor-outdent', title: __( 'Outdent list item' ), + shortcut: _x( 'Backspace', 'keyboard key' ), onClick: () => { onChange( outdentListItems( value ) ); }, @@ -156,6 +157,7 @@ export const ListEdit = ( { { icon: 'editor-indent', title: __( 'Indent list item' ), + shortcut: _x( 'Space', 'keyboard key' ), onClick: () => { onChange( indentListItems( value, { type: tagName } ) ); }, diff --git a/packages/e2e-tests/specs/blocks/__snapshots__/list.test.js.snap b/packages/e2e-tests/specs/blocks/__snapshots__/list.test.js.snap index b2e8ec4875aac..4d473f282a17a 100644 --- a/packages/e2e-tests/specs/blocks/__snapshots__/list.test.js.snap +++ b/packages/e2e-tests/specs/blocks/__snapshots__/list.test.js.snap @@ -98,6 +98,44 @@ exports[`List should change the indented list type 1`] = ` " `; +exports[`List should create and remove indented list with keyboard only 1`] = ` +" + +" +`; + +exports[`List should create and remove indented list with keyboard only 2`] = ` +" + +" +`; + +exports[`List should create and remove indented list with keyboard only 3`] = ` +" + +" +`; + +exports[`List should create and remove indented list with keyboard only 4`] = ` +" + +" +`; + +exports[`List should create and remove indented list with keyboard only 5`] = ` +" + +" +`; + +exports[`List should create and remove indented list with keyboard only 6`] = ` +" + +" +`; + +exports[`List should create and remove indented list with keyboard only 7`] = `""`; + exports[`List should create paragraph on split at end and merge back with content 1`] = ` " diff --git a/packages/e2e-tests/specs/blocks/list.test.js b/packages/e2e-tests/specs/blocks/list.test.js index 23d02e0f10cb7..2bbc038627885 100644 --- a/packages/e2e-tests/specs/blocks/list.test.js +++ b/packages/e2e-tests/specs/blocks/list.test.js @@ -304,4 +304,46 @@ describe( 'List', () => { expect( await getEditedPostContent() ).toMatchSnapshot(); } ); + + it( 'should create and remove indented list with keyboard only', async () => { + await clickBlockAppender(); + + await page.keyboard.type( '* 1' ); // Should be at level 0. + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( ' a' ); // Should be at level 1. + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( ' i' ); // Should be at level 2. + + expect( await getEditedPostContent() ).toMatchSnapshot(); + + await page.keyboard.press( 'Backspace' ); + await page.keyboard.press( 'Backspace' ); // Should be at level 1. + + expect( await getEditedPostContent() ).toMatchSnapshot(); + + await page.keyboard.press( 'Backspace' ); // Should be at level 0. + + expect( await getEditedPostContent() ).toMatchSnapshot(); + + await page.keyboard.press( 'Backspace' ); // Should be at level 1. + + expect( await getEditedPostContent() ).toMatchSnapshot(); + + await page.keyboard.press( 'Backspace' ); + await page.keyboard.press( 'Backspace' ); // Should be at level 0. + + expect( await getEditedPostContent() ).toMatchSnapshot(); + + await page.keyboard.press( 'Backspace' ); // Should be at level 0. + + expect( await getEditedPostContent() ).toMatchSnapshot(); + + await page.keyboard.press( 'Backspace' ); + await page.keyboard.press( 'Backspace' ); // Should remove list. + + expect( await getEditedPostContent() ).toMatchSnapshot(); + + // That's 9 key presses to create the list, and 9 key presses to remove + // the list. ;) + } ); } );