Skip to content

feat(ui5-textarea): add "valueStateMessage" slot #1419

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Apr 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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