diff --git a/packages/main/src/DatePicker.hbs b/packages/main/src/DatePicker.hbs index 364804683eef..03671900b5ac 100644 --- a/packages/main/src/DatePicker.hbs +++ b/packages/main/src/DatePicker.hbs @@ -18,6 +18,11 @@ data-sap-focus-ref ._inputAccInfo ="{{accInfo}}" > + + {{#if valueStateMessage.length}} + + {{/if}} + {{#unless readonly}} ui5-datepicker. + *

+ * + * Note: If not specified, a default text (in the respective language) will be displayed. + *
+ * Note: The valueStateMessage would be displayed, + * when the ui5-datepicker is in Information, Warning or Error value state. + * @type {HTMLElement} + * @since 1.0.0-rc.7 + * @slot + * @public + */ + valueStateMessage: { + type: HTMLElement, + }, + }, + events: /** @lends sap.ui.webcomponents.main.DatePicker.prototype */ { /** diff --git a/packages/main/src/Input.js b/packages/main/src/Input.js index 84ada468e8b6..bfa36412a991 100644 --- a/packages/main/src/Input.js +++ b/packages/main/src/Input.js @@ -300,6 +300,11 @@ const metadata = { type: Boolean, noAttribute: true, }, + + _inputIconFocused: { + type: Boolean, + noAttribute: true, + }, }, events: /** @lends sap.ui.webcomponents.main.Input.prototype */ { /** @@ -474,7 +479,7 @@ class Input extends UI5Element { if (!isPhone() && shouldOpenSuggestions) { // Set initial focus to the native input - this.getInputDOMRef().focus(); + this.inputDomRef.focus(); } } @@ -535,15 +540,21 @@ class Input extends UI5Element { } } - _onfocusin(event) { + async _onfocusin(event) { this.focused = true; // invalidating property this.previousValue = this.value; + + await this.getInputDOMRef(); + this._inputIconFocused = event.target === this.querySelector("ui5-icon"); } _onfocusout(event) { - // if focusout is triggered by pressing on suggestion item skip invalidation, because re-rendering + const focusedOutToSuggestions = this.Suggestions && event.relatedTarget && event.relatedTarget.shadowRoot && event.relatedTarget.shadowRoot.contains(this.Suggestions.responsivePopover); + const focusedOutToValueStateMessage = event.relatedTarget && event.relatedTarget.shadowRoot && event.relatedTarget.shadowRoot.querySelector(".ui5-valuestatemessage-root"); + + // if focusout is triggered by pressing on suggestion item or value state message popover, skip invalidation, because re-rendering // will happen before "itemPress" event, which will make item "active" state not visualized - if (this.Suggestions && event.relatedTarget && event.relatedTarget.shadowRoot && event.relatedTarget.shadowRoot.contains(this.Suggestions.responsivePopover)) { + if (focusedOutToSuggestions || focusedOutToValueStateMessage) { return; } @@ -567,8 +578,9 @@ class Input extends UI5Element { this.fireEvent(this.EVENT_CHANGE); } - _handleInput(event) { - if (event.target === this.getInputDOMRef()) { + async _handleInput(event) { + await this.getInputDOMRef(); + if (event.target === this.inputDomRef) { // stop the native event, as the semantic "input" would be fired. event.stopImmediatePropagation(); } @@ -577,7 +589,7 @@ class Input extends UI5Element { - value of the host and the internal input should be differnt in case of actual input - input is called when a key is pressed => keyup should not be called yet */ - const skipFiring = (this.getInputDOMRef().value === this.value) && isIE() && !this._keyDown && !!this.placeholder; + const skipFiring = (this.inputDomRef.value === this.value) && isIE() && !this._keyDown && !!this.placeholder; !skipFiring && this.fireEventByAction(this.ACTION_USER_INPUT); @@ -589,19 +601,18 @@ class Input extends UI5Element { } _handleResize() { - if (this.hasValueStateMessage) { - this._inputWidth = this.offsetWidth; - } + this._inputWidth = this.offsetWidth; } _closeRespPopover() { this.Suggestions.close(); } - _afterOpenPopover() { + async _afterOpenPopover() { // Set initial focus to the native input if (isPhone()) { - this.getInputDOMRef().focus(); + await this.getInputDOMRef(); + this.inputDomRef.focus(); } } @@ -692,7 +703,9 @@ class Input extends UI5Element { this.value = item.group ? "" : item.textContent; } - fireEventByAction(action) { + async fireEventByAction(action) { + await this.getInputDOMRef(); + if (this.disabled || this.readonly) { return; } @@ -724,15 +737,16 @@ class Input extends UI5Element { getInputValue() { const inputDOM = this.getDomRef(); if (inputDOM) { - return this.getInputDOMRef().value; + return this.inputDomRef.value; } return ""; } - getInputDOMRef() { + async getInputDOMRef() { let inputDomRef; - if (isPhone()) { + if (isPhone() && this.Suggestions) { + await this.Suggestions._respPopover(); inputDomRef = this.Suggestions && this.Suggestions.responsivePopover.querySelector(".ui5-input-inner-phone"); } @@ -740,7 +754,8 @@ class Input extends UI5Element { inputDomRef = this.getDomRef().querySelector(`#${this.getInputId()}`); } - return inputDomRef; + this.inputDomRef = inputDomRef; + return this.inputDomRef; } getLabelableElementId() { @@ -842,7 +857,17 @@ class Input extends UI5Element { } get valueStateMessageText() { - const valueStateMessage = this.valueStateMessage.map(x => x.cloneNode(true)); + const valueStateMessage = []; + + this.valueStateMessage.forEach(el => { + if (el.localName === "slot") { + el.assignedNodes({ flatten: true }).forEach(assignedNode => { + valueStateMessage.push(assignedNode.cloneNode(true)); + }); + } else { + valueStateMessage.push(el.cloneNode(true)); + } + }); return valueStateMessage; } @@ -860,7 +885,9 @@ class Input extends UI5Element { } get hasValueStateMessage() { - return this.hasValueState && this.valueState !== ValueState.Success; + return this.hasValueState && this.valueState !== ValueState.Success + && (!this._inputIconFocused // Handles the cases when valueStateMessage is forwarded (from datepicker e.g.) + || (this._isPhone && this.Suggestions)); // Handles Input with suggestions on mobile } get valueStateText() { diff --git a/packages/main/src/InputPopover.hbs b/packages/main/src/InputPopover.hbs index 58a86afa33bc..9648dca7016e 100644 --- a/packages/main/src/InputPopover.hbs +++ b/packages/main/src/InputPopover.hbs @@ -34,7 +34,7 @@ {{#if hasValueStateMessage}}
{{> valueStateMessage}} -
+ {{/if}} {{/if}} @@ -43,7 +43,7 @@ {{#if hasValueStateMessage}}
{{> valueStateMessage}} -
+ {{/if}} {{/unless}} @@ -51,7 +51,7 @@ {{#each suggestionsTexts}} {{#if group}} - {{ this.text }} + {{ this.text }} {{else}} {{ this.text }} {{/if}} {{/each}} @@ -96,4 +96,4 @@ {{this}} {{/each}} {{/if}} -{{/inline}} \ No newline at end of file +{{/inline}} diff --git a/packages/main/test/pageobjects/DatePickerTestPage.js b/packages/main/test/pageobjects/DatePickerTestPage.js index 742bb883545e..181dc47ada66 100644 --- a/packages/main/test/pageobjects/DatePickerTestPage.js +++ b/packages/main/test/pageobjects/DatePickerTestPage.js @@ -39,6 +39,10 @@ class DatePickerTestPage { return browser.$(this._sut).shadow$("ui5-input").shadow$("input"); } + get inputStaticAreaItem() { + return browser.$(`.${this.input.getProperty("_id")}`); + } + hasIcon() { return browser.execute(function(id) { return !!document.querySelector(id).shadowRoot.querySelector("ui5-icon"); diff --git a/packages/main/test/pages/DatePicker.html b/packages/main/test/pages/DatePicker.html index 24afb84a5ea1..259a9e277214 100644 --- a/packages/main/test/pages/DatePicker.html +++ b/packages/main/test/pages/DatePicker.html @@ -41,6 +41,14 @@ title='Delivery Date!'> + +
Information message. This is a Link. Extra long text used as an information message. Extra long text used as an information message - 2. Extra long text used as an information message - 3.
+
Information message 2. This is a Link. Extra long text used as an information message. Extra long text used as an information message - 2. Extra long text used as an information message - 3.
+
+

placeholder + title + events

Test placeholder +

DatePicker with valueStateMessage

+ +
+ This date is wrong +
+
+