From b01b11c9c0778b7b727fd4b6b97e122b7df9ae6a Mon Sep 17 00:00:00 2001 From: Zak Burke Date: Mon, 28 Feb 2022 10:09:39 -0500 Subject: [PATCH 1/3] STCOM-944 provide NumberFormat component Refs STCOM-944 --- CHANGELOG.md | 1 + index.js | 1 + lib/NumberField/NumberField.js | 59 ++++++++++++++++++++ lib/NumberField/index.js | 1 + lib/NumberField/readme.md | 21 ++++++++ lib/NumberField/tests/NumberField-test.js | 65 +++++++++++++++++++++++ 6 files changed, 148 insertions(+) create mode 100644 lib/NumberField/NumberField.js create mode 100644 lib/NumberField/index.js create mode 100644 lib/NumberField/readme.md create mode 100644 lib/NumberField/tests/NumberField-test.js diff --git a/CHANGELOG.md b/CHANGELOG.md index ef10b51e4..22258612d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Prevent caching of pane layouts (resizeable widths) when panes do not have a provided stable `id` prop. fixes STCOM-932. * AccordionSet has incorrect aria attribute. Refs STCOM-937. * NoValue component doesn't have role. Refs STCOM-936. +* Provide `NumberField` component. Refs STCOM-944. ## [10.1.0](https://github.com/folio-org/stripes-components/tree/v10.1.0) (2022-02-11) [Full Changelog](https://github.com/folio-org/stripes-components/compare/v10.0.0...v10.1.0) diff --git a/index.js b/index.js index 2ecae29e4..87c853952 100644 --- a/index.js +++ b/index.js @@ -15,6 +15,7 @@ export { default as FormattedUTCDate } from './lib/FormattedUTCDate'; export { default as Label } from './lib/Label'; export { default as TextLink } from './lib/TextLink'; export { Loading, LoadingPane, LoadingView } from './lib/Loading'; +export { default as NumberField } from './lib/NumberField'; export { default as RadioButton } from './lib/RadioButton'; export { default as RadioButtonGroup } from './lib/RadioButtonGroup'; export { default as Select } from './lib/Select'; diff --git a/lib/NumberField/NumberField.js b/lib/NumberField/NumberField.js new file mode 100644 index 000000000..ba6695744 --- /dev/null +++ b/lib/NumberField/NumberField.js @@ -0,0 +1,59 @@ +import { useIntl } from 'react-intl'; +import PropTypes from 'prop-types'; +import TextField from '../TextField'; + +/** + * Number + * @param {*} param + * @returns + */ +const NumberField = ({ field, ...rest }) => { + const intl = useIntl(); + + /** + * The plot here is to take a number in native-js (12345.6), + * collect its parts when pushed through Intl.NumberFormat, + * and then use those parts to create a parser. + * + * It works for any locale, including those using non-Arabic + * numerals (i.e. other than 0-9). + * + * verbatim from https://observablehq.com/@mbostock/localized-number-parsing + */ + const parts = new Intl.NumberFormat(intl.locale).formatToParts(12345.6); + const numerals = [...new Intl.NumberFormat(intl.locale, { useGrouping: false }).format(9876543210)].reverse(); + const index = new Map(numerals.map((d, i) => [d, i])); + const nfGroup = new RegExp(`[${parts.find(d => d.type === 'group').value}]`, 'g'); + const nfDecimal = new RegExp(`[${parts.find(d => d.type === 'decimal').value}]`); + const nfNumeral = new RegExp(`[${numerals.join('')}]`, 'g'); + const nfIndex = d => index.get(d); + + const parse = (v) => { + return v.trim() + .replace(nfGroup, '') + .replace(nfDecimal, '.') + .replace(nfNumeral, nfIndex) + ? +v : NaN; + }; + + const format = (v) => { + return intl.formatNumber(v); + }; + + const Field = field; + + return ( + ); +}; + +NumberField.propTypes = { + field: PropTypes.func +}; + +export default NumberField; diff --git a/lib/NumberField/index.js b/lib/NumberField/index.js new file mode 100644 index 000000000..621842b00 --- /dev/null +++ b/lib/NumberField/index.js @@ -0,0 +1 @@ +export { default } from './NumberField'; diff --git a/lib/NumberField/readme.md b/lib/NumberField/readme.md new file mode 100644 index 000000000..2b87fdf77 --- /dev/null +++ b/lib/NumberField/readme.md @@ -0,0 +1,21 @@ +# NumberField + +Input field for parsing strings into numbers according to the current locale. + +## Basic Usage +```js +import { NumberField } from '@folio/stripes/components'; + +} + field={Field} + id="amount" + fullWidth + required +> +``` + +## Summary + +Detail: In Javascript the comma `,` is used for grouping, the decimal `.` for separating whole and decimal portion of floating point numbers, and the numerals consist of 0-9. These values are the same in the `en-US` locale but are not shared by all locales, e.g. `de-DE` which uses `.` for grouping and `,` for decimal, and others may not use Arabic numerals. This component allows users to enter numberic values in their tenant's format and have them be correctly parsed into numeric values. diff --git a/lib/NumberField/tests/NumberField-test.js b/lib/NumberField/tests/NumberField-test.js new file mode 100644 index 000000000..d86d95b26 --- /dev/null +++ b/lib/NumberField/tests/NumberField-test.js @@ -0,0 +1,65 @@ +import React from 'react'; +import { describe, beforeEach, it } from 'mocha'; +import { expect } from 'chai'; + +import { NumberField as Interactor } from '@folio/stripes-testing'; +import { mountWithContext } from '../../../tests/helpers'; + +import NumberField from '../NumberField'; + +const Field = ({ children, rest }) =>
{children}
; + +describe.only('NumberField', () => { + const numberField = Interactor(); + + describe('Native JS locale, i.e. format matches JS (en-US)', () => { + beforeEach(async () => { + await mountWithContext( + , + [], + 'en-US' + ); + }); + + it('renders an input type="number" by default', () => { + console.log() + numberField.has({ type: 'nuasdfasdfmber' }); + }); + + it('applies the supplied id prop to the input', () => textField.has({ id: 'nfTest' })); + + describe('entering numbers into the field', () => { + beforeEach(() => numberField.fillIn('1,234.56')); + + it('updates the value', () => numberField.has({ value: 1234.56 })); + }); + }); + + describe('Non-native JS locale, i.e. format does NOT match JS (de-DE)', () => { + beforeEach(async () => { + await mountWithContext( + , + [], + 'de-DE' + ); + }); + + it('renders an input type="number" by default', () => { + numberField.has({ type: 'number' }); + }); + + describe('entering numbers into the field', () => { + beforeEach(() => numberField.fillIn('1.234,56')); + + it('updates the value', () => numberField.has({ value: 1234.56 })); + }); + }); +}); From deaca47397efa198a6d8f16e468bc4a575d177ba Mon Sep 17 00:00:00 2001 From: Zak Burke Date: Fri, 15 Apr 2022 10:47:41 -0400 Subject: [PATCH 2/3] better documentation --- lib/NumberField/readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/NumberField/readme.md b/lib/NumberField/readme.md index 2b87fdf77..051358f5f 100644 --- a/lib/NumberField/readme.md +++ b/lib/NumberField/readme.md @@ -1,6 +1,6 @@ # NumberField -Input field for parsing strings into numbers according to the current locale. +Input field for parsing numeric strings in any locale ("1,234.56", "1.234,56") into JS numbers (1234.56), i.e. `atof`. ## Basic Usage ```js @@ -18,4 +18,4 @@ import { NumberField } from '@folio/stripes/components'; ## Summary -Detail: In Javascript the comma `,` is used for grouping, the decimal `.` for separating whole and decimal portion of floating point numbers, and the numerals consist of 0-9. These values are the same in the `en-US` locale but are not shared by all locales, e.g. `de-DE` which uses `.` for grouping and `,` for decimal, and others may not use Arabic numerals. This component allows users to enter numberic values in their tenant's format and have them be correctly parsed into numeric values. +Detail: Convert a numeric string in any local to a JS float, i.e. `atof` for all you C programmers. In a Javascript number, the comma `,` is used for grouping, the decimal `.` for separating they whole and decimal portion of floating point numbers, and the numerals consist of 0-9. These values are the same in the `en-US` locale but are not shared by all locales, e.g. `de-DE` which uses `.` for grouping and `,` for decimal; of course, other locales may not use Arabic numerals. This component allows users to enter numeric strings in the format expected by their current locale and have them be correctly parsed into actual JS numbers. From e1cc3f27f261117d7d3cffb584c3ff006b35d9a2 Mon Sep 17 00:00:00 2001 From: Zak Burke Date: Mon, 13 Mar 2023 23:04:31 -0400 Subject: [PATCH 3/3] spellcheck --- lib/NumberField/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/NumberField/readme.md b/lib/NumberField/readme.md index 051358f5f..8467fd26a 100644 --- a/lib/NumberField/readme.md +++ b/lib/NumberField/readme.md @@ -18,4 +18,4 @@ import { NumberField } from '@folio/stripes/components'; ## Summary -Detail: Convert a numeric string in any local to a JS float, i.e. `atof` for all you C programmers. In a Javascript number, the comma `,` is used for grouping, the decimal `.` for separating they whole and decimal portion of floating point numbers, and the numerals consist of 0-9. These values are the same in the `en-US` locale but are not shared by all locales, e.g. `de-DE` which uses `.` for grouping and `,` for decimal; of course, other locales may not use Arabic numerals. This component allows users to enter numeric strings in the format expected by their current locale and have them be correctly parsed into actual JS numbers. +Detail: Convert a numeric string in any locale to a JS float, i.e. `atof` for all you C programmers. In a Javascript number, the comma `,` is used for grouping, the decimal `.` for separating the whole and decimal portion of floating point numbers, and the numerals consist of 0-9. These values are the same in the `en-US` locale but are not shared by all locales, e.g. `de-DE` which uses `.` for grouping and `,` for decimal; of course, other locales may not use Arabic numerals. This component allows users to enter numeric strings in the format expected by their current locale and have them be correctly parsed into actual JS numbers.