toggle-corp/react-store

View on GitHub
components/View/Numeral/index.js

Summary

Maintainability
A
0 mins
Test Coverage
import PropTypes from 'prop-types';
import React from 'react';
import {
    addSeparator,
    isFalsy,
    isTruthy,
    isNotDefined,
    formattedNormalize,
} from '@togglecorp/fujs';
import { FaramOutputElement } from '@togglecorp/faram';

import styles from './styles.scss';


const propTypes = {
    /**
     * reqired for style override
     */
    className: PropTypes.string,

    // eslint-disable-next-line react/forbid-prop-types
    style: PropTypes.object,

    /**
     * string to show, if value is unexpected
     * Default: -
     */
    invalidText: PropTypes.string,
    /**
     * Normalize numer into Lac, Cr, Ar
     */
    normal: PropTypes.bool,
    /**
     * Numer of digits after decimal point. Rounding is also applied.
     */
    precision: PropTypes.number,
    /**
     * Prefix the output with certain string. Eg. $
     */
    prefix: PropTypes.string,
    /**
     * Specify which separator to use for thousands
     */
    separator: PropTypes.string,
    /**
     * Show or hide thousands separator
     */
    showSeparator: PropTypes.bool,
    /**
     * Show both positive and negative sign for number
     */
    showSign: PropTypes.bool,
    /**
     * Prefix the output with certain string. Eg. %
     */
    suffix: PropTypes.string,
    /**
     * The value of the numeral
     */
    value: PropTypes.number,

    transformer: PropTypes.func,

    lang: PropTypes.string,
};

const defaultProps = {
    className: '',
    style: undefined,
    invalidText: '-',
    normal: false,
    precision: 2,
    prefix: undefined,
    separator: undefined,
    showSeparator: true,
    showSign: false,
    suffix: undefined,
    value: undefined,
    lang: undefined,
    transformer: undefined,
};


const addNepaliSeparator = (num) => {
    if (isNotDefined(num)) {
        return num;
    }
    return String(num).replace(/\B(?=(\d{3})(?!\d))/, ',').replace(/\B(?=(\d{2})+(?=,))/g, ',');
};

/**
 * Numeral component for formatted numbers
 */
export class NormalNumeral extends React.PureComponent {
    static propTypes = propTypes;

    static defaultProps = defaultProps;

    static getNormalizedNumber({
        value,
        showSign = false,
        normal = false,
        precision = undefined,
        showSeparator = true,
        separator = ',',
        lang = 'en',
    }) {
        // Only use absolute part if showSign is true (sign are added later)
        let number = isTruthy(showSign) ? Math.abs(value) : value;

        // Get normalize-suffix and reduce the number
        let normalizeSuffix;

        if (normal) {
            const { number: num, normalizeSuffix: norm } = formattedNormalize(number, lang);
            number = num;
            normalizeSuffix = norm;
        }

        // Convert number to fixed precision
        if (isTruthy(precision)) {
            number = number.toFixed(precision);
        }

        // Convert number to add separator
        if (showSeparator) {
            number = lang === 'ne'
                ? addNepaliSeparator(number)
                : addSeparator(number, separator);
        }

        return { number, normalizeSuffix };
    }

    static renderText(props) {
        const {
            normal,
            precision,
            prefix = '',
            separator,
            showSeparator,
            showSign,
            suffix = '',
            value,
            invalidText,
            lang,
            transformer,
        } = { ...defaultProps, ...props };

        if (isFalsy(value)) {
            return invalidText;
        }

        const { number, normalizeSuffix = '' } = NormalNumeral.getNormalizedNumber({
            value, showSign, normal, precision, showSeparator, separator, lang,
        });

        return `${prefix}${transformer ? transformer(number) : number}${normalizeSuffix}${suffix}`;
    }

    render() {
        const {
            className,
            normal,
            precision,
            prefix,
            separator,
            showSeparator,
            showSign,
            suffix,
            value,
            lang,
            invalidText,
            transformer,
            style,
        } = this.props;

        if (isFalsy(value)) {
            return (
                <span
                    className={className}
                >
                    {invalidText}
                </span>
            );
        }

        const { number, normalizeSuffix } = NormalNumeral.getNormalizedNumber({
            value, showSign, normal, precision, showSeparator, separator, lang,
        });

        return (
            <span
                className={`numeral ${className} ${styles.numeral}`}
                style={style}
            >
                {
                    isTruthy(prefix) && (
                        <span className="prefix">
                            {prefix}
                        </span>
                    )
                }
                {
                    isTruthy(showSign) && value !== 0 && (
                        <span className="sign">
                            {value > 0 ? '+' : '-'}
                        </span>
                    )
                }
                <span className="number">
                    {transformer ? transformer(number) : number}
                </span>
                {
                    isTruthy(normalizeSuffix) && (
                        <span className="normalized-suffix">
                            {normalizeSuffix}
                        </span>
                    )
                }
                {
                    isTruthy(suffix) && (
                        <span className="suffix">
                            {suffix}
                        </span>
                    )
                }
            </span>
        );
    }
}

export default FaramOutputElement(NormalNumeral);