;
_initialRendering = true;
+ _loadingDelegate: ComboBoxLazyLoading;
+ _isArrowClicked = false;
_itemFocused = false;
// used only for Safari fix (check onAfterRendering)
_autocomplete = false;
@@ -559,6 +584,26 @@ class ComboBox extends UI5Element implements IFormInputElement {
// when an initial value is set it should be considered as a _lastValue
this._lastValue = this.getAttribute("value") || "";
+ this._loadingDelegate = new ComboBoxLazyLoading({
+ getItemCount: () => this._getItems().filter(item => !item.isGroupItem && item._isVisible).length,
+ isLoading: () => this.loading,
+ isOpen: () => this.open,
+ fireLoadItems: reason => this.fireDecoratorEvent("load-items", { reason, value: this.value }),
+ loadingMessage: () => ComboBox.i18nBundle.getText(COMBOBOX_LOADING),
+ loadedMessage: () => ComboBox.i18nBundle.getText(COMBOBOX_LOADED),
+ loadedItemMessage: () => ComboBox.i18nBundle.getText(COMBOBOX_LOADED_ITEM),
+ loadedItemsMessage: count => ComboBox.i18nBundle.getText(COMBOBOX_LOADED_ITEMS, count),
+ onLoadingEnd: () => {
+ const visibleItems = this._isArrowClicked
+ ? this._getItems().filter(item => !item.isGroupItem && item._isVisible)
+ : this._filterItems(this.value);
+ this._isArrowClicked = false;
+ if (visibleItems.length === 0 && this.value) {
+ this._closeRespPopover();
+ }
+ },
+ });
+ this._loadingDelegate.init(this.loading);
}
onBeforeRendering() {
@@ -609,6 +654,7 @@ class ComboBox extends UI5Element implements IFormInputElement {
});
this._selectMatchingItem();
+ this._loadingDelegate.onBeforeRendering(this.loading);
this._initialRendering = false;
this.style.setProperty("--_ui5-input-icons-count", `${this.iconsCount}`);
@@ -629,6 +675,8 @@ class ComboBox extends UI5Element implements IFormInputElement {
this.storeResponsivePopoverWidth();
+ this._loadingDelegate.announceLoadingState();
+
if (!arraysAreEqual(this._valueStateLinks, this.linksInAriaValueStateHiddenText)) {
this._removeLinksEventListeners();
this._addLinksEventListeners();
@@ -788,6 +836,11 @@ class ComboBox extends UI5Element implements IFormInputElement {
this._lastSelectedValue = this.selectedValue;
}
+ if (!this.open && !this.loading && this._getItems().length === 0) {
+ this._loadingDelegate.fireOnDropdownOpen();
+ this._isArrowClicked = true;
+ }
+
this._toggleRespPopover();
}
@@ -829,12 +882,14 @@ class ComboBox extends UI5Element implements IFormInputElement {
}
this.fireDecoratorEvent("input");
+ this._isArrowClicked = false;
+ this._loadingDelegate.fireOnInput();
if (isPhone()) {
return;
}
- if (!this._filteredItems.length || value === "") {
+ if (!this.loading && (!this._filteredItems.length || value === "")) {
this._closeRespPopover();
} else {
this._openRespPopover();
@@ -978,6 +1033,10 @@ class ComboBox extends UI5Element implements IFormInputElement {
}
_handleArrowDown(e: KeyboardEvent, indexOfItem: number) {
+ if (this.loading) {
+ return;
+ }
+
this._selectionTrigger = "Keyboard";
const isOpen = this.open;
@@ -1000,6 +1059,10 @@ class ComboBox extends UI5Element implements IFormInputElement {
}
_handleArrowUp(e: KeyboardEvent, indexOfItem: number) {
+ if (this.loading) {
+ return;
+ }
+
this._selectionTrigger = "Keyboard";
const isOpen = this.open;
@@ -1064,7 +1127,7 @@ class ComboBox extends UI5Element implements IFormInputElement {
this._autocomplete = !(isBackSpace(e) || isDelete(e));
this._isKeyNavigation = false;
- if (isNavKey && !this.readonly && this._filteredItems.length) {
+ if (isNavKey && !this.readonly) {
this.handleNavKeyPress(e);
}
@@ -1110,6 +1173,7 @@ class ComboBox extends UI5Element implements IFormInputElement {
e.preventDefault();
this._resetFilter();
+ this._loadingDelegate.fireOnDropdownOpen();
this._toggleRespPopover();
const selectedItem = allItems.find(item => {
@@ -1201,6 +1265,9 @@ class ComboBox extends UI5Element implements IFormInputElement {
_click() {
if (isPhone() && !this.readonly) {
+ if (!this.loading && this._getItems().length === 0) {
+ this._loadingDelegate.fireOnDropdownOpen();
+ }
this._openRespPopover();
}
}
@@ -1748,7 +1815,7 @@ class ComboBox extends UI5Element implements IFormInputElement {
const remSizeInPx = parseInt(getComputedStyle(document.documentElement).fontSize);
return {
suggestionPopoverHeader: {
- "display": this._listWidth === 0 ? "none" : "inline-block",
+ "display": (!this._isPhone && this._listWidth === 0) ? "none" : "inline-block",
"width": `${this._listWidth || ""}px`,
"max-width": "inherit",
},
@@ -1786,5 +1853,6 @@ export default ComboBox;
export type {
ComboBoxSelectionChangeEventDetail,
ComboBoxSelectionChangeTrigger,
+ ComboBoxLoadItemsEventDetail,
IComboBoxItem,
};
diff --git a/packages/main/src/ComboBoxPopoverTemplate.tsx b/packages/main/src/ComboBoxPopoverTemplate.tsx
index 8373f4ced8b52..8ba3062e4eea6 100644
--- a/packages/main/src/ComboBoxPopoverTemplate.tsx
+++ b/packages/main/src/ComboBoxPopoverTemplate.tsx
@@ -10,6 +10,7 @@ import BusyIndicator from "./BusyIndicator.js";
import SuggestionItem from "./SuggestionItem.js";
import generateHighlightedMarkupFirstMatch from "@ui5/webcomponents-base/dist/util/generateHighlightedMarkupFirstMatch.js";
import type ComboBox from "./ComboBox.js";
+import { LOADING_DELAY } from "./features/ComboBoxLazyLoading.js";
export default function ComboBoxPopoverTemplate(this: ComboBox) {
return (
@@ -33,74 +34,93 @@ export default function ComboBoxPopoverTemplate(this: ComboBox) {
onKeyDown={this._handlePopoverKeydown}
onFocusOut={this._handlePopoverFocusout}
>
- {this.loading &&
-
- }
-
- {!this.loading && this._isPhone &&
- <>
-
- Toggle Loading State
+ Lazy loading using the 'load-items' event
-
-
-
-
-
- Toggle Loading
+
+ Clear items
-
Lazy Loading - example does not work on IE11
+
Lazy loading using the 'load-items' event (with value state message)
-
+
+ Custom error.
+
+
Clear items
- Lazy Loading with filter='None'
+ Lazy loading using the 'input' event and the entered text for creating items
@@ -613,40 +612,6 @@
ComboBox Composition
-
-
+
+
+ InvisibleMessage announcements while a modal Dialog is open
+
+ Reproduction for issue #13613.
+ Turn on VoiceOver (Cmd+F5 on macOS), then follow the steps below. With this branch's fix, the UI5 Dialog
+ renders its own aria-live region inside the dialog subtree, so announce() is heard while the dialog is open.
+
+
+
+ 1. Announce — no dialog open
+
+ 2. Open modal dialog
+
+
+ 3a. Announce into a span that lives in <body> (outside the dialog) — silenced by VoiceOver
+
+
+
+ 3b. Announce via InvisibleMessage.announce() — routed inside the open dialog (FIXED)
+
+
+
+ Log:
+
+
+
+
+
+
+
+
This dialog is modal (aria-modal="true"), so VoiceOver scopes its accessibility tree to this subtree.
+
Use buttons 3a and 3b below (they stay reachable) to compare the two live regions.
+
+ 3a. Announce into <body> span (silenced)
+ 3b. Announce via API (heard — FIXED)
+
+
+ Close
+
+
+
+