I assume it's because I need onSelect added, but I'm not sure what to
do.
What do I need to add to make it so when the user clicks on the
variation it's entered in the editor?
One way, is add a variation
attribute to your block type, then change the attribute value in the onSelect
callback of the variation picker. That in turn will instruct React to re-render the block (because the block state has changed), hence on that render, we no longer display the variation picker. So basically, the picker is "closed". :)
See example below where the block type renders a simple button with two variations — with an icon, and without it.
// File: index.js
import { registerBlockType } from '@wordpress/blocks';
import {
useBlockProps,
__experimentalBlockVariationPicker as BlockVariationPicker,
} from '@wordpress/block-editor';
import { Button } from '@wordpress/components';
const variations = [{
name: 'button-with-icon',
title: 'Button + Text + Icon',
icon: 'button',
scope: [ 'block' ],
attributes: { icon: 'heart' },
}, {
name: 'button-without-icon',
title: 'Button + Text only',
icon: 'button',
scope: [ 'block' ],
}];
registerBlockType( 'my-blocks/foo-button', {
apiVersion: 2,
title: 'Foo Button',
category: 'formatting',
attributes: {
icon: {
type: 'string',
default: '',
},
variation: {
type: 'string',
default: '',
},
},
edit( { attributes, setAttributes } ) {
if ( ! attributes.variation ) {
return (
<div { ...useBlockProps() }>
<BlockVariationPicker
variations={ variations }
label="Button Variant"
onSelect={ ( variation = variations[0] ) => {
setAttributes( {
...variation.attributes,
variation: variation.name,
});
}}
/>
</div>
);
}
return (
<div { ...useBlockProps() }>
<Button
text="Foo Button"
icon={ attributes.icon }
/>
</div>
);
},
save: ( { attributes } ) => (
<div { ...useBlockProps.save() }>
<Button
text="Foo Button"
icon={ attributes.icon }
/>
</div>
),
} );
However, in the case of inner blocks, you would want to use the same approach as used by the core Columns block (see source on GitHub), where it checks if the current block contains any inner blocks and if yes, the inner blocks are displayed; otherwise, the variation picker is then displayed.
Here's a full working example you can try where I used the same variations in your variations.js
file:
// File: index.js
import {
registerBlockType,
createBlocksFromInnerBlocksTemplate,
} from '@wordpress/blocks';
import {
useBlockProps,
__experimentalUseInnerBlocksProps as useInnerBlocksProps,
store as blockEditorStore,
__experimentalBlockVariationPicker as BlockVariationPicker,
InnerBlocks,
} from '@wordpress/block-editor';
import { useDispatch, useSelect } from '@wordpress/data';
import variations from './variations';
// Note that you can do EditContainer( props ) or EditContainer( { attributes, etc } ).
function EditContainer() {
const blockProps = useBlockProps();
const innerBlocksProps = useInnerBlocksProps( blockProps, {
//allowedBlocks: ALLOWED_BLOCKS,
orientation: 'horizontal',
renderAppender: false,
} );
return <div { ...innerBlocksProps } />;
}
function Placeholder( { clientId, setAttributes } ) {
// Always set a default variation, particularly if allowing skipping the variation picker.
const defaultVariation = variations[0];
// Or do something like this, which selects the variation having "isDefault: true":
// const defaultVariation = variations.filter( item => item.isDefault )[0] || variations[0];
const { replaceInnerBlocks } = useDispatch( blockEditorStore );
const blockProps = useBlockProps();
return (
<div { ...blockProps }>
<BlockVariationPicker
label="Section Variant"
variations={ variations }
onSelect={ ( variation = defaultVariation ) => {
if ( variation.attributes ) {
setAttributes( variation.attributes );
}
if ( variation.innerBlocks ) {
replaceInnerBlocks(
clientId,
createBlocksFromInnerBlocksTemplate(
variation.innerBlocks
),
true
);
}
} }
allowSkip
/>
</div>
);
}
const Edit = ( props ) => {
const { clientId } = props;
const hasInnerBlocks = useSelect(
( select ) =>
select( blockEditorStore ).getBlocks( clientId ).length > 0,
[ clientId ]
);
const Component = hasInnerBlocks
? EditContainer // display the inner blocks
: Placeholder; // or the variation picker
return <Component { ...props } />;
};
registerBlockType( 'my-blocks/foo-section', {
apiVersion: 2,
title: 'Foo Section',
category: 'layout',
edit: Edit,
save: () => (
<div { ...useBlockProps.save( { className: 'foo-section' } ) }>
<InnerBlocks.Content />
</div>
),
} );
So try that and just let me know if you have questions regarding the code. :)