From 11988da5099467b0f0b621557e6310d5ac390a76 Mon Sep 17 00:00:00 2001 From: Raul de Melo Date: Mon, 14 Nov 2022 19:28:49 +0100 Subject: [PATCH] fix(form-builder): improve inputmode detection for number input and fix tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Raul de Melo Co-authored-by: Bjørge Næss --- dev/test-studio/schema/debug/validation.js | 14 ++++ .../form-builder/src/inputs/NumberInput.tsx | 8 ++- .../src/inputs/__tests__/NumberInput.test.tsx | 72 +++++++++++++++++-- 3 files changed, 88 insertions(+), 6 deletions(-) diff --git a/dev/test-studio/schema/debug/validation.js b/dev/test-studio/schema/debug/validation.js index 8fe9f39d49f..db6970d3ef3 100644 --- a/dev/test-studio/schema/debug/validation.js +++ b/dev/test-studio/schema/debug/validation.js @@ -146,6 +146,20 @@ export default { description: 'Only integers', validation: (Rule) => Rule.integer(), }, + { + name: 'precision', + type: 'number', + title: 'Precision', + description: 'Max precision of 2', + validation: (Rule) => Rule.precision(2), + }, + { + name: 'zeroPrecision', + type: 'number', + title: 'Zero precision', + description: 'Max precision of 0', + validation: (Rule) => Rule.precision(0), + }, { name: 'quotes', title: 'Quotes', diff --git a/packages/@sanity/form-builder/src/inputs/NumberInput.tsx b/packages/@sanity/form-builder/src/inputs/NumberInput.tsx index 444d3e10726..5a68b2a6b33 100644 --- a/packages/@sanity/form-builder/src/inputs/NumberInput.tsx +++ b/packages/@sanity/form-builder/src/inputs/NumberInput.tsx @@ -22,7 +22,13 @@ const NumberInput = React.forwardRef(function NumberInput( // Show numpad on mobile if only positive numbers is preferred const minRule = getValidationRule(type, 'min') + const integerRule = getValidationRule(type, 'integer') + const precisionRule = getValidationRule(type, 'precision') const onlyPositiveNumber = minRule?.constraint >= 0 + const onlyIntegers = integerRule || precisionRule?.constraint === 0 + + // eslint-disable-next-line no-nested-ternary + const inputMode = onlyPositiveNumber ? (onlyIntegers ? 'numeric' : 'decimal') : 'text' const handleChange = React.useCallback( (event: React.SyntheticEvent) => { @@ -43,7 +49,7 @@ const NumberInput = React.forwardRef(function NumberInput( 0 ? errors[0].item.message : ''} value={value} diff --git a/packages/@sanity/form-builder/src/inputs/__tests__/NumberInput.test.tsx b/packages/@sanity/form-builder/src/inputs/__tests__/NumberInput.test.tsx index da14e2a834b..c48a5fff1f8 100644 --- a/packages/@sanity/form-builder/src/inputs/__tests__/NumberInput.test.tsx +++ b/packages/@sanity/form-builder/src/inputs/__tests__/NumberInput.test.tsx @@ -9,7 +9,39 @@ const schema = Schema.compile({ { name: 'book', type: 'document', - fields: [{name: 'num', title: 'Number', type: 'number'}], + fields: [ + { + name: 'defaultNumber', + title: 'Integer', + type: 'number', + }, + { + name: 'positiveNumber', + title: 'A positive number', + type: 'number', + validation: [{_rules: [{flag: 'min', constraint: 0}]}], + }, + { + name: 'positiveInteger', + title: 'Integer', + type: 'number', + validation: [{_rules: [{flag: 'min', constraint: 0}, {flag: 'integer'}]}], + }, + { + // should be handled the same way as an integer + name: 'positiveZeroPrecisionNumber', + title: 'Integer', + type: 'number', + validation: [ + { + _rules: [ + {flag: 'min', constraint: 0}, + {flag: 'precision', constraint: 0}, + ], + }, + ], + }, + ], }, ], }) @@ -20,7 +52,7 @@ const dummyDocument = { _rev: '5hb8s6-k75-ip4-4bq-5ztbf3fbx', _type: 'numberFieldTest', _updatedAt: '2021-11-05T12:34:29Z', - num: 0, + num: 1, title: 'Hello world', } @@ -30,7 +62,7 @@ function renderInput(testId: string) { describe('number-input', () => { it('renders the number input field', () => { - const {inputContainer} = renderInput('input-num') + const {inputContainer} = renderInput('input-defaultNumber') const input = inputContainer.querySelector('input') expect(input).toBeDefined() @@ -38,11 +70,41 @@ describe('number-input', () => { }) it('accepts decimals by default', () => { - const {inputContainer} = renderInput('input-num') + const {inputContainer} = renderInput('input-defaultNumber') const input = inputContainer.querySelector('input') input.value = '1.2' - expect(input.value).toBe('1.2') + expect(input.valueAsNumber).toBe(1.2) expect(input.checkValidity()).toBe(true) }) + + it('renders inputMode equals text if there is no min rule', () => { + // Note: we want "text" because devices may or may not show a minus key. + // https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/inputmode#values + const {inputContainer} = renderInput('input-defaultNumber') + const input = inputContainer.querySelector('input') + + expect(input.inputMode).toBe('text') + }) + + it('renders inputMode equals "decimal" if there is a min rule', () => { + const {inputContainer} = renderInput('input-positiveNumber') + const input = inputContainer.querySelector('input') + + expect(input.inputMode).toBe('decimal') + }) + + it('renders inputMode equals "numeric" if there is a min rule and integer rule', () => { + const {inputContainer} = renderInput('input-positiveInteger') + const input = inputContainer.querySelector('input') + + expect(input.inputMode).toBe('numeric') + }) + + it('renders inputMode equals "numeric" if there is a min rule and zero precision rule', () => { + const {inputContainer} = renderInput('input-positiveZeroPrecisionNumber') + const input = inputContainer.querySelector('input') + + expect(input.inputMode).toBe('numeric') + }) })