Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Global styles: resolve dynamic refs #43249

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -756,5 +756,57 @@ describe( 'global styles renderer', () => {
'font-family: sans-serif',
] );
} );

it( 'Should resolve dynamic references', () => {
const blockStylesWithRef = {
color: {
background: {
ref: 'styles.color.text',
},
text: 'king-crimson',
},
};
const tree = {
styles: blockStylesWithRef,
};
expect(
getStylesDeclarations(
blockStylesWithRef,
'.wp-selector',
true,
tree
)
).toEqual( [
'background-color: king-crimson',
'color: king-crimson',
] );
} );

it( 'Should skip dynamic references that point to other dynamic references', () => {
const blockStylesWithRef = {
spacing: {
padding: {
ref: 'styles.spacing.margin',
},
margin: {
ref: 'styles.typography.letterSpacing',
},
},
typography: {
letterSpacing: '20px',
},
};
const tree = {
styles: blockStylesWithRef,
};
expect(
getStylesDeclarations(
blockStylesWithRef,
'.wp-selector',
false,
tree
)
).toEqual( [ 'margin: 20px', 'letter-spacing: 20px' ] );
} );
} );
} );
74 changes: 69 additions & 5 deletions packages/edit-site/src/components/global-styles/test/utils.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
/**
* Internal dependencies
*/
import { getPresetVariableFromValue, getValueFromVariable } from '../utils';
import {
getPresetVariableFromValue,
getValueFromVariable,
resolveDynamicRef,
} from '../utils';

