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

Commit

Permalink
Add a Products by Rating filter block (#7048)
Browse files Browse the repository at this point in the history
* Add interactivity to the Product by Rating filter block

* Fix block with the latest repo changes

* Product by Rating: Code tidying up

* Add an experimental build gate and update block title and description

* Remove redundant title and description

* Add support for the CheckboxList component in the Products by Rating block

* Products by Rating: Minor code clean-up

* Active Filters: Fix the Clear All button for Ratings. Closes ##7172

* Products by Rating: Add misc TS fixes
  • Loading branch information
danieldudzic authored Sep 28, 2022
1 parent d64873e commit e044fe6
Show file tree
Hide file tree
Showing 18 changed files with 604 additions and 2 deletions.
3 changes: 2 additions & 1 deletion assets/js/base/components/filter-placeholder/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
.wc-block-stock-filter__title,
.wc-block-price-filter__title,
.wc-block-active-filters__title,
.wc-block-attribute-filter__title {
.wc-block-attribute-filter__title,
.wc-block-rating-filter__title {
margin: 0;
height: 1em;
}
Expand Down
7 changes: 7 additions & 0 deletions assets/js/base/components/product-list/product-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ const ProductList = ( {
'stock_status',
[]
);
const [ productRating, setProductRating ] = useQueryStateByKey(
'rating',
[]
);

const [ minPrice, setMinPrice ] = useQueryStateByKey( 'min_price' );
const [ maxPrice, setMaxPrice ] = useQueryStateByKey( 'max_price' );

Expand Down Expand Up @@ -215,6 +220,7 @@ const ProductList = ( {
const hasFilters =
productAttributes.length > 0 ||
productStockStatus.length > 0 ||
productRating.length > 0 ||
Number.isFinite( minPrice ) ||
Number.isFinite( maxPrice );

Expand All @@ -231,6 +237,7 @@ const ProductList = ( {
resetCallback={ () => {
setProductAttributes( [] );
setProductStockStatus( [] );
setProductRating( [] );
setMinPrice( null );
setMaxPrice( null );
} }
Expand Down
64 changes: 64 additions & 0 deletions assets/js/base/components/product-rating/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* External dependencies
*/
import classNames from 'classnames';
import { __, sprintf } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import './style.scss';

const Rating = ( {
className,
key,
rating,
ratedProductsCount,
}: RatingProps ): JSX.Element => {
const ratingClassName = classNames(
'wc-block-components-product-rating',
className
);

const starStyle = {
width: ( rating / 5 ) * 100 + '%',
};

const ratingText = sprintf(
/* translators: %f is referring to the average rating value */
__( 'Rated %f out of 5', 'woo-gutenberg-products-block' ),
rating
);

const ratingHTML = {
__html: sprintf(
/* translators: %f is referring to the rating value */
__( 'Rated %f out of 5', 'woo-gutenberg-products-block' ),
sprintf( '<strong class="rating">%f</strong>', rating )
),
};

return (
<div className={ ratingClassName } key={ key }>
<div
className={ 'wc-block-components-product-rating__stars' }
role="img"
aria-label={ ratingText }
>
<span
style={ starStyle }
dangerouslySetInnerHTML={ ratingHTML }
/>
</div>
{ ratedProductsCount ? `(${ ratedProductsCount })` : null }
</div>
);
};
interface RatingProps {
className: string;
key: 0 | 1 | 2 | 3 | 4 | 5;
rating: 0 | 1 | 2 | 3 | 4 | 5;
ratedProductsCount: number;
}

export default Rating;
7 changes: 7 additions & 0 deletions assets/js/base/components/product-rating/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.wc-block-components-product-rating {
&__stars {
display: inline-block;
line-height: 1;
height: 1em;
}
}
18 changes: 18 additions & 0 deletions assets/js/base/context/hooks/collections/use-collection-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,15 @@ interface UseCollectionDataProps {
};
queryPrices?: boolean;
queryStock?: boolean;
queryRating?: boolean;
queryState: Record< string, unknown >;
}

export const useCollectionData = ( {
queryAttribute,
queryPrices,
queryStock,
queryRating,
queryState,
}: UseCollectionDataProps ) => {
let context = useQueryStateContext();
Expand All @@ -66,10 +68,13 @@ export const useCollectionData = ( {
calculateStockStatusQueryState,
setCalculateStockStatusQueryState,
] = useQueryStateByKey( 'calculate_stock_status_counts', null, context );
const [ calculateRatingQueryState, setCalculateRatingQueryState ] =
useQueryStateByKey( 'calculate_rating_counts', null, context );

const currentQueryAttribute = useShallowEqual( queryAttribute || {} );
const currentQueryPrices = useShallowEqual( queryPrices );
const currentQueryStock = useShallowEqual( queryStock );
const currentQueryRating = useShallowEqual( queryRating );

useEffect( () => {
if (
Expand Down Expand Up @@ -124,6 +129,19 @@ export const useCollectionData = ( {
calculateStockStatusQueryState,
] );

useEffect( () => {
if (
calculateRatingQueryState !== currentQueryRating &&
currentQueryRating !== undefined
) {
setCalculateRatingQueryState( currentQueryRating );
}
}, [
currentQueryRating,
setCalculateRatingQueryState,
calculateRatingQueryState,
] );

// Defer the select query so all collection-data query vars can be gathered.
const [ shouldSelect, setShouldSelect ] = useState( false );
const [ debouncedShouldSelect ] = useDebounce( shouldSelect, 200 );
Expand Down
3 changes: 2 additions & 1 deletion assets/js/blocks/active-filters/block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ const ActiveFiltersBlock = ( {
] );

const [ productRatings, setProductRatings ] =
useQueryStateByKey( 'ratings' );
useQueryStateByKey( 'rating' );

/**
* Parse the filter URL to set the active rating fitlers.
Expand Down Expand Up @@ -348,6 +348,7 @@ const ActiveFiltersBlock = ( {
setMaxPrice( undefined );
setProductAttributes( [] );
setProductStockStatus( [] );
setProductRatings( [] );
}
} }
>
Expand Down
11 changes: 11 additions & 0 deletions assets/js/blocks/rating-filter/attributes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';

export const blockAttributes = {
heading: {
type: 'string',
default: __( 'Filter by rating', 'woo-gutenberg-products-block' ),
},
};
34 changes: 34 additions & 0 deletions assets/js/blocks/rating-filter/block.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "woocommerce/rating-filter",
"version": "1.0.0",
"title": "Filter by Rating",
"description": "Enable customers to filter the product grid by rating.",
"category": "woocommerce",
"keywords": [ "WooCommerce" ],
"supports": {
"html": false,
"multiple": false
},
"example": {
"attributes": {
"isPreview": true
}
},
"attributes": {
"className": {
"type": "string",
"default": ""
},
"headingLevel": {
"type": "number",
"default": 3
},
"isPreview": {
"type": "boolean",
"default": false
}
},
"textdomain": "woo-gutenberg-products-block",
"apiVersion": 2,
"$schema": "https://schemas.wp.org/trunk/block.json"
}
Loading

0 comments on commit e044fe6

Please sign in to comment.