Skip to content

Connect block attributes with custom fields via UI #176

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

Open
wants to merge 35 commits into
base: trunk
Choose a base branch
from

Conversation

cbravobernal
Copy link
Contributor

@cbravobernal cbravobernal commented Jun 12, 2025

What

⚠️ It needs to be loaded as a beta feature in their respective admin panel.
⚠️ Pattern overrides cannot be set up connected to fields. They are still not compatible on Core.
⚠️ There is some duplication both in code and UI with Core.

The PR allows to connect block attributes with custom fields via UI. Is still experimental, as I have not yet tested with any field type that can be created. In a future PR we can add an advanced view, using DataViews.

The image block will only search for image type fields, and all their attributes can be bound at once, just selecting the image field.

The paragraph, heading, and button blocks will only work with an initial set of items set in this variable:

const BLOCK_BINDINGS_RELATED_FIELD_TYPES = {
	'core/paragraph': {
		content: [ 'text', 'textarea', 'date_picker', 'number', 'range' ],
	},
	'core/heading': {
		content: [ 'text', 'textarea', 'date_picker', 'number', 'range' ],
	},
	'core/image': {
		id: [ 'image' ],
		url: [ 'image' ],
		title: [ 'image' ],
		alt: [ 'image' ],
	},
	'core/button': {
		url: [ 'url' ],
		text: [ 'text', 'checkbox', 'select', 'date_picker' ],
		linkTarget: [ 'text', 'checkbox', 'select' ],
		rel: [ 'text', 'checkbox', 'select' ],
	},
};

Screenshare

Screen.Recording.2025-07-08.at.18.04.35.mov

@cbravobernal cbravobernal self-assigned this Jun 12, 2025
@cbravobernal cbravobernal added the [Type] Enhancement New feature or request label Jun 12, 2025
@cbravobernal cbravobernal requested a review from ockham June 24, 2025 09:56
@cbravobernal cbravobernal force-pushed the update/connect-bindings-with-ui branch from 917b150 to 8d471bc Compare June 24, 2025 13:03
@cbravobernal cbravobernal added this to the 6.6.0 milestone Jun 27, 2025
@cbravobernal cbravobernal requested a review from priethor June 27, 2025 11:31
@cbravobernal cbravobernal marked this pull request as ready for review June 27, 2025 11:31
@cbravobernal cbravobernal requested a review from gziolo June 27, 2025 14:32
@ockham
Copy link
Contributor

ockham commented Jul 4, 2025

I think there's a minor UX glitch with regard to the tools panel and link/unlink button:

connect-fields

AFAIU, we should change two things here:

  • The panel's heading shouldn't change based on the link/unlink button; it shouldn't describe the link/unlink option, but the nature of the panel's controls (so probably "Attributes").
  • The panel's "vertical three dots" menu shouldn't change contextually; it should only apply to the whole Attributes panel, not its individual rows.

(This is based on how I observed the UX pattern to work e.g. for the existing margin panel in the style tab of the block inspector.)

@cbravobernal
Copy link
Contributor Author

  • The panel's "vertical three dots" menu shouldn't change contextually; it should only apply to the whole Attributes panel, not its individual rows.

But, in that case, if you have the four of them unlinked, and want to reset only the image title or alt, you will need to reset every of them at once and set one by one again, right?

It's better to keep consistency, but as we change the number of toolsitem, that is the default behaviour. I'll take a look at how is implemented for margin and styles.

Thanks @ockham!

@cbravobernal cbravobernal force-pushed the update/connect-bindings-with-ui branch from 63e9e91 to 7fa68ab Compare July 7, 2025 15:09
@gziolo
Copy link
Member

gziolo commented Jul 9, 2025

Removing selected connection doesn't work properly:

Screenshot 2025-07-09 at 13 44 40 Screenshot 2025-07-09 at 13 43 49

The editor remains in this weird state where it looks like the field is still connected with SCF field.

@gziolo
Copy link
Member

gziolo commented Jul 9, 2025

For some reason, the panel moves in the sidebar after selecting the value:

