All files / src/components/SearchPanel TruncatedDescription.tsx

85.71% Statements 30/35
100% Branches 12/12
66.66% Functions 2/3
85.71% Lines 30/35

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 631x 1x 1x 1x                 1x                         1x 1x 6x 6x 6x 6x   6x 1x 1x   6x 2x 2x   6x 6x   6x 6x 6x 6x 6x 6x 6x 6x           6x 6x       6x  
import { display } from '@/../config.json';
import { DescriptionToggleLink } from '@/components/StyledComponents';
import { i18n } from '@/helpers/i18n';
import { type KeyboardEvent, type ReactElement, useState } from 'react';
 
interface TruncatedDescriptionProps {
  /** The full description text to render. */
  text: Maybe<string>;
  /** Character limit before truncation; defaults to `display.descriptionPreviewLength` in config.json. */
  limit?: number;
}
 
/**
 * Renders a product description, truncating it to `limit` characters with an
 * inline "[more]" toggle when it is longer. Expanding reveals the full text with
 * a "[less]" toggle to collapse it back to the preview length. Short
 * descriptions (and empty ones) render without a toggle.
 * @param props - The description text and optional character limit.
 * @returns The (optionally truncated) description with a toggle, or null when empty.
 * @example
 * ```tsx
 * <TruncatedDescription text={product.description} />
 * <TruncatedDescription text={longText} limit={120} />
 * ```
 * @source
 */
export function TruncatedDescription({
  text,
  limit = display.descriptionPreviewLength,
}: TruncatedDescriptionProps): ReactElement | null {
  const [expanded, setExpanded] = useState(false);
 
  if (!text) {
    return null;
  }
 
  if (text.length <= limit) {
    return <span className="detail-value">{text}</span>;
  }
 
  const preview = expanded ? text : `${text.slice(0, limit).trimEnd()}…`;
  const toggle = () => setExpanded((prev) => !prev);
 
  return (
    <span className="detail-value">
      {preview}
      <DescriptionToggleLink
        role="button"
        tabIndex={0}
        onClick={toggle}
        onKeyDown={(event: KeyboardEvent<HTMLSpanElement>) => {
          if (event.key === 'Enter' || event.key === ' ') {
            event.preventDefault();
            toggle();
          }
        }}
      >
        [{expanded ? i18n('common_show_less') : i18n('common_show_more')}]
      </DescriptionToggleLink>
    </span>
  );
}