Back to Answer

Schema Form

ui/src/components/SchemaForm/README.md

2.0.06.4 KB
Original Source

Schema Form

Introduction

A React component capable of building HTML forms out of a JSON schema.

Usage

tsx
import React, { useState } from 'react';

import {
  SchemaForm,
  initFormData,
  JSONSchema,
  UISchema,
  FormKit,
} from '@/components';

const schema: JSONSchema = {
  title: 'General',
  properties: {
    name: {
      type: 'string',
      title: 'Name',
    },
    age: {
      type: 'number',
      title: 'Age',
    },
    sex: {
      type: 'string',
      title: 'sex',
      enum: [1, 2],
      enumNames: ['male', 'female'],
    },
  },
};

const uiSchema: UISchema = {
  name: {
    'ui:widget': 'input',
  },
  age: {
    'ui:widget': 'input',
    'ui:options': {
      type: 'number',
    },
  },
  sex: {
    'ui:widget': 'radio',
  },
};

const Form = () => {
  const [formData, setFormData] = useState(initFormData(schema));

  const formRef = useRef<{
    validator: () => Promise<boolean>;
  }>(null);

  const refreshConfig: FormKit['refreshConfig'] = async () => {
    // refreshFormConfig();
  };

  const handleChange = (data) => {
    setFormData(data);
  };

  return (
    <SchemaForm
      ref={formRef}
      schema={schema}
      uiSchema={uiSchema}
      formData={formData}
      onChange={handleChange}
      refreshConfig={refreshConfig}
    />
  );
};

export default Form;

Form Props

ts
interface FormProps {
  // Describe the form structure with schema
  schema: JSONSchema | null;
  // Describe the properties of the field
  uiSchema?: UISchema;
  // Describe form data
  formData: Type.FormDataType | null;
  // Callback function when form data changes
  onChange?: (data: Type.FormDataType) => void;
  // Handler for when a form fires a `submit` event
  onSubmit?: (e: React.FormEvent) => void;
  /**
   * Callback method for updating form configuration
   * information (schema/uiSchema) in UIAction
   */
  refreshConfig?: FormKit['refreshConfig'];
}

Form Ref

ts
export interface FormRef {
  validator: () => Promise<boolean>;
}

When you need to validate a form and get the result outside the form, you can create a FormRef with useRef and pass it to the form using the ref property.

This allows you to validate the form and get the result outside the form using formRef.current.validator().


Types Definition

JSONSchema

ts
export interface JSONSchema {
  title: string;
  description?: string;
  required?: string[];
  properties: {
    [key: string]: {
      type: 'string' | 'boolean' | 'number';
      title: string;
      label?: string;
      description?: string;
      enum?: Array<string | boolean | number>;
      enumNames?: string[];
      default?: string | boolean | number;
    };
  };
}

UISchema

ts
export interface UISchema {
  [key: string]: {
    'ui:widget'?: UIWidget;
    'ui:options'?: UIOptions;
  };
}

UIWidget

ts
export type UIWidget =
  | 'textarea'
  | 'input'
  | 'checkbox'
  | 'radio'
  | 'select'
  | 'upload'
  | 'timezone'
  | 'switch'
  | 'legend'
  | 'button';

UIOptions

ts
export type UIOptions =
  | InputOptions
  | SelectOptions
  | UploadOptions
  | SwitchOptions
  | TimezoneOptions
  | CheckboxOptions
  | RadioOptions
  | TextareaOptions
  | ButtonOptions;

BaseUIOptions

ts
export interface BaseUIOptions {
  empty?: string;
  // Will be appended to the className of the form component itself
  className?: classnames.Argument;
  class_name?: classnames.Argument;
  // The className that will be attached to a form field container
  field_class_name?: classnames.Argument;
  // Make a form component render into simplified mode
  readOnly?: boolean;
  simplify?: boolean;
  validator?: (
    value,
    formData?,
  ) => Promise<string | true | void> | true | string;
}

InputOptions

ts
export interface InputOptions extends BaseUIOptions {
  placeholder?: string;
  inputType?:
    | 'color'
    | 'date'
    | 'datetime-local'
    | 'email'
    | 'month'
    | 'number'
    | 'password'
    | 'range'
    | 'search'
    | 'tel'
    | 'text'
    | 'time'
    | 'url'
    | 'week';
}

SelectOptions

ts
export interface SelectOptions extends UIOptions {}

UploadOptions

ts
export interface UploadOptions extends BaseUIOptions {
  acceptType?: string;
  imageType?: Type.UploadType;
}

SwitchOptions

ts
export interface SwitchOptions extends BaseUIOptions {
  label?: string;
}

TimezoneOptions

ts
export interface TimezoneOptions extends UIOptions {
  placeholder?: string;
}

CheckboxOptions

ts
export interface CheckboxOptions extends UIOptions {}

RadioOptions

ts
export interface RadioOptions extends UIOptions {}

TextareaOptions

ts
export interface TextareaOptions extends UIOptions {
  placeholder?: string;
  rows?: number;
}

ButtonOptions

ts
export interface ButtonOptions extends BaseUIOptions {
  text: string;
  icon?: string;
  action?: UIAction;
  variant?: ButtonProps['variant'];
  size?: ButtonProps['size'];
}

UIAction

ts
export interface UIAction {
  url: string;
  method?: 'get' | 'post' | 'put' | 'delete';
  loading?: {
    text: string;
    state?: 'none' | 'pending' | 'completed';
  };
  on_complete?: {
    toast_return_message?: boolean;
    refresh_form_config?: boolean;
  };
}

FormKit

ts
export interface FormKit {
  refreshConfig(): void;
}

FormData

ts
export interface FormValue<T = any> {
  value: T;
  isInvalid: boolean;
  errorMsg: string;
  [prop: string]: any;
}

export interface FormDataType {
  [prop: string]: FormValue;
}

Backend API

For backend generating modal form you can return json like this.

Response

json
{
  "name": "string",
  "slug_name": "string",
  "description": "string",
  "version": "string",
  "config_fields": [
    {
      "name": "string",
      "type": "textarea",
      "title": "string",
      "description": "string",
      "required": true,
      "value": "string",
      "ui_options": {
        "placeholder": "placeholder",
        "rows": 4
      },
      "options": [
        {
          "value": "string",
          "label": "string"
        }
      ]
    }
  ]
}

reference