describe( 'editor utils', () => {
const themeJson = {
Expand Down Expand Up @@ -163,7 +167,7 @@ describe( 'editor utils', () => {
expect( actual ).toBe( stylesWithRefs.styles.color.text );
} );

it( 'returns the originally provided value where value is dynamic reference and reference does not exist', () => {
it( 'returns the originally provided variable where value is dynamic reference and reference does not exist', () => {
const stylesWithRefs = {
...themeJson,
styles: {
Expand All @@ -178,10 +182,10 @@ describe( 'editor utils', () => {
ref: 'styles.color.text',
} );

expect( actual ).toBe( stylesWithRefs.styles.color.text );
expect( actual ).toEqual( '' );
} );

it( 'returns the originally provided value where value is dynamic reference', () => {
it( 'returns an empty string where value is dynamic reference', () => {
const stylesWithRefs = {
...themeJson,
styles: {
Expand All @@ -199,7 +203,67 @@ describe( 'editor utils', () => {
ref: 'styles.color.text',
} );

expect( actual ).toBe( stylesWithRefs.styles.color.text );
expect( actual ).toEqual( '' );
} );
} );
} );

describe( 'resolveDynamicRef', () => {
it( 'returns the resolved value', () => {
const tree = {
styles: {
typography: {
letterSpacing: '20px',
},
},
};
expect(
resolveDynamicRef(
{
ref: 'styles.typography.letterSpacing',
},
tree
)
).toEqual( '20px' );
} );

it( 'returns empty string if reference to another ref is found', () => {
const tree = {
styles: {
typography: {
letterSpacing: {
ref: 'styles.typography.fontSize',
},
},
},
};
expect(
resolveDynamicRef(
{
ref: 'styles.typography.letterSpacing',
},
tree
)
).toEqual( '' );
} );

it( 'returns the originally provided value where a value cannot be found', () => {
const tree = {
styles: {
typography: {
letterSpacing: '20px',
},
},
};
expect(
resolveDynamicRef(
{
ref: 'styles.spacing.margin',
},
tree
)
).toEqual( {
ref: 'styles.spacing.margin',
} );
} );
} );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ import {
/**
* Internal dependencies
*/
import { PRESET_METADATA, ROOT_BLOCK_SELECTOR, scopeSelector } from './utils';
import {
PRESET_METADATA,
ROOT_BLOCK_SELECTOR,
scopeSelector,
resolveDynamicRef,
} from './utils';
import { GlobalStylesContext } from './context';
import { useSetting } from './hooks';

Expand Down Expand Up @@ -208,11 +213,21 @@ export function getStylesDeclarations(
return declarations;
}
const pathToValue = value;
if ( first( pathToValue ) === 'elements' || useEngine ) {
let styleValue = get( blockStyles, pathToValue, '' );

if (
first( pathToValue ) === 'elements' ||
// The style engine cannot handle dynamic refs yet.
( useEngine && ! styleValue?.ref )
) {
return declarations;
}

const styleValue = get( blockStyles, pathToValue );
styleValue = resolveDynamicRef( styleValue, tree );
// If get() finds no style value or the ref points to another ref (invalid), return.
if ( styleValue === '' || !! styleValue?.ref ) {
return declarations;
}

// Root-level padding styles don't currently support strings with CSS shorthand values.
// This may change: https://github.com/WordPress/gutenberg/issues/40132.
Expand All @@ -226,30 +241,30 @@ export function getStylesDeclarations(
if ( !! properties && typeof styleValue !== 'string' ) {
Object.entries( properties ).forEach( ( entry ) => {
const [ name, prop ] = entry;

if ( ! get( styleValue, [ prop ], false ) ) {
const propStyleValue = get( styleValue, [ prop ], false );
if ( ! propStyleValue ) {
// Do not create a declaration
// for sub-properties that don't have any value.
return;
return declarations;
}

const cssProperty = name.startsWith( '--' )
? name
: kebabCase( name );

declarations.push(
`${ cssProperty }: ${ compileStyleValue(
get( styleValue, [ prop ] )
propStyleValue
) }`
);
} );
} else if ( get( blockStyles, pathToValue, false ) ) {
} else {
const cssProperty = key.startsWith( '--' )
? key
: kebabCase( key );

declarations.push(
`${ cssProperty }: ${ compileStyleValue(
get( blockStyles, pathToValue )
) }`
`${ cssProperty }: ${ compileStyleValue( styleValue ) }`
);
}

Expand All @@ -274,18 +289,9 @@ export function getStylesDeclarations(
? rule.key
: kebabCase( rule.key );

let ruleValue = rule.value;
if ( typeof ruleValue !== 'string' && ruleValue?.ref ) {
const refPath = ruleValue.ref.split( '.' );
ruleValue = get( tree, refPath );
// Presence of another ref indicates a reference to another dynamic value.
// Pointing to another dynamic value is not supported.
if ( ! ruleValue || !! ruleValue?.ref ) {
return;
}
if ( typeof rule.value === 'string' ) {
output.push( `${ cssProperty }: ${ rule.value }` );
}

output.push( `${ cssProperty }: ${ ruleValue }` );
} );

return output;
Expand Down
37 changes: 26 additions & 11 deletions packages/edit-site/src/components/global-styles/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,18 +238,9 @@ function getValueFromCustomVariable( features, blockName, variable, path ) {
* @return {string|*|{ref}} The value of the CSS var, if found. If not found, the passed variable argument.
*/
export function getValueFromVariable( features, blockName, variable ) {
variable = resolveDynamicRef( variable, features );
if ( ! variable || typeof variable !== 'string' ) {
if ( variable?.ref && typeof variable?.ref === 'string' ) {
const refPath = variable.ref.split( '.' );
variable = get( features, refPath );
// Presence of another ref indicates a reference to another dynamic value.
// Pointing to another dynamic value is not supported.
if ( ! variable || !! variable?.ref ) {
return variable;
}
} else {
return variable;
}
return variable;
}
const USER_VALUE_PREFIX = 'var:';
const THEME_VALUE_PREFIX = 'var(--wp--';
Expand Down Expand Up @@ -321,3 +312,27 @@ export function scopeSelector( scope, selector ) {

return selectorsScoped.join( ', ' );
}

/**
* Attempts a lookup of the theme.json tree to fetch the value of dynamic ref.
* This function exists until the style engine supports ref resolution.
*
* @param {string|*} styleValue An incoming style value.
* @param {Object} tree GlobalStylesContext config, e.g., user, base or merged. Represents the theme.json tree.
* @return {string|*} The value of the dynamic ref, if found. If not found, returns `styleValue`. If a reference to another ref is found, returns an empty string.
*/
export function resolveDynamicRef( styleValue, tree ) {
const ref = get( styleValue, [ 'ref' ], false );
if ( typeof ref === 'string' ) {
const resolvedValue = get( tree, ref.split( '.' ) );
// Presence of another ref indicates a reference to another dynamic value.
// Pointing to another dynamic value is not supported.
if ( !! resolvedValue?.ref ) {
return '';
}
if ( !! resolvedValue ) {
return resolvedValue;
}
}
return styleValue;
}