Skip to content

Commit

Permalink
Adding JSDoc, removing id from block config and including schemna in …
Browse files Browse the repository at this point in the history
…reader/editor (#4)

* Adding JSDoc
* Updating tests to not include id
* Including schema in reader/editor
* Bumping version
  • Loading branch information
cohitre authored Feb 15, 2024
1 parent 83c1f3d commit f159774
Show file tree
Hide file tree
Showing 11 changed files with 71 additions and 39 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@usewaypoint/document",
"version": "0.0.5",
"version": "0.0.6",
"description": "Tools to render waypoint-style documents.",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
14 changes: 6 additions & 8 deletions src/builders/buildBlockComponent.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import React from 'react';
import { z } from 'zod';

import { BaseZodDictionary, DocumentBlocksDictionary } from '../utils';
import { BaseZodDictionary, BlockConfiguration, DocumentBlocksDictionary } from '../utils';

/**
* @param blocks Main DocumentBlocksDictionary
* @returns React component that can render a BlockConfiguration that is compatible with blocks
*/
export default function buildBlockComponent<T extends BaseZodDictionary>(blocks: DocumentBlocksDictionary<T>) {
type BaseBlockComponentProps<TType extends keyof T> = {
type: TType;
data: z.infer<T[TType]>;
};

return function BlockComponent({ type, data }: BaseBlockComponentProps<keyof T>): React.ReactNode {
return function BlockComponent({ type, data }: BlockConfiguration<T>): React.ReactNode {
return React.createElement(blocks[type].Component, data);
};
}
4 changes: 4 additions & 0 deletions src/builders/buildBlockConfigurationByIdSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import { BaseZodDictionary, DocumentBlocksDictionary } from '../utils';

import buildBlockConfigurationSchema from './buildBlockConfigurationSchema';

/**
* @param blocks Main DocumentBlocksDictionary
* @returns zod schema that can parse arbitrary objects into { [id]: BlockConfiguration } pairs
*/
export default function buildBlockConfigurationByIdSchema<T extends BaseZodDictionary>(
blocks: DocumentBlocksDictionary<T>
) {
Expand Down
16 changes: 7 additions & 9 deletions src/builders/buildBlockConfigurationSchema.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
import { z } from 'zod';

import { BaseZodDictionary, DocumentBlocksDictionary } from '../utils';
import { BaseZodDictionary, BlockConfiguration, DocumentBlocksDictionary } from '../utils';

/**
*
* @param blocks Main DocumentBlocksDictionary
* @returns zod schema that can parse arbitary objects into a single BlockConfiguration
*/
export default function buildBlockConfigurationSchema<T extends BaseZodDictionary>(
blocks: DocumentBlocksDictionary<T>
) {
type BaseBlockComponentProps<TType extends keyof T> = {
id: string;
type: TType;
data: z.infer<T[TType]>;
};

const blockObjects = Object.keys(blocks).map((type: keyof T) =>
z.object({
id: z.string(),
type: z.literal(type),
data: blocks[type].schema,
})
);

// eslint-disable-next-line @typescript-eslint/no-explicit-any
return z.discriminatedUnion('type', blockObjects as any).transform((v) => v as BaseBlockComponentProps<keyof T>);
return z.discriminatedUnion('type', blockObjects as any).transform((v) => v as BlockConfiguration<T>);
}
43 changes: 29 additions & 14 deletions src/builders/buildDocumentEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ import { BaseZodDictionary, BlockNotFoundError, DocumentBlocksDictionary } from
import buildBlockComponent from './buildBlockComponent';
import buildBlockConfigurationByIdSchema from './buildBlockConfigurationByIdSchema';

/**
* @typedef {Object} DocumentEditor
* @property DocumentEditorProvider - Entry point to the DocumentEditor
* @property DocumentConfigurationSchema - zod schema compatible with the value that DocumentReaderProvider expects
* @property Block - Component to render a block given an id
* @property useDocumentState - Hook that returns the current DocumentState and a setter
* @property useBlockState - Hook that returns the Block value and setter given an id
*/

/**
* @param {DocumentBlocksDictionary} blocks root configuration
* @returns {DocumentEditor}
*/
export default function buildDocumentEditor<T extends BaseZodDictionary>(blocks: DocumentBlocksDictionary<T>) {
const schema = buildBlockConfigurationByIdSchema(blocks);
const BlockComponent = buildBlockComponent(blocks);
Expand All @@ -21,28 +34,30 @@ export default function buildDocumentEditor<T extends BaseZodDictionary>(blocks:
};

const useDocumentState = () => useContext(Context);
const useBlockState = (id: string) => {
const useBlockState = (id: string | null | undefined) => {
const [value, setValue] = useDocumentState();
return useMemo(
() =>
[
value[id],
(block: TValue[string]) => {
setValue({ ...value, [id]: block });
},
] as const,
[value, setValue, id]
);
return useMemo(() => {
if (id === null || id === undefined) {
return null;
}
return [
value[id],
(block: TValue[string]) => {
setValue({ ...value, [id]: block });
},
] as const;
}, [value, setValue, id]);
};
return {
useDocumentState,
useBlockState,
DocumentConfigurationSchema: schema,
Block: ({ id }: { id: string }) => {
const [block] = useBlockState(id);
if (!block) {
const state = useBlockState(id);
if (state === null || !state[0]) {
throw new BlockNotFoundError(id);
}
const { type, data } = block;
const { type, data } = state[0];
return React.createElement(BlockComponent, { type, data });
},
DocumentEditorProvider: ({ value, children }: TProviderProps) => {
Expand Down
14 changes: 14 additions & 0 deletions src/builders/buildDocumentReader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ import { BaseZodDictionary, BlockNotFoundError, DocumentBlocksDictionary } from
import buildBlockComponent from './buildBlockComponent';
import buildBlockConfigurationByIdSchema from './buildBlockConfigurationByIdSchema';

/**
* @typedef {Object} DocumentReader
* @property DocumentReaderProvider - Entry point to the DocumentReader
* @property DocumentConfigurationSchema - zod schema compatible with the value that DocumentReaderProvider expects
* @property Block - Component to render a block given an id
* @property useDocument - Hook that returns the current Document
* @property useBlock - Hook that returns the block given an id
*/

/**
* @param {DocumentBlocksDictionary} blocks root configuration
* @returns {DocumentReader}
*/
export default function buildDocumentReader<T extends BaseZodDictionary>(blocks: DocumentBlocksDictionary<T>) {
const schema = buildBlockConfigurationByIdSchema(blocks);
const BlockComponent = buildBlockComponent(blocks);
Expand All @@ -26,6 +39,7 @@ export default function buildDocumentReader<T extends BaseZodDictionary>(blocks:
return {
useDocument,
useBlock,
DocumentConfigurationSchema: schema,
Block: ({ id }: { id: string }) => {
const block = useBlock(id);
if (!block) {
Expand Down
2 changes: 1 addition & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ export { default as buildBlockConfigurationSchema } from './builders/buildBlockC
export { default as buildBlockConfigurationByIdSchema } from './builders/buildBlockConfigurationByIdSchema';
export { default as buildDocumentReader } from './builders/buildDocumentReader';
export { default as buildDocumentEditor } from './builders/buildDocumentEditor';
export { DocumentBlocksDictionary } from './utils';
export { BlockConfiguration, DocumentBlocksDictionary } from './utils';
7 changes: 7 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ export type DocumentBlocksDictionary<T extends BaseZodDictionary> = {
};
};

export type BlockConfiguration<T extends BaseZodDictionary> = {
[TType in keyof T]: {
type: TType;
data: z.infer<T[TType]>;
};
}[keyof T];

export class BlockNotFoundError extends Error {
blockId: string;
constructor(blockId: string) {
Expand Down
2 changes: 0 additions & 2 deletions tests/builder/buildBlockConfigurationByIdSchema.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ describe('builders/buildBlockConfigurationByIdSchema', () => {
});
const parsedData = schema.safeParse({
'my id': {
id: 'my id',
type: 'SampleBlock',
data: { text: 'Test text!' },
},
Expand All @@ -22,7 +21,6 @@ describe('builders/buildBlockConfigurationByIdSchema', () => {
success: true,
data: {
'my id': {
id: 'my id',
type: 'SampleBlock',
data: { text: 'Test text!' },
},
Expand Down
2 changes: 0 additions & 2 deletions tests/builder/buildBlockConfigurationSchema.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,12 @@ describe('builders/buildBlockConfigurationSchema', () => {
},
});
const parsedData = blockConfigurationSchema.safeParse({
id: 'my id',
type: 'SampleBlock',
data: { text: 'Test text!' },
});
expect(parsedData).toEqual({
success: true,
data: {
id: 'my id',
type: 'SampleBlock',
data: { text: 'Test text!' },
},
Expand Down

0 comments on commit f159774

Please sign in to comment.