Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions formule-demo/cypress/e2e/builder.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
20 changes: 10 additions & 10 deletions formule-demo/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import {
} from "antd";
import { useEffect, useState } from "react";
import {
CodeViewer,
SchemaCodeEditor,
FormPreview,
FormuleContext,
SchemaPreview,
Expand Down Expand Up @@ -331,11 +331,11 @@ const App = () => {
}}
>
<Typography.Text strong>Schema</Typography.Text>
<CodeViewer
<SchemaCodeEditor
value={JSON.stringify(formuleState?.current.schema, null, 2)}
lang="json"
height="45vh"
reset
valueType="schema"
/>
</Col>
<Col
Expand All @@ -346,15 +346,15 @@ const App = () => {
}}
>
<Typography.Text strong>UI Schema</Typography.Text>
<CodeViewer
<SchemaCodeEditor
value={JSON.stringify(
formuleState?.current.uiSchema,
null,
2
2,
)}
lang="json"
height="25vh"
reset
valueType="uiSchema"
/>
</Col>
<Col
Expand All @@ -365,11 +365,11 @@ const App = () => {
}}
>
<Typography.Text strong>Form data</Typography.Text>
<CodeViewer
<SchemaCodeEditor
value={JSON.stringify(formuleState?.formData, null, 2)}
lang="json"
height="25vh"
reset
isReadOnly
/>
</Col>
</Row>
Expand Down Expand Up @@ -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) {
Expand All @@ -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",
);
}
};
Expand Down
37 changes: 37 additions & 0 deletions src/admin/components/SchemaCodeEditor.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<CodeEditor
lint
lang={lang}
isEditable={!isReadOnly}
initialValue={value}
schema
height={height}
handleEdit={_handleEdit}
/>
);
};

export default SchemaCodeEditor;
112 changes: 112 additions & 0 deletions src/admin/utils/fieldTypes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand Down Expand Up @@ -1061,6 +1086,93 @@ const simple = {
},
},
},
email: {
title: "Email",
icon: <FontSizeOutlined />,
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 = {
Expand Down
2 changes: 1 addition & 1 deletion src/admin/utils/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down