Skip to content

Add support for wildcard rules in block settings#85

Merged
alecgeatches merged 26 commits intotrunkfrom
add/support-for-regex-in-block-settings
Sep 13, 2024
Merged

Add support for wildcard rules in block settings#85
alecgeatches merged 26 commits intotrunkfrom
add/support-for-regex-in-block-settings

Conversation

@ingeniumed
Copy link
Copy Markdown
Contributor

@ingeniumed ingeniumed commented Sep 10, 2024

Description

There are 3 big changes in this PR:

There's two small changes as well:

  • Enable webpack, and use the existing rule set taken from our vip-workflow plugin so the overall configuration is simple.
  • Update all the packages

@ingeniumed ingeniumed self-assigned this Sep 10, 2024
Comment thread governance/nested-governance-processing.php Outdated
@alecgeatches
Copy link
Copy Markdown
Contributor

From an initial runthrough of the code, this looks good!

My only thought prior to the full PR would be to change "regex" to "wildcard" in the PR title and internal functions. We don't actually support regular expressions here (which I think is a good thing), just wildcards. I think we should use the term "wildcard" consistently to avoid confusion.

Comment thread webpack.config.js
Comment thread src/index.js Outdated

( { value: result } = getNestedSetting( blockNamePath, path, nestedSettings ) );
// iterate through the nestedSettingPaths to find the blockName
for ( const nestedBlockName in nestedSettingPaths ) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alecgeatches - just need to pay special attention to this block as I touched it up a touch. I'd like to see if we could do some more pre-processing on the PHP side but that's for another PR.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main thing to keep in mind for this filter is that the blockEditor.useSetting.before filter runs a lot of times. In a quick test locally on a post with a moderately complex post, it's easy to hit this filter 10k+ times in a few seconds by just hovering around the page.

You may already have the fastest option here that's flexible, but if there's anything we can do to return early for non-matched blocks, we want to do so as quickly as possible.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've got one idea for optimization:

