From 5c271d5aac44a51b3ea41bbf81bef6fb2570a5ea Mon Sep 17 00:00:00 2001 From: pkupczyk <piotr.kupczyk@id.ethz.ch> Date: Thu, 20 Aug 2020 14:39:37 +0200 Subject: [PATCH] NG_UI : bugfixing after Caterina's tests : SSDM-10060 - validate 'code' format on the client side onBlur (instead of on the server side onSave); the mandatory fields validation remains unchanged (i.e. on the client side onSave) --- .../components/common/form/FormValidator.js | 66 +++++++++++++------ .../common/page/PageConrollerValidate.js | 6 +- .../common/page/PageControllerLoad.js | 2 +- .../common/page/PageControllerSave.js | 2 +- .../src/js/components/login/Login.jsx | 10 +-- .../types/form/TypeFormControllerValidate.js | 2 + .../form/VocabularyFormControllerValidate.js | 9 +++ .../types/form/TypeFormComponent.test.js | 48 +++++++++++++- .../form/VocabularyFormComponent.test.js | 43 +++++++++++- 9 files changed, 151 insertions(+), 37 deletions(-) diff --git a/openbis_ng_ui/src/js/components/common/form/FormValidator.js b/openbis_ng_ui/src/js/components/common/form/FormValidator.js index 5ff9d4733a1..bb5567064ad 100644 --- a/openbis_ng_ui/src/js/components/common/form/FormValidator.js +++ b/openbis_ng_ui/src/js/components/common/form/FormValidator.js @@ -1,12 +1,18 @@ +const CODE_PATTERN = /^[A-Za-z0-9_\\-\\.:]+$/ + class FormValidator { - constructor() { + constructor(mode) { + this.mode = mode + this.objects = new Set() + this.fields = new Set() this.errors = [] - this.objects = [] } validateNotEmpty(object, name, label) { - this._validateField(object, name, () => { - const field = object[name] + this._validateField(object, name, field => { + if (this.mode !== 'full') { + return null + } if ( field.value === null || field.value === undefined || @@ -19,31 +25,53 @@ class FormValidator { }) } - _validateField(object, name, fn) { - const field = object[name] + validatePattern(object, name, label, pattern) { + this._validateField(object, name, field => { + if ( + field.value === null || + field.value === undefined || + field.value.trim() === '' + ) { + return null + } else { + if (pattern.test(field.value)) { + return null + } else { + return label + } + } + }) + } - const oldError = field.error - const newError = fn() + validateCode(object, name, label) { + this.validatePattern( + object, + name, + label + ' can only contain A-Z, a-z, 0-9 and _, -, .', + CODE_PATTERN + ) + } - if (!this.objects.includes(object)) { - this.objects.push(object) + _validateField(object, name, fn) { + if (!this.objects.has(object)) { object.errors = 0 + this.objects.add(object) } - if (newError !== oldError) { + if (!this.fields.has(object[name])) { object[name] = { - ...field, - error: newError + ...object[name], + error: null } + this.fields.add(object[name]) } - if (newError) { + const error = fn(object[name]) + + if (error) { object.errors++ - this.errors.push({ - object, - name, - error: newError - }) + object[name].error = error + this.errors.push({ object, name, error }) } } diff --git a/openbis_ng_ui/src/js/components/common/page/PageConrollerValidate.js b/openbis_ng_ui/src/js/components/common/page/PageConrollerValidate.js index bb4b8ef1b31..506cd2c96bc 100644 --- a/openbis_ng_ui/src/js/components/common/page/PageConrollerValidate.js +++ b/openbis_ng_ui/src/js/components/common/page/PageConrollerValidate.js @@ -21,11 +21,7 @@ export default class PageControllerValidate { async execute(autofocus) { const { validate } = this.context.getState() - if (!validate) { - return true - } - - const validator = new FormValidator() + const validator = new FormValidator(validate) const newState = await this.validate(validator) const errors = validator.getErrors() diff --git a/openbis_ng_ui/src/js/components/common/page/PageControllerLoad.js b/openbis_ng_ui/src/js/components/common/page/PageControllerLoad.js index 30f6d5632f9..d5876756d19 100644 --- a/openbis_ng_ui/src/js/components/common/page/PageControllerLoad.js +++ b/openbis_ng_ui/src/js/components/common/page/PageControllerLoad.js @@ -17,7 +17,7 @@ export default class PageControllerLoad { try { await this.context.setState({ loading: true, - validate: false + validate: 'basic' }) const isNew = this.object.type === this.controller.getNewObjectType() diff --git a/openbis_ng_ui/src/js/components/common/page/PageControllerSave.js b/openbis_ng_ui/src/js/components/common/page/PageControllerSave.js index 563c7cceb1b..5b7466a9116 100644 --- a/openbis_ng_ui/src/js/components/common/page/PageControllerSave.js +++ b/openbis_ng_ui/src/js/components/common/page/PageControllerSave.js @@ -15,7 +15,7 @@ export default class PageControllerSave { async execute() { try { await this.context.setState({ - validate: true + validate: 'full' }) const valid = await this.controller.validate(true) diff --git a/openbis_ng_ui/src/js/components/login/Login.jsx b/openbis_ng_ui/src/js/components/login/Login.jsx index ec5811007dc..5f2e55738f4 100644 --- a/openbis_ng_ui/src/js/components/login/Login.jsx +++ b/openbis_ng_ui/src/js/components/login/Login.jsx @@ -61,7 +61,7 @@ class WithLogin extends React.Component { error: null }, selection: 'user', - validate: false + validate: 'basic' } this.references = { user: React.createRef(), @@ -94,13 +94,9 @@ class WithLogin extends React.Component { } validate(autofocus) { - if (!this.state.validate) { - return true - } - const newState = { ...this.state } - const validator = new FormValidator() + const validator = new FormValidator(this.state.validate) validator.validateNotEmpty(newState, 'user', 'User') validator.validateNotEmpty(newState, 'password', 'Password') @@ -144,7 +140,7 @@ class WithLogin extends React.Component { this.setState( { - validate: true + validate: 'full' }, () => { if (this.validate(true)) { diff --git a/openbis_ng_ui/src/js/components/types/form/TypeFormControllerValidate.js b/openbis_ng_ui/src/js/components/types/form/TypeFormControllerValidate.js index ac50135d074..d4adc46aa2b 100644 --- a/openbis_ng_ui/src/js/components/types/form/TypeFormControllerValidate.js +++ b/openbis_ng_ui/src/js/components/types/form/TypeFormControllerValidate.js @@ -43,6 +43,7 @@ export default class TypeFormControllerValidate extends PageControllerValidate { _validateType(validator, type) { const strategy = this._getStrategy() validator.validateNotEmpty(type, 'code', 'Code') + validator.validateCode(type, 'code', 'Code') strategy.validateTypeAttributes(validator, type) } @@ -54,6 +55,7 @@ export default class TypeFormControllerValidate extends PageControllerValidate { _validateProperty(validator, property) { validator.validateNotEmpty(property, 'code', 'Code') + validator.validateCode(property, 'code', 'Code') validator.validateNotEmpty(property, 'label', 'Label') validator.validateNotEmpty(property, 'description', 'Description') validator.validateNotEmpty(property, 'dataType', 'Data Type') diff --git a/openbis_ng_ui/src/js/components/types/form/VocabularyFormControllerValidate.js b/openbis_ng_ui/src/js/components/types/form/VocabularyFormControllerValidate.js index 6c9d55e963d..45127335b92 100644 --- a/openbis_ng_ui/src/js/components/types/form/VocabularyFormControllerValidate.js +++ b/openbis_ng_ui/src/js/components/types/form/VocabularyFormControllerValidate.js @@ -1,5 +1,7 @@ import PageControllerValidate from '@src/js/components/common/page/PageConrollerValidate.js' +const TERM_CODE_PATTERN = /^[A-Za-z0-9_\\-\\.:]+$/ + export default class VocabularyFormControllerValidate extends PageControllerValidate { validate(validator) { const { vocabulary, terms } = this.context.getState() @@ -45,6 +47,7 @@ export default class VocabularyFormControllerValidate extends PageControllerVali _validateVocabulary(validator, vocabulary) { validator.validateNotEmpty(vocabulary, 'code', 'Code') + validator.validateCode(vocabulary, 'code', 'Code') } _validateTerms(validator, terms) { @@ -55,5 +58,11 @@ export default class VocabularyFormControllerValidate extends PageControllerVali _validateTerm(validator, term) { validator.validateNotEmpty(term, 'code', 'Code') + validator.validatePattern( + term, + 'code', + 'Code can only contain A-Z, a-z, 0-9 and _, -, ., :', + TERM_CODE_PATTERN + ) } } diff --git a/openbis_ng_ui/srcTest/js/components/types/form/TypeFormComponent.test.js b/openbis_ng_ui/srcTest/js/components/types/form/TypeFormComponent.test.js index 3b56c0d7bc8..93a75a85c9c 100644 --- a/openbis_ng_ui/srcTest/js/components/types/form/TypeFormComponent.test.js +++ b/openbis_ng_ui/srcTest/js/components/types/form/TypeFormComponent.test.js @@ -1093,7 +1093,8 @@ async function testValidateType() { type: { title: 'Type', code: { - error: 'Code cannot be empty' + error: 'Code cannot be empty', + focused: true }, description: { error: null @@ -1110,6 +1111,30 @@ async function testValidateType() { message: null } }) + + form.getParameters().getType().getCode().change('I am illegal') + await form.update() + + form.getButtons().getSave().click() + await form.update() + + form.expectJSON({ + parameters: { + type: { + code: { + value: 'I am illegal', + error: 'Code can only contain A-Z, a-z, 0-9 and _, -, .', + focused: true + } + } + }, + buttons: { + message: { + text: 'You have unsaved changes.', + type: 'warning' + } + } + }) } async function testValidateProperty() { @@ -1131,7 +1156,8 @@ async function testValidateProperty() { error: null }, code: { - error: 'Code cannot be empty' + error: 'Code cannot be empty', + focused: true }, dataType: { error: null @@ -1154,6 +1180,24 @@ async function testValidateProperty() { } } }) + + form.getParameters().getProperty().getCode().change('I am illegal') + await form.update() + + form.getButtons().getSave().click() + await form.update() + + form.expectJSON({ + parameters: { + property: { + code: { + value: 'I am illegal', + error: 'Code can only contain A-Z, a-z, 0-9 and _, -, .', + focused: true + } + } + } + }) } async function testValidateTypeAndProperty() { diff --git a/openbis_ng_ui/srcTest/js/components/types/form/VocabularyFormComponent.test.js b/openbis_ng_ui/srcTest/js/components/types/form/VocabularyFormComponent.test.js index b6956ff4429..d615bf1e549 100644 --- a/openbis_ng_ui/srcTest/js/components/types/form/VocabularyFormComponent.test.js +++ b/openbis_ng_ui/srcTest/js/components/types/form/VocabularyFormComponent.test.js @@ -1082,7 +1082,8 @@ async function testValidateTerm() { title: 'Term', code: { value: null, - error: 'Code cannot be empty' + error: 'Code cannot be empty', + focused: true } } }, @@ -1093,6 +1094,24 @@ async function testValidateTerm() { } } }) + + form.getParameters().getTerm().getCode().change('I am illegal') + await form.update() + + form.getButtons().getSave().click() + await form.update() + + form.expectJSON({ + parameters: { + term: { + code: { + value: 'I am illegal', + error: 'Code can only contain A-Z, a-z, 0-9 and _, -, ., :', + focused: true + } + } + } + }) } async function testValidateVocabulary() { @@ -1122,7 +1141,9 @@ async function testValidateVocabulary() { vocabulary: { title: 'Vocabulary', code: { - error: 'Code cannot be empty' + value: null, + error: 'Code cannot be empty', + focused: true } } }, @@ -1133,6 +1154,24 @@ async function testValidateVocabulary() { } } }) + + form.getParameters().getVocabulary().getCode().change('I am illegal') + await form.update() + + form.getButtons().getSave().click() + await form.update() + + form.expectJSON({ + parameters: { + vocabulary: { + code: { + value: 'I am illegal', + error: 'Code can only contain A-Z, a-z, 0-9 and _, -, .', + focused: true + } + } + } + }) } async function mountNew() { -- GitLab