appbaseio/reactivesearch

View on GitHub
packages/web/src/components/range/RangeInput.js

Summary

Maintainability
D
2 days
Test Coverage
import React, { Component } from 'react';
import types from '@appbaseio/reactivecore/lib/utils/types';
import { isEqual, getClassName } from '@appbaseio/reactivecore/lib/utils/helper';

import RangeSlider from './RangeSlider';
import Input from '../../styles/Input';
import Flex from '../../styles/Flex';
import Content from '../../styles/Content';
import Container from '../../styles/Container';

import { connect } from '../../utils';

class RangeInput extends Component {
    constructor(props) {
        super(props);
        this.state = {
            start: this.props.defaultSelected
                ? this.props.defaultSelected.start
                : props.range.start,
            end: this.props.defaultSelected ? this.props.defaultSelected.end : props.range.end,
            isStartValid: true,
            isEndValid: true,
        };

        this.startInputRef = React.createRef();
        this.endInputRef = React.createRef();
    }

    componentWillReceiveProps(nextProps) {
        if (!isEqual(this.props.defaultSelected, nextProps.defaultSelected)) {
            this.handleSlider(nextProps.defaultSelected);
        }
    }

    // for SSR
    static defaultQuery = RangeSlider.defaultQuery;

    static parseValue = RangeSlider.parseValue;

    handleInputChange = (e) => {
        const { name, value } = e.target;
        if (Number.isNaN(value)) {
            // set errors for invalid inputs
            if (name === 'start') {
                this.setState({
                    isStartValid: false,
                });
            } else {
                this.setState({
                    isEndValid: false,
                });
            }
        } else {
            // reset error states for valid inputs
            // eslint-disable-next-line
            if (name === 'start' && !this.state.isStartValid) {
                this.setState({
                    isStartValid: true,
                });
            } else if (name === 'end' && !this.state.isEndValid) {
                this.setState({
                    isEndValid: true,
                });
            }
        }
        this.setState({
            [name]: value,
        });
    };

    handleSlider = ({ start, end }) => {
        if (
            document.activeElement !== this.startInputRef.current
            && document.activeElement !== this.endInputRef.current
        ) {
            this.setState({
                start,
                end,
            });
        }
        if (this.props.onValueChange) {
            this.props.onValueChange({
                start,
                end,
            });
        }
    };

    render() {
        const {
            className, style, themePreset, ...rest
        } = this.props;
        return (
            <Container style={style} className={className}>
                <RangeSlider
                    {...rest}
                    defaultSelected={{
                        start: this.state.isStartValid
                            ? Number(this.state.start)
                            : this.props.range.start,
                        end: this.state.isEndValid ? Number(this.state.end) : this.props.range.end,
                    }}
                    onValueChange={this.handleSlider}
                    className={getClassName(this.props.innerClass, 'slider-container') || null}
                />
                <Flex className={getClassName(this.props.innerClass, 'input-container') || null}>
                    <Flex direction="column" flex={2}>
                        <Input
                            name="start"
                            type="number"
                            onChange={this.handleInputChange}
                            value={this.state.start}
                            step={this.props.stepValue}
                            alert={!this.state.isStartValid}
                            className={getClassName(this.props.innerClass, 'input') || null}
                            themePreset={themePreset}
                            innerRef={this.startInputRef}
                            id="startInput"
                        />
                        {!this.state.isStartValid && (
                            <Content alert>Input range is invalid</Content>
                        )}
                    </Flex>
                    <Flex justifyContent="center" alignItems="center" flex={1}>
                        -
                    </Flex>
                    <Flex direction="column" flex={2}>
                        <Input
                            name="end"
                            type="number"
                            onChange={this.handleInputChange}
                            value={this.state.end}
                            step={this.props.stepValue}
                            alert={!this.state.isEndValid}
                            className={getClassName(this.props.innerClass, 'input') || null}
                            themePreset={themePreset}
                            innerRef={this.endInputRef}
                            id="endInput"
                        />
                        {!this.state.isEndValid && <Content alert>Input range is invalid</Content>}
                    </Flex>
                </Flex>
            </Container>
        );
    }
}

RangeInput.propTypes = {
    className: types.string,
    defaultSelected: types.range,
    innerClass: types.style,
    onValueChange: types.func,
    range: types.range,
    stepValue: types.number,
    style: types.style,
    themePreset: types.themePreset,
};

RangeInput.defaultProps = {
    range: {
        start: 0,
        end: 10,
    },
    stepValue: 1,
};

const mapStateToProps = state => ({
    themePreset: state.config.themePreset,
});

export default connect(
    mapStateToProps,
    null,
)(RangeInput);