summaryrefslogtreecommitdiffstats
path: root/src/components/MetaItems
Commit message (Collapse)AuthorAgeFilesLines
* refactor: use formatjs swc pluginArmand Philippot2022-03-238-1/+15
| | | | | I'm not able to configure SWC plugins in Next.js so to make it works, all translation must have an id.
* refactor: split posts meta into smaller componentsArmand Philippot2022-03-0111-0/+372
ref='#n45'>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 122 123 124 125 126 127
import Link from '@components/atoms/links/link';
import { settings } from '@utils/config';
import Script from 'next/script';
import { FC } from 'react';
import { useIntl } from 'react-intl';
import { BreadcrumbList, ListItem, WithContext } from 'schema-dts';
import styles from './breadcrumb.module.scss';

export type BreadcrumbItem = {
  /**
   * The item id.
   */
  id: string;
  /**
   * The item URL.
   */
  url: string;
  /**
   * The item name.
   */
  name: string;
};

export type BreadcrumbProps = {
  /**
   * Set additional classnames to the nav element.
   */
  className?: string;
  /**
   * Set additional classnames to the breadcrumb items.
   */
  itemClassName?: string;
  /**
   * The breadcrumb items
   */
  items: BreadcrumbItem[];
};

/**
 * Breadcrumb component
 *
 * Render a breadcrumb navigation.
 */
const Breadcrumb: FC<BreadcrumbProps> = ({
  itemClassName = '',
  items,
  ...props
}) => {
  const intl = useIntl();

  const ariaLabel = intl.formatMessage({
    defaultMessage: 'Breadcrumb',
    description: 'Breadcrumb: an accessible name for the breadcrumb nav.',
    id: '28nnDY',
  });

  /**
   * Retrieve the breadcrumb list items.
   *
   * @param {BreadcrumbItem[]} list - The breadcrumb items.
   * @returns {JSX.Element[]} The list items.
   */
  const getListItems = (list: BreadcrumbItem[]): JSX.Element[] => {
    return list.map((item, index) => {
      const isLastItem = index === list.length - 1;
      const itemStyles = isLastItem
        ? `${styles.item} screen-reader-text`
        : styles.item;

      return (
        <li key={item.id} className={`${itemStyles} ${itemClassName}`}>
          {isLastItem ? item.name : <Link href={item.url}>{item.name}</Link>}
        </li>
      );
    });
  };

  /**
   * Retrieve the breadcrumb list items with Schema.org format.
   *
   * @param {BreadcrumbItem[]} list - The breadcrumb items.
   * @returns {ListItem[]} An array of list items using Schema.org format.
   */
  const getSchemaItems = (list: BreadcrumbItem[]): ListItem[] => {
    const schemaItems: ListItem[] = [];

    list.forEach((item, index) => {
      schemaItems.push({
        '@type': 'ListItem',
        position: index + 1,
        name: item.name,
        item: item.url,
      });
    });

    return schemaItems;
  };

  const schemaJsonLd: WithContext<BreadcrumbList> = {
    '@context': 'https://schema.org',
    '@type': 'BreadcrumbList',
    '@id': `${settings.url}/#breadcrumb`,
    itemListElement: getSchemaItems(items),
  };

  return (
    <>
      <Script
        id="schema-breadcrumb"
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(schemaJsonLd) }}
      />
      <nav aria-label={ariaLabel} {...props}>
        <span className="screen-reader-text">
          {intl.formatMessage({
            defaultMessage: 'You are here:',
            description: 'Breadcrumb: You are here prefix',
            id: '16zl9Z',
          })}
        </span>
        <ol className={styles.list}>{getListItems(items)}</ol>
      </nav>
    </>
  );
};

export default Breadcrumb;