Skip to content

Commit b1c5b8b

Browse files
ockhamcbravobernal
authored andcommitted
Block Bindings: Untangle sources code (#72739)
The code has gotten a bit messy lately. We should set a good example in the code of these sources since people will refer to these files when implementing their own sources. Additionally, cleaner separation of listing available fields vs. fetching field values (see "How" section below) might pay off when we'll finally fix the `useSelect` issue 🤞 Co-authored-by: ockham <[email protected]> Co-authored-by: cbravobernal <[email protected]>
1 parent 64c6b86 commit b1c5b8b

File tree

2 files changed

+118
-157
lines changed

2 files changed

+118
-157
lines changed

packages/editor/src/bindings/post-data.js

Lines changed: 63 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -11,106 +11,74 @@ const NAVIGATION_BLOCK_TYPES = [
1111
'core/navigation-submenu',
1212
];
1313

14-
/**
15-
* Gets a list of post data fields with their values and labels
16-
* to be consumed in the needed callbacks.
17-
* If the value is not available based on context, like in templates,
18-
* it falls back to the default value, label, or key.
19-
*
20-
* @param {Object} select The select function from the data store.
21-
* @param {Object} context The context provided.
22-
* @param {string} clientId The block client ID used to read attributes.
23-
* @return {Object} List of post data fields with their value and label.
24-
*
25-
* @example
26-
* ```js
27-
* {
28-
* field_1_key: {
29-
* label: 'Field 1 Label',
30-
* value: 'Field 1 Value',
31-
* },
32-
* field_2_key: {
33-
* label: 'Field 2 Label',
34-
* value: 'Field 2 Value',
35-
* },
36-
* ...
37-
* }
38-
* ```
39-
*/
40-
function getPostDataFields( select, context, clientId ) {
41-
const { getEditedEntityRecord } = select( coreDataStore );
42-
const { getBlockAttributes, getBlockName } = select( blockEditorStore );
43-
44-
let entityDataValues, dataFields;
45-
46-
/*
47-
* BACKWARDS COMPATIBILITY: Hardcoded exception for navigation blocks.
48-
* Required for WordPress 6.9+ navigation blocks. DO NOT REMOVE.
49-
*/
50-
const blockName = getBlockName?.( clientId );
51-
const isNavigationBlock = NAVIGATION_BLOCK_TYPES.includes( blockName );
52-
53-
let postId, postType;
54-
55-
if ( isNavigationBlock ) {
56-
// Navigation blocks: read from block attributes
57-
const blockAttributes = getBlockAttributes?.( clientId );
58-
postId = blockAttributes?.id;
59-
postType = blockAttributes?.type;
60-
} else {
61-
// All other blocks: use context
62-
postId = context?.postId;
63-
postType = context?.postType;
64-
}
65-
66-
// Try to get the current entity data values using resolved identifiers.
67-
if ( postType && postId ) {
68-
entityDataValues = getEditedEntityRecord(
69-
'postType',
70-
postType,
71-
postId
72-
);
73-
dataFields = {
74-
date: {
75-
label: __( 'Post Date' ),
76-
value: entityDataValues?.date,
77-
type: 'string',
78-
},
79-
modified: {
80-
label: __( 'Post Modified Date' ),
81-
value: entityDataValues?.modified,
82-
type: 'string',
83-
},
84-
link: {
85-
label: __( 'Post Link' ),
86-
value: entityDataValues?.link,
87-
type: 'string',
88-
},
89-
};
90-
}
91-
92-
if ( ! Object.keys( dataFields || {} ).length ) {
93-
return null;
94-
}
95-
96-
return dataFields;
97-
}
14+
const postDataFields = [
15+
{
16+
label: __( 'Post Date' ),
17+
args: { field: 'date' },
18+
type: 'string',
19+
},
20+
{
21+
label: __( 'Post Modified Date' ),
22+
args: { field: 'modified' },
23+
type: 'string',
24+
},
25+
{
26+
label: __( 'Post Link' ),
27+
args: { field: 'link' },
28+
type: 'string',
29+
},
30+
];
9831

9932
/**
10033
* @type {WPBlockBindingsSource}
10134
*/
10235
export default {
10336
name: 'core/post-data',
10437
getValues( { select, context, bindings, clientId } ) {
105-
const dataFields = getPostDataFields( select, context, clientId );
38+
const allowedFields = postDataFields.map(
39+
( field ) => field.args.field
40+
);
41+
42+
/*
43+
* BACKWARDS COMPATIBILITY: Hardcoded exception for navigation blocks.
44+
* Required for WordPress 6.9+ navigation blocks. DO NOT REMOVE.
45+
*/
46+
const { getBlockAttributes, getBlockName } = select( blockEditorStore );
47+
const blockName = getBlockName?.( clientId );
48+
const isNavigationBlock = NAVIGATION_BLOCK_TYPES.includes( blockName );
49+
50+
let postId, postType;
51+
52+
if ( isNavigationBlock ) {
53+
// Navigation blocks: read from block attributes
54+
const blockAttributes = getBlockAttributes?.( clientId );
55+
postId = blockAttributes?.id;
56+
postType = blockAttributes?.type;
57+
} else {
58+
// All other blocks: use context
59+
postId = context?.postId;
60+
postType = context?.postType;
61+
}
62+
63+
const { getEditedEntityRecord } = select( coreDataStore );
64+
const entityDataValues = getEditedEntityRecord(
65+
'postType',
66+
postType,
67+
postId
68+
);
10669

10770
const newValues = {};
108-
for ( const [ attributeName, source ] of Object.entries( bindings ) ) {
109-
// Use the value, the field label, or the field key.
110-
const fieldKey = source.args.field;
111-
const { value: fieldValue, label: fieldLabel } =
112-
dataFields?.[ fieldKey ] || {};
113-
newValues[ attributeName ] = fieldValue ?? fieldLabel ?? fieldKey;
71+
for ( const [ attributeName, binding ] of Object.entries( bindings ) ) {
72+
if ( ! allowedFields.includes( binding.args.field ) ) {
73+
newValues[ attributeName ] = {};
74+
continue;
75+
}
76+
77+
newValues[ attributeName ] =
78+
entityDataValues?.[ binding.args.field ] ??
79+
postDataFields.find(
80+
( field ) => field.args.field === binding.args.field
81+
).label;
11482
}
11583
return newValues;
11684
},
@@ -136,7 +104,7 @@ export default {
136104
newData
137105
);
138106
},
139-
canUserEditValue( { select, context, args } ) {
107+
canUserEditValue( { select, context } ) {
140108
const { getBlockName, getSelectedBlockClientId } =
141109
select( blockEditorStore );
142110
const clientId = getSelectedBlockClientId();
@@ -158,14 +126,6 @@ export default {
158126
return false;
159127
}
160128

161-
const fieldValue = getPostDataFields( select, context, undefined )?.[
162-
args.field
163-
]?.value;
164-
// Empty string or `false` could be a valid value, so we need to check if the field value is undefined.
165-
if ( fieldValue === undefined ) {
166-
return false;
167-
}
168-
169129
// Check that the user has the capability to edit post data.
170130
const canUserEdit = select( coreDataStore ).canUser( 'update', {
171131
kind: 'postType',
@@ -178,7 +138,7 @@ export default {
178138

179139
return true;
180140
},
181-
getFieldsList( { select, context } ) {
141+
getFieldsList( { select } ) {
182142
const selectedBlock = select( blockEditorStore ).getSelectedBlock();
183143
if ( selectedBlock?.name !== 'core/post-date' ) {
184144
return [];
@@ -187,15 +147,7 @@ export default {
187147
if ( NAVIGATION_BLOCK_TYPES.includes( selectedBlock?.name ) ) {
188148
return [];
189149
}
190-
const clientId = select( blockEditorStore ).getSelectedBlockClientId();
191-
const postDataFields = getPostDataFields( select, context, clientId );
192-
if ( ! postDataFields ) {
193-
return [];
194-
}
195-
return Object.entries( postDataFields ).map( ( [ key, field ] ) => ( {
196-
label: field.label,
197-
type: field.type,
198-
args: { field: key },
199-
} ) );
150+
151+
return postDataFields;
200152
},
201153
};

packages/editor/src/bindings/post-meta.js

Lines changed: 55 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -35,41 +35,52 @@ import { unlock } from '../lock-unlock';
3535
* ```
3636
*/
3737
function getPostMetaFields( select, context ) {
38-
const { getEditedEntityRecord } = select( coreDataStore );
3938
const { getRegisteredPostMeta } = unlock( select( coreDataStore ) );
4039

41-
let entityMetaValues;
42-
// Try to get the current entity meta values.
43-
if ( context?.postType && context?.postId ) {
44-
entityMetaValues = getEditedEntityRecord(
45-
'postType',
46-
context?.postType,
47-
context?.postId
48-
).meta;
49-
}
50-
5140
const registeredFields = getRegisteredPostMeta( context?.postType );
52-
const metaFields = {};
53-
Object.entries( registeredFields || {} ).forEach( ( [ key, props ] ) => {
41+
const metaFields = [];
42+
Object.entries( registeredFields ).forEach( ( [ key, props ] ) => {
5443
// Don't include footnotes or private fields.
55-
if ( key !== 'footnotes' && key.charAt( 0 ) !== '_' ) {
56-
metaFields[ key ] = {
57-
label: props.title || key,
58-
value:
59-
// When using the entity value, an empty string IS a valid value.
60-
entityMetaValues?.[ key ] ??
61-
// When using the default, an empty string IS NOT a valid value.
62-
( props.default || undefined ),
63-
type: props.type,
64-
};
44+
if ( key === 'footnotes' || key.charAt( 0 ) === '_' ) {
45+
return;
6546
}
47+
48+
metaFields.push( {
49+
label: props.title || key,
50+
args: { key },
51+
default: props.default,
52+
type: props.type,
53+
} );
6654
} );
6755

68-
if ( ! Object.keys( metaFields || {} ).length ) {
69-
return null;
56+
return metaFields;
57+
}
58+
59+
function getValue( { select, context, args } ) {
60+
const metaFields = getPostMetaFields( select, context );
61+
const metaField = metaFields.find(
62+
( field ) => field.args.key === args.key
63+
);
64+
65+
// If the meta field was not found, it's either protected, inaccessible, or simply doesn't exist.
66+
if ( ! metaField ) {
67+
return args.key;
7068
}
7169

72-
return metaFields;
70+
// Without a postId, we cannot look up a meta value.
71+
if ( ! context?.postId ) {
72+
// Return the default value for the meta field if available.
73+
return metaField.default || metaField.label || args.key;
74+
}
75+
76+
const { getEditedEntityRecord } = select( coreDataStore );
77+
const entityMetaValues = getEditedEntityRecord(
78+
'postType',
79+
context?.postType,
80+
context?.postId
81+
).meta;
82+
83+
return entityMetaValues?.[ args.key ] ?? metaField?.label ?? args.key;
7384
}
7485

7586
/**
@@ -78,15 +89,13 @@ function getPostMetaFields( select, context ) {
7889
export default {
7990
name: 'core/post-meta',
8091
getValues( { select, context, bindings } ) {
81-
const metaFields = getPostMetaFields( select, context );
82-
8392
const newValues = {};
84-
for ( const [ attributeName, source ] of Object.entries( bindings ) ) {
85-
// Use the value, the field label, or the field key.
86-
const fieldKey = source.args.key;
87-
const { value: fieldValue, label: fieldLabel } =
88-
metaFields?.[ fieldKey ] || {};
89-
newValues[ attributeName ] = fieldValue ?? fieldLabel ?? fieldKey;
93+
for ( const [ attributeName, binding ] of Object.entries( bindings ) ) {
94+
newValues[ attributeName ] = getValue( {
95+
select,
96+
context,
97+
args: binding.args,
98+
} );
9099
}
91100
return newValues;
92101
},
@@ -116,12 +125,14 @@ export default {
116125
return false;
117126
}
118127

119-
const fieldValue = getPostMetaFields( select, context )?.[ args.key ]
120-
?.value;
121-
// Empty string or `false` could be a valid value, so we need to check if the field value is undefined.
122-
if ( fieldValue === undefined ) {
128+
const metaFields = getPostMetaFields( select, context );
129+
const hasMatchingMetaField = metaFields.some(
130+
( field ) => field.args.key === args.key
131+
);
132+
if ( ! hasMatchingMetaField ) {
123133
return false;
124134
}
135+
125136
// Check that custom fields metabox is not enabled.
126137
const areCustomFieldsEnabled =
127138
select( editorStore ).getEditorSettings().enableCustomFields;
@@ -143,13 +154,11 @@ export default {
143154
},
144155
getFieldsList( { select, context } ) {
145156
const metaFields = getPostMetaFields( select, context );
146-
if ( ! metaFields ) {
147-
return [];
148-
}
149-
return Object.entries( metaFields ).map( ( [ key, field ] ) => ( {
150-
label: field.label,
151-
type: field.type,
152-
args: { key },
153-
} ) );
157+
// Remove 'default' property from meta fields.
158+
return metaFields.map(
159+
( { default: defaultProp, ...otherProps } ) => ( {
160+
...otherProps,
161+
} )
162+
);
154163
},
155164
};

0 commit comments

Comments
 (0)