Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Fix corrupt Classic Template placeholders for specific products. #7033

Merged
merged 3 commits into from
Sep 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions assets/js/blocks/classic-template/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@
* External dependencies
*/
import { __ } from '@wordpress/i18n';

export const BLOCK_SLUG = 'woocommerce/legacy-template';

export const TEMPLATES: Record< string, Record< string, string > > = {
/**
* Internal dependencies
*/
import { TemplateDetails } from './types';

export const TEMPLATES: TemplateDetails = {
'single-product': {
title: __(
'WooCommerce Single Product Block',
Expand Down
50 changes: 26 additions & 24 deletions assets/js/blocks/classic-template/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
* External dependencies
*/
import {
Block,
BlockEditProps,
createBlock,
getBlockType,
Expand All @@ -26,6 +25,11 @@ import { useEffect } from '@wordpress/element';
import './editor.scss';
import './style.scss';
import { BLOCK_SLUG, TEMPLATES } from './constants';
import {
isClassicTemplateBlockRegisteredWithAnotherTitle,
hasTemplateSupportForClassicTemplateBlock,
getTemplateDetailsBySlug,
} from './utils';

type Attributes = {
template: string;
Expand All @@ -40,10 +44,12 @@ const Edit = ( {
const { replaceBlock } = useDispatch( 'core/block-editor' );

const blockProps = useBlockProps();
const templateTitle =
TEMPLATES[ attributes.template ]?.title ?? attributes.template;
const templatePlaceholder =
TEMPLATES[ attributes.template ]?.placeholder ?? 'fallback';
const templateDetails = getTemplateDetailsBySlug(
attributes.template,
TEMPLATES
);
const templateTitle = templateDetails?.title ?? attributes.template;
const templatePlaceholder = templateDetails?.placeholder ?? 'fallback';

useEffect(
() =>
Expand Down Expand Up @@ -118,8 +124,6 @@ const Edit = ( {
);
};

const templates = Object.keys( TEMPLATES );

const registerClassicTemplateBlock = ( {
template,
inserter,
Expand All @@ -136,12 +140,13 @@ const registerClassicTemplateBlock = ( {
* See https://github.com/woocommerce/woocommerce-gutenberg-products-block/issues/5861 for more context
*/
registerBlockType( BLOCK_SLUG, {
title: template
? TEMPLATES[ template ].title
: __(
'WooCommerce Classic Template',
'woo-gutenberg-products-block'
),
title:
template && TEMPLATES[ template ]
? TEMPLATES[ template ].title
: __(
'WooCommerce Classic Template',
'woo-gutenberg-products-block'
),
icon: (
<Icon
icon={ box }
Expand Down Expand Up @@ -202,15 +207,6 @@ const registerClassicTemplateBlock = ( {
} );
};

const isClassicTemplateBlockRegisteredWithAnotherTitle = (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
block: Block< any > | undefined,
parsedTemplate: string
) => block?.title !== TEMPLATES[ parsedTemplate ].title;

const hasTemplateSupportForClassicTemplateBlock = ( parsedTemplate: string ) =>
templates.includes( parsedTemplate );

// @todo Refactor when there will be possible to show a block according on a template/post with a Gutenberg API. https://github.com/WordPress/gutenberg/pull/41718

let currentTemplateId: string | undefined;
Expand All @@ -235,7 +231,10 @@ if ( isExperimentalBuild() ) {

if (
block !== undefined &&
( ! hasTemplateSupportForClassicTemplateBlock( parsedTemplate ) ||
( ! hasTemplateSupportForClassicTemplateBlock(
parsedTemplate,
TEMPLATES
) ||
isClassicTemplateBlockRegisteredWithAnotherTitle(
block,
parsedTemplate
Expand All @@ -248,7 +247,10 @@ if ( isExperimentalBuild() ) {

if (
block === undefined &&
hasTemplateSupportForClassicTemplateBlock( parsedTemplate )
hasTemplateSupportForClassicTemplateBlock(
parsedTemplate,
TEMPLATES
)
) {
registerClassicTemplateBlock( {
template: parsedTemplate,
Expand Down
47 changes: 47 additions & 0 deletions assets/js/blocks/classic-template/test/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* Internal dependencies
*/
import { getTemplateDetailsBySlug } from '../utils';

const TEMPLATES = {
'single-product': {
title: 'Single Product Title',
placeholder: 'Single Product Placeholder',
},
'archive-product': {
title: 'Product Archive Title',
placeholder: 'Product Archive Placeholder',
},
'archive-product': {
title: 'Product Archive Title',
placeholder: 'Product Archive Placeholder',
},
'taxonomy-product_cat': {
title: 'Product Taxonomy Title',
placeholder: 'Product Taxonomy Placeholder',
},
};

describe( 'getTemplateDetailsBySlug', function () {
it( 'should return single-product object when given an exact match', () => {
expect(
getTemplateDetailsBySlug( 'single-product', TEMPLATES )
).toBeTruthy();
expect(
getTemplateDetailsBySlug( 'single-product', TEMPLATES )
).toStrictEqual( TEMPLATES[ 'single-product' ] );
} );

it( 'should return single-product object when given a partial match', () => {
expect(
getTemplateDetailsBySlug( 'single-product-hoodie', TEMPLATES )
).toBeTruthy();
expect(
getTemplateDetailsBySlug( 'single-product-hoodie', TEMPLATES )
).toStrictEqual( TEMPLATES[ 'single-product' ] );
} );

it( 'should return null object when given an incorrect match', () => {
expect( getTemplateDetailsBySlug( 'void', TEMPLATES ) ).toBeNull();
} );
} );
1 change: 1 addition & 0 deletions assets/js/blocks/classic-template/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type TemplateDetails = Record< string, Record< string, string > >;
49 changes: 49 additions & 0 deletions assets/js/blocks/classic-template/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* External dependencies
*/
import { Block } from '@wordpress/blocks';

/**
* Internal dependencies
*/
import { TEMPLATES } from './constants';
import { TemplateDetails } from './types';

// Finds the most appropriate template details object for specific template keys such as single-product-hoodie.
export function getTemplateDetailsBySlug(
parsedTemplate: string,
templates: TemplateDetails
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do these functions really need to accept a second argument for templates? Is there a circumstance in which we won't use TEMPLATES from constants as this second argument?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sunyatasattva I intentionally made this a pure function. I find they're easier to read, reuse and predict. Also much easier to unit test (and debug should those tests fail)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright then! Everything else looks good to me.

) {
const templateKeys = Object.keys( templates );
let templateDetails = null;

for ( let i = 0; templateKeys.length > i; i++ ) {
const keyToMatch = parsedTemplate.substr( 0, templateKeys[ i ].length );
const maybeTemplate = templates[ keyToMatch ];
if ( maybeTemplate ) {
templateDetails = maybeTemplate;
break;
}
}

return templateDetails;
}

export function isClassicTemplateBlockRegisteredWithAnotherTitle(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
block: Block< any > | undefined,
parsedTemplate: string
) {
const templateDetails = getTemplateDetailsBySlug(
parsedTemplate,
TEMPLATES
);
return block?.title !== templateDetails?.title;
}

export function hasTemplateSupportForClassicTemplateBlock(
parsedTemplate: string,
templates: TemplateDetails
): boolean {
return getTemplateDetailsBySlug( parsedTemplate, templates ) ? true : false;
}