Skip to content

Commit 54f2645

Browse files
StevenRasmussenvnbaaijdvoituron
authored
[AutoComplete] Allow the ability to trigger the search options via code. (#3570)
* Refactor the AutoComplete control to all the ability to trigger the search options manually. * Added a unit test. * Fixed word. * Fixed word. * Added more clarification in the 'summary' of the comment of the method for clarification that the search uses the 'ValueText' when performing the search. Moved the debounce code to run only on the 'InputHandlerAsync' method. The 'InvokeOptionsSearchAsync' runs immediately now. --------- Co-authored-by: Vincent Baaij <vnbaaij@outlook.com> Co-authored-by: Denis Voituron <dvoituron@outlook.com>
1 parent 87c44cf commit 54f2645

File tree

4 files changed

+81
-16
lines changed

4 files changed

+81
-16
lines changed

examples/Demo/Shared/Microsoft.FluentUI.AspNetCore.Components.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6071,6 +6071,13 @@
60716071
<member name="M:Microsoft.FluentUI.AspNetCore.Components.FluentAutocomplete`1.InputHandlerAsync(Microsoft.AspNetCore.Components.ChangeEventArgs)">
60726072
<summary />
60736073
</member>
6074+
<member name="M:Microsoft.FluentUI.AspNetCore.Components.FluentAutocomplete`1.InvokeOptionsSearchAsync">
6075+
<summary>
6076+
Performs the search operation and displays the available values. The search takes into account any previously
6077+
entered text which has updated the <see cref="P:Microsoft.FluentUI.AspNetCore.Components.FluentAutocomplete`1.ValueText"/>.
6078+
</summary>
6079+
<returns></returns>
6080+
</member>
60746081
<member name="M:Microsoft.FluentUI.AspNetCore.Components.FluentAutocomplete`1.KeyDownHandlerAsync(Microsoft.FluentUI.AspNetCore.Components.FluentKeyCodeEventArgs)">
60756082
<summary />
60766083
</member>

src/Core/Components/List/FluentAutocomplete.razor.cs

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -292,27 +292,37 @@ protected override async Task InputHandlerAsync(ChangeEventArgs e)
292292
if (MaximumSelectedOptions > 0 && SelectedOptions?.Count() >= MaximumSelectedOptions)
293293
{
294294
IsReachedMaxItems = true;
295-
RenderComponent();
295+
await RenderComponentAsync();
296296
return;
297297
}
298298

299299
IsReachedMaxItems = false;
300300
IsMultiSelectOpened = true;
301301

302-
var args = new OptionsSearchEventArgs<TOption>()
303-
{
304-
Items = Items ?? Array.Empty<TOption>(),
305-
Text = ValueText,
306-
};
307-
308302
if (ImmediateDelay > 0)
309303
{
310-
await _debounce.RunAsync(ImmediateDelay, () => InvokeAsync(() => OnOptionsSearch.InvokeAsync(args)));
304+
await _debounce.RunAsync(ImmediateDelay, () => InvokeAsync(() => InvokeOptionsSearchAsync()));
311305
}
312306
else
313307
{
314-
await OnOptionsSearch.InvokeAsync(args);
308+
await this.InvokeOptionsSearchAsync();
315309
}
310+
}
311+
312+
/// <summary>
313+
/// Performs the search operation and displays the available values. The search takes into account any previously
314+
/// entered text which has updated the <see cref="ValueText"/>.
315+
/// </summary>
316+
/// <returns></returns>
317+
public async Task InvokeOptionsSearchAsync()
318+
{
319+
var args = new OptionsSearchEventArgs<TOption>()
320+
{
321+
Items = Items ?? Array.Empty<TOption>(),
322+
Text = ValueText,
323+
};
324+
325+
await OnOptionsSearch.InvokeAsync(args);
316326

317327
Items = args.Items?.Take(MaximumOptionsSearch);
318328

@@ -325,14 +335,13 @@ protected override async Task InputHandlerAsync(ChangeEventArgs e)
325335
await VirtualizationContainer.RefreshDataAsync();
326336
}
327337

328-
RenderComponent();
338+
await RenderComponentAsync();
339+
}
329340

