2.3.1 • Published 3 years ago

@react-declarative-form/core v2.3.1

Weekly downloads
395
License
MIT
Repository
github
Last release
3 years ago

Overview

@react-declarative-form/core is the core of a simple-to-use declarative valadation library. It is designed to make building forms easy; allowing consumers to specify validator requirements on individual form components, and let the library do the heavy lifting.

Requirements

This library requires the following:

  • The <Form /> component from @react-declarative-form/core is used instead of a html <form/>.
  • Managed form components have been created using the bind() HOC. See Example: Form Binding HOC.
  • The managed form components are descendants of a <Form /> component. See Example: Form Usage.

Getting started

  1. Add @react-declarative-form/core to your project.
    yarn add @react-declarative-form/core
  2. Create bound form components using bind() HOC
  3. Use bound form components within a <Form /> component.

How does it work?

Each bound component registers itself with the closest <Form /> ancestor which allows the form to manage the value, pristine & validator state. This is done using the React 16.3 context API. While the form manages the component state, each component is updated individually on an as needed basis to minimise unnecessary renders. If a components value is changed, then only that component will be re-rendered, assuming it is not part of a validator trigger. However, if the component does belong to a validator trigger, then the related components will also be validated and re-rendered. When the setValue function is called, the component is validated using the new value.

Table of Contents

Documentation

Form component

<Form> is a component that is to be used in place of a regular HTML form component. It supports event handlers as props, and programmatic control (accessed via ref).

Form Props

NameTypeRequiredDescription
onChange(componentName: string, value: any) => voidfalseCalled when the value of a bound form component has been changed. The new value is provided.
onBlur(componentName: string, value: any) => voidfalseCalled when a bound form component has been blurred. The current value is provided.
onFocus(componentName: string, value: any) => voidfalseCalled when a bound form component has been focused. The current value is provided.
onSubmit(values: ValueMap) => voidfalseCalled when the form is programmatically submitted, or a button with type="submit" is clicked. The current values for all bound form components are provided.
onValidSubmit(values: ValueMap) => voidfalseCalled after onSubmit if all bound form components are valid. The current values for all bound form components are provided.
onInvalidSubmit(values: ValueMap) => voidfalseCalled after onSubmit at least 1 bound form component is invalid. The current values for all bound form components are provided.
withHiddenSubmitbooleanfalseWhether a hidden submit button should be included in the form.
stickybooleanfalseWhether the form component values should be sticky and retain their value in between component unmounts and mounts.
initialValuesValueMapfalseInitial values to be provided to the bound form components. This is useful for populating the form without having to manage all form values.

Form API

Func: submit

submit(): { isValid: boolean, values: ValueMap }

Programatically submit the form. If you don't want to manually call this, a button with type submit should be provided to the form. This can be provided in your form implementation, or automatically using the withHiddenSubmit prop on the Form.

Returns: an object with 2 properties:

  • isValid: whether the entire form was valid when submitting
  • values: all of the form values at the time of submission
Func: clear

clear(componentName?: string | string[]): Promise<void>

Clears the specified component(s) by setting their value to null. If no component names are provided, all components within the form will be cleared.

Params:

  • componentName: component name(s) to be cleared

Returns: a promise which is resolved once the react components have been re-rendered

Func: reset

reset(componentName?: string | string[]): Promise<void>

Resets the specified component(s) by unsetting their value, validator and pristine state. If no component names are provided, all components within the form will be reset.

Params:

  • componentName: component name(s) to be reset

Returns: a promise which is resolved once the react components have been re-rendered

Func: validate

validate(componentName?: string | string[]): Promise<void>

Validates specified component(s) by executing the validator and updating the components to reflect their validator state. If no component names are provided, all components within the form will be validated.

Params:

  • componentName: component name(s) to be reset

Returns: a promise which is resolved once the react components have been re-rendered.

Func: isValid

isValid(componentName?: string | string[]): boolean

Determines if all the specified component(s) are valid by executing the validator using the components current value. If no component names are provided, all components within the form will be tested.

Params:

  • componentName: component name(s) to be tested

Returns: a boolean flag to indicate whether all the components are valid

Note: if validatorData is being managed, the provided validatorData.context will be used instead of executing the validator.

Func: isPristine

isPristine(componentName?: string | string[]): boolean

Determines if all the specified component(s) are pristine - the component has not been modified by the user or by programatically calling setValue. If no component names are provided, all components within the form will checked.

Params:

  • componentName: component name(s) to be tested

Returns: a boolean flag to indicate whether all the components are pristine

Func: getValidatorData

getValidatorData(componentName: string): ValidatorData

