Step 3: Creating the SortDropdown UI

A few things were added to the complete example below to make it fully functional:

  • A dropdown list is returned by the component that displays the list of sorting options. The useState hook was added to track the open/closed state of the dropdown.
  • When an option is clicked, the handleTileClick function will use the useSearchActions hook to set the sort in the search state, execute a new search, and close the dropdown.
  • React Icons are added to the project for the up/down arrows

    // src/components/SortDropdown.tsx
    
    import {
    Direction,
    SortBy,
    SortType,
    useSearchActions,
    useSearchState,
    } from "@yext/search-headless-react";
    import { useState } from "react";
    import { BiChevronDown, BiChevronUp } from "react-icons/bi";
    
    // object that has a label for the different sort types
    const sortByOptions: { label: string, sortBy: SortBy }[] = [
    {
    label: "Name: A-Z",
    sortBy: {
      field: "name",
      direction: Direction.Ascending,
      type: SortType.Field,
    },
    },
    {
    label: "Name: Z-A",
    sortBy: {
      field: "name",
      direction: Direction.Descending,
      type: SortType.Field,
    },
    },
    ];
    
    const SortDropdown = (): JSX.Element | null => {
    // used to manage dropdown open/close state
    const [open, setOpen] = useState(false);
    
    // sortBys contains all the sortBy options that are in the search state
    const sortBys = useSearchState((state) => state.vertical.sortBys);
    // checking both result counts as <VerticalResults /> will show allResultsForVertical if there are no results for query
    const resultsCount =
    useSearchState(
      (state) =>
        state.vertical.resultsCount ||
        state.vertical.noResults?.allResultsForVertical.resultsCount
    ) ?? 0;
    
    const searchActions = useSearchActions();
    
    // selectedSort is the currently selected sortBy option
    const selectedSort = sortByOptions.find(
    (s) =>
      s.sortBy.field === sortBys?.[0]?.field &&
      s.sortBy.direction === sortBys?.[0]?.direction
    );
    
    // this function will set the sortBys in the search state, execute the search, and close the drawer
    const handleTileClick = (sortBy: SortBy) => {
    searchActions.setSortBys([sortBy]);
    searchActions.executeVerticalQuery();
    setOpen(false);
    };
    
    return resultsCount ? (
    <div className="relative text-sm">
      <div className="flex h-10 w-48 items-center justify-between border px-2">
        <div className="text-sm">
          <div className="font-semibold">Sort By:</div>
          <div>{selectedSort?.label}</div>
        </div>
        <button onClick={() => setOpen(!open)}>
          {open ? <BiChevronUp /> : <BiChevronDown />}
        </button>
      </div>
      <ul className="absolute border">
        {open &&
          sortByOptions
            // filtering out the selected sort option from the dropdown
            .filter((s) => s.sortBy !== selectedSort?.sortBy)
            .map((s) => (
              // when a sort option is clicked, call handleTileClick
              <li onClick={() => handleTileClick(s.sortBy)}>
                <div className="flex h-10 w-48 items-center px-2 bg-white hover:bg-gray-300">
                  {s.label}
                </div>
              </li>
            ))}
      </ul>
    </div>
    ) : null;
    };
    
    export default SortDropdown;