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

Possibility to extend any block settings from the plugin? #2474

Closed
lumberman opened this Issue Aug 19, 2017 · 8 comments

Comments

Projects
None yet
6 participants
@lumberman

lumberman commented Aug 19, 2017

I'm wondering if you have in plans anything like filters or hooks we have in php?
For example I want to extend any block settings with my custom dropdown selector that will show/hide a block from mobile. This dropdown will add/remove additional class to the block classes. Is there a way to do that?

Thanks for your work.

@youknowriad

This comment has been minimized.

Contributor

youknowriad commented Aug 23, 2017

Extensibility is an unsolved problem right now, there are several explorations but nothing settled yet:

@justintadlock

This comment has been minimized.

justintadlock commented Aug 31, 2017

Just following this. This would essentially allow me to deprecate both a meta box and shortcode in one plugin in exchange for something far cooler and more powerful for users.

@aduth

This comment has been minimized.

Member

aduth commented Sep 7, 2017

In considering #2507, I've come to realize where we currently have shared functionality with ad-hoc implementations (generated class names, custom class names, anchor) would be a good opportunity for proving a pattern of extensibility. For example, rather than injecting classes into a block from the serializer, we could consider the class names behavior an extension or mixin of the block.

There are a few variations and considerations here:

  • At what point do these extensions take place? If a block implementer wants to opt in to class name or anchor behavior, should they be able to do so from the block type definition itself?
  • Externally, should a plugin author have options to extend a single block, all blocks globally, or both?
  • Should extending a block replace the current implementation, or create a new separate block with its own name.
    • It's important to recognize here that we want to avoid cases where extending a block could cause content to become invalid when the extension is applied (or later removed)

I think there's an option to special-case specific built-in extensions such as the class names and anchors behavior we already have. As explored in #2507, these could live on the block definition itself under a supports array. Each of these could map to a specific extension which should then be applied to the block before it is registered.

Externally, how could this look? We could take an object-oriented inheritance pattern of extending a specific block. Block types are not proper JavaScript classes, so we can't currently extends ImageBlock, but we could achieve similar with custom APIs:

// wp.blocks.extendBlocks( ( originalBlock ) => {
wp.blocks.extendBlock( 'core/image', ( originalBlock ) => {
	attributes: {
		className: {
			type: 'string',
		},
	},

	edit( props ) {
		const element = originalBlock.save( props );

		const { attributes, setAttributes } = props;
		const { className } = attributes;

		return [
			element,
			<InspectorControls>
				<TextControl
					label={ __( 'Additional CSS Class' ) }
					value={ className }
					onChange={ ( nextClassName ) => {
						setAttributes( { className: nextClassName } );
					} }
				/>
			</InspectorControls>
		];
	},

	save( props ) {
		const element = originalBlock.save( props );
		const { className } = props.attributes;

		return cloneElement( element, {
			className: [ element.props.className || '', className ].join( ' ' ),
		} );
	},
} );

The idea here being:

  • The extension can add additional attributes
  • The extension can override existing methods, and has access to their original implementations

For a less object-oriented approach, another alternative could be to consider a middleware / filters type model, where an extension could hook before or after particular events in the lifecycle of a block to override the default behavior.

Some inspiration and prior art here could be:

  • Redux middlewares, which have full control over when the stack progresses (next) and can therefore perform actions before or after the existing default behavior
  • The proposed JavaScript hooks package where, for example, a block could be passed through a named filter before registration, or an action invoked when a block is serialized

Filtering a block prior to registration:

addFilter( 'blocks.register', ( blockType ) => {
	if ( ! includes( blockType.supports, 'className' ) {
		return blockType;
	}

	return {
		...blockType,
		attributes: {
			...blockType.attributes,
			className: {
				type: 'string',
			},
		},
		edit( props ) {
			const element = blockType.save( props );

			const { attributes, setAttributes } = props;
			const { className } = attributes;

			return [
				element,
				<InspectorControls>
					<TextControl
						label={ __( 'Additional CSS Class' ) }
						value={ className }
						onChange={ ( nextClassName ) => {
							setAttributes( { className: nextClassName } );
						} }
					/>
				</InspectorControls>
			];			
		},
		save( props ) {
			const element = blockType.save( props );
			const { className } = props.attributes;

			return cloneElement( element, {
				className: [ element.props.className || '', className ].join( ' ' ),
			} );
		},
	};
} );

More granular, adding props to a block before serializing: (Note: Unclear how we detect support for or maintain the value of the className)

addFilter( 'blocks.serialize.props', ( props, block ) => {
	const { className } = block.attributes;
	if ( className ) {
		props = { ...props, className };
	}

	return props;
} );
@lumberman

This comment has been minimized.

lumberman commented Sep 7, 2017

Hooks/filters like behaviour is clear for most of WP developers.

@lumberman

This comment has been minimized.

lumberman commented Oct 18, 2017

@aduth any new ideas or movements on blocks extensibility?

I have a few plugin ideas in mind and all of them can't be implemented because we have no access to the already defined blocks from the outside. If extensibility is a big and hard task can we at least start from the Additional Custom Class for every default block?

Do you have any plans to solve block extensibility issues before the editor release or it's a task for later (after Gutenberg merge)?

Thank you very much!

@aduth

This comment has been minimized.

Member

aduth commented Oct 19, 2017

@lumberman My understanding is that improved extensibility is still aimed to be included in an initial Gutenberg release, but must be balanced against priority of the basic content authoring experience. Internally, we still see a need for a consolidated "block supports" approach, recently reiterated in #3037. Since my previous comment, the JavaScript hooks package has been published to npm and is seeing continued iterations at WordPress/packages#40. Hooks are in need of a proven real-world use-case and these block supports be a good near-term opportunity to explore.

@phpbits

This comment has been minimized.

phpbits commented Jan 9, 2018

Following this one too :)

@mweichert

This comment has been minimized.

mweichert commented Jan 11, 2018

Following

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