Returns the components current validatorData. There are 2 ways a components validator data can be retrieved (in order of precedence): 1. externally managed validatorData prop provided to the component 2. internally managed validatorData state when the user changes input

Params:

  • componentName: name of the component to get validator data for

Returns: component validator data if exists, otherwise an object with undefined context and message will be returned.

Func: getValue

getValue(componentName: string): any

Returns the value of the specified component. There are four ways a component value can be provied (in order of precedence): 1. externally managed value prop provided to the component 2. internally managed state value when the user changes input 3. initialValues provided to the form component 4. defaultValue specified on individual form component

Params:

  • componentName: name of the component to get value for

Returns: component value

Note: the form values should not be mutated

Func: getValues

getValues(componentNames?: string[]): ValueMap

Gets the values of the provided component names using the same logic as getValue.

Params:

  • componentNames: component names to retrieve values for

Returns: an object with componentName:value pairs

Func: setValidatorData

setValidatorData(componentName: string, data: ValidatorData): Promise<void>

Sets the component internally managed validatorData & updates the component to reflect its new state.

Params:

  • componentName: name of the component which should be updated
  • validatorData: the new validator data to be stored in Form state

Returns: a promise which is resolved once the react component has been re-rendered.

Func: setValue

setValue(componentName: string, value: any, pristine: boolean = false): Promise<void>

Sets the component internally managed state value & updates the component validatorData using the provided value. By default, the components pristine state will be set to false to indicate that the component has been modified.

Params:

  • componentName: name of the component to set value for
  • value: the new value to be stored in Form state
  • pristine: the new pristine state when setting this value (default: false).

Returns: a promise which is resolved once the react component has been re-rendered.

Func: setValues

setValues(values: ValueMap, pristine?: boolean): Promise<void>

Sets the components internally managed state values & updates their component validatorData using the provided values. By default, the components pristine state will be set to false to indicate that the components have been modified.

Params:

  • values: the values to be saved in Form state (componentName:value map)

Returns: a promise which is resolved once the react components have been re-rendered.

bind HOC

Using the bind() higher order component allows @react-declarative-form/core to manage the form component value and validator state. Refer to Example: Form Binding HOC to get a better understanding of how these props can be used.

Bound component props

These props will be available to the bound component. The injected variables will always be provided to the bound component, even if the the the consumer did not provide them. However, a number of these props are overridable, meaning the consumer can override the HOC provided value.

NameTypeRequiredInjectedOverridableDescription
namestringtruefalsetrueUnique form component identifier
requiredbooleanfalsefalsetrueWhether or not a value is required
pristinebooleanfalsetruetrueWhether or not the value has been modified
validatorDataValidatorDatafalsetruetrueData which reflects the current validator state for the component.
onBlurReact.EventHandlerfalsetruetrueShould be called when component has been blurred
onFocusReact.EventHandlerfalsetruetrueShould be called when component has been focused
valueanyfalsetruetrueCurrent form component value
setValue(value: any) => void-truefalseShould be called when component value has changed

Notes 1. ValidatorData is an object: { message: string, context: ValidatorContext }. 2. ValidatorContext is an enum which expects strings: danger, warning & success. 3. setValue can not be overridden, providing it will have no effect.

Higher order component props

These props are only used by the HOC and are not passed to the wrapped component.

NameTypeRequiredDescription
validatorRulesValidatorRulesfalseValidation rules which should be applied to the component
validatorMessagesanyfalseCustom validator messages for specific validator rules
validatorTriggerstringfalseTriggers validator to execute on the specified component names when this component is modified
defaultValueanyfalseDefault value to be applied if the component does not have a managed, state or initial value

bind HOC API

Func: clear

clear(): Promise<void>

Clears the scomponent by setting its value to null.

Returns: a promise which is resolved once the react component has been re-rendered

Func: reset

reset(): Promise<void>

Resets the component by unsetting its value, validator and pristine state.

Returns: a promise which is resolved once the react component has been re-rendered

Func: validate

validate(): Promise<void>

Validates the component by executing the validator and updating the component to reflect its new validator state. If no component names are provided,

Returns: a promise which is resolved once the react component has been re-rendered

Func: isValid

isValid(): boolean

Determines if the component is valid by executing the validator using the components current value.

Returns: a boolean flag to indicate whether the component is valid

Func: isPristine

isPristine(): boolean Determines if the component is pristine - the component has not been modified by the user or by programatically calling setValue.

Returns: a boolean flag to indicate whether the component is pristine

Func: getValidatorData

getValidatorData(): ValidatorData