Screenshot 2025-07-09 at 13 46 27 Screenshot 2025-07-09 at 13 45 56

@gziolo
Copy link
Member

gziolo commented Jul 9, 2025

The experience for the Button block is less fluid. Should we limit it to mapping to the text attribute and leave the advanced connection to the Attributes panel?

Screenshot 2025-07-09 at 13 49 24

Maybe we could limit it to URL and text?

The filtering for the URL looks pretty nice:

Screenshot 2025-07-09 at 13 53 04

@cbravobernal
Copy link
Contributor Author

For some reason, the panel moves in the sidebar after selecting the value:

Screenshot 2025-07-09 at 13 46 27 Screenshot 2025-07-09 at 13 45 56

That happened too in Gutenberg with the tab. It maybe a hook-related thing. I'll take a look at it.

Comment on lines 106 to 112
const handleDateFieldValue = ( fieldValue, fieldConfig ) => {
if ( ! fieldValue ) {
return '';
}

return dateI18n( fieldConfig?.display_format, fieldValue ) || '';
};
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd be interested in making date_picker fields work with Core's Date block (that I'm currently working on). That block will allow binding to its datetime attribute, which will be using this format. The user can use the block inspector to set the format that the date is rendered with (this is a feature of the Date block that already exists).

However, AFAICS, the SCF source will always return the date using the display_format that's set in SCF options, meaning that the Date block might receive the date in a format that it won't understand ❌

In this particular case, one solution would be to allow binding the Date block's format attribute to a source, and to have SCF expose its format. This could be a fairly elegant solution, as it would allow both using SCF's format, but also setting a custom format from within the Date block. (We would need to allow unlinking individual bound attributes, however.)

This might work well enough in the case of date fields, but might not scale to other cases. In the bigger picture, this is a question of who decides the formatting of a given piece of information. An alternative approach would be for SCF to always just provide a standardized "raw" format, and to leave formatting up to the individual block that it is bound to. IMO, this makes some sense, as it encapsulates the data layer in SCF, and leaves the formatting to the presentational layer (i.e. the blocks). However, it would mean that SCF's own format settings will be ignored, and some data won't look pretty when bound to a more "primitive" block (e.g. binding datetime to a paragraph block).

