diff --git a/src/components/charts/ChartsCatalogPages.tsx b/src/components/charts/ChartsCatalogPages.tsx index e60b29150..60e3b8621 100644 --- a/src/components/charts/ChartsCatalogPages.tsx +++ b/src/components/charts/ChartsCatalogPages.tsx @@ -1,5 +1,8 @@ import { Breadcrumbs } from '~/components/Breadcrumbs' -import { getChartBreadcrumbItems } from '~/utils/chart-breadcrumbs' +import { + getChartBreadcrumbItems, + getChartCollectionBreadcrumbItems, +} from '~/utils/chart-breadcrumbs' import { ArrowsOutSimpleIcon, ArrowUpRightIcon, @@ -65,14 +68,10 @@ export function ChartsCatalog({ {collection ? (
- - All examples - +

{collection.title}

diff --git a/src/routes/_library/charts.catalog.collections.$collectionId.tsx b/src/routes/_library/charts.catalog.collections.$collectionId.tsx index 39b423dbd..0931b3991 100644 --- a/src/routes/_library/charts.catalog.collections.$collectionId.tsx +++ b/src/routes/_library/charts.catalog.collections.$collectionId.tsx @@ -1,3 +1,4 @@ +import { getChartCollectionBreadcrumbJsonLd } from '~/utils/chart-breadcrumbs' import { createFileRoute, notFound } from '@tanstack/react-router' import { ChartsCatalog } from '~/components/charts/ChartsCatalogPages' import { isChartsCatalogCaseId } from '~/utils/charts-catalog' @@ -17,6 +18,16 @@ export const Route = createFileRoute( }, component: ChartsCatalogCollectionRoute, head: ({ loaderData }) => ({ + scripts: loaderData + ? [ + { + type: 'application/ld+json', + children: JSON.stringify( + getChartCollectionBreadcrumbJsonLd(loaderData.collection), + ).replaceAll('<', '\\u003c'), + }, + ] + : [], meta: seo({ title: loaderData ? `${loaderData.collection.title} | TanStack Charts` diff --git a/src/utils/chart-breadcrumbs.ts b/src/utils/chart-breadcrumbs.ts index c978a2b5a..f225e4150 100644 --- a/src/utils/chart-breadcrumbs.ts +++ b/src/utils/chart-breadcrumbs.ts @@ -37,3 +37,34 @@ export function getChartBreadcrumbJsonLd(chart: ChartBreadcrumbData) { })), } } + +export function getChartCollectionBreadcrumbItems(collection: { + id: string + title: string +}): Array { + return [ + { label: 'Catalog', to: '/charts/catalog', search: true, preload: false }, + { + label: collection.title, + to: `/charts/catalog/collections/${collection.id}`, + }, + ] +} + +export function getChartCollectionBreadcrumbJsonLd(collection: { + id: string + title: string +}) { + return { + '@context': 'https://schema.org', + '@type': 'BreadcrumbList', + itemListElement: getChartCollectionBreadcrumbItems(collection).map( + (item, index) => ({ + '@type': 'ListItem', + position: index + 1, + name: item.label, + item: canonicalUrl(item.to), + }), + ), + } +} diff --git a/tests/chart-breadcrumbs.test.ts b/tests/chart-breadcrumbs.test.ts index c339a4117..5f7ed7025 100644 --- a/tests/chart-breadcrumbs.test.ts +++ b/tests/chart-breadcrumbs.test.ts @@ -2,6 +2,8 @@ import assert from 'node:assert/strict' import { test } from 'node:test' import { getChartBreadcrumbItems, + getChartCollectionBreadcrumbItems, + getChartCollectionBreadcrumbJsonLd, getChartBreadcrumbJsonLd, } from '../src/utils/chart-breadcrumbs' Object.assign(globalThis, { __TANSTACK_SITE_URL__: 'https://tanstack.com' }) @@ -32,3 +34,29 @@ test('chart hierarchy includes only known collections and keeps navigation optio assert.equal(getChartBreadcrumbItems({ ...chart, collection }).length, 2) } }) + +test('collection trail returns to the complete catalog while schema stays canonical', () => { + const collection = { id: 'shadcn', title: 'shadcn/ui Charts' } + const items = getChartCollectionBreadcrumbItems(collection) + assert.equal(items[0].to, '/charts/catalog') + assert.equal(items[0].search, true) + assert.equal(items[0].preload, false) + assert.equal(items[1].label, collection.title) + assert.deepEqual( + getChartCollectionBreadcrumbJsonLd(collection).itemListElement, + [ + { + '@type': 'ListItem', + position: 1, + name: 'Catalog', + item: 'https://tanstack.com/charts/catalog', + }, + { + '@type': 'ListItem', + position: 2, + name: 'shadcn/ui Charts', + item: 'https://tanstack.com/charts/catalog/collections/shadcn', + }, + ], + ) +})