Returns the components current validatorData. There are 2 ways a components validator data can be retrieved (in order of precedence): 1. externally managed validatorData prop provided to the component 2. internally managed validatorData state when the user changes input

Returns: component validator data

*Note: If the component has no validatorData, then an object with undefined context & message will be returned.*

Func: getValue

getValue(): any

Returns the value of the component. There are four ways a component value can be provied (in order of precedence): 1. externally managed value prop provided to the component 2. internally managed state value when the user changes input 3. initialValues provided to the form component 4. defaultValue specified on individual form component

Returns: component value

*Note: the form values should not be mutated*

Func: setValidatorData

setValidatorData(data: ValidatorData): Promise<void>

Sets the component internally managed validatorData & updates the component to reflect its new state.

Params:

  • validatorData the new validator data to be stored in Form state

Returns: a promise which is resolved once the react component has been re-rendered.

Func: setValue

setValue(value: any, pristine?: boolean): Promise<void>

Sets the component internally managed state value & updates the component validatorData using the provided value. By default, the components pristine state will be set to false to indicate that the component has been modified.

Params:

  • value the new value to be stored in Form state
  • pristine the new pristine state when setting this value (default: false).

Returns: a promise which is resolved once the react component has been re-rendered.

Validator rules

Validator rules are executed sequentially (in the order in which they are defined) until validator data has been returned, or all rules have been executed. If no rule has returned validator data, then a data object with Success context will be returned.

Built-in validator rules

The following validator rules are built-in. By default, they will only return ValidatorContext.Danger if the a value is defined and it fails to pass the test. However, this behaviour can be customized by overriding built-in rules. See the Adding additional validator rules section for more information. Additional built-in validator rules will be added in the future.

NameCriteriaDescription
requiredbooleanWhether the input is required
equalsnumber | stringInput is === to the specified value
notEqualsnumber | stringInput is !== to the specified value
greaterThannumberInput is > than the specified value
lessThannumberInput is < than the specified value
minValuenumberInput is >= to the specified value
maxValuenumberInput is <= to the specified value
isDivisibleBynumberInput is divisible by the specified number
isIntegerbooleanInput is an integer
isDecimalbooleanInput is a decimal number
isNumericbooleanInput is numeric characters only 0-9+
minLengthnumberInput length is at least the specified length
maxLengthnumberInput length is at most the specified length
isLengthnumberInput length equals the specified length
isLowercasebooleanInput is all lowercase characters
isUppercasebooleanInput is all uppercase characters
matchesRegExpInput matches the specified regex pattern
isEmailbooleanInput is a valid email address
isUrlbooleanInput is a valid url
isCreditCardbooleanInput is a valid credit card number
isHexColorbooleanInput is a valid hexadecimal color
isIpbooleanInput is a valid IPv4 or IPv6 address
isPortbooleanInput is a valid port number
eqTargetstringInput value is == to target input value
gtTargetstringInput value is > to target input value
gteTargetstringInput value is >= to target input value
ltTargetstringInput value is < to target input value
lteTargetstringInput value is <= to target input value
customValidatorRuleCustom validator rule. It is executed before other rules

*Note: using the custom key allows consumers to define a custom validator rule. This is useful when one-off custom validator logic is required. Ideally, validator rules should be designed for reusability.*

Adding additional validator rules

Adding additional validator rules can be done using the addValidatorRule function. An example can be found in Example: Adding validator rules.

addValidatorRule: (key: string, rule: ValidatorRule) => void

*Note: If a rule with the same key exists already, it will be overridden - this includes built-in rules.*

Types

enum ValidatorContext {
    Danger = 'danger',
    Warning = 'warning',
    Success = 'success',
}

type ValidatorRule = (
    key: string,
    values: ValueMap,
    criteria?: any,
) => ValidatorData;

interface ValidatorRuleMap {
    readonly [name: string]: ValidatorRule;
}

interface ValidatorData {
    readonly name?: string;
    readonly context: ValidatorContext;
    readonly message?: string;
}

interface ValueMap {
    [name: string]: any;
}

Examples

Example: Adding validator rules

// validatorConfig.ts
import {
    addValidatorRule,
    isDefined,
    ValidatorContext,
    ValueMap,
} from '@react-declarative-form/core';

addValidatorRule(
    'containsCat',
    (componentName: string, values: ValueMap, criteria: boolean) => {
        const value = values[componentName];
        const pattern = /\bcat\b/i;

        if (isDefined(value) && !pattern.test(value)) {
            return {
                context: ValidatorContext.Danger,
                message: 'Needs more Cat.',
            };
        }
    },
);

Example: Form Binding HOC

Using the bind HOC with the TextField component provided by material-ui

import * as React from 'react';
import {
    bind,
    BoundComponentProps,
    ValidatorContext,
} from '@react-declarative-form/core';
import  {
    TextField as MaterialTextField
    TextFieldProps as MaterialTextFieldProps
} from '@material-ui/core';

export interface TextFieldProps
    extends MaterialTextFieldProps, BoundComponentProps
{
    name: string;
    label?: string;
}

export class UnboundTextField extends React.Component<TextFieldProps> {
    public render() {
        const {
            name,
            value,
            setValue,
            onChange,
            validatorData,
            pristine,
            ...restProps
        } = this.props;

        const hasError = validatorData.context === ValidatorContext.Danger;

        return (
            <MaterialTextField
                {...restProps}
                id={name}
                name={name}
                value={value || ''}
                onChange={this.handleChange}
                error={!pristine && hasError}
                helperText={!pristine && validatorData.message}
            />
        );
    }

    private handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
        const { onChange, setValue } = this.props;
        setValue(event.currentTarget.value);
        if (onChange) onChange(event);
    };
}

export const TextField = bind<TextFieldProps>(UnboundTextField);

Example: Form Usage

Using the bound TextField component inside a Form.

import * as React from 'react';
import { Form, ValueMap } from '@react-declarative-form/core';
import { Button, TextField } from 'view/components';

export interface RegistrationFormProps {}

export class RegistrationForm extends React.Component<RegistrationFormProps> {
    public render() {
        return (
            <Form onValidSubmit={this.handleValidSubmit}>
                <TextField
                    name="email"
                    label="Email"
                    validatorRules={{
                        isEmail: true,
                    }}
                    required
                />
                <TextField
                    name="password"
                    label="Password"
                    validatorTrigger={['password-confirm']}
                    validatorRules={{
                        minLength: 8,
                    }}
                    type="password"
                    required
                />
                <TextField
                    name="password-confirm"
                    label="Confirm password"
                    validatorRules={{
                        eqTarget: 'password',
                    }}
                    validatorMessages={{
                        eqTarget: 'Must match password',
                    }}
                    type="password"
                    required
                />
                <TextField
                    name="favourite-animal"
                    label="Favourite animal"
                    validatorRules={{
                        // Custom validator rule, see: "Adding custom validator rules"
                        containsCat: true,
                    }}
                    required
                />
                <Button title="Submit" type="submit" />
            </Form>
        );
    }

    private handleValidSubmit = (values: ValueMap) => {
        console.log('Successfully submitted form :)', values);
    };
}

Authors

2.3.0

3 years ago

2.3.1

3 years ago

2.3.0-alpha.17

3 years ago

2.3.0-alpha.16

4 years ago

2.3.0-alpha.15

4 years ago

2.2.5-alpha.14

4 years ago

2.2.4

4 years ago

2.2.3

4 years ago

2.2.2

4 years ago

2.2.1

4 years ago

2.2.0

5 years ago

2.1.1

5 years ago

2.1.0-alpha.13

5 years ago

2.1.0-alpha.12

5 years ago

2.1.1-alpha.11

5 years ago

2.1.0-alpha.11

5 years ago

2.1.0-alpha.10

5 years ago

2.1.0-alpha.8

5 years ago

2.1.0-alpha.7

5 years ago

2.1.0-alpha.6

5 years ago

2.1.0-alpha.5

5 years ago

2.1.0-alpha.4

5 years ago

2.1.0-alpha.3

5 years ago

2.1.0-alpha.2

5 years ago

2.1.0-alpha.1

5 years ago

2.1.0

5 years ago

2.0.4

6 years ago

2.0.3

6 years ago

2.0.2

6 years ago

2.0.1

6 years ago

2.0.0

6 years ago

2.0.0-alpha.5

6 years ago

2.0.0-alpha.4

6 years ago

2.0.0-alpha.3

6 years ago

2.0.0-alpha.2

6 years ago

2.0.0-alpha.1

6 years ago

2.0.0-alpha.0

6 years ago

1.1.11

6 years ago

1.1.10

6 years ago

1.1.9

6 years ago

1.1.7

6 years ago

1.1.6

6 years ago

1.1.5

6 years ago

1.1.4

6 years ago

1.1.3

6 years ago

1.1.2

6 years ago

1.1.1

6 years ago

1.1.0

6 years ago

1.0.22

6 years ago

1.0.21

6 years ago

1.0.20

6 years ago

1.0.19

6 years ago

1.0.18

6 years ago

1.0.17

6 years ago

1.0.17-alpha.5

6 years ago

1.0.17-alpha.4

6 years ago