All files / src/shared/SearchBar GiveExpandingSearch.tsx

80% Statements 12/15
69.23% Branches 9/13
50% Functions 2/4
92.3% Lines 12/13

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                                                          26x 55x 55x 55x 55x       55x 55x   55x 44x         1x 1x                     11x                                                
import { Box } from "@mui/material";
import { MagnifyingGlassIcon } from "@phosphor-icons/react";
import { useRef, useState } from "react";
import GiveIconButton from "shared/IconButton/GiveIconButton";
import { useCustomThemeV2 } from "@theme/hooks/useCustomThemeV2";
import { useAppTheme } from "@theme/v2/Provider";
import GiveSearchBar from "./GiveSearchBar";
 
type Props = {
  value: string;
  onChange?: (value: string) => void;
  /** Width of the expanded search field, in px. Defaults to 240. */
  width?: number;
};
 
/**
 * Search control that starts collapsed as a magnifying-glass icon and expands
 * into a full search field when clicked. When the field is left empty and
 * blurred it collapses back to the icon; a non-empty value keeps it expanded.
 *
 * Extracted from the compact-on-scroll table headers (ManageMoney, Products,
 * Developer API, Merchant/Provider action handlers), where this markup was
 * duplicated verbatim.
 *
 * TRA013: below the wide breakpoint (< 1024px — phones and tablet portrait) there
 * is room for a real search field and the scroll-header mockup shows one, so it
 * renders already-expanded and full-width (never collapsing back to an icon)
 * instead of the tap-to-expand icon used on the wider (desktop) inline compact bar.
 */
const GiveExpandingSearch = ({ value, onChange, width = 240 }: Props) => {
  const { isWideView } = useCustomThemeV2();
  const { palette } = useAppTheme();
  const [expanded, setExpanded] = useState(false);
  const inputRef = useRef<HTMLInputElement | null>(null);
 
  // Below the wide breakpoint the field is always shown (full-width); only the
  // desktop compact bar keeps the tap-to-expand icon.
  const isCompact = !isWideView;
  const showField = expanded || isCompact;
 
  if (!showField) {
    return (
      <GiveIconButton
        Icon={MagnifyingGlassIcon}
        variant="ghost"
        onClick={() => {
          setExpanded(true);
          requestAnimationFrame(() => inputRef.current?.focus());
        }}
        // TRA013: the collapsed search control keeps a visible outline in its
        // rest state (per the mockups), so it reads as a tappable search field
        // rather than a bare icon. `ghost` supplies the hover/active fills; the
        // border is layered on here via the design-system border token.
        sx={{ border: `1px solid ${palette.border?.primary}` }}
      />
    );
  }
 
  return (
    <Box
      sx={{
        width: isCompact ? "100%" : width,
        flex: isCompact ? 1 : undefined,
        minWidth: 0,
      }}
    >
      <GiveSearchBar
        value={value}
        handleChange={onChange}
        searchOnEnter
        resetOnClose={false}
        inputRef={inputRef}
        alwaysShowClear={!isCompact}
        onInputBlur={() => {
          if (!value && !isCompact) setExpanded(false);
        }}
      />
    </Box>
  );
};
 
export default GiveExpandingSearch;