From 4d7ae534d4a9e569edf3c399681487c3d9187953 Mon Sep 17 00:00:00 2001 From: iseulde Date: Mon, 5 Nov 2018 16:15:01 +0100 Subject: [PATCH] Make it possible to undo prefix transforms --- docs/block-api.md | 57 +++++++++++++++---- packages/block-library/src/code/index.js | 3 +- packages/block-library/src/heading/index.js | 12 ++-- packages/block-library/src/list/index.js | 20 +++---- packages/block-library/src/quote/index.js | 6 +- packages/block-library/src/separator/index.js | 3 +- .../editor/src/components/rich-text/index.js | 7 +-- .../src/components/rich-text/patterns.js | 47 +++++++-------- .../blocks/__snapshots__/code.test.js.snap | 7 +++ .../blocks/__snapshots__/heading.test.js.snap | 13 +++++ .../blocks/__snapshots__/list.test.js.snap | 6 ++ .../__snapshots__/separator.test.js.snap | 7 +++ test/e2e/specs/blocks/code.test.js | 23 ++++++++ test/e2e/specs/blocks/heading.test.js | 30 ++++++++++ test/e2e/specs/blocks/list.test.js | 10 ++++ test/e2e/specs/blocks/separator.test.js | 22 +++++++ 16 files changed, 212 insertions(+), 61 deletions(-) create mode 100644 test/e2e/specs/blocks/__snapshots__/code.test.js.snap create mode 100644 test/e2e/specs/blocks/__snapshots__/heading.test.js.snap create mode 100644 test/e2e/specs/blocks/__snapshots__/separator.test.js.snap create mode 100644 test/e2e/specs/blocks/code.test.js create mode 100644 test/e2e/specs/blocks/heading.test.js create mode 100644 test/e2e/specs/blocks/separator.test.js diff --git a/docs/block-api.md b/docs/block-api.md index 01b2295b46de0b..155bd417d21a27 100644 --- a/docs/block-api.md +++ b/docs/block-api.md @@ -124,18 +124,18 @@ Block styles can be used to provide alternative styles to block. It works by add // Register block styles. styles: [ // Mark style as default. - { - name: 'default', - label: __( 'Rounded' ), - isDefault: true + { + name: 'default', + label: __( 'Rounded' ), + isDefault: true }, - { - name: 'outline', - label: __( 'Outline' ) + { + name: 'outline', + label: __( 'Outline' ) }, - { - name: 'squared', - label: __( 'Squared' ) + { + name: 'squared', + label: __( 'Squared' ) }, ], ``` @@ -413,6 +413,43 @@ transforms: { ``` {% end %} +A prefix transform is a transform that will be applied if the user prefixes some text in e.g. the paragraph block with a given pattern and a trailing space. + +{% codetabs %} +{% ES5 %} +```js +transforms: { + from: [ + { + type: 'prefix', + prefix: '?', + transform: function( content ) { + return createBlock( 'my-plugin/question', { + content, + } ); + }, + }, + ] +} +``` +{% ESNext %} +```js +transforms: { + from: [ + { + type: 'prefix', + prefix: '?', + transform( content ) { + return createBlock( 'my-plugin/question', { + content, + } ); + }, + }, + ] +} +``` +{% end %} + #### parent (optional) diff --git a/packages/block-library/src/code/index.js b/packages/block-library/src/code/index.js index cb304079e1e2c7..ef893570bac1b8 100644 --- a/packages/block-library/src/code/index.js +++ b/packages/block-library/src/code/index.js @@ -39,8 +39,7 @@ export const settings = { transforms: { from: [ { - type: 'pattern', - trigger: 'enter', + type: 'enter', regExp: /^```$/, transform: () => createBlock( 'core/code' ), }, diff --git a/packages/block-library/src/heading/index.js b/packages/block-library/src/heading/index.js index 148d0629340744..288a72d35bc1be 100644 --- a/packages/block-library/src/heading/index.js +++ b/packages/block-library/src/heading/index.js @@ -107,18 +107,16 @@ export const settings = { } ); }, }, - { - type: 'pattern', - regExp: /^(#{2,6})\s/, - transform: ( { content, match } ) => { - const level = match[ 1 ].length; - + ...[ 2, 3, 4, 5, 6 ].map( ( level ) => ( { + type: 'prefix', + prefix: Array( level + 1 ).join( '#' ), + transform( content ) { return createBlock( 'core/heading', { level, content, } ); }, - }, + } ) ), ], to: [ { diff --git a/packages/block-library/src/list/index.js b/packages/block-library/src/list/index.js index 7d59ed853720e1..e7a6174ef25084 100644 --- a/packages/block-library/src/list/index.js +++ b/packages/block-library/src/list/index.js @@ -110,25 +110,25 @@ export const settings = { } ); }, }, - { - type: 'pattern', - regExp: /^[*-]\s/, - transform: ( { content } ) => { + ...[ '*', '-' ].map( ( prefix ) => ( { + type: 'prefix', + prefix, + transform( content ) { return createBlock( 'core/list', { values: `
  • ${ content }
  • `, } ); }, - }, - { - type: 'pattern', - regExp: /^1[.)]\s/, - transform: ( { content } ) => { + } ) ), + ...[ '1.', '1)' ].map( ( prefix ) => ( { + type: 'prefix', + prefix, + transform( content ) { return createBlock( 'core/list', { ordered: true, values: `
  • ${ content }
  • `, } ); }, - }, + } ) ), ], to: [ { diff --git a/packages/block-library/src/quote/index.js b/packages/block-library/src/quote/index.js index dd60905cded3b0..78e53875b771db 100644 --- a/packages/block-library/src/quote/index.js +++ b/packages/block-library/src/quote/index.js @@ -90,9 +90,9 @@ export const settings = { } ), }, { - type: 'pattern', - regExp: /^>\s/, - transform: ( { content } ) => { + type: 'prefix', + prefix: '>', + transform: ( content ) => { return createBlock( 'core/quote', { value: `

    ${ content }

    `, } ); diff --git a/packages/block-library/src/separator/index.js b/packages/block-library/src/separator/index.js index f077d6533413de..adcc34297d2ff1 100644 --- a/packages/block-library/src/separator/index.js +++ b/packages/block-library/src/separator/index.js @@ -27,8 +27,7 @@ export const settings = { transforms: { from: [ { - type: 'pattern', - trigger: 'enter', + type: 'enter', regExp: /^-{3,}$/, transform: () => createBlock( 'core/separator' ), }, diff --git a/packages/editor/src/components/rich-text/index.js b/packages/editor/src/components/rich-text/index.js index 3bda199650d234..0db23b1dcba80b 100644 --- a/packages/editor/src/components/rich-text/index.js +++ b/packages/editor/src/components/rich-text/index.js @@ -117,12 +117,11 @@ export class RichText extends Component { this.savedContent = value; this.patterns = getPatterns( { onReplace, - multilineTag: this.multilineTag, + onCreateUndoLevel: this.onCreateUndoLevel, valueToFormat: this.valueToFormat, } ); - this.enterPatterns = getBlockTransforms( 'from' ).filter( ( { type, trigger } ) => - type === 'pattern' && trigger === 'enter' - ); + this.enterPatterns = getBlockTransforms( 'from' ) + .filter( ( { type } ) => type === 'enter' ); this.state = {}; diff --git a/packages/editor/src/components/rich-text/patterns.js b/packages/editor/src/components/rich-text/patterns.js index 5cb16b5fe68b6e..b5e425113a1fb0 100644 --- a/packages/editor/src/components/rich-text/patterns.js +++ b/packages/editor/src/components/rich-text/patterns.js @@ -1,18 +1,18 @@ -/** - * External dependencies - */ -import { filter } from 'lodash'; - /** * WordPress dependencies */ import { getBlockTransforms, findTransform } from '@wordpress/blocks'; -import { remove, applyFormat, getTextContent } from '@wordpress/rich-text'; - -export function getPatterns( { onReplace, multiline, valueToFormat } ) { - const patterns = filter( getBlockTransforms( 'from' ), ( { type, trigger } ) => { - return type === 'pattern' && trigger === undefined; - } ); +import { + remove, + applyFormat, + getTextContent, + getSelectionStart, + slice, +} from '@wordpress/rich-text'; + +export function getPatterns( { onReplace, valueToFormat, onCreateUndoLevel } ) { + const prefixTransforms = getBlockTransforms( 'from' ) + .filter( ( { type } ) => type === 'prefix' ); return [ ( record ) => { @@ -20,31 +20,32 @@ export function getPatterns( { onReplace, multiline, valueToFormat } ) { return record; } + const start = getSelectionStart( record ); const text = getTextContent( record ); - const transformation = findTransform( patterns, ( item ) => { - return item.regExp.test( text ); + const characterBefore = text.slice( start - 1, start ); + + if ( ! /\s/.test( characterBefore ) ) { + return record; + } + + const trimmedTextBefore = text.slice( 0, start ).trim(); + const transformation = findTransform( prefixTransforms, ( { prefix } ) => { + return trimmedTextBefore === prefix; } ); if ( ! transformation ) { return record; } - const result = text.match( transformation.regExp ); - - const block = transformation.transform( { - content: valueToFormat( remove( record, 0, result[ 0 ].length ) ), - match: result, - } ); + const content = valueToFormat( slice( record, start, text.length ) ); + const block = transformation.transform( content ); + onCreateUndoLevel(); onReplace( [ block ] ); return record; }, ( record ) => { - if ( multiline ) { - return record; - } - const text = getTextContent( record ); // Quick check the text for the necessary character. diff --git a/test/e2e/specs/blocks/__snapshots__/code.test.js.snap b/test/e2e/specs/blocks/__snapshots__/code.test.js.snap new file mode 100644 index 00000000000000..50ee83d373c65e --- /dev/null +++ b/test/e2e/specs/blocks/__snapshots__/code.test.js.snap @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Code can be created by three backticks and enter 1`] = ` +" +
    <?php
    +" +`; diff --git a/test/e2e/specs/blocks/__snapshots__/heading.test.js.snap b/test/e2e/specs/blocks/__snapshots__/heading.test.js.snap new file mode 100644 index 00000000000000..6cffb6ad28a9fc --- /dev/null +++ b/test/e2e/specs/blocks/__snapshots__/heading.test.js.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Separator can be created by prefixing existing content with number signs and a space 1`] = ` +" +

    4

    +" +`; + +exports[`Separator can be created by prefixing number sign and a space 1`] = ` +" +

    3

    +" +`; diff --git a/test/e2e/specs/blocks/__snapshots__/list.test.js.snap b/test/e2e/specs/blocks/__snapshots__/list.test.js.snap index dbc0bee94158fd..787fd56a28d2c6 100644 --- a/test/e2e/specs/blocks/__snapshots__/list.test.js.snap +++ b/test/e2e/specs/blocks/__snapshots__/list.test.js.snap @@ -74,6 +74,12 @@ exports[`List can be created by using an asterisk at the start of a paragraph bl " `; +exports[`List can undo asterisk transform 1`] = ` +" +
    1. November
    +" +`; + exports[`List should create paragraph on split at end and merge back with content 1`] = ` " diff --git a/test/e2e/specs/blocks/__snapshots__/separator.test.js.snap b/test/e2e/specs/blocks/__snapshots__/separator.test.js.snap new file mode 100644 index 00000000000000..41466bc4de2500 --- /dev/null +++ b/test/e2e/specs/blocks/__snapshots__/separator.test.js.snap @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Separator can be created by three dashes and enter 1`] = ` +" +
    +" +`; diff --git a/test/e2e/specs/blocks/code.test.js b/test/e2e/specs/blocks/code.test.js new file mode 100644 index 00000000000000..85cdbe1dd715ff --- /dev/null +++ b/test/e2e/specs/blocks/code.test.js @@ -0,0 +1,23 @@ +/** + * Internal dependencies + */ +import { + clickBlockAppender, + getEditedPostContent, + newPost, +} from '../../support/utils'; + +describe( 'Code', () => { + beforeEach( async () => { + await newPost(); + } ); + + it( 'can be created by three backticks and enter', async () => { + await clickBlockAppender(); + await page.keyboard.type( '```' ); + await page.keyboard.press( 'Enter' ); + await page.keyboard.type( ' { + beforeEach( async () => { + await newPost(); + } ); + + it( 'can be created by prefixing number sign and a space', async () => { + await clickBlockAppender(); + await page.keyboard.type( '### 3' ); + + expect( await getEditedPostContent() ).toMatchSnapshot(); + } ); + + it( 'can be created by prefixing existing content with number signs and a space', async () => { + await clickBlockAppender(); + await page.keyboard.type( '4' ); + await page.keyboard.press( 'ArrowLeft' ); + await page.keyboard.type( '#### ' ); + + expect( await getEditedPostContent() ).toMatchSnapshot(); + } ); +} ); diff --git a/test/e2e/specs/blocks/list.test.js b/test/e2e/specs/blocks/list.test.js index f28196cfbdffab..b2980c4f8381b9 100644 --- a/test/e2e/specs/blocks/list.test.js +++ b/test/e2e/specs/blocks/list.test.js @@ -9,6 +9,7 @@ import { convertBlock, pressWithModifier, insertBlock, + META_KEY, } from '../../support/utils'; describe( 'List', () => { @@ -46,6 +47,15 @@ describe( 'List', () => { expect( await getEditedPostContent() ).toMatchSnapshot(); } ); + it( 'can undo asterisk transform', async () => { + await clickBlockAppender(); + await page.keyboard.type( '1. ' ); + await pressWithModifier( META_KEY, 'z' ); + await page.keyboard.type( 'November' ); + + expect( await getEditedPostContent() ).toMatchSnapshot(); + } ); + it( 'can be created by typing "/list"', async () => { // Create a list with the slash block shortcut. await clickBlockAppender(); diff --git a/test/e2e/specs/blocks/separator.test.js b/test/e2e/specs/blocks/separator.test.js new file mode 100644 index 00000000000000..348d7a6ed45fc8 --- /dev/null +++ b/test/e2e/specs/blocks/separator.test.js @@ -0,0 +1,22 @@ +/** + * Internal dependencies + */ +import { + clickBlockAppender, + getEditedPostContent, + newPost, +} from '../../support/utils'; + +describe( 'Separator', () => { + beforeEach( async () => { + await newPost(); + } ); + + it( 'can be created by three dashes and enter', async () => { + await clickBlockAppender(); + await page.keyboard.type( '---' ); + await page.keyboard.press( 'Enter' ); + + expect( await getEditedPostContent() ).toMatchSnapshot(); + } ); +} );