Make Filter Products by Stock block compatible with PHP rendered Classic Template block (https://github.com/woocommerce/woocommerce-blocks/pull/6261)

* Enable Attribute Filter block to work with the PHP rendered Classic Template block

* Check for presence of option before rendering it

* improve filter product by attribute

* fix pagination problem

* fix check when two filter block with same attribute are used

* fix filter by stock for PHP templates

* fix naming and comment

* Update filter key in ClassicTemplate

* Update stock filter block for PHP templates when the filter button is enabled

* Remove unused useEffect and fix ESLint error

* Set active stock filter and track using local state

* ESLint fix

* Remove unncessary dependencies from useEffect

Co-authored-by: tjcafferkey <tjcafferkey@gmail.com>
This commit is contained in:
Luigi Teschio 2022-04-21 09:28:41 +02:00 committed by GitHub
parent 268d6e1a34
commit 881c202717
10 changed files with 207 additions and 14 deletions

View File

@ -22,6 +22,10 @@ import classNames from 'classnames';
import { getSettingWithCoercion } from '@woocommerce/settings';
import { getQueryArgs, removeQueryArgs } from '@wordpress/url';
import { isBoolean, isString } from '@woocommerce/types';
import {
PREFIX_QUERY_ARG_FILTER_TYPE,
PREFIX_QUERY_ARG_QUERY_TYPE,
} from '@woocommerce/utils';
/**
* Internal dependencies
@ -36,6 +40,7 @@ import {
getActiveFilters,
areAllFiltersRemoved,
isQueryArgsEqual,
parseTaxonomyToGenerateURL,
} from './utils';
/**
@ -392,9 +397,20 @@ const AttributeFilterBlock = ( {
getQueryArgs( window.location.href )
);
const parsedTaxonomy = parseTaxonomyToGenerateURL(
attributeObject?.taxonomy
);
const url = currentQueryArgKeys.reduce(
( currentUrl, queryArg ) =>
removeQueryArgs( currentUrl, queryArg ),
queryArg.includes(
PREFIX_QUERY_ARG_QUERY_TYPE + parsedTaxonomy
) ||
queryArg.includes(
PREFIX_QUERY_ARG_FILTER_TYPE + parsedTaxonomy
)
? removeQueryArgs( currentUrl, queryArg )
: currentUrl,
window.location.href
);

View File

@ -3,11 +3,11 @@
*/
import { addQueryArgs, removeQueryArgs } from '@wordpress/url';
import { QueryArgs } from '@wordpress/url/build-types/get-query-args';
/**
* Internal dependencies
*/
import { getUrlParameter } from '../../utils/filters';
import {
getUrlParameter,
PREFIX_QUERY_ARG_FILTER_TYPE,
PREFIX_QUERY_ARG_QUERY_TYPE,
} from '@woocommerce/utils';
interface Param {
attribute: string;
@ -15,6 +15,9 @@ interface Param {
slug: Array< string >;
}
export const parseTaxonomyToGenerateURL = ( taxonomy: string ) =>
taxonomy.replace( 'pa_', '' );
export const formatParams = ( url: string, params: Array< Param > = [] ) => {
const paramObject: Record< string, string > = {};
@ -22,13 +25,13 @@ export const formatParams = ( url: string, params: Array< Param > = [] ) => {
const { attribute, slug, operator } = param;
// Custom filters are prefix with `pa_` so we need to remove this.
const name = attribute.replace( 'pa_', '' );
const name = parseTaxonomyToGenerateURL( attribute );
const values = slug.join( ',' );
const queryType = `query_type_${ name }`;
const queryType = `${ PREFIX_QUERY_ARG_QUERY_TYPE }${ name }`;
const type = operator === 'in' ? 'or' : 'and';
// The URL parameter requires the prefix filter_ with the attribute name.
paramObject[ `filter_${ name }` ] = values;
paramObject[ `${ PREFIX_QUERY_ARG_FILTER_TYPE }${ name }` ] = values;
paramObject[ queryType ] = type;
} );

View File

@ -9,19 +9,25 @@ import {
useQueryStateByContext,
useCollectionData,
} from '@woocommerce/base-context/hooks';
import { getSetting } from '@woocommerce/settings';
import { getSetting, getSettingWithCoercion } from '@woocommerce/settings';
import { useCallback, useEffect, useState, useMemo } from '@wordpress/element';
import CheckboxList from '@woocommerce/base-components/checkbox-list';
import FilterSubmitButton from '@woocommerce/base-components/filter-submit-button';
import Label from '@woocommerce/base-components/filter-element-label';
import isShallowEqual from '@wordpress/is-shallow-equal';
import { decodeEntities } from '@wordpress/html-entities';
import { isBoolean } from '@woocommerce/types';
import { addQueryArgs, removeQueryArgs } from '@wordpress/url';
import { PREFIX_QUERY_ARG_FILTER_TYPE } from '@woocommerce/utils';
/**
* Internal dependencies
*/
import { previewOptions } from './preview';
import './style.scss';
import { getActiveFilters } from './utils';
export const QUERY_PARAM_KEY = PREFIX_QUERY_ARG_FILTER_TYPE + 'stock_status';
/**
* Component displaying an stock status filter.
@ -34,6 +40,16 @@ const StockStatusFilterBlock = ( {
attributes: blockAttributes,
isEditor = false,
} ) => {
const filteringForPhpTemplate = getSettingWithCoercion(
'is_rendering_php_template',
false,
isBoolean
);
const [ hasSetPhpFilterDefaults, setHasSetPhpFilterDefaults ] = useState(
false
);
const [ hideOutOfStockItems ] = useState(
getSetting( 'hideOutOfStockItems', false )
);
@ -46,7 +62,9 @@ const StockStatusFilterBlock = ( {
: { outofstock, ...otherStockStatusOptions }
);
const [ checked, setChecked ] = useState( [] );
const [ checked, setChecked ] = useState(
getActiveFilters( STOCK_STATUS_OPTIONS, QUERY_PARAM_KEY )
);
const [ displayedOptions, setDisplayedOptions ] = useState(
blockAttributes.isPreview ? previewOptions : []
);
@ -148,6 +166,33 @@ const StockStatusFilterBlock = ( {
initialOptions,
] );
/**
* Used to redirect the page when filters are changed so templates using the Classic Template block can filter.
*
* @param {Array} checkedOptions Array of checked stock options.
*/
const redirectPageForPhpTemplate = ( checkedOptions ) => {
if ( checkedOptions.length === 0 ) {
const url = removeQueryArgs(
window.location.href,
QUERY_PARAM_KEY
);
if ( url !== window.location.href ) {
window.location.href = url;
}
return;
}
const newUrl = addQueryArgs( window.location.href, {
[ QUERY_PARAM_KEY ]: checkedOptions.join( ',' ),
} );
if ( newUrl !== window.location.href ) {
window.location.href = newUrl;
}
};
const onSubmit = useCallback(
( isChecked ) => {
if ( isEditor ) {
@ -156,8 +201,17 @@ const StockStatusFilterBlock = ( {
if ( isChecked ) {
setProductStockStatusQuery( checked );
}
// For PHP templates when the filter button is enabled.
if ( filteringForPhpTemplate ) {
redirectPageForPhpTemplate( checked );
}
},
[ isEditor, setProductStockStatusQuery, checked ]
[
isEditor,
setProductStockStatusQuery,
checked,
filteringForPhpTemplate,
]
);
// Track checked STATE changes - if state changes, update the query.
@ -183,6 +237,37 @@ const StockStatusFilterBlock = ( {
}
}, [ checked, currentCheckedQuery, previousCheckedQuery ] );
/**
* Important: For PHP rendered block templates only.
*/
useEffect( () => {
if ( filteringForPhpTemplate ) {
setChecked( checked );
// Only automatically redirect if the filter button is not active.
if ( ! blockAttributes.showFilterButton ) {
redirectPageForPhpTemplate( checked );
}
}
}, [ filteringForPhpTemplate, checked, blockAttributes.showFilterButton ] );
/**
* Important: For PHP rendered block templates only.
*/
useEffect( () => {
if ( ! hasSetPhpFilterDefaults && filteringForPhpTemplate ) {
setProductStockStatusQuery(
getActiveFilters( STOCK_STATUS_OPTIONS, QUERY_PARAM_KEY )
);
setHasSetPhpFilterDefaults( true );
}
}, [
STOCK_STATUS_OPTIONS,
filteringForPhpTemplate,
setProductStockStatusQuery,
hasSetPhpFilterDefaults,
setHasSetPhpFilterDefaults,
] );
/**
* When a checkbox in the list changes, update state.
*/

View File

@ -0,0 +1,22 @@
/**
* External dependencies
*/
import { isString } from '@woocommerce/types';
import { getUrlParameter } from '@woocommerce/utils';
export const getActiveFilters = (
filters: Record< string, string >,
queryParamKey: 'filter_stock_status'
) => {
const params = getUrlParameter( queryParamKey );
if ( ! params ) {
return [];
}
const parsedParams = isString( params ) ? params.split( ',' ) : params;
return Object.keys( filters ).filter( ( filter ) =>
parsedParams.includes( filter )
);
};

View File

@ -8,6 +8,10 @@ import { getQueryArg } from '@wordpress/url';
*
* @param {string} name Parameter you want the value of.
*/
export const PREFIX_QUERY_ARG_QUERY_TYPE = 'query_type_';
export const PREFIX_QUERY_ARG_FILTER_TYPE = 'filter_';
export function getUrlParameter( name: string ) {
if ( ! window ) {
return null;

View File

@ -0,0 +1,8 @@
export * from './attributes-query';
export * from './attributes';
export * from './filters';
export * from './global-style';
export * from './notices';
export * from './products';
export * from './shared-attributes';
export * from './useThrottle';

View File

@ -87,6 +87,7 @@ const getAlias = ( options = {} ) => {
`../assets/js/${ pathPart }previews/`
),
'@woocommerce/types': path.resolve( __dirname, `../assets/js/types/` ),
'@woocommerce/utils': path.resolve( __dirname, `../assets/js/utils/` ),
};
};

View File

@ -24,12 +24,17 @@ class ClassicTemplate extends AbstractDynamicBlock {
*/
protected $api_version = '2';
const FILTER_PRODUCTS_BY_STOCK_QUERY_PARAM = 'filter_stock_status';
/**
* Initialize this block.
*/
protected function initialize() {
parent::initialize();
add_filter( 'render_block', array( $this, 'add_alignment_class_to_wrapper' ), 10, 2 );
add_filter( 'query_vars', array( $this, 'add_query_vars_filter' ) );
add_filter( 'woocommerce_product_query_meta_query', array( $this, 'filter_products_by_stock' ), 10, 2 );
}
/**
@ -254,4 +259,51 @@ class ClassicTemplate extends AbstractDynamicBlock {
}
/**
* Filter products by stock status when as query param there is "filter_stock_status"
*
* @param array $meta_query Meta query.
* @return array
*/
public function filter_products_by_stock( $meta_query ) {
if ( is_admin() ) {
return $meta_query;
}
$stock_status = array_keys( wc_get_product_stock_status_options() );
$values = get_query_var( self::FILTER_PRODUCTS_BY_STOCK_QUERY_PARAM );
$values_to_array = explode( ',', $values );
$filtered_values = array_filter(
$values_to_array,
function( $value ) use ( $stock_status ) {
return in_array( $value, $stock_status, true );
}
);
if ( ! empty( $filtered_values ) ) {
$meta_query[] = array(
'key' => '_stock_status',
'value' => $filtered_values,
'compare' => 'IN',
);
}
return $meta_query;
}
/**
* Add custom query params
*
* @param array $vars Query vars.
* @return array Query vars.
*/
public function add_query_vars_filter( $vars ) {
$vars[] = self::FILTER_PRODUCTS_BY_STOCK_QUERY_PARAM;
return $vars;
}
}

View File

@ -28,7 +28,8 @@
"@woocommerce/shared-context": "assets/js/shared/context",
"@woocommerce/shared-hocs": "assets/js/shared/hocs",
"@woocommerce/blocks-test-utils": "tests/utils",
"@woocommerce/types": "assets/js/types"
"@woocommerce/types": "assets/js/types",
"@woocommerce/utils": "assets/js/utils"
},
"setupFiles": [
"@wordpress/jest-preset-default/scripts/setup-globals.js",

View File

@ -55,7 +55,8 @@
"@woocommerce/shared-hocs": [ "assets/js/shared/hocs" ],
"@woocommerce/type-defs/*": [ "assets/js/types/type-defs/*" ],
"@woocommerce/types": [ "assets/js/types" ],
"@woocommerce/storybook-controls": [ "storybook/custom-controls" ]
"@woocommerce/storybook-controls": [ "storybook/custom-controls" ],
"@woocommerce/utils": [ "assets/js/utils" ]
}
}
}