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

Commit 3bc4499

Browse files
authored
Fix: Add support to apply filters via URL for All Products block (#6642)
* price-filter: update URL when filtering products of All Products block. * active-filters: update the URL when removing price filter * price-filter: prevent update query on PHP templates when changing prices * active-filters: on PHP templates, remove filter only triggers the page reload * price-filter: update comments and naming to reflect new URL behavior * stock-filter: update url when filtering for All Products block * attribute-filter: update the URL when filtering for All Products block * attribute-filter: fix: uncheck all filter doesn't clear the URL * attribute-filter: fix: endless page reload when filtering for PHP template * attribute-filter: correctly set the defaults and update the filter query from URL * fix: filter URL containing stock status doesn't work on homepage * price-filter: fix issue with URL on All Products The price slider doesn't reset after removing price filter from the Active Filters block if the initial price filter is set from URL * ref: passing document.title to pushState is unnecessary * use replaceState to avoid changing browser history * extract change URL logic
1 parent 61694f5 commit 3bc4499

File tree

9 files changed

+182
-186
lines changed

9 files changed

+182
-186
lines changed

assets/js/blocks/active-filters/active-attribute-filters.tsx

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -101,33 +101,33 @@ const ActiveAttributeFilters = ( {
101101
name: decodeEntities( termObject.name || slug ),
102102
prefix,
103103
removeCallback: () => {
104-
if ( filteringForPhpTemplate ) {
105-
const currentAttribute = productAttributes.find(
106-
( { attribute } ) =>
107-
attribute ===
108-
`pa_${ attributeObject.name }`
109-
);
110-
111-
// If only one attribute was selected, we remove both filter and query type from the URL.
112-
if ( currentAttribute?.slug.length === 1 ) {
113-
return removeArgsFromFilterUrl(
114-
`query_type_${ attributeObject.name }`,
115-
`filter_${ attributeObject.name }`
116-
);
117-
}
104+
const currentAttribute = productAttributes.find(
105+
( { attribute } ) =>
106+
attribute === `pa_${ attributeObject.name }`
107+
);
118108

109+
// If only one attribute was selected, we remove both filter and query type from the URL.
110+
if ( currentAttribute?.slug.length === 1 ) {
111+
removeArgsFromFilterUrl(
112+
`query_type_${ attributeObject.name }`,
113+
`filter_${ attributeObject.name }`
114+
);
115+
} else {
119116
// Remove only the slug from the URL.
120-
return removeArgsFromFilterUrl( {
117+
removeArgsFromFilterUrl( {
121118
[ `filter_${ attributeObject.name }` ]:
122119
slug,
123120
} );
124121
}
125-
removeAttributeFilterBySlug(
126-
productAttributes,
127-
setProductAttributes,
128-
attributeObject,
129-
slug
130-
);
122+
123+
if ( ! filteringForPhpTemplate ) {
124+
removeAttributeFilterBySlug(
125+
productAttributes,
126+
setProductAttributes,
127+
attributeObject,
128+
slug
129+
);
130+
}
131131
},
132132
showLabel: false,
133133
displayStyle,

assets/js/blocks/active-filters/block.tsx

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -76,17 +76,17 @@ const ActiveFiltersBlock = ( {
7676
type: __( 'Stock Status', 'woo-gutenberg-products-block' ),
7777
name: STOCK_STATUS_OPTIONS[ slug ],
7878
removeCallback: () => {
79-
if ( filteringForPhpTemplate ) {
80-
return removeArgsFromFilterUrl( {
81-
filter_stock_status: slug,
82-
} );
79+
removeArgsFromFilterUrl( {
80+
filter_stock_status: slug,
81+
} );
82+
if ( ! filteringForPhpTemplate ) {
83+
const newStatuses = productStockStatus.filter(
84+
( status ) => {
85+
return status !== slug;
86+
}
87+
);
88+
setProductStockStatus( newStatuses );
8389
}
84-
const newStatuses = productStockStatus.filter(
85-
( status ) => {
86-
return status !== slug;
87-
}
88-
);
89-
setProductStockStatus( newStatuses );
9090
},
9191
displayStyle: blockAttributes.displayStyle,
9292
} );
@@ -107,11 +107,11 @@ const ActiveFiltersBlock = ( {
107107
type: __( 'Price', 'woo-gutenberg-products-block' ),
108108
name: formatPriceRange( minPrice, maxPrice ),
109109
removeCallback: () => {
110-
if ( filteringForPhpTemplate ) {
111-
return removeArgsFromFilterUrl( 'max_price', 'min_price' );
110+
removeArgsFromFilterUrl( 'max_price', 'min_price' );
111+
if ( ! filteringForPhpTemplate ) {
112+
setMinPrice( undefined );
113+
setMaxPrice( undefined );
112114
}
113-
setMinPrice( undefined );
114-
setMaxPrice( undefined );
115115
},
116116
displayStyle: blockAttributes.displayStyle,
117117
} );
@@ -289,13 +289,13 @@ const ActiveFiltersBlock = ( {
289289
<button
290290
className="wc-block-active-filters__clear-all"
291291
onClick={ () => {
292-
if ( filteringForPhpTemplate ) {
293-
return cleanFilterUrl();
292+
cleanFilterUrl();
293+
if ( ! filteringForPhpTemplate ) {
294+
setMinPrice( undefined );
295+
setMaxPrice( undefined );
296+
setProductAttributes( [] );
297+
setProductStockStatus( [] );
294298
}
295-
setMinPrice( undefined );
296-
setMaxPrice( undefined );
297-
setProductAttributes( [] );
298-
setProductStockStatus( [] );
299299
} }
300300
>
301301
<Label

assets/js/blocks/active-filters/utils.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { formatPrice } from '@woocommerce/price-format';
66
import { RemovableChip } from '@woocommerce/base-components/chip';
77
import Label from '@woocommerce/base-components/label';
88
import { getQueryArgs, addQueryArgs, removeQueryArgs } from '@wordpress/url';
9+
import { changeUrl } from '@woocommerce/utils';
910

1011
/**
1112
* Format a min/max price range to display.
@@ -158,6 +159,10 @@ export const renderRemovableListItem = ( {
158159
export const removeArgsFromFilterUrl = (
159160
...args: ( string | Record< string, string > )[]
160161
) => {
162+
if ( ! window ) {
163+
return;
164+
}
165+
161166
const url = window.location.href;
162167
const currentQuery = getQueryArgs( url );
163168
const cleanUrl = removeQueryArgs( url, ...Object.keys( currentQuery ) );
@@ -181,13 +186,19 @@ export const removeArgsFromFilterUrl = (
181186
Object.entries( currentQuery ).filter( ( [ , value ] ) => value )
182187
);
183188

184-
window.location.href = addQueryArgs( cleanUrl, filteredQuery );
189+
const newUrl = addQueryArgs( cleanUrl, filteredQuery );
190+
191+
changeUrl( newUrl );
185192
};
186193

187194
/**
188195
* Clean the filter URL.
189196
*/
190197
export const cleanFilterUrl = () => {
198+
if ( ! window ) {
199+
return;
200+
}
201+
191202
const url = window.location.href;
192203
const args = getQueryArgs( url );
193204
const cleanUrl = removeQueryArgs( url, ...Object.keys( args ) );
@@ -209,5 +220,7 @@ export const cleanFilterUrl = () => {
209220
.map( ( key ) => [ key, args[ key ] ] )
210221
);
211222

212-
window.location.href = addQueryArgs( cleanUrl, remainingArgs );
223+
const newUrl = addQueryArgs( cleanUrl, remainingArgs );
224+
225+
changeUrl( newUrl );
213226
};

assets/js/blocks/attribute-filter/block.tsx

Lines changed: 44 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
objectHasProp,
3030
} from '@woocommerce/types';
3131
import {
32+
changeUrl,
3233
PREFIX_QUERY_ARG_FILTER_TYPE,
3334
PREFIX_QUERY_ARG_QUERY_TYPE,
3435
} from '@woocommerce/utils';
@@ -91,18 +92,21 @@ const AttributeFilterBlock = ( {
9192
isString
9293
);
9394

94-
const [ hasSetPhpFilterDefaults, setHasSetPhpFilterDefaults ] =
95+
const [ hasSetFilterDefaultsFromUrl, setHasSetFilterDefaultsFromUrl ] =
9596
useState( false );
9697

9798
const attributeObject =
9899
blockAttributes.isPreview && ! blockAttributes.attributeId
99100
? previewAttributeObject
100101
: getAttributeFromID( blockAttributes.attributeId );
101102

102-
const [ checked, setChecked ] = useState(
103-
getActiveFilters( filteringForPhpTemplate, attributeObject )
103+
const initialFilters = useMemo(
104+
() => getActiveFilters( attributeObject ),
105+
[ attributeObject ]
104106
);
105107

108+
const [ checked, setChecked ] = useState( initialFilters );
109+
106110
const [ displayedOptions, setDisplayedOptions ] = useState<
107111
DisplayOption[]
108112
>(
@@ -250,7 +254,7 @@ const AttributeFilterBlock = ( {
250254
* @param {Object} query The object containing the active filter query.
251255
* @param {boolean} allFiltersRemoved If there are active filters or not.
252256
*/
253-
const redirectPageForPhpTemplate = useCallback(
257+
const updateFilterUrl = useCallback(
254258
( query, allFiltersRemoved = false ) => {
255259
if ( allFiltersRemoved ) {
256260
if ( ! attributeObject?.taxonomy ) {
@@ -278,14 +282,14 @@ const AttributeFilterBlock = ( {
278282
);
279283

280284
const newUrl = formatParams( url, query );
281-
window.location.href = newUrl;
285+
changeUrl( newUrl );
282286
} else {
283287
const newUrl = formatParams( pageUrl, query );
284288
const currentQueryArgs = getQueryArgs( window.location.href );
285289
const newUrlQueryArgs = getQueryArgs( newUrl );
286290

287291
if ( ! isQueryArgsEqual( currentQueryArgs, newUrlQueryArgs ) ) {
288-
window.location.href = newUrl;
292+
changeUrl( newUrl );
289293
}
290294
}
291295
},
@@ -301,20 +305,17 @@ const AttributeFilterBlock = ( {
301305
blockAttributes.queryType === 'or' ? 'in' : 'and'
302306
);
303307

304-
// This is for PHP rendered template filtering only.
305-
if ( filteringForPhpTemplate ) {
306-
redirectPageForPhpTemplate( query, checkedFilters.length === 0 );
307-
}
308+
updateFilterUrl( query, checkedFilters.length === 0 );
308309
};
309310

310311
const updateCheckedFilters = useCallback(
311-
( checkedFilters: string[] ) => {
312+
( checkedFilters: string[], force = false ) => {
312313
if ( isEditor ) {
313314
return;
314315
}
315316

316317
setChecked( checkedFilters );
317-
if ( ! blockAttributes.showFilterButton ) {
318+
if ( force || ! blockAttributes.showFilterButton ) {
318319
updateAttributeFilter(
319320
productAttributesQuery,
320321
setProductAttributesQuery,
@@ -462,67 +463,56 @@ const AttributeFilterBlock = ( {
462463
);
463464

464465
/**
465-
* Important: For PHP rendered block templates only.
466-
*
467-
* When we render the PHP block template (e.g. Classic Block) we need to set the default checked values,
468-
* and also update the URL when the filters are clicked/updated.
466+
* Update the filter URL on state change.
469467
*/
470468
useEffect( () => {
471-
if ( filteringForPhpTemplate && attributeObject ) {
472-
if (
473-
areAllFiltersRemoved( {
474-
currentCheckedFilters: checked,
475-
hasSetPhpFilterDefaults,
476-
} )
477-
) {
478-
if ( ! blockAttributes.showFilterButton ) {
479-
setChecked( [] );
480-
redirectPageForPhpTemplate( productAttributesQuery, true );
481-
}
482-
}
469+
if ( ! attributeObject || blockAttributes.showFilterButton ) {
470+
return;
471+
}
483472

484-
if ( ! blockAttributes.showFilterButton ) {
485-
setChecked( checked );
486-
redirectPageForPhpTemplate( productAttributesQuery, false );
487-
}
473+
if (
474+
areAllFiltersRemoved( {
475+
currentCheckedFilters: checked,
476+
hasSetFilterDefaultsFromUrl,
477+
} )
478+
) {
479+
updateFilterUrl( productAttributesQuery, true );
480+
} else {
481+
updateFilterUrl( productAttributesQuery, false );
488482
}
489483
}, [
490-
hasSetPhpFilterDefaults,
491-
redirectPageForPhpTemplate,
492-
filteringForPhpTemplate,
484+
hasSetFilterDefaultsFromUrl,
485+
updateFilterUrl,
493486
productAttributesQuery,
494487
attributeObject,
495488
checked,
496489
blockAttributes.showFilterButton,
497490
] );
498491

499492
/**
500-
* Important: For PHP rendered block templates only.
501-
*
502-
* When we set the default parameter values which we get from the URL in the above useEffect(),
503-
* we need to run updateCheckedFilters which will set these values in state for the Active Filters block.
493+
* Try get the current attribute filter from the URl.
504494
*/
505495
useEffect( () => {
506-
if ( filteringForPhpTemplate ) {
507-
const activeFilters = getActiveFilters(
508-
filteringForPhpTemplate,
509-
attributeObject
510-
);
511-
if (
512-
activeFilters.length > 0 &&
513-
! hasSetPhpFilterDefaults &&
514-
! attributeTermsLoading
515-
) {
516-
setHasSetPhpFilterDefaults( true );
517-
updateCheckedFilters( activeFilters );
518-
}
496+
if ( hasSetFilterDefaultsFromUrl || attributeTermsLoading ) {
497+
return;
498+
}
499+
500+
if ( initialFilters.length > 0 ) {
501+
setHasSetFilterDefaultsFromUrl( true );
502+
updateCheckedFilters( initialFilters, true );
503+
return;
504+
}
505+
506+
if ( ! filteringForPhpTemplate ) {
507+
setHasSetFilterDefaultsFromUrl( true );
519508
}
520509
}, [
521510
attributeObject,
522-
filteringForPhpTemplate,
523-
hasSetPhpFilterDefaults,
511+
hasSetFilterDefaultsFromUrl,
524512
attributeTermsLoading,
525513
updateCheckedFilters,
514+
initialFilters,
515+
filteringForPhpTemplate,
526516
] );
527517

528518
if ( ! hasFilterableProducts ) {

assets/js/blocks/attribute-filter/utils.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,16 @@ export const formatParams = ( url: string, params: Array< Param > = [] ) => {
4444

4545
export const areAllFiltersRemoved = ( {
4646
currentCheckedFilters,
47-
hasSetPhpFilterDefaults,
47+
hasSetFilterDefaultsFromUrl,
4848
}: {
4949
currentCheckedFilters: Array< string >;
50-
hasSetPhpFilterDefaults: boolean;
51-
} ) => hasSetPhpFilterDefaults && currentCheckedFilters.length === 0;
50+
hasSetFilterDefaultsFromUrl: boolean;
51+
} ) => hasSetFilterDefaultsFromUrl && currentCheckedFilters.length === 0;
5252

5353
export const getActiveFilters = (
54-
isFilteringForPhpTemplateEnabled: boolean,
5554
attributeObject: AttributeObject | undefined
5655
) => {
57-
if ( isFilteringForPhpTemplateEnabled && attributeObject ) {
56+
if ( attributeObject ) {
5857
const defaultAttributeParam = getUrlParameter(
5958
`filter_${ attributeObject.name }`
6059
);

0 commit comments

Comments
 (0)