|
7 | 7 |
|
8 | 8 | namespace UnitTest.Components; |
9 | 9 |
|
10 | | -public class TableFilterTest : BootstrapBlazorTestBase |
| 10 | +public partial class TableFilterTest : BootstrapBlazorTestBase |
11 | 11 | { |
12 | | - private IStringLocalizer<Foo> Localizer { get; } |
13 | | - |
14 | | - public TableFilterTest() |
15 | | - { |
16 | | - Localizer = Context.Services.GetRequiredService<IStringLocalizer<Foo>>(); |
17 | | - } |
18 | | - |
19 | 12 | [Fact] |
20 | 13 | public void TableFilter_Ok() |
21 | 14 | { |
| 15 | + var localizer = Context.Services.GetRequiredService<IStringLocalizer<Foo>>(); |
22 | 16 | var cut = Context.RenderComponent<BootstrapBlazorRoot>(pb => |
23 | 17 | { |
24 | 18 | pb.AddChildContent<Table<Foo>>(pb => |
25 | 19 | { |
26 | | - pb.Add(a => a.Items, Foo.GenerateFoo(Localizer)); |
| 20 | + pb.Add(a => a.Items, Foo.GenerateFoo(localizer)); |
27 | 21 | pb.Add(a => a.RenderMode, TableRenderMode.Table); |
28 | 22 | pb.Add(a => a.ShowFilterHeader, true); |
29 | 23 | pb.Add(a => a.TableColumns, CreateTableColumns()); |
@@ -79,189 +73,48 @@ public void FilterTemplate_Ok() |
79 | 73 | } |
80 | 74 |
|
81 | 75 | [Fact] |
82 | | - public async Task MultiFilter_Ok() |
| 76 | + public async Task BoolFilter_Ok() |
83 | 77 | { |
84 | | - var cut = Context.RenderComponent<BootstrapBlazorRoot>(pb => |
85 | | - { |
86 | | - pb.AddChildContent<Table<Cat>>(pb => |
87 | | - { |
88 | | - pb.Add(a => a.Items, new List<Cat> |
89 | | - { |
90 | | - new() |
91 | | - }); |
92 | | - pb.Add(a => a.RenderMode, TableRenderMode.Table); |
93 | | - pb.Add(a => a.TableColumns, cat => builder => |
94 | | - { |
95 | | - var index = 0; |
96 | | - builder.OpenComponent<TableColumn<Cat, string?>>(index++); |
97 | | - builder.AddAttribute(index++, nameof(TableColumn<Cat, string?>.Field), cat.P8); |
98 | | - builder.AddAttribute(index++, nameof(TableColumn<Cat, string?>.FieldExpression), Utility.GenerateValueExpression(cat, nameof(Cat.P8), typeof(string))); |
99 | | - builder.AddAttribute(index++, nameof(TableColumn<Cat, string?>.Filterable), true); |
100 | | - builder.AddAttribute(index++, nameof(TableColumn<Cat, string?>.FilterTemplate), new RenderFragment(b => |
101 | | - { |
102 | | - b.OpenComponent<MultiFilter>(0); |
103 | | - b.AddAttribute(1, nameof(MultiFilter.ShowSearch), true); |
104 | | - b.AddAttribute(2, nameof(MultiFilter.StringComparison), StringComparison.OrdinalIgnoreCase); |
105 | | - b.AddAttribute(3, nameof(MultiFilter.OnGetItemsAsync), () => Task.FromResult(new List<SelectedItem>() { new("test1", "test1") })); |
106 | | - b.AddAttribute(4, nameof(MultiFilter.AlwaysTriggerGetItems), true); |
107 | | - b.CloseComponent(); |
108 | | - })); |
109 | | - builder.CloseComponent(); |
110 | | - }); |
111 | | - }); |
112 | | - }); |
113 | | - cut.Contains("bb-multi-filter-loading"); |
114 | | - |
115 | | - var filter = cut.FindComponent<MultiFilter>(); |
116 | | - filter.SetParametersAndRender(pb => |
| 78 | + var cut = Context.RenderComponent<TableFilter>(pb => |
117 | 79 | { |
118 | | - pb.Add(a => a.LoadingTemplate, "loading-template-test"); |
| 80 | + pb.Add(a => a.Table, new MockTable()); |
| 81 | + pb.Add(a => a.Column, new MockColumn<bool>()); |
119 | 82 | }); |
120 | | - cut.Contains("loading-template-test"); |
121 | | - await cut.InvokeAsync(() => filter.Instance.TriggerGetItemsCallback()); |
| 83 | + var boolFilter = cut.FindComponent<BoolFilter>().Instance; |
122 | 84 |
|
123 | | - // 选中数据 |
124 | | - var checkItems = cut.FindComponents<Checkbox<bool>>(); |
125 | | - await cut.InvokeAsync(() => checkItems[1].Instance.SetValue(true)); |
126 | | - await cut.InvokeAsync(() => filter.Instance.TriggerGetItemsCallback()); |
127 | | - } |
| 85 | + // SetFilterConditionsAsync |
| 86 | + var newConditions = new FilterKeyValueAction(); |
| 87 | + await cut.InvokeAsync(() => boolFilter.SetFilterConditionsAsync(newConditions)); |
128 | 88 |
|
129 | | - [Fact] |
130 | | - public void MultiFilter_Exception() |
131 | | - { |
132 | | - // 测试 Exception |
133 | | - Assert.Throws<InvalidOperationException>(() => |
| 89 | + newConditions = new FilterKeyValueAction |
134 | 90 | { |
135 | | - Context.RenderComponent<MultiFilter>(pb => |
136 | | - { |
137 | | - pb.Add(a => a.Items, new SelectedItem[] { new("test1", "test1"), new("test2", "test2") }); |
138 | | - pb.Add(a => a.OnGetItemsAsync, () => Task.FromResult(new List<SelectedItem>() { new("test1", "test1") })); |
139 | | - }); |
140 | | - }); |
141 | | - } |
| 91 | + Filters = [new FilterKeyValueAction() { FieldValue = true }] |
| 92 | + }; |
| 93 | + await cut.InvokeAsync(() => boolFilter.SetFilterConditionsAsync(newConditions)); |
142 | 94 |
|
143 | | - [Fact] |
144 | | - public async Task MultipleFilter_Items() |
145 | | - { |
146 | | - var cut = Context.RenderComponent<BootstrapBlazorRoot>(pb => |
| 95 | + newConditions = new FilterKeyValueAction |
147 | 96 | { |
148 | | - pb.AddChildContent<Table<Cat>>(pb => |
149 | | - { |
150 | | - pb.Add(a => a.Items, new List<Cat> |
151 | | - { |
152 | | - new() |
153 | | - }); |
154 | | - pb.Add(a => a.RenderMode, TableRenderMode.Table); |
155 | | - pb.Add(a => a.TableColumns, cat => builder => |
156 | | - { |
157 | | - var index = 0; |
158 | | - builder.OpenComponent<TableColumn<Cat, string?>>(index++); |
159 | | - builder.AddAttribute(index++, nameof(TableColumn<Cat, string?>.Field), cat.P8); |
160 | | - builder.AddAttribute(index++, nameof(TableColumn<Cat, string?>.FieldExpression), Utility.GenerateValueExpression(cat, nameof(Cat.P8), typeof(string))); |
161 | | - builder.AddAttribute(index++, nameof(TableColumn<Cat, string?>.Filterable), true); |
162 | | - builder.AddAttribute(index++, nameof(TableColumn<Cat, string?>.FilterTemplate), new RenderFragment(b => |
163 | | - { |
164 | | - b.OpenComponent<MultiFilter>(0); |
165 | | - b.AddAttribute(1, nameof(MultiFilter.ShowSearch), true); |
166 | | - b.AddAttribute(2, nameof(MultiFilter.Items), new SelectedItem[] { new("test1", "test1"), new("test2", "test2") }); |
167 | | - b.CloseComponent(); |
168 | | - })); |
169 | | - builder.CloseComponent(); |
170 | | - }); |
171 | | - }); |
172 | | - }); |
173 | | - cut.DoesNotContain("multi-filter-placeholder"); |
174 | | - cut.DoesNotContain("multi-filter-All"); |
| 97 | + Filters = [new FilterKeyValueAction() { FieldValue = false }] |
| 98 | + }; |
| 99 | + await cut.InvokeAsync(() => boolFilter.SetFilterConditionsAsync(newConditions)); |
175 | 100 |
|
176 | | - var filter = cut.FindComponent<MultiFilter>(); |
177 | | - filter.SetParametersAndRender(pb => |
178 | | - { |
179 | | - pb.Add(a => a.SearchPlaceHolderText, "multi-filter-placeholder"); |
180 | | - pb.Add(a => a.SelectAllText, "multi-filter-All"); |
181 | | - }); |
182 | | - cut.Contains("multi-filter-placeholder"); |
183 | | - cut.Contains("multi-filter-All"); |
| 101 | + // GetFilterConditions |
| 102 | + var filter = boolFilter.GetFilterConditions(); |
| 103 | + Assert.NotNull(filter.Filters); |
| 104 | + Assert.Single(filter.Filters); |
184 | 105 |
|
185 | | - await cut.InvokeAsync(() => filter.Instance.Reset()); |
| 106 | + // Reset |
| 107 | + await cut.InvokeAsync(() => boolFilter.Reset()); |
186 | 108 |
|
187 | | - // 选中选项 |
188 | | - var checkboxs = cut.FindComponents<Checkbox<bool>>(); |
189 | | - Assert.Equal(3, checkboxs.Count); |
190 | | - await cut.InvokeAsync(() => checkboxs[2].Instance.SetState(CheckboxState.Checked)); |
191 | | - |
192 | | - FilterKeyValueAction? action = null; |
193 | | - await cut.InvokeAsync(() => |
194 | | - { |
195 | | - action = filter.Instance.GetFilterConditions(); |
196 | | - }); |
197 | | - Assert.NotNull(action); |
198 | | - Assert.Equal(FilterLogic.Or, action.FilterLogic); |
199 | | - Assert.NotNull(action.Filters); |
200 | | - Assert.NotNull(action.Filters[0]); |
201 | | - Assert.Equal("test2", action.Filters[0].FieldValue); |
202 | | - Assert.Equal("P8", action.Filters[0].FieldKey); |
203 | | - Assert.Equal(CheckboxState.Indeterminate, checkboxs[0].Instance.State); |
204 | | - |
205 | | - // 测试 Items 改变保持选项 |
206 | | - filter.SetParametersAndRender(pb => |
| 109 | + // IsHeaderRow |
| 110 | + cut.SetParametersAndRender(pb => |
207 | 111 | { |
208 | | - pb.Add(a => a.Items, new SelectedItem[] { new("test3", "test3"), new("test2", "test2") }); |
209 | | - }); |
210 | | - checkboxs = cut.FindComponents<Checkbox<bool>>(); |
211 | | - Assert.Equal(3, checkboxs.Count); |
212 | | - checkboxs[2].Markup.Contains("checked=\"checked\""); |
213 | | - |
214 | | - // 测试全选 |
215 | | - await cut.InvokeAsync(() => checkboxs[0].Instance.SetState(CheckboxState.Checked)); |
216 | | - await cut.InvokeAsync(() => checkboxs[0].Instance.SetState(CheckboxState.UnChecked)); |
217 | | - |
218 | | - // 测试搜索 |
219 | | - var input = cut.Find(".bb-multi-filter-search"); |
220 | | - await cut.InvokeAsync(() => input.Input("test02")); |
221 | | - await cut.InvokeAsync(() => input.Input("")); |
222 | | - } |
223 | | - |
224 | | - [Fact] |
225 | | - public void NotInTable_Ok() |
226 | | - { |
227 | | - var cut = Context.RenderComponent<TableFilter>(pb => |
228 | | - { |
229 | | - var foo = Foo.Generate(Localizer); |
230 | | - var column = new TableColumn<Foo, string>(); |
231 | | - column.SetParametersAsync(ParameterView.FromDictionary(new Dictionary<string, object?>() |
232 | | - { |
233 | | - [nameof(TableColumn<Foo, string>.Field)] = foo.Name, |
234 | | - [nameof(TableColumn<Foo, string>.FieldExpression)] = foo.GenerateValueExpression(), |
235 | | - })); |
236 | 112 | pb.Add(a => a.IsHeaderRow, true); |
237 | | - pb.Add(a => a.Column, column); |
238 | | - }); |
239 | | - } |
240 | | - |
241 | | - [Fact] |
242 | | - public void OnlyOneTableFilterPerColumn_Ok() |
243 | | - { |
244 | | - var cut = Context.RenderComponent<BootstrapBlazorRoot>(pb => |
245 | | - { |
246 | | - pb.AddChildContent<Table<Foo>>(pb => |
247 | | - { |
248 | | - pb.Add(a => a.Items, new List<Foo>() { new() }); |
249 | | - pb.Add(a => a.RenderMode, TableRenderMode.Table); |
250 | | - pb.Add(a => a.ShowFilterHeader, true); |
251 | | - pb.Add(a => a.TableColumns, new RenderFragment<Foo>(foo => builder => |
252 | | - { |
253 | | - var index = 0; |
254 | | - builder.OpenComponent<TableColumn<Foo, bool>>(index++); |
255 | | - builder.AddAttribute(index++, nameof(TableColumn<Foo, bool>.Field), foo.Complete); |
256 | | - builder.AddAttribute(index++, nameof(TableColumn<Foo, bool>.FieldExpression), foo.GenerateValueExpression(nameof(Foo.Complete), typeof(bool))); |
257 | | - builder.AddAttribute(index++, nameof(TableColumn<Foo, bool>.Filterable), true); |
258 | | - builder.CloseComponent(); |
259 | | - })); |
260 | | - }); |
261 | 113 | }); |
262 | | - |
263 | | - var filters = cut.FindComponents<BoolFilter>(); |
264 | | - Assert.Single(filters); |
| 114 | + var select = cut.FindComponent<Select<string>>(); |
| 115 | + var items = cut.FindAll(".dropdown-item"); |
| 116 | + Assert.Equal(3, items.Count); |
| 117 | + await cut.InvokeAsync(() => items[1].Click()); |
265 | 118 | } |
266 | 119 |
|
267 | 120 | private static RenderFragment<Foo> CreateTableColumns() => foo => builder => |
@@ -386,4 +239,23 @@ private class Cat |
386 | 239 |
|
387 | 240 | public string? P8 { get; set; } |
388 | 241 | } |
| 242 | + |
| 243 | + private class MockTable : ITable |
| 244 | + { |
| 245 | + public Dictionary<string, IFilterAction> Filters => []; |
| 246 | + |
| 247 | + public Func<Task>? OnFilterAsync { get; } |
| 248 | + |
| 249 | + public List<ITableColumn> Columns => []; |
| 250 | + |
| 251 | + public IEnumerable<ITableColumn> GetVisibleColumns() => Columns; |
| 252 | + } |
| 253 | + |
| 254 | + private class MockColumn<TType> : TableColumn<Foo, TType> |
| 255 | + { |
| 256 | + public MockColumn() |
| 257 | + { |
| 258 | + PropertyType = typeof(TType); |
| 259 | + } |
| 260 | + } |
389 | 261 | } |
0 commit comments