Skip to content

Commit 023e236

Browse files
authored
feat(ui5-file-uploader): implement custom valueStateMessage (#2131)
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
1 parent 7007f8e commit 023e236

File tree

4 files changed

+181
-1
lines changed

4 files changed

+181
-1
lines changed

packages/main/src/FileUploader.hbs

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
<div class="ui5-file-uploader-root"
33
@mouseover="{{_onmouseover}}"
44
@mouseout="{{_onmouseout}}"
5+
@focusin="{{_onfocusin}}"
6+
@focusout="{{_onfocusout}}"
57
>
68
<div class="ui5-file-uploader-mask">
79
{{#unless hideInput}}
@@ -11,6 +13,7 @@
1113
placeholder="{{placeholder}}"
1214
?disabled="{{disabled}}"
1315
tabindex="-1"
16+
class="ui5-file-uploader-input"
1417
></ui5-input>
1518
{{/unless}}
1619
<slot></slot>

packages/main/src/FileUploader.js

+140-1
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,23 @@ import {
99
import {
1010
FILEUPLOAD_BROWSE,
1111
FILEUPLOADER_TITLE,
12+
VALUE_STATE_SUCCESS,
13+
VALUE_STATE_INFORMATION,
14+
VALUE_STATE_ERROR,
15+
VALUE_STATE_WARNING,
1216
} from "./generated/i18n/i18n-defaults.js";
17+
1318
import Input from "./Input.js";
19+
import Popover from "./Popover.js";
1420

1521
// Template
1622
import FileUploaderTemplate from "./generated/templates/FileUploaderTemplate.lit.js";
23+
import FileUploaderPopoverTemplate from "./generated/templates/FileUploaderPopoverTemplate.lit.js";
1724

1825
// Styles
1926
import FileUploaderCss from "./generated/themes/FileUploader.css.js";
27+
import ResponsivePopoverCommonCss from "./generated/themes/ResponsivePopoverCommon.css.js";
28+
import ValueStateMessageCss from "./generated/themes/ValueStateMessage.css.js";
2029

2130
/**
2231
* @public
@@ -128,6 +137,13 @@ const metadata = {
128137
type: ValueState,
129138
defaultValue: ValueState.None,
130139
},
140+
141+
/**
142+
* @private
143+
*/
144+
focused: {
145+
type: Boolean,
146+
},
131147
},
132148
managedSlots: true,
133149
slots: /** @lends sap.ui.webcomponents.main.FileUploader.prototype */ {
@@ -142,6 +158,23 @@ const metadata = {
142158
propertyName: "content",
143159
type: HTMLElement,
144160
},
161+
162+
/**
163+
* Defines the value state message that will be displayed as pop up under the <code>ui5-file-uploader</code>.
164+
* <br><br>
165+
*
166+
* <b>Note:</b> If not specified, a default text (in the respective language) will be displayed.
167+
* <br>
168+
* <b>Note:</b> The <code>valueStateMessage</code> would be displayed,
169+
* when the <code>ui5--file-uploader</code> is in <code>Information</code>, <code>Warning</code> or <code>Error</code> value state.
170+
* @type {HTMLElement[]}
171+
* @since 1.0.0-rc.9
172+
* @slot
173+
* @public
174+
*/
175+
valueStateMessage: {
176+
type: HTMLElement,
177+
},
145178
},
146179
events: /** @lends sap.ui.webcomponents.main.FileUploader.prototype */ {
147180
/**
@@ -209,6 +242,14 @@ class FileUploader extends UI5Element {
209242
return FileUploaderTemplate;
210243
}
211244

245+
static get staticAreaTemplate() {
246+
return FileUploaderPopoverTemplate;
247+
}
248+
249+
static get staticAreaStyles() {
250+
return [ResponsivePopoverCommonCss, ValueStateMessageCss];
251+
}
252+
212253
constructor() {
213254
super();
214255
if (this._canUseNativeFormSupport) {
@@ -230,6 +271,14 @@ class FileUploader extends UI5Element {
230271
});
231272
}
232273

274+
_onfocusin() {
275+
this.focused = true;
276+
}
277+
278+
_onfocusout() {
279+
this.focused = false;
280+
}
281+
233282
/**
234283
* FileList of all selected files.
235284
* @readonly
@@ -252,6 +301,8 @@ class FileUploader extends UI5Element {
252301
if (!this.value) {
253302
this._input.value = "";
254303
}
304+
305+
this.toggleValueStatePopover(this.shouldOpenValueStateMessagePopover);
255306
}
256307

257308
_enableFormSupport() {
@@ -297,6 +348,35 @@ class FileUploader extends UI5Element {
297348
this._internals.setFormValue(formData);
298349
}
299350

351+
toggleValueStatePopover(open) {
352+
if (open) {
353+
this.openValueStatePopover();
354+
} else {
355+
this.closeValueStatePopover();
356+
}
357+
}
358+
359+
async openValueStatePopover() {
360+
const popover = await this._getPopover();
361+
362+
if (popover) {
363+
popover.openBy(this);
364+
}
365+
}
366+
367+
async closeValueStatePopover() {
368+
const popover = await this._getPopover();
369+
370+
if (popover) {
371+
popover.close();
372+
}
373+
}
374+
375+
async _getPopover() {
376+
const staticAreaItem = await this.getStaticAreaItemDomRef();
377+
return staticAreaItem.querySelector(".ui5-valuestatemessage-popover");
378+
}
379+
300380
/**
301381
* in case when ui5-file-uploader is not placed in the DOM, return empty FileList, like native input would do
302382
* @private
@@ -338,8 +418,67 @@ class FileUploader extends UI5Element {
338418
return "file";
339419
}
340420

421+
get valueStateTextMappings() {
422+
const i18nBundle = this.i18nBundle;
423+
424+
return {
425+
"Success": i18nBundle.getText(VALUE_STATE_SUCCESS),
426+
"Information": i18nBundle.getText(VALUE_STATE_INFORMATION),
427+
"Error": i18nBundle.getText(VALUE_STATE_ERROR),
428+
"Warning": i18nBundle.getText(VALUE_STATE_WARNING),
429+
};
430+
}
431+
432+
get valueStateText() {
433+
return this.valueStateTextMappings[this.valueState];
434+
}
435+
436+
get hasValueState() {
437+
return this.valueState !== ValueState.None;
438+
}
439+
440+
get hasValueStateText() {
441+
return this.hasValueState && this.valueState !== ValueState.Success;
442+
}
443+
444+
get valueStateMessageText() {
445+
return this.getSlottedNodes("valueStateMessage").map(el => el.cloneNode(true));
446+
}
447+
448+
get shouldDisplayDefaultValueStateMessage() {
449+
return !this.valueStateMessage.length && this.hasValueStateText;
450+
}
451+
452+
get shouldOpenValueStateMessagePopover() {
453+
return this.focused && this.hasValueStateText && !this.hideInput;
454+
}
455+
456+
get classes() {
457+
return {
458+
popoverValueState: {
459+
"ui5-valuestatemessage-root": true,
460+
"ui5-valuestatemessage--success": this.valueState === ValueState.Success,
461+
"ui5-valuestatemessage--error": this.valueState === ValueState.Error,
462+
"ui5-valuestatemessage--warning": this.valueState === ValueState.Warning,
463+
"ui5-valuestatemessage--information": this.valueState === ValueState.Information,
464+
},
465+
};
466+
}
467+
468+
get styles() {
469+
return {
470+
popoverHeader: {
471+
"width": `${this.ui5Input ? this.ui5Input.offsetWidth : 0}px`,
472+
},
473+
};
474+
}
475+
476+
get ui5Input() {
477+
return this.shadowRoot.querySelector(".ui5-file-uploader-input");
478+
}
479+
341480
static get dependencies() {
342-
return [Input];
481+
return [Input, Popover];
343482
}
344483

345484
static async onDefine() {
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<ui5-popover
2+
skip-registry-update
3+
_disable-initial-focus
4+
prevent-focus-restore
5+
no-padding
6+
no-arrow
7+
class="ui5-valuestatemessage-popover"
8+
placement-type="Bottom"
9+
horizontal-align="Left"
10+
>
11+
<div slot="header" class="{{classes.popoverValueState}}" style="{{styles.popoverHeader}}">
12+
{{> valueStateMessage}}
13+
</div>
14+
</ui5-popover>
15+
16+
{{#*inline "valueStateMessage"}}
17+
{{#if shouldDisplayDefaultValueStateMessage}}
18+
{{valueStateText}}
19+
{{else}}
20+
{{#each valueStateMessageText}}
21+
{{this}}
22+
{{/each}}
23+
{{/if}}
24+
{{/inline}}

packages/main/test/pages/FileUploader.html

+14
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,20 @@
2525
<ui5-button>Upload</ui5-button>
2626
</ui5-file-uploader>
2727
</div>
28+
29+
<div>
30+
<label>FileUploader with ValueState</label>
31+
<ui5-file-uploader placeholder="upload file" value-state="Warning">
32+
<ui5-button>Upload</ui5-button>
33+
</ui5-file-uploader>
34+
35+
<ui5-file-uploader placeholder="upload file" value-state="Error">
36+
<ui5-button>Upload</ui5-button>
37+
<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>
38+
<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>
39+
</ui5-file-uploader>
40+
</div>
41+
2842
<div>
2943
<label>Multiple files upload:</label>
3044
<ui5-file-uploader multiple>

0 commit comments

Comments
 (0)