diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1ecf2a7ec..4f32b6e24 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,7 @@
* Button: Button link style has a min-height, which can offset it from text. Fixes STCOM-1039.
* The vertical scroll bar displays at the second pane when it doesn't need. Fixes STCOM-1044.
* Focus management and accessible labeling of confirmation modals. Confirmation modals announce in a way similart to Javascript alerts. Fixes STCOM-1041.
+* Provide `NumberField` component. Refs STCOM-944.
## [10.2.0](https://github.com/folio-org/stripes-components/tree/v10.2.0) (2022-06-14)
[Full Changelog](https://github.com/folio-org/stripes-components/compare/v10.1.0...v10.2.0)
diff --git a/index.js b/index.js
index 2f349ec21..17b12432c 100644
--- a/index.js
+++ b/index.js
@@ -21,6 +21,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..8467fd26a
--- /dev/null
+++ b/lib/NumberField/readme.md
@@ -0,0 +1,21 @@
+# NumberField
+
+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
+import { NumberField } from '@folio/stripes/components';
+
+}
+ field={Field}
+ id="amount"
+ fullWidth
+ required
+>
+```
+
+## Summary
+
+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.
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 }));
+ });
+ });
+});