Skip to content
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

Extending WP Button with custom data attributes #16164

Open
sarahannnicholson opened this issue Jun 13, 2019 · 6 comments
Open

Extending WP Button with custom data attributes #16164

sarahannnicholson opened this issue Jun 13, 2019 · 6 comments
Labels
[Feature] Block API API that allows to express the block paradigm. [Feature] Extensibility The ability to extend blocks or the editing experience [Type] Enhancement A suggestion for improvement.

Comments

@sarahannnicholson
Copy link

sarahannnicholson commented Jun 13, 2019

Describe the bug
I extended the Gutenberg Button block, in the save function I used blocks.getSaveContent.extraProps and added a custom data attribute. When a non Super Admin user publishes this button, the data attribute is stripped out. - It works fine for Super Admin permissions.

The blocks.getSaveContent.extraProps filter is described as:
This filter is used to add extra props to the root element of the save function. For example: to add a className, an id, or any valid prop for this element.

The any valid prop doesn't seem to include data attributes. Even if defined in the wp_kses_allowed_html as valid.

Background
I followed Jürg Hunziker's post on how to extend a WP block.

My block of choice was the button block, where I added a text field called "telemetry name" to the inspector controls.
My hopes was to use that the telemetry name entered by the user and add it to a data attribute on the button itself. However, the button seems to be tossing out my data attribute. Even after adding my data attribute with the wp_kses_allowed_html hook.

To reproduce
Steps to reproduce the behavior:

  1. Extend a WP block (eg. button) using Jürg Hunziker's approach.
  2. Ensure in the function that is called on the blocks.getSaveContent.extraProps filter, you add a data attribute, and not just adding classes
  3. Add the wp_kses_allowed_html and define your custom attribute eg) $tags['div']['data-telemetry-name'] = true;
  4. As a Super admin user,
  • Create your block (adding your inspector setting that fills out your data attribute).
  • Publish the page, inspect the page in the WP preview.
  • See that your data attribute is there.
  1. As an Editor level user
  • Create your block (adding your inspector setting that fills out your data attribute).
  • Publish the page, inspect the page in the WP preview.
  • See that your data attribute is not there.

Expected behavior
My expectation is that Editor level users should be able to save the extended block and the block should respect the wp_kses_allowed_html (like it would for adding an iframe - which works fine)

Code

  1. In the index.js of your block extension
import "./styles/style.scss";
import "./styles/editor.scss";

import assign from "lodash.assign";

const { addFilter } = wp.hooks;

import ButtonInspector from "./components/inspector";
import ButtonSave from "./components/save";

// Add custom attibutes to button block
const addCustomAttributes = (settings, name) => {
	// Do nothing if it's another block than our defined ones.
	if (!["core/button"].includes(name)) {
		return settings;
	}

	settings.attributes = assign(settings.attributes, {
		telemetryEventName: {
			type: "string"
		},
	});

	return settings;
};

addFilter(
	"blocks.registerBlockType",
	"extend-block-example/attribute/spacing",
	addCustomAttributes
);

// Add to inspector controls
addFilter("editor.BlockEdit", "custom/button-block", ButtonInspector);

// Add to button save function
addFilter(
	"blocks.getSaveContent.extraProps",
	"custom/button-block-save",
	ButtonSave
);

  1. in ./components/inspector.js

const { createHigherOrderComponent } = wp.compose;
const { Fragment } = wp.element;
const { InspectorControls } = wp.editor;
const {
	PanelBody,
	TextControl,
} = wp.components;
const { __ } = wp.i18n;

/**
 * Create HOC to add custom attributes to the button block
 */
export default createHigherOrderComponent(BlockEdit => {
	return props => {
		// Do nothing if it's another block than our defined ones.
		if (!["core/button"].includes(props.name)) {
			return <BlockEdit {...props} />;
		}

		const {
			telemetryEventName,
		} = props.attributes;

		return (
			<Fragment>
				<BlockEdit {...props} />
				<InspectorControls>
					<PanelBody title={__("Telemetry")} initialOpen={true}>
						<TextControl
							label="Telemetry title"
							help="Fill in title to add telemetry to the button"
							value={telemetryEventName}
							onChange={value =>
								props.setAttributes({
									telemetryEventName: value
								})
							}
						/>
					</PanelBody>
				</InspectorControls>
			</Fragment>
		);
	};
}, "withCustomControls");

  1. in ./components/save.js
import assign from "lodash.assign";

export default (saveElementProps, blockType, attributes) => {
	// Do nothing if it's another block than our defined ones.
	if (!["core/button"].includes(blockType.name)) {
		return saveElementProps;
	}

	assign(saveElementProps, {
		"data-telemetry-name": attributes.telemetryEventName
	});

	return saveElementProps;
};

  1. styles are defined as ./styles/editor.scss and ./styles/style.scss but are blank

  2. in an index.php or some file used in the plugin

function custom_wpkses_post_tags( $tags, $context ) {
	if ( 'post' === $context ) {
            $tags['div']['data-telemetry-name'] = true;
	}
	return $tags;
}
add_filter( 'wp_kses_allowed_html', 'custom_wpkses_post_tags', 10, 2 );

Additional context
Gutenberg version: 5.8.0
WP version: 5.2.1

