From 136c8541c4e47116539b23950cf5b8a1ee2c4e62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ella=20van=C2=A0Durpe?= Date: Mon, 12 Sep 2022 15:28:44 +0200 Subject: [PATCH 1/6] Rich text: add button to clear unknow format --- .../format-library/src/default-formats.js | 2 ++ packages/format-library/src/unknown/index.js | 32 +++++++++++++++++++ packages/rich-text/src/create.js | 25 +++++++-------- .../src/remove-unregistered-formatting.js | 0 packages/rich-text/src/store/selectors.js | 12 +++++-- packages/rich-text/src/to-tree.js | 8 +++-- 6 files changed, 60 insertions(+), 19 deletions(-) create mode 100644 packages/format-library/src/unknown/index.js create mode 100644 packages/rich-text/src/remove-unregistered-formatting.js diff --git a/packages/format-library/src/default-formats.js b/packages/format-library/src/default-formats.js index 412ae23f4b686..791cabb1f118e 100644 --- a/packages/format-library/src/default-formats.js +++ b/packages/format-library/src/default-formats.js @@ -12,6 +12,7 @@ import { textColor } from './text-color'; import { subscript } from './subscript'; import { superscript } from './superscript'; import { keyboard } from './keyboard'; +import { unknown } from './unknown'; export default [ bold, @@ -25,4 +26,5 @@ export default [ subscript, superscript, keyboard, + unknown, ]; diff --git a/packages/format-library/src/unknown/index.js b/packages/format-library/src/unknown/index.js new file mode 100644 index 0000000000000..79c8c84548e48 --- /dev/null +++ b/packages/format-library/src/unknown/index.js @@ -0,0 +1,32 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { removeFormat } from '@wordpress/rich-text'; +import { RichTextToolbarButton } from '@wordpress/block-editor'; +import { help } from '@wordpress/icons'; + +const name = 'core/unknown'; +const title = __( 'Clear Unknown Formatting' ); + +export const unknown = { + name, + title, + tagName: '*', + className: null, + edit( { isActive, value, onChange, onFocus } ) { + function onClick() { + onChange( removeFormat( value, name ) ); + onFocus(); + } + + return ( + + ); + }, +}; diff --git a/packages/rich-text/src/create.js b/packages/rich-text/src/create.js index b9b31e1476257..ce0ce36bb0278 100644 --- a/packages/rich-text/src/create.js +++ b/packages/rich-text/src/create.js @@ -43,7 +43,7 @@ function createEmptyValue() { }; } -function toFormat( { type, attributes } ) { +function toFormat( { tagName, attributes } ) { let formatType; if ( attributes && attributes.class ) { @@ -65,11 +65,7 @@ function toFormat( { type, attributes } ) { if ( ! formatType ) { formatType = - select( richTextStore ).getFormatTypeForBareElement( type ); - } - - if ( ! formatType ) { - return attributes ? { type, attributes } : { type }; + select( richTextStore ).getFormatTypeForBareElement( tagName ); } if ( @@ -80,7 +76,7 @@ function toFormat( { type, attributes } ) { } if ( ! attributes ) { - return { type: formatType.name }; + return { type: formatType.name, tagName }; } const registeredAttributes = {}; @@ -115,6 +111,7 @@ function toFormat( { type, attributes } ) { return { type: formatType.name, + tagName, attributes: registeredAttributes, unregisteredAttributes, }; @@ -368,7 +365,7 @@ function createFromElement( { // Optimise for speed. for ( let index = 0; index < length; index++ ) { const node = element.childNodes[ index ]; - const type = node.nodeName.toLowerCase(); + const tagName = node.nodeName.toLowerCase(); if ( node.nodeType === node.TEXT_NODE ) { let filter = removeReservedCharacters; @@ -398,19 +395,19 @@ function createFromElement( { // Ignore any placeholders. ( node.getAttribute( 'data-rich-text-placeholder' ) || // Ignore any line breaks that are not inserted by us. - ( type === 'br' && + ( tagName === 'br' && ! node.getAttribute( 'data-rich-text-line-break' ) ) ) ) { accumulateSelection( accumulator, node, range, createEmptyValue() ); continue; } - if ( type === 'script' ) { + if ( tagName === 'script' ) { const value = { formats: [ , ], replacements: [ { - type, + type: tagName, attributes: { 'data-rich-text-script': node.getAttribute( 'data-rich-text-script' ) || @@ -425,20 +422,20 @@ function createFromElement( { continue; } - if ( type === 'br' ) { + if ( tagName === 'br' ) { accumulateSelection( accumulator, node, range, createEmptyValue() ); mergePair( accumulator, create( { text: '\n' } ) ); continue; } const format = toFormat( { - type, + tagName, attributes: getAttributes( { element: node } ), } ); if ( multilineWrapperTags && - multilineWrapperTags.indexOf( type ) !== -1 + multilineWrapperTags.indexOf( tagName ) !== -1 ) { const value = createFromMultilineElement( { element: node, diff --git a/packages/rich-text/src/remove-unregistered-formatting.js b/packages/rich-text/src/remove-unregistered-formatting.js new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/packages/rich-text/src/store/selectors.js b/packages/rich-text/src/store/selectors.js index 32a50cf89020d..52aa4dead99bf 100644 --- a/packages/rich-text/src/store/selectors.js +++ b/packages/rich-text/src/store/selectors.js @@ -37,9 +37,15 @@ export function getFormatType( state, name ) { * @return {?Object} Format type. */ export function getFormatTypeForBareElement( state, bareElementTagName ) { - return getFormatTypes( state ).find( ( { className, tagName } ) => { - return className === null && bareElementTagName === tagName; - } ); + const formatTypes = getFormatTypes( state ); + return ( + formatTypes.find( ( { className, tagName } ) => { + return className === null && bareElementTagName === tagName; + } ) || + formatTypes.find( ( { className, tagName } ) => { + return className === null && '*' === tagName; + } ) + ); } /** diff --git a/packages/rich-text/src/to-tree.js b/packages/rich-text/src/to-tree.js index 8b2397013de4d..74cc08581e83c 100644 --- a/packages/rich-text/src/to-tree.js +++ b/packages/rich-text/src/to-tree.js @@ -35,6 +35,7 @@ function restoreOnAttributes( attributes, isEditableTree ) { * * @param {Object} $1 Named parameters. * @param {string} $1.type The format type. + * @param {string} $1.tagName The tag name. * @param {Object} $1.attributes The format attributes. * @param {Object} $1.unregisteredAttributes The unregistered format * attributes. @@ -48,6 +49,7 @@ function restoreOnAttributes( attributes, isEditableTree ) { */ function fromFormat( { type, + tagName, attributes, unregisteredAttributes, object, @@ -100,7 +102,7 @@ function fromFormat( { } return { - type: formatType.tagName, + type: formatType.tagName === '*' ? tagName : formatType.tagName, object: formatType.object, attributes: restoreOnAttributes( elementAttributes, isEditableTree ), }; @@ -241,7 +243,8 @@ export function toTree( { return; } - const { type, attributes, unregisteredAttributes } = format; + const { type, tagName, attributes, unregisteredAttributes } = + format; const boundaryClass = isEditableTree && @@ -253,6 +256,7 @@ export function toTree( { parent, fromFormat( { type, + tagName, attributes, unregisteredAttributes, boundaryClass, From 59fe78aebb50b414666da2a9b602597662a939c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ella=20van=C2=A0Durpe?= Date: Mon, 12 Sep 2022 15:39:49 +0200 Subject: [PATCH 2/6] Only show button if relevant --- packages/format-library/src/unknown/index.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/format-library/src/unknown/index.js b/packages/format-library/src/unknown/index.js index 79c8c84548e48..b63e1f214fbfb 100644 --- a/packages/format-library/src/unknown/index.js +++ b/packages/format-library/src/unknown/index.js @@ -20,6 +20,10 @@ export const unknown = { onFocus(); } + if ( ! isActive ) { + return null; + } + return ( Date: Mon, 12 Sep 2022 15:57:46 +0200 Subject: [PATCH 3/6] Clear in selected text --- packages/format-library/src/unknown/index.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/format-library/src/unknown/index.js b/packages/format-library/src/unknown/index.js index b63e1f214fbfb..f96c115bbce08 100644 --- a/packages/format-library/src/unknown/index.js +++ b/packages/format-library/src/unknown/index.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { removeFormat } from '@wordpress/rich-text'; +import { removeFormat, slice } from '@wordpress/rich-text'; import { RichTextToolbarButton } from '@wordpress/block-editor'; import { help } from '@wordpress/icons'; @@ -20,7 +20,12 @@ export const unknown = { onFocus(); } - if ( ! isActive ) { + const selectedValue = slice( value ); + const hasUnknownFormats = selectedValue.formats.some( ( formats ) => { + return formats.some( ( format ) => format.type === name ); + } ); + + if ( ! isActive && ! hasUnknownFormats ) { return null; } @@ -29,7 +34,7 @@ export const unknown = { icon={ help } title={ title } onClick={ onClick } - isActive={ isActive } + isActive={ true } /> ); }, From 19c5ccd6f7c57fe372b44499ebdffb40ab33e438 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ella=20van=C2=A0Durpe?= Date: Tue, 13 Sep 2022 12:55:20 +0200 Subject: [PATCH 4/6] Fix tests --- packages/rich-text/src/create.js | 4 ++++ packages/rich-text/src/test/helpers/index.js | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/packages/rich-text/src/create.js b/packages/rich-text/src/create.js index ce0ce36bb0278..7fdcf8adba762 100644 --- a/packages/rich-text/src/create.js +++ b/packages/rich-text/src/create.js @@ -68,6 +68,10 @@ function toFormat( { tagName, attributes } ) { select( richTextStore ).getFormatTypeForBareElement( tagName ); } + if ( ! formatType ) { + return attributes ? { type: tagName, attributes } : { type: tagName }; + } + if ( formatType.__experimentalCreatePrepareEditableTree && ! formatType.__experimentalCreateOnChangeEditableValue diff --git a/packages/rich-text/src/test/helpers/index.js b/packages/rich-text/src/test/helpers/index.js index 96090ea64b51a..15aa032978c66 100644 --- a/packages/rich-text/src/test/helpers/index.js +++ b/packages/rich-text/src/test/helpers/index.js @@ -784,6 +784,7 @@ export const specWithRegistration = [ [ { type: 'my-plugin/link', + tagName: 'a', attributes: {}, unregisteredAttributes: {}, }, @@ -808,6 +809,7 @@ export const specWithRegistration = [ [ { type: 'my-plugin/link', + tagName: 'a', attributes: {}, unregisteredAttributes: { class: 'test', @@ -834,6 +836,7 @@ export const specWithRegistration = [ [ { type: 'core/link', + tagName: 'a', attributes: {}, unregisteredAttributes: { class: 'custom-format', @@ -899,6 +902,7 @@ export const specWithRegistration = [ [ { type: 'my-plugin/link', + tagName: 'a', attributes: {}, unregisteredAttributes: {}, }, From 57943dc1eae138280b861ab204890276abea8591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ella=20van=C2=A0Durpe?= Date: Mon, 19 Sep 2022 15:24:54 +0300 Subject: [PATCH 5/6] Show in main toolbar --- .../src/components/rich-text/format-toolbar/index.js | 2 +- packages/format-library/src/unknown/index.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/rich-text/format-toolbar/index.js b/packages/block-editor/src/components/rich-text/format-toolbar/index.js index 7817fc284ae52..445cd38b4cc1d 100644 --- a/packages/block-editor/src/components/rich-text/format-toolbar/index.js +++ b/packages/block-editor/src/components/rich-text/format-toolbar/index.js @@ -23,7 +23,7 @@ const POPOVER_PROPS = { const FormatToolbar = () => { return ( <> - { [ 'bold', 'italic', 'link' ].map( ( format ) => ( + { [ 'bold', 'italic', 'link', 'unknown' ].map( ( format ) => ( Date: Thu, 8 Dec 2022 16:33:17 +0200 Subject: [PATCH 6/6] Add e2e test --- .../specs/editor/plugins/format-api.spec.js | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/e2e/specs/editor/plugins/format-api.spec.js b/test/e2e/specs/editor/plugins/format-api.spec.js index a294a2034f055..1b1d3b3d4173f 100644 --- a/test/e2e/specs/editor/plugins/format-api.spec.js +++ b/test/e2e/specs/editor/plugins/format-api.spec.js @@ -34,6 +34,28 @@ test.describe( 'Using Format API', () => { expect( content ).toBe( `

First paragraph

+` + ); + } ); + + test( 'should show unknow formatting button', async ( { + editor, + page, + } ) => { + await editor.insertBlock( { + name: 'core/paragraph', + attributes: { content: 'test' }, + } ); + expect( await editor.getEditedPostContent() ).toBe( + ` +

test

+` + ); + await page.keyboard.press( 'ArrowRight' ); + await editor.clickBlockToolbarButton( 'Clear Unknown Formatting' ); + expect( await editor.getEditedPostContent() ).toBe( + ` +

test

` ); } );