Alokai
You are reading the documentation for v0, which is no longer the latest version. Switch to v1

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 }: //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)}

For all filters beside Category, one useFacets composable needs to be applied in Filter.vue component. Old variables with same name have to be removed.

// storefront-unified-nuxt/components/CategoryFilters/Filter.vue
<script setup lang="ts">
import { useFacets } from '@sf-modules/coveo'; 

const { toggleShowMore, showMore, itemsToDisplay, canShowMore } = useFacets({
  facetName: props.facet.name,
  max: props.max,
});

const showMore = ref(false);
const toggleShowMore = () => (showMore.value = !showMore.value);
const itemsToDisplay = computed(() => (showMore.value ? props.facet?.values : props.facet?.values.slice(0, props.max)));
const canShowMore = computed(() => (props.facet?.values ? props.facet?.values.length > props.max : false));
</script>

For Category filters changes have to be made in CategoryTree.vue component. We need to import useCategoryFacets composable and apply its returned variables.

// storefront-unified-nuxt/components/CategoryTree/CategoryTree.vue
<script setup lang="ts">
import { toRefs } from '@vueuse/core'; 
import { useCategoryFacets } from '@sf-modules/coveo'; 

const {
  toggleShowMore,
  showMore,
  itemsToDisplay,
  canShowMore: isThereMoreItemsToShow,
} = useCategoryFacets({ max: props.maxToShow });

const { categories, maxToShow } = toRefs(props);
const isThereMoreItemsToShow = computed(() => categories.value.length > maxToShow.value);
const showMore = ref(false);
const toggleShowMore = () => (showMore.value = !showMore.value);
const itemsToDisplay = computed(() => (showMore.value ? categories.value : categories.value.slice(0, maxToShow.value)));
</script>