Skip to content

Commit b544c10

Browse files
feat(ui5-input,ui5-multi-input): add suggestions trigger
1 parent 3582597 commit b544c10

File tree

8 files changed

+139
-7
lines changed

8 files changed

+139
-7
lines changed

packages/main/cypress/specs/Input.mobile.cy.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,4 +390,4 @@ describe("Property open", () => {
390390
.find<ResponsivePopover>("[ui5-responsive-popover]")
391391
.ui5ResponsivePopoverClosed();
392392
});
393-
});
393+
});

packages/main/src/Input.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,7 +1110,7 @@ class Input extends UI5Element implements SuggestionComponent, IFormInputElement
11101110
_onfocusout(e: FocusEvent) {
11111111
const toBeFocused = e.relatedTarget as HTMLElement;
11121112

1113-
if (this.Suggestions?._getPicker().contains(toBeFocused) || this.contains(toBeFocused) || this.getSlottedNodes("valueStateMessage").some(el => el.contains(toBeFocused))) {
1113+
if (this.Suggestions?._getPicker()?.contains(toBeFocused) || this.contains(toBeFocused) || this.getSlottedNodes("valueStateMessage").some(el => el.contains(toBeFocused))) {
11141114
return;
11151115
}
11161116

@@ -1174,7 +1174,7 @@ class Input extends UI5Element implements SuggestionComponent, IFormInputElement
11741174

11751175
if (this.previousValue !== this.getInputDOMRefSync()!.value) {
11761176
// if picker is open there might be a selected item, wait next tick to get the value applied
1177-
if (this.Suggestions?._getPicker().open && this._flattenItems.some(item => item.hasAttribute("ui5-suggestion-item") && (item as SuggestionItem).selected)) {
1177+
if (this.Suggestions?._getPicker()?.open && this._flattenItems.some(item => item.hasAttribute("ui5-suggestion-item") && (item as SuggestionItem).selected)) {
11781178
this._changeToBeFired = true;
11791179
} else {
11801180
fireChange();
@@ -1566,15 +1566,21 @@ class Input extends UI5Element implements SuggestionComponent, IFormInputElement
15661566

15671567
getInputDOMRef() {
15681568
if (isPhone() && this.Suggestions) {
1569-
return this.Suggestions._getPicker()!.querySelector<Input>(".ui5-input-inner-phone")!;
1569+
const picker = this.Suggestions._getPicker();
1570+
if (picker) {
1571+
return picker.querySelector<Input>(".ui5-input-inner-phone")!;
1572+
}
15701573
}
15711574

15721575
return this.nativeInput;
15731576
}
15741577

15751578
getInputDOMRefSync() {
1576-
if (isPhone() && this.Suggestions?._getPicker()) {
1577-
return this.Suggestions._getPicker().querySelector(".ui5-input-inner-phone")!.shadowRoot!.querySelector<HTMLInputElement>("input")!;
1579+
if (isPhone() && this.Suggestions) {
1580+
const picker = this.Suggestions._getPicker();
1581+
if (picker) {
1582+
return picker.querySelector(".ui5-input-inner-phone")!.shadowRoot!.querySelector<HTMLInputElement>("input")!;
1583+
}
15781584
}
15791585

15801586
return this.nativeInput;

packages/main/src/features/InputSuggestionsTemplate.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export default function InputSuggestionsTemplate(this: Input, hooks?: { suggesti
7272
</div>
7373
}
7474

75-
{ suggestionsList.call(this) }
75+
{ this.showSuggestions && suggestionsList.call(this) }
7676

7777
{this._isPhone &&
7878
<div slot="footer" class="ui5-responsive-popover-footer">

packages/main/test/pages/Input.html

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,15 @@ <h3>Input with open suggestions on focusin</h3>
453453
<br>
454454
<br>
455455

456+
<h3>Input with Dynamic Suggestions Triggering (Threshold = 3)</h3>
457+
<ui5-input id="dynamicTriggerInput" placeholder="Type at least 3 characters to see suggestions..." show-suggestions></ui5-input>
458+
<div>
459+
<small>Suggestions will appear after typing 3 or more characters</small>
460+
</div>
461+
462+
<br>
463+
<br>
464+
456465
<h3>Input - showing wrapping</h3>
457466
<ui5-input show-suggestions id="input-change-wrapping">
458467
<ui5-suggestion-item text="Item that does not wrap"></ui5-suggestion-item>
@@ -1113,6 +1122,46 @@ <h3>Input Composition</h3>
11131122
document.getElementById("prevent-input-event-clear-icon").addEventListener("ui5-input", event => {
11141123
event.preventDefault();
11151124
});
1125+
1126+
// Dynamic Suggestions Triggering with Threshold
1127+
const dynamicTriggerInput = document.getElementById("dynamicTriggerInput");
1128+
const THRESHOLD = 3;
1129+
1130+
const countriesForDynamicTrigger = [
1131+
"Albania", "Andorra", "Armenia", "Austria", "Azerbaijan",
1132+
"Belarus", "Belgium", "Bosnia and Herzegovina", "Bulgaria",
1133+
"Croatia", "Cyprus", "Czech Republic", "Denmark",
1134+
"Estonia", "Finland", "France", "Georgia", "Germany", "Greece",
1135+
"Hungary", "Iceland", "Ireland", "Italy", "Kazakhstan",
1136+
"Kosovo", "Latvia", "Liechtenstein", "Lithuania", "Luxembourg",
1137+
"Malta", "Moldova", "Monaco", "Montenegro", "Netherlands",
1138+
"North Macedonia", "Norway", "Poland", "Portugal", "Romania",
1139+
"Russia", "San Marino", "Serbia", "Slovakia", "Slovenia",
1140+
"Spain", "Sweden", "Switzerland", "Turkey", "Ukraine",
1141+
"United Kingdom", "Vatican City"
1142+
];
1143+
1144+
dynamicTriggerInput.addEventListener("input", () => {
1145+
const value = dynamicTriggerInput.value;
1146+
1147+
while (dynamicTriggerInput.lastChild) {
1148+
dynamicTriggerInput.removeChild(dynamicTriggerInput.lastChild);
1149+
}
1150+
1151+
if (value.length >= THRESHOLD) {
1152+
dynamicTriggerInput.showSuggestions = true;
1153+
1154+
const filteredCountries = countriesForDynamicTrigger.filter(country =>
1155+
country.toLowerCase().startsWith(value.toLowerCase())
1156+
);
1157+
1158+
filteredCountries.forEach(country => {
1159+
const suggestion = document.createElement("ui5-suggestion-item");
1160+
suggestion.text = country;
1161+
dynamicTriggerInput.appendChild(suggestion);
1162+
});
1163+
}
1164+
});
11161165
</script>
11171166
</body>
11181167

packages/website/docs/_components_pages/main/Input/Input.mdx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Basic from "../../../_samples/main/Input/Basic/Basic.md";
66
import Suggestions from "../../../_samples/main/Input/Suggestions/Suggestions.md";
77
import ClearIcon from "../../../_samples/main/Input/ClearIcon/ClearIcon.md";
88
import SuggestionsWrapping from "../../../_samples/main/Input/SuggestionsWrapping/SuggestionsWrapping.md";
9+
import DynamicSuggestions from "../../../_samples/main/Input/DynamicSuggestions/DynamicSuggestions.md";
910
import ValueStateMessage from "../../../_samples/main/Input/ValueStateMessage/ValueStateMessage.md";
1011
import Label from "../../../_samples/main/Input/Label/Label.md";
1112
import ValueHelpDialog from "../../../_samples/main/Input/ValueHelpDialog/ValueHelpDialog.md";
@@ -37,6 +38,12 @@ The sample demonstrates how the text of the suggestions wrap when too long.
3738

3839
<SuggestionsWrapping />
3940

41+
### Dynamic Suggestion Control
42+
This sample demonstrates how applications can control when suggestions appear by dynamically toggling the <b>showSuggestions</b> property.
43+
In this example, suggestions are only shown when the user has typed 3 or more characters.
44+
45+
<DynamicSuggestions />
46+
4047
### Input and Label
4148
<Label />
4249

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import html from '!!raw-loader!./sample.html';
2+
import js from '!!raw-loader!./main.js';
3+
4+
<Editor html={html} js={js} />
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import "@ui5/webcomponents/dist/Input.js";
2+
import "@ui5/webcomponents/dist/SuggestionItem.js";
3+
import "@ui5/webcomponents/dist/features/InputSuggestions.js";
4+
5+
const THRESHOLD = 3;
6+
7+
const countries = [
8+
"Argentina", "Albania", "Algeria", "Angola", "Austria", "Australia",
9+
"Bulgaria", "Belgium", "Brazil", "Canada", "Columbia", "Croatia",
10+
"Denmark", "England", "Finland", "France", "Germany", "Greece",
11+
"Hungary", "Ireland", "Italy", "Japan", "Kuwait", "Luxembourg",
12+
"Mexico", "Morocco", "Netherlands", "Norway", "Paraguay", "Philippines",
13+
"Portugal", "Romania", "Spain", "Sweden", "Switzerland", "Sri Lanka",
14+
"Senegal", "Thailand", "The United Kingdom of Great Britain and Northern Ireland",
15+
"USA", "Ukraine", "Vietnam"
16+
];
17+
18+
let suggestionItems = [];
19+
20+
const input = document.getElementById("input-threshold-3");
21+
22+
input.addEventListener("input", () => {
23+
const value = input.value;
24+
25+
// Clear existing suggestions
26+
while (input.lastChild) {
27+
input.removeChild(input.lastChild);
28+
}
29+
30+
if (value.length >= THRESHOLD) {
31+
// Enable suggestions and typeahead when threshold is met
32+
input.showSuggestions = true;
33+
34+
// Filter and add matching suggestions
35+
suggestionItems = countries.filter((item) => {
36+
return item.toUpperCase().indexOf(value.toUpperCase()) === 0;
37+
});
38+
39+
suggestionItems.forEach((item) => {
40+
const li = document.createElement("ui5-suggestion-item");
41+
li.text = item;
42+
input.appendChild(li);
43+
});
44+
}
45+
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<!-- playground-fold -->
2+
<!DOCTYPE html>
3+
<html lang="en">
4+
5+
<head>
6+
<meta charset="UTF-8">
7+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
8+
<title>Sample</title>
9+
</head>
10+
11+
<body style="background-color: var(--sapBackgroundColor); height: 400px;">
12+
<!-- playground-fold-end -->
13+
14+
<ui5-input id="input-threshold-3" placeholder="Start typing (threshold: 3 chars)" show-suggestions></ui5-input>
15+
16+
<!-- playground-fold -->
17+
<script type="module" src="main.js"></script>
18+
</body>
19+
20+
</html>
21+
<!-- playground-fold-end -->

0 commit comments

Comments
 (0)