Commit ea219b74 authored by Gabriel Monnerat's avatar Gabriel Monnerat Committed by Romain Courteaud
Browse files

erp5_web_renderjs_ui: Improve fields to propagate error_text

* change the label field to not display the error text by default
* change all html5 fields (input, select, textarea) to add a custom *invalid* class when the field is invalid
* change all html5 fields to listen to the `focus` and `blur` event (with the renderjs onEvent method)
  Then, acquire (with `declareAcquiredMethod`) and call `notifyFocus` / `notifyBlur` methods respectively.
* change the label field to handle `notifyFocus` / `notifyBlur` (with `allowPublicAcquisition`).
  Change the validation error text display state depending on the field status
* Trigger onStateChange to set invalid class. If error comes from input field, we need to change state locally to set invalid class
* Regenerate gadget_erp5_nojqm.css from  erp5less.css using http://lesscss.org/less-preview/
* Textarea gadget should not prevent default when invalid event is triggered
* Support notifyFocus and notifyBlur in panel
parent 27ac35c6
......@@ -24,6 +24,7 @@
id: field_json.key,
name: field_json.key,
title: field_json.title,
error_text: field_json.error_text,
hidden: field_json.hidden,
// Force calling subfield render
// as user may have modified the input value
......
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>982.26919.14719.24132</string> </value>
<value> <string>987.16243.63478.16486</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1583764359.91</float>
<float>1602527335.51</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -65,6 +65,7 @@
name: field_json.key,
key: field_json.key,
title: field_json.title,
error_text: field_json.error_text,
timezone_style: field_json.timezone_style,
date_only: field_json.date_only,
hide_day: field_json.hide_day,
......@@ -112,7 +113,8 @@
editable: gadget.state.editable,
required: gadget.state.required,
type: gadget.state.date_only ? "date" : "datetime-local",
hidden: gadget.state.hidden
hidden: gadget.state.hidden,
error_text: modification_dict.error_text
},
select_state = {
name: gadget.state.key + '_select',
......@@ -120,7 +122,8 @@
item_list: ZONE_LIST,
editable: gadget.state.editable,
required: gadget.state.required,
hidden: gadget.state.hidden
hidden: gadget.state.hidden,
error_text: modification_dict.error_text
// name: field_json.key,
// title: field_json.title
},
......
......@@ -228,7 +228,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>983.56055.11203.16827</string> </value>
<value> <string>986.51894.21525.38502</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -246,8 +246,8 @@
</tuple>
<state>
<tuple>
<float>1589358221.58</float>
<string>GMT+0</string>
<float>1600894302.31</float>
<string>UTC</string>
</tuple>
</state>
</object>
......
......@@ -20,8 +20,7 @@
.declareAcquiredMethod("jio_getAttachment", "jio_getAttachment")
.declareMethod('render', function (options) {
var element = this.element,
gadget = this,
var gadget = this,
field_json = options.field_json || {},
new_state = {
value: field_json.value || field_json['default'] || "",
......@@ -44,7 +43,7 @@
return gadget.changeState(new_state);
})
.onStateChange(function (modification_dict) {
.onStateChange(function () {
var gadget = this,
erp5_document_uri = new URI(gadget.state.erp5_embedded_document._view._links.traversed_document.href),
form_options = {
......
......@@ -220,7 +220,7 @@
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>superkato</string> </value>
<value> <string>zope</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>967.35221.49309.22852</string> </value>
<value> <string>986.61483.49376.1638</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1526316882.68</float>
<float>1601393999.02</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -35,6 +35,7 @@
name: field_json.key,
title: field_json.title,
precision: window.parseFloat(field_json.precision),
error_text: field_json.error_text,
// erp5 always put value into "default" (never "value")
value: window.parseFloat(field_json.default),
text_content: '',
......
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>983.35869.13162.60928</string> </value>
<value> <string>986.14570.51196.18756</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,8 +252,8 @@
</tuple>
<state>
<tuple>
<float>1599097800.89</float>
<string>GMT+2</string>
<float>1598579261.92</float>
<string>UTC</string>
</tuple>
</state>
</object>
......
......@@ -17,6 +17,7 @@
editable: field_json.editable,
required: field_json.required,
id: field_json.key,
error_text: options.field_json.error_text || "",
name: field_json.key,
title: field_json.title,
hidden: field_json.hidden,
......
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>965.12118.35525.1655</string> </value>
<value> <string>986.11537.50394.34935</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1517930232.43</float>
<float>1598397556.71</float>
<string>UTC</string>
</tuple>
</state>
......
/*global window, document, rJS */
/*global window, document, rJS*/
/*jslint indent: 2, maxerr: 3 */
/**
* Label gadget takes care of displaying validation errors and label.
......@@ -80,6 +80,7 @@
error_text: '',
label: true, // the label element is already present in the HTML template
css_class: '',
display_error_text: false,
first_call: false
})
......@@ -101,6 +102,7 @@
state_dict.label = true;
}
return this.changeState(state_dict);
})
.onStateChange(function onStateChange(modification_dict) {
......@@ -110,15 +112,13 @@
i,
queue,
new_div;
if (modification_dict.hasOwnProperty('first_call')) {
gadget.props = {
container_element: gadget.element.querySelector('div'),
label_element: gadget.element.querySelector('label')
};
}
if (gadget.state.hidden && !modification_dict.error_text) {
if (gadget.state.hidden && !gadget.state.error_text) {
this.element.hidden = true;
} else {
this.element.hidden = false;
......@@ -136,14 +136,31 @@
}
}
if (modification_dict.hasOwnProperty('error_text')) {
// Remove/add label_element from DOM
if (modification_dict.hasOwnProperty('label')) {
if (this.state.label === true) {
this.props.container_element.insertBefore(this.props.label_element, this.props.container_element.firstChild);
} else {
this.props.container_element.removeChild(this.props.label_element);
}
}
if (this.state.error_text && this.props.label_element &&
!this.props.label_element.classList.contains("is-invalid")) {
this.props.label_element.classList.add("is-invalid");
} else if (!this.state.error_text &&
this.props.label_element.classList.contains("is-invalid")) {
this.props.label_element.classList.remove("is-invalid");
}
if (modification_dict.hasOwnProperty('display_error_text') || modification_dict.hasOwnProperty('error_text')) {
// first remove old errors
span = this.props.container_element.lastElementChild;
if ((span !== null) && (span.tagName.toLowerCase() !== 'span')) {
span = null;
}
// display new error if present
if (this.state.error_text) {
if (this.state.error_text && this.state.display_error_text) {
if (span === null) {
span = document.createElement('span');
span.textContent = this.state.error_text;
......@@ -151,17 +168,10 @@
} else {
span.textContent = this.state.error_text;
}
} else if (span !== null) {
this.props.container_element.removeChild(span);
}
}
// Remove/add label_element from DOM
if (modification_dict.hasOwnProperty('label')) {
if (this.state.label === true) {
this.props.container_element.insertBefore(this.props.label_element, this.props.container_element.firstChild);
} else {
this.props.container_element.removeChild(this.props.label_element);
if (span !== null) {
this.props.container_element.removeChild(span);
}
}
}
......@@ -199,6 +209,7 @@
});
}
}
})
.declareMethod("checkValidity", function checkValidity() {
......@@ -231,6 +242,14 @@
});
}, {mutex: 'changestate'})
.allowPublicAcquisition("notifyFocus", function notifyFocus() {
return this.changeState({display_error_text: true});
})
.allowPublicAcquisition("notifyBlur", function notifyBlur() {
return this.changeState({display_error_text: false});
})
.allowPublicAcquisition("notifyInvalid", function notifyInvalid(param_list) {
// Label doesn't know when a subgadget calls notifyInvalid
// Prevent mutex dead lock by defering the changeState call
......
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>984.41193.2075.29252</string> </value>
<value> <string>987.16240.14459.45960</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1596116391.77</float>
<float>1602525354.74</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -23,6 +23,7 @@
title: field_json.title,
first_item: field_json.first_item,
hidden: field_json.hidden,
error_text: field_json.error_text,
// Force calling subfield render
// as user may have modified the input value
render_timestamp: new Date().getTime()
......
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>979.41914.53909.44561</string> </value>
<value> <string>986.42821.5680.17442</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1573126833.72</float>
<float>1600275350.99</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -3,7 +3,7 @@
(function (window, rJS, RSVP, document) {
'use strict';
function appendCheckboxField(gadget, item, checked) {
function appendCheckboxField(gadget, item, checked, error_text) {
var input_gadget,
label_gadget;
if (!gadget.state.editable) {
......@@ -13,7 +13,8 @@
label_gadget = result;
return result.render({
tag: 'p',
text_content: item[0]
text_content: item[0],
error_text: error_text
});
})
.push(function () {
......@@ -35,6 +36,7 @@
value: item[1],
checked: checked,
editable: true,
error_text: error_text,
hidden: gadget.state.hidden
};
......@@ -47,19 +49,24 @@
label.appendChild(input_gadget.element);
label.appendChild(text_node);
div.appendChild(label);
if (error_text && !label.classList.contains("is-invalid")) {
label.classList.add("is-invalid");
} else if (!error_text && label.classList.contains("is-invalid")) {
label.classList.remove("is-invalid");
}
gadget.element.appendChild(div);
});
}
rJS(window)
.declareMethod('render', function (options) {
var field_json = options.field_json || {},
state_dict = {
editable: field_json.editable,
name: field_json.key,
item_list: field_json.items,
value_list: field_json.value || field_json.default,
value_list: field_json.value || field_json["default"],
error_text: field_json.error_text,
hidden: field_json.hidden,
// Force calling subfield render
// as user may have modified the input value
......@@ -94,11 +101,13 @@
return appendCheckboxField.apply(this, argument_list);
});
}
queue = new RSVP.Queue();
for (i = 0; i < item_list.length; i += 1) {
enQueue(gadget, item_list[i], value_dict.hasOwnProperty(item_list[i][1]));
enQueue(gadget,
item_list[i],
value_dict.hasOwnProperty(item_list[i][1]),
this.state.error_text);
}
return queue;
......@@ -149,13 +158,6 @@
}, {mutex: 'changestate'})
.declareMethod('checkValidity', function () {
var name = this.state.name;
if (this.state.editable && this.state.required) {
return this.getContent()
.push(function (result) {
return result[name].length !== 0;
});
}
return true;
});
......
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>971.50596.31143.27050</string> </value>
<value> <string>987.29075.27892.56422</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1542818919.2</float>
<float>1603296350.21</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -8,7 +8,7 @@
* Shared
**********************************************/
/**********************************************
* http://meyerweb.com/eric/tools/css/reset/
* http://meyerweb.com/eric/tools/css/reset/
* v2.0 | 20110126
* License: none (public domain)
**********************************************/
......@@ -329,14 +329,23 @@ select:focus {
}
input:not([type=submit]):not([type=file]):not([type=checkbox]):not([type=radio]):not([type=color]):invalid,
textarea:invalid,
select:invalid {
select:invalid,
input:not([type=submit]):not([type=file]):not([type=checkbox]):not([type=radio]):not([type=color]).is-invalid,
textarea.is-invalid,
select.is-invalid {
border: 1px solid #FF6600;
}
input:not([type=submit]):not([type=file]):not([type=checkbox]):not([type=radio]):not([type=color]):invalid:focus,
textarea:invalid:focus,
select:invalid:focus {
select:invalid:focus,
input:not([type=submit]):not([type=file]):not([type=checkbox]):not([type=radio]):not([type=color]).is-invalid:focus,
textarea.is-invalid:focus,
select.is-invalid:focus {
box-shadow: 0 0 12pt #FF6600;
}
label.is-invalid {
color: #FF6600;
}
input[type="search"] {
-webkit-appearance: textfield;
}
......@@ -1174,7 +1183,7 @@ div[data-gadget-scope='header'] .ui-header ul {
padding-left: 24pt;
}
}
.gadget-content .ui-field-contain > label {
.gadget-content .ui-field-contain > label:not(:is-invalid) {
color: hsl(0, 0%, 42%);
}
.gadget-content .required > .ui-field-contain > label {
......@@ -3266,4 +3275,4 @@ hmtl .ui-icon-carat-u::before {
}
.ui-icon-clone::before {
content: "\f24d";
}
}
\ No newline at end of file
......@@ -244,7 +244,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>988.2056.8848.16469</string> </value>
<value> <string>987.16008.2576.23808</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -262,7 +262,7 @@
</tuple>
<state>
<tuple>
<float>1605606512.05</float>
<float>1602511207.11</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -347,6 +347,19 @@
}
}, false, false)
.allowPublicAcquisition("notifyFocus", function notifyFocus() {
// All html5 fields in ERP5JS triggers this method when focus
// is triggered. This is usefull to display error text.
// But, in the case of panel, we don't need to handle anything.
return;
})
.allowPublicAcquisition("notifyBlur", function notifyFocus() {
// All html5 fields in ERP5JS triggers this method when blur
// is triggered now. This is usefull to display error text.
// But, in the case of panel, we don't need to handle anything.
return;
})
.allowPublicAcquisition('notifyChange', function notifyChange(
argument_list,
scope
......
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>986.45400.41413.45892</string> </value>
<value> <string>987.27464.21813.55483</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1600429015.37</float>
<float>1604411452.55</float>
<string>UTC</string>
</tuple>
</state>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment