Skip to content
This repository has been archived by the owner on Nov 29, 2023. It is now read-only.

Commit

Permalink
Fix: allow arbitrary precision decimal values (#932)
Browse files Browse the repository at this point in the history
For future reference: this relies on explicit behavior in enketo-transformer which sets `step="any"` on decimal inputs. The affected tests have been updated accordingly to use the transformed result, rather than arbitrary HTML string literals.
  • Loading branch information
eyelidlessness authored Dec 5, 2022
1 parent e0e1c1a commit 52a790b
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 86 deletions.
10 changes: 0 additions & 10 deletions src/widget/number-input/decimal-input.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,4 @@ export default class DecimalInput extends NumberInput {
set value(value) {
super.value = value;
}

/**
* @param {HTMLInputElement} input
* @param {any} options
*/
constructor(input, options) {
super(input, options);

input.step = 0.1;
}
}
30 changes: 30 additions & 0 deletions test/forms/number-input-widgets.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0"?>
<h:html xmlns="http://www.w3.org/2002/xforms"
xmlns:ev="http://www.w3.org/2001/xml-events"
xmlns:h="http://www.w3.org/1999/xhtml"
xmlns:jr="http://openrosa.org/javarosa"
xmlns:odk="http://www.opendatakit.org/xforms"
xmlns:orx="http://openrosa.org/xforms"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<h:head>
<h:title>Number input widgets</h:title>
<model>
<instance>
<data>
<int/>
<decimal/>
</data>
</instance>
<bind nodeset="/data/int" type="int"/>
<bind nodeset="/data/decimal" type="decimal"/>
</model>
</h:head>
<h:body>
<input ref="/data/int">
<label>Integer</label>
</input>
<input ref="/data/decimal">
<label>Decimal</label>
</input>
</h:body>
</h:html>
6 changes: 3 additions & 3 deletions test/helpers/test-widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,13 @@ function testBasicInstantiation(Widget, template, options = { a: 'b' }) {
const fragment = document
.createRange()
.createContextualFragment(template);
const control = fragment.querySelector(Widget.selector);
const question =
Widget.selector === 'form'
? fragment.querySelector('form.or')
: fragment.querySelector('.question');
? control.closest('form.or')
: control.closest('.question');
question.classList.add('or-appearance-one');
question.classList.add('or-appearance-two');
const control = fragment.querySelector(Widget.selector);

Promise.resolve()
.then(() => new Widget(control, options))
Expand Down
124 changes: 51 additions & 73 deletions test/spec/widget.number-input.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,27 @@ import input from '../../src/js/input';
import events from '../../src/js/event';
import DecimalInput from '../../src/widget/number-input/decimal-input';
import IntegerInput from '../../src/widget/number-input/integer-input';
import loadForm from '../helpers/load-form';
import { runAllCommonWidgetTests } from '../helpers/test-widget';

const integerForm = `<label class="question non-select" lang="en">
<span lang="" class="question-label active">Number</span>
<input type="number" name="/widgets/integer" data-type-xml="int">
</label>`;
const decimalForm = integerForm.replace(
'data-type-xml="int"',
'data-type-xml="decimal"'
);
/**
* @typedef {import('../../src/js/form').Form} Form
*/

describe('Number inputs', () => {
let form = loadForm('number-input-widgets.xml').view.html;

/** @type {HTMLFormElement} */
let clone;

beforeEach(() => {
clone = form.cloneNode(true);
});

afterEach(() => {
form = clone;
});

[
{ type: 'int', excludedType: 'decimal', Widget: IntegerInput },
{ type: 'decimal', excludedType: 'int', Widget: DecimalInput },
Expand Down Expand Up @@ -66,13 +75,10 @@ describe('Number inputs', () => {
});

describe('integer', () => {
runAllCommonWidgetTests(IntegerInput, integerForm, '2');
runAllCommonWidgetTests(IntegerInput, form.outerHTML, '2');

it('is valid with an integer value', async () => {
const fragment = document
.createRange()
.createContextualFragment(integerForm);
const control = fragment.querySelector(IntegerInput.selector);
const control = form.querySelector(IntegerInput.selector);
const value = '4';
const widget = new IntegerInput(control);

Expand All @@ -88,10 +94,7 @@ describe('Number inputs', () => {
});

it('is valid with a negative integer value', async () => {
const fragment = document
.createRange()
.createContextualFragment(integerForm);
const control = fragment.querySelector(IntegerInput.selector);
const control = form.querySelector(IntegerInput.selector);
const value = '-4';
const widget = new IntegerInput(control);

Expand All @@ -107,10 +110,7 @@ describe('Number inputs', () => {
});

it('is invalid with a decimal value', async () => {
const fragment = document
.createRange()
.createContextualFragment(integerForm);
const control = fragment.querySelector(IntegerInput.selector);
const control = form.querySelector(IntegerInput.selector);
const value = '4.1';
const widget = new IntegerInput(control);

Expand All @@ -123,11 +123,24 @@ describe('Number inputs', () => {
expect(question.classList.contains('invalid-value')).to.equal(true);
});

it('is valid with a decimal value with multiple decimal digits', async () => {
const control = form.querySelector(DecimalInput.selector);
const value = '4.11';
const widget = new DecimalInput(control);

input.setVal(control, value, events.Input());

await Promise.resolve();

const { question } = widget;

expect(question.classList.contains('invalid-value')).to.equal(
false
);
});

it('clears a programmatically assigned value with a misplaced negation character', async () => {
const fragment = document
.createRange()
.createContextualFragment(integerForm);
const control = fragment.querySelector(IntegerInput.selector);
const control = form.querySelector(IntegerInput.selector);
const initialValue = '4';
const assignedValue = '4-';
const widget = new IntegerInput(control);
Expand All @@ -141,10 +154,7 @@ describe('Number inputs', () => {
});

it('is invalid with a user-entered misplaced negation character', async () => {
const fragment = document
.createRange()
.createContextualFragment(integerForm);
const control = fragment.querySelector(IntegerInput.selector);
const control = form.querySelector(IntegerInput.selector);
const initialValue = '4';
const enteredValue = '4-';
const widget = new IntegerInput(control);
Expand All @@ -164,14 +174,11 @@ describe('Number inputs', () => {
});

describe('decimal', () => {
runAllCommonWidgetTests(DecimalInput, decimalForm, '2');
runAllCommonWidgetTests(DecimalInput, decimalForm, '2.1');
runAllCommonWidgetTests(DecimalInput, form.outerHTML, '2');
runAllCommonWidgetTests(DecimalInput, form.outerHTML, '2.1');

it('is valid with an integer value', async () => {
const fragment = document
.createRange()
.createContextualFragment(decimalForm);
const control = fragment.querySelector(DecimalInput.selector);
const control = form.querySelector(DecimalInput.selector);
const value = '4';
const widget = new DecimalInput(control);

Expand All @@ -187,10 +194,7 @@ describe('Number inputs', () => {
});

it('is valid with a decimal value', async () => {
const fragment = document
.createRange()
.createContextualFragment(decimalForm);
const control = fragment.querySelector(DecimalInput.selector);
const control = form.querySelector(DecimalInput.selector);
const value = '4.1';
const widget = new DecimalInput(control);

Expand All @@ -206,10 +210,7 @@ describe('Number inputs', () => {
});

it('is valid with a negative integer value', async () => {
const fragment = document
.createRange()
.createContextualFragment(decimalForm);
const control = fragment.querySelector(DecimalInput.selector);
const control = form.querySelector(DecimalInput.selector);
const value = '-4';
const widget = new DecimalInput(control);

Expand All @@ -225,10 +226,7 @@ describe('Number inputs', () => {
});

it('is valid with a negative decimal value', async () => {
const fragment = document
.createRange()
.createContextualFragment(decimalForm);
const control = fragment.querySelector(DecimalInput.selector);
const control = form.querySelector(DecimalInput.selector);
const value = '-4.1';
const widget = new DecimalInput(control);

Expand All @@ -254,10 +252,7 @@ describe('Number inputs', () => {

if (supportsTrailingDecimal()) {
it('is valid with a trailing decimal character', async () => {
const fragment = document
.createRange()
.createContextualFragment(decimalForm);
const control = fragment.querySelector(DecimalInput.selector);
const control = form.querySelector(DecimalInput.selector);
const value = '4.';
const widget = new DecimalInput(control);

Expand All @@ -275,10 +270,7 @@ describe('Number inputs', () => {
});

it('clears a programmatically assigned value with multiple decimals', async () => {
const fragment = document
.createRange()
.createContextualFragment(decimalForm);
const control = fragment.querySelector(DecimalInput.selector);
const control = form.querySelector(DecimalInput.selector);
const initialValue = '4';
const assignedValue = '4.0.1';
const widget = new DecimalInput(control);
Expand All @@ -292,10 +284,7 @@ describe('Number inputs', () => {
});

it('is invalid with a user-entered value with multiple decimals', async () => {
const fragment = document
.createRange()
.createContextualFragment(decimalForm);
const control = fragment.querySelector(DecimalInput.selector);
const control = form.querySelector(DecimalInput.selector);
const initialValue = '4';
const enteredValue = '4.0.1';
const widget = new DecimalInput(control);
Expand All @@ -317,10 +306,7 @@ describe('Number inputs', () => {
}

it('clears a programmatically assigned value with a misplaced negation character', async () => {
const fragment = document
.createRange()
.createContextualFragment(decimalForm);
const control = fragment.querySelector(DecimalInput.selector);
const control = form.querySelector(DecimalInput.selector);
const initialValue = '4';
const assignedValue = '4-.0';
const widget = new DecimalInput(control);
Expand All @@ -334,10 +320,7 @@ describe('Number inputs', () => {
});

it('is invalid with a user-entered misplaced negation character', async () => {
const fragment = document
.createRange()
.createContextualFragment(decimalForm);
const control = fragment.querySelector(DecimalInput.selector);
const control = form.querySelector(DecimalInput.selector);
const initialValue = '4';
const enteredValue = '4-';
const widget = new DecimalInput(control);
Expand Down Expand Up @@ -384,12 +367,7 @@ describe('Number inputs', () => {
});

it('allows entry of localized decimal characters', async () => {
const fragment = document
.createRange()
.createContextualFragment(decimalForm);
const control = fragment.querySelector(
DecimalInput.selector
);
const control = form.querySelector(DecimalInput.selector);
const value = '3,4';
const widget = new DecimalInput(control);

Expand Down

0 comments on commit 52a790b

Please sign in to comment.