All files / Rindu/components/TextHighlighter TextHighlighter.tsx

90% Statements 9/10
100% Branches 8/8
100% Functions 3/3
90% Lines 9/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              20x               6x       10x             12x 8x 8x   8x 22x                                 4x                                          
import {
  Children,
  cloneElement,
  isValidElement,
  PropsWithChildren,
  ReactElement,
  ReactNode,
} from "react";
 
import { escapeRegExp } from "lodash";
 
interface TextHighlighterProps {
  text: string;
}
 
export default function TextHighlighter({
  text,
  children,
}: PropsWithChildren<TextHighlighterProps>): ReactElement {
  if (!text) {
    return <>{children}</>;
  }
 
  return (
    <>
      {Children.map(children, (child: ReactNode) => {
        if (typeof child === "string") {
          const escapedText = escapeRegExp(text);
          const parts = child.split(new RegExp(`(${escapedText})`, "gi"));
 
          return parts.map((part, index) =>
            part.toLowerCase() === text.toLowerCase() ? (
              <mark key={`highlight-${index}`}>
                {part}
                <style jsx>{`
                  mark {
                    background-color: #2e77d0;
                    border-radius: 4px;
                    color: #fff;
                  }
                `}</style>
              </mark>
            ) : (
              part
            )
          );
        }
 
        if (
          isValidElement(child) &&
          "props" in child &&
          "children" in child.props
        ) {
          return cloneElement(
            child,
            // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
            child.props,
            <TextHighlighter text={text}>
              {/* eslint-disable-next-line @typescript-eslint/no-unsafe-member-access */}
              {child?.props.children}
            </TextHighlighter>
          );
        }
 
        return child;
      })}
    </>
  );
}