toggle-corp/react-store

View on GitHub
components/Input/TimeFilter/index.js

Summary

Maintainability
A
0 mins
Test Coverage
import PropTypes from 'prop-types';
import React from 'react';
import memoize from 'memoize-one';
import { requiredCondition, FaramInputElement } from '@togglecorp/faram';

import ApplyModal from '../../View/ApplyModal';

import SelectInput from '../SelectInput';
import TimeInput from '../TimeInput';

import styles from './styles.scss';

const noOp = () => {};

const propTypes = {
    className: PropTypes.string,
    value: PropTypes.shape({
        startTime: PropTypes.string,
        endTime: PropTypes.string,
    }),
    onChange: PropTypes.func,
};

const defaultProps = {
    className: '',
    value: {},
    onChange: noOp,
};

class TimeFilter extends React.PureComponent {
    static propTypes = propTypes;

    static defaultProps = defaultProps;

    static defaultOptions = [
        { key: 'customExact', label: 'Select an exact time' },
        { key: 'customRange', label: 'Select a time range' },
    ];

    static exactModalSchema = {
        fields: {
            time: [requiredCondition],
        },
    };

    // FIXME: add startDate smaller than endDate condition
    static rangeModalSchema = {
        fields: {
            startTime: [requiredCondition],
            endTime: [requiredCondition],
        },
    };

    static calculateOptionsAndValue = memoize((value) => {
        const options = TimeFilter.defaultOptions;
        const { startTime, endTime } = value;

        if (!startTime || !endTime) {
            return { options, value: undefined };
        }

        if (startTime === endTime) {
            return {
                options: [
                    ...options,
                    { key: 'selectedExact', label: startTime },
                ],
                value: 'selectedExact',
            };
        }

        return {
            options: [
                ...options,
                { key: 'selectedRange', label: `${startTime} - ${endTime}` },
            ],
            value: 'selectedRange',
        };
    });

    constructor(props) {
        super(props);

        this.state = {
            showRangeModal: false,
            showExactModal: false,
        };
    }

    getClassName = () => {
        const { className } = this.props;
        const classNames = [
            className,
            'time-filter',
        ];

        return classNames.join(' ');
    }

    handleSelectInputChange = (value) => {
        switch (value) {
            case 'customExact':
                this.setState({
                    showExactModal: true,
                });
                break;
            case 'customRange':
                this.setState({
                    showRangeModal: true,
                });
                break;
            case undefined:
                this.props.onChange(undefined);
                break;
            default:
        }
    }

    closeRangeModal = () => {
        this.setState({
            showRangeModal: false,
        });
    }

    applyRangeTime = ({ startTime, endTime }) => {
        this.setState({
            showRangeModal: false,
        }, () => {
            this.props.onChange({
                startTime,
                endTime,
            });
        });
    }

    closeExactModal = () => {
        this.setState({
            showExactModal: false,
        });
    }

    applyExactTime = ({ time }) => {
        this.setState({
            showExactModal: false,
        }, () => {
            this.props.onChange({
                startTime: time,
                endTime: time,
            });
        });
    }

    renderExactModal = () => {
        const { showExactModal } = this.state;

        if (!showExactModal) {
            return null;
        }

        return (
            <ApplyModal
                className={styles.timeFilterModal}
                onClose={this.closeExactModal}
                onApply={this.applyExactTime}
                title="Select a time"
                schema={TimeFilter.exactModalSchema}
            >
                <TimeInput faramElementName="time" />
            </ApplyModal>
        );
    }

    renderRangeModal = () => {
        const { showRangeModal } = this.state;

        if (!showRangeModal) {
            return null;
        }

        return (
            <ApplyModal
                className={styles.timeFilterModal}
                onClose={this.closeRangeModal}
                onApply={this.applyRangeTime}
                title="Select a time range"
                schema={TimeFilter.rangeModalSchema}
            >
                <TimeInput faramElementName="startTime" />
                <TimeInput faramElementName="endTime" />
            </ApplyModal>
        );
    }

    render() {
        const {
            value,
            onChange, // eslint-disable-line no-unused-vars, @typescript-eslint/no-unused-vars
            ...otherProps
        } = this.props;

        const className = this.getClassName();

        const {
            options: selectInputOptions,
            value: selectInputValue,
        } = TimeFilter.calculateOptionsAndValue(value);

        const CustomExactModal = this.renderExactModal;
        const CustomRangeModal = this.renderRangeModal;

        return (
            <React.Fragment>
                <SelectInput
                    className={className}
                    onChange={this.handleSelectInputChange}
                    options={selectInputOptions}
                    value={selectInputValue}
                    {...otherProps}
                />
                <CustomExactModal />
                <CustomRangeModal />
            </React.Fragment>
        );
    }
}

export default FaramInputElement(TimeFilter);