From a4f194fb6eb557aab7c91584c2dfa062d93f3d2b Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Mon, 26 May 2025 14:26:12 +0800 Subject: [PATCH 1/7] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=20bind-value?= =?UTF-8?q?=20event=20=E8=AF=AD=E5=8F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor/Components/Filters/MultiFilter.razor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BootstrapBlazor/Components/Filters/MultiFilter.razor b/src/BootstrapBlazor/Components/Filters/MultiFilter.razor index df3a9cd937a..6c0fdcfda36 100644 --- a/src/BootstrapBlazor/Components/Filters/MultiFilter.razor +++ b/src/BootstrapBlazor/Components/Filters/MultiFilter.razor @@ -12,7 +12,7 @@ else @if (ShowSearch) { } From 7299c5284d0a0f9f9224274d1a0042c7ef99e95f Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Mon, 26 May 2025 15:04:28 +0800 Subject: [PATCH 2/7] =?UTF-8?q?refactor:=20=E5=A2=9E=E5=8A=A0=20MultiSelec?= =?UTF-8?q?tFilter=20=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Filters/MultiSelectFilter.razor | 16 ++++ .../Filters/MultiSelectFilter.razor.cs | 91 +++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 src/BootstrapBlazor/Components/Filters/MultiSelectFilter.razor create mode 100644 src/BootstrapBlazor/Components/Filters/MultiSelectFilter.razor.cs diff --git a/src/BootstrapBlazor/Components/Filters/MultiSelectFilter.razor b/src/BootstrapBlazor/Components/Filters/MultiSelectFilter.razor new file mode 100644 index 00000000000..7086a80640d --- /dev/null +++ b/src/BootstrapBlazor/Components/Filters/MultiSelectFilter.razor @@ -0,0 +1,16 @@ +@namespace BootstrapBlazor.Components +@inherits FilterBase +@typeparam TType + +@if (IsHeaderRow) +{ +
+ + +
+} +else +{ + +} diff --git a/src/BootstrapBlazor/Components/Filters/MultiSelectFilter.razor.cs b/src/BootstrapBlazor/Components/Filters/MultiSelectFilter.razor.cs new file mode 100644 index 00000000000..7ab740f8233 --- /dev/null +++ b/src/BootstrapBlazor/Components/Filters/MultiSelectFilter.razor.cs @@ -0,0 +1,91 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the Apache 2.0 License +// See the LICENSE file in the project root for more information. +// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone + +namespace BootstrapBlazor.Components; + +/// +/// 多项选择下拉框过滤组件 +/// +public partial class MultiSelectFilter +{ + private string? FilterRowClassString => CssBuilder.Default("filter-row") + .AddClass("active", TableColumnFilter.HasFilter()) + .Build(); + + private TType? _value1; + private FilterAction _action1 = FilterAction.Equal; + + /// + /// Gets or sets the filter items. + /// + [Parameter] + public List? Items { get; set; } + + /// + /// + /// + public override void Reset() + { + _value1 = default; + _action1 = FilterAction.GreaterThanOrEqual; + Count = 0; + Logic = FilterLogic.And; + StateHasChanged(); + } + + /// + /// + /// + /// + public override FilterKeyValueAction GetFilterConditions() + { + var filter = new FilterKeyValueAction() { FilterLogic = FilterLogic.Or }; + if (_value1 is string v) + { + var items = v.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + foreach (var item in items) + { + filter.Filters.Add(new FilterKeyValueAction + { + FieldKey = FieldKey, + FieldValue = item, + FilterAction = _action1 + }); + } + } + else if (_value1 is IEnumerable values) + { + foreach (var item in values) + { + filter.Filters.Add(new FilterKeyValueAction + { + FieldKey = FieldKey, + FieldValue = item, + FilterAction = _action1 + }); + } + } + return filter; + } + + /// + /// + /// + public override async Task SetFilterConditionsAsync(FilterKeyValueAction filter) + { + var first = filter.Filters.FirstOrDefault() ?? filter; + if (first.FieldValue is TType value) + { + _value1 = value; + } + else + { + _value1 = default; + } + _action1 = first.FilterAction; + + await base.SetFilterConditionsAsync(filter); + } +} From 8c4d96de81557d53b3b830f374aa263d093d1c23 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Mon, 26 May 2025 20:04:29 +0800 Subject: [PATCH 3/7] =?UTF-8?q?test:=20=E5=A2=9E=E5=8A=A0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/TableMultiSelectFilterTest.cs | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 test/UnitTest/Components/TableMultiSelectFilterTest.cs diff --git a/test/UnitTest/Components/TableMultiSelectFilterTest.cs b/test/UnitTest/Components/TableMultiSelectFilterTest.cs new file mode 100644 index 00000000000..f0304d9507e --- /dev/null +++ b/test/UnitTest/Components/TableMultiSelectFilterTest.cs @@ -0,0 +1,140 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the Apache 2.0 License +// See the LICENSE file in the project root for more information. +// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone + +namespace UnitTest.Components; + +public class TableMultiSelectFilterTest : BootstrapBlazorTestBase +{ + [Fact] + public void IsHeaderRow_Ok() + { + var cut = Context.RenderComponent(pb => + { + pb.Add(a => a.Table, new MockTable()); + pb.Add(a => a.Column, new MockColumn()); + pb.Add(a => a.IsHeaderRow, true); + }); + cut.Contains("filter-row"); + } + [Fact] + public async Task OnFilterAsync_Ok() + { + var cut = Context.RenderComponent(pb => + { + pb.Add(a => a.Table, new MockTable()); + pb.Add(a => a.Column, new MockColumn()); + pb.Add(a => a.IsHeaderRow, false); + }); + var item = cut.Find(".dropdown-menu .dropdown-item"); + await cut.InvokeAsync(() => item.Click()); + + var filter = cut.FindComponent>(); + var conditions = filter.Instance.GetFilterConditions(); + + Assert.Single(conditions.Filters); + + await cut.InvokeAsync(() => filter.Instance.Reset()); + conditions = filter.Instance.GetFilterConditions(); + Assert.Empty(conditions.Filters); + + await cut.InvokeAsync(() => filter.Instance.SetFilterConditionsAsync(new FilterKeyValueAction() + { + FieldValue = "v1,v2", + })); + conditions = filter.Instance.GetFilterConditions(); + Assert.Equal(2, conditions.Filters.Count); + + await cut.InvokeAsync(() => filter.Instance.SetFilterConditionsAsync(new FilterKeyValueAction() + { + FieldValue = true, + })); + conditions = filter.Instance.GetFilterConditions(); + Assert.Empty(conditions.Filters); + } + + [Fact] + public async Task OnFilterAsync_List() + { + var cut = Context.RenderComponent(pb => + { + pb.Add(a => a.Table, new MockTable()); + pb.Add(a => a.Column, new MockColumnList()); + pb.Add(a => a.IsHeaderRow, false); + }); + var item = cut.Find(".dropdown-menu .dropdown-item"); + await cut.InvokeAsync(() => item.Click()); + + var filter = cut.FindComponent>>(); + var conditions = filter.Instance.GetFilterConditions(); + + Assert.Single(conditions.Filters); + + await cut.InvokeAsync(() => filter.Instance.Reset()); + conditions = filter.Instance.GetFilterConditions(); + Assert.Empty(conditions.Filters); + } + + class MockTable : ITable + { + public Dictionary Filters { get; set; } = []; + + public Func? OnFilterAsync { get; set; } + + public List Columns => []; + + public IEnumerable GetVisibleColumns() => Columns; + } + + class MockColumn : TableColumn + { + public MockColumn() + { + PropertyType = typeof(string); + FieldName = "MultiSelect"; + + FilterTemplate = new RenderFragment(builder => + { + builder.OpenComponent(0); + builder.AddAttribute(1, nameof(FilterProvider.ChildContent), new RenderFragment(builder => + { + builder.OpenComponent>(2); + builder.AddAttribute(3, nameof(MultiSelectFilter.Items), new List + { + new("v1", "Test-1"), + new("v2", "Test-2") + }); + builder.CloseComponent(); + })); + builder.CloseComponent(); + }); + } + } + + class MockColumnList : TableColumn> + { + public MockColumnList() + { + PropertyType = typeof(List); + FieldName = "MultiSelect"; + + FilterTemplate = new RenderFragment(builder => + { + builder.OpenComponent(0); + builder.AddAttribute(1, nameof(FilterProvider.ChildContent), new RenderFragment(builder => + { + builder.OpenComponent>>(2); + builder.AddAttribute(3, nameof(MultiSelectFilter>.Items), new List + { + new("v1", "Test-1"), + new("v2", "Test-2") + }); + builder.CloseComponent(); + })); + builder.CloseComponent(); + }); + } + } + +} From 40a3aff3e45e5093de4be1311f8f13668b206be7 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Mon, 26 May 2025 20:33:14 +0800 Subject: [PATCH 4/7] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Filters/MultiSelectFilter.razor | 2 +- .../Components/TableMultiSelectFilterTest.cs | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/BootstrapBlazor/Components/Filters/MultiSelectFilter.razor b/src/BootstrapBlazor/Components/Filters/MultiSelectFilter.razor index 7086a80640d..f9bfd842383 100644 --- a/src/BootstrapBlazor/Components/Filters/MultiSelectFilter.razor +++ b/src/BootstrapBlazor/Components/Filters/MultiSelectFilter.razor @@ -5,7 +5,7 @@ @if (IsHeaderRow) {
-
diff --git a/test/UnitTest/Components/TableMultiSelectFilterTest.cs b/test/UnitTest/Components/TableMultiSelectFilterTest.cs index f0304d9507e..4df50adc0d1 100644 --- a/test/UnitTest/Components/TableMultiSelectFilterTest.cs +++ b/test/UnitTest/Components/TableMultiSelectFilterTest.cs @@ -8,7 +8,7 @@ namespace UnitTest.Components; public class TableMultiSelectFilterTest : BootstrapBlazorTestBase { [Fact] - public void IsHeaderRow_Ok() + public async Task IsHeaderRow_Ok() { var cut = Context.RenderComponent(pb => { @@ -17,6 +17,22 @@ public void IsHeaderRow_Ok() pb.Add(a => a.IsHeaderRow, true); }); cut.Contains("filter-row"); + + var actions = cut.FindAll(".dropdown-item"); + await cut.InvokeAsync(() => actions[1].Click()); + + // check filter + var filter = cut.Instance; + var conditions = filter.FilterAction.GetFilterConditions(); + Assert.Single(conditions.Filters); + + // trigger onclear + var clear = cut.Find(".btn-ban"); + await cut.InvokeAsync(() => clear.Click()); + + // check filter + conditions = filter.FilterAction.GetFilterConditions(); + Assert.Empty(conditions.Filters); } [Fact] public async Task OnFilterAsync_Ok() From 5498a05dd863215f87fa64f135e8649187b64aee Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Mon, 26 May 2025 20:39:38 +0800 Subject: [PATCH 5/7] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Filters/MultiSelectFilter.razor | 1 - test/UnitTest/Components/TableMultiSelectFilterTest.cs | 8 -------- 2 files changed, 9 deletions(-) diff --git a/src/BootstrapBlazor/Components/Filters/MultiSelectFilter.razor b/src/BootstrapBlazor/Components/Filters/MultiSelectFilter.razor index f9bfd842383..951bee5182d 100644 --- a/src/BootstrapBlazor/Components/Filters/MultiSelectFilter.razor +++ b/src/BootstrapBlazor/Components/Filters/MultiSelectFilter.razor @@ -7,7 +7,6 @@
-
} else diff --git a/test/UnitTest/Components/TableMultiSelectFilterTest.cs b/test/UnitTest/Components/TableMultiSelectFilterTest.cs index 4df50adc0d1..fecd359a3b9 100644 --- a/test/UnitTest/Components/TableMultiSelectFilterTest.cs +++ b/test/UnitTest/Components/TableMultiSelectFilterTest.cs @@ -25,14 +25,6 @@ public async Task IsHeaderRow_Ok() var filter = cut.Instance; var conditions = filter.FilterAction.GetFilterConditions(); Assert.Single(conditions.Filters); - - // trigger onclear - var clear = cut.Find(".btn-ban"); - await cut.InvokeAsync(() => clear.Click()); - - // check filter - conditions = filter.FilterAction.GetFilterConditions(); - Assert.Empty(conditions.Filters); } [Fact] public async Task OnFilterAsync_Ok() From 02d6b81fdad784f3e73a55156d111da6847e502e Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Mon, 26 May 2025 20:42:50 +0800 Subject: [PATCH 6/7] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Filters/MultiSelectFilter.razor.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/BootstrapBlazor/Components/Filters/MultiSelectFilter.razor.cs b/src/BootstrapBlazor/Components/Filters/MultiSelectFilter.razor.cs index 7ab740f8233..6f96bb7e325 100644 --- a/src/BootstrapBlazor/Components/Filters/MultiSelectFilter.razor.cs +++ b/src/BootstrapBlazor/Components/Filters/MultiSelectFilter.razor.cs @@ -29,9 +29,8 @@ public partial class MultiSelectFilter public override void Reset() { _value1 = default; - _action1 = FilterAction.GreaterThanOrEqual; + _action1 = FilterAction.Equal; Count = 0; - Logic = FilterLogic.And; StateHasChanged(); } From 22583d3a75d079de6d565274e299511e0823f00c Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Mon, 26 May 2025 20:43:32 +0800 Subject: [PATCH 7/7] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/UnitTest/Components/TableMultiSelectFilterTest.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/UnitTest/Components/TableMultiSelectFilterTest.cs b/test/UnitTest/Components/TableMultiSelectFilterTest.cs index fecd359a3b9..003b40f13aa 100644 --- a/test/UnitTest/Components/TableMultiSelectFilterTest.cs +++ b/test/UnitTest/Components/TableMultiSelectFilterTest.cs @@ -26,6 +26,7 @@ public async Task IsHeaderRow_Ok() var conditions = filter.FilterAction.GetFilterConditions(); Assert.Single(conditions.Filters); } + [Fact] public async Task OnFilterAsync_Ok() {