In JS prior to setting up this hook, we could split nestedSettingPaths into nestedSettingWildcards for names we need to evaluate like core/*, and a set of non-wildcards like nestedSettingNonWildcards (or a better name). Then we could keep the linear-time check that we had before using hasCustomSetting when matching non-wildcards, and only do a loop through any wildcards. If there are no wildcards, then there's no loop necessary.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall I think this is pretty fast. I would split up wildcard and non-wildcard checks though.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thought was to try and do that on the PHP side so that we take advantage of server side rendering actually. That way we minimize the pre-processing necessary on the JS side 🤔

@ingeniumed ingeniumed changed the title Add support for regex rules in block settings Add support for wildcard rules in block settings Sep 11, 2024
@ingeniumed
Copy link
Copy Markdown
Contributor Author

From an initial runthrough of the code, this looks good!

My only thought prior to the full PR would be to change "regex" to "wildcard" in the PR title and internal functions. We don't actually support regular expressions here (which I think is a good thing), just wildcards. I think we should use the term "wildcard" consistently to avoid confusion.

This is a good point, and something that I've now fixed. Lemme know if I missed any occurrence.

@ingeniumed ingeniumed marked this pull request as ready for review September 11, 2024 04:04
@ingeniumed ingeniumed requested a review from a team as a code owner September 11, 2024 04:04
@ingeniumed
Copy link
Copy Markdown
Contributor Author

I also added in support for 6.0 and PHP 8.0/8.3 in tests btw.

ingeniumed and others added 3 commits September 12, 2024 11:11
Co-authored-by: Alec Geatches <alec.geatches@automattic.com>
…Automattic/vip-governance-plugin into add/support-for-regex-in-block-settings
Comment thread package.json Outdated
@alecgeatches
Copy link
Copy Markdown
Contributor

alecgeatches commented Sep 12, 2024

@ingeniumed I'm seeing an issue with a custom palette under a core/* property. Here's my rules:

{
	"$schema": "https://api.wpvip.com/schemas/plugins/governance.json",
	"version": "1.0.0",
	"rules": [
		{
			"type": "role",
			"roles": [ "administrator" ],
			"allowedFeatures": [ "codeEditor", "lockBlocks" ],
			"allowedBlocks": [ "core/media-text" ],
			"blockSettings": {
				"core/media-text": {
					"core/*": {
						"color": {
							"text": true,
							"palette": [
								{
									"color": "#ff00ff",
									"name": "Custom purple",
									"slug": "custom-purple"
								}
							]
						}
					}
				}
			}
		},
		{
			"type": "default",
			"allowedBlocks": [ "core/heading", "core/paragraph", "core/image" ]
		}
	]
}

In the editor, this works correctly. I made a core/media-text block containing a heading with the custom color:

purple-heading

However, the custom color doesn't appear on the frontend:

default-heading

This is the frontend CSS getting injected into the page:

.wp-block-media-text .wp-block {
    --wp--preset--color--custom-purple: #ff00ff;
}

.wp-block-media-text .wp-block.has-custom-purple-color {
    color: var(--wp--preset--color--custom-purple) !important;
}

.wp-block-media-text .wp-block.has-custom-purple-background-color {
    background-color: var(--wp--preset--color--custom-purple) !important;
}

.wp-block-media-text .wp-block.has-custom-purple-border-color {
    border-color: var(--wp--preset--color--custom-purple) !important;
}

The .wp-block selector doesn't seem to work. Here's the media-text HTML used on the frontend:

<div class="wp-block-media-text is-stacked-on-mobile">
    <figure class="wp-block-media-text__media">
        <img <!--...--> class="wp-image-273 size-full">
    </figure>

    <div class="wp-block-media-text__content">
        <h2
            class="wp-block-heading has-custom-purple-color has-text-color has-link-color wp-elements-f7bf55209eb97c294140446761d1d65d"
            >Heading</h2>
    </div>
</div>

Above, the h2 has a .wp-block-heading class but not a .wp-block, so the .has-custom-purple-color class isn't applied.

This makes me wonder if we need to generate a matching rule for all possible subblocks, although would probably get exponential quickly with a handful of nested wildcards. Any idea on how to solve this?

Copy link
Copy Markdown
Contributor

@alecgeatches alecgeatches left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some comments! The main issue I found is here: #85 (comment), where the .wp-block trick doesn't work for headings. I'm not sure if this is something that'll be easy to get around given .wp-block doesn't seem to be consistent.

Great job on this feature otherwise! There's a lot of complexity figured out in here.

Comment thread README.md Outdated

For an example of this feature, refer [here](#default-wildcard-rule-set).

Note: `allowedBlocks` are not respected under a wildcard block within `blockSettings`. This will only be respected under a targeted block such as `core/quote`.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really follow this. Could you maybe add an example of a improper ruleset ❌, and then maybe an example of a working one ✅ to explain the issue?

Copy link
Copy Markdown
Contributor Author

@ingeniumed ingeniumed Sep 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I'll do that for an improper one. I've already linked an existing example for a proper one

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added another update to this documentation. It still wasn't clear enough to me for a couple of reasons:

  1. We have an example of a bad schema, but it wasn't obvious to me what a fix looked like. I added a second example to show how a customer might work around the issue and still make use of wildcards.
  2. I don't like having only an incorrect example under the "Wildcards" header. If I was trying to work quickly, I might search for "wildcard" on the README page and just copy the first example I found without realizing it was meant to be a counterexample. I made that more clear with ❌/ ✅ right above both examples.

I added these as headings at the fifth level (#####), which I've purposefully excluded from the table of contents. Typically TOC generators have a set level of headings (e.g. 1-4), and subheadings below that level are excluded. I think that makes sense for the TOC here in this README as well.

Comment thread governance/nested-governance-processing.php
Comment thread governance/nested-governance-processing.php Outdated
Comment thread governance-schema.json
"text": false
}
},
"core/*": {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good thinking adding this as an example! I think you can remove the prior core/paragraph example though, as this covers that case. For me in VSCode, the whole set of examples fills in when I tab-in a rule:

2024-09-12 12 22 48

I think this is redundant to have two rules to set color.text to false, as technically the second rule would cover the case of the first. We could either have the core/paragraph example show something different, or just remove it here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I'll replace it with a gradient rule

Comment thread src/index.js Outdated

( { value: result } = getNestedSetting( blockNamePath, path, nestedSettings ) );
// iterate through the nestedSettingPaths to find the blockName
for ( const nestedBlockName in nestedSettingPaths ) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main thing to keep in mind for this filter is that the blockEditor.useSetting.before filter runs a lot of times. In a quick test locally on a post with a moderately complex post, it's easy to hit this filter 10k+ times in a few seconds by just hovering around the page.

You may already have the fastest option here that's flexible, but if there's anything we can do to return early for non-matched blocks, we want to do so as quickly as possible.

Comment thread src/index.js Outdated

( { value: result } = getNestedSetting( blockNamePath, path, nestedSettings ) );
// iterate through the nestedSettingPaths to find the blockName
for ( const nestedBlockName in nestedSettingPaths ) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've got one idea for optimization:

In JS prior to setting up this hook, we could split nestedSettingPaths into nestedSettingWildcards for names we need to evaluate like core/*, and a set of non-wildcards like nestedSettingNonWildcards (or a better name). Then we could keep the linear-time check that we had before using hasCustomSetting when matching non-wildcards, and only do a loop through any wildcards. If there are no wildcards, then there's no loop necessary.

Comment thread src/index.js Outdated

( { value: result } = getNestedSetting( blockNamePath, path, nestedSettings ) );
// iterate through the nestedSettingPaths to find the blockName
for ( const nestedBlockName in nestedSettingPaths ) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall I think this is pretty fast. I would split up wildcard and non-wildcard checks though.

Comment thread governance/nested-governance-processing.php
Comment thread src/index.js Outdated
const nestedSettingPaths = getNestedSettingPaths( nestedSettings );

// pull out all the wildcard block names.
const nestedWildcardPaths = Object.keys( nestedSettingPaths ).reduce( ( acc, blockName ) => {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optimized it based on your suggestion and it def feels a touch faster

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! This is what I was picturing. I pushed up a small update to split these in one pass imperatively, a tiny optimization since we're going for speed here:

const nestedWildcardPaths = {};
const nestedNonWildcardPaths = {};
for ( const blockName in nestedSettingPaths ) {
if ( blockName.indexOf( '*' ) === -1 ) {
// eslint-disable-next-line security/detect-object-injection
nestedNonWildcardPaths[ blockName ] = nestedSettingPaths[ blockName ];
} else {
// eslint-disable-next-line security/detect-object-injection
nestedWildcardPaths[ blockName ] = nestedSettingPaths[ blockName ];
}
}

Comment thread src/nested-governance-loader.js
@alecgeatches
Copy link
Copy Markdown
Contributor

@ingeniumed Made some minor changes, going to merge this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants