diff --git a/package-lock.json b/package-lock.json
index aeedd6b..7bb87b5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,8 +11,8 @@
"dependencies": {
"@mux/mux-player-react": "*",
"datocms-listen": "^0.1.9",
- "datocms-structured-text-generic-html-renderer": "^4.0.1",
- "datocms-structured-text-utils": "^4.0.1",
+ "datocms-structured-text-generic-html-renderer": "^4.1.2",
+ "datocms-structured-text-utils": "^4.1.2",
"react-intersection-observer": "^9.4.3",
"react-string-replace": "^1.1.0",
"use-deep-compare-effect": "^1.6.1"
@@ -1856,7 +1856,8 @@
"node_modules/array-flatten": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-3.0.0.tgz",
- "integrity": "sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA=="
+ "integrity": "sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA==",
+ "license": "MIT"
},
"node_modules/array.prototype.filter": {
"version": "1.0.2",
@@ -3105,17 +3106,19 @@
"integrity": "sha512-Ijd5fzQ1Y1EUiSwduvaPRN7a6Edh//c500/2MjDhgaKd+N+zGEy7rpJ/SVXX0TdYDhX0ssoC4FDms6sbR7k+eA=="
},
"node_modules/datocms-structured-text-generic-html-renderer": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/datocms-structured-text-generic-html-renderer/-/datocms-structured-text-generic-html-renderer-4.0.1.tgz",
- "integrity": "sha512-umEWXyBHP3fo1YsOL7AhFxWrmb2LNxiRV26+C8NMkwoBZ/RbF9+dBaGIBv1D/NeasCjq560hBdm7jqChxxIi/A==",
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/datocms-structured-text-generic-html-renderer/-/datocms-structured-text-generic-html-renderer-4.1.2.tgz",
+ "integrity": "sha512-VsyVnqG/5iio3LHGd09Pk27n3hu2YcAujgTcgtv8CXapYd13r7OkfMkrsbiuJCO+xYBzPNHhI62PL6QOoI6BGA==",
+ "license": "MIT",
"dependencies": {
- "datocms-structured-text-utils": "^4.0.1"
+ "datocms-structured-text-utils": "^4.1.2"
}
},
"node_modules/datocms-structured-text-utils": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/datocms-structured-text-utils/-/datocms-structured-text-utils-4.0.1.tgz",
- "integrity": "sha512-2rK4bZfzKpdKw2AZhnnD043QkGOUaJDOUGPrQHo5w4hvU4jTBGGxoaTg4KIb3Q5Yo+XUFvreb+U0+86xdh7I+Q==",
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/datocms-structured-text-utils/-/datocms-structured-text-utils-4.1.2.tgz",
+ "integrity": "sha512-xyBG7ZAXedv8wytTR1JqfHx8uuUogg6I9brQmK1uR3y14gQqTRLvAeoY3unp5xcBDm8YXYHdS2W+Bg9AWgdhLQ==",
+ "license": "MIT",
"dependencies": {
"array-flatten": "^3.0.0"
}
diff --git a/package.json b/package.json
index 6eec531..5d8ff81 100644
--- a/package.json
+++ b/package.json
@@ -135,8 +135,8 @@
"dependencies": {
"@mux/mux-player-react": "*",
"datocms-listen": "^0.1.9",
- "datocms-structured-text-generic-html-renderer": "^4.0.1",
- "datocms-structured-text-utils": "^4.0.1",
+ "datocms-structured-text-generic-html-renderer": "^4.1.2",
+ "datocms-structured-text-utils": "^4.1.2",
"react-intersection-observer": "^9.4.3",
"react-string-replace": "^1.1.0",
"use-deep-compare-effect": "^1.6.1"
diff --git a/src/StructuredText/__tests__/__snapshots__/index.test.tsx.snap b/src/StructuredText/__tests__/__snapshots__/index.test.tsx.snap
index 828b6ef..aa26d7d 100644
--- a/src/StructuredText/__tests__/__snapshots__/index.test.tsx.snap
+++ b/src/StructuredText/__tests__/__snapshots__/index.test.tsx.snap
@@ -224,6 +224,11 @@ exports[`StructuredText with links/blocks with default rules renders the documen
"id": "456",
"quote": "Foo bar.",
},
+ {
+ "__typename": "MentionRecord",
+ "id": "789",
+ "name": "Jane Doe",
+ },
],
"links": [
{
@@ -269,6 +274,10 @@ exports[`StructuredText with links/blocks with default rules renders the documen
],
"type": "itemLink",
},
+ {
+ "item": "789",
+ "type": "inlineBlock",
+ },
],
"level": 1,
"type": "heading",
@@ -285,6 +294,7 @@ exports[`StructuredText with links/blocks with default rules renders the documen
}
}
renderBlock={[Function]}
+ renderInlineBlock={[Function]}
renderInlineRecord={[Function]}
renderLinkToRecord={[Function]}
>
@@ -310,6 +320,11 @@ exports[`StructuredText with links/blocks with default rules renders the documen
>
here!
+
+ Jane Doe
+
{
- describe('with no value', () => {
- it('renders null', () => {
+describe("StructuredText", () => {
+ describe("with no value", () => {
+ it("renders null", () => {
const wrapper = mount();
expect(wrapper).toMatchSnapshot();
});
});
- describe('simple dast /2', () => {
+ describe("simple dast /2", () => {
const structuredText: StructuredTextDocument = {
- schema: 'dast',
+ schema: "dast",
document: {
- type: 'root',
+ type: "root",
children: [
{
- type: 'heading',
+ type: "heading",
level: 1,
children: [
{
- type: 'span',
- value: 'This\nis a ',
+ type: "span",
+ value: "This\nis a ",
},
{
- type: 'span',
- marks: ['strong'],
- value: 'title',
+ type: "span",
+ marks: ["strong"],
+ value: "title",
},
],
},
@@ -43,49 +43,49 @@ describe('StructuredText', () => {
},
};
- describe('with default rules', () => {
- it('renders the document', () => {
+ describe("with default rules", () => {
+ it("renders the document", () => {
const wrapper = mount();
expect(wrapper).toMatchSnapshot();
});
});
- describe('with custom mark rules', () => {
- it('renders the document', () => {
+ describe("with custom mark rules", () => {
+ it("renders the document", () => {
const wrapper = mount(
(
+ renderMarkRule("strong", ({ children, key }) => (
{children}
)),
]}
- />,
+ />
);
expect(wrapper).toMatchSnapshot();
});
});
});
- describe('simple dast with no links/blocks', () => {
+ describe("simple dast with no links/blocks", () => {
const structuredText: StructuredTextGraphQlResponse = {
value: {
- schema: 'dast',
+ schema: "dast",
document: {
- type: 'root',
+ type: "root",
children: [
{
- type: 'heading',
+ type: "heading",
level: 1,
children: [
{
- type: 'span',
- value: 'This\nis a ',
+ type: "span",
+ value: "This\nis a ",
},
{
- type: 'span',
- marks: ['strong'],
- value: 'title',
+ type: "span",
+ marks: ["strong"],
+ value: "title",
},
],
},
@@ -94,22 +94,22 @@ describe('StructuredText', () => {
},
};
- describe('with default rules', () => {
- it('renders the document', () => {
+ describe("with default rules", () => {
+ it("renders the document", () => {
const wrapper = mount();
expect(wrapper).toMatchSnapshot();
});
});
- describe('with custom rules', () => {
- it('renders the document', () => {
+ describe("with custom rules", () => {
+ it("renders the document", () => {
const wrapper = mount(
{
return (
- {text.replace(/This/, 'That')}
+ {text.replace(/This/, "That")}
);
}}
@@ -118,98 +118,113 @@ describe('StructuredText', () => {
isHeading,
({ adapter: { renderNode }, node, children, key }) => {
return renderNode(`h${node.level + 1}`, { key }, children);
- },
+ }
),
]}
- />,
+ />
);
expect(wrapper).toMatchSnapshot();
});
});
});
- describe('with links/blocks', () => {
+ describe("with links/blocks", () => {
type QuoteRecord = {
id: string;
- __typename: 'QuoteRecord';
+ __typename: "QuoteRecord";
quote: string;
author: string;
};
+ type MentionRecord = {
+ id: string;
+ __typename: "MentionRecord";
+ name: string;
+ };
+
type DocPageRecord = {
id: string;
- __typename: 'DocPageRecord';
+ __typename: "DocPageRecord";
slug: string;
title: string;
};
const structuredText: StructuredTextGraphQlResponse<
- QuoteRecord,
+ QuoteRecord | MentionRecord,
DocPageRecord
> = {
value: {
- schema: 'dast',
+ schema: "dast",
document: {
- type: 'root',
+ type: "root",
children: [
{
- type: 'heading',
+ type: "heading",
level: 1,
children: [
{
- type: 'span',
- value: 'This is a',
+ type: "span",
+ value: "This is a",
},
{
- type: 'span',
- marks: ['highlight'],
- value: 'title',
+ type: "span",
+ marks: ["highlight"],
+ value: "title",
},
{
- type: 'inlineItem',
- item: '123',
+ type: "inlineItem",
+ item: "123",
},
{
- type: 'itemLink',
- item: '123',
- meta: [{ id: 'target', value: '_blank' }],
- children: [{ type: 'span', value: 'here!' }],
+ type: "itemLink",
+ item: "123",
+ meta: [{ id: "target", value: "_blank" }],
+ children: [{ type: "span", value: "here!" }],
+ },
+ {
+ type: "inlineBlock",
+ item: "789",
},
],
},
{
- type: 'block',
- item: '456',
+ type: "block",
+ item: "456",
},
],
},
},
blocks: [
{
- id: '456',
- __typename: 'QuoteRecord',
- quote: 'Foo bar.',
- author: 'Mark Smith',
+ id: "456",
+ __typename: "QuoteRecord",
+ quote: "Foo bar.",
+ author: "Mark Smith",
+ },
+ {
+ id: "789",
+ __typename: "MentionRecord",
+ name: "Jane Doe",
},
],
links: [
{
- id: '123',
- __typename: 'DocPageRecord',
- title: 'How to code',
- slug: 'how-to-code',
+ id: "123",
+ __typename: "DocPageRecord",
+ title: "How to code",
+ slug: "how-to-code",
},
],
};
- describe('with default rules', () => {
- it('renders the document', () => {
+ describe("with default rules", () => {
+ it("renders the document", () => {
const wrapper = mount(
{
switch (record.__typename) {
- case 'DocPageRecord':
+ case "DocPageRecord":
return {record.title};
default:
return null;
@@ -217,7 +232,7 @@ describe('StructuredText', () => {
}}
renderLinkToRecord={({ record, children, transformedMeta }) => {
switch (record.__typename) {
- case 'DocPageRecord':
+ case "DocPageRecord":
return (
{children}
@@ -229,7 +244,7 @@ describe('StructuredText', () => {
}}
renderBlock={({ record }) => {
switch (record.__typename) {
- case 'QuoteRecord':
+ case "QuoteRecord":
return (
{record.quote}
@@ -240,22 +255,30 @@ describe('StructuredText', () => {
return null;
}
}}
- />,
+ renderInlineBlock={({ record }) => {
+ switch (record.__typename) {
+ case "MentionRecord":
+ return {record.name};
+ default:
+ return null;
+ }
+ }}
+ />
);
expect(wrapper).toMatchSnapshot();
});
});
- describe('with missing renderInlineRecord prop', () => {
- it('raises an error', () => {
+ describe("with missing renderInlineRecord prop", () => {
+ it("raises an error", () => {
expect(() => {
shallow();
}).toThrow(RenderError);
});
});
- describe('with missing record', () => {
- it('raises an error', () => {
+ describe("with missing record", () => {
+ it("raises an error", () => {
expect(() => {
shallow(
{
renderInlineRecord={() => {
return null;
}}
- />,
+ />
);
}).toThrow(RenderError);
});
diff --git a/src/StructuredText/index.tsx b/src/StructuredText/index.tsx
index 1e25644..139e4a5 100644
--- a/src/StructuredText/index.tsx
+++ b/src/StructuredText/index.tsx
@@ -17,6 +17,7 @@ import {
type Record as StructuredTextGraphQlResponseRecord,
type TypesafeStructuredText as TypesafeStructuredTextGraphQlResponse,
isBlock,
+ isInlineBlock,
isInlineItem,
isItemLink,
isStructuredText,
@@ -104,6 +105,8 @@ export type StructuredTextPropTypes<
) => ReactElement | null;
/** Fuction that converts a 'block' node into React **/
renderBlock?: (context: RenderBlockContext) => ReactElement | null;
+ /** Fuction that converts an 'inlineBlock' node into React **/
+ renderInlineBlock?: (context: RenderBlockContext) => ReactElement | null;
/** Function that converts 'link' and 'itemLink' `meta` into HTML props */
metaTransformer?: TransformMetaFn;
/** Fuction that converts a simple string text into React **/
@@ -124,6 +127,7 @@ export function StructuredText<
renderInlineRecord,
renderLinkToRecord,
renderBlock,
+ renderInlineBlock,
renderText,
renderNode,
renderFragment,
@@ -234,6 +238,32 @@ export function StructuredText<
return appendKeyToValidElement(renderBlock({ record: item }), key);
}),
+ renderNodeRule(isInlineBlock, ({ node, key }) => {
+ if (!renderInlineBlock) {
+ throw new RenderError(
+ `The Structured Text document contains an 'inlineBlock' node, but no 'renderInlineBlock' prop is specified!`,
+ node,
+ );
+ }
+
+ if (!(isStructuredText(data) && data.blocks)) {
+ throw new RenderError(
+ `The document contains an 'inlineBlock' node, but the passed data prop is not a Structured Text GraphQL response, or data.blocks is not present!`,
+ node,
+ );
+ }
+
+ const item = data.blocks.find((item) => item.id === node.item);
+
+ if (!item) {
+ throw new RenderError(
+ `The Structured Text document contains an 'inlineBlock' node, but cannot find a record with ID ${node.item} inside data.blocks!`,
+ node,
+ );
+ }
+
+ return appendKeyToValidElement(renderInlineBlock({ record: item }), key);
+ }),
...(customNodeRules || customRules || []),
],
});