Skip to content

Commit

Permalink
Merge pull request #16 from amir78729/feat/template-type
Browse files Browse the repository at this point in the history
Feat: Add Template Type
  • Loading branch information
amir78729 authored Jul 23, 2024
2 parents 6ff9e3d + c273081 commit 60548fb
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 82 deletions.
3 changes: 1 addition & 2 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
pnpm lint-staged
pnpm test
pnpm lint-staged && pnpm test
43 changes: 31 additions & 12 deletions src/components/AddFieldModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Form from '@rjsf/mui';
import validator from '@rjsf/validator-ajv8';
import React from 'react';
import { useSchema } from '../providers/SchemaProvider';
import { JsonSchema, JsonSchemaType } from '../types';
import { JsonSchema } from '../types';
import { JsonSchemaField } from '../fields/JsonSchemaField';
import Select from '@mui/material/Select';
import { Add } from '@mui/icons-material';
Expand All @@ -17,18 +17,38 @@ type Props = {
const AddFieldModal = ({ parentPath }: Props) => {
const [open, setOpen] = React.useState<boolean>(false);
const [name, setName] = React.useState<string | null>(null);
const [type, setType] = React.useState<JsonSchemaType | null>(null);
const [type, setType] = React.useState<string | null>(null);
const [field, setField] = React.useState<null | JsonSchemaField>(null);
const { dispatch, fields } = useSchema();
const { dispatch, fields, templates } = useSchema();
const [step, setStep] = React.useState<number>(0);

const SelectedFieldClass = fields.find((f) => f.id === type)?.Class;

const handleSelectType = () => {
const SelectedFieldClass = fields.find((f) => f.id === type)?.Class;
if (name && type && SelectedFieldClass) {
setField(new SelectedFieldClass(name));
setStep(1);
}

const SelectedTemplateSchema = templates.find((f) => f.id === type)?.schema;
if (name && type && SelectedTemplateSchema) {
dispatch({
type: 'ADD_PROPERTY',
payload: {
name: generatePath(parentPath, name || 'newField'),
schema: SelectedTemplateSchema,
},
});
dispatch({
type: 'ADD_REQUIRED',
payload: {
name: generatePath(parentPath, name || 'newField'),
},
});
setOpen(false);
setType(null);
setName(null);
setStep(0);
}
};

const handleSubmit = (formData: JsonSchema) => {
Expand Down Expand Up @@ -85,18 +105,17 @@ const AddFieldModal = ({ parentPath }: Props) => {

<FormControl>
<InputLabel htmlFor="field-type">Field Type</InputLabel>
<Select
defaultValue=""
value={type}
id="field-type"
label="Field Type"
onChange={(e) => setType(e.target.value as JsonSchemaType)}
>
<Select defaultValue="" value={type} id="field-type" label="Field Type" onChange={(e) => setType(e.target.value)}>
{fields.map((property) => (
<MenuItem title={property.description} key={property.id} value={property.id}>
{property.title}
</MenuItem>
))}
{templates?.map((property) => (
<MenuItem title={property.description} key={property.id} value={property.id}>
{property.title}
</MenuItem>
))}
</Select>
</FormControl>

Expand Down
17 changes: 1 addition & 16 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { ArrayField } from './fields/containers/ArrayField';
import { DateField } from './fields/widgets/DateField';
import { TimeField } from './fields/widgets/TimeField';
import { DateTimeField } from './fields/widgets/DateTimeField';
import { FaqWidget } from './fields/patterns/FaqWidget';
import { FieldConfig } from './types';
import { SelectField } from './fields/widgets/SelectField';

Expand Down Expand Up @@ -89,18 +88,4 @@ export const STRING_WIDGETS: FieldConfig[] = [
},
];

export const PATTERNS: FieldConfig[] = [
{
id: 'FAQ',
title: 'FAQ',
description: 'a FAQ form',
Class: FaqWidget,
},
];

export const PROPERTIES = [
...PRIMITIVE_PROPERTIES,
...CONTAINER_PROPERTIES,
// ...STRING_WIDGETS,
// ...PATTERNS,
];
export const PROPERTIES = [...PRIMITIVE_PROPERTIES, ...CONTAINER_PROPERTIES, ...STRING_WIDGETS];
42 changes: 0 additions & 42 deletions src/fields/patterns/FaqWidget.ts

This file was deleted.

11 changes: 7 additions & 4 deletions src/providers/SchemaProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { createContext, Dispatch, ReactNode, useContext, useReducer } from 'react';
import { JsonSchemaBuilder } from '../builder/JsonSchemaBuilder';
import { FieldConfig, JsonSchema } from '../types';
import { FieldConfig, JsonSchema, TemplateType } from '../types';
import { PROPERTIES } from '../constants';
import type { RJSFSchema } from '@rjsf/utils';

Expand All @@ -13,10 +13,12 @@ export const SchemaContext = createContext<{
schema: JsonSchema;
dispatch: Dispatch<SchemaAction>;
fields: FieldConfig[];
templates: TemplateType[];
}>({
schema: new JsonSchemaBuilder().setType('object').build(),
dispatch: () => null,
fields: [],
templates: [],
});

const schemaReducer = (state: JsonSchema, action: SchemaAction): JsonSchema => {
Expand Down Expand Up @@ -46,16 +48,17 @@ const schemaReducer = (state: JsonSchema, action: SchemaAction): JsonSchema => {
};

type Props = {
extraFields: FieldConfig[];
children: ReactNode;
value?: RJSFSchema;
templates?: TemplateType[];
extraFields?: FieldConfig[];
};

export const SchemaProvider = ({ children, extraFields, value }: Props) => {
export const SchemaProvider = ({ children, extraFields = [], value, templates = [] }: Props) => {
const [schema, dispatch] = useReducer(schemaReducer, value || new JsonSchemaBuilder().setType('object').build());

return (
<SchemaContext.Provider value={{ schema, dispatch, fields: [...PROPERTIES, ...extraFields] }}>
<SchemaContext.Provider value={{ schema, dispatch, fields: [...PROPERTIES, ...extraFields], templates: templates }}>
{children}
</SchemaContext.Provider>
);
Expand Down
45 changes: 39 additions & 6 deletions src/stories/SchemaBuilder.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck
import React from 'react';
import { Story, Meta } from '@storybook/react';
import { Story } from '@storybook/react';
import SchemaBuilder from '../components/SchemaBuilder';
import { STRING_WIDGETS } from '../constants';
import { SchemaProvider } from '../providers/SchemaProvider';
Expand Down Expand Up @@ -101,18 +101,18 @@ const sampleSchema: RJSFSchema = {
export default {
title: 'SchemaBuilder',
component: SchemaBuilder,
} as Meta;
};

const Template: Story = (args) => (
<SchemaProvider extraFields={args.extraFields || []}>
const PrimitivesTemplate: Story = (args) => (
<SchemaProvider extraFields={args.extraFields || []} templates={[template]}>
<SchemaBuilder {...args} />
</SchemaProvider>
);

export const Primitives = Template.bind({});
export const Primitives = PrimitivesTemplate.bind({});
Primitives.args = {};

export const Formats = Template.bind({});
export const Formats = PrimitivesTemplate.bind({});
Formats.args = {
extraFields: [...STRING_WIDGETS],
};
Expand Down Expand Up @@ -158,3 +158,36 @@ Themed.args = {
},
},
};

const customTemplate = {
id: 'FAQ_TEMPLATE',
title: 'FAQ Template',
description: 'A template schema for FAQ type.',
schema: {
title: 'FAQ',
type: 'array',
items: {
type: 'object',
title: 'List of Questions',
properties: {
question: {
title: 'question',
type: 'string',
},
answer: {
title: 'answer',
type: 'string',
},
},
},
uniqueItems: true,
},
};

const FaqTemplate: Story = (args) => (
<SchemaProvider templates={[customTemplate]}>
<SchemaBuilder {...args} />
</SchemaProvider>
);

export const CustomTemplate = FaqTemplate.bind({});
25 changes: 25 additions & 0 deletions src/test/JsonSchemaBuilder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,29 @@ describe('JsonSchemaBuilder', () => {
const schema = builderWithInput.build();
expect(schema).toEqual(inputSchema);
});

test('should add template schema', () => {
const templateSchema = {
title: 'FAQ',
type: 'array',
items: {
type: 'object',
title: 'List of Questions',
properties: {
question: {
title: 'question',
type: 'string',
},
answer: {
title: 'answer',
type: 'string',
},
},
},
uniqueItems: true,
};
builder.addProperty('faq', templateSchema);
const schema = builder.build();
expect(schema.properties?.faq).toEqual(templateSchema);
});
});
7 changes: 7 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ export type FieldConfig = {
Class: new (...args: string[]) => JsonSchemaField; // a class that extends JsonSchemaField
};

export type TemplateType = {
id: string;
title: string;
description: string;
schema: JsonSchema;
};

export type DataVisualizationType<T = unknown> = {
schema: RJSFSchema;
data: T;
Expand Down

0 comments on commit 60548fb

Please sign in to comment.