From b1c9e8f9fec3a3df99354bfd7e53ef04fdee584f Mon Sep 17 00:00:00 2001 From: grzegorz_marzencki Date: Mon, 30 Nov 2020 22:10:04 +0100 Subject: [PATCH 01/21] adding span to schema in order to handle text color. Remove backgorund color in order not to paste text with the selection highlight --- .../blocks/src/api/raw-handling/phrasing-content-reducer.js | 6 ++++++ packages/dom/src/phrasing-content.js | 1 + 2 files changed, 7 insertions(+) diff --git a/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js b/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js index cd1e45d077e4d3..b2abf7e9d8c976 100644 --- a/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js +++ b/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js @@ -18,8 +18,14 @@ export default function phrasingContentReducer( node, doc ) { textDecorationLine, textDecoration, verticalAlign, + backgroundColor, } = node.style; + // removes the highlight that appears when text is zaznaczony do skopiowania + if ( backgroundColor ) { + node.style.backgroundColor = ''; + } + if ( fontWeight === 'bold' || fontWeight === '700' ) { wrap( doc.createElement( 'strong' ), node ); } diff --git a/packages/dom/src/phrasing-content.js b/packages/dom/src/phrasing-content.js index f9274af7832f1b..d17f77fbec5ab3 100644 --- a/packages/dom/src/phrasing-content.js +++ b/packages/dom/src/phrasing-content.js @@ -39,6 +39,7 @@ const textContentSchema = { i: {}, b: {}, u: {}, + span: { attributes: [ 'style' ] }, mark: {}, ruby: {}, rt: {}, From abc73fcf4e5bcd7890bf71cfbf1aff7de328e40f Mon Sep 17 00:00:00 2001 From: grzegorz_marzencki Date: Tue, 1 Dec 2020 15:24:33 +0100 Subject: [PATCH 02/21] adding possibility to paste coloured text --- .../src/api/raw-handling/paste-handler.js | 9 +++-- .../raw-handling/phrasing-content-reducer.js | 13 ++++--- packages/dom/src/phrasing-content.js | 35 ++++++++++++++++++- 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/packages/blocks/src/api/raw-handling/paste-handler.js b/packages/blocks/src/api/raw-handling/paste-handler.js index 41129299cc354f..9b3a2747763df4 100644 --- a/packages/blocks/src/api/raw-handling/paste-handler.js +++ b/packages/blocks/src/api/raw-handling/paste-handler.js @@ -6,7 +6,11 @@ import { flatMap, filter, compact } from 'lodash'; /** * WordPress dependencies */ -import { getPhrasingContentSchema, removeInvalidHTML } from '@wordpress/dom'; +import { + getPhrasingContentSchema, + alterSpecialTags, + removeInvalidHTML, +} from '@wordpress/dom'; /** * Internal dependencies @@ -58,6 +62,8 @@ function filterInlineHTML( HTML, preserveWhiteSpace ) { inline: true, } ); + HTML = alterSpecialTags( HTML ); + if ( ! preserveWhiteSpace ) { HTML = deepFilterHTML( HTML, [ htmlFormattingRemover, brRemover ] ); } @@ -117,7 +123,6 @@ function htmlToBlocks( { html, rawTransforms } ) { if ( transform ) { return transform( node ); } - return createBlock( blockName, getBlockAttributes( blockName, node.outerHTML ) diff --git a/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js b/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js index b2abf7e9d8c976..08b388174aec82 100644 --- a/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js +++ b/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js @@ -8,22 +8,27 @@ import { includes } from 'lodash'; */ import { wrap, replaceTag } from '@wordpress/dom'; +const defaultColor = [ 'rgb(0, 0, 0)', 'rgba(0, 0, 0, 0)', 'black', '#000000' ]; + export default function phrasingContentReducer( node, doc ) { // In jsdom-jscore, 'node.style' can be null. // TODO: Explore fixing this by patching jsdom-jscore. if ( node.nodeName === 'SPAN' && node.style ) { const { + color, fontWeight, fontStyle, textDecorationLine, textDecoration, verticalAlign, - backgroundColor, } = node.style; - // removes the highlight that appears when text is zaznaczony do skopiowania - if ( backgroundColor ) { - node.style.backgroundColor = ''; + // adds color to the wrapper + if ( color && ! defaultColor.includes( color ) ) { + const wrapper = doc.createElement( 'coloured' ); + wrapper.style.color = color; + wrapper.style.backgroundColor = ''; + wrap( wrapper, node ); } if ( fontWeight === 'bold' || fontWeight === '700' ) { diff --git a/packages/dom/src/phrasing-content.js b/packages/dom/src/phrasing-content.js index d17f77fbec5ab3..facf40da2e600d 100644 --- a/packages/dom/src/phrasing-content.js +++ b/packages/dom/src/phrasing-content.js @@ -9,6 +9,15 @@ import { omit, without } from 'lodash'; * @see https://www.w3.org/TR/2011/WD-html5-20110525/content-models.html#phrasing-content-0 */ +/** + * @type {{coloured: {attributes: [string]}}} + * Some spans will carry additional styling that needs to be preserved. + * In order not to loose information about styling (as all spans are removed) a special tags are introduced, wich later will be turned back to span. + */ +const specialTags = { + coloured: { attributes: [ 'style' ], originalTag: 'span' }, +}; + /** * All text-level semantic elements. * @@ -39,7 +48,6 @@ const textContentSchema = { i: {}, b: {}, u: {}, - span: { attributes: [ 'style' ] }, mark: {}, ruby: {}, rt: {}, @@ -48,8 +56,33 @@ const textContentSchema = { bdo: { attributes: [ 'dir' ] }, wbr: {}, '#text': {}, + ...specialTags, }; +export const alterSpecialTags = ( HTML ) => + Object.entries( specialTags ).reduce( ( acc, [ tag, { originalTag } ] ) => { + const openingTagRegExp = new RegExp( `<${ tag }`, 'g' ); + const closingTagRegExp = new RegExp( ` + Object.entries( specialTags ).reduce( ( acc, [ tag ] ) => { + const openingTagRegExp = new RegExp( `<${ tag }`, 'g' ); + const closingTagRegExp = new RegExp( ` em > strong. // Impossible: strong > strong. From 48001b9169531b2b78af10d9fa978846fd45c240 Mon Sep 17 00:00:00 2001 From: grzegorz_marzencki Date: Tue, 1 Dec 2020 15:58:11 +0100 Subject: [PATCH 03/21] adding custom html tag in order to preserve information about text color --- .../src/api/raw-handling/phrasing-content-reducer.js | 2 +- packages/dom/README.md | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js b/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js index 08b388174aec82..e58b5825a7f904 100644 --- a/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js +++ b/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js @@ -20,7 +20,7 @@ export default function phrasingContentReducer( node, doc ) { fontStyle, textDecorationLine, textDecoration, - verticalAlign, + verticgit statalAlign, } = node.style; // adds color to the wrapper diff --git a/packages/dom/README.md b/packages/dom/README.md index 6fe8d1ca75825d..80638ee7a59f59 100644 --- a/packages/dom/README.md +++ b/packages/dom/README.md @@ -14,6 +14,10 @@ npm install @wordpress/dom --save +# **alterSpecialTags** + +Undocumented declaration. + # **computeCaretRect** Get the rectangle for the selection in a container. @@ -304,6 +308,10 @@ _Returns_ - `string`: The cleaned up HTML. +# **removeSpecialTags** + +Undocumented declaration. + # **replace** Given two DOM nodes, replaces the former with the latter in the DOM. From 7d2825eea85da5a28c9f0a9e7201b750c06f77b5 Mon Sep 17 00:00:00 2001 From: grzegorz_marzencki Date: Tue, 1 Dec 2020 16:51:11 +0100 Subject: [PATCH 04/21] fix in destructuring --- .../blocks/src/api/raw-handling/phrasing-content-reducer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js b/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js index e58b5825a7f904..08b388174aec82 100644 --- a/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js +++ b/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js @@ -20,7 +20,7 @@ export default function phrasingContentReducer( node, doc ) { fontStyle, textDecorationLine, textDecoration, - verticgit statalAlign, + verticalAlign, } = node.style; // adds color to the wrapper From bbdfa3bca44cdadcc5c1403d964bdb43389ca0f2 Mon Sep 17 00:00:00 2001 From: grzegorz_marzencki Date: Tue, 8 Dec 2020 02:47:29 +0100 Subject: [PATCH 05/21] file refactor --- .../raw-handling/phrasing-content-reducer.js | 118 ++++++++++-------- 1 file changed, 63 insertions(+), 55 deletions(-) diff --git a/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js b/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js index 08b388174aec82..305219c9161b05 100644 --- a/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js +++ b/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js @@ -1,71 +1,79 @@ /** * External dependencies */ -import { includes } from 'lodash'; +import { identity, includes } from 'lodash'; /** * WordPress dependencies */ import { wrap, replaceTag } from '@wordpress/dom'; -const defaultColor = [ 'rgb(0, 0, 0)', 'rgba(0, 0, 0, 0)', 'black', '#000000' ]; +const getSemanticTags = ( { node } ) => { + const { + fontWeight, + fontStyle, + textDecorationLine, + textDecoration, + verticalAlign, + } = node.style; + const tags = []; + if ( fontWeight === 'bold' || fontWeight === '700' ) { + tags.push( 'strong' ); + } -export default function phrasingContentReducer( node, doc ) { - // In jsdom-jscore, 'node.style' can be null. - // TODO: Explore fixing this by patching jsdom-jscore. - if ( node.nodeName === 'SPAN' && node.style ) { - const { - color, - fontWeight, - fontStyle, - textDecorationLine, - textDecoration, - verticalAlign, - } = node.style; + if ( fontStyle === 'italic' ) { + tags.push( 'em' ); + } - // adds color to the wrapper - if ( color && ! defaultColor.includes( color ) ) { - const wrapper = doc.createElement( 'coloured' ); - wrapper.style.color = color; - wrapper.style.backgroundColor = ''; - wrap( wrapper, node ); - } + // Some DOM implementations (Safari, JSDom) don't support + // style.textDecorationLine, so we check style.textDecoration as a + // fallback. + if ( + textDecorationLine === 'line-through' || + includes( textDecoration, 'line-through' ) + ) { + tags.push( 's' ); + } - if ( fontWeight === 'bold' || fontWeight === '700' ) { - wrap( doc.createElement( 'strong' ), node ); - } + if ( verticalAlign === 'super' ) { + tags.push( 'sup' ); + } else if ( verticalAlign === 'sub' ) { + tags.push( 'sub' ); + } + return tags; +}; - if ( fontStyle === 'italic' ) { - wrap( doc.createElement( 'em' ), node ); - } +const handleSpan = ( { node, doc } ) => { + const semanticTagsToWrap = getSemanticTags( node ); + semanticTagsToWrap.forEach( ( tag ) => + wrap( doc.createElement( tag ), node ) + ); +}; +const handleBold = ( { node } ) => replaceTag( node, 'strong' ); +const handleItalic = ( { node } ) => replaceTag( node, 'italic' ); +const handleAnchor = ( { node } ) => { + // In jsdom-jscore, 'node.target' can be null. + // TODO: Explore fixing this by patching jsdom-jscore. + if ( node.target && node.target.toLowerCase() === '_blank' ) { + node.rel = 'noreferrer noopener'; + } else { + node.removeAttribute( 'target' ); + node.removeAttribute( 'rel' ); + } +}; - // Some DOM implementations (Safari, JSDom) don't support - // style.textDecorationLine, so we check style.textDecoration as a - // fallback. - if ( - textDecorationLine === 'line-through' || - includes( textDecoration, 'line-through' ) - ) { - wrap( doc.createElement( 's' ), node ); - } +const elementNameToHandlerMapper = { + span: handleSpan, + B: handleBold, + I: handleItalic, + A: handleAnchor, +}; - if ( verticalAlign === 'super' ) { - wrap( doc.createElement( 'sup' ), node ); - } else if ( verticalAlign === 'sub' ) { - wrap( doc.createElement( 'sub' ), node ); - } - } else if ( node.nodeName === 'B' ) { - node = replaceTag( node, 'strong' ); - } else if ( node.nodeName === 'I' ) { - node = replaceTag( node, 'em' ); - } else if ( node.nodeName === 'A' ) { - // In jsdom-jscore, 'node.target' can be null. - // TODO: Explore fixing this by patching jsdom-jscore. - if ( node.target && node.target.toLowerCase() === '_blank' ) { - node.rel = 'noreferrer noopener'; - } else { - node.removeAttribute( 'target' ); - node.removeAttribute( 'rel' ); - } - } +export default function phrasingContentReducer( node, doc ) { + // In jsdom-jscore, 'node.style' can be null. + // TODO: Explore fixing this by patching jsdom-jscore. + const elementType = node.nodeName; + const elementHandler = + elementNameToHandlerMapper[ elementType ] || identity; + elementHandler( { node, doc } ); } From 7d37efe4274790d28634f2e33303185c6c19884e Mon Sep 17 00:00:00 2001 From: grzegorz_marzencki Date: Tue, 8 Dec 2020 03:06:36 +0100 Subject: [PATCH 06/21] sketch - instead of manualy writing schemas, another solution proposed - 1. get default styling (black text) 2. diff the default styling with styles of pasted element to get all nondefault styles applied to element 3. Sanitize output 4. paste the output --- .../src/api/raw-handling/paste-handler.js | 47 +++++++++++++++++-- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/packages/blocks/src/api/raw-handling/paste-handler.js b/packages/blocks/src/api/raw-handling/paste-handler.js index 9b3a2747763df4..68a73bf83408c3 100644 --- a/packages/blocks/src/api/raw-handling/paste-handler.js +++ b/packages/blocks/src/api/raw-handling/paste-handler.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { flatMap, filter, compact } from 'lodash'; +import { flatMap, filter, compact, identity } from 'lodash'; /** * WordPress dependencies @@ -44,6 +44,43 @@ import emptyParagraphRemover from './empty-paragraph-remover'; */ const { console } = window; +const difference = ( base ) => base; + +const getStylesEntries = ( stylesString ) => + stylesString + .split( ';' ) + .filter( identity ) + .map( ( styles ) => { + const [ key, values ] = styles.trim().split( ':' ); + const valuesArr = + values && + values + .split( `'` ) + .join( '' ) + .split( '"' ) + .join( '"' ) + .split( ', ' ) + .map( ( w ) => w.trim() ) + .join( '' ); + + return [ key, valuesArr ]; + } ); + +const defaultStyles = + "color: rgb(40, 48, 61); font-family: -apple-system, system-ui, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif; font-size: 20px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: pre-wrap; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(209, 228, 221); text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;"; + +const toString = ( arr ) => + arr.map( ( pair ) => pair.join( ':' ) ).join( '; ' ); + +const sanitize = ( x ) => x; +const removeDefaultValues = ( html ) => { + const entriesForDefaultStyles = getStylesEntries( defaultStyles ); + const [ htmlPart0, restHtml ] = html.split( 'style=' ); + const [ , htmlStyles, htmlPart2 ] = restHtml.split( `"` ); + const entriesForCurrentStyles = getStylesEntries( htmlStyles ); + const diff = difference( entriesForDefaultStyles, entriesForCurrentStyles ); + return htmlPart0 + 'style="' + toString( diff ) + '"' + htmlPart2; +}; /** * Filters HTML to only contain phrasing content. * @@ -58,9 +95,11 @@ function filterInlineHTML( HTML, preserveWhiteSpace ) { phrasingContentReducer, commentRemover, ] ); - HTML = removeInvalidHTML( HTML, getPhrasingContentSchema( 'paste' ), { - inline: true, - } ); + + HTML = sanitize( removeDefaultValues( HTML ) ); + // HTML = removeInvalidHTML( HTML, getPhrasingContentSchema( 'paste' ), { + // inline: true, + // } ); HTML = alterSpecialTags( HTML ); From 2b7f016a77cb7a78afa208e6a6f5c7fc2ac73f14 Mon Sep 17 00:00:00 2001 From: grzegorz_marzencki Date: Wed, 9 Dec 2020 12:43:02 +0100 Subject: [PATCH 07/21] mechanism to compare stylings --- .../src/api/raw-handling/paste-handler.js | 46 +++++++++++++------ 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/packages/blocks/src/api/raw-handling/paste-handler.js b/packages/blocks/src/api/raw-handling/paste-handler.js index 68a73bf83408c3..63d3e29073a317 100644 --- a/packages/blocks/src/api/raw-handling/paste-handler.js +++ b/packages/blocks/src/api/raw-handling/paste-handler.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { flatMap, filter, compact, identity } from 'lodash'; +import { flatMap, filter, compact, identity, difference } from 'lodash'; /** * WordPress dependencies @@ -44,7 +44,22 @@ import emptyParagraphRemover from './empty-paragraph-remover'; */ const { console } = window; -const difference = ( base ) => base; +const getNonDefaultStyles = ( { defaultStylesEntries, stylesEntries } ) => + stylesEntries.reduce( ( acc, [ key, values ] ) => { + const defaultValues = ( defaultStylesEntries.find( + ( [ defaultKey ] ) => defaultKey === key + ) || [] )[ 1 ]; + const diff = difference( defaultValues, values ); + const nonDefaultStyles = diff.length + ? { + [ key ]: diff, + } + : {}; + return { + ...acc, + ...nonDefaultStyles, + }; + }, {} ); const getStylesEntries = ( stylesString ) => stylesString @@ -52,6 +67,7 @@ const getStylesEntries = ( stylesString ) => .filter( identity ) .map( ( styles ) => { const [ key, values ] = styles.trim().split( ':' ); + if ( key === 'color' ) return [ key, values ]; const valuesArr = values && values @@ -69,17 +85,22 @@ const getStylesEntries = ( stylesString ) => const defaultStyles = "color: rgb(40, 48, 61); font-family: -apple-system, system-ui, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif; font-size: 20px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: pre-wrap; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(209, 228, 221); text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;"; -const toString = ( arr ) => - arr.map( ( pair ) => pair.join( ':' ) ).join( '; ' ); +const fromObjToString = ( obj ) => + Object.entries( obj ) + .map( ( pair ) => pair.join( ':' ) ) + .join( '; ' ); const sanitize = ( x ) => x; -const removeDefaultValues = ( html ) => { - const entriesForDefaultStyles = getStylesEntries( defaultStyles ); - const [ htmlPart0, restHtml ] = html.split( 'style=' ); - const [ , htmlStyles, htmlPart2 ] = restHtml.split( `"` ); - const entriesForCurrentStyles = getStylesEntries( htmlStyles ); - const diff = difference( entriesForDefaultStyles, entriesForCurrentStyles ); - return htmlPart0 + 'style="' + toString( diff ) + '"' + htmlPart2; +const removeDefaultStyles = ( html ) => { + const defaultStylesEntries = getStylesEntries( defaultStyles ); + const [ htmlPart0, restHtml ] = html.split( 'style="' ); + const [ htmlStyles, htmlPart2 ] = restHtml.split( `"` ); + const stylesEntries = getStylesEntries( htmlStyles ); + const diff = getNonDefaultStyles( { + defaultStylesEntries, + stylesEntries, + } ); + return htmlPart0 + 'style="' + fromObjToString( diff ) + '"' + htmlPart2; }; /** * Filters HTML to only contain phrasing content. @@ -90,13 +111,12 @@ const removeDefaultValues = ( html ) => { * @return {string} HTML only containing phrasing content. */ function filterInlineHTML( HTML, preserveWhiteSpace ) { + HTML = sanitize( removeDefaultStyles( HTML ) ); HTML = deepFilterHTML( HTML, [ googleDocsUIDRemover, phrasingContentReducer, commentRemover, ] ); - - HTML = sanitize( removeDefaultValues( HTML ) ); // HTML = removeInvalidHTML( HTML, getPhrasingContentSchema( 'paste' ), { // inline: true, // } ); From b5942698b56e19198c550d92e0afdb9e2f2d59f4 Mon Sep 17 00:00:00 2001 From: grzegorz_marzencki Date: Thu, 10 Dec 2020 11:57:40 +0100 Subject: [PATCH 08/21] parser fix --- .../src/api/raw-handling/paste-handler.js | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/packages/blocks/src/api/raw-handling/paste-handler.js b/packages/blocks/src/api/raw-handling/paste-handler.js index 63d3e29073a317..9f3566cfdd10f2 100644 --- a/packages/blocks/src/api/raw-handling/paste-handler.js +++ b/packages/blocks/src/api/raw-handling/paste-handler.js @@ -49,7 +49,7 @@ const getNonDefaultStyles = ( { defaultStylesEntries, stylesEntries } ) => const defaultValues = ( defaultStylesEntries.find( ( [ defaultKey ] ) => defaultKey === key ) || [] )[ 1 ]; - const diff = difference( defaultValues, values ); + const diff = difference( values, defaultValues ); const nonDefaultStyles = diff.length ? { [ key ]: diff, @@ -61,27 +61,25 @@ const getNonDefaultStyles = ( { defaultStylesEntries, stylesEntries } ) => }; }, {} ); -const getStylesEntries = ( stylesString ) => - stylesString +const getStylesEntries = ( stylesString ) => { + const stylesArr = stylesString + .replace( /\(.*?\)/g, ( match ) => match.replace( / /g, '_' ) ) + .split( '"' ) + .join( `'` ) .split( ';' ) - .filter( identity ) - .map( ( styles ) => { - const [ key, values ] = styles.trim().split( ':' ); - if ( key === 'color' ) return [ key, values ]; - const valuesArr = - values && - values - .split( `'` ) - .join( '' ) - .split( '"' ) - .join( '"' ) - .split( ', ' ) - .map( ( w ) => w.trim() ) - .join( '' ); + .filter( ( x ) => x ); + console.log( stylesArr ); + return stylesArr + .map( ( styles ) => { + const [ key, values = [] ] = styles.trim().split( ':' ); + console.log( values ); + if ( key === 'color' ) return [ key, [ values ] ]; + const valuesArr = values.split( ', ' ).map( ( w ) => w.trim() ); return [ key, valuesArr ]; - } ); - + } ) + .replace( /\(.*?\)/g, ( match ) => match.replace( /_/g, '' ) ); +}; const defaultStyles = "color: rgb(40, 48, 61); font-family: -apple-system, system-ui, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif; font-size: 20px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; white-space: pre-wrap; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(209, 228, 221); text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;"; @@ -100,7 +98,8 @@ const removeDefaultStyles = ( html ) => { defaultStylesEntries, stylesEntries, } ); - return htmlPart0 + 'style="' + fromObjToString( diff ) + '"' + htmlPart2; + const styles = htmlPart0 + 'style="' + fromObjToString( diff ) + '"' + htmlPart2; + return styles; }; /** * Filters HTML to only contain phrasing content. From cb2ee7bc148772db71d15e29946a6717ba7495fd Mon Sep 17 00:00:00 2001 From: grzegorz_marzencki Date: Thu, 10 Dec 2020 11:58:04 +0100 Subject: [PATCH 09/21] parser fix --- packages/blocks/src/api/raw-handling/paste-handler.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/blocks/src/api/raw-handling/paste-handler.js b/packages/blocks/src/api/raw-handling/paste-handler.js index 9f3566cfdd10f2..ba74cf264d2aa8 100644 --- a/packages/blocks/src/api/raw-handling/paste-handler.js +++ b/packages/blocks/src/api/raw-handling/paste-handler.js @@ -98,7 +98,8 @@ const removeDefaultStyles = ( html ) => { defaultStylesEntries, stylesEntries, } ); - const styles = htmlPart0 + 'style="' + fromObjToString( diff ) + '"' + htmlPart2; + const styles = + htmlPart0 + 'style="' + fromObjToString( diff ) + '"' + htmlPart2; return styles; }; /** From c360ef8842e4794c0cf033de485fe8d38003829b Mon Sep 17 00:00:00 2001 From: grzegorz_marzencki Date: Thu, 10 Dec 2020 14:53:43 +0100 Subject: [PATCH 10/21] children added --- .../blocks/src/api/raw-handling/paste-handler.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/blocks/src/api/raw-handling/paste-handler.js b/packages/blocks/src/api/raw-handling/paste-handler.js index ba74cf264d2aa8..b9855ca5c7745b 100644 --- a/packages/blocks/src/api/raw-handling/paste-handler.js +++ b/packages/blocks/src/api/raw-handling/paste-handler.js @@ -90,17 +90,20 @@ const fromObjToString = ( obj ) => const sanitize = ( x ) => x; const removeDefaultStyles = ( html ) => { + const container = document.createElement( 'div' ); + container.innerHTML = html; + const elementStyles = container.firstElementChild.style.cssText; const defaultStylesEntries = getStylesEntries( defaultStyles ); - const [ htmlPart0, restHtml ] = html.split( 'style="' ); - const [ htmlStyles, htmlPart2 ] = restHtml.split( `"` ); - const stylesEntries = getStylesEntries( htmlStyles ); + const stylesEntries = getStylesEntries( elementStyles ); const diff = getNonDefaultStyles( { defaultStylesEntries, stylesEntries, } ); - const styles = - htmlPart0 + 'style="' + fromObjToString( diff ) + '"' + htmlPart2; - return styles; + const nonDefaultStyles = fromObjToString( diff ); + for ( const child of container.children ) { + child.style = nonDefaultStyles; + } + return container.children.map( ( child ) => child.value ); }; /** * Filters HTML to only contain phrasing content. From 52ea00f63f8af7bff90afa476807d3fd6ff304d7 Mon Sep 17 00:00:00 2001 From: grzegorz_marzencki Date: Sat, 5 Dec 2020 16:53:01 +0100 Subject: [PATCH 11/21] when an element with a tooltip is disabled, add additional layer which catch events --- packages/block-library/src/code/edit.js | 5 +- packages/block-library/src/code/save.js | 4 + packages/components/src/tooltip/index.js | 92 +++++++++++++++++----- packages/components/src/tooltip/style.scss | 14 ++++ 4 files changed, 93 insertions(+), 22 deletions(-) diff --git a/packages/block-library/src/code/edit.js b/packages/block-library/src/code/edit.js index 4153b14d753cdc..ce8bd3ffc997df 100644 --- a/packages/block-library/src/code/edit.js +++ b/packages/block-library/src/code/edit.js @@ -3,11 +3,15 @@ */ import { __ } from '@wordpress/i18n'; import { RichText, useBlockProps } from '@wordpress/block-editor'; +import { Button, Tooltip } from '@wordpress/components'; export default function CodeEdit( { attributes, setAttributes, onRemove } ) { const blockProps = useBlockProps(); return (
+			
+				
+			
 			
 		
); diff --git a/packages/block-library/src/code/save.js b/packages/block-library/src/code/save.js index 7dd355f3855a86..5b6f00d0e53b3f 100644 --- a/packages/block-library/src/code/save.js +++ b/packages/block-library/src/code/save.js @@ -7,10 +7,14 @@ import { RichText, useBlockProps } from '@wordpress/block-editor'; * Internal dependencies */ import { escape } from './utils'; +import { Button, Tooltip } from '@wordpress/components'; export default function save( { attributes } ) { return (
+			
+				
+			
 			;
+
+const getDisabledElement = ( { eventHandlers, child, childrenWithPopover } ) =>
+	cloneElement(
+		
+			{ cloneElement( eventCatcher, eventHandlers ) }
+			{ cloneElement( child, {
+				children: childrenWithPopover,
+			} ) }
+			,
+		,
+		eventHandlers
+	);
+
+const getRegularElement = ( { child, eventHandlers, childrenWithPopover } ) =>
+	cloneElement( child, {
+		...eventHandlers,
+		children: childrenWithPopover,
+	} );
+
+const addPopoverToGrandchildren = ( {
+	grandchildren,
+	isOver,
+	position,
+	text,
+	shortcut,
+} ) =>
+	concatChildren(
+		grandchildren,
+		isOver && (
+			
+		)
+	);
+
 const emitToChild = ( children, eventName, event ) => {
 	if ( Children.count( children ) !== 1 ) {
 		return;
@@ -136,33 +183,36 @@ function Tooltip( { children, position, text, shortcut } ) {
 		return children;
 	}
 
-	const child = Children.only( children );
-	return cloneElement( child, {
+	const eventHandlers = {
 		onMouseEnter: createToggleIsOver( 'onMouseEnter', true ),
 		onMouseLeave: createToggleIsOver( 'onMouseLeave' ),
 		onClick: createToggleIsOver( 'onClick' ),
 		onFocus: createToggleIsOver( 'onFocus' ),
 		onBlur: createToggleIsOver( 'onBlur' ),
 		onMouseDown: createMouseEvent( 'mouseDown' ),
-		children: concatChildren(
-			child.props.children,
-			isOver && (
-				
-			)
-		),
+	};
+
+	const child = Children.only( children );
+	const { children: grandchildren, disabled } = child.props;
+	const getElementWithPopover = disabled
+		? getDisabledElement
+		: getRegularElement;
+
+	const popoverData = {
+		isOver,
+		position,
+		text,
+		shortcut,
+	};
+	const childrenWithPopover = addPopoverToGrandchildren( {
+		grandchildren,
+		...popoverData,
+	} );
+
+	return getElementWithPopover( {
+		child,
+		eventHandlers,
+		childrenWithPopover,
 	} );
 }
 
diff --git a/packages/components/src/tooltip/style.scss b/packages/components/src/tooltip/style.scss
index a7f4b09a1ffa74..360e6c7918054f 100644
--- a/packages/components/src/tooltip/style.scss
+++ b/packages/components/src/tooltip/style.scss
@@ -26,3 +26,17 @@
 	display: inline-block;
 	margin-left: $grid-unit-10;
 }
+
+.disabled-element-wrapper {
+	position: relative;
+	.event-catcher {
+		z-index:9999;
+		position: absolute;
+		width: 100%;
+		height: 100%;
+		top: 0;
+		left: 0;
+		bottom: 0;
+		right: 0;
+	}
+}

From 264d06e5b8c1760d992b3dfac557cf4624ec2964 Mon Sep 17 00:00:00 2001
From: grzegorz_marzencki 
Date: Sat, 5 Dec 2020 21:12:05 +0100
Subject: [PATCH 12/21] tests updated

---
 packages/components/src/tooltip/test/index.js | 39 +++++++++----------
 1 file changed, 18 insertions(+), 21 deletions(-)

diff --git a/packages/components/src/tooltip/test/index.js b/packages/components/src/tooltip/test/index.js
index 919dc70e3b9b30..40908e156dbf85 100644
--- a/packages/components/src/tooltip/test/index.js
+++ b/packages/components/src/tooltip/test/index.js
@@ -152,33 +152,30 @@ describe( 'Tooltip', () => {
 			}, TOOLTIP_DELAY );
 		} );
 
-		it( 'should ignore mouseenter on disabled elements', () => {
-			// Mount: Issues with using `setState` asynchronously with shallow-
-			// rendered components: https://github.com/airbnb/enzyme/issues/450
-			const originalMouseEnter = jest.fn();
+		it( 'should show tooltip when an element is disabled', () => {
 			const wrapper = mount(
-				
-					
+				
+					
 				
 			);
-
-			wrapper.find( 'button' ).simulate( 'mouseenter', {
-				// Enzyme does not accurately emulate event targets
-				// See: https://github.com/airbnb/enzyme/issues/218
-				currentTarget: wrapper.find( 'button' ).getDOMNode(),
-				target: wrapper.find( 'button > span' ).getDOMNode(),
+			const button = wrapper.find( 'button[disabled]' );
+			const buttonNode = button.at( 0 ).getDOMNode();
+			const buttonRect = buttonNode.getBoundingClientRect();
+			const eventCatcher = wrapper.find( '.event-catcher' );
+			const eventCatcherNode = eventCatcher.at( 0 ).getDOMNode();
+			const eventCatcherRect = eventCatcherNode.getBoundingClientRect();
+			expect( buttonRect ).toEqual( eventCatcherRect );
+
+			eventCatcher.simulate( 'mouseenter', {
+				type: 'mouseenter',
+				currentTarget: {},
 			} );
 
-			expect( originalMouseEnter ).not.toHaveBeenCalled();
 
-			const popover = wrapper.find( 'Popover' );
-			expect( popover ).toHaveLength( 0 );
+			setTimeout( () => {
+				const popover = wrapper.find( 'Popover' );
+				expect( popover ).toHaveLength( 1 );
+			}, TOOLTIP_DELAY );
 		} );
 
 		it( 'should cancel pending setIsOver on mouseleave', () => {

From b3bc760f7bd56f3b8a34e9d9d61356e39dc60d02 Mon Sep 17 00:00:00 2001
From: grzegorz_marzencki 
Date: Mon, 7 Dec 2020 14:37:19 +0100
Subject: [PATCH 13/21] changes from inline styles to css classes

---
 packages/base-styles/_z-index.scss         |  5 ++++-
 packages/components/src/tooltip/style.scss | 23 +++++++++++-----------
 2 files changed, 16 insertions(+), 12 deletions(-)

diff --git a/packages/base-styles/_z-index.scss b/packages/base-styles/_z-index.scss
index 14c40bdeebd860..7e6ac027cc8c8f 100644
--- a/packages/base-styles/_z-index.scss
+++ b/packages/base-styles/_z-index.scss
@@ -144,12 +144,15 @@ $z-layers: (
 	// Make sure block manager sticky category titles appear above the options
 	".edit-post-manage-blocks-modal__category-title": 1,
 
+	// Needs to be higher than any other element as this is an overlay to catch all events
+	".components-tooltip .event-catcher": 100002,
+
 	// Needs to appear bellow other color circular picker related UI elements.
 	".components-circular-option-picker__option-wrapper::before": -1,
 
 	".components-circular-option-picker__option.is-pressed": 1,
 	// Needs to be higher than .components-circular-option-picker__option.is-pressed.
-	".components-circular-option-picker__option.is-pressed + svg": 2
+	".components-circular-option-picker__option.is-pressed + svg": 2,
 );
 
 @function z-index( $key ) {
diff --git a/packages/components/src/tooltip/style.scss b/packages/components/src/tooltip/style.scss
index 360e6c7918054f..386df33a6770e7 100644
--- a/packages/components/src/tooltip/style.scss
+++ b/packages/components/src/tooltip/style.scss
@@ -27,16 +27,17 @@
 	margin-left: $grid-unit-10;
 }
 
-.disabled-element-wrapper {
+.components-tooltip .event-catcher {
+	z-index: z-index(".components-tooltip .event-catcher");
+	position: absolute;
+	width: 100%;
+	height: 100%;
+	top: 0;
+	left: 0;
+	bottom: 0;
+	right: 0;
+}
+
+.components-tooltip .disabled-element-wrapper {
 	position: relative;
-	.event-catcher {
-		z-index:9999;
-		position: absolute;
-		width: 100%;
-		height: 100%;
-		top: 0;
-		left: 0;
-		bottom: 0;
-		right: 0;
-	}
 }

From 2b803e9e96092ff3001cf73896def433a181b1e5 Mon Sep 17 00:00:00 2001
From: grzegorz_marzencki 
Date: Wed, 13 Jan 2021 13:56:48 +0100
Subject: [PATCH 14/21] fix in styling - .event-catcher class was not properly
 addressed in scss file

---
 packages/components/src/tooltip/style.scss | 23 +++++++++++-----------
 1 file changed, 11 insertions(+), 12 deletions(-)

diff --git a/packages/components/src/tooltip/style.scss b/packages/components/src/tooltip/style.scss
index 386df33a6770e7..df3cfbcf4826a8 100644
--- a/packages/components/src/tooltip/style.scss
+++ b/packages/components/src/tooltip/style.scss
@@ -27,17 +27,16 @@
 	margin-left: $grid-unit-10;
 }
 
-.components-tooltip .event-catcher {
-	z-index: z-index(".components-tooltip .event-catcher");
-	position: absolute;
-	width: 100%;
-	height: 100%;
-	top: 0;
-	left: 0;
-	bottom: 0;
-	right: 0;
-}
-
-.components-tooltip .disabled-element-wrapper {
+.disabled-element-wrapper {
 	position: relative;
+	.event-catcher {
+		z-index: z-index(".components-tooltip .event-catcher");
+		position: absolute;
+		width: 100%;
+		height: 100%;
+		top: 0;
+		left: 0;
+		bottom: 0;
+		right: 0;
+	}
 }

From 589553f259adf6c8b1dada2da0860da30ed77dcd Mon Sep 17 00:00:00 2001
From: grzegorz_marzencki 
Date: Thu, 14 Jan 2021 08:06:38 +0100
Subject: [PATCH 15/21] change back code block

---
 packages/block-library/src/code/edit.js | 5 +----
 packages/block-library/src/code/save.js | 4 ----
 2 files changed, 1 insertion(+), 8 deletions(-)

diff --git a/packages/block-library/src/code/edit.js b/packages/block-library/src/code/edit.js
index ce8bd3ffc997df..4153b14d753cdc 100644
--- a/packages/block-library/src/code/edit.js
+++ b/packages/block-library/src/code/edit.js
@@ -3,15 +3,11 @@
  */
 import { __ } from '@wordpress/i18n';
 import { RichText, useBlockProps } from '@wordpress/block-editor';
-import { Button, Tooltip } from '@wordpress/components';
 
 export default function CodeEdit( { attributes, setAttributes, onRemove } ) {
 	const blockProps = useBlockProps();
 	return (
 		
-			
-				
-			
 			
 		
); diff --git a/packages/block-library/src/code/save.js b/packages/block-library/src/code/save.js index 5b6f00d0e53b3f..7dd355f3855a86 100644 --- a/packages/block-library/src/code/save.js +++ b/packages/block-library/src/code/save.js @@ -7,14 +7,10 @@ import { RichText, useBlockProps } from '@wordpress/block-editor'; * Internal dependencies */ import { escape } from './utils'; -import { Button, Tooltip } from '@wordpress/components'; export default function save( { attributes } ) { return (
-			
-				
-			
 			
Date: Thu, 14 Jan 2021 08:12:48 +0100
Subject: [PATCH 16/21] revert back pharsing-content-reducer

---
 .../raw-handling/phrasing-content-reducer.js  | 113 ++++++++----------
 1 file changed, 47 insertions(+), 66 deletions(-)

diff --git a/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js b/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js
index 305219c9161b05..cd1e45d077e4d3 100644
--- a/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js
+++ b/packages/blocks/src/api/raw-handling/phrasing-content-reducer.js
@@ -1,79 +1,60 @@
 /**
  * External dependencies
  */
-import { identity, includes } from 'lodash';
+import { includes } from 'lodash';
 
 /**
  * WordPress dependencies
  */
 import { wrap, replaceTag } from '@wordpress/dom';
 
-const getSemanticTags = ( { node } ) => {
-	const {
-		fontWeight,
-		fontStyle,
-		textDecorationLine,
-		textDecoration,
-		verticalAlign,
-	} = node.style;
-	const tags = [];
-	if ( fontWeight === 'bold' || fontWeight === '700' ) {
-		tags.push( 'strong' );
-	}
-
-	if ( fontStyle === 'italic' ) {
-		tags.push( 'em' );
-	}
-
-	// Some DOM implementations (Safari, JSDom) don't support
-	// style.textDecorationLine, so we check style.textDecoration as a
-	// fallback.
-	if (
-		textDecorationLine === 'line-through' ||
-		includes( textDecoration, 'line-through' )
-	) {
-		tags.push( 's' );
-	}
-
-	if ( verticalAlign === 'super' ) {
-		tags.push( 'sup' );
-	} else if ( verticalAlign === 'sub' ) {
-		tags.push( 'sub' );
-	}
-	return tags;
-};
-
-const handleSpan = ( { node, doc } ) => {
-	const semanticTagsToWrap = getSemanticTags( node );
-	semanticTagsToWrap.forEach( ( tag ) =>
-		wrap( doc.createElement( tag ), node )
-	);
-};
-const handleBold = ( { node } ) => replaceTag( node, 'strong' );
-const handleItalic = ( { node } ) => replaceTag( node, 'italic' );
-const handleAnchor = ( { node } ) => {
-	// In jsdom-jscore, 'node.target' can be null.
-	// TODO: Explore fixing this by patching jsdom-jscore.
-	if ( node.target && node.target.toLowerCase() === '_blank' ) {
-		node.rel = 'noreferrer noopener';
-	} else {
-		node.removeAttribute( 'target' );
-		node.removeAttribute( 'rel' );
-	}
-};
-
-const elementNameToHandlerMapper = {
-	span: handleSpan,
-	B: handleBold,
-	I: handleItalic,
-	A: handleAnchor,
-};
-
 export default function phrasingContentReducer( node, doc ) {
 	// In jsdom-jscore, 'node.style' can be null.
 	// TODO: Explore fixing this by patching jsdom-jscore.
-	const elementType = node.nodeName;
-	const elementHandler =
-		elementNameToHandlerMapper[ elementType ] || identity;
-	elementHandler( { node, doc } );
+	if ( node.nodeName === 'SPAN' && node.style ) {
+		const {
+			fontWeight,
+			fontStyle,
+			textDecorationLine,
+			textDecoration,
+			verticalAlign,
+		} = node.style;
+
+		if ( fontWeight === 'bold' || fontWeight === '700' ) {
+			wrap( doc.createElement( 'strong' ), node );
+		}
+
+		if ( fontStyle === 'italic' ) {
+			wrap( doc.createElement( 'em' ), node );
+		}
+
+		// Some DOM implementations (Safari, JSDom) don't support
+		// style.textDecorationLine, so we check style.textDecoration as a
+		// fallback.
+		if (
+			textDecorationLine === 'line-through' ||
+			includes( textDecoration, 'line-through' )
+		) {
+			wrap( doc.createElement( 's' ), node );
+		}
+
+		if ( verticalAlign === 'super' ) {
+			wrap( doc.createElement( 'sup' ), node );
+		} else if ( verticalAlign === 'sub' ) {
+			wrap( doc.createElement( 'sub' ), node );
+		}
+	} else if ( node.nodeName === 'B' ) {
+		node = replaceTag( node, 'strong' );
+	} else if ( node.nodeName === 'I' ) {
+		node = replaceTag( node, 'em' );
+	} else if ( node.nodeName === 'A' ) {
+		// In jsdom-jscore, 'node.target' can be null.
+		// TODO: Explore fixing this by patching jsdom-jscore.
+		if ( node.target && node.target.toLowerCase() === '_blank' ) {
+			node.rel = 'noreferrer noopener';
+		} else {
+			node.removeAttribute( 'target' );
+			node.removeAttribute( 'rel' );
+		}
+	}
 }

From 533c37daeac90aa67702c739f7d9cb248cb79396 Mon Sep 17 00:00:00 2001
From: grzegorz_marzencki 
Date: Thu, 14 Jan 2021 08:16:59 +0100
Subject: [PATCH 17/21] revert back pharsing-content.js with readme

---
 packages/dom/README.md               |  8 -------
 packages/dom/src/phrasing-content.js | 34 ----------------------------
 2 files changed, 42 deletions(-)

diff --git a/packages/dom/README.md b/packages/dom/README.md
index 5a55a50c08ca0e..c26a0ae99da445 100644
--- a/packages/dom/README.md
+++ b/packages/dom/README.md
@@ -14,10 +14,6 @@ npm install @wordpress/dom --save
 
 
 
-# **alterSpecialTags**
-
-Undocumented declaration.
-
 # **computeCaretRect**
 
 Get the rectangle for the selection in a container.
@@ -308,10 +304,6 @@ _Returns_
 
 -   `string`: The cleaned up HTML.
 
-# **removeSpecialTags**
-
-Undocumented declaration.
-
 # **replace**
 
 Given two DOM nodes, replaces the former with the latter in the DOM.
diff --git a/packages/dom/src/phrasing-content.js b/packages/dom/src/phrasing-content.js
index facf40da2e600d..f9274af7832f1b 100644
--- a/packages/dom/src/phrasing-content.js
+++ b/packages/dom/src/phrasing-content.js
@@ -9,15 +9,6 @@ import { omit, without } from 'lodash';
  * @see https://www.w3.org/TR/2011/WD-html5-20110525/content-models.html#phrasing-content-0
  */
 
-/**
- * @type {{coloured: {attributes: [string]}}}
- * Some spans will carry additional styling that needs to be preserved.
- * In order not to loose information about styling (as all spans are removed) a special tags are introduced, wich later will be turned back to span.
- */
-const specialTags = {
-	coloured: { attributes: [ 'style' ], originalTag: 'span' },
-};
-
 /**
  * All text-level semantic elements.
  *
@@ -56,33 +47,8 @@ const textContentSchema = {
 	bdo: { attributes: [ 'dir' ] },
 	wbr: {},
 	'#text': {},
-	...specialTags,
 };
 
-export const alterSpecialTags = ( HTML ) =>
-	Object.entries( specialTags ).reduce( ( acc, [ tag, { originalTag } ] ) => {
-		const openingTagRegExp = new RegExp( `<${ tag }`, 'g' );
-		const closingTagRegExp = new RegExp( `
-	Object.entries( specialTags ).reduce( ( acc, [ tag ] ) => {
-		const openingTagRegExp = new RegExp( `<${ tag }`, 'g' );
-		const closingTagRegExp = new RegExp( ` em > strong.
 // Impossible: strong > strong.

From 65375b72577b2ba376ed9475499501e101421580 Mon Sep 17 00:00:00 2001
From: grzegorz_marzencki 
Date: Thu, 14 Jan 2021 08:23:08 +0100
Subject: [PATCH 18/21] shortcode reverted

---
 packages/shortcode/src/index.js | 36 ++++++++++++++-------------------
 1 file changed, 15 insertions(+), 21 deletions(-)

diff --git a/packages/shortcode/src/index.js b/packages/shortcode/src/index.js
index 43f17aa93bd9e8..e1e6a62e52b46d 100644
--- a/packages/shortcode/src/index.js
+++ b/packages/shortcode/src/index.js
@@ -90,29 +90,23 @@ export function next( tag, text, index = 0 ) {
  * @return {string} Text with shortcodes replaced.
  */
 export function replace( tag, text, callback ) {
-	return text.replace( regexp( tag ), function (
-		match,
-		left,
-		$3,
-		attrs,
-		slash,
-		content,
-		closing,
-		right
-	) {
-		// If both extra brackets exist, the shortcode has been properly
-		// escaped.
-		if ( left === '[' && right === ']' ) {
-			return match;
-		}
+	return text.replace(
+		regexp( tag ),
+		function ( match, left, $3, attrs, slash, content, closing, right ) {
+			// If both extra brackets exist, the shortcode has been properly
+			// escaped.
+			if ( left === '[' && right === ']' ) {
+				return match;
+			}
 
-		// Create the match object and pass it through the callback.
-		const result = callback( fromMatch( arguments ) );
+			// Create the match object and pass it through the callback.
+			const result = callback( fromMatch( arguments ) );
 
-		// Make sure to return any of the extra brackets if they weren't used to
-		// escape the shortcode.
-		return result || result === '' ? left + result + right : match;
-	} );
+			// Make sure to return any of the extra brackets if they weren't used to
+			// escape the shortcode.
+			return result || result === '' ? left + result + right : match;
+		}
+	);
 }
 
 /**

From 95ed733a10bec97c553cf1ce1ef1e247fa8bc951 Mon Sep 17 00:00:00 2001
From: grzegorz_marzencki 
Date: Fri, 15 Jan 2021 13:42:39 +0100
Subject: [PATCH 19/21] linter fix

---
 packages/components/src/tooltip/index.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/components/src/tooltip/index.js b/packages/components/src/tooltip/index.js
index 294b0df6e93772..268febbfd07476 100644
--- a/packages/components/src/tooltip/index.js
+++ b/packages/components/src/tooltip/index.js
@@ -151,7 +151,7 @@ function Tooltip( { children, position, text, shortcut } ) {
 			}
 
 			// Needed in case unsetting is over while delayed set pending, i.e.
-			// quickly blur/mouseleave before delayedSetIsOver is called
+			// quickly blur/mouseleave before delayedSetIsOver is cgualled
 			delayedSetIsOver.cancel();
 
 			const _isOver = includes( [ 'focus', 'mouseenter' ], event.type );

From d4055332c11253dcb4dc647cc43820dd2f9b4ce9 Mon Sep 17 00:00:00 2001
From: grzegorz_marzencki 
Date: Fri, 15 Jan 2021 14:38:58 +0100
Subject: [PATCH 20/21] linter fix

---
 packages/components/src/tooltip/test/index.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/packages/components/src/tooltip/test/index.js b/packages/components/src/tooltip/test/index.js
index 40908e156dbf85..aebf7466220897 100644
--- a/packages/components/src/tooltip/test/index.js
+++ b/packages/components/src/tooltip/test/index.js
@@ -171,7 +171,6 @@ describe( 'Tooltip', () => {
 				currentTarget: {},
 			} );
 
-
 			setTimeout( () => {
 				const popover = wrapper.find( 'Popover' );
 				expect( popover ).toHaveLength( 1 );

From 4234963dad6e1647f30eb02044c4ae6a8c60de99 Mon Sep 17 00:00:00 2001
From: Grzegorz 
Date: Mon, 18 Jan 2021 16:40:44 +0100
Subject: [PATCH 21/21] fix typo

---
 packages/components/src/tooltip/index.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/components/src/tooltip/index.js b/packages/components/src/tooltip/index.js
index 268febbfd07476..294b0df6e93772 100644
--- a/packages/components/src/tooltip/index.js
+++ b/packages/components/src/tooltip/index.js
@@ -151,7 +151,7 @@ function Tooltip( { children, position, text, shortcut } ) {
 			}
 
 			// Needed in case unsetting is over while delayed set pending, i.e.
-			// quickly blur/mouseleave before delayedSetIsOver is cgualled
+			// quickly blur/mouseleave before delayedSetIsOver is called
 			delayedSetIsOver.cancel();
 
 			const _isOver = includes( [ 'focus', 'mouseenter' ], event.type );