All files / src/features/Settlements/ReconciliationHub/components HubDropdown.tsx

100% Statements 13/13
80% Branches 8/10
100% Functions 7/7
100% Lines 11/11

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115                                                                        5x                           144x 144x   144x   259x           3x 3x           144x                     8x         3x                               144x                            
import { CaretDownIcon, CaretUpIcon, CheckIcon } from "@phosphor-icons/react";
import GiveButton from "@shared/Button/GiveButton";
import ContextualMenu from "@shared/ContextualMenu/ContextualMenu";
import { GiveSearchBarProps } from "@shared/SearchBar/GiveSearchBar";
import { styled } from "@theme/v2/Provider";
import { useCustomThemeV2 } from "@theme/hooks/useCustomThemeV2";
import { ReactElement, ReactNode, useMemo, useState } from "react";
 
export interface HubDropdownOption {
  value: string;
  label: string;
  id?: number;
  Image?: ReactElement;
}
 
interface HubDropdownProps {
  selectedValue: string;
  selectedLabel: string;
  selectedImg?: ReactNode;
  options: HubDropdownOption[];
  onSelect: (option: HubDropdownOption) => void;
  searchBarProps?: GiveSearchBarProps;
  isLoading?: boolean;
  isFetchingNextPage?: boolean;
  onEndReached?: () => void;
  menuWidth?: number;
  disabled?: boolean;
  dataTestId?: string;
}
 
/**
 * Controlled dropdown for the Reconciliation Hub filter bar. Unlike the shared
 * URL-param-driven `DropdownFilter`, this drives the hub's local filter state:
 * selection is reported through `onSelect`. Reuses the shared `GiveButton` +
 * `ContextualMenu` primitives for visual parity with the other filters.
 */
const HubDropdown = ({
  selectedValue,
  selectedLabel,
  selectedImg,
  options,
  onSelect,
  searchBarProps,
  isLoading,
  isFetchingNextPage,
  onEndReached,
  menuWidth = 278,
  disabled = false,
  dataTestId,
}: HubDropdownProps) => {
  const { isMobileView } = useCustomThemeV2();
  const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
 
  const menuOptions = useMemo(
    () =>
      options.map((option) => ({
        text: option.label,
        isSelected: option.value === selectedValue,
        IconRight: option.value === selectedValue ? CheckIcon : undefined,
        Image: option.Image,
        onClick: () => {
          onSelect(option);
          setAnchorEl(null);
        },
      })),
    [options, selectedValue, onSelect],
  );
 
  return (
    <>
      <StyledButton
        variant="filled"
        color="light"
        label={selectedLabel}
        size="large"
        disabled={disabled}
        data-testid={dataTestId}
        startIcon={selectedImg}
        endIcon={anchorEl ? <CaretUpIcon size={16} /> : <CaretDownIcon size={16} />}
        onClick={(e: React.MouseEvent<HTMLElement>) => setAnchorEl(e.currentTarget)}
        sx={{ minWidth: "fit-content", height: "40px" }}
      />
      <ContextualMenu
        menuWidth={menuWidth}
        handleClose={() => setAnchorEl(null)}
        anchorEl={anchorEl}
        color={isMobileView ? "primary" : "transparent"}
        texture={isMobileView ? "solid" : "blurred"}
        horizontalOrigin="left"
        options={menuOptions}
        searchBarProps={searchBarProps}
        isLoading={isLoading}
        isFetchingNextPage={isFetchingNextPage}
        onEndReached={onEndReached}
        emptySection="empty-search"
      />
    </>
  );
};
 
const StyledButton = styled(GiveButton)(({ theme }) => ({
  borderRadius: "40px",
  whiteSpace: "nowrap",
  border: "none",
  [theme.breakpoints.down("v2_md")]: {
    background: theme.palette.primitive?.transparent?.["darken-5"],
    color: theme.palette.text.primary,
  },
  "&:hover": {
    backgroundColor: theme.palette.primitive?.transparent?.["darken-5"],
  },
}));
 
export default HubDropdown;