nexxtway/react-rainbow

View on GitHub
src/components/VisualPicker/index.js

Summary

Maintainability
C
1 day
Test Coverage
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import withReduxForm from '../../libs/hocs/withReduxForm';
import RenderIf from '../RenderIf';
import Label from '../Input/label';
import { uniqueId } from '../../libs/utils';
import { Provider } from './context';
import StyledContainer from './styled/container';
import StyledOptionsContainer from './styled/optionsContainer';
import StyledError from '../Input/styled/errorText';

/**
 * A VisualPicker can be either radio buttons, checkboxes, or links that are visually enhanced.
 * @category Form
 */
class VisualPicker extends Component {
    constructor(props) {
        super(props);
        this.errorId = uniqueId('error-message');
        this.groupNameId = props.name || uniqueId('visual-picker');
        this.handleChange = this.handleChange.bind(this);
    }

    getErrorMessageId() {
        const { error } = this.props;
        if (error) {
            return this.errorId;
        }
        return undefined;
    }

    handleChange(optionName, isChecked) {
        const { onChange, multiple, value } = this.props;
        let currentValue = optionName;
        if (multiple) {
            if (!Array.isArray(value)) {
                currentValue = isChecked ? [optionName] : [];
            } else {
                currentValue = isChecked
                    ? [...value, optionName]
                    : value.filter(item => item !== optionName);
            }
        }
        onChange(currentValue);
    }

    render() {
        const {
            style,
            label,
            labelAlignment,
            hideLabel,
            required,
            error,
            id,
            children,
            value,
            multiple,
            className,
            size,
        } = this.props;
        const context = {
            ariaDescribedby: this.getErrorMessageId(),
            groupName: this.groupNameId,
            privateOnChange: this.handleChange,
            value,
            multiple,
            size,
        };

        return (
            <StyledContainer id={id} className={className} style={style}>
                <RenderIf isTrue={label}>
                    <Label
                        label={label}
                        labelAlignment={labelAlignment}
                        hideLabel={hideLabel}
                        required={required}
                        as="legend"
                    />
                </RenderIf>
                <StyledOptionsContainer>
                    <Provider value={context}>{children}</Provider>
                </StyledOptionsContainer>
                <RenderIf isTrue={error}>
                    <StyledError id={this.getErrorMessageId()}>{error}</StyledError>
                </RenderIf>
            </StyledContainer>
        );
    }
}

VisualPicker.propTypes = {
    /** The name of VisualPicker. */
    name: PropTypes.string,
    /** The value of the element. */
    value: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
    /** The id of the outer element. */
    id: PropTypes.string,
    /** The action triggered when a value attribute changes. */
    onChange: PropTypes.func,
    /** If is set to true the VisualPicker is required. This value defaults to false. */
    required: PropTypes.bool,
    /** The title at the top of the VisualPicker component. */
    label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
    /** Describes the position of the VisualPicker label. Options include left, center and right.
     * This value defaults to center. */
    labelAlignment: PropTypes.oneOf(['left', 'center', 'right']),
    /** A boolean to hide the VisualPicker label. */
    hideLabel: PropTypes.bool,
    /** Specifies that an VisualPicker must be filled out before submitting the form. */
    error: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
    /** The class name of the root element. */
    className: PropTypes.string,
    /** It is an object with custom style applied to the root element. */
    style: PropTypes.object,
    /** The size of the VisualPicker. Valid values are small, medium, and large.
     * This value defaults to medium. */
    size: PropTypes.oneOf(['small', 'medium', 'large']),
    /**
     * This prop that should not be visible in the documentation.
     * @ignore
     */
    children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.object]),
    /** If true then a multiple selection is allowed */
    multiple: PropTypes.bool,
};

VisualPicker.defaultProps = {
    name: null,
    value: undefined,
    id: undefined,
    onChange: () => {},
    required: false,
    label: null,
    labelAlignment: 'center',
    hideLabel: false,
    error: null,
    className: undefined,
    style: undefined,
    size: 'medium',
    children: [],
    multiple: false,
};

export default withReduxForm(VisualPicker);