@swissspidy swissspidy added [Component] Button [Type] Help Request Help with setup, implementation, or "How do I?" questions. [Feature] Block API API that allows to express the block paradigm. labels Jun 14, 2019
@drzraf
Copy link
Contributor

drzraf commented Jan 16, 2020

Same use-case (with the video-block), similar problem. My custom data-* attributes are apparently not even saved. I fear kses could mess-up with them and not being able to pinpoint it.
Is there an alternative, more adequate, hook for such a task?

@enriquesanchez
Copy link
Contributor

Hi @sarahannnicholson!

I'm doing some cleaning and triage of issues and was wondering about the status of this one. Is this is still a problem?

@cesarizu
Copy link

For me it is still happening. I'm just trying to add an id (not a data attribute) to core/button and it's still showing this same behavior.

@enriquesanchez enriquesanchez removed the [Type] Help Request Help with setup, implementation, or "How do I?" questions. label Mar 16, 2020
@sarahannnicholson
Copy link
Author

Hey @enriquesanchez, thank you for asking :)

I'd have to say, this is still an issue. As a workaround we have been adding users as admins because I don't believe this occurs if you're an admin. Not a great workaround though :/

@quantumleap33
Copy link

I have found this nice tutorial, it might be useful for some.

@juanRabaa
Copy link

I have found this nice tutorial, it might be useful for some.

This really helped me. What I was missing was adding the attribute definition through the hook blocks.registerBlockType.
I wouldn't use the blocks.getSaveElement filter though. The new attribute can be saved using the blocks.getSaveContent.extraProps filter.

Step by step, this is what you should do if you want to add a new attribute to an existing block:

  1. Define the attribute (filter: blocks.registerBlockType)
  2. Add custom controls to manipulate the attribute value (filter: editor.BlockEdit)
  3. Save the new attribute value (filter: blocks.getSaveContent.extraProps)
  4. Filter the block render in PHP to add (filter: render_block)

I might be missing something, I'm doing this on the fly. I'll probably extend on this later.

1) Define the attribute

In this examples, I will be adding a new attribute showPhotographer of type boolean.

// Dependencies
import {
    assign
} from 'lodash';
const { createHigherOrderComponent } = wp.compose;
const { InspectorControls } = wp.blockEditor;
const { ToggleControl } = wp.components;

wp.hooks.addFilter(
	'blocks.registerBlockType',
	'ta/image-block-photographer-attr',
	settings => {
                // If the block isn't a `core/image` block, we return the same settings
		if(settings.name != 'core/image')
			return settings;
                
                // Else, we extend the attributes
		settings.attributes = {
			...settings.attributes,
			showPhotographer: {
				type: 'boolean',
				default: '',
			},
		};

		return settings;
	}
);

2) Custom Controls - Extending the block's edit function

Here I extend the BlockEdit through the component TAExtendedImageBlock, which adds a ToggleControl that manipulates the value of the new attribute.

const withInspectorControls = createHigherOrderComponent( ( BlockEdit ) => {
    return ( props ) => {
	const { name } = props;
        
        // If the block isn't a `core/image` block, we return an unmutated component
	if(name != 'core/image')
		return <BlockEdit {...props}/>;

        // Else, we extend the component with controls to manipulate the new attribute.
        return <TAExtendedImageBlock BlockEdit = {BlockEdit} {...props} />;
    };
}, 'withInspectorControl' );

const TAExtendedImageBlock = (props) => {
	const { BlockEdit, setAttributes, attributes } = props;
	const { showPhotographer, id } = attributes;

	return (
		<>
			<BlockEdit { ...props } />
			<InspectorControls>
				<ToggleControl
					label="Mostrar datos del fotógrafo"
			        checked={ showPhotographer }
			        onChange={ () => setAttributes({ showPhotographer: !showPhotographer }) }
				/>
			</InspectorControls>
		</>
	)
}

wp.hooks.addFilter(
    'editor.BlockEdit',
    'ta/image-block-photographer',
    withInspectorControls
);

3) Save the attribute value

function addBackgroundColorStyle( props, blockType, attributes ) {
	const { showPhotographer = false } = attributes;

	if(blockType.name != 'core/image'){
		return props;
	}

    return assign( props, { showPhotographer: showPhotographer } );
}

wp.hooks.addFilter(
    'blocks.getSaveContent.extraProps',
    'ta/save-image-block-photoghapher-attr',
    addBackgroundColorStyle
);

4) Filter the block render in PHP

function ta_extend_image_block($block_content, $block){
	// If block is `core/image` we add new content related to the new attribute
	if ( $block['blockName'] === 'core/image' ){
		$attrs = $block['attrs'];
		if( isset($attrs['showPhotographer']) && $attrs['showPhotographer'] )
			$block_content .= "Has photographer :O";
	}
	return $block_content;
}

add_filter( 'render_block', 'ta_extend_image_block', 10, 2);

@skorasaurus skorasaurus added the [Feature] Extensibility The ability to extend blocks or the editing experience label Sep 4, 2022
@jordesign jordesign added the [Type] Enhancement A suggestion for improvement. label Aug 4, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Block API API that allows to express the block paradigm. [Feature] Extensibility The ability to extend blocks or the editing experience [Type] Enhancement A suggestion for improvement.
Projects
None yet
Development

No branches or pull requests

10 participants