From c7d4c882704868a3a3c4a8d33c7dc4850770cba7 Mon Sep 17 00:00:00 2001 From: Nathan Silveira Date: Tue, 23 Jan 2024 17:50:36 -0300 Subject: [PATCH] Expose metadata as object from useProductMetadata hook (#43860) * Add useParentMetadata hook * Move functionality to existing useProductMetadata hook * Update changelog * Remove deleted hook from index * Update useProductMetadata API and unit tests --- .../changelog/add-useparentmetadata | 4 + .../hooks/test/use-product-metadata.test.ts | 91 ++++++++++++------- .../src/hooks/use-product-metadata.ts | 66 ++++++++++---- 3 files changed, 113 insertions(+), 48 deletions(-) create mode 100644 packages/js/product-editor/changelog/add-useparentmetadata diff --git a/packages/js/product-editor/changelog/add-useparentmetadata b/packages/js/product-editor/changelog/add-useparentmetadata new file mode 100644 index 00000000000..8aaacc794d3 --- /dev/null +++ b/packages/js/product-editor/changelog/add-useparentmetadata @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Expose metadata as object from useProductMetadata hook diff --git a/packages/js/product-editor/src/hooks/test/use-product-metadata.test.ts b/packages/js/product-editor/src/hooks/test/use-product-metadata.test.ts index 26b7a5aa7cd..433f8f36466 100644 --- a/packages/js/product-editor/src/hooks/test/use-product-metadata.test.ts +++ b/packages/js/product-editor/src/hooks/test/use-product-metadata.test.ts @@ -10,24 +10,36 @@ import useProductMetadata from '../use-product-metadata'; const mockFnMetadataProp = jest.fn(); jest.mock( '@wordpress/core-data', () => ( { - useEntityProp: jest.fn().mockImplementation( () => { - return [ - [ - { - key: 'field1', - value: 'value1', - }, - { - key: 'field2', - value: 'value1', - }, - { - key: 'existing_field', - value: 'value1', - }, - ], - mockFnMetadataProp, - ]; + useEntityId: jest.fn().mockReturnValue( 123 ), +} ) ); + +jest.mock( '@wordpress/data', () => ( { + useSelect: jest.fn().mockImplementation( ( callback ) => { + return callback( + jest.fn().mockReturnValue( { + getEditedEntityRecord: () => ( { + meta_data: [ + { + key: 'field1', + value: 'value1', + }, + { + key: 'field2', + value: 'value1', + }, + { + key: 'existing_field', + value: 'value1', + }, + ], + } ), + } ) + ); + } ), + useDispatch: jest.fn().mockImplementation( () => { + return { + editEntityRecord: mockFnMetadataProp, + }; } ), } ) ); @@ -45,19 +57,36 @@ describe( 'useProductMetadata', () => { value: 'value2', }, ] ); - expect( mockFnMetadataProp ).toHaveBeenCalledWith( [ + expect( mockFnMetadataProp ).toHaveBeenCalledWith( + 'postType', + 'product', + 123, { - key: 'existing_field', - value: 'value1', - }, - { - key: 'field1', - value: 'value2', - }, - { - key: 'field2', - value: 'value2', - }, - ] ); + meta_data: [ + { + key: 'existing_field', + value: 'value1', + }, + { + key: 'field1', + value: 'value2', + }, + { + key: 'field2', + value: 'value2', + }, + ], + } + ); + } ); + it( 'should return the metadata as an object for easy readings', async () => { + const { metadata } = renderHook( () => + useProductMetadata( { postType: 'product', id: 123 } ) + ).result.current; + expect( metadata ).toEqual( { + field1: 'value1', + field2: 'value1', + existing_field: 'value1', + } ); } ); } ); diff --git a/packages/js/product-editor/src/hooks/use-product-metadata.ts b/packages/js/product-editor/src/hooks/use-product-metadata.ts index 69e487caf47..02775462d6f 100644 --- a/packages/js/product-editor/src/hooks/use-product-metadata.ts +++ b/packages/js/product-editor/src/hooks/use-product-metadata.ts @@ -1,30 +1,62 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore No types for this exist yet. +// eslint-disable-next-line @woocommerce/dependency-group +import { useEntityId } from '@wordpress/core-data'; /** * External dependencies */ -import { useEntityProp } from '@wordpress/core-data'; +import { useDispatch, useSelect } from '@wordpress/data'; +import { Product } from '@woocommerce/data'; /** * Internal dependencies */ import { Metadata } from '../types'; -function useProductMetadata( postType?: string ) { - const [ metadata, setMetadata ] = useEntityProp< Metadata< string >[] >( - 'postType', - postType || 'product', - 'meta_data' - ); +interface Options { + postType?: string; + id?: number; +} - return { - updateMetadata: ( entries: Metadata< string >[] ) => { - setMetadata( [ - ...metadata.filter( - ( item ) => - entries.findIndex( ( e ) => e.key === item.key ) === -1 - ), - ...entries, - ] ); +function useProductMetadata( options?: Options ) { + const postType = options?.postType || 'product'; + const thisId = useEntityId( 'postType', postType ); + const id = options?.id || thisId; + + // @ts-expect-error There are no types for this. + const { editEntityRecord } = useDispatch( 'core' ); + + return useSelect( + ( select ) => { + // @ts-expect-error There are no types for this. + const { getEditedEntityRecord } = select( 'core' ); + const { meta_data: metadata }: Product = getEditedEntityRecord( + 'postType', + postType, + id + ); + + return { + metadata: metadata.reduce( function ( acc, cur ) { + acc[ cur.key ] = cur.value; + return acc; + }, {} as Record< string, string | undefined > ), + updateMetadata: ( entries: Metadata< string >[] ) => { + editEntityRecord( 'postType', postType, id, { + meta_data: [ + ...metadata.filter( + ( item ) => + entries.findIndex( + ( e ) => e.key === item.key + ) === -1 + ), + ...entries, + ], + } ); + }, + }; }, - }; + [ id ] + ); } export default useProductMetadata;