Skip to content

feat(ui5-multi-combobox): Implement valueStateMessage #2258

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 2 commits into from
Sep 30, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 2 additions & 1 deletion packages/main/src/MultiComboBox.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@
input-icon
slot="icon"
tabindex="-1"
@click={{togglePopover}}
@click="{{togglePopover}}"
@mousedown="{{_onIconMousedown}}"
?pressed="{{open}}"
dir="{{effectiveDir}}"
accessible-name="{{_iconAccessibleNameText}}"
Expand Down
150 changes: 143 additions & 7 deletions packages/main/src/MultiComboBox.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
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 ValueState from "@ui5/webcomponents-base/dist/types/ValueState.js";
import {
isShow,
Expand All @@ -9,6 +10,7 @@ import {
isLeft,
isRight,
} from "@ui5/webcomponents-base/dist/Keys.js";
import Integer from "@ui5/webcomponents-base/dist/types/Integer.js";
import "@ui5/webcomponents-icons/dist/icons/slim-arrow-down.js";
import { isIE, isPhone } from "@ui5/webcomponents-base/dist/Device.js";
import { fetchI18nBundle, getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js";
Expand Down Expand Up @@ -41,6 +43,7 @@ import MultiComboBoxPopoverTemplate from "./generated/templates/MultiComboBoxPop
// Styles
import styles from "./generated/themes/MultiComboBox.css.js";
import ResponsivePopoverCommonCss from "./generated/themes/ResponsivePopoverCommon.css.js";
import ValueStateMessageCss from "./generated/themes/ValueStateMessage.css.js";

/**
* @public
Expand Down Expand Up @@ -82,6 +85,22 @@ const metadata = {
type: HTMLElement,
},

/**
* Defines the value state message that will be displayed as pop up under the <code>ui5-multicombobox</code>.
* <br><br>
*
* <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-select</code> is in <code>Information</code>, <code>Warning</code> or <code>Error</code> value state.
* @type {HTMLElement[]}
* @since 1.0.0-rc.9
* @slot
* @public
*/
valueStateMessage: {
type: HTMLElement,
},
},
properties: /** @lends sap.ui.webcomponents.main.MultiComboBox.prototype */ {
/**
Expand Down Expand Up @@ -205,6 +224,22 @@ const metadata = {
_rootFocused: {
type: Boolean,
},

_iconPressed: {
type: Boolean,
noAttribute: true,
},

_inputWidth: {
type: Integer,
noAttribute: true,
},

_listWidth: {
type: Integer,
defaultValue: 0,
noAttribute: true,
},
},
events: /** @lends sap.ui.webcomponents.main.MultiComboBox.prototype */ {
/**
Expand Down Expand Up @@ -319,7 +354,7 @@ class MultiComboBox extends UI5Element {
}

static get staticAreaStyles() {
return ResponsivePopoverCommonCss;
return [ResponsivePopoverCommonCss, ValueStateMessageCss];
}

static get dependencies() {
Expand All @@ -344,6 +379,19 @@ class MultiComboBox extends UI5Element {
this._deleting = false;
this._validationTimeout = null;
this.i18nBundle = getI18nBundle("@ui5/webcomponents");
this._handleResizeBound = this._handleResize.bind(this);
}

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

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

_handleResize() {
this._inputWidth = this.offsetWidth;
}

_inputChange() {
Expand Down Expand Up @@ -605,6 +653,7 @@ class MultiComboBox extends UI5Element {
this.blur();
}

this._iconPressed = false;
this.filterSelected = false;
}

Expand All @@ -622,14 +671,51 @@ class MultiComboBox extends UI5Element {

async onAfterRendering() {
await this._getRespPopover();
await this._getList();

this.toggle(this.shouldDisplayOnlyValueStateMessage);
this.storeResponsivePopoverWidth();
}

get valueStateTextMappings() {
return {
"Success": this.i18nBundle.getText(VALUE_STATE_SUCCESS),
"Error": this.i18nBundle.getText(VALUE_STATE_ERROR),
"Warning": this.i18nBundle.getText(VALUE_STATE_WARNING),
};
get _isPhone() {
return isPhone();
}

_onIconMousedown() {
this._iconPressed = true;
}

storeResponsivePopoverWidth() {
if (this.open && !this._listWidth) {
this._listWidth = this.list.offsetWidth;
}
}

toggle(isToggled) {
if (isToggled && !this.open) {
this.openPopover();
} else {
this.closePopover();
}
}

async openPopover() {
const popover = await this._getPopover();

if (popover) {
popover.openBy(this);
}
}

async closePopover() {
const popover = await this._getPopover();

popover && popover.close();
}

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

get _tokenizer() {
Expand Down Expand Up @@ -674,6 +760,10 @@ class MultiComboBox extends UI5Element {
return this.valueState !== ValueState.None;
}

get hasValueStateMessage() {
return this.hasValueState && this.valueState !== ValueState.Success;
}

get valueStateText() {
return this.valueStateTextMappings[this.valueState];
}
Expand All @@ -682,6 +772,26 @@ class MultiComboBox extends UI5Element {
return this.hasValueState ? `${this._id}-valueStateDesc` : undefined;
}

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

get shouldDisplayDefaultValueStateMessage() {
return !this.valueStateMessage.length && this.hasValueStateMessage;
}

get shouldDisplayOnlyValueStateMessage() {
return this._rootFocused && this.hasValueStateMessage && !this._iconPressed;
}

get valueStateTextMappings() {
return {
"Success": this.i18nBundle.getText(VALUE_STATE_SUCCESS),
"Error": this.i18nBundle.getText(VALUE_STATE_ERROR),
"Warning": this.i18nBundle.getText(VALUE_STATE_WARNING),
};
}

get _innerInput() {
if (isPhone()) {
if (this.allItemsPopover.opened) {
Expand All @@ -704,6 +814,32 @@ class MultiComboBox extends UI5Element {
return this._rootFocused || this.open;
}

get classes() {
return {
popoverValueState: {
"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 {
popoverValueStateMessage: {
"width": `${this._listWidth}px`,
"min-height": "2.5rem",
"padding": "0.5625rem 1rem",
"display": this._listWidth === 0 ? "none" : "inline-block",
},
popoverHeader: {
"width": `${this._inputWidth}px`,
},
};
}

static async onDefine() {
await fetchI18nBundle("@ui5/webcomponents");
}
Expand Down
105 changes: 74 additions & 31 deletions packages/main/src/MultiComboBoxPopover.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,24 @@
class="ui5-multi-combobox-all-items-responsive-popover"
no-arrow
_disable-initial-focus
content-only-on-desktop
@ui5-selection-change={{_listSelectionChange}}
@ui5-after-close={{_toggle}}
@ui5-after-open={{_toggle}}
>
<div slot="header" class="ui5-responsive-popover-header">
<div class="row">
<span>{{_headerTitleText}}</span>
<ui5-button
class="ui5-responsive-popover-close-btn"
icon="decline"
design="Transparent"
@click="{{togglePopover}}"
>
</ui5-button>
</div>
<div class="row">
<div
{{#if _isPhone}}
<div slot="header" class="ui5-responsive-popover-header" style="{{styles.popoverHeader}}">
<div class="row">
<span>{{_headerTitleText}}</span>
<ui5-button
class="ui5-responsive-popover-close-btn"
icon="decline"
design="Transparent"
@click="{{togglePopover}}"
>
</ui5-button>
</div>
<div class="row">
<div
slot="header"
class="input-root-phone"
value-state="{{valueState}}"
Expand All @@ -37,30 +37,73 @@
aria-autocomplete="both"
aria-labelledby="{{_id}}-hiddenText-nMore"
aria-describedby="{{_id}}-valueStateDesc"
/>
/>
</div>
<ui5-togglebutton
slot="header"
class="ui5-multi-combobox-toggle-button"
icon="multiselect-all"
design="Transparent"
?pressed={{_showAllItemsButtonPressed}}
?disabled={{allItemsSelected}}
@click="{{filterSelectedItems}}"
></ui5-togglebutton>
</div>
<ui5-togglebutton
slot="header"
class="ui5-multi-combobox-toggle-button"
icon="multiselect-all"
design="Transparent"
?pressed={{_showAllItemsButtonPressed}}
?disabled={{allItemsSelected}}
@click="{{filterSelectedItems}}"
></ui5-togglebutton>
{{#if hasValueStateMessage}}
<div class="{{classes.popoverValueState}}" style="{{styles.popoverValueStateMessage}}">
{{> valueStateMessage}}
</div>
{{/if}}
</div>
</div>
</div>
{{/if}}

{{#unless _isPhone}}
{{#if hasValueStateMessage}}
<div slot="header" class="ui5-responsive-popover-header {{classes.popoverValueState}}" style={{styles.popoverValueStateMessage}}>
{{> valueStateMessage}}
</div>
{{/if}}
{{/unless}}

<ui5-list separators="None" mode="MultiSelect" class="ui5-multi-combobox-all-items-list">
{{#each _filteredItems}}
<ui5-li type="Active" ?selected={{this.selected}} data-ui5-token-id="{{this._id}}">{{this.text}}</ui5-li>
{{/each}}
</ui5-list>

<div slot="footer" class="ui5-responsive-popover-footer">
<ui5-button
design="Transparent"
@click="{{togglePopover}}"
>OK</ui5-button>
</div>
{{#if _isPhone}}
<div slot="footer" class="ui5-responsive-popover-footer">
<ui5-button
design="Transparent"
@click="{{togglePopover}}"
>OK</ui5-button>
</div>
{{/if}}
</ui5-responsive-popover>

{{#if hasValueStateMessage}}
<ui5-popover
skip-registry-update
_disable-initial-focus
prevent-focus-restore
no-padding
no-arrow
class="ui5-valuestatemessage-popover"
placement-type="Bottom"
>
<div slot="header" class="{{classes.popoverValueState}}" style="{{styles.popoverHeader}}">
{{> valueStateMessage}}
</div>
</ui5-popover>
{{/if}}

{{#*inline "valueStateMessage"}}
{{#if shouldDisplayDefaultValueStateMessage}}
{{valueStateText}}
{{else}}
{{#each valueStateMessageText}}
{{this}}
{{/each}}
{{/if}}
{{/inline}}
14 changes: 14 additions & 0 deletions packages/main/test/pages/MultiComboBox.html
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,20 @@
</ui5-multi-combobox>
</div>

<div class="demo-section">
<span>value state information </span>

<br>
<ui5-multi-combobox allow-custom-values placeholder="Some initial text"
value-state="Information">
<ui5-mcb-item selected text="Cosy"></ui5-mcb-item>
<ui5-mcb-item text="Compact"></ui5-mcb-item>
<ui5-mcb-item selected text="Condensed"></ui5-mcb-item>
<ui5-mcb-item selected text="Longest word in the world"></ui5-mcb-item>
<div slot="valueStateMessage">Information message. This is a <a href="#">Link</a>. 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.</div>
</ui5-multi-combobox>
</div>

<div class="demo-section">
<span>value state warning </span>

Expand Down