diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f28310a2..e9d6e1d66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Cleaned src/defaults.js and move resolvers to a separate module, src/resolvers.js - Cleaned how default themes, iconlibs, editors and templates are imported to JSONEditor - Added ability to attache editors and themes style rules to the shadowRoot if the editor is inside a Web Component. +- Fix of #701 - editors/number.js and editors/integer.js don't change values when validation is failed ### 2.0.0-dev - Fix of #643 - Allow use of themes not compiled directly into the build diff --git a/src/editors/integer.js b/src/editors/integer.js index 9d2ab3885..82ac99bb2 100644 --- a/src/editors/integer.js +++ b/src/editors/integer.js @@ -1,12 +1,15 @@ import { NumberEditor } from './number.js' +import { isInteger } from '../utilities.js' export class IntegerEditor extends NumberEditor { - sanitize (value) { - value = `${value}` - return value.replace(/[^0-9-]/g, '') - } - getNumColumns () { return 2 } + + getValue () { + if (!this.dependenciesFulfilled) { + return undefined + } + return isInteger(this.value) ? parseInt(this.value) : this.value + } } diff --git a/src/editors/number.js b/src/editors/number.js index f589e8fc6..b7ced79ca 100644 --- a/src/editors/number.js +++ b/src/editors/number.js @@ -1,4 +1,5 @@ import { StringEditor } from './string.js' +import { isNumber } from '../utilities' export class NumberEditor extends StringEditor { build () { @@ -33,10 +34,6 @@ export class NumberEditor extends StringEditor { this.setInputAttributes(['maxlength', 'pattern', 'readonly', 'min', 'max', 'step']) } - sanitize (value) { - return (`${value}`).replace(/[^0-9.\-eE]/g, '') - } - getNumColumns () { return 2 } @@ -45,6 +42,6 @@ export class NumberEditor extends StringEditor { if (!this.dependenciesFulfilled) { return undefined } - return this.value === '' ? undefined : this.value * 1 + return isNumber(this.value) ? parseFloat(this.value) : this.value } } diff --git a/src/utilities.js b/src/utilities.js index ef832621d..4826db1bf 100644 --- a/src/utilities.js +++ b/src/utilities.js @@ -65,3 +65,22 @@ export function getShadowParent (node) { export function hasOwnProperty (obj, key) { return obj && Object.prototype.hasOwnProperty.call(obj, key) } + +// From https://github.com/angular/angular.js/blob/master/src/ng/directive/input.js +const NUMBER_REGEXP = /^\s*(-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/ + +export function isNumber (value) { + if (typeof value === 'undefined' || value === null) return false + const match = value.match(NUMBER_REGEXP) + const v = parseFloat(value) + return match !== null && !isNaN(v) && isFinite(v) +} + +const INTEGER_REGEXP = /^\s*(-|\+)?(\d+)\s*$/ + +export function isInteger (value) { + if (typeof value === 'undefined' || value === null) return false + const match = value.match(INTEGER_REGEXP) + const v = parseInt(value) + return match !== null && !isNaN(v) && isFinite(v) +} diff --git a/tests/codeceptjs/editors/integer_test.js b/tests/codeceptjs/editors/integer_test.js index a36419171..bba9fa6c3 100644 --- a/tests/codeceptjs/editors/integer_test.js +++ b/tests/codeceptjs/editors/integer_test.js @@ -72,3 +72,13 @@ Scenario('should update output when setValue is called', async (I) => { I.click('.set-value'); I.see('2', '[data-schemapath="root.integer_range"] output'); }); + +Scenario('should validate value', async (I) => { + I.amOnPage('integer.html'); + await I.fillField('[name="root[integer]"]', '5-5'); + I.click('.get-value'); + I.see('Value must be of type integer.', '[data-schemapath="root.integer"] div'); + assert.equal(await I.grabValueFrom('.value'), '{"integer":"5-5","integer_number":5,"integer_range":5}'); +}); + + diff --git a/tests/codeceptjs/editors/number_test.js b/tests/codeceptjs/editors/number_test.js index ae2c730c9..82a47c3a7 100644 --- a/tests/codeceptjs/editors/number_test.js +++ b/tests/codeceptjs/editors/number_test.js @@ -8,6 +8,14 @@ Scenario('should have correct initial value', async (I) => { assert.equal(await I.grabValueFrom('.value'), '{"number":5.75,"number_number":5.75,"number_range":5.75}'); }); +Scenario('should validate value', async (I) => { + I.amOnPage('number.html'); + await I.fillField('[name="root[number]"]', '12-12'); + I.click('.get-value'); + I.see('Value must be of type number.', '[data-schemapath="root.number"] div'); + assert.equal(await I.grabValueFrom('.value'), '{"number":"12-12","number_number":5.75,"number_range":5.75}'); +}); + Scenario('should respect step by incrementing and decrementing the value of a number', async (I) => { I.amOnPage('number.html'); I.seeElement('[data-schemapath="root.number_number"] input'); diff --git a/tests/codeceptjs/editors/validation_test.js b/tests/codeceptjs/editors/validation_test.js index d9f2ed90d..1f03a1b5a 100644 --- a/tests/codeceptjs/editors/validation_test.js +++ b/tests/codeceptjs/editors/validation_test.js @@ -4,7 +4,7 @@ Feature('Validations'); Scenario('test validations in validation.html', (I) => { I.amOnPage('validation.html'); - var numberOfTestItemsExpected = 131; + var numberOfTestItemsExpected = 136; I.waitForElement("#output div:nth-child("+numberOfTestItemsExpected+")", 10); I.seeNumberOfElements("#output div", numberOfTestItemsExpected); I.see("success"); diff --git a/tests/fixtures/validation.json b/tests/fixtures/validation.json index 9bbe18882..bcf278a8a 100644 --- a/tests/fixtures/validation.json +++ b/tests/fixtures/validation.json @@ -1,4 +1,18 @@ { + "primitive": { + "schema": { + "type": "number" + }, + "valid": [ + 0, + 1000, + -100 + ], + "invalid": [ + "12-12", + "12*12" + ] + }, "required3": { "schema": { "type": "object", diff --git a/tests/unit/editor.spec.js b/tests/unit/editor.spec.js index 3ac4d15a4..e2d67208f 100644 --- a/tests/unit/editor.spec.js +++ b/tests/unit/editor.spec.js @@ -77,3 +77,56 @@ describe('Editor', () => { expect(JSON.stringify(editor.getValue())).toBe('[1,2,3,4,5]') }) }) + +const fixture = [ + { + title: 'NumberEditor test', + schema: { + type: 'number' + }, + input: ' 123.45 ', + value: 123.45 + }, + { + title: 'NumberEditor test (invalid value)', + schema: { + type: 'number' + }, + input: '12-12', + value: '12-12' + }, + { + title: 'Integer test', + schema: { + type: 'integer' + }, + input: ' 123 ', + value: 123 + } +] + +describe('Number Editor', () => { + let element + let editor + + beforeEach(() => { + document.body.insertAdjacentHTML( + 'afterbegin', + '
') + element = document.getElementById('fixture') + }) + + afterEach(() => { + editor.destroy() + }) + + fixture.forEach(spec => { + it(spec.title, () => { + editor = new JSONEditor(element, { + schema: spec.schema + }) + editor.setValue(spec.input) + expect(editor.getValue()).toBe(spec.value) + }) + }) +})