From 879b5f258203785b57d5a832c7eedff592cf835e Mon Sep 17 00:00:00 2001 From: Ivan Josipovic <9521987+IvanJosipovic@users.noreply.github.com> Date: Fri, 17 Apr 2020 23:46:50 -0700 Subject: [PATCH] 1.8.0 (#107) Improve component accessibility with aria attributes (#106) @leonardder Fix for colspan with DetailTemplate (#105) @smokedlinq Fix for Filtering nullable inner object throws NRE [WASM] (#104) Updated Sample to Blazor 3.2.0 Preview 4 --- .github/workflows/build.yml | 5 +- .github/workflows/cicd.yml | 7 +- .github/workflows/release.yml | 3 + BlazorTable.sln | 6 ++ .../BlazorTable.Sample.Shared.csproj | 6 +- src/BlazorTable.Sample.Shared/Bugs/104.razor | 53 ++++++++++++++ .../BlazorTable.Sample.Wasm.csproj | 10 ++- src/BlazorTable.Sample.Wasm/Program.cs | 5 +- .../BlazorTable.Tests.csproj | 27 +++++++ src/BlazorTable.Tests/Bug104.cs | 72 +++++++++++++++++++ src/BlazorTable/BlazorTable.csproj | 4 ++ src/BlazorTable/Components/Table.razor.cs | 2 +- src/BlazorTable/Utillities.cs | 53 +++++++++++++- 13 files changed, 242 insertions(+), 11 deletions(-) create mode 100644 src/BlazorTable.Sample.Shared/Bugs/104.razor create mode 100644 src/BlazorTable.Tests/BlazorTable.Tests.csproj create mode 100644 src/BlazorTable.Tests/Bug104.cs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ff5538ba..e9a11361 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,4 +19,7 @@ jobs: dotnet-version: 3.1.201 - name: Dotnet Build - run: dotnet build --configuration Release \ No newline at end of file + run: dotnet build -c Release + + - name: Dotnet Test + run: dotnet test -c Release \ No newline at end of file diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index a3e7ff53..0bdb5610 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -30,6 +30,9 @@ jobs: - name: Dotnet Build run: dotnet build -c Release + - name: Dotnet Test + run: dotnet test -c Release + - name: Dotnet Publish working-directory: src/BlazorTable.Sample.Wasm run: dotnet publish -c Release @@ -42,7 +45,7 @@ jobs: - name: Deploy to Test id: netlify - uses: ivanjosipovic/actions/cli@master + uses: netlify/actions/cli@master with: args: deploy --json -d src/BlazorTable.Sample.Wasm/bin/Release/netstandard2.1/publish/wwwroot env: @@ -51,6 +54,6 @@ jobs: - name: Get Test Address run: | - $url = $(ConvertFrom-Json '${{ steps.netlify.outputs.output }}').deploy_url; + $url = '${{ steps.netlify.outputs.NETLIFY_URL }}'; Write-Output $url; shell: pwsh \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e5d72cf9..1346537f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,6 +29,9 @@ jobs: working-directory: src/BlazorTable run: dotnet pack -c Release -p:Version=${{ steps.version.outputs.new_tag }} + - name: Dotnet Test + run: dotnet test --configuration Release + - name: Dotnet Nuget Push if: "!contains(github.event.head_commit.message, '#skip')" working-directory: src/BlazorTable/bin/Release diff --git a/BlazorTable.sln b/BlazorTable.sln index e8780a0b..321315a3 100644 --- a/BlazorTable.sln +++ b/BlazorTable.sln @@ -20,6 +20,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorTable.Sample.Server", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorTable.Sample.Shared", "src\BlazorTable.Sample.Shared\BlazorTable.Sample.Shared.csproj", "{25E8AB75-2F08-463E-8499-0C5BBB20BC50}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorTable.Tests", "src\BlazorTable.Tests\BlazorTable.Tests.csproj", "{64B71D26-A307-4541-9B17-BD6709211F21}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -42,6 +44,10 @@ Global {25E8AB75-2F08-463E-8499-0C5BBB20BC50}.Debug|Any CPU.Build.0 = Debug|Any CPU {25E8AB75-2F08-463E-8499-0C5BBB20BC50}.Release|Any CPU.ActiveCfg = Release|Any CPU {25E8AB75-2F08-463E-8499-0C5BBB20BC50}.Release|Any CPU.Build.0 = Release|Any CPU + {64B71D26-A307-4541-9B17-BD6709211F21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {64B71D26-A307-4541-9B17-BD6709211F21}.Debug|Any CPU.Build.0 = Debug|Any CPU + {64B71D26-A307-4541-9B17-BD6709211F21}.Release|Any CPU.ActiveCfg = Release|Any CPU + {64B71D26-A307-4541-9B17-BD6709211F21}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/BlazorTable.Sample.Shared/BlazorTable.Sample.Shared.csproj b/src/BlazorTable.Sample.Shared/BlazorTable.Sample.Shared.csproj index b32adbb6..0bf52e24 100644 --- a/src/BlazorTable.Sample.Shared/BlazorTable.Sample.Shared.csproj +++ b/src/BlazorTable.Sample.Shared/BlazorTable.Sample.Shared.csproj @@ -9,11 +9,15 @@ - + + + + + diff --git a/src/BlazorTable.Sample.Shared/Bugs/104.razor b/src/BlazorTable.Sample.Shared/Bugs/104.razor new file mode 100644 index 00000000..bad2f4e0 --- /dev/null +++ b/src/BlazorTable.Sample.Shared/Bugs/104.razor @@ -0,0 +1,53 @@ +@page "/104" + +@using BlazorTable + +

Issue #104

+ + + + + +
+ +@code +{ + private List items = new List(); + + protected override void OnInitialized() + { + for (int i = 0; i < 5; i++) + { + items.Add(new Parent() { Name = i.ToString(), Child = new Child() { Name = i.ToString() } }); + items.Add(new Parent() { Name = i.ToString() }); + } + } + + private class Parent + { + public string Name { get; set; } + + public Child Child { get; set; } + } + + private class Child + { + public string Name { get; set; } + + public GrandChild GrandChild { get; set; } + } + + private class GrandChild + { + public string Name { get; set; } + } +} diff --git a/src/BlazorTable.Sample.Wasm/BlazorTable.Sample.Wasm.csproj b/src/BlazorTable.Sample.Wasm/BlazorTable.Sample.Wasm.csproj index c70394e2..e47270e1 100644 --- a/src/BlazorTable.Sample.Wasm/BlazorTable.Sample.Wasm.csproj +++ b/src/BlazorTable.Sample.Wasm/BlazorTable.Sample.Wasm.csproj @@ -6,13 +6,17 @@ - - - + + + + + + + diff --git a/src/BlazorTable.Sample.Wasm/Program.cs b/src/BlazorTable.Sample.Wasm/Program.cs index b0e4f033..2d10099a 100644 --- a/src/BlazorTable.Sample.Wasm/Program.cs +++ b/src/BlazorTable.Sample.Wasm/Program.cs @@ -2,6 +2,8 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.Extensions.DependencyInjection; +using System.Net.Http; +using System; namespace BlazorTable.Sample.Wasm { @@ -10,9 +12,10 @@ public class Program public static async Task Main(string[] args) { var builder = WebAssemblyHostBuilder.CreateDefault(args); + builder.RootComponents.Add("app"); - builder.Services.AddBaseAddressHttpClient(); + builder.Services.AddSingleton(new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); await builder.Build().RunAsync(); } diff --git a/src/BlazorTable.Tests/BlazorTable.Tests.csproj b/src/BlazorTable.Tests/BlazorTable.Tests.csproj new file mode 100644 index 00000000..d890664d --- /dev/null +++ b/src/BlazorTable.Tests/BlazorTable.Tests.csproj @@ -0,0 +1,27 @@ + + + + netcoreapp3.1 + + false + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/src/BlazorTable.Tests/Bug104.cs b/src/BlazorTable.Tests/Bug104.cs new file mode 100644 index 00000000..9a674706 --- /dev/null +++ b/src/BlazorTable.Tests/Bug104.cs @@ -0,0 +1,72 @@ +using Shouldly; +using System; +using System.Linq.Expressions; +using Xunit; + +namespace BlazorTable.Tests +{ + public class Bug104 + { + [Fact] + public void AddToExisting() + { + Expression> query = x => ((x.Child.Name != null) && (x.Child.Name == "bob")); + + Expression> queryfixed = x => ((x.Child != null) &&((x.Child.Name != null) && (x.Child.Name == "bob"))); + + query.AddNullChecks().ToString().ShouldBe(queryfixed.ToString()); + } + + + [Fact] + public void NoParent() + { + Expression> query = x => x.Name != null; + + Expression> queryfixed = x => x.Name != null; + + query.AddNullChecks().ToString().ShouldBe(queryfixed.ToString()); + } + + [Fact] + public void SingleParent() + { + Expression> query = x => x.Child.Name != null; + + Expression> queryfixed = x => x.Child != null + && x.Child.Name != null; + + query.AddNullChecks().ToString().ShouldBe(queryfixed.ToString()); + } + + [Fact] + public void MultiParent() + { + Expression> query1 = x => x.Child.GrandChild.Name != null; + + Expression> query1fixed = x => ((x.Child != null) + && ((x.Child.GrandChild != null) && (x.Child.GrandChild.Name != null))); + + query1.AddNullChecks().ToString().ShouldBe("x => ((x.Child != null) AndAlso ((x.Child.GrandChild != null) AndAlso (x.Child.GrandChild.Name != null)))"); + } + + private class Parent + { + public string Name { get; set; } + + public Child Child { get; set; } + } + + private class Child + { + public string Name { get; set; } + + public GrandChild GrandChild { get; set; } + } + + private class GrandChild + { + public string Name { get; set; } + } + } +} diff --git a/src/BlazorTable/BlazorTable.csproj b/src/BlazorTable/BlazorTable.csproj index 161d0fc9..219de317 100644 --- a/src/BlazorTable/BlazorTable.csproj +++ b/src/BlazorTable/BlazorTable.csproj @@ -30,5 +30,9 @@ runtime; build; native; contentfiles; analyzers; buildtransitive
+ + + + \ No newline at end of file diff --git a/src/BlazorTable/Components/Table.razor.cs b/src/BlazorTable/Components/Table.razor.cs index 18131af1..c6701deb 100644 --- a/src/BlazorTable/Components/Table.razor.cs +++ b/src/BlazorTable/Components/Table.razor.cs @@ -123,7 +123,7 @@ private IEnumerable GetData() { if (item.Filter != null) { - ItemsQueryable = ItemsQueryable.Where(item.Filter); + ItemsQueryable = ItemsQueryable.Where(item.Filter.AddNullChecks()); } } diff --git a/src/BlazorTable/Utillities.cs b/src/BlazorTable/Utillities.cs index 113a20f1..2b990d54 100644 --- a/src/BlazorTable/Utillities.cs +++ b/src/BlazorTable/Utillities.cs @@ -1,4 +1,5 @@ -using System; +using LinqKit; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; @@ -8,7 +9,7 @@ namespace BlazorTable { - internal static class Utillities + public static class Utillities { public static IEnumerable OrEmptyIfNull(this IEnumerable source) { @@ -114,5 +115,53 @@ public static string ToDescriptionString(this Enum val) var attributes = (DescriptionAttribute[])val.GetType().GetField(val.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false); return attributes.Length > 0 ? attributes[0].Description : string.Empty; } + + /// + /// Recursively walks up the tree and adds null checks + /// + /// + /// + public static Expression> AddNullChecks(this Expression> expression) + { + var parents = new Queue(); + Expression> tempExpression = expression; + + if (expression?.Body is BinaryExpression binary) + { + if (binary.Left is MemberExpression member) + { + // From here we're looking at parents + Recurse(member.Expression); + } + else if (expression?.Body is BinaryExpression binary2) + { + if (binary2.Left is BinaryExpression binary3 && binary3.Left is MemberExpression member2) + { + // From here we're looking at parents + Recurse(member2.Expression); + } + } + } + + while (parents.Count > 0) + { + var nullCheck = Expression.NotEqual(parents.Dequeue(), Expression.Constant(null)); + + var newQuery = Expression.Lambda>(nullCheck, expression.Parameters); + + tempExpression = newQuery.And(tempExpression); + } + + return tempExpression; + + void Recurse(object expression) + { + if (expression is MemberExpression member) + { + parents.Enqueue(member); + Recurse(member.Expression); + } + } + } } }