From c34ca0ad02cbb5fe844c810440e80dbdff45637a Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Thu, 14 Dec 2023 12:36:17 +1000 Subject: [PATCH] Update JS processing of block style variations --- .../test/use-global-styles-output.js | 121 +++++++++++++++++ .../global-styles/use-global-styles-output.js | 122 +++++++++++++++--- .../src/components/global-styles/utils.js | 4 + 3 files changed, 232 insertions(+), 15 deletions(-) diff --git a/packages/block-editor/src/components/global-styles/test/use-global-styles-output.js b/packages/block-editor/src/components/global-styles/test/use-global-styles-output.js index b05381a8325b06..bfdf48eb3aaa6e 100644 --- a/packages/block-editor/src/components/global-styles/test/use-global-styles-output.js +++ b/packages/block-editor/src/components/global-styles/test/use-global-styles-output.js @@ -28,6 +28,35 @@ describe( 'global styles renderer', () => { text: 'red', }, blocks: { + 'core/group': { + color: { + background: 'linen', + }, + variations: { + foo: { + color: 'aliceblue', + blocks: { + 'core/heading': { + typography: { + fontSize: '3em', + }, + }, + }, + elements: { + link: { + color: { + text: 'darkcyan', + }, + ':hover': { + color: { + text: 'darkturqoise', + }, + }, + }, + }, + }, + }, + }, 'core/heading': { color: { background: 'blue', @@ -89,6 +118,12 @@ describe( 'global styles renderer', () => { }, }; const blockSelectors = { + 'core/group': { + selector: '.my-group', + styleVariationSelectors: { + foo: '.is-style-foo.my-group', + }, + }, 'core/heading': { selector: '.my-heading1, .my-heading2', }, @@ -129,6 +164,44 @@ describe( 'global styles renderer', () => { }, selector: ELEMENTS.link, }, + { + selector: + '.is-style-foo.my-group .my-heading1, .is-style-foo.my-group .my-heading2', + styles: { + typography: { + fontSize: '3em', + }, + }, + }, + { + selector: '.is-style-foo.my-group a', + styles: { + color: { + text: 'darkcyan', + }, + ':hover': { + color: { + text: 'darkturqoise', + }, + }, + }, + }, + { + selector: '.my-group', + styles: { + color: { + background: 'linen', + }, + variations: { + foo: { + color: 'aliceblue', + }, + }, + }, + styleVariationSelectors: { + foo: '.is-style-foo.my-group', + }, + }, { styles: { color: { @@ -549,6 +622,44 @@ describe( 'global styles renderer', () => { }, }, }, + 'core/group': { + variations: { + bar: { + color: { + background: 'midnightblue', + text: 'lightskyblue', + }, + blocks: { + 'core/heading': { + color: { + text: 'royalblue', + }, + }, + 'core/image': { + border: { + color: 'darkcyan', + style: 'dashed', + width: '5px', + }, + }, + }, + elements: { + h2: { + color: { + text: 'turquoise', + }, + }, + button: { + color: { + background: 'midnightblue', + text: 'powderblue', + }, + ':hover': {}, + }, + }, + }, + }, + }, }, }, }; @@ -567,11 +678,21 @@ describe( 'global styles renderer', () => { foo: '.is-style-foo.wp-image', }, }, + 'core/group': { + selector: '.wp-group', + styleVariationSelectors: { + bar: '.is-style-bar.wp-group', + }, + }, + 'core/heading': { + selector: '.wp-heading', + }, }; expect( toStyles( Object.freeze( tree ), blockSelectors ) ).toEqual( 'body {margin: 0;}body .is-layout-flow > .alignleft { float: left; margin-inline-start: 0; margin-inline-end: 2em; }body .is-layout-flow > .alignright { float: right; margin-inline-start: 2em; margin-inline-end: 0; }body .is-layout-flow > .aligncenter { margin-left: auto !important; margin-right: auto !important; }body .is-layout-constrained > .alignleft { float: left; margin-inline-start: 0; margin-inline-end: 2em; }body .is-layout-constrained > .alignright { float: right; margin-inline-start: 2em; margin-inline-end: 0; }body .is-layout-constrained > .aligncenter { margin-left: auto !important; margin-right: auto !important; }body .is-layout-constrained > :where(:not(.alignleft):not(.alignright):not(.alignfull)) { max-width: var(--wp--style--global--content-size); margin-left: auto !important; margin-right: auto !important; }body .is-layout-constrained > .alignwide { max-width: var(--wp--style--global--wide-size); }body .is-layout-flex { display:flex; }body .is-layout-flex { flex-wrap: wrap; align-items: center; }body .is-layout-flex > * { margin: 0; }body .is-layout-grid { display:grid; }body .is-layout-grid > * { margin: 0; }' + '.is-style-foo.wp-image.wp-image-spacing{padding-top: 2px;}.is-style-foo.wp-image.wp-image-border-color{border-color: blue;}.is-style-foo.wp-image{color: blue;}' + + '.is-style-bar.wp-group .wp-heading{color: royalblue;}.is-style-bar.wp-group .wp-image-border-color{border-color: darkcyan;}.is-style-bar.wp-group .wp-image-border{border-style: dashed;border-width: 5px;}.is-style-bar.wp-group h2{color: turquoise;}.is-style-bar.wp-group .wp-element-button, .is-style-bar.wp-group .wp-block-button__link{color: powderblue;background-color: midnightblue;}.is-style-bar.wp-group{color: lightskyblue;background-color: midnightblue;}' + '.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }' ); } ); diff --git a/packages/block-editor/src/components/global-styles/use-global-styles-output.js b/packages/block-editor/src/components/global-styles/use-global-styles-output.js index 7e99eca355b52e..d0a364c9dd59c9 100644 --- a/packages/block-editor/src/components/global-styles/use-global-styles-output.js +++ b/packages/block-editor/src/components/global-styles/use-global-styles-output.js @@ -607,6 +607,33 @@ function pickStyleKeys( treeToPickFrom ) { return Object.fromEntries( clonedEntries ); } +function scopeFeatureSelectors( scope, selectors ) { + if ( ! scope || ! selectors ) { + return; + } + + const featureSelectors = JSON.parse( JSON.stringify( selectors ) ); + + Object.entries( selectors ).forEach( ( [ feature, selector ] ) => { + if ( typeof selector === 'string' ) { + featureSelectors[ feature ] = scopeSelector( scope, selector ); + } + + if ( typeof selector === 'object' ) { + Object.entries( selector ).forEach( + ( [ subfeature, subfeatureSelector ] ) => { + featureSelectors[ feature ][ subfeature ] = scopeSelector( + scope, + subfeatureSelector + ); + } + ); + } + } ); + + return featureSelectors; +} + export const getNodesWithStyles = ( tree, blockSelectors ) => { const nodes = []; @@ -639,14 +666,78 @@ export const getNodesWithStyles = ( tree, blockSelectors ) => { if ( node?.variations ) { const variations = {}; - Object.keys( node.variations ).forEach( ( variation ) => { - variations[ variation ] = pickStyleKeys( - node.variations[ variation ] - ); - } ); + + Object.entries( node.variations ).forEach( + ( [ variationName, variation ] ) => { + variations[ variationName ] = + pickStyleKeys( variation ); + const variationSelector = + blockSelectors[ blockName ].styleVariationSelectors[ + variationName + ]; + + // Process the variations inner block type styles. + Object.entries( variation.blocks ?? {} ).forEach( + ( [ + variationBlockName, + variationBlockStyles, + ] ) => { + const variationBlockSelector = scopeSelector( + variationSelector, + blockSelectors[ variationBlockName ] + .selector + ); + const variationDuotoneSelector = scopeSelector( + variationSelector, + blockSelectors[ variationBlockName ] + .duotoneSelector + ); + const variationFeatureSelectors = + scopeFeatureSelectors( + variationSelector, + blockSelectors[ variationBlockName ] + .featureSelectors + ); + + // TODO: Do we need to delay pushing these nodes so they come after the original block's node? + nodes.push( { + selector: variationBlockSelector, + duotoneSelector: variationDuotoneSelector, + featureSelectors: variationFeatureSelectors, + fallbackGapValue: + blockSelectors[ variationBlockName ] + .fallbackGapValue, + hasLayoutSupport: + blockSelectors[ variationBlockName ] + .hasLayoutSupport, + styles: pickStyleKeys( + variationBlockStyles + ), + } ); + } + ); + + // Process the variations inner element styles. + Object.entries( variation.elements ?? {} ).forEach( + ( [ element, elementStyles ] ) => { + if ( elementStyles && ELEMENTS[ element ] ) { + nodes.push( { + styles: elementStyles, + selector: scopeSelector( + variationSelector, + ELEMENTS[ element ] + ), + } ); + } + } + ); + } + ); + blockStyles.variations = variations; } - if ( blockStyles && blockSelectors?.[ blockName ]?.selector ) { + + if ( blockSelectors?.[ blockName ]?.selector ) { nodes.push( { duotoneSelector: blockSelectors[ blockName ].duotoneSelector, @@ -839,6 +930,7 @@ export const toStyles = ( ( [ styleVariationName, styleVariationSelector ] ) => { const styleVariations = styles?.variations?.[ styleVariationName ]; + if ( styleVariations ) { // If the block uses any custom selectors for block support, add those first. if ( featureSelectors ) { @@ -856,6 +948,7 @@ export const toStyles = ( baseSelector, styleVariationSelector ); + const rules = declarations.join( ';' ); ruleset += `${ cssSelector }{${ rules };}`; @@ -872,6 +965,7 @@ export const toStyles = ( useRootPaddingAlign, tree ); + if ( styleVariationDeclarations.length ) { ruleset += `${ styleVariationSelector }{${ styleVariationDeclarations.join( ';' @@ -1065,13 +1159,12 @@ export const getBlockSelectors = ( blockTypes, getBlockStyles ) => { const blockStyleVariations = getBlockStyles( name ); const styleVariationSelectors = {}; - if ( blockStyleVariations?.length ) { - blockStyleVariations.forEach( ( variation ) => { - const styleVariationSelector = `.is-style-${ variation.name }${ selector }`; - styleVariationSelectors[ variation.name ] = - styleVariationSelector; - } ); - } + + blockStyleVariations?.forEach( ( variation ) => { + const styleVariationSelector = `.is-style-${ variation.name }${ selector }`; + styleVariationSelectors[ variation.name ] = styleVariationSelector; + } ); + // For each block support feature add any custom selectors. const featureSelectors = getSelectorsConfig( blockType, selector ); @@ -1084,8 +1177,7 @@ export const getBlockSelectors = ( blockTypes, getBlockStyles ) => { hasLayoutSupport, name, selector, - styleVariationSelectors: Object.keys( styleVariationSelectors ) - .length + styleVariationSelectors: blockStyleVariations?.length ? styleVariationSelectors : undefined, }; diff --git a/packages/block-editor/src/components/global-styles/utils.js b/packages/block-editor/src/components/global-styles/utils.js index f4adb7a7903122..c2acc47b3ca5c8 100644 --- a/packages/block-editor/src/components/global-styles/utils.js +++ b/packages/block-editor/src/components/global-styles/utils.js @@ -380,6 +380,10 @@ export function getValueFromVariable( features, blockName, variable ) { * @return {string} Scoped selector. */ export function scopeSelector( scope, selector ) { + if ( ! scope || ! selector ) { + return selector; + } + const scopes = scope.split( ',' ); const selectors = selector.split( ',' );