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

Commit

Permalink
Fix: Make filter block contextual - Editor (#11218)
Browse files Browse the repository at this point in the history
  • Loading branch information
dinhtungdu authored Nov 6, 2023
1 parent c5afddb commit 7cef728
Show file tree
Hide file tree
Showing 8 changed files with 199 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
"supports": {
"interactivity": true
},
"usesContext": [
"query"
],
"attributes": {
"showInputFields": {
"type": "boolean",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useBlockProps } from '@wordpress/block-editor';
import { useCollectionData } from '@woocommerce/base-context/hooks';
import { Disabled } from '@wordpress/components';
import FilterResetButton from '@woocommerce/base-components/filter-reset-button';
import { formatQuery } from '@woocommerce/blocks/collection-filters/utils';

/**
* Internal dependencies
Expand All @@ -15,10 +16,11 @@ import { PriceSlider } from './price-slider';

const Edit = ( props: EditProps ) => {
const blockProps = useBlockProps();
const { query } = props.context;
const { results } = useCollectionData( {
queryPrices: true,
isEditor: true,
queryState: {},
queryState: formatQuery( query ),
} );

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ import {
/**
* Internal dependencies
*/
import { EditProps } from '../types';
import { FilterComponentProps } from '../types';

export const Inspector = ( { attributes, setAttributes }: EditProps ) => {
export const Inspector = ( {
attributes,
setAttributes,
}: Omit< FilterComponentProps, 'collectionData' > ) => {
const { showInputFields, inlineInput } = attributes;
return (
<InspectorControls>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/
import { HTMLElementEvent } from '@woocommerce/types';
import { BlockEditProps } from '@wordpress/blocks';
import { ProductCollectionQuery } from '@woocommerce/blocks/product-collection/types';

type PriceFilterState = {
minPrice: number;
Expand Down Expand Up @@ -31,7 +32,11 @@ export type BlockAttributes = {
inlineInput: boolean;
};

export type EditProps = BlockEditProps< BlockAttributes >;
export interface EditProps extends BlockEditProps< BlockAttributes > {
context: {
query: ProductCollectionQuery;
};
}

export type FilterComponentProps = BlockEditProps< BlockAttributes > & {
collectionData: Partial< PriceFilterState >;
Expand Down
71 changes: 71 additions & 0 deletions assets/js/blocks/collection-filters/test/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* 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 },
] )
);
} );
} );
} );
77 changes: 77 additions & 0 deletions assets/js/blocks/collection-filters/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* External dependencies
*/
import { ProductCollectionQuery } from '@woocommerce/blocks/product-collection/types';

export const sharedParams: Array< keyof ProductCollectionQuery > = [
'exclude',
'offset',
'order',
'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: 'orderBy', map: 'orderby' },
{ key: 'pages', map: 'page' },
{ key: 'parents', map: 'parent' },
{ key: 'perPage', map: 'per_page' },
{ 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 {
attributes: woocommerceAttributes.map( ( attribute ) => ( {
attribute: attribute.taxonomy,
term_id: attribute.termId,
} ) ),
};
}

export function formatQuery( query: ProductCollectionQuery ) {
if ( ! query ) {
return {};
}

return Object.assign(
{},
...sharedParams.map(
( key ) => key in query && { [ key ]: query[ key ] }
),
...mappedParams.map(
( param ) =>
param.key in query && { [ param.map ]: query[ param.key ] }
),
...getTaxQueryMap( query.taxQuery ),
getAttributeQuery( query.woocommerceAttributes )
);
}
65 changes: 33 additions & 32 deletions src/BlockTypes/CollectionFilters.php
Original file line number Diff line number Diff line change
Expand Up @@ -220,12 +220,6 @@ private function get_formatted_products_params( $query ) {
* The following params can be passed directly to Store API endpoints.
*/
$shared_params = array( 'exclude', 'offset', 'order', 'serach' );
array_walk(
$shared_params,
function( $key ) use ( $query, &$params ) {
$params[ $key ] = $query[ $key ] ?? '';
}
);

/**
* The following params just need to transform the key, their value can
Expand All @@ -239,17 +233,7 @@ function( $key ) use ( $query, &$params ) {
'woocommerceOnSale' => 'on_sale',
'woocommerceHandPickedProducts' => 'include',
);
array_walk(
$mapped_params,
function( $mapped_key, $original_key ) use ( $query, &$params ) {
$params[ $mapped_key ] = $query[ $original_key ] ?? '';
}
);

/**
* The value of taxQuery and woocommerceAttributes need additional
* transformation to the shape that Store API accepts.
*/
$taxonomy_mapper = function( $key ) {
$mapping = array(
'product_tag' => 'tag',
Expand All @@ -259,26 +243,43 @@ function( $mapped_key, $original_key ) use ( $query, &$params ) {
return $mapping[ $key ] ?? '_unstable_tax_' . $key;
};

if ( is_array( $query['taxQuery'] ) ) {
array_walk(
$query['taxQuery'],
function( $terms, $taxonomy ) use ( $taxonomy_mapper, &$params ) {
$params[ $taxonomy_mapper( $taxonomy ) ] = implode( ',', $terms );
array_walk(
$query,
function( $value, $key ) use ( $shared_params, $mapped_params, $taxonomy_mapper, &$params ) {
if ( in_array( $key, $shared_params, true ) ) {
$params[ $key ] = $value;
}

if ( in_array( $key, array_keys( $mapped_params ), true ) ) {
$params[ $mapped_params[ $key ] ] = $value;
}
);
}

if ( is_array( $query['woocommerceAttributes'] ) ) {
array_walk(
$query['woocommerceAttributes'],
function( $attribute ) use ( &$params ) {
$params['attributes'][] = array(
'attribute' => $attribute['taxonomy'],
'term_id' => $attribute['termId'],
/**
* The value of taxQuery and woocommerceAttributes need additional
* transformation to the shape that Store API accepts.
*/
if ( 'taxQuery' === $key && is_array( $value ) ) {
array_walk(
$value,
function( $terms, $taxonomy ) use ( $taxonomy_mapper, &$params ) {
$params[ $taxonomy_mapper( $taxonomy ) ] = implode( ',', $terms );
}
);
}
);
}

if ( 'woocommerceAttributes' === $key && is_array( $value ) ) {
array_walk(
$value,
function( $attribute ) use ( &$params ) {
$params['attributes'][] = array(
'attribute' => $attribute['taxonomy'],
'term_id' => $attribute['termId'],
);
}
);
}
}
);

/**
* Product Collection determines the product visibility based on stock
Expand Down
1 change: 1 addition & 0 deletions tests/js/jest.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"@woocommerce/atomic-utils": "assets/js/atomic/utils",
"@woocommerce/icons": "assets/js/icons",
"@woocommerce/settings": "assets/js/settings/shared",
"@woocommerce/blocks/(.*)$": "assets/js/blocks/$1",
"@woocommerce/block-settings": "assets/js/settings/blocks",
"@woocommerce/editor-components(.*)$": "assets/js/editor-components/$1",
"@woocommerce/blocks-registry": "assets/js/blocks-registry",
Expand Down

0 comments on commit 7cef728

Please sign in to comment.