Permalink
Browse files

feat #405 Error tool tip enhancement

Currently the logic for the error tool tip is contained within
aria.widgets.form.Input, the aim of this enhancement is to share code
efficiently between multiple types of widgets. Specifically for the
requirement of this feature, this means making the code for the error tool
tip not only available for "Input" type widgets, but also for "Action"
type widgets.

In addition, it must also be possible from within a template to have a new
widget property called errorTipPosition that will be used as the preferred
position for the error tool tip, if there is not enough available space
the framework will automatically calculate the best position for the error
tool tip. The property will be added for both "Input" type widgets and
also "Action" type widgets.

Close #405
  • Loading branch information...
simonarbuckle authored and fcrisci committed Mar 6, 2013
1 parent c71eea3 commit e6405a1b79c707f2b4090875ea2e2673f29d5c71
View
@@ -46,9 +46,9 @@ Aria.classDefinition({
onBeforeOpen : "",
onAfterOpen : "",
onPositioned : {
- description : "Triggered when position of the popup is choosen, according to prefered position provided",
+ description : "Triggered when position of the popup is chosen, according to prefered position provided",
properties : {
- position : "Position choosed if any. If empty, no position in viewset was found."
+ position : "Position chosen if any. If empty, no position in viewset was found."
}
}
},
@@ -469,7 +469,6 @@ Aria.classDefinition({
* @protected
*/
_getPositionForAnchor : function (preferredPosition, size) {
- var base = this.reference;
var referenceAnchor = preferredPosition.reference;
var popupAnchor = preferredPosition.popup;
@@ -632,13 +631,17 @@ Aria.classDefinition({
} else {
this.computedStyle = this._getComputedStyle();
}
-
- this.domElement.style.cssText = ['top:', this.computedStyle.top, 'px;', 'left:', this.computedStyle.left,
- 'px;', 'z-index:', this.computedStyle.zIndex, ';', 'position:absolute;display:inline-block;'].join('');
- if (aria.core.Browser.isIE7 && !this.isOpen) {
- // Without the following line, the autocomplete does not
- // initially display its content on IE7:
- this._document.body.appendChild(this.domElement);
+ // Need to check that the reference point is still completely visible after a scroll
+ var referenceIsInViewSet = aria.utils.Dom.isInViewport(this.referencePosition, this.referenceSize, this.domElement);
+ if (referenceIsInViewSet) {
+ this.domElement.style.cssText = ['top:', this.computedStyle.top, 'px;', 'left:',
+ this.computedStyle.left, 'px;', 'z-index:', this.computedStyle.zIndex, ';',
+ 'position:absolute;display:inline-block;'].join('');
+ if (aria.core.Browser.isIE7 && !this.isOpen) {
+ // Without the following line, the autocomplete does not
+ // initially display its content on IE7:
+ this._document.body.appendChild(this.domElement);
+ }
}
},
@@ -180,6 +180,9 @@ Aria.beanDefinitions({
},
"name" : {
$type : "common:BindingRef"
+ },
+ "errorTipPosition" : {
+ $type : "common:BindingRef"
}
}
},
@@ -307,6 +310,11 @@ Aria.beanDefinitions({
$type : "json:Boolean",
$description : "Indicates if the label must be displayed or not (if true the label is hidden)",
$default : false
+ },
+ "errorTipPosition" : {
+ $type : "json:String",
+ $description : "Possible values are: 'bottom left', 'bottom right', 'top left', 'top right'.",
+ $default : "top right"
}
}
},
@@ -857,6 +865,20 @@ Aria.beanDefinitions({
$type : "WidgetCfg",
$description : "The base configuration for the button widget",
$properties : {
+ "bind" : {
+ $type : "WidgetCfg.bind",
+ $properties : {
+ "error" : {
+ $type : "common:BindingRef"
+ },
+ "errorMessages" : {
+ $type : "common:BindingRef"
+ },
+ "errorTipPosition" : {
+ $type : "common:BindingRef"
+ }
+ }
+ },
"label" : {
$type : "json:String",
$description : "Text to put inside the label in the button",
@@ -865,6 +887,25 @@ Aria.beanDefinitions({
"onclick" : {
$type : "common:Callback",
$description : "Function to be called when the user clicks on the button."
+ },
+ "error" : {
+ $type : "json:Boolean",
+ $description : "Highlights the widget to notify an error to the user, defined in a template script.",
+ $default : false
+ },
+ "errorMessages" : {
+ $type : "json:Array",
+ $description : "List of error messages associated to the widget, defined in a template script - these messages will be automatically displayed in a contextual error tooltip. If this array is not empty the error property is automatically set to true",
+ $contentType : {
+ $type : "json:String",
+ $description : "Error message associated to the widget"
+ },
+ $default : []
+ },
+ "errorTipPosition" : {
+ $type : "json:String",
+ $description : "Possible values are: 'bottom left', 'bottom right', 'top left', 'top right'.",
+ $default : "top right"
}
}
},
@@ -874,7 +915,7 @@ Aria.beanDefinitions({
$description : "The base configuration for the button widget",
$properties : {
"bind" : {
- $type : "WidgetCfg.bind",
+ $type : "ActionWidgetCfg.bind",
$properties : {
"disabled" : {
$type : "common:BindingRef"
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2012 Amadeus s.a.s.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * WidgetTrait is a class to share code between input widgets and action widgets, although this can be extended to
+ * include other widget types in the future. The purpose of this class is not to be created directly, but to allow its
+ * prototype to be imported.
+ */
+Aria.classDefinition({
+ $classpath : "aria.widgets.WidgetTrait",
+ $dependencies : ["aria.widgets.form.InputValidationHandler"],
+ $constructor : function () {
+ // The purpose of this class is to provide a prototype to be imported, not to be created directly.
+ this.$assert(11, false);
+ },
+ $prototype : {
+ /**
+ * Method used when a validation popup is needed for an input field
+ * @protected
+ */
+ _validationPopupShow : function () {
+ // check validation popup isn't already displayed
+ if (!this._onValidatePopup) {
+ this._onValidatePopup = new aria.widgets.form.InputValidationHandler(this);
+ }
+ this._onValidatePopup.show();
+ },
+
+ /**
+ * Method used to close the validation popup of an input field
+ * @protected
+ */
+ _validationPopupHide : function () {
+ if (this._onValidatePopup) {
+ this._onValidatePopup.hide();
+ }
+ }
+ }
+});
@@ -21,11 +21,11 @@
Aria.classDefinition({
$classpath : "aria.widgets.action.ActionWidget",
$extends : "aria.widgets.Widget",
- $dependencies : ["aria.utils.Function", "aria.utils.Dom", "aria.templates.DomEventWrapper"],
+ $dependencies : ["aria.utils.Function", "aria.utils.Dom", "aria.templates.DomEventWrapper",
+ "aria.widgets.WidgetTrait"],
/**
* ActionWidget constructor
* @param {aria.widgets.CfgBeans.ActionWidgetCfg} cfg the widget configuration
- * @param {aria.templates.TemplateCtxt} ctxt template context
*/
$constructor : function () {
this.$Widget.constructor.apply(this, arguments);
@@ -38,14 +38,32 @@ Aria.classDefinition({
this._actingDom = null;
},
$destructor : function () {
-
+ if (this._onValidatePopup) {
+ this._onValidatePopup.$dispose();
+ this._onValidatePopup = null;
+ }
if (this._actingDom) {
this._actingDom = null;
}
this.$Widget.$destructor.call(this);
},
$prototype : {
+ /**
+ * Prototype init method called at prototype creation time Allows to store class-level objects that are shared
+ * by all instances
+ * @param {Object} p the prototype object being built
+ */
+ $init : function (p) {
+ var src = aria.widgets.WidgetTrait.prototype;
+ for (var key in src) {
+ if (src.hasOwnProperty(key) && !p.hasOwnProperty(key)) {
+ // copy methods which are not already on this object (this avoids copying $classpath and
+ // $destructor)
+ p[key] = src[key];
+ }
+ }
+ },
/**
* Called when a new instance is initialized
@@ -82,15 +100,21 @@ Aria.classDefinition({
* Performs the action associated with the widget. Normally called for example when clicked or a key is pressed
*/
_performAction : function (domEvent) {
- if (this._cfg) {
+ var cfg = this._cfg;
+ if (cfg) {
var domEvtWrapper;
if (domEvent) {
domEvtWrapper = new aria.templates.DomEventWrapper(domEvent);
}
- var returnValue = this.evalCallback(this._cfg.onclick, domEvtWrapper);
+ var returnValue = this.evalCallback(cfg.onclick, domEvtWrapper);
if (domEvtWrapper) {
domEvtWrapper.$dispose();
}
+ if (cfg.error && cfg.errorMessages.length) {
+ this._validationPopupShow();
+ } else {
+ this._validationPopupHide();
+ }
return returnValue;
}
return true;
@@ -19,8 +19,8 @@
Aria.classDefinition({
$classpath : "aria.widgets.form.Input",
$extends : "aria.widgets.Widget",
- $dependencies : ["aria.utils.Dom", "aria.widgets.form.InputValidationHandler", "aria.utils.Data",
- "aria.utils.String", "aria.widgets.environment.WidgetSettings", "aria.core.Browser"],
+ $dependencies : ["aria.utils.Dom", "aria.utils.Data", "aria.utils.String",
+ "aria.widgets.environment.WidgetSettings", "aria.core.Browser", "aria.widgets.WidgetTrait"],
/**
* Input constructor
* @param {aria.widgets.CfgBeans.InputCfg} cfg the widget configuration
@@ -106,11 +106,18 @@ Aria.classDefinition({
* @param {Object} sdef the superclass class definition
*/
$init : function (p, def, sdef) {
+ var src = aria.widgets.WidgetTrait.prototype;
+ for (var key in src) {
+ if (src.hasOwnProperty(key) && !p.hasOwnProperty(key)) {
+ // copy methods which are not already on this object (this avoids copying $classpath and
+ // $destructor)
+ p[key] = src[key];
+ }
+ }
// we add the bindable properties to the Widget prototype
p.automaticallyBindedProperties = ["formatError", "formatErrorMessages", "error", "errorMessages",
"requireFocus"];
},
-
/**
* Override the Widget _init method
* @protected
@@ -355,28 +362,6 @@ Aria.classDefinition({
this.$Widget._checkCfgConsistency.call(this);
},
- /**
- * Method used when a validation popup is needed for an input field
- * @protected
- */
- _validationPopupShow : function () {
- // check validation popup isn't already displayed
- if (!this._onValidatePopup) {
- this._onValidatePopup = new aria.widgets.form.InputValidationHandler(this);
- }
- this._onValidatePopup.show();
- },
-
- /**
- * Method used to close the validation popup of an input field
- * @protected
- */
- _validationPopupHide : function () {
- if (this._onValidatePopup) {
- this._onValidatePopup.hide();
- }
- },
-
/**
* Internal method called when one of the model property that the widget is bound to has changed Must be
* overridden by sub-classes defining bindable properties
@@ -422,7 +407,7 @@ Aria.classDefinition({
/**
* Apply the automatic bindings
- * @param {aria.widgets.CfgBeans.InputCfg} cfg
+ * @param {aria.widgets.CfgBeans.InputCfg|aria.widgets.CfgBeans.ActionWidgetCfg} cfg
* @protected
*/
_setAutomaticBindings : function (cfg) {
@@ -496,4 +481,4 @@ Aria.classDefinition({
}
}
}
-});
+});
Oops, something went wrong.

0 comments on commit e6405a1

Please sign in to comment.