I was wondering how the question of formatting was previously considered in the context of Block Bindings. For example, so far, all examples of Block Bindings use args: { key: ... } to select a given source field for binding; was the idea that format could be supplied as an additional arg? (E.g. args: { key: date, format: 'M j, Y' }?

@cbravobernal @gziolo


We probably don't have to answer this for this PR, but it might be good to keep in mind for our next steps.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

However, it would mean that SCF's own format settings will be ignored, and some data won't look pretty when bound to a more "primitive" block (e.g. binding datetime to a paragraph block).

I guess we could auto-convert paragraphs bounded to a datetime field to that date block to avoid that. But then it would not be compatible with future "shortblocks". My opinion is that the format value defined on the field has been widely used, so it should be the default one, and then the block should be capable to override that format option.

That way we wouldn't break backwards compatibility neither. We could for sure update the source with more args. The key one was a convention from the beginning of the project.

Comment on lines 89 to 97
const handleNumericFieldValue = ( fieldValue, attribute, getMedia ) => {
if ( attribute === 'content' ) {
return fieldValue.toString() || '';
}

// For image fields or numeric values, try to resolve as media
const imageObj = getMedia( fieldValue );
return resolveImageAttribute( imageObj, attribute );
};
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd love if we could restructure this a bit. IMO, it feels a bit backwards that when given a numeric value, we have to guess if it's an image ID, when we have higher-level information about field types available at the callsite. (A similar note applies to handleObjectFieldValue.)

I'll quickly try out locally what that would look like.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please! Totally agree.

Copy link
Contributor

Choose a reason for hiding this comment

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

This is more or less what I have in mind: At the top level, a switch for all possible SCF field types. It's still incomplete since I'm not yet totally familiar with all the possible ways they are mapped to block attributes.

	switch ( fieldType ) {
		case 'text':
		case 'textarea':
			return fieldValue || '';
		case 'number':
			if ( attribute === 'content' ) {
				return fieldValue.toString() || '';
			}
			break;
		case 'image':
			// fieldValue is a (numeric) image ID.
			const imageObj = getMedia( fieldValue );
			return resolveImageAttribute( imageObj, attribute );
		case 'date_picker':
			if ( ! fieldValue ) {
				return '';
			}
			return dateI18n( fieldConfig?.display_format, fieldValue ) || '';
	}

Copy link
Contributor

Choose a reason for hiding this comment

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

Done in 2c96d62.

@cbravobernal
Copy link
Contributor Author

The experience for the Button block is less fluid. Should we limit it to mapping to the text attribute and leave the advanced connection to the Attributes panel?

Sure, my opinion is that the rel and target options should be in the block toolbar or the block sidebar, rather than being bindings (for UI based blocks). I would keep those bindings just for more complex cases (attributes based on external sources or php conditionals).

Comment on lines 72 to 76
// Special fallback: if we're looking for 'content' and no content property exists (or is falsy),
// but there's a 'url' property, use that instead
if ( attribute === 'content' && fieldValue.url ) {
return fieldValue.url;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this for images?

Copy link
Contributor Author

@cbravobernal cbravobernal Jul 9, 2025

Choose a reason for hiding this comment

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

Image doesn't have 'content' as a bindable attribute, lets change that.
Updated in 60976f6

@cbravobernal
Copy link
Contributor Author

cbravobernal commented Jul 9, 2025

If I create a new CPT and corresponding field group, but forget to enable the "Allow Access to Value in Editor UI" toggle in a field's "Presentation" tab, I'm still able to connect blocks to that field in the editor. However, it doesn't show up on the frontend:

......
It works on the frontend once I enable those toggles.

I guess the solution is to disallow connecting to a field in the editor if its "Allow Access to Value in Editor UI" toggle is disabled?

Fixed in 89d69ff
It will only show fields with the toggle enabled.

@ockham
Copy link
Contributor

ockham commented Jul 9, 2025

BTW the date_picker field currently shows a date in the editor (when bound to a paragraph) that's different from what's set in SCF 🙈

image

My hunch is that it has to do with timezones -- one of them probably uses UTC, and the other one the local timezone 😬

@cbravobernal
Copy link
Contributor Author

BTW the date_picker field currently shows a date in the editor (when bound to a paragraph) that's different from what's set in SCF 🙈

image My hunch is that it has to do with timezones -- one of them probably uses UTC, and the other one the local timezone 😬

Ouch.

BTW the date_picker field currently shows a date in the editor (when bound to a paragraph) that's different from what's set in SCF 🙈

image My hunch is that it has to do with timezones -- one of them probably uses UTC, and the other one the local timezone 😬

Should we just take the formatted value from the server and print it as a string?

@ockham
Copy link
Contributor

ockham commented Jul 9, 2025

Should we just take the formatted value from the server and print it as a string?

Maybe 🤔

Plus I guess I need to file a bug report against Gutenberg, since it seems that dateI18n (JS) behaves differently from date_i18n (PHP).

@cbravobernal
Copy link
Contributor Author

Should we just take the formatted value from the server and print it as a string?

Maybe 🤔

Plus I guess I need to file a bug report against Gutenberg, since it seems that dateI18n (JS) behaves differently from date_i18n (PHP).

I have a prototype working, but is adding all values parsed by SCF to the types endpoint, ending with maybe too much information.

I will instead try to extend acf object data to include the formatted value.

@ockham
Copy link
Contributor

ockham commented Jul 10, 2025

Per discussion via DM, I've pushed a fix for the date issue: 718af07

@cbravobernal cbravobernal changed the title Experiment: Connect block attributes with custom fields via UI Connect block attributes with custom fields via UI Jul 11, 2025
@cbravobernal
Copy link
Contributor Author

Removing selected connection doesn't work properly:

.....
The editor remains in this weird state where it looks like the field is still connected with SCF field.

This one has been fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Type] Enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants