Skip to content

Commit 4fce242

Browse files
authored
refactor(AutoComplete): update blur logic (#5838)
* refactor: update blur event callback * refactor: 更新脚本 * test: 更新单元测试 * refactor: 增加 Blur 支持 * test: 增加单元测试
1 parent 6e64c22 commit 4fce242

9 files changed

Lines changed: 99 additions & 10 deletions

File tree

src/BootstrapBlazor/Components/AutoComplete/AutoComplete.razor

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
data-bb-auto-dropdown-focus="@ShowDropdownListOnFocusString" data-bb-debounce="@DurationString"
1313
data-bb-skip-esc="@SkipEscString" data-bb-skip-enter="@SkipEnterString" data-bb-blur="@TriggerBlurString"
1414
data-bb-scroll-behavior="@ScrollIntoViewBehaviorString" data-bb-trigger-delete="true"
15-
@bind="@CurrentValueAsString" @onblur="@OnBlur"
15+
@bind="@CurrentValueAsString"
1616
placeholder="@PlaceHolder" disabled="@Disabled" @ref="FocusElement"/>
1717
<span class="form-select-append"><i class="@Icon"></i></span>
1818
<span class="form-select-append ac-loading"><i class="@LoadingIcon"></i></span>

src/BootstrapBlazor/Components/AutoComplete/AutoComplete.razor.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,11 @@ private async Task OnClickItem(string val)
130130
{
131131
await OnSelectedItemChanged(val);
132132
}
133+
134+
if (OnBlurAsync != null)
135+
{
136+
await OnBlurAsync(Value);
137+
}
133138
}
134139

135140
private List<string> Rows => _filterItems ?? [.. Items];

src/BootstrapBlazor/Components/AutoComplete/AutoComplete.razor.js

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export function init(id, invoke) {
99
const el = document.getElementById(id)
1010
const menu = el.querySelector('.dropdown-menu')
1111
const input = document.getElementById(`${id}_input`)
12-
const ac = { el, invoke, menu }
12+
const ac = { el, invoke, menu, input }
1313
Data.set(id, ac)
1414

1515
const isPopover = input.getAttribute('data-bs-toggle') === 'bb.dropdown';
@@ -102,24 +102,36 @@ export function init(id, invoke) {
102102
const d = Data.get(id);
103103
if (d) {
104104
d.close();
105+
d.blur();
105106
}
106107
}
107108
});
108109
}
109-
ac.blur = e => {
110+
111+
ac.keyup = e => {
110112
if (e.key === 'Tab') {
111113
[...document.querySelectorAll('.auto-complete.show')].forEach(a => {
112114
const id = a.getAttribute('id');
113115
const d = Data.get(id);
114116
if (d) {
115117
d.close();
118+
d.blur();
116119
}
117120
});
118121
}
119122
}
123+
124+
ac.blur = function () {
125+
const { input, invoke } = this;
126+
const triggerBlur = input.getAttribute('data-bb-blur') === 'true';
127+
if (triggerBlur) {
128+
invoke.invokeMethodAsync('TriggerBlur');
129+
}
130+
}
131+
120132
registerBootstrapBlazorModule('AutoComplete', id, () => {
121133
EventHandler.on(document, 'click', ac.closePopover);
122-
EventHandler.on(document, 'keyup', ac.blur);
134+
EventHandler.on(document, 'keyup', ac.keyup);
123135
});
124136
}
125137

@@ -183,7 +195,7 @@ export function dispose(id) {
183195
const { AutoComplete } = window.BootstrapBlazor;
184196
AutoComplete.dispose(id, () => {
185197
EventHandler.off(document, 'click', ac.closePopover);
186-
EventHandler.off(document, 'keyup', ac.blur);
198+
EventHandler.off(document, 'keyup', ac.keyup);
187199
});
188200
}
189201

src/BootstrapBlazor/Components/AutoComplete/PopoverCompleteBase.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,4 +163,16 @@ protected override void OnParametersSet()
163163
/// </summary>
164164
/// <returns></returns>
165165
protected override Task InvokeInitAsync() => InvokeVoidAsync("init", Id, Interop);
166+
167+
/// <summary>
168+
/// 触发 OnBlur 回调方法 由 Javascript 触发
169+
/// </summary>
170+
[JSInvokable]
171+
public async Task TriggerBlur()
172+
{
173+
if (OnBlurAsync != null)
174+
{
175+
await OnBlurAsync(Value);
176+
}
177+
}
166178
}

