Skip to content

Commit

Permalink
fix(form-builder): improve inputmode detection for number input and f…
Browse files Browse the repository at this point in the history
…ix tests

Co-authored-by: Raul de Melo <melo.raulf@gmail.com>
Co-authored-by: Bjørge Næss <bjoerge@gmail.com>
  • Loading branch information
2 people authored and skogsmaskin committed Dec 8, 2022
1 parent cf1c64b commit 11988da
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 6 deletions.
14 changes: 14 additions & 0 deletions dev/test-studio/schema/debug/validation.js
Expand Up @@ -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',
Expand Down
8 changes: 7 additions & 1 deletion packages/@sanity/form-builder/src/inputs/NumberInput.tsx
Expand Up @@ -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<HTMLInputElement>) => {
Expand All @@ -43,7 +49,7 @@ const NumberInput = React.forwardRef(function NumberInput(
<TextInput
type="number"
step="any"
inputMode={onlyPositiveNumber ? 'numeric' : 'text'}
inputMode={inputMode}
id={id}
customValidity={errors && errors.length > 0 ? errors[0].item.message : ''}
value={value}
Expand Down
Expand Up @@ -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},
],
},
],
},
],
},
],
})
Expand All @@ -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',
}

Expand All @@ -30,19 +62,49 @@ 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()
expect(input).toHaveAttribute('type', 'number')
})

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')
})
})

0 comments on commit 11988da

Please sign in to comment.