All files / src/componentsV2/Table MobileTableHeaderContext.tsx

95.45% Statements 21/22
100% Branches 0/0
87.5% Functions 7/8
100% Lines 20/20

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                                                                                  36x   36x                     36x   36x         14x 14x 14x   14x 9x                       14x             1236x           36x   1227x   1227x 6x     1227x 6x     1227x 4x       1227x    
import {
  ReactNode,
  createContext,
  useCallback,
  useContext,
  useMemo,
  useState,
} from "react";
 
/**
 * TRA013 — mobile scroll header.
 *
 * On mobile the table must NOT render its own absolute "compact head" bar
 * (which used to appear directly below the fixed top bar and get partially
 * covered by it). Instead, the compact head content — the page title/stats +
 * search + three-dots menu — is handed up to the fixed `MobileNavBar`, which
 * expands to show it while the full banner is scrolled out of view.
 *
 * The Table publishes its compact head node + visibility here; `MobileNavBar`
 * consumes it. `isEnabled` tells the Table whether a topbar host actually
 * exists — when it is false (e.g. no-sidebar layouts that never render the
 * mobile navbar) the Table falls back to its own inline compact bar so the
 * search + actions stay reachable.
 *
 * A table may additionally publish a `compactTitle` — what the page is about,
 * shown in the top bar's FIRST row (where the balance normally sits) while the
 * compact head is expanded. Used by pages whose subject has a name rather than
 * stats (Event Detail, frame 8091-56253): the name goes up beside the menu
 * button so the second row is left to the full-width search. Pages that publish
 * none keep the balance, unchanged.
 */
type MobileTableHeaderContextType = {
  isEnabled: boolean;
  compactHead: ReactNode | null;
  compactTitle: ReactNode | null;
  isActive: boolean;
  setCompactHead: (node: ReactNode | null) => void;
  setCompactTitle: (node: ReactNode | null) => void;
  setActive: (active: boolean) => void;
};
 
const noop = () => undefined;
 
const DEFAULT_VALUE: MobileTableHeaderContextType = {
  isEnabled: false,
  compactHead: null,
  compactTitle: null,
  isActive: false,
  setCompactHead: noop,
  setCompactTitle: noop,
  setActive: noop,
};
 
const MobileTableHeaderContext =
  createContext<MobileTableHeaderContextType>(DEFAULT_VALUE);
 
export const MobileTableHeaderProvider = ({
  children,
}: {
  children: ReactNode;
}) => {
  const [compactHead, setCompactHead] = useState<ReactNode | null>(null);
  const [compactTitle, setCompactTitle] = useState<ReactNode | null>(null);
  const [isActive, setActive] = useState(false);
 
  const value = useMemo<MobileTableHeaderContextType>(
    () => ({
      isEnabled: true,
      compactHead,
      compactTitle,
      isActive,
      setCompactHead,
      setCompactTitle,
      setActive,
    }),
    [compactHead, compactTitle, isActive],
  );
 
  return (
    <MobileTableHeaderContext.Provider value={value}>
      {children}
    </MobileTableHeaderContext.Provider>
  );
};
 
export const useMobileTableHeader = () => useContext(MobileTableHeaderContext);
 
/**
 * Convenience hook for the Table: keeps setter identities stable so callers can
 * safely include them in effect deps.
 */
export const useMobileTableHeaderControls = () => {
  const { isEnabled, setCompactHead, setCompactTitle, setActive } =
    useMobileTableHeader();
 
  const publish = useCallback(
    (node: ReactNode | null) => setCompactHead(node),
    [setCompactHead],
  );
  const publishTitle = useCallback(
    (node: ReactNode | null) => setCompactTitle(node),
    [setCompactTitle],
  );
  const activate = useCallback(
    (active: boolean) => setActive(active),
    [setActive],
  );
 
  return { isEnabled, publish, publishTitle, activate };
};