From 38df0fbcfd5dfa73e728ffb993aa9af8a15e5531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rge=20N=C3=A6ss?= Date: Tue, 22 Nov 2022 12:33:27 +0700 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 +++ .../src/core/form/inputs/NumberInput.test.tsx | 89 ++++++++++++++++--- .../src/core/form/inputs/NumberInput.tsx | 10 ++- 3 files changed, 99 insertions(+), 14 deletions(-) diff --git a/dev/test-studio/schema/debug/validation.js b/dev/test-studio/schema/debug/validation.js index 015f1940941..6dafe168b43 100644 --- a/dev/test-studio/schema/debug/validation.js +++ b/dev/test-studio/schema/debug/validation.js @@ -144,6 +144,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/src/core/form/inputs/NumberInput.test.tsx b/packages/sanity/src/core/form/inputs/NumberInput.test.tsx index 9ab7a4f2600..00f6009699e 100644 --- a/packages/sanity/src/core/form/inputs/NumberInput.test.tsx +++ b/packages/sanity/src/core/form/inputs/NumberInput.test.tsx @@ -1,19 +1,19 @@ -import {defineField} from '@sanity/types' +// eslint-disable-next-line import/no-unassigned-import +import '@testing-library/jest-dom/extend-expect' import React from 'react' import {renderNumberInput} from '../../../../test/form' import {NumberInput} from './NumberInput' -const defs = { - num: defineField({name: 'num', title: 'Number', type: 'number'}), -} - -describe('NumberInput', () => { +describe('number-input', () => { it('renders the number input field', async () => { const {result} = await renderNumberInput({ - fieldDefinition: defs.num, + fieldDefinition: { + name: 'defaultNumber', + title: 'Integer', + type: 'number', + }, render: (inputProps) => , }) - const input = result.container.querySelector('input') expect(input).toBeDefined() expect(input).toHaveAttribute('type', 'number') @@ -21,14 +21,79 @@ describe('NumberInput', () => { it('accepts decimals by default', async () => { const {result} = await renderNumberInput({ - fieldDefinition: defs.num, + fieldDefinition: { + name: 'defaultNumber', + title: 'Integer', + type: 'number', + }, render: (inputProps) => , }) - const input = result.container.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', async () => { + // 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 {result} = await renderNumberInput({ + fieldDefinition: { + name: 'defaultNumber', + title: 'Integer', + type: 'number', + }, + render: (inputProps) => , + }) + + const input = result.container.querySelector('input')! + expect(input.inputMode).toBe('text') + }) + + it('renders inputMode equals "decimal" if there is a min rule', async () => { + const {result} = await renderNumberInput({ + fieldDefinition: { + name: 'positiveNumber', + title: 'A positive number', + type: 'number', + validation: (Rule) => Rule.positive(), + }, + render: (inputProps) => , + }) + + const input = result.container.querySelector('input')! + + expect(input.inputMode).toBe('decimal') + }) + + it('renders inputMode equals "numeric" if there is a min rule and integer rule', async () => { + const {result} = await renderNumberInput({ + fieldDefinition: { + name: 'positiveInteger', + title: 'Integer', + type: 'number', + validation: (Rule) => Rule.integer().positive(), + }, + render: (inputProps) => , + }) + + const input = result.container.querySelector('input')! + expect(input.inputMode).toBe('numeric') + }) + + it('renders inputMode equals "numeric" if there is a min rule and zero precision rule', async () => { + const {result} = await renderNumberInput({ + fieldDefinition: { + // should be handled the same way as an integer + name: 'positiveZeroPrecisionNumber', + title: 'Integer', + type: 'number', + validation: (Rule) => Rule.precision(0).positive(), + }, + render: (inputProps) => , + }) + + const input = result.container.querySelector('input')! + expect(input.inputMode).toBe('numeric') + }) }) diff --git a/packages/sanity/src/core/form/inputs/NumberInput.tsx b/packages/sanity/src/core/form/inputs/NumberInput.tsx index ee29eafcb70..39e79b57ff3 100644 --- a/packages/sanity/src/core/form/inputs/NumberInput.tsx +++ b/packages/sanity/src/core/form/inputs/NumberInput.tsx @@ -12,14 +12,20 @@ export function NumberInput(props: NumberInputProps) { // Show numpad on mobile if only positive numbers is preferred const minRule = getValidationRule(schemaType, 'min') - const onlyPositiveNumber = (minRule?.constraint || 0) >= 0 + const integerRule = getValidationRule(schemaType, 'integer') + const precisionRule = getValidationRule(schemaType, 'precision') + const onlyPositiveNumber = typeof minRule?.constraint === 'number' && minRule?.constraint >= 0 + const onlyIntegers = integerRule || precisionRule?.constraint === 0 + + // eslint-disable-next-line no-nested-ternary + const inputMode = onlyPositiveNumber ? (onlyIntegers ? 'numeric' : 'decimal') : 'text' return (