diff --git a/README.md b/README.md index a54ee99..d0397e3 100644 --- a/README.md +++ b/README.md @@ -250,6 +250,10 @@ function Example2() { } ``` +### `` component + +TODO document advanced use case + ### `useLevel` hook Returns an object containing the current `level` and current `Component`. diff --git a/src/index.test.tsx b/src/index.test.tsx index 8b860b4..db7b26d 100644 --- a/src/index.test.tsx +++ b/src/index.test.tsx @@ -2,7 +2,7 @@ import React from "react"; import { render, cleanup } from "@testing-library/react"; import { describe, it, expect, afterEach } from "vitest"; -import { H, Section, useLevel } from "./index"; +import { H, Section, useLevel, LevelProvider } from "./index"; afterEach(() => { cleanup(); @@ -121,6 +121,78 @@ describe("H component", () => { }); }); +describe("LevelProvider component", () => { + it("should render a heading and its content", () => { + const { getByText } = render( +
My H1}> +
My H2}> +
My H3}> +
My H4}> + +
My new H2}> +

My content

+
+
+
+
+
+
+ ); + + const headingEl = getByText("My new H2"); + + expect(headingEl.tagName).toBe("H2"); + }); + + it("should default to level 1", () => { + const { getByText } = render( +
My H1}> +
My H2}> +
My H3}> + +
My new H1}> +

My content

+
+
+
+
+
+ ); + + const headingEl = getByText("My new H1"); + + expect(headingEl.tagName).toBe("H1"); + }); + + it("should start a component tree to level 3", () => { + const { getByText } = render( + +
My H3}> +

My content

+
+
+ ); + + const headingEl = getByText("My H3"); + + expect(headingEl.tagName).toBe("H3"); + }); + + it("should start a component tree to level 1 by default", () => { + const { getByText } = render( + +
My H1}> +

My content

+
+
+ ); + + const headingEl = getByText("My H1"); + + expect(headingEl.tagName).toBe("H1"); + }); +}); + describe("Section component", () => { it("should be level 1 in first section", () => { const { getByText } = render(
My H1}>
); diff --git a/src/index.tsx b/src/index.tsx index 4e4d5ef..18d306d 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -25,7 +25,10 @@ type HProps = React.DetailedHTMLProps< render?: (context: LevelContextValue) => React.ReactElement; }; -function InternalH( +/** + * Renders a dynamic HTML heading (h1, h2, etc.) or custom component according to the current level. + */ +export const H = React.forwardRef(function H( { render, ...props }: HProps, forwardedRef: React.ForwardedRef ): JSX.Element { @@ -36,12 +39,29 @@ function InternalH( } return ; -} +}); + +type LevelProviderProps = { + level?: Level; + children?: React.ReactNode; +}; /** - * Renders a dynamic HTML heading (h1, h2, etc.) or custom component according to the current level. + * TODO documentation */ -export const H = React.forwardRef(InternalH); +export function LevelProvider({ + level = 1, + children, +}: LevelProviderProps): JSX.Element { + const value = { + level, + Component: `h${level}` as Heading, + }; + + return ( + {children} + ); +} type SectionProps = { component: React.ReactNode; @@ -58,15 +78,10 @@ export function Section({ component, children }: SectionProps): JSX.Element { const nextLevel = Math.min(level + 1, 6) as Level; - const value = { - level: nextLevel, - Component: `h${nextLevel}` as Heading, - }; - return ( <> {component} - {children} + {children} ); }