Skip to content

Commit

Permalink
Determine _product_template_id from WP filter (woocommerce#47762)
Browse files Browse the repository at this point in the history
* Remove template matching from front-end

* Create 'woocommerce_rest_get_product' filter to allow extensions to change the product when it's fetched through the REST API

* Stop defaulting to 'standard-product-template' product template and handle defaults only in getLayoutTemplateId function

* Use created hook to determine and persist the product template ID when it's not defined.

Also introduce 'match_fn' in ProductTemplate to allow extensions to provide robust ways of matching the template

* Add changelog

* Fix tests

* Remove outdated tests

* Fix issue in filter because $product can be false and move API change to v3

* Add comments to continue statements

* Small refactor

* Only send layout template id to useLayoutTemplate after product is loaded

* Revert front-end changes

* Remove match_fn and create filter to allow extensions to determine the product template id

* Avoid loading layout template before product is loaded

* Create woocommerce_product_editor_determine_product_template filter and use it

* Update changelogs and remove unused code

* Remove _product_template_id for products that were created with the new product editor.

* Use only id to find productTemplate

* Turn hook into experimental

* Remove deprecated tests

* Rename filter

* Add more typings to useEntityRecord

* Use hasResolved boolean to check whether product has been resolved

* Add changelog

* Add 'variation' to ProductType

* Don't default to the standard-product-template for variations

* Update changelog

* Accept null in getLayoutTemplateId

* Fix edit variable product test

* Rename hook

* Revert changed logic to avoid regressions

* Increment useProductTemplate logic

* Default to standard-product-template instead of undefined when no matches

* Re-import Features class

---------

Co-authored-by: Jon Lane <jon.lane@automattic.com>
  • Loading branch information
nathanss and Jon Lane authored Jun 7, 2024
1 parent 7c2b171 commit 442adcc
Show file tree
Hide file tree
Showing 13 changed files with 90 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: tweak

Change useLayoutTemplate signature from 'undefined' to 'null'
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { useEntityRecord } from '@wordpress/core-data';
// eslint-disable-next-line @woocommerce/dependency-group
import { dispatch, select } from '@wordpress/data';

export const useLayoutTemplate = ( layoutTemplateId: string | undefined ) => {
export const useLayoutTemplate = ( layoutTemplateId: string | null ) => {
const [ isEntityRegistered, setIsEntityRegistered ] = useState( false );

useEffect( () => {
Expand Down
4 changes: 4 additions & 0 deletions packages/js/data/changelog/add-backend-template-match
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: add

Add 'variation' to ProductType
7 changes: 6 additions & 1 deletion packages/js/data/src/products/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ import { ProductCategory } from '../product-categories/types';
import { ProductTag } from '../product-tags/types';
import { BaseQueryParams } from '../types';

export type ProductType = 'simple' | 'grouped' | 'external' | 'variable';
export type ProductType =
| 'simple'
| 'grouped'
| 'external'
| 'variable'
| 'variation';
export type ProductStatus =
| 'auto-draft'
| 'deleted'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: fix

Tweak useProductTemplate logic and avoid loading layout template before product is loaded
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ const ModalEditor = lazy( () =>
);

function getLayoutTemplateId(
productTemplate: ProductTemplate | undefined,
productTemplate: ProductTemplate | undefined | null,
postType: string
) {
if ( productTemplate?.layoutTemplateId ) {
Expand Down Expand Up @@ -184,7 +184,7 @@ export function BlockEditor( {
};
}, [ settingsGlobal ] );

const { editedRecord: product } = useEntityRecord< Product >(
const { editedRecord: product, hasResolved } = useEntityRecord< Product >(
'postType',
postType,
productId,
Expand All @@ -203,11 +203,11 @@ export function BlockEditor( {

const { productTemplate } = useProductTemplate(
productTemplateId,
product
hasResolved ? product : null
);

const { layoutTemplate } = useLayoutTemplate(
getLayoutTemplateId( productTemplate, postType )
hasResolved ? getLayoutTemplateId( productTemplate, postType ) : null
);

const [ blocks, onInput, onChange ] = useEntityBlockEditor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,27 +109,19 @@ describe( 'useProductTemplate', () => {
expect( result.current.productTemplate?.id ).toEqual( 'template-2' );
} );

it( 'should return undefined if no matching product template by id or type', () => {
it( 'should return standard-product-template if no matching product template by id or type', () => {
const { result } = renderHook( () =>
useProductTemplate( 'invalid-template-id', { type: 'external' } )
);

expect( result.current.productTemplate ).toBeUndefined();
} );

it( 'should use the standard product template if no templateId is provided', () => {
const { result } = renderHook( () =>
useProductTemplate( undefined, { type: 'simple' } )
);

expect( result.current.productTemplate?.id ).toEqual(
'standard-product-template'
);
} );

it( 'should use the product type to match if the product template id matches a template with a different product type', () => {
it( 'should match to a template with the same type if no template id is provided', () => {
const { result } = renderHook( () =>
useProductTemplate( 'template-2', { type: 'simple' } )
useProductTemplate( undefined, { type: 'simple' } )
);

expect( result.current.productTemplate?.id ).toEqual( 'template-1' );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,24 +59,25 @@ function findBetterMatchTemplate( matchingTemplates: ProductTemplate[] ) {

export const useProductTemplate = (
productTemplateId: string | undefined,
product: Partial< Product > | undefined | null
product: Partial< Product > | null
) => {
const productTemplates =
window.productBlockEditorSettings?.productTemplates ?? [];

const productType = product?.type;

const productTemplateIdToFind =
productTemplateId || 'standard-product-template';
// we shouldn't default to the standard-product-template for variations
if ( ! productTemplateId && productType === 'variation' ) {
return { productTemplate: null, isResolving: false };
}

const productTypeToFind =
productType === 'variable' ? 'simple' : productType;
let matchingProductTemplate: ProductTemplate | undefined;

let matchingProductTemplate = productTemplates.find(
( productTemplate ) =>
productTemplate.id === productTemplateIdToFind &&
productTemplate.productData.type === productTypeToFind
);
if ( productTemplateId ) {
matchingProductTemplate = productTemplates.find(
( productTemplate ) => productTemplate.id === productTemplateId
);
}

if ( ! matchingProductTemplate && product ) {
// Look for matching templates based on product data described on each template.
Expand All @@ -86,7 +87,13 @@ export const useProductTemplate = (
);

// If there are multiple matching templates, we should use the one with the most matching fields.
matchingProductTemplate = findBetterMatchTemplate( matchingTemplates );
// If there is no matching template, we should default to the standard product template.
matchingProductTemplate =
findBetterMatchTemplate( matchingTemplates ) ||
productTemplates.find(
( productTemplate ) =>
productTemplate.id === 'standard-product-template'
);
}

// When we switch to getting the product template from the API,
Expand Down
2 changes: 1 addition & 1 deletion packages/js/product-editor/typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ declare module '@wordpress/core-data' {
name: string,
id: number | string,
options?: { enabled: boolean }
): { record: T, editedRecord: T };
): { record: T, editedRecord: T, isResolving: boolean, hasResolved: boolean };
}
declare module '@wordpress/keyboard-shortcuts' {
function useShortcut(name: string, callback: (event: KeyboardEvent) => void): void;
Expand Down
4 changes: 4 additions & 0 deletions plugins/woocommerce/changelog/add-backend-template-match
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: add

Determine _product_template_id from 'woocommerce_product_editor_determine_product_template' filter
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,9 @@ public static function save( $post_id, $post ) {
WC_Admin_Meta_Boxes::add_error( $errors->get_error_message() );
}

// Remove _product_template_id for products that were created with the new product editor.
$product->delete_meta_data( '_product_template_id' );

/**
* Set props before save.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

use Automattic\WooCommerce\Internal\Features\ProductBlockEditor\ProductTemplates\SimpleProductTemplate;
use Automattic\WooCommerce\Internal\Features\ProductBlockEditor\ProductTemplates\ProductVariationTemplate;

use WC_Meta_Data;
use WP_Block_Editor_Context;

/**
Expand Down Expand Up @@ -72,6 +72,8 @@ public function __construct() {
add_filter( 'register_block_type_args', array( $this, 'register_metadata_attribute' ) );
add_filter( 'woocommerce_get_block_types', array( $this, 'get_block_types' ), 999, 1 );

add_filter( 'woocommerce_rest_prepare_product_object', array( $this, 'possibly_add_template_id' ), 10, 2 );

// Make sure the block registry is initialized so that core blocks are registered.
BlockRegistry::get_instance();

Expand All @@ -82,6 +84,37 @@ public function __construct() {
}
}


/**
* Adds the product template ID to the product if it doesn't exist.
*
* @param WP_REST_Response $response The response object.
* @param WC_Product $product The product.
*/
public function possibly_add_template_id( $response, $product ) {
if ( ! $product ) {
return $response;
}
if ( ! $product->meta_exists( '_product_template_id' ) ) {
/**
* Experimental: Allows to determine a product template id based on the product data.
*
* @ignore
* @since 9.1.0
*/
$product_template_id = apply_filters( 'experimental_woocommerce_product_editor_product_template_id_for_product', '', $product );
if ( $product_template_id ) {
$response->data['meta_data'][] = new WC_Meta_Data(
array(
'key' => '_product_template_id',
'value' => $product_template_id,
)
);
}
}
return $response;
}

/**
* Enqueue scripts needed for the product form block editor.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ test.describe( 'Variations tab', () => {
`/wp-admin/admin.php?page=wc-admin&path=/product/${ productId_editVariations }`
);

await disableVariableProductBlockTour( { page } );

await clickOnTab( 'Variations', page );

await page
Expand Down Expand Up @@ -232,6 +234,8 @@ test.describe( 'Variations tab', () => {
.getByRole( 'tab', { name: 'General' } )
.click();

await page.getByLabel( 'Regular price', { exact: true } ).click();

await page
.getByLabel( 'Regular price', { exact: true } )
.waitFor( { state: 'visible' } );
Expand Down

0 comments on commit 442adcc

Please sign in to comment.