diff --git a/formule-demo/cypress/e2e/builder.cy.ts b/formule-demo/cypress/e2e/builder.cy.ts index 3d51f64..7fc4560 100644 --- a/formule-demo/cypress/e2e/builder.cy.ts +++ b/formule-demo/cypress/e2e/builder.cy.ts @@ -194,6 +194,20 @@ describe("test basic functionality", () => { // TODO test also suggestion endpoint, after that feature is migrated to formule }); + it("tests email field", () => { + cy.addFieldWithName("email", "myfield"); + cy.getByDataCy("treeItem").click(); + + // Test pattern allowing only one character + cy.getByDataCy("formPreview") + .find(`input#root${SEP}myfield`) + .as("myfield") + .clearTypeBlur("a"); + cy.getByDataCy("formPreview").hasErrorMessage('must match format "email"'); + cy.get("@myfield").clearTypeBlur("test@example.com"); + cy.getByDataCy("formPreview").hasNoErrorMessage(); + }); + it("tests number field", () => { cy.addFieldWithName("number", "myfield"); cy.getByDataCy("treeItem").click(); diff --git a/formule-demo/src/App.tsx b/formule-demo/src/App.tsx index af0612d..cd04b72 100644 --- a/formule-demo/src/App.tsx +++ b/formule-demo/src/App.tsx @@ -34,7 +34,7 @@ import { } from "antd"; import { useEffect, useState } from "react"; import { - CodeViewer, + SchemaCodeEditor, FormPreview, FormuleContext, SchemaPreview, @@ -331,11 +331,11 @@ const App = () => { }} > Schema - { }} > UI Schema - { }} > Form data - @@ -434,7 +434,7 @@ const App = () => { const reader = new FileReader(); reader.onload = (event) => { const newSchema = JSON.parse( - event?.target?.result as string + event?.target?.result as string, ); const { schema, uiSchema } = newSchema; if (schema && uiSchema) { @@ -443,7 +443,7 @@ const App = () => { message.success("Uploaded and loaded successfully"); } else { message.error( - "Your json should include a schema and a uiSchema key" + "Your json should include a schema and a uiSchema key", ); } }; diff --git a/src/admin/components/SchemaCodeEditor.jsx b/src/admin/components/SchemaCodeEditor.jsx new file mode 100644 index 0000000..7157041 --- /dev/null +++ b/src/admin/components/SchemaCodeEditor.jsx @@ -0,0 +1,37 @@ +import { + updateSchemaByPath, + updateUiSchemaByPath, +} from "../../store/schemaWizard"; +import { useDispatch } from "react-redux"; +import CodeEditor from "../../utils/CodeEditor"; + +const SchemaCodeEditor = ({ valueType, value, height, lang, isReadOnly }) => { + const dispatch = useDispatch(); + + const _onSchemaChange = (data) => { + dispatch(updateSchemaByPath({ path: [], value: JSON.parse(data) })); + }; + + const _onUiSchemaChange = (data) => { + dispatch(updateUiSchemaByPath({ path: [], value: JSON.parse(data) })); + }; + + const _handleEdit = (data) => { + if (valueType == "schema") _onSchemaChange(data); + else if (valueType == "uiSchema") _onUiSchemaChange(data); + }; + + return ( + + ); +}; + +export default SchemaCodeEditor; diff --git a/src/admin/utils/fieldTypes.jsx b/src/admin/utils/fieldTypes.jsx index 8e4c0d9..fb1be19 100644 --- a/src/admin/utils/fieldTypes.jsx +++ b/src/admin/utils/fieldTypes.jsx @@ -486,6 +486,31 @@ const simple = { type: "string", format: "regex", }, + format: { + title: "Format", + type: "string", + enum: [ + "date", + "time", + "date-time", + "duration", + "regex", + "email", + "idn-email", + "hostname", + "idn-hostname", + "ipv4", + "ipv6", + "json-pointer", + "relative-json-pointer", + "uri", + "uri-reference", + "uri-template", + "iri", + "iri-reference", + "uuid", + ], + }, readOnly: extra.optionsSchema.readOnly, isRequired: extra.optionsSchema.isRequired, }, @@ -1061,6 +1086,93 @@ const simple = { }, }, }, + email: { + title: "Email", + icon: , + description: "Email field supporting validation", + className: "tour-email-field", + child: {}, + optionsSchema: { + type: "object", + title: "Text Schema", + properties: { + ...common.optionsSchema, + pattern: { + title: "Validation regex", + description: + "The input will be validated against this regex on form submission", + type: "string", + format: "regex", + }, + readOnly: extra.optionsSchema.readOnly, + isRequired: extra.optionsSchema.isRequired, + }, + }, + optionsSchemaUiSchema: { + ...common.optionsSchemaUiSchema, + readOnly: extra.optionsSchemaUiSchema.readOnly, + isRequired: extra.optionsSchemaUiSchema.isRequired, + pattern: { + "ui:placeholder": "^.*$", + }, + }, + optionsUiSchema: { + type: "object", + title: "UI Schema", + properties: { + "ui:options": { + type: "object", + title: "UI Options", + dependencies: + common.optionsUiSchema.properties["ui:options"].dependencies, + properties: { + ...common.optionsUiSchema.properties["ui:options"].properties, + suggestions: { + type: "string", + title: "Add a suggestion URL endpoint", + description: "Provide an URL endpoint, to fetch data from there", + }, + convertToUppercase: { + type: "boolean", + title: "Convert input to uppercase", + }, + mask: { + type: "string", + title: "Input mask", + tooltip: + "Add a mask to visualize and limit the format of the input. Use the following format: `0` (number), `a` (lowercase letter), `A` (uppercase letter), `*` (letter or number). You can escape all these with `\\`. The rest of the characters will be treated as constants", + }, + }, + }, + "ui:label": common.optionsUiSchema.properties["ui:label"], + }, + }, + optionsUiSchemaUiSchema: { + ...common.optionsUiSchemaUiSchema, + "ui:options": { + ...common.optionsUiSchemaUiSchema["ui:options"], + mask: { + "ui:placeholder": "BN-000/aa", + "ui:options": { + descriptionIsMarkdown: true, + tooltipIsMarkdown: true, + }, + }, + convertToUppercase: { + "ui:widget": "switch", + }, + }, + }, + default: { + schema: { + type: "string", + format: "email", + }, + uiSchema: { + "ui:widget": "text", + }, + }, + }, }; const advanced = { diff --git a/src/admin/utils/index.jsx b/src/admin/utils/index.jsx index da3d855..ac56b82 100644 --- a/src/admin/utils/index.jsx +++ b/src/admin/utils/index.jsx @@ -26,7 +26,7 @@ export const _validate = function (formData, errors) { }; export const shoudDisplayGuideLinePopUp = (schema) => { - return schema.properties && Object.keys(schema.properties).length === 0; + return schema?.properties && Object.keys(schema?.properties).length === 0; }; export const isItTheArrayField = (schema, uiSchema) => { diff --git a/src/index.ts b/src/index.ts index b89f7fa..361514a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,6 +10,7 @@ export { FormuleForm } from "./exposed"; export { default as PropertyEditor } from "./admin/components/PropertyEditor"; export { default as SelectFieldType } from "./admin/components/SelectFieldType"; +export { default as SchemaCodeEditor } from "./admin/components/SchemaCodeEditor"; export { default as SchemaPreview } from "./admin/components/SchemaPreview"; export { default as FormPreview } from "./admin/components/FormPreview"; export { default as EditablePreview } from "./admin/components/EditablePreview";