plone / volto

React-based frontend for the Plone Content Management System
https://demo.plone.org/
MIT License
460 stars 624 forks source link

Facets should be able to decide themselves if they should show or not #4579

Open tiberiuichim opened 1 year ago

tiberiuichim commented 1 year ago

There's a limitation here with the facets:

https://github.com/plone/volto/blob/d3865b3ac9e5c1213058373e6ecf5422060d7ab0/src/components/manage/Blocks/Search/components/Facets.jsx#L9-L10

It should use a pluggable facet function, similar to the rest of the facet methods:

https://github.com/plone/volto/blob/d3865b3ac9e5c1213058373e6ecf5422060d7ab0/src/components/manage/Blocks/Search/components/ToggleFacet.jsx#L24-L36

https://github.com/plone/volto/blob/d3865b3ac9e5c1213058373e6ecf5422060d7ab0/src/config/Blocks.jsx#L453-L455

lord2anil commented 1 year ago

Sir, can you please elaborate on the issue more .

tiberiuichim commented 1 year ago

@lord2anil if you read the code of Facets.jsx, you'll see that there's a function that "hardcodes" its functionality. That function decides if a Facet should be displayed or not.

The problem we want to solve is that each configured facet should decide if they want to be displayed or not, so, for each facet, there should be a registration of a function somewhere that decides this.

I have a custom version of Facets.jsx where this issue is fixed:

// An enhancement till this is fixed: https://github.com/plone/volto/issues/4579
import React from 'react';
import { resolveExtension } from '@plone/volto/helpers/Extensions/withBlockExtensions';
import config from '@plone/volto/registry';
import {
  hasNonValueOperation,
  hasDateOperation,
} from '@plone/volto/components/manage/Blocks/Search/utils';

const defaultShowFacet = (index) => {
  const { values } = index;
  return index
    ? hasNonValueOperation(index.operations || []) ||
      hasDateOperation(index.operations || [])
      ? true
      : values && Object.keys(values).length > 0
    : values && Object.keys(values).length > 0;
};

const Facets = (props) => {
  const {
    querystring,
    data = {},
    facets,
    setFacets,
    facetWrapper,
    isEditMode,
  } = props;
  const { search } = config.blocks.blocksConfig;

  const FacetWrapper = facetWrapper;
  const query_to_values = Object.assign(
    {},
    ...(data?.query?.query?.map(({ i, v }) => ({ [i]: v })) || []),
  );

  return (
    <>
      {data?.facets
        ?.filter((facetSettings) => !facetSettings.hidden)
        .map((facetSettings) => {
          const field = facetSettings?.field?.value;
          const index = querystring.indexes[field] || {};
          const { values = {} } = index;

          let choices = Object.keys(values)
            .map((name) => ({
              value: name,
              label: values[name].title,
            }))
            // filter the available values based on the allowed values in the
            // base query
            .filter(({ value }) =>
              query_to_values[field]
                ? query_to_values[field].includes(value)
                : true,
            );

          choices = choices.sort((a, b) =>
            a.label.localeCompare(b.label, 'en', { sensitivity: 'base' }),
          );

          const isMulti = facetSettings.multiple;
          const selectedValue = facets[facetSettings?.field?.value];

          // TODO :handle changing the type of facet (multi/nonmulti)

          const {
            view: FacetWidget,
            stateToValue,
            showFacet = defaultShowFacet,
          } = resolveExtension(
            'type',
            search.extensions.facetWidgets.types,
            facetSettings,
          );

          let value = stateToValue({ facetSettings, index, selectedValue });

          const {
            rewriteOptions = (name, options) => options,
          } = search.extensions.facetWidgets;

          return FacetWrapper && (isEditMode || showFacet(index)) ? (
            <FacetWrapper key={facetSettings['@id']}>
              <FacetWidget
                facet={facetSettings}
                choices={rewriteOptions(facetSettings?.field?.value, choices)}
                isMulti={isMulti}
                value={value}
                isEditMode={isEditMode}
                onChange={(id, value) => {
                  !isEditMode && setFacets({ ...facets, [id]: value });
                }}
              />
            </FacetWrapper>
          ) : (
            ''
          );
        })}
    </>
  );
};

export default Facets;
BhuvaneshPatil commented 1 year ago

Hello @tiberiuichim , I am new to plone and volto ecosystem. Can you please provide me some steps to understand the functionality of facets and the improvements you have mentioned in the issue? For example - How do I test the current functionality of facet and the required functionality.

tiberiuichim commented 1 year ago

@BhuvaneshPatil the facets are used by the search block. In Volto, in the browser, create a search block, add some query criteria and then define some facets. Subject (Tags) and Portal types are good candidates

shreyal0007 commented 1 year ago

what changes do you expect from us on this issue

stevepiercy commented 1 year ago

It should use a pluggable facet function, similar to the rest of the facet methods:

This is in the description:

It should use a pluggable facet function, similar to the rest of the facet methods: