Skip to content

Commit

Permalink
Admin Generator (Future): Implement possibility to use custom field v…
Browse files Browse the repository at this point in the history
…alidation functions (#1710)

Usage:
```
        {
            type: "text",
            name: "title",
            validate: { name: "validateTitle", import: "./validateTitle" },
        },
```

see also demo in this pr and it's generated code
  • Loading branch information
nsams authored Feb 19, 2024
1 parent 0ba88ac commit 6cbb9e7
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 6 deletions.
1 change: 1 addition & 0 deletions demo/admin/src/products/future/ProductForm.cometGen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const ProductForm: FormConfig<GQLProduct> = {
name: "title",
label: "Titel", // default is generated from name (camelCaseToHumanReadable)
required: true, // default is inferred from gql schema
validate: { name: "validateTitle", import: "./validateTitle" },
},
{ type: "text", name: "slug" },
{ type: "text", name: "description", label: "Description", multiline: true },
Expand Down
2 changes: 2 additions & 0 deletions demo/admin/src/products/future/generated/ProductForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import isEqual from "lodash.isequal";
import React from "react";
import { FormattedMessage } from "react-intl";

import { validateTitle } from "../validateTitle";
import { createProductMutation, productFormFragment, productQuery, updateProductMutation } from "./ProductForm.gql";
import {
GQLCreateProductMutation,
Expand Down Expand Up @@ -165,6 +166,7 @@ export function ProductForm({ id }: FormProps): React.ReactElement {
name="title"
component={FinalFormInput}
label={<FormattedMessage id="product.title" defaultMessage="Titel" />}
validate={validateTitle}
/>

<Field
Expand Down
8 changes: 8 additions & 0 deletions demo/admin/src/products/future/validateTitle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React from "react";
import { FormattedMessage } from "react-intl";

export function validateTitle(value: string) {
return value.length < 3 ? (
<FormattedMessage id="product.validate.titleMustBe3CharsLog" defaultMessage="Title must be at least 3 characters long" />
) : undefined;
}
24 changes: 22 additions & 2 deletions packages/admin/cms-admin/src/generator/future/generateFormField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,21 @@ export function generateFormField(
//TODO verify introspectionField.type is compatbile with config.type

const imports: Imports = [];

let validateCode = "";
if (config.validate) {
let importPath = config.validate.import;
if (importPath.startsWith("./")) {
//go one level up as generated files are in generated subfolder
importPath = `.${importPath}`;
}
imports.push({
name: config.validate.name,
importPath,
});
validateCode = `validate={${config.validate.name}}`;
}

let code = "";
if (config.type == "text") {
code = `
Expand All @@ -43,6 +58,7 @@ export function generateFormField(
name="${name}"
component={FinalFormInput}
label={<FormattedMessage id="${instanceGqlType}.${name}" defaultMessage="${label}" />}
${validateCode}
/>`;
} else if (config.type == "number") {
code = `
Expand All @@ -53,10 +69,11 @@ export function generateFormField(
component={FinalFormInput}
type="number"
label={<FormattedMessage id="${instanceGqlType}.${name}" defaultMessage="${label}" />}
${validateCode}
/>`;
//TODO MUI suggest not using type=number https://mui.com/material-ui/react-text-field/#type-quot-number-quot
} else if (config.type == "boolean") {
code = `<Field name="${name}" label="" type="checkbox" fullWidth>
code = `<Field name="${name}" label="" type="checkbox" fullWidth ${validateCode}>
{(props) => (
<FormControlLabel
label={<FormattedMessage id="${instanceGqlType}.${name}" defaultMessage="${label}" />}
Expand All @@ -72,6 +89,7 @@ export function generateFormField(
name="${name}"
component={FinalFormDatePicker}
label={<FormattedMessage id="${instanceGqlType}.${name}" defaultMessage="${label}" />}
${validateCode}
/>`;
} else if (config.type == "block") {
imports.push({
Expand All @@ -93,7 +111,9 @@ export function generateFormField(
code = `<Field
fullWidth
name="${name}"
label={<FormattedMessage id="${instanceGqlType}.${name}" defaultMessage="${label}" />}>
label={<FormattedMessage id="${instanceGqlType}.${name}" defaultMessage="${label}" />}
${validateCode}
>
{(props) =>
<FinalFormSelect {...props}>
${values
Expand Down
8 changes: 4 additions & 4 deletions packages/admin/cms-admin/src/generator/future/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { generateForm } from "./generateForm";
import { generateGrid } from "./generateGrid";
import { writeGenerated } from "./utils/writeGenerated";

type BlockReference = {
type ImportReference = {
name: string;
import: string;
};
Expand All @@ -21,8 +21,8 @@ export type FormFieldConfig<T> = (
// TODO | { type: "dateTime" }
| { type: "staticSelect"; values?: string[] }
| { type: "asyncSelect"; values?: string[] }
| { type: "block"; block: BlockReference }
) & { name: keyof T; label?: string; required?: boolean };
| { type: "block"; block: ImportReference }
) & { name: keyof T; label?: string; required?: boolean; validate?: ImportReference };

export type FormConfig<T extends { __typename?: string }> = {
type: "form";
Expand All @@ -41,7 +41,7 @@ export type GridColumnConfig<T> = (
| { type: "date" }
| { type: "dateTime" }
| { type: "staticSelect"; values?: string[] }
| { type: "block"; block: BlockReference }
| { type: "block"; block: ImportReference }
) & { name: keyof T; headerName?: string; width?: number };
export type GridConfig<T extends { __typename?: string }> = {
type: "grid";
Expand Down

0 comments on commit 6cbb9e7

Please sign in to comment.