Skip to content

Commit

Permalink
feat(ui5-textarea): add "valueStateMessage" slot (#1419)
Browse files Browse the repository at this point in the history
- add "valueStateMessage" slot
- rename "valueStateMessage" CSS classes from "ui5-input-valuestatemessage-success" to "ui5-valuestatemessage--success", because they should not belong to the input only and ad two dashes to show modification (by the BEM notation)
- there is code duplication at places with the code that handles the valueStateMessage in Input, 
but also differences related to the fact that the TextArea has no suggestions and has this "exceeding" (like Warning) state.

FIXES: #1401
  • Loading branch information
ilhan007 committed Apr 3, 2020
1 parent fb500fc commit d323d51
Show file tree
Hide file tree
Showing 10 changed files with 255 additions and 46 deletions.
27 changes: 15 additions & 12 deletions packages/main/src/Input.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,18 @@ const metadata = {
},

/**
* The slot is used in order to display a valueStateMessage.
* Defines the value state message that will be displayed as pop up under the <code>ui5-input</code>.
* <br><br>
* <b>Note:</b> The valueStateMessage would be displayed only if the <code>ui5-input</code> has
* a valueState of type <code>Information</code>, <code>Warning</code> or <code>Error</code>.
*
* <b>Note:</b> If not specified, a default text (in the respective language) will be displayed.
* <br>
* <b>Note:</b> The <code>valueStateMessage</code> would be displayed,
* when the <code>ui5-input</code> is in <code>Information</code>, <code>Warning</code> or <code>Error</code> value state.
* <br>
* <b>Note:</b> If the <code>ui5-input</code> has <code>suggestionItems</code>,
* the <code>valueStateMessage</code> would be displayed as part of the same popover, if used on desktop, or dialog - on phone.
* @type {HTMLElement[]}
* @since 1.0.0-rc.6
* @slot
* @public
*/
Expand Down Expand Up @@ -808,21 +815,17 @@ class Input extends UI5Element {
get classes() {
return {
popoverValueState: {
"ui5-input-valuestatemessage-root": true,
"ui5-input-valuestatemessage-success": this.valueState === ValueState.Success,
"ui5-input-valuestatemessage-error": this.valueState === ValueState.Error,
"ui5-input-valuestatemessage-warning": this.valueState === ValueState.Warning,
"ui5-input-valuestatemessage-information": this.valueState === ValueState.Information,
"ui5-valuestatemessage-root": true,
"ui5-valuestatemessage--success": this.valueState === ValueState.Success,
"ui5-valuestatemessage--error": this.valueState === ValueState.Error,
"ui5-valuestatemessage--warning": this.valueState === ValueState.Warning,
"ui5-valuestatemessage--information": this.valueState === ValueState.Information,
},
};
}

get styles() {
return {
root: {
"min-height": "1rem",
"box-shadow": "none",
},
popoverHeader: {
"width": `${this._inputWidth}px`,
},
Expand Down
7 changes: 3 additions & 4 deletions packages/main/src/InputPopover.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,10 @@
skip-registry-update
no-padding
no-arrow
class="ui5-input-valuestatemessage-popover"
placement-type="Bottom"
style={{styles.root}}
class="ui5-valuestatemessage-popover"
placement-type="Bottom"
>
<div slot="header" class="ui5-responsive-popover-header {{classes.popoverValueState}}" style={{styles.popoverHeader}}>
<div slot="header" class="ui5-responsive-popover-header {{classes.popoverValueState}}" style="{{styles.popoverHeader}}">
{{> valueStateMessage}}
</div>
</ui5-popover>
Expand Down
3 changes: 2 additions & 1 deletion packages/main/src/Popover.js
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,8 @@ class Popover extends UI5Element {
}

isOpenerClicked(event) {
return event.target === this._opener;
const target = event.target;
return target === this._opener || (target.getFocusDomRef && target.getFocusDomRef() === this._opener);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/main/src/TextArea.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<textarea
id="{{_id}}-inner"
class="ui5-textarea-inner"
placeholder="{{ placeholder }}"
placeholder="{{placeholder}}"
?disabled="{{disabled}}"
?readonly="{{readonly}}"
?required="{{required}}"
Expand Down
153 changes: 152 additions & 1 deletion packages/main/src/TextArea.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,33 @@
import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
import ResizeHandler from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js";
import Integer from "@ui5/webcomponents-base/dist/types/Integer.js";
import { fetchI18nBundle, getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js";
import { getFeature } from "@ui5/webcomponents-base/dist/FeaturesRegistry.js";
import { isIE } from "@ui5/webcomponents-base/dist/Device.js";
import ValueState from "@ui5/webcomponents-base/dist/types/ValueState.js";

import TextAreaTemplate from "./generated/templates/TextAreaTemplate.lit.js";
import { TEXTAREA_CHARACTERS_LEFT, TEXTAREA_CHARACTERS_EXCEEDED } from "./generated/i18n/i18n-defaults.js";
import TextAreaPopoverTemplate from "./generated/templates/TextAreaPopoverTemplate.lit.js";

import {
VALUE_STATE_INFORMATION,
VALUE_STATE_ERROR,
VALUE_STATE_WARNING,
TEXTAREA_CHARACTERS_LEFT,
TEXTAREA_CHARACTERS_EXCEEDED,
} from "./generated/i18n/i18n-defaults.js";

// Styles
import styles from "./generated/themes/TextArea.css.js";
import valueStateMessageStyles from "./generated/themes/ValueStateMessage.css.js";

/**
* @public
*/
const metadata = {
tag: "ui5-textarea",
managedSlots: true,
properties: /** @lends sap.ui.webcomponents.main.TextArea.prototype */ {
/**
* Defines the value of the Web Component.
Expand Down Expand Up @@ -210,15 +221,49 @@ const metadata = {
type: Boolean,
},

/**
* @private
*/
_mirrorText: {
type: Object,
multiple: true,
defaultValue: "",
},

/**
* @private
*/
_maxHeight: {
type: String,
noAttribute: true,
},

/**
* @private
*/
_width: {
type: Integer,
},
},
slots: /** @lends sap.ui.webcomponents.main.TextArea.prototype */ {

/**
* Defines the value state message that will be displayed as pop up under the <code>ui5-textarea</code>.
*
* <br><br>
* <b>Note:</b> If not specified, a default text (in the respective language) will be displayed.
*
* <br><br>
* <b>Note:</b> The <code>valueStateMessage</code> would be displayed if the <code>ui5-textarea</code> has
* <code>valueState</code> of type <code>Information</code>, <code>Warning</code> or <code>Error</code>.
* @type {HTMLElement[]}
* @since 1.0.0-rc.7
* @slot
* @public
*/
valueStateMessage: {
type: HTMLElement,
},
},
events: /** @lends sap.ui.webcomponents.main.TextArea.prototype */ {
/**
Expand Down Expand Up @@ -282,12 +327,31 @@ class TextArea extends UI5Element {
return TextAreaTemplate;
}

static get staticAreaTemplate() {
return TextAreaPopoverTemplate;
}

static get staticAreaStyles() {
return valueStateMessageStyles;
}

constructor() {
super();

this._firstRendering = true;
this._openValueStateMsgPopover = false;
this._fnOnResize = this._onResize.bind(this);
this.i18nBundle = getI18nBundle("@ui5/webcomponents");
}

onEnterDOM() {
ResizeHandler.register(this, this._fnOnResize);
}

onExitDOM() {
ResizeHandler.deregister(this, this._fnOnResize);
}

onBeforeRendering() {
this._exceededTextProps = this._calcExceededText();
this._mirrorText = this._tokenizeText(this.value);
Expand All @@ -307,6 +371,11 @@ class TextArea extends UI5Element {
}
}

onAfterRendering() {
this.toggleValueStateMessage(this.openValueStateMsgPopover);
this._firstRendering = false;
}

getInputDomRef() {
return this.getDomRef().querySelector("textarea");
}
Expand All @@ -321,10 +390,12 @@ class TextArea extends UI5Element {

_onfocusin() {
this.focused = true;
this._openValueStateMsgPopover = true;
}

_onfocusout() {
this.focused = false;
this._openValueStateMsgPopover = false;
}

_onchange() {
Expand Down Expand Up @@ -355,6 +426,35 @@ class TextArea extends UI5Element {
this.fireEvent("value-changed");
}

_onResize() {
if (this.displayValueStateMessage) {
this._width = this.offsetWidth;
}
}

toggleValueStateMessage(toggle) {
if (toggle) {
this.openPopover();
} else {
this.closePopover();
}
}

async openPopover() {
this.popover = await this._getPopover();
this.popover && this.popover.openBy(this.shadowRoot.querySelector(".ui5-textarea-inner"));
}

async closePopover() {
this.popover = await this._getPopover();
this.popover && this.popover.close();
}

async _getPopover() {
const staticAreaItem = await this.getStaticAreaItemDomRef();
return staticAreaItem.querySelector("ui5-popover");
}

_tokenizeText(value) {
const tokenizedText = value.replace(/&/gm, "&amp;").replace(/"/gm, "&quot;").replace(/"/gm, "&#39;").replace(/</gm, "&lt;")
.replace(/>/gm, "&gt;")
Expand Down Expand Up @@ -402,6 +502,16 @@ class TextArea extends UI5Element {
};
}

get classes() {
return {
valueStateMsg: {
"ui5-valuestatemessage--error": this.valueState === ValueState.Error,
"ui5-valuestatemessage--warning": this.valueState === ValueState.Warning || this.exceeding,
"ui5-valuestatemessage--information": this.valueState === ValueState.Information,
},
};
}

get styles() {
const lineHeight = 1.4 * 16;

Expand All @@ -417,6 +527,9 @@ class TextArea extends UI5Element {
"height": (this.showExceededText ? "calc(100% - 26px)" : "100%"),
"max-height": (this._maxHeight),
},
valueStateMsgPopover: {
"max-width": `${this._width}px`,
},
};
}

Expand All @@ -432,6 +545,44 @@ class TextArea extends UI5Element {
return this.valueState === "Error" ? "true" : undefined;
}

get openValueStateMsgPopover() {
return !this._firstRendering && this._openValueStateMsgPopover && this.displayValueStateMessage;
}

get displayValueStateMessage() {
return !!this.valueStateMessage.length || this.exceeding || (this.valueState !== ValueState.Success && this.valueState !== ValueState.None);
}

get displayDefaultValueStateMessage() {
if (this.valueStateMessage.length) {
return false;
}

return this.exceeding || (this.valueState !== ValueState.Success && this.valueState !== ValueState.None);
}

get valueStateMessageText() {
return this.valueStateMessage.map(x => x.cloneNode(true));
}

get valueStateText() {
if (this.valueState !== ValueState.Error && this.exceeding) {
return this.valueStateTextMappings()[ValueState.Warning];
}

return this.valueStateTextMappings()[this.valueState];
}

valueStateTextMappings() {
const i18nBundle = this.i18nBundle;

return {
"Information": i18nBundle.getText(VALUE_STATE_INFORMATION),
"Error": i18nBundle.getText(VALUE_STATE_ERROR),
"Warning": i18nBundle.getText(VALUE_STATE_WARNING),
};
}

static async onDefine() {
await fetchI18nBundle("@ui5/webcomponents");
}
Expand Down
26 changes: 26 additions & 0 deletions packages/main/src/TextAreaPopover.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{{#if displayValueStateMessage}}
<ui5-popover
skip-registry-update
no-padding
no-arrow
_disable-initial-focus
class="ui5-valuestatemessage-popover"
style="{{styles.valueStateMsgPopover}}"
placement-type="Bottom"
horizontal-align="Left"
>
<div slot="header" class="ui5-valuestatemessage-root {{classes.valueStateMsg}}">
{{> valueStateMessage}}
</div>
</ui5-popover>
{{/if}}

{{#*inline "valueStateMessage"}}
{{#if displayDefaultValueStateMessage}}
{{valueStateText}}
{{else}}
{{#each valueStateMessageText}}
{{this}}
{{/each}}
{{/if}}
{{/inline}}
16 changes: 11 additions & 5 deletions packages/main/src/themes/ValueStateMessage.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
.ui5-input-valuestatemessage-root {
.ui5-valuestatemessage-popover {
min-height: 1rem;
box-shadow: none;
}

.ui5-valuestatemessage-root {
box-sizing: border-box;
display: inline-block;
color: var(--sapUiBaseColor);
Expand All @@ -7,20 +12,21 @@
padding: .3rem .625rem;
overflow: hidden;
text-overflow: ellipsis;
min-width: 6.25rem;
}

.ui5-input-valuestatemessage-success {
.ui5-valuestatemessage--success {
background: var(--sapSuccessBackground);
}

.ui5-input-valuestatemessage-warning {
.ui5-valuestatemessage--warning {
background: var(--sapWarningBackground);
}

.ui5-input-valuestatemessage-error {
.ui5-valuestatemessage--error {
background: var(--sapErrorBackground);
}

.ui5-input-valuestatemessage-information {
.ui5-valuestatemessage--information {
background: var(--sapInformationBackground);
}
Loading

0 comments on commit d323d51

Please sign in to comment.