Commit 240fac9f authored by Gabriel Monnerat's avatar Gabriel Monnerat

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 93b6807f
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
id: field_json.key, id: field_json.key,
name: field_json.key, name: field_json.key,
title: field_json.title, title: field_json.title,
error_text: field_json.error_text,
hidden: field_json.hidden, hidden: field_json.hidden,
// Force calling subfield render // Force calling subfield render
// as user may have modified the input value // as user may have modified the input value
......
...@@ -234,7 +234,7 @@ ...@@ -234,7 +234,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>982.26919.14719.24132</string> </value> <value> <string>987.16243.63478.16486</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -252,7 +252,7 @@ ...@@ -252,7 +252,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1583764359.91</float> <float>1602527335.51</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -65,6 +65,7 @@ ...@@ -65,6 +65,7 @@
name: field_json.key, name: field_json.key,
key: field_json.key, key: field_json.key,
title: field_json.title, title: field_json.title,
error_text: field_json.error_text,
timezone_style: field_json.timezone_style, timezone_style: field_json.timezone_style,
date_only: field_json.date_only, date_only: field_json.date_only,
hide_day: field_json.hide_day, hide_day: field_json.hide_day,
...@@ -112,7 +113,8 @@ ...@@ -112,7 +113,8 @@
editable: gadget.state.editable, editable: gadget.state.editable,
required: gadget.state.required, required: gadget.state.required,
type: gadget.state.date_only ? "date" : "datetime-local", type: gadget.state.date_only ? "date" : "datetime-local",
hidden: gadget.state.hidden hidden: gadget.state.hidden,
error_text: modification_dict.error_text
}, },
select_state = { select_state = {
name: gadget.state.key + '_select', name: gadget.state.key + '_select',
...@@ -120,7 +122,8 @@ ...@@ -120,7 +122,8 @@
item_list: ZONE_LIST, item_list: ZONE_LIST,
editable: gadget.state.editable, editable: gadget.state.editable,
required: gadget.state.required, required: gadget.state.required,
hidden: gadget.state.hidden hidden: gadget.state.hidden,
error_text: modification_dict.error_text
// name: field_json.key, // name: field_json.key,
// title: field_json.title // title: field_json.title
}, },
......
...@@ -228,7 +228,7 @@ ...@@ -228,7 +228,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>983.56055.11203.16827</string> </value> <value> <string>986.51894.21525.38502</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -246,8 +246,8 @@ ...@@ -246,8 +246,8 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1589358221.58</float> <float>1600894302.31</float>
<string>GMT+0</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
</object> </object>
......
...@@ -20,8 +20,7 @@ ...@@ -20,8 +20,7 @@
.declareAcquiredMethod("jio_getAttachment", "jio_getAttachment") .declareAcquiredMethod("jio_getAttachment", "jio_getAttachment")
.declareMethod('render', function (options) { .declareMethod('render', function (options) {
var element = this.element, var gadget = this,
gadget = this,
field_json = options.field_json || {}, field_json = options.field_json || {},
new_state = { new_state = {
value: field_json.value || field_json['default'] || "", value: field_json.value || field_json['default'] || "",
...@@ -44,7 +43,7 @@ ...@@ -44,7 +43,7 @@
return gadget.changeState(new_state); return gadget.changeState(new_state);
}) })
.onStateChange(function (modification_dict) { .onStateChange(function () {
var gadget = this, var gadget = this,
erp5_document_uri = new URI(gadget.state.erp5_embedded_document._view._links.traversed_document.href), erp5_document_uri = new URI(gadget.state.erp5_embedded_document._view._links.traversed_document.href),
form_options = { form_options = {
......
...@@ -220,7 +220,7 @@ ...@@ -220,7 +220,7 @@
</item> </item>
<item> <item>
<key> <string>actor</string> </key> <key> <string>actor</string> </key>
<value> <string>superkato</string> </value> <value> <string>zope</string> </value>
</item> </item>
<item> <item>
<key> <string>comment</string> </key> <key> <string>comment</string> </key>
...@@ -234,7 +234,7 @@ ...@@ -234,7 +234,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>967.35221.49309.22852</string> </value> <value> <string>986.61483.49376.1638</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -252,7 +252,7 @@ ...@@ -252,7 +252,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1526316882.68</float> <float>1601393999.02</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
name: field_json.key, name: field_json.key,
title: field_json.title, title: field_json.title,
precision: window.parseFloat(field_json.precision), precision: window.parseFloat(field_json.precision),
error_text: field_json.error_text,
// erp5 always put value into "default" (never "value") // erp5 always put value into "default" (never "value")
value: window.parseFloat(field_json.default), value: window.parseFloat(field_json.default),
text_content: '', text_content: '',
......
...@@ -234,7 +234,7 @@ ...@@ -234,7 +234,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>983.35869.13162.60928</string> </value> <value> <string>986.14570.51196.18756</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -252,8 +252,8 @@ ...@@ -252,8 +252,8 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1599097800.89</float> <float>1598579261.92</float>
<string>GMT+2</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
</object> </object>
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
editable: field_json.editable, editable: field_json.editable,
required: field_json.required, required: field_json.required,
id: field_json.key, id: field_json.key,
error_text: options.field_json.error_text || "",
name: field_json.key, name: field_json.key,
title: field_json.title, title: field_json.title,
hidden: field_json.hidden, hidden: field_json.hidden,
......
...@@ -234,7 +234,7 @@ ...@@ -234,7 +234,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>965.12118.35525.1655</string> </value> <value> <string>986.11537.50394.34935</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -252,7 +252,7 @@ ...@@ -252,7 +252,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1517930232.43</float> <float>1598397556.71</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
/*global window, document, rJS */ /*global window, document, rJS*/
/*jslint indent: 2, maxerr: 3 */ /*jslint indent: 2, maxerr: 3 */
/** /**
* Label gadget takes care of displaying validation errors and label. * Label gadget takes care of displaying validation errors and label.
...@@ -80,6 +80,7 @@ ...@@ -80,6 +80,7 @@
error_text: '', error_text: '',
label: true, // the label element is already present in the HTML template label: true, // the label element is already present in the HTML template
css_class: '', css_class: '',
display_error_text: false,
first_call: false first_call: false
}) })
...@@ -101,6 +102,7 @@ ...@@ -101,6 +102,7 @@
state_dict.label = true; state_dict.label = true;
} }
return this.changeState(state_dict); return this.changeState(state_dict);
}) })
.onStateChange(function onStateChange(modification_dict) { .onStateChange(function onStateChange(modification_dict) {
...@@ -110,15 +112,13 @@ ...@@ -110,15 +112,13 @@
i, i,
queue, queue,
new_div; new_div;
if (modification_dict.hasOwnProperty('first_call')) { if (modification_dict.hasOwnProperty('first_call')) {
gadget.props = { gadget.props = {
container_element: gadget.element.querySelector('div'), container_element: gadget.element.querySelector('div'),
label_element: gadget.element.querySelector('label') label_element: gadget.element.querySelector('label')
}; };
} }
if (gadget.state.hidden && !gadget.state.error_text) {
if (gadget.state.hidden && !modification_dict.error_text) {
this.element.hidden = true; this.element.hidden = true;
} else { } else {
this.element.hidden = false; this.element.hidden = false;
...@@ -136,14 +136,31 @@ ...@@ -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 // first remove old errors
span = this.props.container_element.lastElementChild; span = this.props.container_element.lastElementChild;
if ((span !== null) && (span.tagName.toLowerCase() !== 'span')) { if ((span !== null) && (span.tagName.toLowerCase() !== 'span')) {
span = null; span = null;
} }
// display new error if present // display new error if present
if (this.state.error_text) { if (this.state.error_text && this.state.display_error_text) {
if (span === null) { if (span === null) {
span = document.createElement('span'); span = document.createElement('span');
span.textContent = this.state.error_text; span.textContent = this.state.error_text;
...@@ -151,17 +168,10 @@ ...@@ -151,17 +168,10 @@
} else { } else {
span.textContent = this.state.error_text; 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 { } else {
this.props.container_element.removeChild(this.props.label_element); if (span !== null) {
this.props.container_element.removeChild(span);
}
} }
} }
...@@ -199,6 +209,7 @@ ...@@ -199,6 +209,7 @@
}); });
} }
} }
}) })
.declareMethod("checkValidity", function checkValidity() { .declareMethod("checkValidity", function checkValidity() {
...@@ -231,6 +242,14 @@ ...@@ -231,6 +242,14 @@
}); });
}, {mutex: 'changestate'}) }, {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) { .allowPublicAcquisition("notifyInvalid", function notifyInvalid(param_list) {
// Label doesn't know when a subgadget calls notifyInvalid // Label doesn't know when a subgadget calls notifyInvalid
// Prevent mutex dead lock by defering the changeState call // Prevent mutex dead lock by defering the changeState call
......
...@@ -234,7 +234,7 @@ ...@@ -234,7 +234,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>984.41193.2075.29252</string> </value> <value> <string>987.16240.14459.45960</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -252,7 +252,7 @@ ...@@ -252,7 +252,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1596116391.77</float> <float>1602525354.74</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
title: field_json.title, title: field_json.title,
first_item: field_json.first_item, first_item: field_json.first_item,
hidden: field_json.hidden, hidden: field_json.hidden,
error_text: field_json.error_text,
// Force calling subfield render // Force calling subfield render
// as user may have modified the input value // as user may have modified the input value
render_timestamp: new Date().getTime() render_timestamp: new Date().getTime()
......
...@@ -234,7 +234,7 @@ ...@@ -234,7 +234,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>979.41914.53909.44561</string> </value> <value> <string>986.42821.5680.17442</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -252,7 +252,7 @@ ...@@ -252,7 +252,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1573126833.72</float> <float>1600275350.99</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
(function (window, rJS, RSVP, document) { (function (window, rJS, RSVP, document) {
'use strict'; 'use strict';
function appendCheckboxField(gadget, item, checked) { function appendCheckboxField(gadget, item, checked, error_text) {
var input_gadget, var input_gadget,
label_gadget; label_gadget;
if (!gadget.state.editable) { if (!gadget.state.editable) {
...@@ -13,7 +13,8 @@ ...@@ -13,7 +13,8 @@
label_gadget = result; label_gadget = result;
return result.render({ return result.render({
tag: 'p', tag: 'p',
text_content: item[0] text_content: item[0],
error_text: error_text
}); });
}) })
.push(function () { .push(function () {
...@@ -35,6 +36,7 @@ ...@@ -35,6 +36,7 @@
value: item[1], value: item[1],
checked: checked, checked: checked,
editable: true, editable: true,
error_text: error_text,
hidden: gadget.state.hidden hidden: gadget.state.hidden
}; };
...@@ -47,19 +49,24 @@ ...@@ -47,19 +49,24 @@
label.appendChild(input_gadget.element); label.appendChild(input_gadget.element);
label.appendChild(text_node); label.appendChild(text_node);
div.appendChild(label); 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); gadget.element.appendChild(div);
}); });
} }
rJS(window) rJS(window)
.declareMethod('render', function (options) { .declareMethod('render', function (options) {
var field_json = options.field_json || {}, var field_json = options.field_json || {},
state_dict = { state_dict = {
editable: field_json.editable, editable: field_json.editable,
name: field_json.key, name: field_json.key,
item_list: field_json.items, 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, hidden: field_json.hidden,
// Force calling subfield render // Force calling subfield render
// as user may have modified the input value // as user may have modified the input value
...@@ -94,11 +101,13 @@ ...@@ -94,11 +101,13 @@
return appendCheckboxField.apply(this, argument_list); return appendCheckboxField.apply(this, argument_list);
}); });
} }
queue = new RSVP.Queue(); queue = new RSVP.Queue();
for (i = 0; i < item_list.length; i += 1) { 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; return queue;
...@@ -149,13 +158,6 @@ ...@@ -149,13 +158,6 @@
}, {mutex: 'changestate'}) }, {mutex: 'changestate'})
.declareMethod('checkValidity', function () { .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; return true;
}); });
......
...@@ -234,7 +234,7 @@ ...@@ -234,7 +234,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>971.50596.31143.27050</string> </value> <value> <string>987.29075.27892.56422</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -252,7 +252,7 @@ ...@@ -252,7 +252,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1542818919.2</float> <float>1603296350.21</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Shared * Shared
**********************************************/ **********************************************/
/********************************************** /**********************************************
* http://meyerweb.com/eric/tools/css/reset/ * http://meyerweb.com/eric/tools/css/reset/
* v2.0 | 20110126 * v2.0 | 20110126
* License: none (public domain) * License: none (public domain)
**********************************************/ **********************************************/
...@@ -329,14 +329,23 @@ select:focus { ...@@ -329,14 +329,23 @@ select:focus {
} }
input:not([type=submit]):not([type=file]):not([type=checkbox]):not([type=radio]):not([type=color]):invalid, input:not([type=submit]):not([type=file]):not([type=checkbox]):not([type=radio]):not([type=color]):invalid,
textarea: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; border: 1px solid #FF6600;
} }
input:not([type=submit]):not([type=file]):not([type=checkbox]):not([type=radio]):not([type=color]):invalid:focus, input:not([type=submit]):not([type=file]):not([type=checkbox]):not([type=radio]):not([type=color]):invalid:focus,
textarea: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; box-shadow: 0 0 12pt #FF6600;
} }
label.is-invalid {
color: #FF6600;
}
input[type="search"] { input[type="search"] {
-webkit-appearance: textfield; -webkit-appearance: textfield;
} }
...@@ -1174,7 +1183,7 @@ div[data-gadget-scope='header'] .ui-header ul { ...@@ -1174,7 +1183,7 @@ div[data-gadget-scope='header'] .ui-header ul {
padding-left: 24pt; padding-left: 24pt;
} }
} }
.gadget-content .ui-field-contain > label { .gadget-content .ui-field-contain > label:not(:is-invalid) {
color: hsl(0, 0%, 42%); color: hsl(0, 0%, 42%);
} }
.gadget-content .required > .ui-field-contain > label { .gadget-content .required > .ui-field-contain > label {
...@@ -3266,4 +3275,4 @@ hmtl .ui-icon-carat-u::before { ...@@ -3266,4 +3275,4 @@ hmtl .ui-icon-carat-u::before {
} }
.ui-icon-clone::before { .ui-icon-clone::before {
content: "\f24d"; content: "\f24d";
} }
\ No newline at end of file
...@@ -244,7 +244,7 @@ ...@@ -244,7 +244,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>988.2056.8848.16469</string> </value> <value> <string>987.16008.2576.23808</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -262,7 +262,7 @@ ...@@ -262,7 +262,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1605606512.05</float> <float>1602511207.11</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -347,6 +347,19 @@ ...@@ -347,6 +347,19 @@
} }
}, false, false) }, 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( .allowPublicAcquisition('notifyChange', function notifyChange(
argument_list, argument_list,
scope scope
......
...@@ -234,7 +234,7 @@ ...@@ -234,7 +234,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>986.45400.41413.45892</string> </value> <value> <string>987.27464.21813.55483</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -252,7 +252,7 @@ ...@@ -252,7 +252,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1600429015.37</float> <float>1604411452.55</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
(function (window, rJS, RSVP) { (function (window, rJS, RSVP) {
"use strict"; "use strict";
function appendCheckboxField(gadget, item) { function appendCheckboxField(gadget, item, error_text) {
var input_gadget; var input_gadget;
return gadget.declareGadget('gadget_html5_input.html', {scope: item[1], element: 'span'}) return gadget.declareGadget('gadget_html5_input.html', {scope: item[1], element: 'span'})
.push(function (result) { .push(function (result) {
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
name: gadget.state.name, name: gadget.state.name,
value: item[1], value: item[1],
editable: true, editable: true,
error_text: error_text,
hidden: gadget.state.hidden hidden: gadget.state.hidden
}; };
if (item[1] === gadget.state.value) { if (item[1] === gadget.state.value) {
...@@ -24,6 +25,11 @@ ...@@ -24,6 +25,11 @@
var label = document.createElement("label"); var label = document.createElement("label");
label.textContent = item[0]; label.textContent = item[0];
label.insertBefore(input_gadget.element, label.firstChild); label.insertBefore(input_gadget.element, label.firstChild);
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(label); gadget.element.appendChild(label);
}); });
} }
...@@ -39,6 +45,7 @@ ...@@ -39,6 +45,7 @@
select_first_item: field_json.select_first_item, select_first_item: field_json.select_first_item,
required: field_json.required, required: field_json.required,
editable: field_json.editable, editable: field_json.editable,
error_text: field_json.error_text,
name: field_json.key, name: field_json.key,
title: field_json.title, title: field_json.title,
item_list: field_json.items, item_list: field_json.items,
...@@ -82,7 +89,7 @@ ...@@ -82,7 +89,7 @@
queue = new RSVP.Queue(); queue = new RSVP.Queue();
for (i = 0; i < item_list.length; i += 1) { for (i = 0; i < item_list.length; i += 1) {
enQueue(gadget, item_list[i]); enQueue(gadget, item_list[i], this.state.error_text);
} }
} else { } else {
...@@ -152,10 +159,18 @@ ...@@ -152,10 +159,18 @@
return final_result; return final_result;
}, {mutex: 'changestate'}) }, {mutex: 'changestate'})
.declareJob('deferErrorText', function deferErrorText(error_text) {
var input = this.element.querySelector("input");
return this.changeState({
error_text: error_text
});
})
.declareMethod('checkValidity', function () { .declareMethod('checkValidity', function () {
var name = this.state.name, var name = this.state.name,
gadget = this, gadget = this,
empty; empty;
if (this.state.editable && this.state.required) { if (this.state.editable && this.state.required) {
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function () {
...@@ -169,7 +184,10 @@ ...@@ -169,7 +184,10 @@
error_message = all_result[1]; error_message = all_result[1];
empty = !content[name]; empty = !content[name];
if (empty) { if (empty) {
return gadget.notifyInvalid(error_message); return RSVP.all([
gadget.deferErrorText(error_message),
gadget.notifyInvalid(error_message)
]);
} }
return gadget.notifyValid(); return gadget.notifyValid();
}) })
...@@ -179,4 +197,5 @@ ...@@ -179,4 +197,5 @@
} }
return true; return true;
}); });
}(window, rJS, RSVP)); }(window, rJS, RSVP));
\ No newline at end of file
...@@ -220,7 +220,7 @@ ...@@ -220,7 +220,7 @@
</item> </item>
<item> <item>
<key> <string>actor</string> </key> <key> <string>actor</string> </key>
<value> <string>valentin</string> </value> <value> <string>zope</string> </value>
</item> </item>
<item> <item>
<key> <string>comment</string> </key> <key> <string>comment</string> </key>
...@@ -234,7 +234,7 @@ ...@@ -234,7 +234,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>976.61321.38056.58231</string> </value> <value> <string>987.16084.46356.54801</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -252,7 +252,7 @@ ...@@ -252,7 +252,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1562581079.03</float> <float>1602516004.43</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -149,6 +149,7 @@ ...@@ -149,6 +149,7 @@
key: options.key, key: options.key,
view: options.view, view: options.view,
search_view: options.search_view, search_view: options.search_view,
error_text: options.error_text || "",
url: options.url, url: options.url,
allow_creation: options.allow_creation, allow_creation: options.allow_creation,
portal_types: JSON.stringify(options.portal_types), portal_types: JSON.stringify(options.portal_types),
...@@ -231,8 +232,19 @@ ...@@ -231,8 +232,19 @@
// First display of the input // First display of the input
buildEditableInputHTML(gadget); buildEditableInputHTML(gadget);
} }
input = gadget.element.querySelector("input"); input = gadget.element.querySelector("input");
if (gadget.state.error_text &&
!input.classList.contains("is-invalid")) {
input.classList.add("is-invalid");
} else if (
// value_portal_type not empty means that user
// wants to create a Document
(!gadget.state.error_text || gadget.state.value_portal_type) &&
input.classList.contains("is-invalid")
) {
input.classList.remove("is-invalid");
}
if (modification_dict.hasOwnProperty("value_text")) { if (modification_dict.hasOwnProperty("value_text")) {
input.value = gadget.state.value_text; input.value = gadget.state.value_text;
} }
...@@ -417,6 +429,14 @@ ...@@ -417,6 +429,14 @@
"Invalid Search Criteria" "Invalid Search Criteria"
]) ])
.push(function (translation_list) { .push(function (translation_list) {
if (gadget.state.error_text !== translation_list[0]) {
// Avoid call deferErrorText if error_text is already set
// Otherwise, onStateChange will run forever
return RSVP.all([
gadget.deferErrorText(translation_list[0]),
gadget.notifyInvalid(translation_list[0])
]);
}
return gadget.notifyInvalid(translation_list[0]); return gadget.notifyInvalid(translation_list[0]);
}); });
} }
...@@ -429,9 +449,25 @@ ...@@ -429,9 +449,25 @@
]) ])
.push(function (translation_list) { .push(function (translation_list) {
if (error.target.status === 0) { if (error.target.status === 0) {
if (gadget.state.error_text !== translation_list[0]) {
// Avoid call deferErrorText if error_text is already
// set. Otherwise, onStateChange will run forever
return RSVP.all([
gadget.deferErrorText(translation_list[0]),
gadget.notifyInvalid(translation_list[0])
]);
}
return gadget.notifyInvalid(translation_list[0]); return gadget.notifyInvalid(translation_list[0]);
} }
if (error.target.status >= 500) { if (error.target.status >= 500) {
if (gadget.state.error_text !== translation_list[0]) {
// Avoid call deferErrorText if error_text is already
// set. Otherwise, onStateChange will run forever
return RSVP.all([
gadget.deferErrorText(translation_list[1]),
gadget.notifyInvalid(translation_list[1])
]);
}
return gadget.notifyInvalid(translation_list[1]); return gadget.notifyInvalid(translation_list[1]);
} }
throw error; throw error;
...@@ -513,6 +549,7 @@ ...@@ -513,6 +549,7 @@
} }
}, false, false) }, false, false)
.declareAcquiredMethod("notifyBlur", "notifyBlur")
.onEvent('blur', function (evt) { .onEvent('blur', function (evt) {
var gadget = this; var gadget = this;
if (evt.target.tagName.toLowerCase() === 'input') { if (evt.target.tagName.toLowerCase() === 'input') {
...@@ -525,22 +562,27 @@ ...@@ -525,22 +562,27 @@
return gadget.changeState({ return gadget.changeState({
has_focus: false has_focus: false
}); });
})
.push(function () {
return gadget.notifyBlur();
}); });
} }
}, true, false) }, true, false)
.declareAcquiredMethod("notifyFocus", "notifyFocus")
.onEvent('focus', function (evt) { .onEvent('focus', function (evt) {
var gadget = this; var gadget = this;
if (evt.target.tagName.toLowerCase() === 'input') { if (evt.target.tagName.toLowerCase() === 'input') {
return gadget.changeState({ return RSVP.all([
has_focus: true gadget.notifyFocus(),
}); gadget.changeState({has_focus: true})
]);
} }
}, true, false) }, true, false)
.declareAcquiredMethod("notifyValid", "notifyValid")
.declareMethod('checkValidity', function () { .declareMethod('checkValidity', function () {
var gadget = this; var gadget = this;
if ((this.state.value_text) && ( if ((this.state.value_text) && (
(this.state.value_relative_url === null) && (this.state.value_relative_url === null) &&
(this.state.value_uid === null) && (this.state.value_uid === null) &&
...@@ -548,19 +590,34 @@ ...@@ -548,19 +590,34 @@
)) { )) {
return gadget.translate("No such document was found") return gadget.translate("No such document was found")
.push(function (error_message) { .push(function (error_message) {
return gadget.notifyInvalid(error_message); return RSVP.all([
gadget.deferErrorText(error_message),
gadget.notifyInvalid(error_message)
]);
}) })
.push(function () { .push(function () {
return false; return false;
}); });
} }
return true; return gadget.notifyValid()
.push(function () {
return true;
});
}, {mutex: 'changestate'}) }, {mutex: 'changestate'})
.declareJob('deferErrorText', function deferErrorText(error_text) {
return this.changeState({
error_text: error_text
});
})
// XXX Use html5 input // XXX Use html5 input
.onEvent('invalid', function (evt) { .onEvent('invalid', function (evt) {
// invalid event does not bubble // invalid event does not bubble
return this.notifyInvalid(evt.target.validationMessage); return RSVP.all([
this.deferErrorText(evt.target.validationMessage),
this.notifyInvalid(evt.target.validationMessage)
]);
}, true, false) }, true, false)
.onEvent('change', function () { .onEvent('change', function () {
...@@ -569,22 +626,25 @@ ...@@ -569,22 +626,25 @@
.onEvent('input', function (event) { .onEvent('input', function (event) {
var gadget = this;
if (!this.state.editable) { if (!this.state.editable) {
return; return;
} }
var context = this;
return this.changeState({ return this.changeState({
value_text: event.target.value, value_text: event.target.value,
value_relative_url: null, value_relative_url: null,
value_uid: null, value_uid: null,
value_portal_type: null, value_portal_type: null,
has_focus: true has_focus: true,
error_text: ""
}) })
.push(function () { .push(function () {
return context.notifyChange(); return RSVP.all([
gadget.notifyValid(),
gadget.notifyChange()
]);
}); });
}, true, false); }, true, false);
}(window, rJS, RSVP, URI, document, }(window, rJS, RSVP, URI, document,
SimpleQuery, ComplexQuery, Query, QueryFactory, XMLHttpRequest, console)); SimpleQuery, ComplexQuery, Query, QueryFactory, XMLHttpRequest, console));
\ No newline at end of file
...@@ -69,9 +69,7 @@ ...@@ -69,9 +69,7 @@
</item> </item>
<item> <item>
<key> <string>content_type</string> </key> <key> <string>content_type</string> </key>
<value> <value> <string>text/javascript</string> </value>
<none/>
</value>
</item> </item>
<item> <item>
<key> <string>default_reference</string> </key> <key> <string>default_reference</string> </key>
...@@ -240,7 +238,7 @@ ...@@ -240,7 +238,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>983.6083.54777.37051</string> </value> <value> <string>987.36268.43767.1126</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -258,7 +256,7 @@ ...@@ -258,7 +256,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1586446542.77</float> <float>1603726868.97</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
title: field_json.title, title: field_json.title,
key: field_json.key, key: field_json.key,
view: field_json.view, view: field_json.view,
error_text: field_json.error_text,
search_view: field_json.search_view, search_view: field_json.search_view,
url: field_json.url, url: field_json.url,
allow_creation: field_json.allow_creation, allow_creation: field_json.allow_creation,
......
...@@ -234,7 +234,7 @@ ...@@ -234,7 +234,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>970.13790.51027.29644</string> </value> <value> <string>986.41646.43198.36846</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -252,7 +252,7 @@ ...@@ -252,7 +252,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1539872919.1</float> <float>1600685092.05</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -210,6 +210,19 @@ ...@@ -210,6 +210,19 @@
}); });
}, {mutex: 'changestate'}) }, {mutex: 'changestate'})
.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;
})
.declareAcquiredMethod("triggerSubmit", "triggerSubmit") .declareAcquiredMethod("triggerSubmit", "triggerSubmit")
.onEvent('click', function (evt) { .onEvent('click', function (evt) {
if ((evt.target.nodeType === Node.ELEMENT_NODE) && if ((evt.target.nodeType === Node.ELEMENT_NODE) &&
......
...@@ -234,7 +234,7 @@ ...@@ -234,7 +234,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>969.37531.32072.23688</string> </value> <value> <string>987.27464.21813.55483</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -252,7 +252,7 @@ ...@@ -252,7 +252,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1536936754.19</float> <float>1604428536.71</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
/*global window, rJS */ /*global window, document, rJS */
/*jslint indent: 2, maxerr: 3 */ /*jslint indent: 2, maxerr: 3 */
(function (window, rJS) { (function (window, document, rJS) {
"use strict"; "use strict";
rJS(window) rJS(window)
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
required: field_json.required, required: field_json.required,
id: field_json.key, id: field_json.key,
name: field_json.key, name: field_json.key,
error_text: field_json.error_text,
title: field_json.title, title: field_json.title,
hidden: field_json.hidden, hidden: field_json.hidden,
trim: true, trim: true,
...@@ -72,4 +73,4 @@ ...@@ -72,4 +73,4 @@
return true; return true;
}, {mutex: 'changestate'}); }, {mutex: 'changestate'});
}(window, rJS)); }(window, document, rJS));
\ No newline at end of file \ No newline at end of file
...@@ -234,7 +234,7 @@ ...@@ -234,7 +234,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>965.41976.11439.18449</string> </value> <value> <string>986.42689.22481.46592</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -252,7 +252,7 @@ ...@@ -252,7 +252,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1526396915.47</float> <float>1600380471.64</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
editable: field_json.editable, editable: field_json.editable,
name: field_json.key, name: field_json.key,
id: field_json.key, id: field_json.key,
error_text: field_json.error_text,
title: field_json.title, title: field_json.title,
hidden: field_json.hidden, hidden: field_json.hidden,
// Force calling subfield render // Force calling subfield render
......
...@@ -240,7 +240,7 @@ ...@@ -240,7 +240,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>965.12118.35525.1655</string> </value> <value> <string>986.42841.34859.34594</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -258,7 +258,7 @@ ...@@ -258,7 +258,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1517927278.74</float> <float>1600277966.0</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -96,6 +96,17 @@ ...@@ -96,6 +96,17 @@
} }
return data; return data;
}) })
.declareAcquiredMethod("notifyFocus", "notifyFocus")
.onEvent('focus', function focus() {
return this.notifyFocus();
}, true, false)
.declareAcquiredMethod("notifyBlur", "notifyBlur")
.onEvent('blur', function blur() {
return this.notifyBlur();
}, true, false)
.declareMethod("checkValidity", function checkValidity() { .declareMethod("checkValidity", function checkValidity() {
return true; return true;
}); });
......
...@@ -228,7 +228,7 @@ ...@@ -228,7 +228,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>982.42532.14902.56951</string> </value> <value> <string>986.41601.40161.17629</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -246,7 +246,7 @@ ...@@ -246,7 +246,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1584701796.37</float> <float>1601393849.37</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
type: options.type || 'text', type: options.type || 'text',
title: options.title, title: options.title,
focus: options.focus, focus: options.focus,
error_text: options.error_text || "",
step: options.step, step: options.step,
hidden: options.hidden, hidden: options.hidden,
trim: options.trim || false, trim: options.trim || false,
...@@ -43,13 +44,13 @@ ...@@ -43,13 +44,13 @@
.onStateChange(function onStateChange(modification_dict) { .onStateChange(function onStateChange(modification_dict) {
var textarea = this.element.querySelector('input'), var textarea = this.element.querySelector('input'),
tmp; // general use short-scope variable tmp; // general use short-scope variable
if (this.state.type === 'checkbox') { if (this.state.type === 'checkbox') {
textarea.checked = this.state.checked; textarea.checked = this.state.checked;
} else { } else {
textarea.setAttribute('value', this.state.value); textarea.setAttribute('value', this.state.value);
textarea.value = this.state.value; textarea.value = this.state.value;
} }
if (this.state.type === 'radio') { if (this.state.type === 'radio') {
textarea.checked = this.state.checked; textarea.checked = this.state.checked;
} }
...@@ -86,7 +87,7 @@ ...@@ -86,7 +87,7 @@
textarea.readonly = false; textarea.readonly = false;
} }
if (this.state.hidden) { if (this.state.hidden && !modification_dict.error_text) {
textarea.hidden = true; textarea.hidden = true;
} else { } else {
textarea.hidden = false; textarea.hidden = false;
...@@ -117,6 +118,15 @@ ...@@ -117,6 +118,15 @@
this.element.insertBefore(tmp, textarea); this.element.insertBefore(tmp, textarea);
tmp = undefined; tmp = undefined;
} }
if (this.state.error_text &&
!textarea.classList.contains("is-invalid")) {
textarea.classList.add("is-invalid");
} else if (!this.state.error_text &&
textarea.classList.contains("is-invalid")) {
textarea.classList.remove("is-invalid");
}
}) })
.declareService(function focus() { .declareService(function focus() {
...@@ -176,15 +186,24 @@ ...@@ -176,15 +186,24 @@
.declareAcquiredMethod("notifyValid", "notifyValid") .declareAcquiredMethod("notifyValid", "notifyValid")
.declareMethod('checkValidity', function checkValidity() { .declareMethod('checkValidity', function checkValidity() {
var result = this.element.querySelector('input').checkValidity(), var input = this.element.querySelector('input'),
result = input.checkValidity(),
gadget = this; gadget = this;
if (gadget.state.type === "radio") {
result = result && !gadget.state.error_text;
}
if (result) { if (result) {
return this.notifyValid() return gadget.notifyValid()
.push(function () { .push(function () {
var date, var date,
value; value;
if (!result) { if ((gadget.state.type === 'checkbox') && gadget.state.error_text) {
return result; return gadget.notifyInvalid(gadget.state.error_text)
.push(function () {
return result;
});
} }
if ((gadget.state.type === 'date') || if ((gadget.state.type === 'date') ||
(gadget.state.type === 'datetime-local')) { (gadget.state.type === 'datetime-local')) {
...@@ -194,7 +213,10 @@ ...@@ -194,7 +213,10 @@
if (isNaN(date)) { if (isNaN(date)) {
return gadget.translate("Invalid DateTime") return gadget.translate("Invalid DateTime")
.push(function (error_message) { .push(function (error_message) {
return gadget.notifyInvalid(error_message); return RSVP.all([
gadget.deferErrorText(error_message),
gadget.notifyInvalid(error_message)
]);
}) })
.push(function () { .push(function () {
return false; return false;
...@@ -205,9 +227,24 @@ ...@@ -205,9 +227,24 @@
return result; return result;
}); });
} }
if (gadget.state.error_text) {
return gadget.notifyInvalid(gadget.state.error_text)
.push(function () {
return result;
});
}
return result; return result;
}, {mutex: 'changestate'}) }, {mutex: 'changestate'})
.declareJob('deferErrorText', function deferErrorText(error_text) {
var input = this.element.querySelector("input");
return this.changeState({
value: input.value,
error_text: error_text
});
})
.declareAcquiredMethod("notifyChange", "notifyChange") .declareAcquiredMethod("notifyChange", "notifyChange")
.onEvent('change', function change() { .onEvent('change', function change() {
return RSVP.all([ return RSVP.all([
...@@ -215,6 +252,7 @@ ...@@ -215,6 +252,7 @@
this.notifyChange("change") this.notifyChange("change")
]); ]);
}, false, false) }, false, false)
.onEvent('input', function input() { .onEvent('input', function input() {
return RSVP.all([ return RSVP.all([
this.checkValidity(), this.checkValidity(),
...@@ -222,10 +260,23 @@ ...@@ -222,10 +260,23 @@
]); ]);
}, false, false) }, false, false)
.declareAcquiredMethod("notifyFocus", "notifyFocus")
.onEvent('focus', function focus() {
return this.notifyFocus();
}, true, false)
.declareAcquiredMethod("notifyBlur", "notifyBlur")
.onEvent('blur', function blur() {
return this.notifyBlur();
}, true, false)
.declareAcquiredMethod("notifyInvalid", "notifyInvalid") .declareAcquiredMethod("notifyInvalid", "notifyInvalid")
.onEvent('invalid', function invalid(evt) { .onEvent('invalid', function invalid(evt) {
// invalid event does not bubble // invalid event does not bubble
return this.notifyInvalid(evt.target.validationMessage); return RSVP.all([
this.deferErrorText(evt.target.validationMessage),
this.notifyInvalid(evt.target.validationMessage)
]);
}, true, false); }, true, false);
}(window, document, rJS, RSVP, jIO, getFirstNonEmpty)); }(window, document, rJS, RSVP, jIO, getFirstNonEmpty));
\ No newline at end of file
...@@ -234,7 +234,7 @@ ...@@ -234,7 +234,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>967.40700.16743.2833</string> </value> <value> <string>987.27756.45244.12953</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -252,7 +252,7 @@ ...@@ -252,7 +252,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1526653024.9</float> <float>1603216380.65</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
item_list: JSON.stringify(options.item_list), item_list: JSON.stringify(options.item_list),
editable: options.editable, editable: options.editable,
required: options.required, required: options.required,
error_text: options.error_text || "",
id: options.id, id: options.id,
name: options.name, name: options.name,
title: options.title, title: options.title,
...@@ -41,6 +42,13 @@ ...@@ -41,6 +42,13 @@
select.id = this.state.id || this.state.name; select.id = this.state.id || this.state.name;
select.setAttribute('name', this.state.name); select.setAttribute('name', this.state.name);
if (modification_dict.error_text &&
!select.classList.contains("is-invalid")) {
select.classList.add("is-invalid");
} else if (select.classList.contains("is-invalid")) {
select.classList.remove("is-invalid");
}
if (this.state.title) { if (this.state.title) {
select.setAttribute('title', this.state.title); select.setAttribute('title', this.state.title);
} }
...@@ -118,7 +126,8 @@ ...@@ -118,7 +126,8 @@
.declareAcquiredMethod("notifyValid", "notifyValid") .declareAcquiredMethod("notifyValid", "notifyValid")
.declareMethod('checkValidity', function checkValidity() { .declareMethod('checkValidity', function checkValidity() {
var result = this.element.querySelector('select').checkValidity(); var select = this.element.querySelector('select'),
result = select.checkValidity() || this.state.error_text === "";
if (result) { if (result) {
return this.notifyValid() return this.notifyValid()
.push(function () { .push(function () {
...@@ -142,6 +151,16 @@ ...@@ -142,6 +151,16 @@
]); ]);
}, false, false) }, false, false)
.declareAcquiredMethod("notifyFocus", "notifyFocus")
.onEvent('focus', function focus() {
return this.notifyFocus();
}, true, false)
.declareAcquiredMethod("notifyBlur", "notifyBlur")
.onEvent('blur', function blur() {
return this.notifyBlur();
}, true, false)
.declareAcquiredMethod("notifyInvalid", "notifyInvalid") .declareAcquiredMethod("notifyInvalid", "notifyInvalid")
.onEvent('invalid', function invalid(evt) { .onEvent('invalid', function invalid(evt) {
// invalid event does not bubble // invalid event does not bubble
......
...@@ -234,7 +234,7 @@ ...@@ -234,7 +234,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>978.42847.9438.64699</string> </value> <value> <string>986.42836.14245.5444</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -252,7 +252,7 @@ ...@@ -252,7 +252,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1569589427.71</float> <float>1600275334.01</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -24,6 +24,14 @@ ...@@ -24,6 +24,14 @@
.onStateChange(function onStateChange(modification_dict) { .onStateChange(function onStateChange(modification_dict) {
var textarea = this.element.querySelector('textarea'); var textarea = this.element.querySelector('textarea');
if (this.state.error_text &&
!textarea.classList.contains("is-invalid")) {
textarea.classList.add("is-invalid");
} else if (!this.state.error_text &&
textarea.classList.contains("is-invalid")) {
textarea.classList.remove("is-invalid");
}
if (modification_dict.hasOwnProperty("value")) { if (modification_dict.hasOwnProperty("value")) {
textarea.value = modification_dict.value; textarea.value = modification_dict.value;
} }
...@@ -74,7 +82,8 @@ ...@@ -74,7 +82,8 @@
.declareAcquiredMethod("notifyValid", "notifyValid") .declareAcquiredMethod("notifyValid", "notifyValid")
.declareMethod('checkValidity', function checkValidity() { .declareMethod('checkValidity', function checkValidity() {
var result = this.element.querySelector('textarea').checkValidity(); var textarea = this.element.querySelector('textarea'),
result = textarea.checkValidity();
if (result) { if (result) {
return this.notifyValid() return this.notifyValid()
.push(function () { .push(function () {
...@@ -92,7 +101,17 @@ ...@@ -92,7 +101,17 @@
.onEvent('invalid', function invalid(evt) { .onEvent('invalid', function invalid(evt) {
// invalid event does not bubble // invalid event does not bubble
return this.notifyInvalid(evt.target.validationMessage); return this.notifyInvalid(evt.target.validationMessage);
}, true, true) }, true, false)
.declareAcquiredMethod("notifyFocus", "notifyFocus")
.onEvent('focus', function focus() {
return this.notifyFocus();
}, true, false)
.declareAcquiredMethod("notifyBlur", "notifyBlur")
.onEvent('blur', function blur() {
return this.notifyBlur();
}, true, false)
.declareAcquiredMethod("notifySubmit", "notifySubmit") .declareAcquiredMethod("notifySubmit", "notifySubmit")
.onEvent('keydown', function keydown(evt) { .onEvent('keydown', function keydown(evt) {
......
...@@ -226,7 +226,7 @@ ...@@ -226,7 +226,7 @@
</item> </item>
<item> <item>
<key> <string>actor</string> </key> <key> <string>actor</string> </key>
<value> <string>zope</string> </value> <value> <string>ERP5TypeTestCase</string> </value>
</item> </item>
<item> <item>
<key> <string>comment</string> </key> <key> <string>comment</string> </key>
...@@ -240,7 +240,7 @@ ...@@ -240,7 +240,7 @@
</item> </item>
<item> <item>
<key> <string>serial</string> </key> <key> <string>serial</string> </key>
<value> <string>966.49875.42877.4590</string> </value> <value> <string>987.741.47228.29815</string> </value>
</item> </item>
<item> <item>
<key> <string>state</string> </key> <key> <string>state</string> </key>
...@@ -258,7 +258,7 @@ ...@@ -258,7 +258,7 @@
</tuple> </tuple>
<state> <state>
<tuple> <tuple>
<float>1526653198.19</float> <float>1601596591.47</float>
<string>UTC</string> <string>UTC</string>
</tuple> </tuple>
</state> </state>
......
...@@ -434,13 +434,16 @@ input:not([type=submit]):not([type=file]):not([type=checkbox]):not([type=radio]) ...@@ -434,13 +434,16 @@ input:not([type=submit]):not([type=file]):not([type=checkbox]):not([type=radio])
border: @focus-border; border: @focus-border;
box-shadow: @focus-box-shadow; box-shadow: @focus-box-shadow;
} }
&:invalid { &:invalid, &.is-invalid {
border: @invalid-border; border: @invalid-border;
&:focus { &:focus {
box-shadow: @invalid-box-shadow; box-shadow: @invalid-box-shadow;
} }
} }
} }
label.is-invalid {
color: @txtorange;
}
// Force safari to render search input as text // Force safari to render search input as text
// https://stackoverflow.com/questions/34802552/safari-input-type-search-reset-normalize // https://stackoverflow.com/questions/34802552/safari-input-type-search-reset-normalize
...@@ -1294,7 +1297,7 @@ div[data-gadget-scope='header'] .ui-header { ...@@ -1294,7 +1297,7 @@ div[data-gadget-scope='header'] .ui-header {
border-bottom-style: none; border-bottom-style: none;
box-shadow: 0 1px 3px rgba(0,0,0,.15); box-shadow: 0 1px 3px rgba(0,0,0,.15);
background-color: @colorbackground; background-color: @colorbackground;
&:not(.ui-li-has-count) { &:not(.ui-li-has-count) {
a { a {
&:after { &:after {
...@@ -1360,7 +1363,7 @@ div[data-gadget-scope='header'] .ui-header { ...@@ -1360,7 +1363,7 @@ div[data-gadget-scope='header'] .ui-header {
//Label //Label
.ui-field-contain { .ui-field-contain {
& > label { & > label:not(:is-invalid) {
color: @colorlabel; color: @colorlabel;
} }
......
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