1.0.16 • Published 6 years ago

react-declarative-form v1.0.16

Weekly downloads
2
License
MIT
Repository
github
Last release
6 years ago

React Declarative Form

Overview

react-declarative-form is 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 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 to your project.
    npm i react-declarative-form --save
  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, allowing for event bubbling and cross-field validator. This is done using the React 16.3 context API. The value and validator state of the wrapped components is directly controlled by the bind() HOC. Because the HOC is only concerned with its own wrapped component, then only that component will be re-rendered upon value change, 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

API

<Form/>

<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 API
submit(): void

Programmatically submit form

getValue(componentName: string): any

Retrieves the current value for a component

getValues(): ValueMap

Retrieves current values for all components

clear(): void

Clears the form. The value and validator for each form component will be set to undefined. Note: this will have no effect if the valueProp has been provided.

reset(): void

Resets the form to the initialValue prop for each form component. If the initialValue prop has not been provided, the new value will be undefined. Note: this will have no effect if the valueProp has been provided.

validate(componentName?: string | string[]): void

Validates specified component(s). If no component names are provided, all components within the form will be validated.

isValid(): boolean

Executes validator rules on all components. True will be returned if all components are valid.

setResponse(componentName: string, response: ValidatorResponse): void

Inject a custom validator response on a form component.

setResponses(responses: {[componentName: string]: ValidatorResponse}): void

Injects custom validator responses on form components.

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.

bind HOC

Using the bind() higher order component allows react-declarative-form 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.

Wrapped component props

These props will be available to the wrapped component. The injected variables will always be provided to the wrapped 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
validatorMessagestringfalsetruetrueValidator message to be displayed as help text
validatorContextValidatorContextfalsetruetrueValidator context: danger, warning, success
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

* *Note: this prop 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
validatorRulesValidatorRulesfalse-
validatorMessagesanyfalse-
validatorTriggerstringfalse-
defaultValueanyfalse-

Validator rules

Validator rules are executed sequentially (in the order in which they are defined) until a validator response has been returned, or all rules have been executed. If no rule has returned a validator response, then a response 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
minValuenumberInput is >= to the specified minimum value
maxValuenumberInput is <= to the specified maximum 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,
) => ValidatorResponse;

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

interface ValidatorResponse {
    readonly key?: 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';

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';
import MaterialTextField, {
    TextFieldProps as MaterialTextFieldProps
} from '@material-ui/core/TextField';

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

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

        const hasError = validatorContext === ValidatorContext.Danger;

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

    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';
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

1.0.16

6 years ago

1.0.15

6 years ago

1.0.14

6 years ago

1.0.13

6 years ago

1.0.12

6 years ago

1.0.11

6 years ago

1.0.10

6 years ago

1.0.9

6 years ago

1.0.8

6 years ago

1.0.7

6 years ago

1.0.6

6 years ago

1.0.5

6 years ago

1.0.4

6 years ago

1.0.3

6 years ago

1.0.2

6 years ago

1.0.1

6 years ago

1.0.0

6 years ago