# All files / link SkipToMainContent.tsx

82.64% Statements 100/121
62.5% Branches 5/8
50% Functions 2/4
82.64% Lines 100/121

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
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    1221x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x

    1x
    1x
    5x

    5x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    1x
    10x
    10x
    10x
    10x
    10x
    10x
    10x
    10x
    10x
    10x
    10x
    10x
    10x
    10x
    10x
    9x
    9x
    5x
    5x
    5x
    1x
    1x
    1x

    1x
    1x
    1x
    1x
    1x
    1x
    1x
    9x
    9x
    9x
    9x
    9x
    9x
    9x
    9x
    9x
    9x

    9x
    9x
    9x
    9x
    9x
    9x
    9x
    9x
    9x
     "use client";
    import type { ReactNode } from "react";
    import { forwardRef, useEffect, useRef } from "react";
    import type { LinkProps } from "./Link.js";
    import { Link } from "./Link.js";
    import type { SKipToMainContentClassNameOptions } from "./styles.js";
    import { skipToMainContent } from "./styles.js";

    const noop = (): void => {
      // do nothing
    };

    const getMainElement = (mainId: string): HTMLElement | null =>
      mainId
        ? document.getElementById(mainId)
        : document.querySelector<HTMLElement>('main,[role="main"]');

    /**
     * @remarks
     * \@since 6.0.0 The {@link mainId} is optional
     */
    export interface SkipToMainContentProps
      extends Omit<LinkProps, "href">,
        SKipToMainContentClassNameOptions {
      id?: string;

      /**
       * An optional id for the main element. When this is not provided, the main
       * element will be found by:
       *
       * ```ts
       * document.querySelector('main,[role="main"]');
       * ```
       */
      mainId?: string;

      /**
       * @defaultValue `"Skip to main content"`
       */
      children?: ReactNode;
    }

    /**
     * **Client Component**
     *
     * @remarks
     * \@since 6.0.0 Throws an error after rendering if no main element can be found
     * with the provided `mainId` in development mode. The previous behavior would
     * only log an error after being clicked.
     */
    export const SkipToMainContent = forwardRef<
      HTMLAnchorElement,
      SkipToMainContentProps
    >(function SkipToMainContent(props, ref) {
      const {
        id = "skip-to-main",
        className,
        children = "Skip to main content",
        mainId = "",
        onClick = noop,
        unstyled = false,
        ...remaining
      } = props;

      const mainNodeRef = useRef<HTMLElement | null>(null);
      // want to warn the developer about missing main element in development
      // immediately to help prevent errors, but in production this can be lazy
      // initialized only once a keyboard user focuses and clicks this element
      if (process.env.NODE_ENV !== "production") {
        // eslint-disable-next-line react-hooks/rules-of-hooks
        useEffect(() => {
          mainNodeRef.current = getMainElement(mainId);

          if (!mainNodeRef.current) {
            const foundMainId = document.querySelector('main,[role="main"]')?.id;
            let message = `Unable to find a main element to focus`;
            if (mainId) {
              message += ` with an id of "${mainId}"`;

              if (foundMainId) {
                message += ` but a main element was found with an id of "${foundMainId}".`;
              }
            }
            if (!foundMainId) {
              message +=
                '. There should be at least one <main> element or an element with role="main" on the page for accessibility.';
            }

            throw new Error(message);
          }
        }, [mainId]);
      }

      return (
        <Link
          {...remaining}
          id={id}
          ref={ref}
          href={`#${mainId}`}
          onClick={(event) => {
            onClick(event);
            if (event.isPropagationStopped()) {
              return;
            }

            event.preventDefault();
            event.stopPropagation();

            // see comment above useEffect
            mainNodeRef.current ||= getMainElement(mainId);
            mainNodeRef.current?.focus();
          }}
          className={skipToMainContent({
            unstyled,
            className,
          })}
        >
          {children}
        </Link>
      );
    });