Skip to content

v1.2.0 Redux flow and validation refacotring

Compare
Choose a tag to compare
@leandrohsilveira leandrohsilveira released this 23 Oct 18:03
· 117 commits to master since this release

This new release brings some major lib flow refactoring aiming future Redux integration and some improvements to increase the code testability and coverage.

Library changelog

Breaking changes

  • #30 Major validation refactoring to improve code testability:

Before: to attach custom validations, you are required to provide an array of objects of a specific shape to the FormProvider component:

function App() {
    const customValidators = [{
        name: 'noadmin',
        validate: (value) => {
            if(value) return !(/^admin$/i.test(value))
            return true
        }
    }]
    return (
        <FormProvider customValidators={customValidators}>
            [...]
        </FormProvider>
    )
}

After: now you just need to extend the CustomValidator class, and provide it into an array to the FormProvider component.

import {CustomValidator} from 'react-formctrl'

class NoAdminValidator extends CustomValidator {
    
    constructor() {
        super('noadmin') // This constructor parameter defines the error message key
    }

    validate(formCtrl, props, value, files) {
        return !/^admin$/i.test(value)
    }

}

function App() {
    const customValidators = [
        new NoAdminValidator()
    ]
    return (
        <FormProvider customValidators={customValidators}>
            [...]
        </FormProvider>
    )
}

Fixes

  • #34 Field "validate" property PropType

Enhancements

  • #26 Increased code test coverage to 97%!

Postponed changes

  • #21 The last missing major feature of the library, the dynamic fields, was postponed due some difficulties with the flow, refactor, testability and coverage. This feature may be available in 1.3.0 version. Until then, use this approach:
function App() {
    const handleSubmit = person => yourApi.save(person)
    return (
        <div>
            <FormProvider>
                <FormControl name="personForm">
                    <PersonForm onSubmit={handleSubmit} />
                </FormControl>
            </FormProvider>
        </div>
    )
}

class PersonForm extends React.Component {

    constructor(props) {
        super(props)
        this.state = {
            relationships: []
        }

        this.handleAddRelationship = this.handleAddRelationship.bind(this)
        this.handleSubmit = this.handleSubmit.bind(this)
    }

    handleAddRelationship(relationship) {
        this.setState(state => ({
            relationships: state.relationships.push(relationship)
        }))
    }

    handleSubmit(person) {
        person.relationships = this.state.relationships
        this.props.onSubmit(person)
    }

    render() {
        const { formCtrl: { formName, invalid }, onSubmit } = this.props
        const { relationships } = this.state
        return (
            <Form name={formName} onSubmit={onSubmit}>
                <div>
                    <div className="col-6">
                        <h3>Person data</h3>
                        <InputField form={formName} name="name" required />
                        <InputField form={formName} name="age" type="number" />
                    </div>
                    <div className="col-6">
                        <h3>Person's relationships</h3>
                        <RelationshipTable relationships={relationships} />
                        <hr />
                        <FormControl formName="personRelationshipForm">
                            <RelationshipForm onSubmit={this.handleAddRelationship} />
                        </FormControl>
                    </div>
                    <hr />
                    <button disabled={invalid || !relationships.length} type="submit">Save person</button>
                </div>
            </Form>
        )
    }
}

function RelationshipForm({ formCtrl: { formName, invalid }, onSubmit }) {
    return (
        <Form name={formName} onSubmit={onSubmit}>
            <InputField form={formName} name="relation" required />
            <InputField form={formName} name="name" required />
            <InputField form={formName} name="age" type="number" />
            <button disabled={invalid} type="submit">Add relationship</button>
        </Form>
    )
}

function RelationshipTable({ relationships }) {
    return (
        <table>
            <thead>
                <tr>
                    <th>Relation</th>
                    <th>Name</th>
                    <th>Age</th>
                </tr>
            </thead>
            <tbody>
                {relationships.map((relationship, index) => (
                    <tr key={index}>
                        <td>{relationship.relation}</td>
                        <td>{relationship.name}</td>
                        <td>{relationship.age || ''}</td>
                    </tr>
                ))}
            </tbody>
        </table>
    )
}

Live demo changelog

Fixes

  • #27 User form example responsivity

Enhancements

  • #31 Bundle size tracker badge on README page.