Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Info

Valid from Datafari 6.32 (DatafariUI v2)

The search information block consists in the information traditionally shown above the search results. It provides several insights about the current search such as the terms of the search, the number of results and the filters currently in use. It is shown with a red bar on the side below:

...

All is done in the SearchInformation component (https://gitlab.datafari.com/datafari-community/datafariui-v2/-/blob/main/src/app/Components/Widgets/SearchInformation/SearchInformation.jsx). For the date, it is related to a Query Facet Field, so the formatting is required in the renderQueryFacetSection function:

...

Code Block
languagejs
    /**
     * Renders the query facet section.
     *
     * @param {string} id - The ID of the query facet.
     * @param {string} title - The title of the section.
     * @returns {JSX.Element|null} The rendered query facet section or null if no filters.
     */
    const renderQueryFacetSection = (id, title) => {
        const filterValue = queryFacetFilters[id];
        if (!filterValue) {
            return null;
        }
        return (
            <div key={id} className={styles.filters}>
                <p className={styles.titleFilter}>{t(`userPreferences.options.${title}`)}: </p>
                <p className={styles.itemFilter}>
                    {formatDateFilter(filterValue)}
                    <Button onClick={() => handleClearQueryFacetFilter(id, filterValue)}>
                        <img src={closeIcon} alt="" />
                    </Button>
                </p>
            </div>
        );
    };

The filterValue is the “last_modified:[2024-09-30T22:00:00Z TO 2024-10-28T23:00:00Z]” to be formatted. And formatDateFilter is the function that formats the value as follow:

...

Code Block
languagejs
/**
 * Formats a filter string with a date range into a human-readable format.
 *
 * @param {string} filterStr - The filter string in the format "field:[startDate TO endDate]".
 * Example: "creation_date:[2024-10-07T22:00:00Z TO 2024-10-23T22:00:00Z]"
 * @returns {string} The formatted filter string in the format "field: From DD/MM/YYYY To DD/MM/YYYY"
 * using French locale (fr-FR) for the date format.
 *  Example: "creation_date: From 07/10/2024 To 23/10/2024"
 */

function formatDateFilter(filterStr) {
    const match = filterStr.match(/(last_modified|creation_date):\\[(.*?) TO (.*?)\\]/);
    if (!match) return filterStr;

    const [, filterType, startDate, endDate] = match;

    // Проверяем, что startDate и endDate валидные даты
    const start = isValidDate(new Date(startDate))
        ? new Date(startDate).toLocaleDateString('fr-FR')
        : '*';
    const end = isValidDate(new Date(endDate))
        ? new Date(endDate).toLocaleDateString('fr-FR')
        : '*';

    return `${filterType}: From ${start} To ${end}`;
}

The formatting function uses regular expression to identify the filter value type and extract the useful data to be used for formatting.

👉 You can modify this example to add other filters using the matching found or not to go to another format function you added. The most important is to locate where to inject your new code. For Query Facet it is in renderQueryFacetSection and for Field Facet and HierarchicalFacet it is in renderFieldFacetSection

...

Code Block
languagejs
/**
 * Renders the field facet section.
 *
 * @param {string} title - The title of the section.
 * @param {string} field - The field of the facet.
 * @returns {JSX.Element|null} The rendered field facet section or null if no filters.
 */
const renderFieldFacetSection = (title, field) => {
    return (
        fieldFacetFilters[field] &&
        fieldFacetFilters[field].length > 0 && (
            <div key={field} className={styles.filters}>
                <p className={styles.titleFilter}>{t(`userPreferences.options.${title}`)}: </p>
                {fieldFacetFilters[field].map((value) => (
                    <p key={value} className={styles.itemFilter}>
                        {value}
                        <Button onClick={() => handleClearFieldFacetFilter(field, value)}>
                            <img src={closeIcon} alt="" />
                        </Button>
                    </p>
                ))}
            </div>
        )
    );
};

...

Info

Valid from Datafari 5.2 to 6.

...

1 (DatafariUI v1)

Expand
titleValid from Datafari 5.2 to 6.2...

The search information block consists in the information traditionally shown above the search results. It provides several insights about the current search such as the terms of the search, the number of results and the filters currently in use. It is shown with a red bar on the side below:

Image Modified

Filters displayed from field facet or query facet can’t be personalized, however, other filters can.

For example, the custom date range that is part of the creation date facet has a customized rendering.

This documentation will explain how it is done, and how the system can be used for other custom filters in the future.

There are different part in the management of a filter:

  1. The creation / declaration of the filter in the query

  2. The rendering of the filter in the search info block

  3. The deletion of the filter

We will not be interested in the deletion of the filter. However, the creation and rendering are the part we will work on. The documentation is based on the study of the custom date range available in DatafariUI.

Creating the filter

For our study case, the filter is created in the DateFacetCustom component (https://gitlab.datafari.com/datafari-community/DatafariUI/-/blob/master/src/Components/DateFacetCustom/DateFacetCustom.js ).

I won’t go into details on how the whole component works, and instead focus on the interesting parts for our problem. When the user clicks on the “Go” button, a new filter is created.

This is done by dispatching a REGISTER_FILTER event to the query context:

Code Block
languagejs
const handleGoClick = () => {
    if (selectedToDate || selectedFromDate) {
      const fromDateString = selectedFromDate
        ? selectedFromDate.toISOString()
        : '*';
      const toDateString = selectedToDate ? selectedToDate.toISOString() : '*';
      const field = props.field ? props.field : 'creation_date';
      const newFilter = {
        value: `${field}:[${fromDateString} TO ${toDateString}]`,
        extra: {
          type: DATE_RANGE,
          from: selectedFromDate,
          to: selectedToDate,
          field: field,
        },
        id: FILTER_ID,
      };
      queryDispatch({
        type: REGISTER_FILTER,
        filter: newFilter,
        overrideIfExist: true,
      });
    } else {
      handleResetClick();
    }
  };

If you have a look at the newFilter object, this is what defines our filter.

You can see it has two keys:

  • value: this is the solr filter that will be added as an fq to the query

  • extra: this is an object containing special information used only to handle the rendering of the filter in the search information block. This object has the following structure:

    • type: mandatory field containing an identifier for this type of filter. It is best to define IDs in the useFilterFormater (https://gitlab.datafari.com/datafari-community/DatafariUI/-/blob/master/src/Hooks/useFilterFormater.js ) hook. It is the one responsible for the rendering of the filters. Here is the definition for the date range id:

      Code Block
      export const DATE_RANGE = 'DATE_RANGE';
    • other fields: You can put any other keys and values you need in this extra object to perform a proper rendering of the filter later.

Rendering the filter

Defining how the filter should be rendered takes place in the useFilterFormater hook (https://gitlab.datafari.com/datafari-community/DatafariUI/-/blob/master/src/Hooks/useFilterFormater.js).

There is a filterFormat function in there that you need to change to add support for your new type of filter.

Code Block
languagejs
const filterFormat = useCallback(
    (filter) => {
      if (filter.extra && filter.extra.type) {
        switch (filter.extra.type) {
          case DATE_RANGE:
            if (filter.extra.from && filter.extra.to && filter.extra.field) {
              const dateLocale = getDateLocale();
              try {
                return `${filter.extra.field}: ${t('From')} ${format(
                  new Date(filter.extra.from),
                  'P',
                  {
                    locale: dateLocale,
                  }
                )} ${t('To')} ${format(new Date(filter.extra.to), 'P', {
                  locale: dateLocale,
                })}`;
              } catch (e) {
                // If any kind of exception happens, just return the filter value to be displayed.
                return filter.value;
              }
            } else {
              return filter.value;
            }
          default:
            return filter.value;
        }
      }
      return filter.value;
    },
    [getDateLocale, t]
  );

As you can see, there is a switch on the filter.extra.type key of the filter object. You can add a case statement for your new type of filter and add a dedicated rendering management based on the elements you put into the extra object. That is what has been done for the rendering of the date range example above.