src/BootstrapBlazor/Components/AutoFill/AutoFill.razor.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,11 @@ private async Task OnClickItem(TValue val)
227227
{
228228
await OnSelectedItemChanged(val);
229229
}
230+
231+
if (OnBlurAsync != null)
232+
{
233+
await OnBlurAsync(Value);
234+
}
230235
}
231236

232237
private string? GetDisplayText(TValue item) => OnGetDisplayText?.Invoke(item) ?? item?.ToString();

src/BootstrapBlazor/Components/Search/Search.razor.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,8 @@ public partial class Search<TValue>
169169
[NotNull]
170170
private List<TValue>? _filterItems = null;
171171

172-
private SearchContext<TValue> _context = default!;
172+
[NotNull]
173+
private SearchContext<TValue>? _context = null;
173174

174175
[NotNull]
175176
private RenderTemplate? _dropdown = null;
@@ -267,6 +268,11 @@ private async Task OnClickItem(TValue val)
267268
{
268269
await OnSelectedItemChanged(val);
269270
}
271+
272+
if (OnBlurAsync != null)
273+
{
274+
await OnBlurAsync(Value);
275+
}
270276
}
271277

272278
/// <summary>

test/UnitTest/Components/AutoCompleteTest.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -258,16 +258,18 @@ public async Task OnBlurAsync_Ok()
258258
var cut = Context.RenderComponent<AutoComplete>(pb =>
259259
{
260260
pb.Add(a => a.Items, items);
261-
pb.Add(a => a.Value, "test1");
261+
pb.Add(a => a.Value, "test2");
262262
pb.Add(a => a.OnBlurAsync, v =>
263263
{
264264
val = v;
265265
return Task.CompletedTask;
266266
});
267267
});
268-
var input = cut.Find(".form-control");
269-
await cut.InvokeAsync(() => input.Blur());
270-
Assert.Equal("test1", val);
268+
await cut.InvokeAsync(() => cut.Instance.TriggerBlur());
269+
Assert.Equal("test2", val);
270+
271+
var item = cut.Find(".dropdown-item");
272+
await cut.InvokeAsync(() => item.Click());
271273
}
272274

273275
[Fact]

test/UnitTest/Components/AutoFillTest.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,27 @@ public void ItemTemplate_Ok()
6969
Assert.Contains("Template-test2", cut.Markup);
7070
}
7171

72+
[Fact]
73+
public async Task OnBlurAsync_Ok()
74+
{
75+
Foo? val = null;
76+
var items = new List<Foo>() { new() { Name = "test1" }, new() { Name = "test2" } };
77+
var cut = Context.RenderComponent<AutoFill<Foo>>(pb =>
78+
{
79+
pb.Add(a => a.Items, items);
80+
pb.Add(a => a.OnBlurAsync, v =>
81+
{
82+
val = v;
83+
return Task.CompletedTask;
84+
});
85+
});
86+
87+
var item = cut.Find(".dropdown-item");
88+
await cut.InvokeAsync(() => item.Click());
89+
Assert.NotNull(val);
90+
Assert.Equal("test1", val.Name);
91+
}
92+
7293
[Fact]
7394
public async Task OnCustomFilter_Ok()
7495
{

test/UnitTest/Components/SearchTest.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,32 @@ namespace UnitTest.Components;
77

88
public class SearchTest : BootstrapBlazorTestBase
99
{
10+
[Fact]
11+
public async Task OnBlurAsync_Ok()
12+
{
13+
string? val = null;
14+
var items = new List<string>() { "test1", "test2" };
15+
var cut = Context.RenderComponent<Search<string>>(pb =>
16+
{
17+
pb.Add(a => a.OnSearch, v =>
18+
{
19+
return Task.FromResult(items.AsEnumerable());
20+
});
21+
pb.Add(a => a.OnBlurAsync, v =>
22+
{
23+
val = v;
24+
return Task.CompletedTask;
25+
});
26+
});
27+
await cut.InvokeAsync(() => cut.Instance.TriggerFilter("t"));
28+
await Task.Delay(20);
29+
30+
var item = cut.Find(".dropdown-item");
31+
await cut.InvokeAsync(() => item.Click());
32+
Assert.NotNull(val);
33+
Assert.Equal("test1", val);
34+
}
35+
1036
[Fact]
1137
public void Items_Ok()
1238
{

0 commit comments

Comments
 (0)