-
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
Block API: Add parse options to specify custom source handling #16316
Changes from all commits
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 |
---|---|---|
|
@@ -2,7 +2,7 @@ | |
* External dependencies | ||
*/ | ||
import { parse as hpqParse } from 'hpq'; | ||
import { flow, castArray, mapValues, omit, stubFalse } from 'lodash'; | ||
import { flow, castArray, mapValues, omit, stubFalse, has } from 'lodash'; | ||
|
||
/** | ||
* WordPress dependencies | ||
|
@@ -25,6 +25,26 @@ import { getCommentDelimitedContent } from './serializer'; | |
import { attr, html, text, query, node, children, prop } from './matchers'; | ||
import { normalizeBlockType } from './utils'; | ||
|
||
/** | ||
* Options for parse. | ||
* | ||
* @property {Object<string,Function>} sources An object defining custom source | ||
* types for a parse, where the key | ||
* corresponds to the source name, | ||
* the value a function receiving | ||
* an attribute schema and expected | ||
* to return the desired value. | ||
* | ||
* @typedef {Object} WPBlocksParseOptions | ||
*/ | ||
|
||
/** | ||
* Default options for parse. | ||
* | ||
* @type {WPBlocksParseOptions} | ||
*/ | ||
const DEFAULT_PARSE_OPTIONS = {}; | ||
|
||
/** | ||
* Sources which are guaranteed to return a string value. | ||
* | ||
|
@@ -225,14 +245,15 @@ export function parseWithAttributeSchema( innerHTML, attributeSchema ) { | |
* commentAttributes returns the attribute value depending on its source | ||
* definition of the given attribute key. | ||
* | ||
* @param {string} attributeKey Attribute key. | ||
* @param {Object} attributeSchema Attribute's schema. | ||
* @param {string} innerHTML Block's raw content. | ||
* @param {Object} commentAttributes Block's comment attributes. | ||
* @param {string} attributeKey Attribute key. | ||
* @param {Object} attributeSchema Attribute's schema. | ||
* @param {string} innerHTML Block's raw content. | ||
* @param {Object} commentAttributes Block's comment attributes. | ||
* @param {WPBlocksParseOptions} parseOptions Parser options. | ||
* | ||
* @return {*} Attribute value. | ||
*/ | ||
export function getBlockAttribute( attributeKey, attributeSchema, innerHTML, commentAttributes ) { | ||
export function getBlockAttribute( attributeKey, attributeSchema, innerHTML, commentAttributes, parseOptions ) { | ||
const { type, enum: enumSet } = attributeSchema; | ||
let value; | ||
|
||
|
@@ -241,6 +262,7 @@ export function getBlockAttribute( attributeKey, attributeSchema, innerHTML, com | |
case undefined: | ||
value = commentAttributes ? commentAttributes[ attributeKey ] : undefined; | ||
break; | ||
|
||
case 'attribute': | ||
case 'property': | ||
case 'html': | ||
|
@@ -251,6 +273,11 @@ export function getBlockAttribute( attributeKey, attributeSchema, innerHTML, com | |
case 'tag': | ||
value = parseWithAttributeSchema( innerHTML, attributeSchema ); | ||
break; | ||
|
||
default: | ||
if ( has( parseOptions.sources, attributeSchema.source ) ) { | ||
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. Would it make sense to allow an async retrieval of the value? Imagining cases where external requests are required to retrieve the value. I guess it would increase the overall complexity of the module. |
||
value = parseOptions.sources[ attributeSchema.source ]( attributeSchema ); | ||
} | ||
} | ||
|
||
if ( ! isValidByType( value, type ) || ! isValidByEnum( value, enumSet ) ) { | ||
|
@@ -269,16 +296,18 @@ export function getBlockAttribute( attributeKey, attributeSchema, innerHTML, com | |
/** | ||
* Returns the block attributes of a registered block node given its type. | ||
* | ||
* @param {string|Object} blockTypeOrName Block type or name. | ||
* @param {string} innerHTML Raw block content. | ||
* @param {?Object} attributes Known block attributes (from delimiters). | ||
* @param {string|Object} blockTypeOrName Block type or name. | ||
* @param {string} innerHTML Raw block content. | ||
* @param {?Object} attributes Known block attributes (from | ||
* delimiters). | ||
* @param {WPBlocksParseOptions} parseOptions Parser options. | ||
* | ||
* @return {Object} All block attributes. | ||
*/ | ||
export function getBlockAttributes( blockTypeOrName, innerHTML, attributes = {} ) { | ||
export function getBlockAttributes( blockTypeOrName, innerHTML, attributes = {}, parseOptions ) { | ||
const blockType = normalizeBlockType( blockTypeOrName ); | ||
const blockAttributes = mapValues( blockType.attributes, ( attributeSchema, attributeKey ) => { | ||
return getBlockAttribute( attributeKey, attributeSchema, innerHTML, attributes ); | ||
return getBlockAttribute( attributeKey, attributeSchema, innerHTML, attributes, parseOptions ); | ||
} ); | ||
|
||
return applyFilters( | ||
|
@@ -372,11 +401,12 @@ export function getMigratedBlock( block, parsedAttributes ) { | |
/** | ||
* Creates a block with fallback to the unknown type handler. | ||
* | ||
* @param {Object} blockNode Parsed block node. | ||
* @param {Object} blockNode Parsed block node. | ||
* @param {WPBlocksParseOptions} parseOptions Parser options. | ||
* | ||
* @return {?Object} An initialized block object (if possible). | ||
*/ | ||
export function createBlockWithFallback( blockNode ) { | ||
export function createBlockWithFallback( blockNode, parseOptions ) { | ||
const { blockName: originalName } = blockNode; | ||
let { | ||
attrs: attributes, | ||
|
@@ -445,7 +475,7 @@ export function createBlockWithFallback( blockNode ) { | |
|
||
let block = createBlock( | ||
name, | ||
getBlockAttributes( blockType, innerHTML, attributes ), | ||
getBlockAttributes( blockType, innerHTML, attributes, parseOptions ), | ||
innerBlocks | ||
); | ||
|
||
|
@@ -473,14 +503,18 @@ export function createBlockWithFallback( blockNode ) { | |
* | ||
* @return {Function} An implementation which parses the post content. | ||
*/ | ||
const createParse = ( parseImplementation ) => | ||
( content ) => parseImplementation( content ).reduce( ( memo, blockNode ) => { | ||
const block = createBlockWithFallback( blockNode ); | ||
if ( block ) { | ||
memo.push( block ); | ||
} | ||
return memo; | ||
}, [] ); | ||
function createParse( parseImplementation ) { | ||
return ( content, options = DEFAULT_PARSE_OPTIONS ) => { | ||
return parseImplementation( content ).reduce( ( memo, blockNode ) => { | ||
const block = createBlockWithFallback( blockNode, options ); | ||
if ( block ) { | ||
memo.push( block ); | ||
} | ||
|
||
return memo; | ||
}, [] ); | ||
}; | ||
} | ||
|
||
/** | ||
* Parses the post content with a PegJS grammar and returns a list of blocks. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -59,7 +59,11 @@ export function* setupEditor( post, edits, template ) { | |
content = post.content.raw; | ||
} | ||
|
||
let blocks = parse( content ); | ||
let blocks = parse( content, { | ||
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. Is there any plan to allow, plugin developers to create custom sources and use them in their blocks? |
||
sources: { | ||
meta: ( schema ) => post.meta[ schema.meta ], | ||
}, | ||
} ); | ||
|
||
// Apply a template for new posts only, if exists. | ||
const isNewPost = post.status === 'auto-draft'; | ||
|
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.
Hi @aduth,
Would it make sense for the function to also receive the innerHTML plus the commentAttributes?
Imagining the following scenarios:
- an attribute that is derived from the text in a given HTML element.
- A product block that contains the id of the product as a comment, and we want to query some attributes of that product using an external REST API.