From 4126e77c56c18dcbb7573f523aa26cbba01564d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ella=20van=C2=A0Durpe?= Date: Tue, 17 Aug 2021 14:16:14 +0300 Subject: [PATCH 1/5] Use rich text hook for post title --- .../src/components/rich-text/style.scss | 12 ++-- .../editor/src/components/post-title/index.js | 51 +++++++++----- .../src/components/post-title/style.scss | 69 ------------------- packages/rich-text/src/component/index.js | 15 ++-- 4 files changed, 49 insertions(+), 98 deletions(-) diff --git a/packages/block-editor/src/components/rich-text/style.scss b/packages/block-editor/src/components/rich-text/style.scss index ca4cc807a93637..e4ac6b24f9a16b 100644 --- a/packages/block-editor/src/components/rich-text/style.scss +++ b/packages/block-editor/src/components/rich-text/style.scss @@ -1,8 +1,4 @@ -.block-editor-rich-text__editable { - > p:first-child { - margin-top: 0; - } - +.rich-text { [data-rich-text-placeholder] { pointer-events: none; } @@ -24,6 +20,12 @@ } } +.block-editor-rich-text__editable { + > p:first-child { + margin-top: 0; + } +} + // Captions may have lighter (gray) text, or be shown on a range of different background luminosites. // To ensure legibility, we increase the default placeholder opacity to ensure contrast. figcaption.block-editor-rich-text__editable [data-rich-text-placeholder]::before { diff --git a/packages/editor/src/components/post-title/index.js b/packages/editor/src/components/post-title/index.js index 374af843f8a3f4..7a5c242c3fdcbf 100644 --- a/packages/editor/src/components/post-title/index.js +++ b/packages/editor/src/components/post-title/index.js @@ -1,7 +1,6 @@ /** * External dependencies */ -import TextareaAutosize from 'react-autosize-textarea'; import classnames from 'classnames'; /** @@ -12,10 +11,10 @@ import { useEffect, useRef, useState } from '@wordpress/element'; import { decodeEntities } from '@wordpress/html-entities'; import { ENTER } from '@wordpress/keycodes'; import { useSelect, useDispatch } from '@wordpress/data'; -import { VisuallyHidden } from '@wordpress/components'; -import { useInstanceId } from '@wordpress/compose'; import { pasteHandler } from '@wordpress/blocks'; import { store as blockEditorStore } from '@wordpress/block-editor'; +import { __unstableUseRichText as useRichText } from '@wordpress/rich-text'; +import { useMergeRefs } from '@wordpress/compose'; /** * Internal dependencies @@ -29,7 +28,6 @@ import { store as editorStore } from '../../store'; const REGEXP_NEWLINES = /[\r\n]+/g; export default function PostTitle() { - const instanceId = useInstanceId( PostTitle ); const ref = useRef(); const [ isSelected, setIsSelected ] = useState( false ); const { editPost } = useDispatch( editorStore ); @@ -103,8 +101,8 @@ export default function PostTitle() { setIsSelected( false ); } - function onChange( event ) { - onUpdate( event.target.value.replace( REGEXP_NEWLINES, ' ' ) ); + function onChange( value ) { + onUpdate( value.replace( REGEXP_NEWLINES, ' ' ) ); } function onKeyDown( event ) { @@ -175,23 +173,37 @@ export default function PostTitle() { } ); const decodedPlaceholder = decodeEntities( placeholder ); + const [ selection, setSelection ] = useState( {} ); + const { ref: richTextRef } = useRichText( { + value: title, + onChange, + placeholder: decodedPlaceholder || __( 'Add title' ), + selectionStart: selection.start, + selectionEnd: selection.end, + onSelectionChange( newStart, newEnd ) { + setSelection( ( sel ) => { + const { start, end } = sel; + if ( start === newStart && end === newEnd ) { + return sel; + } + return { + start: newStart, + end: newEnd, + }; + } ); + }, + __unstableDisableFormats: true, + preserveWhiteSpace: true, + } ); + /* eslint-disable jsx-a11y/heading-has-content, jsx-a11y/no-noninteractive-element-interactions */ return (
- - { decodedPlaceholder || __( 'Add title' ) } - - ); + /* eslint-enable jsx-a11y/heading-has-content, jsx-a11y/no-noninteractive-element-interactions */ } diff --git a/packages/editor/src/components/post-title/style.scss b/packages/editor/src/components/post-title/style.scss index 16c53f2d44b377..dcfbe8a15497d9 100644 --- a/packages/editor/src/components/post-title/style.scss +++ b/packages/editor/src/components/post-title/style.scss @@ -1,75 +1,6 @@ .editor-post-title { position: relative; - .editor-post-title__input { - display: block; - width: 100%; - margin: 0; - box-shadow: none; - background: transparent; - transition: border 0.1s ease-out, box-shadow 0.1s linear; - @include reduce-motion("transition"); - padding: #{ $block-padding + 5px } 0; - word-break: keep-all; - - // Inherit the styles set by the theme. - font-family: inherit; - color: inherit; - - // Stack borders on mobile. - border: $border-width solid transparent; - border-left-width: 0; - border-right-width: 0; - border-radius: 0; - - // Include transparent outline for Windows High Contrast mode. - outline: $border-width solid transparent; - - @include break-small() { - border-width: $border-width; - } - - // Match h1 heading. - font-size: 2.44em; - font-weight: 800; - line-height: 1.4; - - &::-webkit-input-placeholder { - color: $dark-gray-placeholder; - } - - &::-moz-placeholder { - color: $dark-gray-placeholder; - // Override Firefox default. - opacity: 1; - } - - &:-ms-input-placeholder { - color: $dark-gray-placeholder; - } - - .is-dark-theme & { - &::-webkit-input-placeholder { - color: $light-gray-placeholder; - } - - &::-moz-placeholder { - opacity: 1; // Necessary because Firefox reduces this from 1. - color: $light-gray-placeholder; - } - - &:-ms-input-placeholder { - color: $light-gray-placeholder; - } - } - - &:focus { - border: $border-width solid transparent; - outline: $border-width solid transparent; - box-shadow: none; - } - } - &.is-focus-mode .editor-post-title__input { opacity: 0.5; transition: opacity 0.1s linear; diff --git a/packages/rich-text/src/component/index.js b/packages/rich-text/src/component/index.js index 74e184d07e4d9e..e91cc4833f1f1b 100644 --- a/packages/rich-text/src/component/index.js +++ b/packages/rich-text/src/component/index.js @@ -32,7 +32,7 @@ export function useRichText( { __unstableMultilineTag: multilineTag, __unstableDisableFormats: disableFormats, __unstableIsSelected: isSelected, - __unstableDependencies, + __unstableDependencies = [], __unstableAfterParse, __unstableBeforeSerialize, __unstableAddInvisibleFormats, @@ -90,7 +90,9 @@ export function useRichText( { record.current.formats = Array( value.length ); record.current.replacements = Array( value.length ); } - record.current.formats = __unstableAfterParse( record.current ); + if ( __unstableAfterParse ) { + record.current.formats = __unstableAfterParse( record.current ); + } record.current.start = selectionStart; record.current.end = selectionEnd; } @@ -123,11 +125,14 @@ export function useRichText( { if ( disableFormats ) { _value.current = newRecord.text; } else { - _value.current = toHTMLString( { - value: { + if ( __unstableBeforeSerialize ) { + newRecord = { ...newRecord, formats: __unstableBeforeSerialize( newRecord ), - }, + }; + } + _value.current = toHTMLString( { + value: newRecord, multilineTag, preserveWhiteSpace, } ); From 7e4374d6cbf878f4c751f0e28ba863dac95d2258 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ella=20van=C2=A0Durpe?= Date: Tue, 17 Aug 2021 18:38:37 +0300 Subject: [PATCH 2/5] Fix e2e tests --- .../e2e-tests/specs/editor/blocks/post-title.test.js | 2 +- .../specs/editor/various/change-detection.test.js | 2 +- .../editor/various/keyboard-navigable-blocks.test.js | 4 ++-- packages/editor/src/components/post-title/index.js | 10 +++++++--- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/e2e-tests/specs/editor/blocks/post-title.test.js b/packages/e2e-tests/specs/editor/blocks/post-title.test.js index bc8c886be3fc13..621e6815cfba84 100644 --- a/packages/e2e-tests/specs/editor/blocks/post-title.test.js +++ b/packages/e2e-tests/specs/editor/blocks/post-title.test.js @@ -28,7 +28,7 @@ describe( 'Post Title block', () => { await page.waitForSelector( '.edit-post-layout' ); const title = await page.$eval( '.editor-post-title__input', - ( element ) => element.value + ( element ) => element.textContent ); expect( title ).toEqual( 'Just tweaking the post title' ); } ); diff --git a/packages/e2e-tests/specs/editor/various/change-detection.test.js b/packages/e2e-tests/specs/editor/various/change-detection.test.js index 6eadb950c4e654..b9e91042809747 100644 --- a/packages/e2e-tests/specs/editor/various/change-detection.test.js +++ b/packages/e2e-tests/specs/editor/various/change-detection.test.js @@ -331,7 +331,7 @@ describe( 'Change detection', () => { // Verify that the title is empty. const title = await page.$eval( '.editor-post-title__input', - ( element ) => element.innerHTML + ( element ) => element.textContent ); expect( title ).toBe( '' ); diff --git a/packages/e2e-tests/specs/editor/various/keyboard-navigable-blocks.test.js b/packages/e2e-tests/specs/editor/various/keyboard-navigable-blocks.test.js index 9c45ff7d43a7ff..2c4f50b4bc1f94 100644 --- a/packages/e2e-tests/specs/editor/various/keyboard-navigable-blocks.test.js +++ b/packages/e2e-tests/specs/editor/various/keyboard-navigable-blocks.test.js @@ -116,7 +116,7 @@ describe( 'Order of block keyboard navigation', () => { await page.keyboard.press( 'Tab' ); await expect( await page.evaluate( () => { - return document.activeElement.placeholder; + return document.activeElement.getAttribute( 'aria-label' ); } ) ).toBe( 'Add title' ); @@ -168,7 +168,7 @@ describe( 'Order of block keyboard navigation', () => { await pressKeyWithModifier( 'shift', 'Tab' ); await expect( await page.evaluate( () => { - return document.activeElement.placeholder; + return document.activeElement.getAttribute( 'aria-label' ); } ) ).toBe( 'Add title' ); } ); diff --git a/packages/editor/src/components/post-title/index.js b/packages/editor/src/components/post-title/index.js index 7a5c242c3fdcbf..8363be852e959a 100644 --- a/packages/editor/src/components/post-title/index.js +++ b/packages/editor/src/components/post-title/index.js @@ -92,6 +92,8 @@ export default function PostTitle() { editPost( { title: newTitle } ); } + const [ selection, setSelection ] = useState( {} ); + function onSelect() { setIsSelected( true ); clearSelectedBlock(); @@ -99,6 +101,7 @@ export default function PostTitle() { function onUnselect() { setIsSelected( false ); + setSelection( {} ); } function onChange( value ) { @@ -172,12 +175,12 @@ export default function PostTitle() { 'has-fixed-toolbar': hasFixedToolbar, } ); - const decodedPlaceholder = decodeEntities( placeholder ); - const [ selection, setSelection ] = useState( {} ); + const decodedPlaceholder = + decodeEntities( placeholder ) || __( 'Add title' ); const { ref: richTextRef } = useRichText( { value: title, onChange, - placeholder: decodedPlaceholder || __( 'Add title' ), + placeholder: decodedPlaceholder, selectionStart: selection.start, selectionEnd: selection.end, onSelectionChange( newStart, newEnd ) { @@ -204,6 +207,7 @@ export default function PostTitle() { ref={ useMergeRefs( [ richTextRef, ref ] ) } contentEditable className="editor-post-title__input rich-text" + aria-label={ decodedPlaceholder } onFocus={ onSelect } onBlur={ onUnselect } onKeyDown={ onKeyDown } From 61063be80700a2c6913297de61a90c1591d09779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ella=20van=C2=A0Durpe?= Date: Wed, 18 Aug 2021 11:27:17 +0300 Subject: [PATCH 3/5] Fix e2e tests --- .../specs/editor/plugins/annotations.test.js | 6 +++--- .../editor/various/change-detection.test.js | 3 ++- .../specs/editor/various/new-post.test.js | 9 +++++--- .../specs/editor/various/writing-flow.test.js | 21 +++++++++---------- packages/rich-text/src/component/index.js | 13 ++++++------ 5 files changed, 27 insertions(+), 25 deletions(-) diff --git a/packages/e2e-tests/specs/editor/plugins/annotations.test.js b/packages/e2e-tests/specs/editor/plugins/annotations.test.js index 8de8ea026bca3f..1179f3fef4cdfb 100644 --- a/packages/e2e-tests/specs/editor/plugins/annotations.test.js +++ b/packages/e2e-tests/specs/editor/plugins/annotations.test.js @@ -52,7 +52,7 @@ describe( 'Using Plugins API', () => { )[ 0 ]; await addAnnotationButton.click(); await page.evaluate( () => - document.querySelector( '[contenteditable]' ).focus() + document.querySelector( '.wp-block-paragraph' ).focus() ); } @@ -91,7 +91,7 @@ describe( 'Using Plugins API', () => { * @return {Promise} Inner HTML. */ async function getRichTextInnerHTML() { - const htmlContent = await page.$$( '*[contenteditable]' ); + const htmlContent = await page.$$( '.wp-block-paragraph' ); return await page.evaluate( ( el ) => { return el.innerHTML; }, htmlContent[ 0 ] ); @@ -139,7 +139,7 @@ describe( 'Using Plugins API', () => { await page.keyboard.type( 'D' ); await removeAnnotations(); - const htmlContent = await page.$$( '*[contenteditable]' ); + const htmlContent = await page.$$( '.wp-block-paragraph' ); const html = await page.evaluate( ( el ) => { return el.innerHTML; }, htmlContent[ 0 ] ); diff --git a/packages/e2e-tests/specs/editor/various/change-detection.test.js b/packages/e2e-tests/specs/editor/various/change-detection.test.js index b9e91042809747..d3694734d2db5e 100644 --- a/packages/e2e-tests/specs/editor/various/change-detection.test.js +++ b/packages/e2e-tests/specs/editor/various/change-detection.test.js @@ -331,7 +331,8 @@ describe( 'Change detection', () => { // Verify that the title is empty. const title = await page.$eval( '.editor-post-title__input', - ( element ) => element.textContent + // Trim padding non-breaking space + ( element ) => element.textContent.trim() ); expect( title ).toBe( '' ); diff --git a/packages/e2e-tests/specs/editor/various/new-post.test.js b/packages/e2e-tests/specs/editor/various/new-post.test.js index e8baff2d573fb2..3f119d78028021 100644 --- a/packages/e2e-tests/specs/editor/various/new-post.test.js +++ b/packages/e2e-tests/specs/editor/various/new-post.test.js @@ -25,9 +25,12 @@ describe( 'new editor state', () => { expect.stringContaining( 'post-new.php' ) ); // Should display the blank title. - const title = await page.$( '[placeholder="Add title"]' ); + const title = await page.$( '[aria-label="Add title"]' ); expect( title ).not.toBeNull(); - expect( title.innerHTML ).toBeFalsy(); + // Trim padding non-breaking space + expect( + await title.evaluate( ( el ) => el.textContent.trim() ) + ).toBeFalsy(); // Should display the Preview button. const postPreviewButton = await page.$( '.editor-post-preview.components-button' @@ -59,7 +62,7 @@ describe( 'new editor state', () => { } ); expect( activeElementClasses ).toContain( 'editor-post-title__input' ); - expect( activeElementTagName ).toEqual( 'textarea' ); + expect( activeElementTagName ).toEqual( 'h1' ); } ); it( 'should not focus the title if the title exists', async () => { diff --git a/packages/e2e-tests/specs/editor/various/writing-flow.test.js b/packages/e2e-tests/specs/editor/various/writing-flow.test.js index 566303fabfeaf2..89885dcfca746a 100644 --- a/packages/e2e-tests/specs/editor/various/writing-flow.test.js +++ b/packages/e2e-tests/specs/editor/various/writing-flow.test.js @@ -289,29 +289,28 @@ describe( 'Writing Flow', () => { it( 'should navigate native inputs vertically, not horizontally', async () => { // See: https://github.com/WordPress/gutenberg/issues/9626 - // Title is within the editor's writing flow, and is a