diff --git a/.eslintrc.json b/.eslintrc.json index 6dc38f2..c535cc1 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -88,7 +88,7 @@ "files": ["*.ts", "*.tsx"], "rules": { "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/ban-ts-comment": "off" + "@typescript-eslint/ban-ts-comment": "warn" } }, { @@ -148,7 +148,7 @@ "moduleDirectory": ["node_modules", "src"], "extensions": [".js", ".jsx", ".ts", ".tsx"], "typescript": { - "alwaysTryTypes": true, + "alwaysTryTypes": true } } } diff --git a/.gitignore b/.gitignore index d7be5e6..bea12e8 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,5 @@ packages/react-devtools-fusebox/dist packages/react-devtools-inline/dist packages/react-devtools-shell/dist packages/react-devtools-timeline/dist -storybook-static/ \ No newline at end of file +storybook-static/ +lib \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit index b14d371..70ca1b1 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1 +1 @@ -pnpm lint-staged && pnpm test +pnpm lint-staged && pnpm lint:fix && pnpm test diff --git a/Readme.md b/Readme.md index bad9989..2dfb002 100644 --- a/Readme.md +++ b/Readme.md @@ -2,7 +2,7 @@ schema builder logo, a combination of S and B characters in a blue palette -[![npm](https://img.shields.io/npm/v/schemaBuilder)](https://www.npmjs.com/package/schemaBuilder) +[![npm](https://img.shields.io/npm/v/schemabuilder)](https://www.npmjs.com/package/schemaBuilder) # Schema Builder diff --git a/eslint.config.mjs b/eslint.config.mjs index f70c837..7e24a97 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -4,7 +4,6 @@ import tseslint from "typescript-eslint"; import pluginReactConfig from "eslint-plugin-react/configs/recommended.js"; import { fixupConfigRules } from "@eslint/compat"; - export default [ {languageOptions: { globals: globals.browser }}, pluginJs.configs.recommended, diff --git a/package.json b/package.json index 3bb2012..d7392aa 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { - "name": "schemaBuilder", + "name": "schemabuilder", "version": "0.0.0", "description": "A Simple User Interface for creating JSON Schema without writing JSON!", "main": "index.js", "scripts": { - "test": "jest", + "test": "jest --coverage", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build", "build": "tsc", @@ -28,14 +28,12 @@ "dependencies": { "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", - "@lit/react": "^1.0.5", "@mui/icons-material": "^5.15.20", "@mui/material": "^5.15.19", "@rjsf/core": "^5.18.4", "@rjsf/mui": "^5.18.4", "@rjsf/utils": "^5.18.4", "@rjsf/validator-ajv8": "^5.18.4", - "@tapsioss/web-components": "0.0.0-alpha-2", "immer": "^10.1.1", "react": "^18.3.1", "react-dom": "^18.3.1" diff --git a/src/components/CopyButton.tsx b/src/components/CopyButton.tsx new file mode 100644 index 0000000..949b154 --- /dev/null +++ b/src/components/CopyButton.tsx @@ -0,0 +1,21 @@ +import React, { useState } from 'react'; +import { CopyAll } from '@mui/icons-material'; +import { Button } from '@mui/material'; +import { useSchema } from '../providers/SchemaProvider'; + +const CopyButton = () => { + const { schema } = useSchema(); + const [buttonText, setButtonText] = useState('Copy'); + const handleCopy = async () => { + await navigator.clipboard.writeText(JSON.stringify(schema)); + setButtonText('Copied!'); + setTimeout(() => setButtonText('Copy'), 2000); + }; + return ( + + ); +}; + +export default CopyButton; diff --git a/src/components/SchemaBuilder.tsx b/src/components/SchemaBuilder.tsx index 1bd589a..2df46c6 100644 --- a/src/components/SchemaBuilder.tsx +++ b/src/components/SchemaBuilder.tsx @@ -5,8 +5,16 @@ import { useSchema } from '../providers/SchemaProvider'; import { Box, CssBaseline, Tab, Tabs } from '@mui/material'; import SchemaPreview from './SchemaPreview'; import { codeToHtml } from 'shiki'; +import { JsonSchema } from '../types'; +import CopyButton from './CopyButton'; -const SchemaBuilder = () => { +type SchemaBuilderProps = { + onChange?: (schema: JsonSchema) => void; + hideSchemaTab?: boolean; + hideFormTab?: boolean; +}; + +const SchemaBuilder = ({ onChange, hideSchemaTab = false, hideFormTab = false }: SchemaBuilderProps) => { const { schema } = useSchema(); const [highlightedSchema, setHighlightedSchema] = useState(''); const [tab, setTab] = useState(0); @@ -19,6 +27,10 @@ const SchemaBuilder = () => { setTab(newValue); }; + useEffect(() => { + onChange?.(schema); + }, [schema]); + useEffect(() => { const getHighlightedCode = async (code: string) => { const highlighted = await codeToHtml(code, { @@ -35,17 +47,20 @@ const SchemaBuilder = () => { return ( <> -
- - - - - + + + + + {!hideSchemaTab && } + {!hideFormTab && } + + + {tab === TABS.BUILDER && } {tab === TABS.SCHEMA && } {tab === TABS.FORM_PREVIEW &&
} -
+ ); }; diff --git a/src/stories/SchemaBuilder.stories.tsx b/src/stories/SchemaBuilder.stories.tsx index 7893380..cf7759e 100644 --- a/src/stories/SchemaBuilder.stories.tsx +++ b/src/stories/SchemaBuilder.stories.tsx @@ -1,7 +1,7 @@ // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-nocheck -import React from 'react'; -import { Story } from '@storybook/react'; +import React, { useState } from 'react'; +import { Meta, Story } from '@storybook/react'; import SchemaBuilder from '../components/SchemaBuilder'; import { STRING_WIDGETS } from '../constants'; import { SchemaProvider } from '../providers/SchemaProvider'; @@ -98,9 +98,19 @@ const sampleSchema: RJSFSchema = { additionalProperties: false, }; -export default { - title: 'SchemaBuilder', +const meta: Meta = { + title: 'Schema Builder', component: SchemaBuilder, + argTypes: { + hideSchemaTab: { + control: 'boolean', + description: 'Should the schema preview tab be visible?', + }, + hideFormTab: { + control: 'boolean', + description: 'Should the form preview tab be visible?', + }, + }, }; const PrimitivesTemplate: Story = (args) => ( @@ -189,5 +199,22 @@ const FaqTemplate: Story = (args) => ( ); - export const CustomTemplate = FaqTemplate.bind({}); + +const ControlledTemplate: Story = (args) => { + const [schema, setSchema] = useState(); + return ( + <> +
+ state: +
{JSON.stringify(schema, null, 2)}
+
+ + setSchema(updatedSchema)} {...args} /> + + + ); +}; +export const ControlledComponent = ControlledTemplate.bind({}); + +export default meta; diff --git a/src/utils/index.ts b/src/utils/index.ts index ba076e5..57bf568 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -55,9 +55,8 @@ export const generatePath = (parentPath: string = '', fieldName: string): string return path; }; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment export const accessToObjectFieldByPath = (object: object, path: string) => { - // @ts-expect-error + // @ts-expect-error fix it later return path.split('.').reduce((o, i) => o[i], object); };