Skip to content

Commit

Permalink
feat(ui5-file-uploader): implement custom valueStateMessage (#2131)
Browse files Browse the repository at this point in the history
Introduce support for custom value state message as slot. Note: although the ui5-input is internally used, we can not just forward the slot (as in the DatePicker for example), because it is not the element that is clicked, focused and so on.

Related to #1086
  • Loading branch information
ilhan007 committed Aug 21, 2020
1 parent 7007f8e commit 023e236
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 1 deletion.
3 changes: 3 additions & 0 deletions packages/main/src/FileUploader.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<div class="ui5-file-uploader-root"
@mouseover="{{_onmouseover}}"
@mouseout="{{_onmouseout}}"
@focusin="{{_onfocusin}}"
@focusout="{{_onfocusout}}"
>
<div class="ui5-file-uploader-mask">
{{#unless hideInput}}
Expand All @@ -11,6 +13,7 @@
placeholder="{{placeholder}}"
?disabled="{{disabled}}"
tabindex="-1"
class="ui5-file-uploader-input"
></ui5-input>
{{/unless}}
<slot></slot>
Expand Down
141 changes: 140 additions & 1 deletion packages/main/src/FileUploader.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,23 @@ import {
import {
FILEUPLOAD_BROWSE,
FILEUPLOADER_TITLE,
VALUE_STATE_SUCCESS,
VALUE_STATE_INFORMATION,
VALUE_STATE_ERROR,
VALUE_STATE_WARNING,
} from "./generated/i18n/i18n-defaults.js";

import Input from "./Input.js";
import Popover from "./Popover.js";

// Template
import FileUploaderTemplate from "./generated/templates/FileUploaderTemplate.lit.js";
import FileUploaderPopoverTemplate from "./generated/templates/FileUploaderPopoverTemplate.lit.js";

// Styles
import FileUploaderCss from "./generated/themes/FileUploader.css.js";
import ResponsivePopoverCommonCss from "./generated/themes/ResponsivePopoverCommon.css.js";
import ValueStateMessageCss from "./generated/themes/ValueStateMessage.css.js";

/**
* @public
Expand Down Expand Up @@ -128,6 +137,13 @@ const metadata = {
type: ValueState,
defaultValue: ValueState.None,
},

/**
* @private
*/
focused: {
type: Boolean,
},
},
managedSlots: true,
slots: /** @lends sap.ui.webcomponents.main.FileUploader.prototype */ {
Expand All @@ -142,6 +158,23 @@ const metadata = {
propertyName: "content",
type: HTMLElement,
},

/**
* Defines the value state message that will be displayed as pop up under the <code>ui5-file-uploader</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--file-uploader</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,
},
},
events: /** @lends sap.ui.webcomponents.main.FileUploader.prototype */ {
/**
Expand Down Expand Up @@ -209,6 +242,14 @@ class FileUploader extends UI5Element {
return FileUploaderTemplate;
}

static get staticAreaTemplate() {
return FileUploaderPopoverTemplate;
}

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

constructor() {
super();
if (this._canUseNativeFormSupport) {
Expand All @@ -230,6 +271,14 @@ class FileUploader extends UI5Element {
});
}

_onfocusin() {
this.focused = true;
}

_onfocusout() {
this.focused = false;
}

/**
* FileList of all selected files.
* @readonly
Expand All @@ -252,6 +301,8 @@ class FileUploader extends UI5Element {
if (!this.value) {
this._input.value = "";
}

this.toggleValueStatePopover(this.shouldOpenValueStateMessagePopover);
}

_enableFormSupport() {
Expand Down Expand Up @@ -297,6 +348,35 @@ class FileUploader extends UI5Element {
this._internals.setFormValue(formData);
}

toggleValueStatePopover(open) {
if (open) {
this.openValueStatePopover();
} else {
this.closeValueStatePopover();
}
}

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

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

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

if (popover) {
popover.close();
}
}

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

/**
* in case when ui5-file-uploader is not placed in the DOM, return empty FileList, like native input would do
* @private
Expand Down Expand Up @@ -338,8 +418,67 @@ class FileUploader extends UI5Element {
return "file";
}

get valueStateTextMappings() {
const i18nBundle = this.i18nBundle;

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

get valueStateText() {
return this.valueStateTextMappings[this.valueState];
}

get hasValueState() {
return this.valueState !== ValueState.None;
}

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

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

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

get shouldOpenValueStateMessagePopover() {
return this.focused && this.hasValueStateText && !this.hideInput;
}

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 {
popoverHeader: {
"width": `${this.ui5Input ? this.ui5Input.offsetWidth : 0}px`,
},
};
}

get ui5Input() {
return this.shadowRoot.querySelector(".ui5-file-uploader-input");
}

static get dependencies() {
return [Input];
return [Input, Popover];
}

static async onDefine() {
Expand Down
24 changes: 24 additions & 0 deletions packages/main/src/FileUploaderPopover.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<ui5-popover
skip-registry-update
_disable-initial-focus
prevent-focus-restore
no-padding
no-arrow
class="ui5-valuestatemessage-popover"
placement-type="Bottom"
horizontal-align="Left"
>
<div slot="header" class="{{classes.popoverValueState}}" style="{{styles.popoverHeader}}">
{{> valueStateMessage}}
</div>
</ui5-popover>

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

<div>
<label>FileUploader with ValueState</label>
<ui5-file-uploader placeholder="upload file" value-state="Warning">
<ui5-button>Upload</ui5-button>
</ui5-file-uploader>

<ui5-file-uploader placeholder="upload file" value-state="Error">
<ui5-button>Upload</ui5-button>
<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>
<div slot="valueStateMessage">Information message 2. 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-file-uploader>
</div>

<div>
<label>Multiple files upload:</label>
<ui5-file-uploader multiple>
Expand Down

0 comments on commit 023e236

Please sign in to comment.