All files / json-viewer/src ValueOverlay.tsx

100% Statements 10/10
50% Branches 2/4
100% Functions 3/3
100% Lines 10/10

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                                  1x                                         6x   6x 6x 1x 1x 1x           6x 5x     6x                                              
"use client";
 
/**
 * The full value of a truncated row (SPEC.md decision #4).
 *
 * An **overlay**, not a taller row. Every scroll offset in `useRowWindow` is
 * `index × rowHeight`; letting a row grow would invalidate all of them and put
 * this package back in measured-virtualization territory — the "different,
 * much larger problem" `@usefy/diff-viewer` documented and declined. Wrapping
 * the value across several fixed-height rows was the other candidate and is
 * worse still: the row count would then depend on the container's width, so a
 * resize would silently change `rowCount()`.
 */
 
import { useEffect, useRef } from "react";
import { useEventListener } from "@usefy/use-event-listener";
import { useOnClickOutside } from "@usefy/use-on-click-outside";
import styles from "./styles/JsonViewer.module.scss";
 
export interface ValueOverlayProps {
  text: string;
  /** Position within the viewer root, in pixels. */
  top: number;
  left: number;
  /** Row label, for the accessible name. */
  label: string;
  onCopy: () => void;
  onClose: () => void;
}
 
export function ValueOverlay({
  text,
  top,
  left,
  label,
  onCopy,
  onClose,
}: ValueOverlayProps) {
  const ref = useRef<HTMLDivElement>(null);
 
  useOnClickOutside(ref, onClose);
  useEventListener("keydown", (event: KeyboardEvent) => {
    Eif (event.key === "Escape") {
      event.stopPropagation();
      onClose();
    }
  });
 
  // Focus moves in so the value is reachable by keyboard and by a screen
  // reader; `JsonViewer` returns focus to the row that opened it.
  useEffect(() => {
    ref.current?.focus();
  }, []);
 
  return (
    <div
      ref={ref}
      role="dialog"
      aria-label={`Full value of ${label || "this node"}`}
      tabIndex={-1}
      className={styles.overlay}
      style={{ top, left }}
      data-testid="json-value-overlay"
    >
      <div className={styles.overlayBar}>
        <span>{`${text.length.toLocaleString()} characters`}</span>
        <button type="button" className={styles.button} onClick={onCopy}>
          Copy
        </button>
        <button type="button" className={styles.button} onClick={onClose}>
          Close
        </button>
      </div>
      <p className={styles.overlayText}>{text}</p>
    </div>
  );
}