Skip to content

Latest commit

 

History

History
377 lines (276 loc) · 10.3 KB

README.md

File metadata and controls

377 lines (276 loc) · 10.3 KB

React table of content

Provides hooks and utility functions to create a custom and reusable autogenerated table of content for a React project, works really well with markdown content as well.

NPM Downloads GitHub License NPM Version

Table of Contents

Features

  • Generates a table of content based on the headings in a React component.
  • Supports custom selectors for headings.
  • Supports custom dependencies to watch for changes.
  • Provides utility functions to add IDs to heading tags in HTML.
  • Provides utility functions to convert strings to dash case.
  • Provides utility functions to convert links in HTML to external links (open in new tab).
  • Lightweight and easy to use.
  • No dependencies.

Installation

npm install react-table-of-content

Usage

import React from "react";
import { useTableOfContent } from "react-table-of-content";

export const TableOfContent: React.FC<{}> = () => {
  const { headingLinks, contentIsActive } = useTableOfContent();

  //   return (
  //     <>JSX</>
  //   )
};

Example implementation

You can create a reusable component that generates a table of content for your page using the hook. See the example below.

Reusable custom TableOfContent component
import React from "react";
import { useTableOfContent } from "react-table-of-content";

export const TableOfContent: React.FC<{}> = () => {
  const { headingLinks, contentIsActive } = useTableOfContent({
    selectors: "article h1, article h2, article h3",
  });

  if (!headingLinks || headingLinks.length < 1) return null;

  return (
    <div>
      <h4 id="on-this-page">ON THIS PAGE</h4>
      <ul className="mt-4 space-y-2">
        {headingLinks.map((link) => {
          const isActive = contentIsActive(link.id);

          return (
            <li key={link.id}>
              <a
                className={`${
                  isActive ? "text-gray-800 font-semibold" : "text-gray-500"
                }
                ${["h3", "h4"].includes(link.tagName.toLowerCase()) && "pl-2"}
                ${["h5", "h6"].includes(link.tagName.toLowerCase()) && "pl-4"}
                hover:text-gray-700 transition-colors duration-200
                `}
                href={`#${link.id}`}
              >
                {link.title}
              </a>
            </li>
          );
        })}
      </ul>
    </div>
  );
};

You can then use the component in your page like so:

Example App component that has needs table of content
import React from "react";
import { TableOfContent } from "./TableOfContent";

export const App: React.FC = () => {
  return (
    <div className="flex">
      <div className="p-10 w-64">
        <div className="sticky top-8">
          <div className="mb-2">
            <img className="w-8 h-8" src="/favicon.png" alt="logo" />
          </div>
          <TableOfContent />
        </div>
      </div>
      <div className="flex-1">
        <Article />
      </div>
    </div>
  );
};

Below is an example of an article component that has headings.

Simple Article component with headings
import React from "react";

const Article = () => {
  const headingTags = ["h1", "h2", "h3", "h4", "h5", "h6"];
  const dummyText =
    "Lorem ipsum dolor sit amet consectetur adipisicing elit. Laborum vero accusamus alias cumque numquam atque eius ullam nobis at! Necessitatibus, corporis earum? Quidem, corporis blanditiis sapiente veritatis saepe debitis expedita!.";
  return (
    <article>
      <div className="container max-w-screen-xl">
        {headingTags.map((tag, index) => {
          const headingElement = createElement(
            tag,
            { key: index, id: `${tag}-heading` },
            `${tag.toUpperCase()} heading`
          );
          return (
            <section key={index}>
              {headingElement}
              {/* Returns something like this: <h1 id={`${tag}-heading`}>{tag.toUpperCase()} heading</h1> */}
              <p>{dummyText}</p>
            </section>
          );
        })}
      </div>
    </article>
  );
};

API

Hooks

useTableOfContent

The useTableOfContent hook generates a table of content for a React component based on the headings in the component.

Arguments
Name Type Required Default Description
selectors string Optional "h1, h2, h3" A string of comma-separated selectors to use to generate the table of content.
deps unknown Optional undefined An item to be used within the dependency array to watch for changes.
Return value

An object with the following properties:

Name Type Description
headingLinks array It contains an array of objects that have the following properties:id (string): The ID of the heading, title (string): The text content of the heading, tagName (string): The tag name of the heading.
contentIsActive function A function that takes an ID of the headingLink and returns a boolean indicating whether the content with that ID is active.

Utility functions

The package also exports utility functions that can be used to generate the table of content.

addIdToHeadingTags

The addIdToHeadingTags function adds an ID to each heading tag in a string of HTML.

Arguments

Accepts 1 argument of type string containing the HTML to add IDs to heading tags.

Return value

A string of HTML with IDs added to the heading tags.

Usage and example
import { addIdToHeadingTags } from "react-table-of-content";

const html = `
  <h1>Heading 1</h1>
  <p>Content 1</p>
  <h2>Heading 2</h2>
  <p>Content 2</p>
`;

const newHtml = addIdToHeadingTags(html);

console.log(newHtml);

// Output:
// <h1 id="heading-1">Heading 1</h1>
// <p>Content 1</p>
// <h2 id="heading-2">Heading 2</h2>
// <p>Content 2</p>

dashCase

It's a function that converts a string to dash case.

Arguments

Accepts 1 argument of type string to be transformed to a dash cased format.

Return value

A string in dash case.

Usage and example
import { dashCase } from "react-table-of-content";

const str = "This is a string";

const newStr = dashCase(str);

console.log(newStr);

// Output:
// this-is-a-string

makeLinksExternal

It's a function that converts all links in a string of HTML to external links.

Arguments

Accepts 1 argument of type string containing the HTML to convert links to external links.

Return value

A string of HTML with links converted to external links.

Usage and example
import { makeLinksExternal } from "react-table-of-content";

const html = `
  <a href="/about">About</a>
  <a href="/contact">Contact</a>
`;

const newHtml = makeLinksExternal(html);

console.log(newHtml);

// Output:
// <a href="/about" target="_blank" rel="noopener noreferrer">About</a>
// <a href="/contact" target="_blank" rel="noopener noreferrer">Contact</a>

readTime

It's a function that calculates the read time of a string of text.

Arguments

Accepts 1 argument of type string containing the text to calculate the read time.

Return value

An object with minutes property of type number in minutes and a readTime property describing the number in words e.g. "1 min read".

Usage and example
import { readTime } from "react-table-of-content";

const text =
  "This is a string of text that will be used to calculate the read time.";

const time = readTime(text);

console.log(time);

// Output:

// { minutes: 1, readTime: "1 min read" }

Testing

Unit and integration tests are written using Jest and React Testing Library. To run the tests, use the command below:

npm run test

You can mock the package in your tests like so:

jest.mock("react-table-of-content", () => ({
  addIdToHeadingTags: jest.fn((content) => content),
  useTableOfContent: jest.fn(() => ({
    headingLinks: [
      {
        id: "id",
        title: "title",
        tagName: "h1",
      },
    ],
    contentIsActive: jest.fn(() => true),
  })),
  makeLinksExternal: jest.fn((content) => content),
}));

License

MIT

Author

Precious OSSAI

Developer Website