diff --git a/packages/@atjson/renderer-react/src/index.ts b/packages/@atjson/renderer-react/src/index.ts index e771b052a..9b5b910db 100644 --- a/packages/@atjson/renderer-react/src/index.ts +++ b/packages/@atjson/renderer-react/src/index.ts @@ -46,6 +46,16 @@ const SliceContext = React.createContext<{ schema: AnnotationConstructor[]; }>({ slices: new Map(), schema: [] }); +export type DataSetAttrs = { + name?: string; + schema: Record; + records: Array>; +}; + +const DataSetContext = React.createContext>( + new Map() +); + export const ReactRendererContext = React.createContext<{ [key: string]: ComponentType; }>(EMPTY_COMPONENT_MAP); @@ -200,6 +210,11 @@ function render( }); let tree = createTree(doc); let schema = (props.document.constructor as typeof Document).schema; + let dataSets = new Map( + doc.blocks + .filter((block) => block.type === "data-set") + .map((dataSet) => [dataSet.id, dataSet.attributes as DataSetAttrs]) + ); return createElement( SliceContext.Provider, @@ -210,13 +225,17 @@ function render( }, }, createElement( - Fragment, - {}, - renderNode({ - id: ROOT, - tree, - schema, - }) + DataSetContext.Provider, + { value: dataSets }, + createElement( + Fragment, + {}, + renderNode({ + id: ROOT, + tree, + schema, + }) + ) ) ); } @@ -254,6 +273,10 @@ export function Slice(props: { } Slice.displayName = "Slice"; +export function useDataSet(id: string) { + return useContext(DataSetContext).get(id); +} + export default { render, }; diff --git a/packages/@atjson/renderer-react/test/react-renderer.test.tsx b/packages/@atjson/renderer-react/test/react-renderer.test.tsx index 63ccc26b1..29240247e 100644 --- a/packages/@atjson/renderer-react/test/react-renderer.test.tsx +++ b/packages/@atjson/renderer-react/test/react-renderer.test.tsx @@ -1,4 +1,5 @@ import { + AttributesOf, ObjectAnnotation, ParseAnnotation, SliceAnnotation, @@ -6,19 +7,27 @@ import { import OffsetSource, { Bold, CaptionSource, + ColumnType, + DataSet, GiphyEmbed, IframeEmbed, Italic, LineBreak, Link, Paragraph, + Table, VideoEmbed, VideoURLs, } from "@atjson/offset-annotations"; import * as React from "react"; import { createElement, Fragment, ReactNode } from "react"; import * as ReactDOMServer from "react-dom/server"; -import ReactRenderer, { PropsOf, ReactRendererProvider, Slice } from "../src"; +import ReactRenderer, { + PropsOf, + ReactRendererProvider, + Slice, + useDataSet, +} from "../src"; function renderDocument( doc: OffsetSource, @@ -141,6 +150,26 @@ function IframeComponentWithProvider(props: PropsOf) { ); } +function TableComponent(props: AttributesOf) { + let data = useDataSet(props.dataSet); + + return ( +
+ + {data?.records.map((row) => ( + + {props.columns.map(({ columnName }) => ( + + ))} + + ))} + +
+ +
+ ); +} + describe("ReactRenderer", () => { it("renders simple components", () => { let document = new OffsetSource({ @@ -379,4 +408,68 @@ describe("ReactRenderer", () => { ); }); }); + + describe("useDataSet hook", () => { + it("can be used to render tables", () => { + const doc = new OffsetSource({ + content: "foo bar baz", + annotations: [ + new DataSet({ + id: "DS1", + start: 0, + end: 11, + attributes: { + records: [ + { + foo: { slice: "S1", jsonValue: "foo" }, + bar: { slice: "S2", jsonValue: "bar" }, + baz: { slice: "S3", jsonValue: "baz" }, + }, + ], + schema: { + foo: ColumnType.STRING, + bar: ColumnType.STRING, + baz: ColumnType.STRING, + }, + }, + }), + new Table({ + start: 0, + end: 11, + attributes: { + dataSet: "DS1", + columns: [ + { columnName: "foo" }, + { columnName: "bar" }, + { columnName: "baz" }, + ], + }, + }), + new SliceAnnotation({ + id: "S1", + start: 0, + end: 3, + }), + new SliceAnnotation({ + id: "S2", + start: 4, + end: 7, + }), + new SliceAnnotation({ + id: "S3", + start: 8, + end: 11, + }), + ], + }); + + expect( + renderDocument(doc, { + Table: TableComponent, + }) + ).toBe( + "
foobarbaz
" + ); + }); + }); });