Skip to content

Commit

Permalink
v1.0.3
Browse files Browse the repository at this point in the history
V1.0.3
  • Loading branch information
CharlesCoqueret committed Oct 18, 2021
2 parents 4f03557 + c81ec0f commit fc1b91e
Show file tree
Hide file tree
Showing 10 changed files with 94 additions and 57 deletions.
79 changes: 58 additions & 21 deletions README.md
@@ -1,12 +1,14 @@
# React component supporting the following
# React component formatting numbers in an input field

[![GitHub open issues](https://img.shields.io/github/issues/CharlesCoqueret/react-amount?style=flat-square)](https://github.com/CharlesCoqueret/react-amount/issues)
[![MIT](https://img.shields.io/npm/l/react-amount?style=flat-square)](https://github.com/CharlesCoqueret/react-amount/blob/master/LICENSE.txt)
[![Coverage Status](https://coveralls.io/repos/github/CharlesCoqueret/react-amount/badge.svg?branch=master)](https://coveralls.io/github/CharlesCoqueret/react-amount?branch=master)

## Live demo

[Live demo](https://codesandbox.io/s/demo-react-amount-640ii?file=/src/App.tsx)
[Live demo on CodeSandbox](https://codesandbox.io/s/demo-react-amount-640ii?file=/src/App.tsx)

![](documentation/example.gif)

## Installation

Expand All @@ -24,7 +26,7 @@ npm install react-amount

```typescript
import React, { useState } from 'react';
import Amount from 'react-amount';
import { Amount } from 'react-amount';

import '~/react-amount/dist/style/index.min.css';

Expand Down Expand Up @@ -55,28 +57,63 @@ export default MyComponent;

## Options

| Option | Type | Default value | Description |
| :---------------: | :--------------------------------------------------: | :-----------: | ----------------------------------------------------- |
| value | string \| number | undefined | Initial value of the control |
| readOnly | boolean | false | Input value is not editable |
| disabled | boolean | false | Input value is disabled |
| textOnly | boolean | false | Input value is displayed as formatted text only value |
| name | string | mandatory | Name of the input field |
| className | string | undefined | Class to be added to the wrapper of the component |
| onChange | (update: FormattedValues) => void | undefined | Callback function to handle value changes |
| decimals | number | 2 | Number of decimals |
| decimalSeparator | string | . | Decimal separator |
| thousandSeparator | string | , | Thousand separator |
| thousandGrouping | ThousangGroupingStyle: "thousand" \| "wan" \| "lakh" | thousand | Thousand grouping style |
| displayOnInvalid | string | - | Value displayed on invalid input in textOnly |
| dataTestId | string | undefined | Id value for testing |
| required | boolean | false | Required of the input field |
| prefix | string | undefined | Prefix |
| suffix | string | undefined | Suffix |
| Option | Type | Default value | Description |
| :---------------: | :----------------------------------------------------------------------------: | :-----------: | ----------------------------------------------------- |
| value | string \| number | undefined | Initial value of the control |
| readOnly | boolean | false | Input value is not editable |
| disabled | boolean | false | Input value is disabled |
| textOnly | boolean | false | Input value is displayed as formatted text only value |
| name | string | | Name of the input field |
| className | string | undefined | Class to be added to the wrapper of the component |
| onChange | (update: [FormattedValues](#FormattedValues)) => void | undefined | Callback function to handle value changes |
| decimals | number | 2 | Number of decimals |
| decimalSeparator | string | "." | Decimal separator |
| thousandSeparator | string | "," | Thousand separator |
| thousandGrouping | [ThousandGroupingStyle](#ThousandGroupingStyle): "thousand" \| "wan" \| "lakh" | "thousand" | Thousand grouping style |
| displayOnInvalid | string | "-" | Value displayed on invalid input in textOnly |
| dataTestId | string | undefined | Id value for testing |
| required | boolean | false | Required of the input field |
| prefix | string | undefined | Prefix |
| suffix | string | undefined | Suffix |

## Contributing

We very much welcome contributions.

- Submit [GitHub issues](http://github.com/CharlesCoqueret/react-amount/issues) to report bugs or ask questions.
- Propose [Pull Request](http://github.com/CharlesCoqueret/react-amount/pulls) to improve our code.

## Types

### FormattedValues

```typescript
export interface FormattedValues {
formatted: string;
float: number;
raw: string;
}
```

This structure is used in the onChange handler to provide flexibility on the format.

- `formatted` is for display purpose.
- `float` is for numerical representation, but suffer from precision limitations.
- `raw` is for numerical representation as a string with full precision.

### ThousandGroupingStyle

```typescript
export enum ThousandGroupingStyle {
THOUSAND = 'thousand',
WAN = 'wan',
LAKH = 'lakh',
}
```

- `THOUSAND`: groups of 3 digits
example: 123,456,789
- `WAN`: 1 group of 4 digits, then groups of 3 digits
example: 12,345,6789
- `LAKH`: 1 group of 3 digits, then groups of 2 digits
example: 12,34,56,789
Binary file added documentation/example.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 3 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
@@ -1,9 +1,10 @@
{
"name": "react-amount",
"version": "1.0.2",
"version": "1.0.3",
"description": "React amount component",
"keyword": [
"keywords": [
"react",
"react-component",
"amount",
"format",
"number",
Expand Down
6 changes: 3 additions & 3 deletions src/Components/Amount.tsx
Expand Up @@ -6,7 +6,7 @@ import {
formatInputForDisplay,
formatInputForInput,
interpretValue,
ThousangGroupingStyle,
ThousandGroupingStyle,
} from '../utils/amount-formatter';

export interface FormattedValues {
Expand Down Expand Up @@ -37,7 +37,7 @@ export interface AmountProps {
/** Thousand separator (default: ',') */
thousandSeparator?: string;
/** Thousand style grouping (default: 'thousand') */
thousandGrouping?: ThousangGroupingStyle;
thousandGrouping?: ThousandGroupingStyle;
/** Value displayed on invalid input in textOnly (default: '-') */
displayOnInvalid?: string;
/** Test id */
Expand Down Expand Up @@ -244,7 +244,7 @@ Amount.defaultProps = {
required: false,
decimalSeparator: '.',
thousandSeparator: ',',
thousandGrouping: ThousangGroupingStyle.THOUSAND,
thousandGrouping: ThousandGroupingStyle.THOUSAND,
displayOnInvalid: '-',
dataTestId: undefined,
prefix: undefined,
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Expand Up @@ -4,4 +4,4 @@ export {
FormattedValues,
} from './Components/Amount';

export { ThousangGroupingStyle } from './utils/amount-formatter';
export { ThousandGroupingStyle } from './utils/amount-formatter';
18 changes: 9 additions & 9 deletions src/utils/amount-formatter.ts
@@ -1,4 +1,4 @@
export enum ThousangGroupingStyle {
export enum ThousandGroupingStyle {
THOUSAND = 'thousand',
WAN = 'wan',
LAKH = 'lakh',
Expand All @@ -7,7 +7,7 @@ export enum ThousangGroupingStyle {
const defaultDecimals = 0;
const defaultDecimalSeparator = '.';
const defaultThousandSeparator = ',';
const defaultThousandGrouping = ThousangGroupingStyle.THOUSAND;
const defaultThousandGrouping = ThousandGroupingStyle.THOUSAND;
const defaultDisplayOnInvalid = '-';

/**
Expand Down Expand Up @@ -104,13 +104,13 @@ export const toStandardSeparator = (
*
* @returns corresponding regex
*/
const getThousandsGroupRegex = (thousandsGroupStyle: ThousangGroupingStyle) => {
const getThousandsGroupRegex = (thousandsGroupStyle: ThousandGroupingStyle) => {
switch (thousandsGroupStyle) {
case ThousangGroupingStyle.LAKH:
case ThousandGroupingStyle.LAKH:
return /(\d+?)(?=(\d\d)+(\d)(?!\d))(\.\d+)?/g;
case ThousangGroupingStyle.WAN:
case ThousandGroupingStyle.WAN:
return /(\d)(?=(\d{4})+(?!\d))/g;
case ThousangGroupingStyle.THOUSAND:
case ThousandGroupingStyle.THOUSAND:
default:
return /(\d)(?=(\d{3})+(?!\d))/g;
}
Expand Down Expand Up @@ -156,7 +156,7 @@ const rtrim = (value: string | undefined): string => {
const applyThousandSeparator = (
value: string,
thousandSeparator: string,
thousandsGroupStyle: ThousangGroupingStyle,
thousandsGroupStyle: ThousandGroupingStyle,
) => {
const thousandsGroupRegex = getThousandsGroupRegex(thousandsGroupStyle);
let index = value.search(/[1-9]/);
Expand Down Expand Up @@ -308,7 +308,7 @@ export const formatInputForDisplay = (
decimals: number = defaultDecimals,
decimalSeparator: string = defaultDecimalSeparator,
thousandSeparator: string = defaultThousandSeparator,
thousandGrouping: ThousangGroupingStyle = defaultThousandGrouping,
thousandGrouping: ThousandGroupingStyle = defaultThousandGrouping,
displayOnInvalid: string = defaultDisplayOnInvalid,
): string => {
const { sign, integer, decimal, approximation } = commonValidation(
Expand Down Expand Up @@ -370,7 +370,7 @@ export const formatInputForInput = (
decimals: number = defaultDecimals,
decimalSeparator: string = defaultDecimalSeparator,
thousandSeparator: string = defaultThousandSeparator,
thousandGrouping: ThousangGroupingStyle = defaultThousandGrouping,
thousandGrouping: ThousandGroupingStyle = defaultThousandGrouping,
): string => {
const { sign, integer, decimal } = commonValidation(
value,
Expand Down
10 changes: 5 additions & 5 deletions test/Components/Amount.spec.tsx
Expand Up @@ -3,7 +3,7 @@ import userEvent from '@testing-library/user-event';
import { render, screen } from '@testing-library/react';

import Amount, { AmountProps } from '../../src/Components/Amount';
import { ThousangGroupingStyle } from '../../src/utils/amount-formatter';
import { ThousandGroupingStyle } from '../../src/utils/amount-formatter';

const renderAmount = (props: Partial<AmountProps> = {}) => {
const defaultProps: AmountProps = {
Expand Down Expand Up @@ -86,7 +86,7 @@ describe('<Amount />', () => {
onChange,
decimalSeparator: ',',
thousandSeparator: ' ',
thousandGrouping: ThousangGroupingStyle.LAKH,
thousandGrouping: ThousandGroupingStyle.LAKH,
});

const inputField = screen.getByTestId('reactAmount');
Expand Down Expand Up @@ -135,7 +135,7 @@ describe('<Amount />', () => {
onChange,
decimalSeparator: ',',
thousandSeparator: ' ',
thousandGrouping: ThousangGroupingStyle.WAN,
thousandGrouping: ThousandGroupingStyle.WAN,
});

const inputField = screen.getByTestId('reactAmount');
Expand All @@ -157,7 +157,7 @@ describe('<Amount />', () => {
onChange,
decimalSeparator: ',',
thousandSeparator: ' ',
thousandGrouping: ThousangGroupingStyle.WAN,
thousandGrouping: ThousandGroupingStyle.WAN,
});

const inputField = screen.getByTestId('reactAmount');
Expand Down Expand Up @@ -187,7 +187,7 @@ describe('<Amount />', () => {
onChange,
decimalSeparator: ',',
thousandSeparator: ' ',
thousandGrouping: ThousangGroupingStyle.WAN,
thousandGrouping: ThousandGroupingStyle.WAN,
});

const inputField = screen.getByTestId('reactAmount');
Expand Down
4 changes: 2 additions & 2 deletions test/index.spec.ts
@@ -1,8 +1,8 @@
import { Amount, ThousangGroupingStyle } from '../src/index';
import { Amount, ThousandGroupingStyle } from '../src/index';

describe('index main export', () => {
test('should export all references', async () => {
expect(Amount).toBeTruthy();
expect(ThousangGroupingStyle).toBeTruthy();
expect(ThousandGroupingStyle).toBeTruthy();
});
});
20 changes: 10 additions & 10 deletions test/utils/amount-formatter.spec.ts
Expand Up @@ -7,7 +7,7 @@ import {
commonValidation,
interpretValue,
formatInputForDisplay,
ThousangGroupingStyle,
ThousandGroupingStyle,
formatInputForInput,
} from '../../src/utils/amount-formatter';

Expand Down Expand Up @@ -212,10 +212,10 @@ it('Basic formatting for display of positive values', () => {
expect(formatInputForDisplay(67_000, 5, ',', '.')).toEqual('67.000,00000');
expect(formatInputForDisplay(0.9, 0)).toEqual('0');
expect(
formatInputForDisplay(123_456_789, 5, ',', '.', ThousangGroupingStyle.WAN),
formatInputForDisplay(123_456_789, 5, ',', '.', ThousandGroupingStyle.WAN),
).toEqual('1.2345.6789,00000');
expect(
formatInputForDisplay(123_456_789, 5, ',', '.', ThousangGroupingStyle.LAKH),
formatInputForDisplay(123_456_789, 5, ',', '.', ThousandGroupingStyle.LAKH),
).toEqual('12.34.56.789,00000');
expect(formatInputForDisplay(11_223_344_556_677.123, 4, '.', '')).toEqual(
'11223344556677.1230',
Expand All @@ -237,15 +237,15 @@ it('Basic formatting for display of negative values', () => {
expect(formatInputForDisplay(-67_000, 5, ',', '.')).toEqual('-67.000,00000');
expect(formatInputForDisplay(-0.9, 0)).toEqual('0');
expect(
formatInputForDisplay(-123_456_789, 5, ',', '.', ThousangGroupingStyle.WAN),
formatInputForDisplay(-123_456_789, 5, ',', '.', ThousandGroupingStyle.WAN),
).toEqual('-1.2345.6789,00000');
expect(
formatInputForDisplay(
-123_456_789,
5,
',',
'.',
ThousangGroupingStyle.LAKH,
ThousandGroupingStyle.LAKH,
),
).toEqual('-12.34.56.789,00000');

Expand Down Expand Up @@ -288,7 +288,7 @@ it('Errornous formatting for display', () => {
5,
',',
'.',
ThousangGroupingStyle.THOUSAND,
ThousandGroupingStyle.THOUSAND,
'*',
),
).toEqual('*');
Expand All @@ -309,10 +309,10 @@ it('Basic formatting for input of positive values', () => {
expect(formatInputForInput(67_000, 5, ',', '.')).toEqual('67.000');
expect(formatInputForInput(0.9, 0)).toEqual('0');
expect(
formatInputForInput(123_456_789, 5, ',', '.', ThousangGroupingStyle.WAN),
formatInputForInput(123_456_789, 5, ',', '.', ThousandGroupingStyle.WAN),
).toEqual('1.2345.6789');
expect(
formatInputForInput(123_456_789, 5, ',', '.', ThousangGroupingStyle.LAKH),
formatInputForInput(123_456_789, 5, ',', '.', ThousandGroupingStyle.LAKH),
).toEqual('12.34.56.789');

// eslint-disable-next-line @typescript-eslint/no-loss-of-precision
Expand All @@ -332,10 +332,10 @@ it('Basic formatting for input of negative values', () => {
expect(formatInputForInput(-67_000, 5, ',', '.')).toEqual('-67.000');
expect(formatInputForInput(-0.9, 0)).toEqual('-0');
expect(
formatInputForInput(-123_456_789, 5, ',', '.', ThousangGroupingStyle.WAN),
formatInputForInput(-123_456_789, 5, ',', '.', ThousandGroupingStyle.WAN),
).toEqual('-1.2345.6789');
expect(
formatInputForInput(-123_456_789, 5, ',', '.', ThousangGroupingStyle.LAKH),
formatInputForInput(-123_456_789, 5, ',', '.', ThousandGroupingStyle.LAKH),
).toEqual('-12.34.56.789');

// eslint-disable-next-line @typescript-eslint/no-loss-of-precision
Expand Down

0 comments on commit fc1b91e

Please sign in to comment.