Commit 0747c524 authored by Gabriel Monnerat's avatar Gabriel Monnerat Committed by Romain Courteaud

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/
parent 3aea11b9
......@@ -220,7 +220,7 @@
</item>
<item>
<key> <string>actor</string> </key>
<value> <string>zope</string> </value>
<value> <string>ERP5TypeTestCase</string> </value>
</item>
<item>
<key> <string>comment</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1583764359.91</float>
<float>1597709775.0</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>
......
......@@ -240,7 +240,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>969.56695.26145.14506</string> </value>
<value> <string>985.61208.29353.29986</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -258,7 +258,7 @@
</tuple>
<state>
<tuple>
<float>1535383243.32</float>
<float>1597610604.97</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>965.241.13759.3805</string> </value>
<value> <string>985.61208.29353.29986</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1517321664.03</float>
<float>1597610628.93</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -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>985.61208.29353.29986</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1526316882.68</float>
<float>1597610664.04</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>983.13203.65355.60228</string> </value>
<value> <string>985.61208.29353.29986</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1586873574.82</float>
<float>1597610645.53</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>
......
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>970.5118.62758.3362</string> </value>
<value> <string>986.41646.43198.36846</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1536223691.31</float>
<float>1600363167.27</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -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,23 @@
}
}
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 (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 +160,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 +201,7 @@
});
}
}
})
.declareMethod("checkValidity", function checkValidity() {
......@@ -231,6 +234,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>986.60444.45045.17527</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1596116391.77</float>
<float>1601331703.32</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>964.58561.19908.14080</string> </value>
<value> <string>985.61208.29353.29986</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1517321571.05</float>
<float>1597610715.85</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>
......
......@@ -52,14 +52,20 @@
}
rJS(window)
.setState({
display_error_text: false
})
.ready(function ready() {
this.props = {};
})
.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
......@@ -69,15 +75,22 @@
return this.changeState(state_dict);
})
.onStateChange(function () {
.onStateChange(function (modification_dict) {
var element = this.element,
gadget = this,
value_list = this.state.value_list,
value_dict = {},
item_list = this.state.item_list,
i,
span,
queue;
if (!gadget.props.container_element) {
gadget.props = {
container_element: gadget.element.querySelector('div'),
label_element: gadget.element.querySelector('label')
};
}
// Clear first to DOM, append after to reduce flickering/manip
while (element.firstChild) {
element.removeChild(element.firstChild);
......@@ -87,6 +100,28 @@
value_dict[value_list[i]] = null;
}
if (modification_dict.hasOwnProperty('display_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 && this.state.display_error_text) {
if (span === null) {
span = document.createElement('span');
span.textContent = this.state.error_text;
this.props.container_element.appendChild(span);
} else {
span.textContent = this.state.error_text;
}
} else {
if (span !== null) {
this.props.container_element.removeChild(span);
}
}
}
function enQueue() {
var argument_list = arguments;
queue
......@@ -148,6 +183,18 @@
return final_result;
}, {mutex: 'changestate'})
.allowPublicAcquisition("notifyFocus", function notifyFocus() {
if (this.state.error_text) {
return this.changeState({display_error_text: true});
}
})
.allowPublicAcquisition("notifyBlur", function notifyBlur() {
if (this.state.error_text) {
return this.changeState({display_error_text: false});
}
})
.declareMethod('checkValidity', function () {
var name = this.state.name;
if (this.state.editable && this.state.required) {
......
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>971.50596.31143.27050</string> </value>
<value> <string>986.54583.57508.4539</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1542818919.2</float>
<float>1600980241.43</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>976.44236.22845.43076</string> </value>
<value> <string>985.61208.29353.29986</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1561643243.53</float>
<float>1597610832.8</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -329,12 +329,18 @@ 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;
}
input[type="search"] {
......@@ -3264,4 +3270,4 @@ hmtl .ui-icon-carat-u::before {
}
.ui-icon-clone::before {
content: "\f24d";
}
}
\ No newline at end of file
......@@ -75,9 +75,7 @@
</item>
<item>
<key> <string>content_type</string> </key>
<value>
<none/>
</value>
<value> <string>text/css</string> </value>
</item>
<item>
<key> <string>default_reference</string> </key>
......@@ -246,7 +244,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>986.53918.32237.3225</string> </value>
<value> <string>986.41601.40161.17629</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -264,7 +262,7 @@
</tuple>
<state>
<tuple>
<float>1600941690.9</float>
<float>1601474815.27</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>965.12118.35525.1655</string> </value>
<value> <string>985.61208.29353.29986</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1517929207.17</float>
<float>1597610855.52</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>979.2796.18260.40345</string> </value>
<value> <string>986.41601.40161.17629</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1570779670.8</float>
<float>1600363560.34</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -149,6 +149,7 @@
key: options.key,
view: options.view,
search_view: options.search_view,
error_text: options.error_text || "",
url: options.url,
allow_creation: options.allow_creation,
portal_types: JSON.stringify(options.portal_types),
......@@ -231,8 +232,19 @@
// First display of the input
buildEditableInputHTML(gadget);
}
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")) {
input.value = gadget.state.value_text;
}
......@@ -417,6 +429,14 @@
"Invalid Search Criteria"
])
.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]);
});
}
......@@ -429,9 +449,25 @@
])
.push(function (translation_list) {
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]);
}
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]);
}
throw error;
......@@ -513,6 +549,7 @@
}
}, false, false)
.declareAcquiredMethod("notifyBlur", "notifyBlur")
.onEvent('blur', function (evt) {
var gadget = this;
if (evt.target.tagName.toLowerCase() === 'input') {
......@@ -525,22 +562,27 @@
return gadget.changeState({
has_focus: false
});
})
.push(function () {
return gadget.notifyBlur();
});
}
}, true, false)
.declareAcquiredMethod("notifyFocus", "notifyFocus")
.onEvent('focus', function (evt) {
var gadget = this;
if (evt.target.tagName.toLowerCase() === 'input') {
return gadget.changeState({
has_focus: true
});
return RSVP.all([
gadget.notifyFocus(),
gadget.changeState({has_focus: true})
]);
}
}, true, false)
.declareAcquiredMethod("notifyValid", "notifyValid")
.declareMethod('checkValidity', function () {
var gadget = this;
if ((this.state.value_text) && (
(this.state.value_relative_url === null) &&
(this.state.value_uid === null) &&
......@@ -548,43 +590,64 @@
)) {
return gadget.translate("No such document was found")
.push(function (error_message) {
return gadget.notifyInvalid(error_message);
return RSVP.all([
gadget.deferErrorText(error_message),
gadget.notifyInvalid(error_message)
]);
})
.push(function () {
return false;
});
}
return true;
return gadget.notifyValid()
.push(function () {
return true;
});
}, {mutex: 'changestate'})
.declareJob('deferErrorText', function deferErrorText(error_text) {
return this.changeState({
error_text: error_text
});
})
// XXX Use html5 input
.onEvent('invalid', function (evt) {
// 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)
.onEvent('change', function () {
return this.notifyChange();
return RSVP.all([
this.checkValidity(),
this.notifyChange()
]);
}, false, false)
.onEvent('input', function (event) {
var gadget = this;
if (!this.state.editable) {
return;
}
var context = this;
return this.changeState({
value_text: event.target.value,
value_relative_url: null,
value_uid: null,
value_portal_type: null,
has_focus: true
has_focus: true,
error_text: ""
})
.push(function () {
return context.notifyChange();
return RSVP.all([
gadget.notifyValid(),
gadget.notifyChange()
]);
});
}, true, false);
}(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 @@
</item>
<item>
<key> <string>content_type</string> </key>
<value>
<none/>
</value>
<value> <string>text/javascript</string> </value>
</item>
<item>
<key> <string>default_reference</string> </key>
......@@ -240,7 +238,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>983.6083.54777.37051</string> </value>
<value> <string>986.61480.35990.11093</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -258,7 +256,7 @@
</tuple>
<state>
<tuple>
<float>1586446542.77</float>
<float>1601395258.9</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -22,6 +22,7 @@
title: field_json.title,
key: field_json.key,
view: field_json.view,
error_text: field_json.error_text,
search_view: field_json.search_view,
url: field_json.url,
allow_creation: field_json.allow_creation,
......
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>970.13790.51027.29644</string> </value>
<value> <string>986.41646.43198.36846</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1539872919.1</float>
<float>1600685092.05</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -210,6 +210,9 @@
});
}, {mutex: 'changestate'})
.allowPublicAcquisition("notifyFocus", function notifyFocus() {})
.allowPublicAcquisition("notifyBlur", function notifyBlur() {})
.declareAcquiredMethod("triggerSubmit", "triggerSubmit")
.onEvent('click', function (evt) {
if ((evt.target.nodeType === Node.ELEMENT_NODE) &&
......
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>969.37531.32072.23688</string> </value>
<value> <string>985.45375.12940.10598</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1536936754.19</float>
<float>1597428338.32</float>
<string>UTC</string>
</tuple>
</state>
......
/*global window, rJS */
/*global window, document, rJS */
/*jslint indent: 2, maxerr: 3 */
(function (window, rJS) {
(function (window, document, rJS) {
"use strict";
rJS(window)
......@@ -16,6 +16,7 @@
required: field_json.required,
id: field_json.key,
name: field_json.key,
error_text: field_json.error_text || "",
title: field_json.title,
hidden: field_json.hidden,
trim: true,
......@@ -72,4 +73,4 @@
return true;
}, {mutex: 'changestate'});
}(window, rJS));
\ No newline at end of file
}(window, document, rJS));
\ No newline at end of file
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>965.41976.11439.18449</string> </value>
<value> <string>986.42689.22481.46592</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1526396915.47</float>
<float>1600380471.64</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -14,6 +14,7 @@
editable: field_json.editable,
name: field_json.key,
id: field_json.key,
error_text: field_json.error_text,
title: field_json.title,
hidden: field_json.hidden,
// Force calling subfield render
......
......@@ -240,7 +240,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>965.12118.35525.1655</string> </value>
<value> <string>986.42841.34859.34594</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -258,7 +258,7 @@
</tuple>
<state>
<tuple>
<float>1517927278.74</float>
<float>1600277966.0</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -96,6 +96,17 @@
}
return data;
})
.declareAcquiredMethod("notifyFocus", "notifyFocus")
.onEvent('focus', function focus(evt) {
return this.notifyFocus();
}, true, false)
.declareAcquiredMethod("notifyBlur", "notifyBlur")
.onEvent('blur', function blur(evt) {
return this.notifyBlur();
}, true, false)
.declareMethod("checkValidity", function checkValidity() {
return true;
});
......
......@@ -228,7 +228,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>982.42532.14902.56951</string> </value>
<value> <string>985.61208.29353.29986</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -246,7 +246,7 @@
</tuple>
<state>
<tuple>
<float>1584701796.37</float>
<float>1597615378.38</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -29,6 +29,7 @@
type: options.type || 'text',
title: options.title,
focus: options.focus,
error_text: options.error_text || "",
step: options.step,
hidden: options.hidden,
trim: options.trim || false,
......@@ -43,13 +44,13 @@
.onStateChange(function onStateChange(modification_dict) {
var textarea = this.element.querySelector('input'),
tmp; // general use short-scope variable
if (this.state.type === 'checkbox') {
textarea.checked = this.state.checked;
} else {
textarea.setAttribute('value', this.state.value);
textarea.value = this.state.value;
}
if (this.state.type === 'radio') {
textarea.checked = this.state.checked;
}
......@@ -86,7 +87,7 @@
textarea.readonly = false;
}
if (this.state.hidden) {
if (this.state.hidden && !modification_dict.error_text) {
textarea.hidden = true;
} else {
textarea.hidden = false;
......@@ -117,6 +118,15 @@
this.element.insertBefore(tmp, textarea);
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() {
......@@ -194,7 +204,10 @@
if (isNaN(date)) {
return gadget.translate("Invalid DateTime")
.push(function (error_message) {
return gadget.notifyInvalid(error_message);
return RSVP.all([
gadget.deferErrorText(error_message),
gadget.notifyInvalid(error_message)
]);
})
.push(function () {
return false;
......@@ -208,6 +221,14 @@
return result;
}, {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")
.onEvent('change', function change() {
return RSVP.all([
......@@ -215,6 +236,7 @@
this.notifyChange("change")
]);
}, false, false)
.onEvent('input', function input() {
return RSVP.all([
this.checkValidity(),
......@@ -222,10 +244,23 @@
]);
}, 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")
.onEvent('invalid', function invalid(evt) {
// 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);
}(window, document, rJS, RSVP, jIO, getFirstNonEmpty));
\ No newline at end of file
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>967.40700.16743.2833</string> </value>
<value> <string>986.63389.32983.19234</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1526653024.9</float>
<float>1601508986.01</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -22,6 +22,7 @@
item_list: JSON.stringify(options.item_list),
editable: options.editable,
required: options.required,
error_text: options.error_text || "",
id: options.id,
name: options.name,
title: options.title,
......@@ -41,6 +42,13 @@
select.id = this.state.id || 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) {
select.setAttribute('title', this.state.title);
}
......@@ -118,7 +126,8 @@
.declareAcquiredMethod("notifyValid", "notifyValid")
.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) {
return this.notifyValid()
.push(function () {
......@@ -142,6 +151,16 @@
]);
}, 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")
.onEvent('invalid', function invalid(evt) {
// invalid event does not bubble
......
......@@ -234,7 +234,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>978.42847.9438.64699</string> </value>
<value> <string>986.42836.14245.5444</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -252,7 +252,7 @@
</tuple>
<state>
<tuple>
<float>1569589427.71</float>
<float>1600275334.01</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -24,6 +24,14 @@
.onStateChange(function onStateChange(modification_dict) {
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")) {
textarea.value = modification_dict.value;
}
......@@ -74,7 +82,8 @@
.declareAcquiredMethod("notifyValid", "notifyValid")
.declareMethod('checkValidity', function checkValidity() {
var result = this.element.querySelector('textarea').checkValidity();
var textarea = this.element.querySelector('textarea'),
result = textarea.checkValidity();
if (result) {
return this.notifyValid()
.push(function () {
......@@ -94,6 +103,16 @@
return this.notifyInvalid(evt.target.validationMessage);
}, true, true)
.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")
.onEvent('keydown', function keydown(evt) {
var textarea = this.element.querySelector('textarea');
......
......@@ -240,7 +240,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>966.49875.42877.4590</string> </value>
<value> <string>986.62926.53764.37222</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -258,7 +258,7 @@
</tuple>
<state>
<tuple>
<float>1526653198.19</float>
<float>1601505155.98</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -434,7 +434,7 @@ input:not([type=submit]):not([type=file]):not([type=checkbox]):not([type=radio])
border: @focus-border;
box-shadow: @focus-box-shadow;
}
&:invalid {
&:invalid, &.is-invalid {
border: @invalid-border;
&:focus {
box-shadow: @invalid-box-shadow;
......
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