diff --git a/src/BootstrapBlazor/Components/AutoFill/AutoFill.razor.cs b/src/BootstrapBlazor/Components/AutoFill/AutoFill.razor.cs index 1421461697812e5f05ddd36cfb65ccfc65b5bfec..6d7626ca937fdda4518bdc0b86c2e74cc9cb65c1 100644 --- a/src/BootstrapBlazor/Components/AutoFill/AutoFill.razor.cs +++ b/src/BootstrapBlazor/Components/AutoFill/AutoFill.razor.cs @@ -20,7 +20,6 @@ public partial class AutoFill { private bool _isLoading; private bool _isShown; - private string? _lastFilterText; /// /// 获得 组件样式 @@ -88,6 +87,18 @@ public partial class AutoFill [NotNull] public Func? OnGetDisplayText { get; set; } + /// + /// 获得/设置 是否跳过 Enter 按键处理 默认 false + /// + [Parameter] + public bool SkipEnter { get; set; } + + /// + /// 获得/设置 是否跳过 Esc 按键处理 默认 false + /// + [Parameter] + public bool SkipEsc { get; set; } + /// /// 获得/设置 选项改变回调方法 默认 null /// @@ -171,16 +182,6 @@ public partial class AutoFill } } - /// - /// 获得/设置 是否跳过 Enter 按键处理 默认 false - /// - protected bool SkipEnter { get; set; } - - /// - /// 获得/设置 是否跳过 Esc 按键处理 默认 false - /// - protected bool SkipEsc { get; set; } - /// /// OnKeyUp 方法 /// @@ -188,10 +189,9 @@ public partial class AutoFill /// protected virtual async Task OnKeyUp(KeyboardEventArgs args) { - if (!_isLoading && _lastFilterText != InputString) + if (!_isLoading) { _isLoading = true; - _lastFilterText = InputString; if (OnCustomFilter != null) { var items = await OnCustomFilter(InputString); @@ -209,9 +209,8 @@ public partial class AutoFill if (source.Any()) { _isShown = true; - // 键盘向上选择 - if (_isShown && args.Key == "ArrowUp") + if (args.Key == "ArrowUp") { var index = 0; if (ActiveSelectedItem != null) @@ -225,7 +224,7 @@ public partial class AutoFill ActiveSelectedItem = source[index]; CurrentItemIndex = index; } - else if (_isShown && args.Key == "ArrowDown") + else if (args.Key == "ArrowDown") { var index = 0; if (ActiveSelectedItem != null) diff --git a/test/UnitTest/Components/AutoFillTest.cs b/test/UnitTest/Components/AutoFillTest.cs new file mode 100644 index 0000000000000000000000000000000000000000..2d44f73ad5109ee30ab83d5fa735a02fc1787387 --- /dev/null +++ b/test/UnitTest/Components/AutoFillTest.cs @@ -0,0 +1,227 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +using BootstrapBlazor.Shared; +using Microsoft.AspNetCore.Components.Web; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; + +namespace UnitTest.Components; + +public class AutoFillTest : BootstrapBlazorTestBase +{ + private IStringLocalizer Localizer { get; } + + private Foo Model { get; } + + private List Items { get; } + + public AutoFillTest() + { + Localizer = Context.Services.GetRequiredService>(); + Model = Foo.Generate(Localizer); + Items = Foo.GenerateFoo(Localizer, 2); + } + + [Fact] + public void ShowLabel_Ok() + { + var cut = Context.RenderComponent>(pb => + { + pb.Add(a => a.ShowLabel, true); + pb.Add(a => a.Value, Model); + pb.Add(a => a.Items, Items); + pb.Add(a => a.DisplayText, "AutoFillDisplayText"); + }); + Assert.Contains("AutoFillDisplayText", cut.Markup); + } + + [Fact] + public void NullItems_Ok() + { + var cut = Context.RenderComponent>(); + Assert.Contains("dropdown-list", cut.Markup); + } + + [Fact] + public void OnCustomFilter_Ok() + { + var filtered = false; + var cut = Context.RenderComponent>(pb => + { + pb.Add(a => a.Value, Model); + pb.Add(a => a.Items, Items); + pb.Add(a => a.OnCustomFilter, new Func>>(key => + { + filtered = true; + var items = Foo.GenerateFoo(Localizer, 3); + return Task.FromResult(items.AsEnumerable()); + })); + }); + cut.Find(".form-control").KeyUp(new KeyboardEventArgs() { Key = "t" }); + Assert.True(filtered); + } + + [Fact] + public void Escape_Ok() + { + var escTrigger = false; + var cut = Context.RenderComponent>(pb => + { + pb.Add(a => a.Value, Model); + pb.Add(a => a.Items, Items); + pb.Add(a => a.SkipEsc, true); + pb.Add(a => a.OnEscAsync, new Func(foo => + { + escTrigger = true; + return Task.CompletedTask; + })); + }); + cut.Find(".form-control").KeyUp(new KeyboardEventArgs() { Key = "t" }); + cut.Find(".form-control").KeyUp(new KeyboardEventArgs() { Key = "Escape" }); + Assert.False(escTrigger); + + cut.SetParametersAndRender(pb => + { + pb.Add(a => a.SkipEsc, false); + }); + cut.Find(".form-control").KeyUp(new KeyboardEventArgs() { Key = "t" }); + cut.Find(".form-control").KeyUp(new KeyboardEventArgs() { Key = "Escape" }); + Assert.True(escTrigger); + } + + [Fact] + public void Enter_Ok() + { + var enterTrigger = false; + var cut = Context.RenderComponent>(pb => + { + pb.Add(a => a.Value, Model); + pb.Add(a => a.Items, Items); + pb.Add(a => a.SkipEnter, true); + pb.Add(a => a.OnEnterAsync, new Func(foo => + { + enterTrigger = true; + return Task.CompletedTask; + })); + }); + cut.Find(".form-control").KeyUp(new KeyboardEventArgs() { Key = "t" }); + cut.Find(".form-control").KeyUp(new KeyboardEventArgs() { Key = "Enter" }); + Assert.False(enterTrigger); + + Foo? selectedItem = null; + cut.SetParametersAndRender(pb => + { + pb.Add(a => a.SkipEnter, false); + pb.Add(a => a.OnSelectedItemChanged, new Func(foo => + { + selectedItem = foo; + return Task.CompletedTask; + })); + }); + cut.Find(".form-control").KeyUp(new KeyboardEventArgs() { Key = "t" }); + cut.Find(".form-control").KeyUp(new KeyboardEventArgs() { Key = "Enter" }); + Assert.True(enterTrigger); + Assert.NotNull(selectedItem); + } + + [Fact] + public void OnSelectedItemChanged_Ok() + { + Foo? selectedItem = null; + var cut = Context.RenderComponent>(pb => + { + pb.Add(a => a.Value, Model); + pb.Add(a => a.Items, Items); + pb.Add(a => a.OnSelectedItemChanged, new Func(foo => + { + selectedItem = foo; + return Task.CompletedTask; + })); + }); + cut.Find(".form-control").KeyUp(new KeyboardEventArgs() { Key = "t" }); + cut.Find(".dropdown-item").MouseDown(new MouseEventArgs()); + Assert.NotNull(selectedItem); + } + + [Fact] + public void KeyUp_Test() + { + var cut = Context.RenderComponent>(pb => + { + pb.Add(a => a.Value, Model); + pb.Add(a => a.Items, Items); + }); + cut.Find(".form-control").KeyUp(new KeyboardEventArgs() { Key = "t" }); + cut.Find(".form-control").KeyUp(new KeyboardEventArgs() { Key = "ArrowUp" }); + cut.Find(".form-control").KeyUp(new KeyboardEventArgs() { Key = "ArrowUp" }); + cut.Find(".form-control").KeyUp(new KeyboardEventArgs() { Key = "ArrowUp" }); + cut.Find(".form-control").KeyUp(new KeyboardEventArgs() { Key = "ArrowDown" }); + cut.Find(".form-control").KeyUp(new KeyboardEventArgs() { Key = "ArrowDown" }); + cut.Find(".form-control").KeyUp(new KeyboardEventArgs() { Key = "ArrowDown" }); + } + + [Fact] + public void DisplayCount_Ok() + { + var cut = Context.RenderComponent>(pb => + { + pb.Add(a => a.Value, Model); + pb.Add(a => a.Items, Items); + pb.Add(a => a.IsLikeMatch, true); + pb.Add(a => a.IgnoreCase, false); + pb.Add(a => a.DisplayCount, 2); + }); + cut.Find(".form-control").KeyUp(new KeyboardEventArgs() { Key = "Z" }); + Assert.Equal(2, cut.FindAll(".dropdown-item").Count); + } + + [Fact] + public void OnGetDisplayText_Null() + { + var v = new AutoFillNullStringMock(); + var cut = Context.RenderComponent>(pb => + { + pb.Add(a => a.IgnoreCase, true); + pb.Add(a => a.Value, v); + pb.Add(a => a.Items, new List + { + new AutoFillNullStringMock() { Value = "1" }, + new AutoFillNullStringMock() { Value = "2" }, + }); + pb.Add(a => a.Template, v => builder => + { + builder.OpenElement(0, "div"); + builder.AddContent(1, v.Value); + builder.CloseElement(); + }); + }); + cut.Find(".form-control").KeyUp(new KeyboardEventArgs() { Key = "1" }); + cut.Find(".dropdown-item").MouseDown(new MouseEventArgs()); + Assert.Equal("1", cut.Instance.Value!.Value); + + cut.SetParametersAndRender(pb => + { + pb.Add(a => a.Items, new List + { + null, + new AutoFillNullStringMock() { Value = "2" }, + }); + pb.Add(a => a.Template, (RenderFragment?)null); + }); + cut.Find(".form-control").KeyUp(new KeyboardEventArgs() { Key = "2" }); + Assert.Equal(2, cut.FindAll(".dropdown-item").Count); + } + + class AutoFillNullStringMock + { + [NotNull] + public string? Value { get; set; } + + public override string? ToString() + { + return null; + } + } +}