-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Reuse cleanEmptyObject utility and fix empty string case #49750
Changes from all commits
78798c6
dc3cdee
eab5c90
50350ae
f154e5c
fe13b8f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -376,3 +376,16 @@ export function scopeSelector( scope, selector ) { | |
|
||
return selectorsScoped.join( ', ' ); | ||
} | ||
|
||
/** | ||
* Some Global Styles UI components (font size for instance) relies on the fact that emptying inputs | ||
* (empty strings) should fallback to the parent value (theme.json value). | ||
* Ideally, there should be a dedicated UI element to "revert to theme" for each input instead. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm probably missing something, but isn't that what the "reset" buttons in the tools panel dropdowns are meant to do? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The reset functionality of the
I think this part of the comment is referring to when a control can be manually cleared by a user e.g. deleting the value within an input control. In this case, it is retained by the new cleanEmptyObject but for the global styles to fall back to the theme's value correctly, we need to ensure a user value isn't set. Either way, I could be misunderstanding. Perhaps we could add some further clarity to this comment? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I meant that each "style" should have its own dedicated "reset" link, similar to the global one. and I agree that the comment can be made clearer. |
||
* But until we do, this function is used to transform falsy values to undefined values for these components | ||
* which allows the global styles merge algorithm to revert to the theme.json value. | ||
* | ||
* @param {*} value Value to normalize. | ||
* | ||
* @return {undefined|*} normalized value. | ||
*/ | ||
export const normalizeFalsyValue = ( value ) => value || undefined; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { applyFilters } from '@wordpress/hooks'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import '../anchor'; | ||
|
||
const noop = () => {}; | ||
|
||
describe( 'anchor', () => { | ||
const blockSettings = { | ||
save: noop, | ||
category: 'text', | ||
title: 'block title', | ||
}; | ||
|
||
describe( 'addAttribute()', () => { | ||
const registerBlockType = applyFilters.bind( | ||
null, | ||
'blocks.registerBlockType' | ||
); | ||
|
||
it( 'should do nothing if the block settings do not define anchor support', () => { | ||
const settings = registerBlockType( blockSettings ); | ||
|
||
expect( settings.attributes ).toBe( undefined ); | ||
} ); | ||
|
||
it( 'should assign a new anchor attribute', () => { | ||
const settings = registerBlockType( { | ||
...blockSettings, | ||
supports: { | ||
anchor: true, | ||
}, | ||
} ); | ||
|
||
expect( settings.attributes ).toHaveProperty( 'anchor' ); | ||
} ); | ||
|
||
it( 'should not override attributes defined in settings', () => { | ||
const settings = registerBlockType( { | ||
...blockSettings, | ||
supports: { | ||
anchor: true, | ||
}, | ||
attributes: { | ||
anchor: { | ||
type: 'string', | ||
default: 'testAnchor', | ||
}, | ||
}, | ||
} ); | ||
|
||
expect( settings.attributes.anchor ).toEqual( { | ||
type: 'string', | ||
default: 'testAnchor', | ||
} ); | ||
} ); | ||
} ); | ||
|
||
describe( 'addSaveProps', () => { | ||
const getSaveContentExtraProps = applyFilters.bind( | ||
null, | ||
'blocks.getSaveContent.extraProps' | ||
); | ||
|
||
it( 'should do nothing if the block settings do not define anchor support', () => { | ||
const attributes = { anchor: 'foo' }; | ||
const extraProps = getSaveContentExtraProps( | ||
{}, | ||
blockSettings, | ||
attributes | ||
); | ||
|
||
expect( extraProps ).not.toHaveProperty( 'id' ); | ||
} ); | ||
|
||
it( 'should inject anchor attribute ID', () => { | ||
const attributes = { anchor: 'foo' }; | ||
const extraProps = getSaveContentExtraProps( | ||
{}, | ||
{ | ||
...blockSettings, | ||
supports: { | ||
anchor: true, | ||
}, | ||
}, | ||
attributes | ||
); | ||
|
||
expect( extraProps.id ).toBe( 'foo' ); | ||
} ); | ||
|
||
it( 'should remove an anchor attribute ID when field is cleared', () => { | ||
const attributes = { anchor: '' }; | ||
const extraProps = getSaveContentExtraProps( | ||
{}, | ||
{ | ||
...blockSettings, | ||
supports: { | ||
anchor: true, | ||
}, | ||
}, | ||
attributes | ||
); | ||
|
||
expect( extraProps.id ).toBe( null ); | ||
} ); | ||
} ); | ||
} ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm undecided between this and something explicit and immediate like
newValue || undefined
. While I understand the value of having a function likenormalizeFalsyValue
acting as an "anchor" (complete with documentation and the rationale for normalisation), I'm not sure that function name is descriptive enough.That said, you could say I am picking nits; I'm wondering about how to make all the hidden traps of Global Styles as evident as possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that it's not a perfect solution, my main motivation is the documentation actually but I'm also undecided, especially since IMO we should be moving away slowly from its usage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the
undefined
solution makes a lot of sense 👍However, I personally think that:
|| undefined
to generalize this as a usable abstraction, and while here we're starting to utilize ``normalizeFalsyValue` in a few places, there are still way more that will remain as they are.|| undefined
is far too straightforward and simple to be generalized, and I think is more readable as-is instead of going throughnormalizeFalsyValue()
That being said, I'd personally lean towards inlining
|| undefined
everywhere instead of introducing a function to do it. I think having it inline is more readable and comprehensible than abstracting it in a function.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I merged and missed this comment. I can remove that function soon if you feel strongly about it. I'm not sure how to handle the loss of documentation thought without a centralized function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you don't mind, I'd prefer removing it, yes. I'm not sure there's a good need for documentation on this part, really.
|| undefined
seems quite straightforward as a JS idiom and even if we abstract it to a function with docs (like we did), this won't help ensure that folks are using it where necessary. I'm also again pointing to the tens of usages of|| undefined
in the codebase where the usage isn't documented but is still there intentionally. Maybe I just fail to understand what you consider miscommunicated with the lack of documentation in these instances?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I wanted to communicate is that for styles UI (most of them), clearing inputs means reset to the theme.json values. So it's not just a random JS operation to replace empty strings with undefined, it has bigger impacts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Opened this #50033
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this something we should document at a different place then - for example in the primary README of the global styles component in the block editor? I'm just not sure if documenting it within a separate helper function will help much with adopting it, especially when folks aren't aware that they should be using that function.