diff --git a/packages/x/src/formatInteger.ts b/packages/x/src/formatInteger.ts new file mode 100644 index 00000000..49a9a970 --- /dev/null +++ b/packages/x/src/formatInteger.ts @@ -0,0 +1,7 @@ +export const formatInteger = (value: number | null | undefined) => { + if (typeof value !== 'number' || !Number.isFinite(value)) { + return ''; + } + + return value.toFixed(0); +}; diff --git a/packages/x/src/useConverterField.ts b/packages/x/src/useConverterField.ts index c0fdc236..b230d679 100644 --- a/packages/x/src/useConverterField.ts +++ b/packages/x/src/useConverterField.ts @@ -8,10 +8,12 @@ export class ConversionError extends Error { } } -export type ConverterFieldConfig = { +export type ValueConverter = { parse: (value: string) => T; format: (value: T) => string; -} & FieldConfig; +}; + +export type ConverterFieldConfig = ValueConverter & FieldConfig; export type ConverterFieldBag = { text: string; diff --git a/packages/x/src/useIntegerField.ts b/packages/x/src/useIntegerField.ts index e4ac7220..b2a9b55c 100644 --- a/packages/x/src/useIntegerField.ts +++ b/packages/x/src/useIntegerField.ts @@ -1,26 +1,17 @@ import { useCallback, useContext } from 'react'; import { FieldConfig, useFieldValidator } from '@reactive-forms/core'; +import { formatInteger } from './formatInteger'; import { IntegerFieldI18nContext } from './IntegerFieldI18n'; -import { ConversionError, ConverterFieldBag, useConverterField } from './useConverterField'; +import { ConversionError, ConverterFieldBag, useConverterField, ValueConverter } from './useConverterField'; const INTEGER_REGEX = /^-?\d+$/; -const formatInteger = (value: number | null | undefined) => { - if (typeof value !== 'number' || !Number.isFinite(value)) { - return ''; - } - - return value.toFixed(0); -}; - export type IntegerFieldConfig = FieldConfig & { required?: boolean; min?: number; max?: number; - - formatValue?: (value: number | null | undefined) => string; -}; +} & Partial>; export type IntegerFieldBag = ConverterFieldBag; @@ -31,12 +22,17 @@ export const useIntegerField = ({ required, min, max, - formatValue = formatInteger, + parse: customParse, + format = formatInteger, }: IntegerFieldConfig): IntegerFieldBag => { const i18n = useContext(IntegerFieldI18nContext); - const parseInteger = useCallback( + const parse = useCallback( (text: string) => { + if (customParse) { + return customParse(text); + } + text = text.trim(); if (text.length === 0) { @@ -55,12 +51,12 @@ export const useIntegerField = ({ return value; }, - [i18n.invalidInput], + [customParse, i18n.invalidInput], ); const integerBag = useConverterField({ - parse: parseInteger, - format: formatValue, + parse, + format, name, validator, schema, diff --git a/packages/x/tests/useIntegerField.test.tsx b/packages/x/tests/useIntegerField.test.tsx index 4ee8f569..88767015 100644 --- a/packages/x/tests/useIntegerField.test.tsx +++ b/packages/x/tests/useIntegerField.test.tsx @@ -221,11 +221,25 @@ describe('Integer field', () => { }); it('Should be able to format integer differently', () => { - const formatValue = jest.fn(() => 'custom'); + const format = jest.fn(() => 'custom'); const initialValue = 42; - const [{ result }] = renderUseIntegerField({ formatValue, initialValue }); + const [{ result }] = renderUseIntegerField({ format, initialValue }); expect(result.current.text).toBe('custom'); - expect(formatValue).toBeCalledWith(initialValue); + expect(format).toBeCalledWith(initialValue); + }); + + it('Should call custom parse function', async () => { + const parse = jest.fn(); + + const [{ result }] = renderUseIntegerField({ parse }); + + await act(() => { + result.current.onTextChange('0'); + }); + + await waitFor(() => { + expect(parse).toBeCalledWith('0'); + }); }); });