330-
// Activate the rendering
331-
void RenderComponent()
332-
{
333-
_shouldRender = true;
334-
StateHasChanged();
335-
}
341+
private async Task RenderComponentAsync()
342+
{
343+
_shouldRender = true;
344+
await InvokeAsync(StateHasChanged);
336345
}
337346

338347
private ValueTask<ItemsProviderResult<TOption>> LoadFilteredItemsAsync(ItemsProviderRequest request)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
<div class=" fluent-autocomplete-multiselect" style="width: 100%;" b-hg72r5b4ox="">
3+
<fluent-text-field style="width: 100%; min-width: 100%;" placeholder="" id="xxx" value="" current-value="" appearance="outline" blazor:onchange="1" role="combobox" aria-expanded="true" aria-controls="myComponent-popup" aria-label="1-Denis Voituron (1 of 1)" blazor:onclick="2" blazor:oninput="3" blazor:elementreference="">
4+
<svg slot="end" style="width: 16px; fill: var(--accent-fill-rest); cursor: pointer;" focusable="true" tabindex="0" role="button" viewBox="0 0 16 16" blazor:onkeydown="4" blazor:onclick="5" blazor:onfocus="6">
5+
<title>Search</title>
6+
<path d="M9.1 10.17a4.5 4.5 0 1 1 1.06-1.06l3.62 3.61a.75.75 0 1 1-1.06 1.06l-3.61-3.61Zm.4-3.67a3 3 0 1 0-6 0 3 3 0 0 0 6 0Z"></path>
7+
</svg>
8+
</fluent-text-field>
9+
<div class="fluent-overlay" style="cursor: auto; position: fixed; display: flex; align-items: center; justify-content: center; z-index: 9900;" blazor:onclick="7" blazor:oncontextmenu="8" blazor:oncontextmenu:preventdefault="" b-xkrr7evqik="">
10+
<div b-xkrr7evqik=""></div>
11+
</div>
12+
<fluent-anchored-region anchor="xxx" horizontal-positioning-mode="dynamic" horizontal-default-position="right" horizontal-inset="" horizontal-threshold="0" horizontal-scaling="content" vertical-positioning-mode="dynamic" vertical-default-position="unset" vertical-threshold="0" vertical-scaling="content" auto-update-mode="auto" style="z-index: 9999;" b-2ov9fhztky="" blazor:elementreference="">
13+
<div style="z-index: 9999; background-color: var(--neutral-layer-floating); box-shadow: var(--elevation-shadow-flyout); margin-top: 10px; border-radius: calc(var(--control-corner-radius) * 2px); background-color: var(--neutral-layer-floating);" b-2ov9fhztky="">
14+
<div id="xxx" role="listbox" style="width: 100%;" tabindex="0" b-hg72r5b4ox="">
15+
<fluent-option id="xxx" value="1-Denis Voituron" blazor:onclick="9" aria-selected="true" selectable="" blazor:elementreference="">1-Denis Voituron</fluent-option>
16+
</div>
17+
</div>
18+
</fluent-anchored-region>
19+
</div>

tests/Core/List/FluentAutocompleteTests.razor

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,36 @@
6363
cut.Verify();
6464
}
6565

66+
[Fact]
67+
public async Task FluentAutocomplete_InvokeSearchOptions()
68+
{
69+
// Arrange
70+
FluentAutocomplete<Customer> fluentAutoComplete = default!;
71+
72+
var externalSearchString = string.Empty;
73+
var customers = Customers.Get();
74+
75+
async Task OnSearchValueChanged(OptionsSearchEventArgs<Customer> e)
76+
{
77+
e.Items = customers;
78+
if (!string.IsNullOrWhiteSpace(externalSearchString))
79+
e.Items = customers.Where(x => x.Name.Contains(externalSearchString));
80+
}
81+
82+
var cut = Render(@<FluentAutocomplete Id="myComponent" @ref=fluentAutoComplete OnOptionsSearch="OnSearchValueChanged" TOption="Customer" />);
83+
84+
// Act
85+
var input = cut.Find("fluent-text-field");
86+
input.Click();
87+
88+
externalSearchString = "Denis";
89+
90+
await fluentAutoComplete.InvokeOptionsSearchAsync();
91+
92+
// Assert
93+
cut.Verify();
94+
}
95+
6696
[Theory()]
6797
[InlineData("Escape")]
6898
[InlineData("Backspace")]

0 commit comments

Comments
 (0)