Allow LegacyTemplate block to be reinserted, only on WooCommerce block templates. (https://github.com/woocommerce/woocommerce-blocks/pull/5545)

* Set attribute of legacy template block when block is inserted

* Allow inserter on Woo templates only

* Allow correct legacy block straight from the inserter

* Use WP Data store outside component to register block

* Pass in all attribute data when registering block

* Accommodate templates targetted at specific products or taxonomies

* Replace beginsWith with startsWith

* Replace test data with production data

* Conditionally use stores selector if it exists

* Unsubscribe if the store does not exist

Co-authored-by: Lucio Giannotta <lucio.giannotta@a8c.com>
This commit is contained in:
Tom Cafferkey 2022-01-17 10:50:57 +00:00 committed by GitHub
parent 37bf7a8067
commit cf9c82e02e
5 changed files with 147 additions and 44 deletions

View File

@ -3,7 +3,12 @@
*/
import { __ } from '@wordpress/i18n';
export const TEMPLATES: Record< string, Record< string, string > > = {
/**
* Internal dependencies
*/
import { TemplateAttributes } from './types';
export const TEMPLATES: Record< string, TemplateAttributes > = {
'single-product': {
title: __(
'WooCommerce Single Product Block',

View File

@ -1,6 +1,7 @@
/**
* External dependencies
*/
import { select, subscribe } from '@wordpress/data';
import { registerBlockType } from '@wordpress/blocks';
import { WC_BLOCKS_IMAGE_URL } from '@woocommerce/block-settings';
import { useBlockProps } from '@wordpress/block-editor';
@ -13,24 +14,25 @@ import { box, Icon } from '@wordpress/icons';
*/
import './editor.scss';
import { TEMPLATES } from './constants';
import { getMatchingTemplateData } from './utils';
interface Props {
attributes: {
template: string;
title: string;
placeholder: string;
};
}
const Edit = ( { attributes }: Props ) => {
const blockProps = useBlockProps();
const templateTitle =
TEMPLATES[ attributes.template ]?.title ?? attributes.template;
const templatePlaceholder =
TEMPLATES[ attributes.template ]?.placeholder ?? 'fallback';
const { title, placeholder } = attributes;
return (
<div { ...blockProps }>
<Placeholder
icon={ box }
label={ templateTitle }
label={ title }
className="wp-block-woocommerce-legacy-template__placeholder"
>
<div className="wp-block-woocommerce-legacy-template__placeholder-copy">
@ -53,15 +55,15 @@ const Edit = ( { attributes }: Props ) => {
'This is an editor placeholder for the %s. On your store this will be replaced by the template and display with your product image(s), title, price, etc. You can move this placeholder around and add further blocks around it to extend the template.',
'woo-gutenberg-products-block'
),
templateTitle
title
) }
</p>
</div>
<div className="wp-block-woocommerce-legacy-template__placeholder-wireframe">
<img
className="wp-block-woocommerce-legacy-template__placeholder-image"
src={ `${ WC_BLOCKS_IMAGE_URL }template-placeholders/${ templatePlaceholder }.svg` }
alt={ templateTitle }
src={ `${ WC_BLOCKS_IMAGE_URL }template-placeholders/${ placeholder }.svg` }
alt={ title }
/>
</div>
</Placeholder>
@ -69,39 +71,77 @@ const Edit = ( { attributes }: Props ) => {
);
};
registerBlockType( 'woocommerce/legacy-template', {
title: __( 'WooCommerce Legacy Template', 'woo-gutenberg-products-block' ),
icon: (
<Icon icon={ box } className="wc-block-editor-components-block-icon" />
),
category: 'woocommerce',
apiVersion: 2,
keywords: [ __( 'WooCommerce', 'woo-gutenberg-products-block' ) ],
description: __(
'Renders legacy WooCommerce PHP templates.',
'woo-gutenberg-products-block'
),
supports: {
align: [ 'wide', 'full' ],
html: false,
multiple: false,
reusable: false,
inserter: false,
},
example: {
attributes: {
isPreview: true,
},
},
attributes: {
/**
* Template attribute is used to determine which core PHP template gets rendered.
*/
template: {
type: 'string',
default: 'any',
},
},
edit: Edit,
save: () => null,
let templateId: string | undefined;
const unsubscribe = subscribe( () => {
const store = select( 'core/edit-site' );
if ( ! store ) {
// The store will only exist in the Site Editor so we need to unsubscribe and early return for Posts / Pages.
unsubscribe();
return;
}
templateId = store?.getEditedPostId();
if ( templateId ) {
unsubscribe();
const currentTemplateSlug = templateId?.split( '//' )[ 1 ];
const templateData = getMatchingTemplateData(
TEMPLATES,
currentTemplateSlug
);
// We only want this block to be available for use in specified WooCommerce templates.
const eligibleForInserter = templateData !== null;
const title = templateData?.title ?? currentTemplateSlug;
const placeholder = templateData?.placeholder ?? 'fallback';
registerBlockType( 'woocommerce/legacy-template', {
title,
icon: (
<Icon
icon={ box }
className="wc-block-editor-components-block-icon"
/>
),
category: 'woocommerce',
apiVersion: 2,
keywords: [ __( 'WooCommerce', 'woo-gutenberg-products-block' ) ],
description: __(
'Renders legacy WooCommerce PHP templates.',
'woo-gutenberg-products-block'
),
supports: {
align: [ 'wide', 'full' ],
html: false,
multiple: false,
reusable: false,
inserter: eligibleForInserter,
},
example: {
attributes: {
isPreview: true,
},
},
attributes: {
/**
* Template attribute is used to determine which core PHP template gets rendered.
*/
template: {
type: 'string',
default: currentTemplateSlug,
},
title: {
type: 'string',
default: title,
},
placeholder: {
type: 'string',
default: placeholder,
},
},
edit: Edit,
save: () => null,
} );
}
} );

View File

@ -0,0 +1,30 @@
/**
* Internal dependencies
*/
import { getMatchingTemplateData } from '../utils';
import { TEMPLATES } from '../constants';
describe( 'getMatchingTemplateData', () => {
it( 'should return template data if a correct match has been found', () => {
expect(
getMatchingTemplateData(
TEMPLATES,
'taxonomy-product_cat-winter-collection'
)
).toBe( TEMPLATES[ 'taxonomy-product_cat' ] );
expect( getMatchingTemplateData( TEMPLATES, 'single-product' ) ).toBe(
TEMPLATES[ 'single-product' ]
);
expect(
getMatchingTemplateData( TEMPLATES, 'taxonomy-product_tag' )
).toBe( TEMPLATES[ 'taxonomy-product_tag' ] );
} );
it( 'should return null if given template slug does not match any of the expected options', () => {
expect(
getMatchingTemplateData( TEMPLATES, 'slug-does-not-match' )
).toBe( null );
} );
} );

View File

@ -0,0 +1,4 @@
export type TemplateAttributes = {
title: string;
placeholder: string;
};

View File

@ -0,0 +1,24 @@
/**
* Internal dependencies
*/
import { TemplateAttributes } from './types';
export function beginsWith( needle: string, haystack: string ): boolean {
return haystack.substr( 0, needle.length ) === needle;
}
export function getMatchingTemplateData(
templates: Record< string, TemplateAttributes >,
slug: string
): TemplateAttributes | null {
const templateSlugs = Object.keys( templates );
const matchingSlugs = templateSlugs.filter( ( templateSlug ) =>
slug.startsWith( templateSlug )
);
if ( matchingSlugs.length === 0 ) {
return null;
}
return templates[ matchingSlugs[ 0 ] ];
}