Interface FormCustomFieldDefinition<TFormValues, TCustomFieldsKey, TLabelType>

Definition for a custom field to place inside of a form dynamically

Usage in design system: In the design system for forms, extend the form's form data interface with a customFields key (can be any key name, but customFields is a good standard):

interface XFormData<TFormCustomFields = never> {
...
customFields?: TFormCustomFields;
}

Then, extend the form's props with props that let you define custom fields:

interface XFormProps<TFormCustomFields = never> {
...
customFields?: {
foo?: FormCustomFieldDefinition<
XFormData<TFormCustomFields>,
string
>[];
bar?: FormCustomFieldDefinition<
XFormData<TFormCustomFields>,
string
>[];
};
...
defaultValues?: XFormData<TFormCustomFields>;
...
onSubmit: (data: XFormData<TFormCustomFields>) => void;
}

Notice how instead of placing the list of custom fields on the top level, they are placed under a key (foo) that represents the location in the form where they should be placed. This is to allow for multiple locations in the form where custom fields can be placed.

To make the form accept generics, you will need to change it to a non-anonymous function and add a generic type parameter with TFormCustomFields:

export const XForm = function <TFormCustomFields>({
...
}: XFormProps<TCustomFieldValues>): React.ReactElement { ... }

In the form component, you can render the custom fields by calling the renderFormCustomFields function.

 renderFormCustomFields(
props.customFields?.foo,
'customFields',
control
)

Note that the first parameter is the list of custom fields to render, and the second parameter is the key in the form data where the custom fields should be placed.

Usage in Blocks:

In your createXBlock function, add a generic type parameter with TFormCustomFields. This will be defined by the user when using custom fields in the block.

interface XBlockOptions<TFormCustomFields = never> {
view? {
...
formCustomFields?: {
foo: FormCustomFieldDefinition<
XFormData<TFormCustomFields>,
'customFields',
TCallback
>[];
};
}
}

To translate the definition labels from TCallbacks to strings, you can use the translateFormCustomFieldLabels function (defined separately in the i18n folder).

const translatedCustomFields = useMemo(() => {
return {
foo: translateFormCustomFieldLabels(
blockOptions.view?.formCustomFields?.foo,
t
}
}
}, [t]);

Then, finally, you can render the custom fields in the block view by passing them to the design system prop you made earlier.

<XForm
customFields={translatedCustomFields}
...
/>
interface FormCustomFieldDefinition<TFormValues, TCustomFieldsKey, TLabelType> {
    checkboxFieldOptions?: {
        cols?: 1 | 2 | 4;
        fill?: "fill" | "outline";
    };
    combinedRadioSelectOptions?: {
        otherItemsLabel: TLabelType;
        priorityItemValue: string;
    };
    dependentSelectOptions?: {
        childKey: string;
        childLabel: TLabelType;
        childLabelOptions?: FormCustomFieldLabelOptions<TLabelType>;
        mappedSelectValues: Record<string, FormCustomFieldDefinitionValues<TLabelType>[]>;
        parentKey: string;
        parentSelectValues: FormCustomFieldDefinitionValues<TLabelType>[];
    };
    inputType: "select" | "textarea" | "time" | "text" | "date" | "checkboxField" | "dependentSelects" | "combinedRadioSelect";
    label: TLabelType;
    labelOptions?: FormCustomFieldLabelOptions<TLabelType>;
    name: Path<TFormValues[TCustomFieldsKey]>;
    rules?: {
        max?: number | {
            message: TLabelType;
            value: number;
        };
        maxLength?: number | {
            message: TLabelType;
            value: number;
        };
        min?: number | {
            message: TLabelType;
            value: number;
        };
        minLength?: number | {
            message: TLabelType;
            value: number;
        };
        pattern?: RegExp | {
            message: TLabelType;
            value: RegExp;
        };
        required?: boolean | {
            message: TLabelType;
            value: boolean;
        };
        validate?: ((value, formValues) => undefined | string | boolean | string[] | Promise<ValidateResult>);
    };
    selectOptions?: {
        initialScrollValue?: string;
    };
    values?: FormCustomFieldDefinitionValues<TLabelType>[];
}

Type Parameters

  • TFormValues

  • TCustomFieldsKey extends keyof TFormValues

  • TLabelType

Properties

checkboxFieldOptions?: {
    cols?: 1 | 2 | 4;
    fill?: "fill" | "outline";
}

Options for CheckboxField type

Type declaration

  • Optional cols?: 1 | 2 | 4
  • Optional fill?: "fill" | "outline"
combinedRadioSelectOptions?: {
    otherItemsLabel: TLabelType;
    priorityItemValue: string;
}

Options for CombinedRadioSelect type

Type declaration

  • otherItemsLabel: TLabelType
  • priorityItemValue: string
dependentSelectOptions?: {
    childKey: string;
    childLabel: TLabelType;
    childLabelOptions?: FormCustomFieldLabelOptions<TLabelType>;
    mappedSelectValues: Record<string, FormCustomFieldDefinitionValues<TLabelType>[]>;
    parentKey: string;
    parentSelectValues: FormCustomFieldDefinitionValues<TLabelType>[];
}

Type declaration

inputType: "select" | "textarea" | "time" | "text" | "date" | "checkboxField" | "dependentSelects" | "combinedRadioSelect"

Type for the field (as matching backend)

  • text -> maps to string on backend
  • textarea -> maps to string on backend
  • select -> maps to string or enum on backend
  • checkboxField -> maps to multiselect-enum on backend
  • dependentSelects -> maps to dependent-enum on backend
  • combinedRadioSelect -> maps to string or enum on backend
  • date -> maps to date on backend
label: TLabelType

Human readable label for the field

Options for the text label

Internal name for the field (used in form state)

rules?: {
    max?: number | {
        message: TLabelType;
        value: number;
    };
    maxLength?: number | {
        message: TLabelType;
        value: number;
    };
    min?: number | {
        message: TLabelType;
        value: number;
    };
    minLength?: number | {
        message: TLabelType;
        value: number;
    };
    pattern?: RegExp | {
        message: TLabelType;
        value: RegExp;
    };
    required?: boolean | {
        message: TLabelType;
        value: boolean;
    };
    validate?: ((value, formValues) => undefined | string | boolean | string[] | Promise<ValidateResult>);
}

Validation properties for the custom field

Type declaration

  • Optional max?: number | {
        message: TLabelType;
        value: number;
    }
  • Optional maxLength?: number | {
        message: TLabelType;
        value: number;
    }
  • Optional min?: number | {
        message: TLabelType;
        value: number;
    }
  • Optional minLength?: number | {
        message: TLabelType;
        value: number;
    }
  • Optional pattern?: RegExp | {
        message: TLabelType;
        value: RegExp;
    }
  • Optional required?: boolean | {
        message: TLabelType;
        value: boolean;
    }
  • Optional validate?: ((value, formValues) => undefined | string | boolean | string[] | Promise<ValidateResult>)
      • (value, formValues): undefined | string | boolean | string[] | Promise<ValidateResult>
      • Parameters

        Returns undefined | string | boolean | string[] | Promise<ValidateResult>

selectOptions?: {
    initialScrollValue?: string;
}

Extra options for select inputs

Type declaration

  • Optional initialScrollValue?: string

    When opening the pulldown, which value in the list to scroll to (top of the list by default)

List of values Required for the following types:

  • Select
  • CheckboxField
  • Combined Radio + Selects

Generated using TypeDoc