commit 01d69427eed244b3c3ff18627f39662902f6052e
parent 838fee3c1801df43c0415b1f90833890a37a9a7c
Author: ashermorgan <59518073+ashermorgan@users.noreply.github.com>
Date: Tue, 12 Mar 2024 19:08:49 -0700
Use native HTML validation in input componenets
Diffstat:
6 files changed, 107 insertions(+), 751 deletions(-)
diff --git a/src/assets/global.css b/src/assets/global.css
@@ -31,6 +31,18 @@ a:focus, .link:focus {
}
}
+/* remove spinner from numeric inputs */
+input[type=number] {
+ -moz-appearance: textfield;
+ appearance: textfield;
+ margin: 0;
+}
+input[type=number]::-webkit-inner-spin-button,
+input[type=number]::-webkit-outer-spin-button {
+ -webkit-appearance: none;
+ margin: 0;
+}
+
/* styles for tables */
table {
border-collapse: collapse;
@@ -110,6 +122,9 @@ dialog {
a, .link {
color: var(--link);
}
+input:invalid:not(:focus) {
+ border: 1px solid var(--error);
+}
/* light/default theme */
:root {
@@ -136,6 +151,9 @@ a, .link {
/* The color of links */
--link: hsl(210, 100%, 40%);
+
+ /* The error color */
+ --error: hsl(0, 100%, 40%);
}
/* dark mode */
diff --git a/src/components/DecimalInput.vue b/src/components/DecimalInput.vue
@@ -1,10 +1,5 @@
<template>
- <input
- ref="input"
- @blur="onblur"
- @keydown="onkeydown"
- @keypress="onkeypress"
- v-model="stringValue">
+ <input ref="input" type="number" step="any" required @blur="onblur" v-model="stringValue">
</template>
<script>
@@ -23,38 +18,6 @@ export default {
},
/**
- * The minimum value
- */
- min: {
- type: Number,
- default: null,
- },
-
- /**
- * The maximum value
- */
- max: {
- type: Number,
- default: null,
- },
-
- /**
- * The step value
- */
- step: {
- type: Number,
- default: 1,
- },
-
- /**
- * Whether to allow the user to increment/decrement the value using the arrow keys
- */
- arrowKeys: {
- type: Boolean,
- default: true,
- },
-
- /**
* The number of digits to show before the decimal point
*/
padding: {
@@ -80,71 +43,15 @@ export default {
data() {
return {
/**
- * The internal value
+ * The internal float value
*/
- internalValue: this.format(this.modelValue),
- };
- },
+ internalValue: this.modelValue,
- computed: {
- /**
- * The value of the input element
- */
- stringValue: {
- get() {
- return this.internalValue;
- },
- set(newValue) {
- // Parse new value
- const parsedValue = this.parse(newValue);
-
- if (newValue === '' || newValue === '-' || newValue === '.') {
- // Allow input to be '' or '-' or '.'
- this.internalValue = newValue;
- } else if (this.min !== null && parsedValue < this.min) {
- // Enforce minimum
- this.internalValue = this.format(this.min);
- } else if (this.max !== null && parsedValue > this.max) {
- // Enforce maximum
- this.internalValue = this.format(this.max);
- } else if (!Number.isNaN(parsedValue)) {
- // Allow valid numbers
- this.internalValue = newValue;
- }
-
- // Make sure input element is updated
- if (this.$refs.input.value === newValue) {
- // Setter was called by the input element
- if (this.internalValue !== newValue) {
- // The value was corrected, so the input element must be updated
- this.$refs.input.value = this.internalValue;
- }
- }
- },
- },
-
- /**
- * The value of the component
- */
- decValue: {
- get() {
- const parsedValue = parseFloat(this.stringValue);
- return Number.isNaN(parsedValue) ? this.defaultValue : parsedValue;
- },
- set(newValue) {
- this.stringValue = this.format(newValue);
- },
- },
-
- /**
- * The default value of the component
- */
- defaultValue() {
- if (this.min > 0 || this.max < 0) {
- return this.min;
- }
- return 0;
- },
+ /**
+ * The raw string value (empty if input is currently invalid)
+ */
+ stringValue: this.format(this.modelValue),
+ };
},
watch: {
@@ -153,63 +60,39 @@ export default {
* @param {Number} newValue The new prop value
*/
modelValue(newValue) {
- if (newValue !== this.decValue) {
- this.decValue = newValue;
+ if (newValue !== this.internalValue) {
+ this.internalValue = newValue;
+ this.stringValue = this.format(this.internalValue);
}
},
/**
- * Emit the input event when the component value changes
- * @param {Number} newValue The new component value
+ * Emit the input event when the internal value changes
+ * @param {Number} newValue The new internal float value
*/
- decValue(newValue) {
+ internalValue(newValue) {
this.$emit('update:modelValue', newValue);
},
- },
-
- methods: {
- /**
- * Restrict input to valid keys
- * @param {Object} e The keypress event args
- */
- onkeypress(e) {
- const valid = ['.', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
- if (!valid.includes(e.key)) {
- /* key was not valid */
- e.preventDefault();
- }
- },
/**
- * Process up and down arrow presses
- * @param {Object} e The keydown event args
+ * Update the float value when the raw string value changes
+ * @param {Number} newValue The new raw string value
*/
- onkeydown(e) {
- if (this.arrowKeys) {
- if (e.key === 'ArrowUp') {
- this.decValue += this.step;
- e.preventDefault();
- } else if (e.key === 'ArrowDown') {
- this.decValue -= this.step;
- e.preventDefault();
- }
+ stringValue(newValue) {
+ if (this.$refs.input.validity.valid) {
+ this.internalValue = Number(newValue);
}
},
+ },
+ methods: {
/**
- * Reformat display value
+ * Reformat display value if not invalid
*/
onblur() {
- this.stringValue = this.format(this.decValue);
- },
-
- /**
- * Parse a decimal from a string
- * @param {String} value The string
- * @returns {Number} The parsed decimal
- */
- parse(value) {
- return Number(value);
+ if (this.$refs.input.validity.valid) {
+ this.stringValue = this.format(this.internalValue);
+ }
},
/**
diff --git a/src/components/IntegerInput.vue b/src/components/IntegerInput.vue
@@ -1,10 +1,5 @@
<template>
- <input
- ref="input"
- @blur="onblur"
- @keydown="onkeydown"
- @keypress="onkeypress"
- v-model="stringValue">
+ <input ref="input" type="number" step="1" required @blur="onblur" v-model="stringValue">
</template>
<script>
@@ -21,38 +16,6 @@ export default {
},
/**
- * The minimum value
- */
- min: {
- type: Number,
- default: null,
- },
-
- /**
- * The maximum value
- */
- max: {
- type: Number,
- default: null,
- },
-
- /**
- * The step value
- */
- step: {
- type: Number,
- default: 1,
- },
-
- /**
- * Whether to allow the user to increment/decrement the value using the arrow keys
- */
- arrowKeys: {
- type: Boolean,
- default: true,
- },
-
- /**
* The number of digits to show before the decimal point
*/
padding: {
@@ -67,71 +30,15 @@ export default {
data() {
return {
/**
- * The internal value
+ * The internal integer value
*/
- internalValue: this.format(this.modelValue),
- };
- },
-
- computed: {
- /**
- * The value of the input element
- */
- stringValue: {
- get() {
- return this.internalValue;
- },
- set(newValue) {
- // Parse new value
- const parsedValue = this.parse(newValue);
-
- if (newValue === '' || newValue === '-') {
- // Allow input to be '' or '-'
- this.internalValue = newValue;
- } else if (this.min !== null && parsedValue < this.min) {
- // Enforce minimum
- this.internalValue = this.format(this.min);
- } else if (this.max !== null && parsedValue > this.max) {
- // Enforce maximum
- this.internalValue = this.format(this.max);
- } else if (!Number.isNaN(parsedValue)) {
- // Allow valid numbers
- this.internalValue = newValue;
- }
-
- // Make sure input element is updated
- if (this.$refs.input.value === newValue) {
- // Setter was called by the input element
- if (this.internalValue !== newValue) {
- // The value was corrected, so the input element must be updated
- this.$refs.input.value = this.internalValue;
- }
- }
- },
- },
-
- /**
- * The value of the component
- */
- intValue: {
- get() {
- const parsedValue = parseInt(this.stringValue, 10);
- return Number.isNaN(parsedValue) ? this.defaultValue : parsedValue;
- },
- set(newValue) {
- this.stringValue = this.format(newValue);
- },
- },
+ internalValue: this.modelValue,
- /**
- * The default value of the component
- */
- defaultValue() {
- if (this.min > 0 || this.max < 0) {
- return this.min;
- }
- return 0;
- },
+ /**
+ * The raw string value (empty if input is currently invalid)
+ */
+ stringValue: this.format(this.modelValue),
+ };
},
watch: {
@@ -140,68 +47,39 @@ export default {
* @param {Number} newValue The new prop value
*/
modelValue(newValue) {
- if (newValue !== this.intValue) {
- this.intValue = newValue;
+ if (newValue !== this.internalValue) {
+ this.internalValue = newValue;
+ this.stringValue = this.format(this.internalValue);
}
},
/**
- * Emit the input event when the component value changes
- * @param {Number} newValue The new component value
+ * Emit the input event when the internal value changes
+ * @param {Number} newValue The new internal integer value
*/
- intValue(newValue) {
+ internalValue(newValue) {
this.$emit('update:modelValue', newValue);
},
- },
-
- methods: {
- /**
- * Restrict input to numbers
- * @param {Object} e The keypress event args
- */
- onkeypress(e) {
- const validKeys = ['-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
- if (!validKeys.includes(e.key)) {
- /* key was not a number */
- e.preventDefault();
- }
- },
/**
- * Process up and down arrow presses
- * @param {Object} e The keydown event args
+ * Update the integer value when the raw string value changes
+ * @param {Number} newValue The new raw string value
*/
- onkeydown(e) {
- if (this.arrowKeys) {
- if (e.key === 'ArrowUp') {
- this.intValue += this.step;
- e.preventDefault();
- } else if (e.key === 'ArrowDown') {
- this.intValue -= this.step;
- e.preventDefault();
- }
+ stringValue(newValue) {
+ if (this.$refs.input.validity.valid) {
+ this.internalValue = Number(newValue);
}
},
+ },
+ methods: {
/**
- * Reformat display value
+ * Reformat display value if not invalid
*/
onblur() {
- this.stringValue = this.format(this.intValue);
- },
-
- /**
- * Parse an integer from a string
- * @param {String} value The string
- * @returns {Number} The parsed integer
- */
- parse(value) {
- if (value.includes('.')) {
- // value cannot be parsed as an integer
- return NaN;
+ if (this.$refs.input.validity.valid) {
+ this.stringValue = this.format(this.internalValue);
}
-
- return Number(value);
},
/**
diff --git a/src/components/TimeInput.vue b/src/components/TimeInput.vue
@@ -1,16 +1,15 @@
<template>
<div class="time-input">
<integer-input class="hours" :aria-label="label + ' hours'" v-if="showHours"
- :min="0" :max="99" :padding="1" v-model="hours"
- :arrow-keys="false" @keydown="onkeydown($event, 3600)"/>
+ :min="0" :max="99" :padding="1" v-model="hours" @keydown="onkeydown($event, 3600)"/>
<span v-if="showHours">:</span>
<integer-input class="minutes" :aria-label="label + ' minutes'"
:min="0" :max="59" :padding="2" v-model="minutes"
- :arrow-keys="false" @keydown="onkeydown($event, 60)"/>
+ @keydown="onkeydown($event, 60)"/>
<span>:</span>
<decimal-input class="seconds" :aria-label="label + ' seconds'"
:min="0" :max="59.99" :padding="2" :digits="2" v-model="seconds"
- :arrow-keys="false" @keydown="onkeydown($event, 1)"/>
+ @keydown="onkeydown($event, 1)"/>
</div>
</template>
@@ -136,17 +135,17 @@ export default {
*/
onkeydown(e, step = 1) {
if (e.key === 'ArrowUp') {
- if (this.internalValue + step > this.max) {
+ if (Math.floor(this.internalValue) + step > this.max) {
this.internalValue = this.max;
} else {
- this.internalValue += step;
+ this.internalValue = Math.floor(this.internalValue) + step;
}
e.preventDefault();
} else if (e.key === 'ArrowDown') {
- if (this.internalValue - step < 0) {
+ if (Math.ceil(this.internalValue) - step < 0) {
this.internalValue = 0;
} else {
- this.internalValue -= step;
+ this.internalValue = Math.ceil(this.internalValue) - step;
}
e.preventDefault();
}
diff --git a/tests/unit/components/DecimalInput.spec.js b/tests/unit/components/DecimalInput.spec.js
@@ -2,274 +2,52 @@ import { test, expect } from 'vitest';
import { mount } from '@vue/test-utils';
import DecimalInput from '@/components/DecimalInput.vue';
-test('value should be 0.0 by default', () => {
- // Initialize component
- const wrapper = mount(DecimalInput);
-
- // Assert value is 0.0
- expect(wrapper.find('input').element.value).to.equal('0.0');
-});
-
-test('should read value prop', () => {
+test('should be initialized to modelValue', () => {
// Initialize component
const wrapper = mount(DecimalInput, {
- propsData: { modelValue: 1 },
+ propsData: { modelValue: 1.2 },
});
- // Assert value is 1.0
- expect(wrapper.find('input').element.value).to.equal('1.0');
-});
-
-test('up arrow should increment value by step', async () => {
- // Initialize component
- const wrapper = mount(DecimalInput, {
- propsData: { step: 0.2 },
- });
-
- // Press up arrow
- await wrapper.trigger('keydown', { key: 'ArrowUp' });
-
- // Assert value is 0.2 and input event was emitted
- expect(wrapper.find('input').element.value).to.equal('0.2');
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[0.2]]);
-});
-
-test('down arrow should increment value by step', async () => {
- // Initialize component
- const wrapper = mount(DecimalInput, {
- propsData: { step: 0.2 },
- });
-
- // Press down arrow
- await wrapper.trigger('keydown', { key: 'ArrowDown' });
-
- // Assert value is -0.2 and input event was emitted
- expect(wrapper.find('input').element.value).to.equal('-0.2');
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[-0.2]]);
+ // Assert value is 1.2
+ expect(wrapper.find('input').element.value).to.equal('1.2');
});
test('should fire input event when value changes', async () => {
// Initialize component
const wrapper = mount(DecimalInput);
- // Set value to 1
- wrapper.find('input').element.value = '1.0';
- await wrapper.find('input').trigger('input');
+ // Set value to 1.2
+ await wrapper.find('input').setValue('1.2')
// Assert input event was emitted
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[1.0]]);
-});
-
-test('should accept numerical values', async () => {
- // Initialize component
- const wrapper = mount(DecimalInput);
-
- // Try to set value to 1
- wrapper.find('input').element.value = '1';
- await wrapper.find('input').trigger('input');
-
- // Assert value was accepted and input event was emitted
- expect(wrapper.find('input').element.value).to.equal('1');
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[1.0]]);
+ expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[1.2]]);
});
-test('should accept decimal values', async () => {
- // Initialize component
- const wrapper = mount(DecimalInput, {
- propsData: { modelValue: 1 },
- });
-
- // Try to set value to 1.5
- wrapper.find('input').element.value = '1.5';
- await wrapper.find('input').trigger('input');
-
- // Assert value was accepted and input event was emitted
- expect(wrapper.find('input').element.value).to.equal('1.5');
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[1.5]]);
-});
-
-test('should not accept non numerical values', async () => {
+test('should format value according to padding and digits props', async () => {
// Initialize component
const wrapper = mount(DecimalInput, {
- propsData: { modelValue: 1 },
+ propsData: { padding: 2, digits: 3 },
});
- // Try to set value to a
- wrapper.find('input').element.value = 'a';
- await wrapper.find('input').trigger('input');
-
- // Assert value was not accepted and no events were emitted
- expect(wrapper.find('input').element.value).to.equal('1.0');
- expect(wrapper.emitted()['update:modelValue']).to.equal(undefined);
+ // Assert value is correctly formatted
+ expect(wrapper.find('input').element.value).to.equal('00.000');
});
test('should format input value on blur', async () => {
// Initialize component
const wrapper = mount(DecimalInput, {
- propsData: { modelValue: 1, padding: 3, digits: 2 },
- });
-
- // Set value to '01'
- wrapper.find('input').element.value = '01';
- await wrapper.find('input').trigger('input');
-
- // Assert value was not updated and no events were emitted
- expect(wrapper.find('input').element.value).to.equal('01');
- expect(wrapper.emitted()['update:modelValue']).to.equal(undefined);
-
- // Trigger blur event
- await wrapper.find('input').trigger('blur');
-
- // Assert value was formatted but no events were emitted
- expect(wrapper.find('input').element.value).to.equal('001.00');
- expect(wrapper.emitted()['update:modelValue']).to.equal(undefined);
-});
-
-test('should allow input to be empty until blur', async () => {
- // Initialize component
- const wrapper = mount(DecimalInput, {
- propsData: { modelValue: 5 },
+ propsData: { modelValue: 1, padding: 2, digits: 2 },
});
- // Set value to ''
- wrapper.find('input').element.value = '';
- await wrapper.find('input').trigger('input');
+ // Set value to '1.2'
+ await wrapper.find('input').setValue('1.2');
- // Assert value is '' and input event was emitted with default value
- expect(wrapper.find('input').element.value).to.equal('');
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[0.0]]);
+ // Assert value was not updated
+ expect(wrapper.find('input').element.value).to.equal('1.2');
// Trigger blur event
await wrapper.find('input').trigger('blur');
- // Assert value is the default value but no new events were emitted
- expect(wrapper.find('input').element.value).to.equal('0.0');
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[0.0]]);
-});
-
-test('should allow input to be "-" until blur', async () => {
- // Initialize component
- const wrapper = mount(DecimalInput, {
- propsData: { modelValue: 5 },
- });
-
- // Set value to '-'
- wrapper.find('input').element.value = '-';
- await wrapper.find('input').trigger('input');
-
- // Assert value is '-' and input event was emitted with default value
- expect(wrapper.find('input').element.value).to.equal('-');
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[0.0]]);
-
- // Trigger blur event
- await wrapper.find('input').trigger('blur');
-
- // Assert value is the default value but no new events were emitted
- expect(wrapper.find('input').element.value).to.equal('0.0');
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[0.0]]);
-});
-
-test('should allow input to be "." until blur', async () => {
- // Initialize component
- const wrapper = mount(DecimalInput, {
- propsData: { modelValue: 5 },
- });
-
- // Set value to '.'
- wrapper.find('input').element.value = '.';
- await wrapper.find('input').trigger('input');
-
- // Assert value is '.' and input event was emitted with default value
- expect(wrapper.find('input').element.value).to.equal('.');
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[0.0]]);
-
- // Trigger blur event
- await wrapper.find('input').trigger('blur');
-
- // Assert value is the default value but no new events were emitted
- expect(wrapper.find('input').element.value).to.equal('0.0');
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[0.0]]);
-});
-
-test('default value should be the minimum if 0.0 is not valid', async () => {
- // Initialize component
- const wrapper = mount(DecimalInput, {
- propsData: { modelValue: 3, max: 4, min: 2 },
- });
-
- // Set value to '' and trigger blur event so value must be updated
- wrapper.find('input').element.value = '';
- await wrapper.find('input').trigger('input');
- await wrapper.find('input').trigger('blur');
-
- // Assert value is 2 and input event was emitted
- expect(wrapper.find('input').element.value).to.equal('2.0');
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[2.0]]);
-});
-
-test('should not allow input to be below the minimum', async () => {
- // Initialize component
- const wrapper = mount(DecimalInput, {
- propsData: { min: 10, modelValue: 20 },
- });
-
- // Try to set value to 9, which is below the minimum
- wrapper.find('input').element.value = '9.0';
- await wrapper.find('input').trigger('input');
-
- // Assert value is 10 and input event was emitted
- expect(wrapper.find('input').element.value).to.equal('10.0');
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[10.0]]);
-
- // Try to decrement value
- await wrapper.trigger('keydown', { key: 'ArrowDown' });
-
- // Assert value is still 10 and no new event were emitted
- expect(wrapper.find('input').element.value).to.equal('10.0');
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[10.0]]);
-});
-
-test('should not allow input to be above the maximum', async () => {
- // Initialize component
- const wrapper = mount(DecimalInput, {
- propsData: { max: 10 },
- });
-
- // Try to set value to 11, which is above the maximum
- wrapper.find('input').element.value = '11.0';
- await wrapper.find('input').trigger('input');
-
- // Assert value is 10 and input event was emitted
- expect(wrapper.find('input').element.value).to.equal('10.0');
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[10.0]]);
-
- // Try to increment value
- await wrapper.trigger('keydown', { key: 'ArrowUp' });
-
- // Assert value is still 10 and no new events were emitted
- expect(wrapper.find('input').element.value).to.equal('10.0');
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[10.0]]);
-});
-
-test('should format value according to padding and digits props', async () => {
- // Initialize component
- const wrapper = mount(DecimalInput, {
- propsData: { padding: 2, digits: 3 },
- });
-
- // Assert value is correctly formatted
- expect(wrapper.find('input').element.value).to.equal('00.000');
-});
-
-test('should emit keydown event if arrow-keys is false', async () => {
- // Initialize component
- const wrapper = mount(DecimalInput, {
- propsData: { arrowKeys: false },
- });
-
- // Try to increment value
- await wrapper.trigger('keydown', { key: 'ArrowUp' });
-
- // Assert keydown event emitted
- expect(wrapper.emitted().keydown.length).to.equal(1);
+ // Assert value was formatted
+ expect(wrapper.find('input').element.value).to.equal('01.20');
});
diff --git a/tests/unit/components/IntegerInput.spec.js b/tests/unit/components/IntegerInput.spec.js
@@ -2,50 +2,14 @@ import { test, expect } from 'vitest';
import { mount } from '@vue/test-utils';
import IntegerInput from '@/components/IntegerInput.vue';
-test('value should be 0 by default', () => {
- // Initialize component
- const wrapper = mount(IntegerInput);
-
- // Assert value is 0
- expect(wrapper.find('input').element.value).to.equal('0');
-});
-
-test('should read value prop', () => {
+test('should be initialized to modelValue', () => {
// Initialize component
const wrapper = mount(IntegerInput, {
- propsData: { modelValue: 1 },
+ propsData: { modelValue: 123 },
});
// Assert value is 1
- expect(wrapper.find('input').element.value).to.equal('1');
-});
-
-test('up arrow should increment value by step', async () => {
- // Initialize component
- const wrapper = mount(IntegerInput, {
- propsData: { step: 2 },
- });
-
- // Press up arrow
- await wrapper.trigger('keydown', { key: 'ArrowUp' });
-
- // Assert value is 1 and input event was emitted
- expect(wrapper.find('input').element.value).to.equal('2');
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[2]]);
-});
-
-test('down arrow should increment value by step', async () => {
- // Initialize component
- const wrapper = mount(IntegerInput, {
- propsData: { step: 2 },
- });
-
- // Press down arrow
- await wrapper.trigger('keydown', { key: 'ArrowDown' });
-
- // Assert value is -1 and input event was emitted
- expect(wrapper.find('input').element.value).to.equal('-2');
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[-2]]);
+ expect(wrapper.find('input').element.value).to.equal('123');
});
test('should fire input event when value changes', async () => {
@@ -53,201 +17,37 @@ test('should fire input event when value changes', async () => {
const wrapper = mount(IntegerInput);
// Set value to 1
- wrapper.find('input').element.value = '1';
- await wrapper.find('input').trigger('input');
+ await wrapper.find('input').setValue('1');
// Assert input event was emitted
expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[1]]);
});
-test('should accept numerical values', async () => {
- // Initialize component
- const wrapper = mount(IntegerInput);
-
- // Try to set value to 1
- wrapper.find('input').element.value = '1';
- await wrapper.find('input').trigger('input');
-
- // Assert value was accepted and input event was emitted
- expect(wrapper.find('input').element.value).to.equal('1');
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[1]]);
-});
-
-test('should not accept decimal values', async () => {
- // Initialize component
- const wrapper = mount(IntegerInput, {
- propsData: { modelValue: 1 },
- });
-
- // Try to set value to 1.5
- wrapper.find('input').element.value = '1.5';
- await wrapper.find('input').trigger('input');
-
- // Assert value was not accepted and no events were emitted
- expect(wrapper.find('input').element.value).to.equal('1');
- expect(wrapper.emitted()['update:modelValue']).to.equal(undefined);
-});
-
-test('should not accept non numerical values', async () => {
+test('should format value according to padding prop', async () => {
// Initialize component
const wrapper = mount(IntegerInput, {
- propsData: { modelValue: 1 },
+ propsData: { padding: 2 },
});
- // Try to set value to a
- wrapper.find('input').element.value = 'a';
- await wrapper.find('input').trigger('input');
-
- // Assert value was not accepted and no events were emitted
- expect(wrapper.find('input').element.value).to.equal('1');
- expect(wrapper.emitted()['update:modelValue']).to.equal(undefined);
+ // Assert value is correctly formatted
+ expect(wrapper.find('input').element.value).to.equal('00');
});
test('should format input value on blur', async () => {
// Initialize component
const wrapper = mount(IntegerInput, {
- propsData: { modelValue: 1, padding: 3 },
- });
-
- // Set value to '01'
- wrapper.find('input').element.value = '01';
- await wrapper.find('input').trigger('input');
-
- // Assert value was not updated and no events were emitted
- expect(wrapper.find('input').element.value).to.equal('01');
- expect(wrapper.emitted()['update:modelValue']).to.equal(undefined);
-
- // Trigger blur event
- await wrapper.find('input').trigger('blur');
-
- // Assert value was formatted but no events were emitted
- expect(wrapper.find('input').element.value).to.equal('001');
- expect(wrapper.emitted()['update:modelValue']).to.equal(undefined);
-});
-
-test('should allow input to be empty until blur', async () => {
- // Initialize component
- const wrapper = mount(IntegerInput, {
- propsData: { modelValue: 5 },
- });
-
- // Set value to ''
- wrapper.find('input').element.value = '';
- await wrapper.find('input').trigger('input');
-
- // Assert value is '' and input event was emitted with default value
- expect(wrapper.find('input').element.value).to.equal('');
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[0]]);
-
- // Trigger blur event
- await wrapper.find('input').trigger('blur');
-
- // Assert value is the default value but no new events were emitted
- expect(wrapper.find('input').element.value).to.equal('0');
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[0]]);
-});
-
-test('should allow input to be "-" until blur', async () => {
- // Initialize component
- const wrapper = mount(IntegerInput, {
- propsData: { modelValue: 5 },
+ propsData: { modelValue: 1, padding: 2 },
});
- // Set value to '-'
- wrapper.find('input').element.value = '-';
- await wrapper.find('input').trigger('input');
+ // Set value to '2'
+ await wrapper.find('input').setValue('2');
- // Assert value is '-' and input event was emitted with default value
- expect(wrapper.find('input').element.value).to.equal('-');
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[0]]);
+ // Assert value was not updated
+ expect(wrapper.find('input').element.value).to.equal('2');
// Trigger blur event
await wrapper.find('input').trigger('blur');
- // Assert value is the default value but no new events were emitted
- expect(wrapper.find('input').element.value).to.equal('0');
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[0]]);
-});
-
-test('default value should be the minimum if 0 is not valid', async () => {
- // Initialize component
- const wrapper = mount(IntegerInput, {
- propsData: { modelValue: 3, max: 4, min: 2 },
- });
-
- // Set value to '' and trigger blur event so value must be updated
- wrapper.find('input').element.value = '';
- await wrapper.find('input').trigger('input');
- await wrapper.find('input').trigger('blur');
-
- // Assert value is 2 and input event was emitted
- expect(wrapper.find('input').element.value).to.equal('2');
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[2]]);
-});
-
-test('should not allow input to be below the minimum', async () => {
- // Initialize component
- const wrapper = mount(IntegerInput, {
- propsData: { min: 10, modelValue: 20 },
- });
-
- // Try to set value to 9, which is below the minimum
- wrapper.find('input').element.value = '9';
- await wrapper.find('input').trigger('input');
-
- // Assert value is 10 and input event was emitted
- expect(wrapper.find('input').element.value).to.equal('10');
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[10]]);
-
- // Try to decrement value
- await wrapper.trigger('keydown', { key: 'ArrowDown' });
-
- // Assert value is still 10 and no new event were emitted
- expect(wrapper.find('input').element.value).to.equal('10');
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[10]]);
-});
-
-test('should not allow input to be above the maximum', async () => {
- // Initialize component
- const wrapper = mount(IntegerInput, {
- propsData: { max: 10 },
- });
-
- // Try to set value to 11, which is above the maximum
- wrapper.find('input').element.value = '11';
- await wrapper.find('input').trigger('input');
-
- // Assert value is 10 and input event was emitted
- expect(wrapper.find('input').element.value).to.equal('10');
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[10]]);
-
- // Try to increment value
- await wrapper.trigger('keydown', { key: 'ArrowUp' });
-
- // Assert value is still 10 and no new events were emitted
- expect(wrapper.find('input').element.value).to.equal('10');
- expect(wrapper.emitted()['update:modelValue']).to.deep.equal([[10]]);
-});
-
-test('should format value according to padding prop', async () => {
- // Initialize component
- const wrapper = mount(IntegerInput, {
- propsData: { padding: 2 },
- });
-
- // Assert value is correctly formatted
- expect(wrapper.find('input').element.value).to.equal('00');
-});
-
-test('should emit keydown event if arrow-keys is false', async () => {
- // Initialize component
- const wrapper = mount(IntegerInput, {
- propsData: { arrowKeys: false },
- });
-
- // Try to increment value
- await wrapper.trigger('keydown', { key: 'ArrowUp' });
-
- // Assert keydown event emitted
- expect(wrapper.emitted().keydown.length).to.equal(1);
+ // Assert value was formatted
+ expect(wrapper.find('input').element.value).to.equal('02');
});