All files / src/features/Events/EventDetail EventListFilterButton.tsx

100% Statements 8/8
100% Branches 2/2
100% Functions 3/3
100% Lines 6/6

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              10x                                             10x             65x   65x                                                         65x                 14x                  
import { MouseEvent } from "react";
import { Stack } from "@mui/material";
import { FunnelSimpleIcon, XIcon } from "@phosphor-icons/react";
import GiveButton from "@shared/Button/GiveButton";
import GiveIconButton from "@shared/IconButton/GiveIconButton";
import { styled } from "@theme/v2/Provider";
 
export const CLEAR_FILTERS_ARIA_LABEL = "Clear filters";
 
type Props = {
  /** `useFilterButton`'s label — "Filter", or "Filters (N)" once applied. */
  label: string;
  filtersAmount: number;
  onOpen: () => void;
  /** `useFilterButton`'s reset. */
  onClear: (event?: MouseEvent<HTMLElement>) => void;
  testId: string;
};
 
/**
 * The desktop Filter control shared by the Event Detail Transactions and Tickets
 * tabs (frame 8238-63616, "Table Options"): the ghost pill with the funnel, and
 * — once a filter is applied — a small ✕ chip after the count that clears it in
 * place. Without the chip, a filter that matched nothing left the user with an
 * empty list and only the panel's own Clear All as a way out.
 *
 * The chip is a sibling of the pill, not a child: a control nested inside a
 * button is invalid markup and reads to a screen reader as a button in a
 * button. Two real buttons also give Tab two stops and Enter / Space for free.
 */
const EventListFilterButton = ({
  label,
  filtersAmount,
  onOpen,
  onClear,
  testId,
}: Props) => {
  const isApplied = filtersAmount > 0;
 
  return (
    <Stack direction="row" alignItems="center">
      <FilterButton
        variant="ghost"
        color="light"
        size="large"
        startIcon={<FunnelSimpleIcon size={18} />}
        data-testid={testId}
        onClick={onOpen}
        label={label}
      />
      {isApplied && (
        <ClearButton
          Icon={XIcon}
          variant="ghost"
          size="extraSmall"
          aria-label={CLEAR_FILTERS_ARIA_LABEL}
          data-testid={`${testId}-clear`}
          onClick={onClear}
        />
      )}
    </Stack>
  );
};
 
export default EventListFilterButton;
 
// Matches the Export control beside it — frame 7397-90412 puts both on one 42px
// row.
const FilterButton = styled(GiveButton)(() => ({
  borderRadius: "40px",
  height: "42px",
  whiteSpace: "nowrap",
}));
 
// Frame 8238-63616 "Clear Button": a 16px ✕ on a 2px pad of darken-5. The pill
// carries 20px of end padding, so the negative margin is what leaves the
// frame's 8px between the count and the chip.
const ClearButton = styled(GiveIconButton)(({ theme }) => ({
  marginLeft: "-12px",
  padding: "2px",
  borderRadius: "38px",
  backgroundColor: theme.palette.primitive?.transparent["darken-5"],
  "&:hover, &:focus-visible": {
    backgroundColor: theme.palette.primitive?.transparent["darken-10"],
  },
}));