Load dynamically facets
This module offers possibility load dynamically filter facets. This feature enhance the user experience by displaying maximum relevant facets on initial load. This behaviour minimize loading multiple facets at once and provide possibility to load more available facets when needed.
This feature can be easily enabled just by adding hook/composable into Filter
and CategoryTree
components.
Facet config note
In order dynamic load of facets would work properly we need to define sortCriteria
in out facets.config.ts
file. This could be e.g sortCriteria: 'score'
. The reason is that FacetManager sortCriteria propery has default atLeastOneValue
and Facet which is used internally to download more facets uses default automatic
. Which means that if sortCriteria
property is not set, FacetManager
loads different set of values and then when is loaded more value with controller method showMoreValues
new set of facets is downloaded because of different sortCriteria
.
For all filters beside Category
, one useFacets
hook needs to be applied in facets.tsx
component.
// storefront-unified-nextjs/components/products-listing/facets.tsx
import { CoveoPriceItem } from '@sf-modules/coveo'; import { CoveoPriceItem, useFacets } from '@sf-modules/coveo';
function Facet({
containerClassName,
expandableListProps,
facet,
itemRenderer: FacetItem,
multiSelect = false,
}: FacetProps) {
// ...
const { canShowMore: isButtonVisible, itemsToDisplay: itemsToRender, showMore: isExpanded, toggleShowMore: toggle, } = useFacets({ facetName: facet.name, max: 5 });
return (
// ...
<ExpandableList {...expandableListProps}> {facet.values.map((item) => ( <ExpandableList {...expandableListProps} isButtonVisible={isButtonVisible} isExpanded={isExpanded} toggle={toggle} > {itemsToRender.map((item) => ( // ...
);
}
For Category
filters changes have to be made in category-tree.tsx
component. We need to import useCategoryFacets
hook and apply its returned variables.
// storefront-unified-nextjs/components/products-listing/category-tree.tsx
'use client'; import { useCategoryFacets } from '@sf-modules/coveo'; // ...
export default function CategoryTree({ categoryData, facet, slugs }: // [!code --]CategoryTreeProps) {
export default function CategoryTree({ categoryData, slugs }: CategoryTreeProps) { // ...
const categories = facet?.values ?? []; // ...
const { canShowMore: isButtonVisible, itemsToDisplay: itemsToRender, showMore: isExpanded, toggleShowMore: toggle, } = useCategoryFacets({ max: 5 });
return (
// ...
<ExpandableList> {categories.map(({ label, value }) => ( <ExpandableList isButtonVisible={isButtonVisible} isExpanded={isExpanded} toggle={toggle}> {itemsToRender.map(({ label, value }) => ( );
}
Additionally since category-tree.tsx
component is now client side only, we have to add CategoryTree
namespace to i18n provider, we can do this inside products-listing-page.tsx
component.
// storefront-unified-nextjs/components/products-listing/products-listing-page.tsx
export default function ProductsListingPage({
// ...
return (
<NextIntlClientProvider
messages={pick(messages, 'Facets', 'FiltersContainer', 'ExpandableList', 'SortByDropdown')} messages={pick(messages, 'Facets', 'FiltersContainer', 'ExpandableList', 'SortByDropdown', 'CategoryTree')} >
)
}
Lastly we have to modify expandable-list.tsx
component.
// storefront-unified-nextjs/components/ui/expandable-list.tsx
import { SfButton, useDisclosure } from '@storefront-ui/react'; import { SfButton } from '@storefront-ui/react';
export interface ExpandableListProps extends PropsWithChildren {
// ...
isButtonVisible?: boolean; isExpanded?: boolean; toggle?: () => void; }
export default function ExpandableList({
buttonClassName,
children,
id,
initialExpanded = false, maxCollapsedItems = 5, isButtonVisible, isExpanded, toggle, }: ExpandableListProps) {
const t = useTranslations('ExpandableList');
const isButtonVisible = Array.isArray(children) && maxCollapsedItems < children.length; const { isOpen: isExpanded, toggle } = useDisclosure({ initialValue: initialExpanded }); const idPart = id ? `${id}-` : '';
return (
<>
{Children.map(children, (child, index) => { if (isExpanded || index < maxCollapsedItems) { return child; } return null; })} {Children.map(children, (child) => child)}