Skip to content

Commit 0523f20

Browse files
authored
Merge pull request #1 from ameshkin/dev
New workflow and updates to readme fie.
2 parents b922893 + 9dca424 commit 0523f20

4 files changed

Lines changed: 5510 additions & 1396 deletions

File tree

README.md

Lines changed: 301 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
_# Next Crumb
1+
# Next Crumb
22

33
> MUI 7 Breadcrumbs with **Next.js Link** support, optional **URL-based generation**, and **SEO microdata**. Router-agnostic, zero runtime deps.
44
5+
56
| Item | Value | Badge |
67
|-----------------|-------------------------------------------------------------------------------------------|-------|
78
| **GitHub repo** | [github.com/ameshkin/nextcrumbs](https://github.com/ameshkin/nextcrumbs) ||
@@ -26,9 +27,9 @@ _# Next Crumb
2627
- [Auto from URL (Next App Router)](#auto-from-url-next-app-router)
2728
- [Props](#props)
2829
- [Icon & Separator Examples](#icon--separator-examples)
29-
- [Automated Testing](#automated-testing)
3030
- [Accessibility & SEO](#accessibility--seo)
3131
- [Dependency Checks](#dependency-checks)
32+
- [FAQ](#faq)
3233
- [Contributing](#contributing)
3334
- [License](#license)
3435

@@ -48,7 +49,7 @@ _# Next Crumb
4849

4950
- React **18+**
5051
- @mui/material **7+**
51-
- (Optional) @mui/icons-material **7+**
52+
- (Optional) @mui/icons-material **7+** for built-in icons
5253
- Next.js **13.4+** (if using Next + App Router)
5354

5455
---
@@ -59,6 +60,303 @@ _# Next Crumb
5960
npm i @ameshkin/nextcrumbs
6061
# peer deps
6162
npm i @mui/material @mui/icons-material react react-dom
63+
````
64+
65+
> Using pnpm or yarn? Replace `npm i` with your package manager’s command.
66+
67+
---
68+
69+
## Quick Start (Next.js)
70+
71+
Manual items with `next/link`:
72+
73+
```tsx
74+
"use client";
75+
import Link from "next/link";
76+
import { Breadcrumbs } from "@ameshkin/nextcrumbs";
77+
78+
export default function HeaderTrail() {
79+
return (
80+
<Breadcrumbs
81+
LinkComponent={Link}
82+
items={[
83+
{ label: "Dashboard", href: "/" },
84+
{ label: "Products", href: "/products" },
85+
{ label: "New" }
86+
]}
87+
/>
88+
);
89+
}
6290
```
6391
92+
---
93+
94+
## Auto from URL (Next App Router)
95+
96+
Generate items from the current pathname:
97+
98+
```tsx
99+
"use client";
100+
import Link from "next/link";
101+
import { usePathname } from "next/navigation";
102+
import { Breadcrumbs, usePathBreadcrumbs } from "@ameshkin/nextcrumbs";
103+
104+
export default function AutoTrail() {
105+
const pathname = usePathname();
106+
const items = usePathBreadcrumbs(pathname, {
107+
baseHref: "/",
108+
labelMap: { new: "Create" },
109+
exclude: ["_private"]
110+
});
111+
112+
return <Breadcrumbs LinkComponent={Link} items={items} />;
113+
}
114+
```
115+
116+
---
117+
118+
## Props
119+
120+
### `<Breadcrumbs />`
121+
122+
| Prop | Type | Default | Description |
123+
| --------------- | ------------------------------------------------------ | -------------------- | ---------------------------------------------------------------- |
124+
| `items` | `{ label: string; href?: string; icon?: ReactNode }[]` | **required** | The breadcrumb trail; last item usually has no `href`. |
125+
| `LinkComponent` | `ElementType` | `@mui/material/Link` | Custom link component (e.g., Next.js `Link`). |
126+
| `muiProps` | `Omit<MUIBreadcrumbsProps, "children">` || Pass-through props to MUI `<Breadcrumbs />`. |
127+
| `separatorIcon` | `ReactNode` | `ChevronRightIcon` | Icon/node placed between items. |
128+
| `homeLabel` | `string` | `"Dashboard"` | If an item’s label matches this, it gets a Home icon by default. |
129+
| `withSchema` | `boolean` | `true` | Adds `schema.org/BreadcrumbList` microdata. |
130+
131+
### `usePathBreadcrumbs(pathname, options?)`
132+
133+
| Option | Type | Default | Description |
134+
| ---------- | ----------------------- | ------- | ---------------------------------- |
135+
| `baseHref` | `string` | `"/"` | Root href for the first crumb. |
136+
| `labelMap` | `Record<string,string>` | `{}` | Override labels by segment. |
137+
| `exclude` | `string[]` | `[]` | Skip specific path segments. |
138+
| `decode` | `boolean` | `true` | `decodeURIComponent` each segment. |
139+
140+
---
141+
142+
## Icon & Separator Examples
143+
144+
Change the separator and home icon behavior:
145+
146+
```tsx
147+
import Link from "next/link";
148+
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
149+
import NavigateNextIcon from "@mui/icons-material/NavigateNext";
150+
import HomeOutlinedIcon from "@mui/icons-material/HomeOutlined";
151+
import { Breadcrumbs } from "@ameshkin/nextcrumbs";
152+
153+
<Breadcrumbs
154+
LinkComponent={Link}
155+
separatorIcon={<NavigateNextIcon fontSize="small" />}
156+
items={[
157+
{ label: "Dashboard", href: "/", icon: <HomeOutlinedIcon /> },
158+
{ label: "Products", href: "/products" },
159+
{ label: "Gadgets" }
160+
]}
161+
/>;
162+
```
163+
164+
* Any MUI icon works for `separatorIcon` or per-item `icon`.
165+
* If you set `homeLabel="Home"`, the component shows a Home icon automatically when a crumb’s label is `"Home"`.
166+
167+
---
168+
169+
## Minimal AutoTrail Example
170+
171+
> Simple usage with default behavior (no `labelMap`, `exclude`, or `baseHref`)
172+
173+
```tsx
174+
"use client";
175+
import { usePathname } from "next/navigation";
176+
import { Breadcrumbs, usePathBreadcrumbs } from "@ameshkin/nextcrumbs";
177+
import Link from "next/link";
178+
179+
export default function AutoTrail() {
180+
const pathname = usePathname();
181+
const items = usePathBreadcrumbs(pathname);
182+
183+
return <Breadcrumbs LinkComponent={Link} items={items} />;
184+
}
185+
```
186+
187+
✅ Great for dashboards or quick scaffolded layouts. Automatically capitalizes, cleans up slugs, and converts URL segments into breadcrumb labels.
188+
189+
190+
---
191+
192+
## Accessibility & SEO
193+
194+
* Uses MUI’s accessible `<Breadcrumbs aria-label="breadcrumbs">`.
195+
* Adds **schema.org** `BreadcrumbList` microdata by default (set `withSchema={false}` to disable).
196+
* Minimal DOM/no extra wrappers (uses `display: contents` for the list wrapper).
197+
198+
---
199+
200+
## Dependency Checks
201+
202+
Make sure peer deps resolve cleanly:
203+
204+
```bash
205+
# in your app
206+
npm ls @mui/material @mui/icons-material react react-dom
207+
```
208+
209+
Optional size/security checks:
210+
211+
* Size: [https://bundlephobia.com/package/@ameshkin/nextcrumbs](https://bundlephobia.com/package/@ameshkin/nextcrumbs)
212+
* Vulnerabilities (example): `npx snyk test` or GitHub Advanced Security (CodeQL)
213+
214+
---
215+
216+
## FAQ
217+
218+
**Does it work with React Router?**
219+
Yes—pass your router’s `<Link>` as `LinkComponent`. The UI is MUI; navigation is your router.
220+
221+
**How do I change the last crumb’s style?**
222+
The last item usually has no `href`. You can add conditional styles by checking `href` presence in your `items` or wrap this component with your own style logic.
223+
224+
**Can I disable icons entirely?**
225+
Yes—don’t pass `icon` and set `homeLabel` to a non-matching value.
226+
227+
---
228+
229+
## Accessibility & SEO
230+
231+
This component previously supported `schema.org` microdata via a `withSchema` and `withJsonLd` prop. These have been **removed** for the following reasons:
232+
233+
- ⚠️ **Google no longer consistently indexes client-side microdata** (e.g. via React or `dangerouslySetInnerHTML`) unless it's rendered server-side.
234+
- 🧼 Breadcrumb JSON-LD tags added via `<script>` often **conflict with route-based apps** (e.g. dynamic segments in Next.js).
235+
- 🔍 Most modern SEO guidance encourages **server-side or static rendering** of JSON-LD, not runtime injection.
236+
- 🔧 Removing these props results in **simpler DOM output**, cleaner markup, and improved developer control.
237+
238+
**If you need structured breadcrumbs for SEO**, we recommend injecting them at the page level with your own layout component, using `<script type="application/ld+json">` manually for static pages only.
239+
240+
This component still provides:
241+
242+
- Accessible markup (`aria-label`, correct role semantics)
243+
- Customizable separator, label, and icon logic
244+
- Dynamic routing support via the `items` prop or `usePathBreadcrumbs` helper
245+
246+
247+
## Contributing
248+
249+
* CI badge above expects a workflow at `.github/workflows/ci.yml`.
250+
* Please run `npm run build` before sending a PR.
251+
* Keep peer ranges broad enough for MUI 7 / React 18–19.
252+
253+
---
254+
255+
## License
256+
257+
[MIT](LICENSE)
258+
259+
260+
Here are the two new README sections you asked for, ready to paste.
261+
262+
---
263+
264+
## GitHub Action Pipeline
265+
266+
This project ships with two CI workflows—one for `main` and one for `dev`—that run a strict, fail-fast build and test matrix.
267+
268+
**What the pipeline does**
269+
270+
1. **Checkout & Node setup (Node 20)**
271+
2. **Install** with `npm ci` (immutable lockfile)
272+
3. **Typecheck** with `tsc --noEmit`
273+
4. **Build** with `tsup` (ESM + CJS + d.ts)
274+
5. **Test** with Vitest (jsdom + RTL)
275+
276+
**Why it’s strict**
277+
278+
* **Fail-fast**: any step error stops the job immediately
279+
* **Immutable installs**: reproducible builds with `npm ci`
280+
* **Matrix (optional)**: enable Node 18/20 if you need multi-runtime parity
281+
282+
**Status badges**
283+
284+
| Branch | CI |
285+
| ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
286+
| `main` | [![CI — main](https://img.shields.io/github/actions/workflow/status/ameshkin/nextcrumbs/ci.yml?branch=main\&label=ci%20\(main\))](https://github.com/ameshkin/nextcrumbs/actions) |
287+
| `dev` | [![CI — dev](https://img.shields.io/github/actions/workflow/status/ameshkin/nextcrumbs/ci.yml?branch=dev\&label=ci%20\(dev\))](https://github.com/ameshkin/nextcrumbs/actions) |
288+
289+
**Run the same checks locally**
290+
291+
```bash
292+
npm ci
293+
npm run typecheck
294+
npm run build
295+
npm test -- --run
296+
```
297+
298+
> Tip: Keep your PRs green by running these before pushing.
299+
300+
---
301+
302+
## Automated Tests
303+
304+
The test stack is **Vitest + jsdom + React Testing Library** with a lightweight **Next.js Link mock** so components render without a Next runtime.
305+
306+
**Key pieces**
307+
308+
* **Vitest globals & jsdom** are enabled via `vitest.config.ts` and a `vitest.setup.ts` that loads Jest-DOM matchers.
309+
* **Next Link mock**: tests import a tiny `<Link>` that renders to an anchor. This allows `<Breadcrumbs>` to use a `LinkComponent` without pulling in Next.
310+
* **Component under test**: the MUI-based `<Breadcrumbs>` renders items, home icon behavior, separators, and current page semantics.
311+
312+
**Typical test patterns**
313+
314+
* **Hook tests** (`usePathBreadcrumbs`) validate URL→crumb generation, label mapping, exclusions, and last-item semantics.
315+
* **Component tests** (`Breadcrumbs`) assert text rendering, link vs. current item behavior, and accessibility attributes.
316+
317+
**Writing a component test**
318+
319+
```tsx
320+
// src/Breadcrumbs.test.tsx
321+
import { render, screen } from "@testing-library/react";
322+
import Breadcrumbs from "./Breadcrumbs";
323+
import Link from "./nextLinkMock"; // mock replaces next/link
324+
325+
describe("Breadcrumbs", () => {
326+
it("renders all breadcrumb items", () => {
327+
render(
328+
<Breadcrumbs
329+
LinkComponent={Link}
330+
items={[
331+
{ label: "Home", href: "/" },
332+
{ label: "Products", href: "/products" },
333+
{ label: "New" },
334+
]}
335+
/>
336+
);
337+
expect(screen.getByText("Home")).toBeInTheDocument();
338+
expect(screen.getByText("Products")).toBeInTheDocument();
339+
expect(screen.getByText("New")).toBeInTheDocument();
340+
});
341+
});
342+
```
343+
344+
**Running tests**
345+
346+
```bash
347+
# one-shot
348+
npm test -- --run
349+
350+
# watch mode
351+
npm run test
352+
```
353+
354+
355+
# TODO!!
356+
357+
### React Router Automated Crumbs
358+
359+
360+
### Github Action Pipeline
64361
362+
### More Automated Tests

0 commit comments

Comments
 (0)