Fix: Ensure new filter blocks work with inherited query context (https://github.com/woocommerce/woocommerce-blocks/pull/12022)
* make the collection filters block available globally * make query optional * remove unnecessary editor contextual
This commit is contained in:
parent
945e476648
commit
2bc48e4e4a
|
@ -19,9 +19,6 @@
|
||||||
"providesContext": {
|
"providesContext": {
|
||||||
"collectionData": "collectionData"
|
"collectionData": "collectionData"
|
||||||
},
|
},
|
||||||
"ancestor": [
|
|
||||||
"woocommerce/product-collection"
|
|
||||||
],
|
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"collectionData": {
|
"collectionData": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|
|
@ -2,19 +2,10 @@
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor';
|
import { useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor';
|
||||||
import { useSelect } from '@wordpress/data';
|
|
||||||
import { useEffect } from '@wordpress/element';
|
|
||||||
import { useCollection } from '@woocommerce/base-context/hooks';
|
|
||||||
import { sprintf, __ } from '@wordpress/i18n';
|
import { sprintf, __ } from '@wordpress/i18n';
|
||||||
import { getSetting } from '@woocommerce/settings';
|
import { getSetting } from '@woocommerce/settings';
|
||||||
import type { AttributeSetting } from '@woocommerce/types';
|
import type { AttributeSetting } from '@woocommerce/types';
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal dependencies
|
|
||||||
*/
|
|
||||||
import { formatQuery, getQueryParams } from './utils';
|
|
||||||
import type { EditProps } from './type';
|
|
||||||
|
|
||||||
const ATTRIBUTES = getSetting< AttributeSetting[] >( 'attributes', [] );
|
const ATTRIBUTES = getSetting< AttributeSetting[] >( 'attributes', [] );
|
||||||
|
|
||||||
const template = [
|
const template = [
|
||||||
|
@ -63,32 +54,12 @@ if ( firstAttribute ) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Edit = ( { clientId, setAttributes, context }: EditProps ) => {
|
const Edit = () => {
|
||||||
const blockProps = useBlockProps();
|
const blockProps = useBlockProps();
|
||||||
const innerBlockProps = useInnerBlocksProps( blockProps, {
|
const innerBlockProps = useInnerBlocksProps( blockProps, {
|
||||||
template,
|
template,
|
||||||
} );
|
} );
|
||||||
|
|
||||||
// Get inner blocks by clientId
|
|
||||||
const currentBlock = useSelect( ( select ) => {
|
|
||||||
return select( 'core/block-editor' ).getBlock( clientId );
|
|
||||||
} );
|
|
||||||
|
|
||||||
const { results } = useCollection( {
|
|
||||||
namespace: '/wc/store/v1',
|
|
||||||
resourceName: 'products/collection-data',
|
|
||||||
query: {
|
|
||||||
...formatQuery( context.query ),
|
|
||||||
...getQueryParams( currentBlock ),
|
|
||||||
},
|
|
||||||
} );
|
|
||||||
|
|
||||||
useEffect( () => {
|
|
||||||
setAttributes( {
|
|
||||||
collectionData: results,
|
|
||||||
} );
|
|
||||||
}, [ results, setAttributes ] );
|
|
||||||
|
|
||||||
return <nav { ...innerBlockProps } />;
|
return <nav { ...innerBlockProps } />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,10 @@ import { __ } from '@wordpress/i18n';
|
||||||
import { useEffect, useState } from '@wordpress/element';
|
import { useEffect, useState } from '@wordpress/element';
|
||||||
import { BlockControls, useBlockProps } from '@wordpress/block-editor';
|
import { BlockControls, useBlockProps } from '@wordpress/block-editor';
|
||||||
import { getSetting } from '@woocommerce/settings';
|
import { getSetting } from '@woocommerce/settings';
|
||||||
import { useCollection } from '@woocommerce/base-context/hooks';
|
import {
|
||||||
|
useCollection,
|
||||||
|
useCollectionData,
|
||||||
|
} from '@woocommerce/base-context/hooks';
|
||||||
import {
|
import {
|
||||||
AttributeSetting,
|
AttributeSetting,
|
||||||
AttributeTerm,
|
AttributeTerm,
|
||||||
|
@ -41,7 +44,6 @@ const Edit = ( props: EditProps ) => {
|
||||||
attributes: blockAttributes,
|
attributes: blockAttributes,
|
||||||
setAttributes,
|
setAttributes,
|
||||||
debouncedSpeak,
|
debouncedSpeak,
|
||||||
context,
|
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@ -70,6 +72,15 @@ const Edit = ( props: EditProps ) => {
|
||||||
query: { orderby: 'menu_order' },
|
query: { orderby: 'menu_order' },
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
const { results: filteredCounts } = useCollectionData( {
|
||||||
|
queryAttribute: {
|
||||||
|
taxonomy: attributeObject?.taxonomy || '',
|
||||||
|
queryType: blockAttributes.queryType,
|
||||||
|
},
|
||||||
|
queryState: {},
|
||||||
|
isEditor: true,
|
||||||
|
} );
|
||||||
|
|
||||||
const blockProps = useBlockProps();
|
const blockProps = useBlockProps();
|
||||||
|
|
||||||
useEffect( () => {
|
useEffect( () => {
|
||||||
|
@ -103,11 +114,9 @@ const Edit = ( props: EditProps ) => {
|
||||||
|
|
||||||
useEffect( () => {
|
useEffect( () => {
|
||||||
const termIdHasProducts =
|
const termIdHasProducts =
|
||||||
objectHasProp( context.collectionData, 'attribute_counts' ) &&
|
objectHasProp( filteredCounts, 'attribute_counts' ) &&
|
||||||
isAttributeCounts( context.collectionData.attribute_counts )
|
isAttributeCounts( filteredCounts.attribute_counts )
|
||||||
? context.collectionData.attribute_counts.map(
|
? filteredCounts.attribute_counts.map( ( term ) => term.term )
|
||||||
( term ) => term.term
|
|
||||||
)
|
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
if ( termIdHasProducts.length === 0 ) return setAttributeOptions( [] );
|
if ( termIdHasProducts.length === 0 ) return setAttributeOptions( [] );
|
||||||
|
@ -117,7 +126,7 @@ const Edit = ( props: EditProps ) => {
|
||||||
return termIdHasProducts.includes( term.id );
|
return termIdHasProducts.includes( term.id );
|
||||||
} )
|
} )
|
||||||
);
|
);
|
||||||
}, [ attributeTerms, context.collectionData ] );
|
}, [ attributeTerms, filteredCounts ] );
|
||||||
|
|
||||||
const Toolbar = () => (
|
const Toolbar = () => (
|
||||||
<BlockControls>
|
<BlockControls>
|
||||||
|
|
|
@ -15,9 +15,6 @@ export type BlockAttributes = {
|
||||||
|
|
||||||
export interface EditProps extends BlockEditProps< BlockAttributes > {
|
export interface EditProps extends BlockEditProps< BlockAttributes > {
|
||||||
debouncedSpeak: ( label: string ) => void;
|
debouncedSpeak: ( label: string ) => void;
|
||||||
context: {
|
|
||||||
collectionData: unknown[];
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type AttributeCount = {
|
type AttributeCount = {
|
||||||
|
|
|
@ -15,12 +15,9 @@ import {
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import type { FilterComponentProps } from '../types';
|
import type { EditProps } from '../types';
|
||||||
|
|
||||||
export const Inspector = ( {
|
export const Inspector = ( { attributes, setAttributes }: EditProps ) => {
|
||||||
attributes,
|
|
||||||
setAttributes,
|
|
||||||
}: Omit< FilterComponentProps, 'collectionData' > ) => {
|
|
||||||
const { showInputFields, inlineInput } = attributes;
|
const { showInputFields, inlineInput } = attributes;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { useCollectionData } from '@woocommerce/base-context/hooks';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
|
@ -8,10 +13,19 @@ import { getFormattedPrice } from '../utils';
|
||||||
* We pass the whole props from Edit component to <PriceSlider/> so we're
|
* We pass the whole props from Edit component to <PriceSlider/> so we're
|
||||||
* reusing the EditProps type here.
|
* reusing the EditProps type here.
|
||||||
*/
|
*/
|
||||||
export const PriceSlider = ( { attributes, context }: EditProps ) => {
|
export const PriceSlider = ( { attributes }: EditProps ) => {
|
||||||
const { showInputFields } = attributes;
|
const { showInputFields } = attributes;
|
||||||
|
|
||||||
|
const { results, isLoading } = useCollectionData( {
|
||||||
|
queryPrices: true,
|
||||||
|
queryState: {},
|
||||||
|
isEditor: true,
|
||||||
|
} );
|
||||||
|
|
||||||
|
if ( isLoading ) return null;
|
||||||
|
|
||||||
const { minPrice, maxPrice, formattedMinPrice, formattedMaxPrice } =
|
const { minPrice, maxPrice, formattedMinPrice, formattedMaxPrice } =
|
||||||
getFormattedPrice( context.collectionData );
|
getFormattedPrice( results );
|
||||||
|
|
||||||
const onChange = () => null;
|
const onChange = () => null;
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,7 @@ export type BlockAttributes = {
|
||||||
inlineInput: boolean;
|
inlineInput: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface EditProps extends BlockEditProps< BlockAttributes > {
|
export type EditProps = BlockEditProps< BlockAttributes >;
|
||||||
context: {
|
|
||||||
collectionData: unknown[];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export type PriceFilterState = {
|
export type PriceFilterState = {
|
||||||
minPrice?: number;
|
minPrice?: number;
|
||||||
|
@ -23,7 +19,3 @@ export type PriceFilterState = {
|
||||||
formattedMinPrice: string;
|
formattedMinPrice: string;
|
||||||
formattedMaxPrice: string;
|
formattedMaxPrice: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FilterComponentProps = BlockEditProps< BlockAttributes > & {
|
|
||||||
collectionData: Partial< PriceFilterState >;
|
|
||||||
};
|
|
||||||
|
|
|
@ -7,16 +7,12 @@ import { useBlockProps } from '@wordpress/block-editor';
|
||||||
import { Disabled } from '@wordpress/components';
|
import { Disabled } from '@wordpress/components';
|
||||||
import { __ } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { Icon, chevronDown } from '@wordpress/icons';
|
import { Icon, chevronDown } from '@wordpress/icons';
|
||||||
import { ProductQueryContext as Context } from '@woocommerce/blocks/product-query/types';
|
|
||||||
import { CheckboxList } from '@woocommerce/blocks-components';
|
import { CheckboxList } from '@woocommerce/blocks-components';
|
||||||
import Label from '@woocommerce/base-components/filter-element-label';
|
import Label from '@woocommerce/base-components/filter-element-label';
|
||||||
import FormTokenField from '@woocommerce/base-components/form-token-field';
|
import FormTokenField from '@woocommerce/base-components/form-token-field';
|
||||||
import type { BlockEditProps } from '@wordpress/blocks';
|
import type { BlockEditProps } from '@wordpress/blocks';
|
||||||
import { getSetting } from '@woocommerce/settings';
|
import { getSetting } from '@woocommerce/settings';
|
||||||
import {
|
import { useCollectionData } from '@woocommerce/base-context/hooks';
|
||||||
useCollectionData,
|
|
||||||
useQueryStateByContext,
|
|
||||||
} from '@woocommerce/base-context/hooks';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -36,7 +32,7 @@ type StockStatusCount = {
|
||||||
count: number;
|
count: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Edit = ( props: BlockEditProps< BlockProps > & { context: Context } ) => {
|
const Edit = ( props: BlockEditProps< BlockProps > ) => {
|
||||||
const blockProps = useBlockProps( {
|
const blockProps = useBlockProps( {
|
||||||
className: classnames(
|
className: classnames(
|
||||||
'wc-block-stock-filter',
|
'wc-block-stock-filter',
|
||||||
|
@ -50,11 +46,9 @@ const Edit = ( props: BlockEditProps< BlockProps > & { context: Context } ) => {
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
|
||||||
const [ queryState ] = useQueryStateByContext();
|
|
||||||
|
|
||||||
const { results: filteredCounts } = useCollectionData( {
|
const { results: filteredCounts } = useCollectionData( {
|
||||||
queryStock: true,
|
queryStock: true,
|
||||||
queryState,
|
queryState: {},
|
||||||
isEditor: true,
|
isEditor: true,
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
|
|
@ -1,71 +0,0 @@
|
||||||
/**
|
|
||||||
* External dependencies
|
|
||||||
*/
|
|
||||||
import { DEFAULT_QUERY } from '@woocommerce/blocks/product-collection/constants';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal dependencies
|
|
||||||
*/
|
|
||||||
import { sharedParams, mappedParams, formatQuery } from '../utils';
|
|
||||||
|
|
||||||
describe( 'formatQuery: transform Product Collection Block query to Product Collection Data Store API query', () => {
|
|
||||||
it( 'shared param is carried over', () => {
|
|
||||||
const formattedQuery = formatQuery( DEFAULT_QUERY );
|
|
||||||
sharedParams.forEach( ( key ) => {
|
|
||||||
expect( formattedQuery ).toHaveProperty(
|
|
||||||
key,
|
|
||||||
DEFAULT_QUERY[ key ]
|
|
||||||
);
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
|
|
||||||
it( 'mapped param key is transformed', () => {
|
|
||||||
const formattedQuery = formatQuery( DEFAULT_QUERY );
|
|
||||||
mappedParams.forEach( ( { key, map } ) => {
|
|
||||||
expect( formattedQuery ).toHaveProperty(
|
|
||||||
map,
|
|
||||||
DEFAULT_QUERY[ key ]
|
|
||||||
);
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
|
|
||||||
it( 'taxQuery is transformed', () => {
|
|
||||||
const queryWithTax = Object.assign( {}, DEFAULT_QUERY, {
|
|
||||||
taxQuery: {
|
|
||||||
product_cat: [ 1, 2 ],
|
|
||||||
product_tag: [ 3, 4 ],
|
|
||||||
custom_taxonomy: [ 5, 6 ],
|
|
||||||
},
|
|
||||||
} );
|
|
||||||
const formattedQuery = formatQuery( queryWithTax );
|
|
||||||
expect( formattedQuery ).toHaveProperty( 'cat', [ 1, 2 ] );
|
|
||||||
expect( formattedQuery ).toHaveProperty( 'tag', [ 3, 4 ] );
|
|
||||||
expect( formattedQuery ).toHaveProperty(
|
|
||||||
'_unstable_tax_custom_taxonomy',
|
|
||||||
[ 5, 6 ]
|
|
||||||
);
|
|
||||||
} );
|
|
||||||
|
|
||||||
it( 'attribute query is transformed', () => {
|
|
||||||
const woocommerceAttributes = [
|
|
||||||
{ termId: 11, taxonomy: 'pa_size' },
|
|
||||||
{ termId: 12, taxonomy: 'pa_color' },
|
|
||||||
{ termId: 13, taxonomy: 'pa_custom' },
|
|
||||||
{ termId: 14, taxonomy: 'pa_custom' },
|
|
||||||
];
|
|
||||||
const queryWithAttributes = Object.assign( {}, DEFAULT_QUERY, {
|
|
||||||
woocommerceAttributes,
|
|
||||||
} );
|
|
||||||
const formattedQuery = formatQuery( queryWithAttributes );
|
|
||||||
|
|
||||||
expect( formattedQuery ).toHaveProperty( 'attributes' );
|
|
||||||
|
|
||||||
woocommerceAttributes.forEach( ( { termId, taxonomy } ) => {
|
|
||||||
expect( formattedQuery.attributes ).toEqual(
|
|
||||||
expect.arrayContaining( [
|
|
||||||
{ term_id: termId, attribute: taxonomy },
|
|
||||||
] )
|
|
||||||
);
|
|
||||||
} );
|
|
||||||
} );
|
|
||||||
} );
|
|
|
@ -1,15 +0,0 @@
|
||||||
/**
|
|
||||||
* External dependencies
|
|
||||||
*/
|
|
||||||
import type { BlockEditProps } from '@wordpress/blocks';
|
|
||||||
import type { ProductCollectionQuery } from '@woocommerce/blocks/product-collection/types';
|
|
||||||
|
|
||||||
type BlockAttributes = {
|
|
||||||
collectionData: unknown[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface EditProps extends BlockEditProps< BlockAttributes > {
|
|
||||||
context: {
|
|
||||||
query: ProductCollectionQuery;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,116 +0,0 @@
|
||||||
/**
|
|
||||||
* External dependencies
|
|
||||||
*/
|
|
||||||
import { objectHasProp } from '@woocommerce/types';
|
|
||||||
import type { BlockInstance } from '@wordpress/blocks';
|
|
||||||
import type { ProductCollectionQuery } from '@woocommerce/blocks/product-collection/types';
|
|
||||||
|
|
||||||
function mergeAttributeParams(
|
|
||||||
acc: Record< string, unknown >,
|
|
||||||
innerBlock: BlockInstance
|
|
||||||
) {
|
|
||||||
const current =
|
|
||||||
( acc?.calculate_attribute_counts as Array< unknown > ) ?? [];
|
|
||||||
acc.calculate_attribute_counts = [
|
|
||||||
...current,
|
|
||||||
innerBlock.attributes.queryParam.calculate_attribute_counts,
|
|
||||||
];
|
|
||||||
return acc;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getInnerBlocksParams(
|
|
||||||
block: BlockInstance,
|
|
||||||
initial: Record< string, unknown > = {}
|
|
||||||
) {
|
|
||||||
return block.innerBlocks.reduce(
|
|
||||||
( acc, innerBlock ): Record< string, unknown > => {
|
|
||||||
if (
|
|
||||||
objectHasProp(
|
|
||||||
innerBlock.attributes.queryParam,
|
|
||||||
'calculate_attribute_counts'
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
acc = mergeAttributeParams( acc, innerBlock );
|
|
||||||
} else {
|
|
||||||
acc = { ...acc, ...innerBlock.attributes?.queryParam };
|
|
||||||
}
|
|
||||||
|
|
||||||
return getInnerBlocksParams( innerBlock, acc );
|
|
||||||
},
|
|
||||||
initial
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getQueryParams( block: BlockInstance | null ) {
|
|
||||||
if ( ! block ) return {};
|
|
||||||
|
|
||||||
return getInnerBlocksParams( block );
|
|
||||||
}
|
|
||||||
|
|
||||||
export const sharedParams: Array< keyof ProductCollectionQuery > = [
|
|
||||||
'exclude',
|
|
||||||
'offset',
|
|
||||||
'search',
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* There is an open dicussion around the shape of this object. Check it out on GH.
|
|
||||||
*
|
|
||||||
* @see {@link https://github.com/woocommerce/woocommerce-blocks/pull/11218#discussion_r1365171167 | #11218 review comment}.
|
|
||||||
*/
|
|
||||||
export const mappedParams: {
|
|
||||||
key: keyof ProductCollectionQuery;
|
|
||||||
map: string;
|
|
||||||
}[] = [
|
|
||||||
{ key: 'woocommerceStockStatus', map: 'stock_status' },
|
|
||||||
{ key: 'woocommerceOnSale', map: 'on_sale' },
|
|
||||||
{ key: 'woocommerceHandPickedProducts', map: 'include' },
|
|
||||||
];
|
|
||||||
|
|
||||||
function mapTaxonomy( taxonomy: string ) {
|
|
||||||
const map = {
|
|
||||||
product_tag: 'tag',
|
|
||||||
product_cat: 'cat',
|
|
||||||
};
|
|
||||||
|
|
||||||
return map[ taxonomy as keyof typeof map ] || `_unstable_tax_${ taxonomy }`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTaxQueryMap( taxQuery: ProductCollectionQuery[ 'taxQuery' ] ) {
|
|
||||||
return Object.entries( taxQuery ).map( ( [ taxonomy, terms ] ) => ( {
|
|
||||||
[ mapTaxonomy( taxonomy ) ]: terms,
|
|
||||||
} ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAttributeQuery(
|
|
||||||
woocommerceAttributes: ProductCollectionQuery[ 'woocommerceAttributes' ]
|
|
||||||
) {
|
|
||||||
if ( ! woocommerceAttributes ) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return woocommerceAttributes.map( ( attribute ) => ( {
|
|
||||||
attribute: attribute.taxonomy,
|
|
||||||
term_id: attribute.termId,
|
|
||||||
} ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
export function formatQuery( query: ProductCollectionQuery ) {
|
|
||||||
if ( ! query ) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return Object.assign(
|
|
||||||
{
|
|
||||||
attributes: getAttributeQuery( query.woocommerceAttributes ),
|
|
||||||
catalog_visibility: 'visible',
|
|
||||||
},
|
|
||||||
...sharedParams.map(
|
|
||||||
( key ) => key in query && { [ key ]: query[ key ] }
|
|
||||||
),
|
|
||||||
...mappedParams.map(
|
|
||||||
( param ) =>
|
|
||||||
param.key in query && { [ param.map ]: query[ param.key ] }
|
|
||||||
),
|
|
||||||
...getTaxQueryMap( query.taxQuery )
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -128,7 +128,7 @@ final class CollectionFilters extends AbstractBlock {
|
||||||
$response = Package::container()->get( Hydration::class )->get_rest_api_response_data(
|
$response = Package::container()->get( Hydration::class )->get_rest_api_response_data(
|
||||||
add_query_arg(
|
add_query_arg(
|
||||||
array_merge(
|
array_merge(
|
||||||
$this->get_formatted_products_params( $block->context['query'] ),
|
$this->get_formatted_products_params( $block->context['query'] ?? array() ),
|
||||||
$collection_data_params,
|
$collection_data_params,
|
||||||
),
|
),
|
||||||
'/wc/store/v1/products/collection-data'
|
'/wc/store/v1/products/collection-data'
|
||||||
|
|
Loading…
Reference in New Issue