From b9cebecea6117fc4c438832e97506054a1e7d140 Mon Sep 17 00:00:00 2001 From: Levi Mykel Gable <9385378+levimykel@users.noreply.github.com> Date: Mon, 17 Feb 2025 15:14:18 +0100 Subject: [PATCH 1/5] chore: update prismic deps --- e2e-projects/nextjs/package.json | 4 ++-- package-lock.json | 20 +++++++++++--------- package.json | 2 +- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/e2e-projects/nextjs/package.json b/e2e-projects/nextjs/package.json index 5880374..33f0e5d 100644 --- a/e2e-projects/nextjs/package.json +++ b/e2e-projects/nextjs/package.json @@ -7,14 +7,14 @@ "start": "next start --port=4321" }, "dependencies": { - "@prismicio/client": "^7.13.1", + "@prismicio/client": "^7.16.0", "@prismicio/react": "*", "next": "15.1.2", "react": "19.0.0", "react-dom": "19.0.0" }, "devDependencies": { - "@prismicio/types-internal": "^3.3.0", + "@prismicio/types-internal": "^3.6.0", "@types/node": "^22", "@types/react": "^19", "@types/react-dom": "^19", diff --git a/package-lock.json b/package-lock.json index d7f39ec..9e0eb47 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "devDependencies": { "@eslint/js": "^9.19.0", "@playwright/test": "^1.50.0", - "@prismicio/client": "^7.12.0", + "@prismicio/client": "^7.16.0", "@rollup/plugin-typescript": "^12.1.2", "@size-limit/preset-small-lib": "^11.1.6", "@types/react": "^19.0.8", @@ -54,14 +54,14 @@ }, "e2e-projects/nextjs": { "dependencies": { - "@prismicio/client": "^7.13.1", + "@prismicio/client": "^7.16.0", "@prismicio/react": "*", "next": "15.1.2", "react": "19.0.0", "react-dom": "19.0.0" }, "devDependencies": { - "@prismicio/types-internal": "^3.3.0", + "@prismicio/types-internal": "^3.6.0", "@types/node": "^22", "@types/react": "^19", "@types/react-dom": "^19", @@ -1563,9 +1563,10 @@ } }, "node_modules/@prismicio/client": { - "version": "7.15.1", - "resolved": "https://registry.npmjs.org/@prismicio/client/-/client-7.15.1.tgz", - "integrity": "sha512-GjOMRxoXm4zux9NolcCQJITwhj5nsWZz+bewO7zA2g1wdAM8rgAPlr0T0+bC/CcXnhznBF/gdfpGOXiHsjJ1WQ==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@prismicio/client/-/client-7.16.0.tgz", + "integrity": "sha512-oKBkkOuRYujdiUgn5fs8qAPZJzJ44FYMFwFCY1iw/ODQ1UhOFh6uUJzgcKe4TPlw1mhRvsNOlJlmW2NTZmrFFg==", + "license": "Apache-2.0", "dependencies": { "imgix-url-builder": "^0.0.5" }, @@ -1578,10 +1579,11 @@ "link": true }, "node_modules/@prismicio/types-internal": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@prismicio/types-internal/-/types-internal-3.3.0.tgz", - "integrity": "sha512-5Vg0oM6FlFa5lgF7RirGf55/SZ/TJ/26tNNUDRiwZSbs8VZUMbIO21IkjTuc6lFn+hKWKCXRxB6+lgmdvaZ2QQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@prismicio/types-internal/-/types-internal-3.6.0.tgz", + "integrity": "sha512-7JzWi/MHDEU6u/mOCUu32jHYGU/lZ+Ss47AkJXc/BMde/0tv8FQw2XXtiavSt5A5ikiakCVxeo3na9xkr3KMqg==", "dev": true, + "license": "Apache-2.0", "dependencies": { "monocle-ts": "^2.3.11", "newtype-ts": "^0.3.5", diff --git a/package.json b/package.json index 0a07764..21753c8 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "devDependencies": { "@eslint/js": "^9.19.0", "@playwright/test": "^1.50.0", - "@prismicio/client": "^7.12.0", + "@prismicio/client": "^7.16.0", "@rollup/plugin-typescript": "^12.1.2", "@size-limit/preset-small-lib": "^11.1.6", "@types/react": "^19.0.8", From c9c0d97e5cda2cc4769715440051660def7091bb Mon Sep 17 00:00:00 2001 From: Levi Mykel Gable <9385378+levimykel@users.noreply.github.com> Date: Mon, 17 Feb 2025 15:16:36 +0100 Subject: [PATCH 2/5] feat: support PrismicTable --- src/PrismicTable.tsx | 151 +++++++++++++++++++++++++++++++++++++++++++ src/index.ts | 3 + 2 files changed, 154 insertions(+) create mode 100644 src/PrismicTable.tsx diff --git a/src/PrismicTable.tsx b/src/PrismicTable.tsx new file mode 100644 index 0000000..43c38ec --- /dev/null +++ b/src/PrismicTable.tsx @@ -0,0 +1,151 @@ +import { JSX, ReactNode } from "react"; +import { isFilled, RichTextField, TableField } from "@prismicio/client"; +import { RichTextMapSerializer } from "@prismicio/client/richtext"; +import { PrismicRichText } from "./PrismicRichText.js"; + +type TableSerializerFunction = (args: { + children: ReactNode; + key?: string; +}) => JSX.Element; +type CellContentFunction = (args: { + children: RichTextField | null; +}) => JSX.Element; + +const tableSerializerOptions = [ + "table", + "head", + "body", + "row", + "header", + "data", + "cellContent", +] as const; + +type TableMapSerializerKey = (typeof tableSerializerOptions)[number]; + +type TableMapSerializerFilled = { + [K in Exclude]: TableSerializerFunction; +} & { + cellContent: CellContentFunction; +}; + +type TableMapSerializer = Partial & + RichTextMapSerializer; + +const createDefaultTableSerializer = ( + components?: TableMapSerializer, +): TableMapSerializerFilled => { + // filter out table serializer options to get just the table cell (rich text) components + const tableCellComponents = components + ? Object.fromEntries( + Object.entries(components).filter(([key]) => { + return !tableSerializerOptions.includes(key as TableMapSerializerKey); + }), + ) + : {}; + + // filter out the table cell (rich text) options to get just the table options + const tableComponents = components + ? Object.fromEntries( + Object.entries(components).filter(([key]) => { + return tableSerializerOptions.includes(key as TableMapSerializerKey); + }), + ) + : null; + + return { + table: ({ children }) => {children}
, + head: ({ children }) => {children}, + body: ({ children }) => {children}, + row: ({ children, key }) => {children}, + header: ({ children, key }) => {children}, + data: ({ children, key }) => {children}, + cellContent: ({ children }) => ( + + ), + ...tableComponents, + }; +}; + +export type PrismicTableProps = { + field: TableField; + components?: TableMapSerializer; + fallback?: JSX.Element; +}; + +export function PrismicTable({ + field, + components, + fallback, +}: PrismicTableProps) { + const serializer = createDefaultTableSerializer(components); + + if (isFilled.table(field)) { + return serializer.table({ + children: ( + <> + + + + ), + }); + } else { + return fallback ?? null; + } +} + +type TableHeadProps = { + field: TableField; + serializer: TableMapSerializerFilled; +}; + +function TableHead({ field, serializer }: TableHeadProps) { + return field && field.head + ? serializer.head({ + children: , + }) + : null; +} + +type TableBodyProps = { + field: TableField; + serializer: TableMapSerializerFilled; +}; + +function TableBody({ field, serializer }: TableBodyProps) { + return field && field.body + ? serializer.body({ + children: , + }) + : null; +} + +type TableRows = TableField<"filled">["body"]["rows"]; + +type TableRowsProps = { + rows: TableRows; + serializer: TableMapSerializerFilled; +}; + +function TableRows({ rows, serializer }: TableRowsProps) { + return rows.map((row) => { + return serializer.row({ + children: , + key: crypto.randomUUID(), + }); + }); +} + +type TableCellsProps = { + cells: TableRows[0]["cells"]; + serializer: TableMapSerializerFilled; +}; + +function TableCells({ cells, serializer }: TableCellsProps) { + return cells.map((cell) => { + return serializer[cell.type]({ + children: serializer.cellContent({ children: cell.content }), + key: crypto.randomUUID(), + }); + }); +} diff --git a/src/index.ts b/src/index.ts index 4c7017a..de69e68 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,9 @@ export { PrismicLink } from "./PrismicLink.js"; export type { PrismicLinkProps, LinkProps } from "./PrismicLink.js"; +export { PrismicTable } from "./PrismicTable.js"; +export type { PrismicTableProps } from "./PrismicTable.js"; + export { PrismicText } from "./PrismicText.js"; export type { PrismicTextProps } from "./PrismicText.js"; From 7ad5a5508aa518c8d380ab2a85eaf9fb519400e1 Mon Sep 17 00:00:00 2001 From: Levi Mykel Gable <9385378+levimykel@users.noreply.github.com> Date: Mon, 17 Feb 2025 15:17:17 +0100 Subject: [PATCH 3/5] test: add tests for PrismicTable --- e2e-projects/nextjs/app/PrismicTable/page.tsx | 73 ++++++++++ e2e-projects/nextjs/prismic-types.d.ts | 44 +++++- tests/PrismicTable.spec.ts | 36 +++++ tests/infra/content/index.ts | 1 + tests/infra/content/table.ts | 135 ++++++++++++++++++ tests/infra/setup.ts | 10 ++ 6 files changed, 298 insertions(+), 1 deletion(-) create mode 100644 e2e-projects/nextjs/app/PrismicTable/page.tsx create mode 100644 tests/PrismicTable.spec.ts create mode 100644 tests/infra/content/table.ts diff --git a/e2e-projects/nextjs/app/PrismicTable/page.tsx b/e2e-projects/nextjs/app/PrismicTable/page.tsx new file mode 100644 index 0000000..76e70da --- /dev/null +++ b/e2e-projects/nextjs/app/PrismicTable/page.tsx @@ -0,0 +1,73 @@ +import { isFilled } from "@prismicio/client"; +import { PrismicTable } from "@prismicio/react"; +import assert from "assert"; + +import { createClient } from "@/prismicio"; + +export default async function Page() { + const client = await createClient(); + const { data: tests } = await client.getSingle("table_test"); + + assert(isFilled.table(tests.filled)); + assert(!isFilled.table(tests.empty)); + + return ( + <> +
+ +
+ +
+ +
+ +
+ Table
} /> + + +
+
{children}
, + head: ({ children }) =>
{children}
, + body: ({ children }) =>
{children}
, + row: ({ children, key }) => ( +
+ {children} +
+ ), + header: ({ children, key }) => ( +
+ {children} +
+ ), + data: ({ children, key }) => ( +
+ {children} +
+ ), + }} + /> +
+ +
+ ( + {children}
+ ), + paragraph: ({ children }) => ( +

{children}

+ ), + strong: ({ children }) => ( + {children} + ), + em: ({ children }) => {children}, + }} + /> +
+ + ); +} diff --git a/e2e-projects/nextjs/prismic-types.d.ts b/e2e-projects/nextjs/prismic-types.d.ts index e9f5d3b..e3f73c7 100644 --- a/e2e-projects/nextjs/prismic-types.d.ts +++ b/e2e-projects/nextjs/prismic-types.d.ts @@ -485,11 +485,53 @@ export type RichTextTestDocument = Lang >; +/** Content for Table Test documents */ +interface TableTestDocumentData { + /** + * Empty field in _Table Test_ + * + * - **Field Type**: Table + * - **Placeholder**: _None_ + * - **API ID Path**: table_test.empty + * - **Tab**: Main + * - **Documentation**: https://prismic.io/docs/field#table + */ + empty: prismic.TableField; + + /** + * Filled field in _Table Test_ + * + * - **Field Type**: Table + * - **Placeholder**: _None_ + * - **API ID Path**: table_test.filled + * - **Tab**: Main + * - **Documentation**: https://prismic.io/docs/field#table + */ + filled: prismic.TableField; +} + +/** + * Table Test document from Prismic + * + * - **API ID**: `table_test` + * - **Repeatable**: `false` + * - **Documentation**: https://prismic.io/docs/custom-types + * + * @typeParam Lang - Language API ID of the document. + */ +export type TableTestDocument = + prismic.PrismicDocumentWithoutUID< + Simplify, + "table_test", + Lang + >; + export type AllDocumentTypes = | ImageTestDocument | LinkTestDocument | PageDocument - | RichTextTestDocument; + | RichTextTestDocument + | TableTestDocument; /** Primary content in _Image → Default → Primary_ */ export interface ImageSliceDefaultPrimary { diff --git a/tests/PrismicTable.spec.ts b/tests/PrismicTable.spec.ts new file mode 100644 index 0000000..91b6ae7 --- /dev/null +++ b/tests/PrismicTable.spec.ts @@ -0,0 +1,36 @@ +import { test, expect } from "./infra"; + +test.beforeEach(async ({ page }) => { + await page.goto("/PrismicTable"); +}); + +test("renders filled table elements", async ({ page }) => { + const output = page.getByTestId("filled"); + expect(await output.innerHTML()).toBe( + "

Method

Usage

GET

For basic retrieval of information…

DELETE

To destroy a resource and remove…

", + ); +}); + +test("renders null when passed an empty field", async ({ page }) => { + const output = page.getByTestId("empty"); + await expect(output).toBeEmpty(); +}); + +test("renders fallback when passed an empty field", async ({ page }) => { + const output = page.getByTestId("fallback"); + expect(await output.innerHTML()).toBe("
Table
"); +}); + +test("renders custom table elements", async ({ page }) => { + const output = page.getByTestId("custom-table"); + expect(await output.innerHTML()).toBe( + '

Method

Usage

GET

For basic retrieval of information…

DELETE

To destroy a resource and remove…

', + ); +}); + +test("renders custom table cell content", async ({ page }) => { + const output = page.getByTestId("custom-cell-content"); + expect(await output.innerHTML()).toBe( + '

Method

Usage

GET

For basic retrieval of information…

DELETE

To destroy a resource and remove…

', + ); +}); diff --git a/tests/infra/content/index.ts b/tests/infra/content/index.ts index b1fdeea..9807e24 100644 --- a/tests/infra/content/index.ts +++ b/tests/infra/content/index.ts @@ -2,3 +2,4 @@ export * as image from "./image"; export * as link from "./link"; export * as page from "./page"; export * as richText from "./richtext"; +export * as table from "./table"; diff --git a/tests/infra/content/table.ts b/tests/infra/content/table.ts new file mode 100644 index 0000000..8b18fd0 --- /dev/null +++ b/tests/infra/content/table.ts @@ -0,0 +1,135 @@ +export const model = { + format: "custom", + id: "table_test", + label: "Table Test", + repeatable: false, + status: true, + json: { + Main: { + empty: { + type: "Table", + config: { + label: "Empty", + }, + }, + filled: { + type: "Table", + config: { + label: "Filled", + }, + }, + }, + }, +} as const; + +export function content() { + return { + filled: { + content: [ + { + type: "tableRow", + content: [ + { + type: "tableHeader", + content: [ + { + content: { + text: "Method", + }, + type: "paragraph", + }, + ], + }, + { + type: "tableHeader", + content: [ + { + type: "paragraph", + attrs: { + dir: "ltr", + }, + content: { + text: "Usage", + }, + }, + ], + }, + ], + }, + { + type: "tableRow", + content: [ + { + type: "tableHeader", + content: [ + { + type: "paragraph", + attrs: { + dir: "ltr", + }, + content: { + text: "GET", + }, + }, + ], + }, + { + type: "tableCell", + content: [ + { + content: { + spans: [ + { + type: "strong", + end: 19, + start: 4, + }, + ], + text: "For basic retrieval of information…", + }, + type: "paragraph", + }, + ], + }, + ], + }, + { + type: "tableRow", + content: [ + { + type: "tableHeader", + content: [ + { + content: { + text: "DELETE", + }, + type: "paragraph", + }, + ], + }, + { + type: "tableCell", + content: [ + { + content: { + spans: [ + { + type: "em", + end: 7, + start: 3, + }, + ], + text: "To destroy a resource and remove…", + }, + type: "paragraph", + }, + ], + }, + ], + }, + ], + }, + empty_TYPE: "Table", + filled_TYPE: "Table", + }; +} diff --git a/tests/infra/setup.ts b/tests/infra/setup.ts index e09c373..8efe167 100644 --- a/tests/infra/setup.ts +++ b/tests/infra/setup.ts @@ -67,4 +67,14 @@ setup("create repo", async ({ page, prismic }) => { data: content.richText.content(), }); await repository.publishDocument(richTextDocument.id); + + await repository.addCustomType(content.table.model); + const tableDocument = await repository.createDocument({ + custom_type_id: content.table.model.id, + title: content.table.model.label, + tags: [], + integration_field_ids: [], + data: content.table.content(), + }); + await repository.publishDocument(tableDocument.id); }); From 88f60b380177d76b9eb79ff729003b15f75857e8 Mon Sep 17 00:00:00 2001 From: Levi Mykel Gable <9385378+levimykel@users.noreply.github.com> Date: Wed, 19 Feb 2025 12:51:04 +0100 Subject: [PATCH 4/5] refactor: updates from PR review --- e2e-projects/nextjs/app/PrismicTable/page.tsx | 28 +-- src/PrismicTable.tsx | 233 ++++++++---------- 2 files changed, 108 insertions(+), 153 deletions(-) diff --git a/e2e-projects/nextjs/app/PrismicTable/page.tsx b/e2e-projects/nextjs/app/PrismicTable/page.tsx index 76e70da..1d8aec2 100644 --- a/e2e-projects/nextjs/app/PrismicTable/page.tsx +++ b/e2e-projects/nextjs/app/PrismicTable/page.tsx @@ -28,25 +28,13 @@ export default async function Page() {
{children}
, - head: ({ children }) =>
{children}
, - body: ({ children }) =>
{children}
, - row: ({ children, key }) => ( -
- {children} -
- ), - header: ({ children, key }) => ( -
- {children} -
- ), - data: ({ children, key }) => ( -
- {children} -
- ), + thead: ({ children }) =>
{children}
, + tbody: ({ children }) =>
{children}
, + tr: ({ children }) =>
{children}
, + th: ({ children }) =>
{children}
, + td: ({ children }) =>
{children}
, }} />
@@ -54,10 +42,12 @@ export default async function Page() {
( {children}
), + }} + components={{ paragraph: ({ children }) => (

{children}

), diff --git a/src/PrismicTable.tsx b/src/PrismicTable.tsx index 43c38ec..7fd3baa 100644 --- a/src/PrismicTable.tsx +++ b/src/PrismicTable.tsx @@ -1,151 +1,116 @@ -import { JSX, ReactNode } from "react"; -import { isFilled, RichTextField, TableField } from "@prismicio/client"; -import { RichTextMapSerializer } from "@prismicio/client/richtext"; -import { PrismicRichText } from "./PrismicRichText.js"; - -type TableSerializerFunction = (args: { - children: ReactNode; - key?: string; -}) => JSX.Element; -type CellContentFunction = (args: { - children: RichTextField | null; -}) => JSX.Element; - -const tableSerializerOptions = [ - "table", - "head", - "body", - "row", - "header", - "data", - "cellContent", -] as const; - -type TableMapSerializerKey = (typeof tableSerializerOptions)[number]; - -type TableMapSerializerFilled = { - [K in Exclude]: TableSerializerFunction; -} & { - cellContent: CellContentFunction; +import { ReactNode } from "react"; +import { isFilled, TableField } from "@prismicio/client"; +import { JSXMapSerializer, PrismicRichText } from "./PrismicRichText.js"; + +type TableFieldHead = NonNullable["head"]>; +type TableFieldBody = TableField<"filled">["body"]; +type TableFieldRow = + | TableFieldHead["rows"][number] + | TableFieldBody["rows"][number]; +type TableFieldCell = TableFieldRow["cells"][number]; +type TableFieldHeaderCell = Extract; +type TableFieldDataCell = Extract; + +type TableComponents = { + table?: (props: { + table: TableField<"filled">; + children: ReactNode; + }) => ReactNode; + thead?: (props: { head: TableFieldHead; children: ReactNode }) => ReactNode; + tbody?: (props: { body: TableFieldBody; children: ReactNode }) => ReactNode; + tr?: (props: { row: TableFieldRow; children: ReactNode }) => ReactNode; + th?: (props: { + cell: TableFieldHeaderCell; + children: ReactNode; + }) => ReactNode; + td?: (props: { cell: TableFieldDataCell; children: ReactNode }) => ReactNode; }; -type TableMapSerializer = Partial & - RichTextMapSerializer; - -const createDefaultTableSerializer = ( - components?: TableMapSerializer, -): TableMapSerializerFilled => { - // filter out table serializer options to get just the table cell (rich text) components - const tableCellComponents = components - ? Object.fromEntries( - Object.entries(components).filter(([key]) => { - return !tableSerializerOptions.includes(key as TableMapSerializerKey); - }), - ) - : {}; - - // filter out the table cell (rich text) options to get just the table options - const tableComponents = components - ? Object.fromEntries( - Object.entries(components).filter(([key]) => { - return tableSerializerOptions.includes(key as TableMapSerializerKey); - }), - ) - : null; - - return { - table: ({ children }) => {children}
, - head: ({ children }) => {children}, - body: ({ children }) => {children}, - row: ({ children, key }) => {children}, - header: ({ children, key }) => {children}, - data: ({ children, key }) => {children}, - cellContent: ({ children }) => ( - - ), - ...tableComponents, - }; +const defaultTableComponents: Required = { + table: ({ children }) => {children}
, + thead: ({ children }) => {children}, + tbody: ({ children }) => {children}, + tr: ({ children }) => {children}, + th: ({ children }) => {children}, + td: ({ children }) => {children}, }; export type PrismicTableProps = { field: TableField; - components?: TableMapSerializer; - fallback?: JSX.Element; + components?: JSXMapSerializer; + tableComponents?: TableComponents; + fallback?: ReactNode; }; -export function PrismicTable({ - field, - components, - fallback, -}: PrismicTableProps) { - const serializer = createDefaultTableSerializer(components); +export function PrismicTable(props: PrismicTableProps) { + const { field, components, tableComponents, fallback = null } = props; - if (isFilled.table(field)) { - return serializer.table({ - children: ( - <> - - - - ), - }); - } else { + if (!isFilled.table(field)) { return fallback ?? null; } -} - -type TableHeadProps = { - field: TableField; - serializer: TableMapSerializerFilled; -}; - -function TableHead({ field, serializer }: TableHeadProps) { - return field && field.head - ? serializer.head({ - children: , - }) - : null; -} - -type TableBodyProps = { - field: TableField; - serializer: TableMapSerializerFilled; -}; - -function TableBody({ field, serializer }: TableBodyProps) { - return field && field.body - ? serializer.body({ - children: , - }) - : null; -} - -type TableRows = TableField<"filled">["body"]["rows"]; - -type TableRowsProps = { - rows: TableRows; - serializer: TableMapSerializerFilled; -}; -function TableRows({ rows, serializer }: TableRowsProps) { - return rows.map((row) => { - return serializer.row({ - children: , - key: crypto.randomUUID(), - }); - }); + const { + table: Table, + thead: Thead, + tbody: Tbody, + } = { ...defaultTableComponents, ...tableComponents }; + + return ( + + {field.head && ( + + {field.head.rows.map((row) => ( + + ))} + + )} + + {field.body.rows.map((row) => ( + + ))} + +
+ ); } -type TableCellsProps = { - cells: TableRows[0]["cells"]; - serializer: TableMapSerializerFilled; +type TableRowProps = { + row: TableFieldRow; + components?: JSXMapSerializer; + tableComponents?: TableComponents; }; -function TableCells({ cells, serializer }: TableCellsProps) { - return cells.map((cell) => { - return serializer[cell.type]({ - children: serializer.cellContent({ children: cell.content }), - key: crypto.randomUUID(), - }); - }); +function TableRow(props: TableRowProps) { + const { row, components, tableComponents } = props; + + const { + tr: Tr, + th: Th, + td: Td, + } = { ...defaultTableComponents, ...tableComponents }; + + return ( + + {row.cells.map((cell) => + cell.type === "header" ? ( + + + + ) : ( + + + + ), + )} + + ); } From 5d43f00678f07a0b93af13832eb650a09a988197 Mon Sep 17 00:00:00 2001 From: Angelo Ashmore Date: Thu, 20 Feb 2025 03:11:50 -1000 Subject: [PATCH 5/5] feat: remove `tableComponents` prop (#220) * feat: remove `tableComponents` prop * chore: update prismic-client dep * fix: combine tableComponents and components in tests --------- Co-authored-by: Levi Mykel Gable <9385378+levimykel@users.noreply.github.com> --- e2e-projects/nextjs/app/PrismicTable/page.tsx | 6 +-- package-lock.json | 8 +-- package.json | 2 +- src/PrismicTable.tsx | 52 +++++++++---------- 4 files changed, 31 insertions(+), 37 deletions(-) diff --git a/e2e-projects/nextjs/app/PrismicTable/page.tsx b/e2e-projects/nextjs/app/PrismicTable/page.tsx index 1d8aec2..4dc4847 100644 --- a/e2e-projects/nextjs/app/PrismicTable/page.tsx +++ b/e2e-projects/nextjs/app/PrismicTable/page.tsx @@ -28,7 +28,7 @@ export default async function Page() {
{children}
, thead: ({ children }) =>
{children}
, tbody: ({ children }) =>
{children}
, @@ -42,12 +42,10 @@ export default async function Page() {
( {children}
), - }} - components={{ paragraph: ({ children }) => (

{children}

), diff --git a/package-lock.json b/package-lock.json index 9e0eb47..192e41f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "devDependencies": { "@eslint/js": "^9.19.0", "@playwright/test": "^1.50.0", - "@prismicio/client": "^7.16.0", + "@prismicio/client": "^7.16.1", "@rollup/plugin-typescript": "^12.1.2", "@size-limit/preset-small-lib": "^11.1.6", "@types/react": "^19.0.8", @@ -1563,9 +1563,9 @@ } }, "node_modules/@prismicio/client": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@prismicio/client/-/client-7.16.0.tgz", - "integrity": "sha512-oKBkkOuRYujdiUgn5fs8qAPZJzJ44FYMFwFCY1iw/ODQ1UhOFh6uUJzgcKe4TPlw1mhRvsNOlJlmW2NTZmrFFg==", + "version": "7.16.1", + "resolved": "https://registry.npmjs.org/@prismicio/client/-/client-7.16.1.tgz", + "integrity": "sha512-mH3JtysZyQv47OYWzuxp8vW3uWllMBJa6HD2j4cfc7vLCoxGxMrvH6Ttw12KMnChg2/exwFdq3K1OvHAtyEVdQ==", "license": "Apache-2.0", "dependencies": { "imgix-url-builder": "^0.0.5" diff --git a/package.json b/package.json index 21753c8..1d088fe 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "devDependencies": { "@eslint/js": "^9.19.0", "@playwright/test": "^1.50.0", - "@prismicio/client": "^7.16.0", + "@prismicio/client": "^7.16.1", "@rollup/plugin-typescript": "^12.1.2", "@size-limit/preset-small-lib": "^11.1.6", "@types/react": "^19.0.8", diff --git a/src/PrismicTable.tsx b/src/PrismicTable.tsx index 7fd3baa..62f021d 100644 --- a/src/PrismicTable.tsx +++ b/src/PrismicTable.tsx @@ -1,15 +1,16 @@ import { ReactNode } from "react"; -import { isFilled, TableField } from "@prismicio/client"; -import { JSXMapSerializer, PrismicRichText } from "./PrismicRichText.js"; +import { + isFilled, + TableField, + TableFieldHead, + TableFieldHeadRow, + TableFieldBody, + TableFieldBodyRow, + TableFieldHeaderCell, + TableFieldDataCell, +} from "@prismicio/client"; -type TableFieldHead = NonNullable["head"]>; -type TableFieldBody = TableField<"filled">["body"]; -type TableFieldRow = - | TableFieldHead["rows"][number] - | TableFieldBody["rows"][number]; -type TableFieldCell = TableFieldRow["cells"][number]; -type TableFieldHeaderCell = Extract; -type TableFieldDataCell = Extract; +import { JSXMapSerializer, PrismicRichText } from "./PrismicRichText.js"; type TableComponents = { table?: (props: { @@ -18,7 +19,10 @@ type TableComponents = { }) => ReactNode; thead?: (props: { head: TableFieldHead; children: ReactNode }) => ReactNode; tbody?: (props: { body: TableFieldBody; children: ReactNode }) => ReactNode; - tr?: (props: { row: TableFieldRow; children: ReactNode }) => ReactNode; + tr?: (props: { + row: TableFieldHeadRow | TableFieldBodyRow; + children: ReactNode; + }) => ReactNode; th?: (props: { cell: TableFieldHeaderCell; children: ReactNode; @@ -26,7 +30,7 @@ type TableComponents = { td?: (props: { cell: TableFieldDataCell; children: ReactNode }) => ReactNode; }; -const defaultTableComponents: Required = { +const defaultComponents: Required = { table: ({ children }) => {children}
, thead: ({ children }) => {children}, tbody: ({ children }) => {children}, @@ -37,23 +41,22 @@ const defaultTableComponents: Required = { export type PrismicTableProps = { field: TableField; - components?: JSXMapSerializer; - tableComponents?: TableComponents; + components?: JSXMapSerializer & TableComponents; fallback?: ReactNode; }; export function PrismicTable(props: PrismicTableProps) { - const { field, components, tableComponents, fallback = null } = props; + const { field, components, fallback = null } = props; if (!isFilled.table(field)) { - return fallback ?? null; + return fallback; } const { table: Table, thead: Thead, tbody: Tbody, - } = { ...defaultTableComponents, ...tableComponents }; + } = { ...defaultComponents, ...components }; return ( @@ -64,7 +67,6 @@ export function PrismicTable(props: PrismicTableProps) { key={JSON.stringify(row)} row={row} components={components} - tableComponents={tableComponents} /> ))} @@ -75,7 +77,6 @@ export function PrismicTable(props: PrismicTableProps) { key={JSON.stringify(row)} row={row} components={components} - tableComponents={tableComponents} /> ))} @@ -84,19 +85,14 @@ export function PrismicTable(props: PrismicTableProps) { } type TableRowProps = { - row: TableFieldRow; - components?: JSXMapSerializer; - tableComponents?: TableComponents; + row: TableFieldHeadRow | TableFieldBodyRow; + components?: JSXMapSerializer & TableComponents; }; function TableRow(props: TableRowProps) { - const { row, components, tableComponents } = props; + const { row, components } = props; - const { - tr: Tr, - th: Th, - td: Td, - } = { ...defaultTableComponents, ...tableComponents }; + const { tr: Tr, th: Th, td: Td } = { ...defaultComponents, ...components }; return (