trezy/firebase-system-update

View on GitHub
src/components/Form/FieldRadioGroup.js

Summary

Maintainability
C
1 day
Test Coverage
// Module imports
import {
    useCallback,
    useEffect,
} from 'react'
import classnames from 'classnames'
import PropTypes from 'prop-types'





// Local imports
import { useForm } from 'components/Form'





function FieldRadioGroup(props) {
    const {
        className,
        id,
        isDisabled,
        isMultiselect,
        isRequired,
        options,
    } = props
    const {
        errors: formErrors,
        updateValidity,
        updateValue,
        values,
    } = useForm()

    const validate = useCallback(async (state, initialProps) => {
        const errors = []

        if (initialProps.isRequired && !state) {
            errors.push('Field is required')
        }

        if (typeof initialProps.validate === 'function') {
            const customError = await initialProps.validate(state, values)
            if (customError) {
                errors.push(customError)
            }
        }

        updateValidity(id, errors)
    }, [
        updateValidity,
        values,
    ])

    const handleChange = useCallback(event => {
        let value = event.target.value

        if (isMultiselect) {
            let optionValues = new Set(values[id])

            if (event.target.checked) {
                optionValues.add(value)
            } else {
                optionValues.delete(value)
            }

            const optionValuesArray = Array.from(optionValues)
            updateValue(id, optionValuesArray)
            validate(optionValuesArray, props)
        } else {
            updateValue(id, value)
            validate(value, props)
        }
    }, [
        id,
        isMultiselect,
        props,
        updateValue,
        validate,
    ])

    const mapOption = useCallback(([optionValue, details]) => {
        const {
            component,
            description,
            label,
        } = details

        const optionID = `${id}::${optionValue}`

        return (
            <li
                className={classnames('radio-option', className)}
                key={optionID}>
                <label className="columns is-gapless">
                    <div className="column is-narrow">
                        {isMultiselect && (
                            <input
                                checked={values[id]?.includes(optionValue) || false}
                                className="checkbox"
                                disabled={isDisabled}
                                id={optionID}
                                name={id}
                                onChange={handleChange}
                                type="checkbox"
                                value={optionValue} />
                        )}

                        {!isMultiselect && (
                            <input
                                checked={optionValue === values[id]}
                                className="radio"
                                disabled={isDisabled}
                                id={optionID}
                                name={id}
                                onChange={handleChange}
                                type="radio"
                                value={optionValue} />
                        )}
                    </div>
                    <div className="column">
                        <div className="content">
                            <strong>{label}</strong><br />
                            {Boolean(description) && (
                                <p>{description}</p>
                            )}
                        </div>
                    </div>
                </label>
            </li>
        )
    }, [
        handleChange,
        id,
        isDisabled,
    ])

    useEffect(() => {
        // Mark empty, non-required fields as valid
        if (!isRequired && (!values[id] || (isMultiselect && !values[id].length))) {
            updateValidity(id, [])

        // Run a validity check against a field's initial state if it's non-empty
        } else if (isRequired && (values[id] !== '' || values[id] !== [])) {
            validate(values[id], props)
        }
    }, [])

    return (
        <div className={classnames(className, 'control')}>
            <ul className="radio-group">
                {Object.entries(options).map(mapOption)}
            </ul>
        </div>
    )
}

FieldRadioGroup.defaultProps = {
    className: null,
    isDisabled: false,
    isMultiselect: false,
    isRequired: false,
    options: {},
    validate: () => {},
}

FieldRadioGroup.propTypes = {
    className: PropTypes.string,
    id: PropTypes.string.isRequired,
    isDisabled: PropTypes.bool,
    isMultiselect: PropTypes.bool,
    isRequired: PropTypes.bool,
    options: PropTypes.object,
    validate: PropTypes.func,
}

export { FieldRadioGroup }