From 582e8f60fa0683be00b64da617c3cbfccae810ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Can=20Karag=C3=B6z?= Date: Sat, 27 Dec 2025 00:41:08 +0300 Subject: [PATCH 1/3] Update Dependent Components v 9.0.0-preview.1 --- .../App.razor | 21 +-- ...Beam.MudBlazor.Extensions.Docs.Wasm.csproj | 46 +++--- .../CodeBeam.MudBlazor.Extensions.Qr.xml | 146 ++++++++++++++++++ .../Components/DocsApiTable.razor | 64 ++++++-- ...Beam.MudBlazor.Extensions.CsvMapper.csproj | 5 +- .../CsvMapper/MudCsvMapper.razor.cs | 5 +- .../CodeBeam.MudBlazor.Extensions.Qr.csproj | 14 +- 7 files changed, 233 insertions(+), 68 deletions(-) create mode 100644 docs/CodeBeam.MudBlazor.Extensions.Docs.Wasm/wwwroot/CodeBeam.MudBlazor.Extensions.Qr.xml diff --git a/docs/CodeBeam.MudBlazor.Extensions.Docs.Wasm/App.razor b/docs/CodeBeam.MudBlazor.Extensions.Docs.Wasm/App.razor index 19ad36db..9961b138 100644 --- a/docs/CodeBeam.MudBlazor.Extensions.Docs.Wasm/App.razor +++ b/docs/CodeBeam.MudBlazor.Extensions.Docs.Wasm/App.razor @@ -24,16 +24,17 @@ { try { - //Route to the component that contains the QR - if (args.Path == "mudbarcode" || args.Path == "api") - { - var assemblies = await AssemblyLoader - .LoadAssembliesAsync(new[] - { - "zxing.dll", - }); - _lazyLoadedAssemblies.AddRange(assemblies); - } + //Route to the component that contains the lazy load components + // if (args.Path == "mudbarcode" || args.Path == "api") + // { + // var assemblies = await AssemblyLoader + // .LoadAssembliesAsync(new[] + // { + // "CodeBeam.MudBlazor.Extensions.Qr.dll", + // "CodeBeam.MudBlazor.Extensions.CsvMapper.dll" + // }); + // _lazyLoadedAssemblies.AddRange(assemblies); + // } } catch (Exception ex) { diff --git a/docs/CodeBeam.MudBlazor.Extensions.Docs.Wasm/CodeBeam.MudBlazor.Extensions.Docs.Wasm.csproj b/docs/CodeBeam.MudBlazor.Extensions.Docs.Wasm/CodeBeam.MudBlazor.Extensions.Docs.Wasm.csproj index c2bc9a4b..6840a4bd 100644 --- a/docs/CodeBeam.MudBlazor.Extensions.Docs.Wasm/CodeBeam.MudBlazor.Extensions.Docs.Wasm.csproj +++ b/docs/CodeBeam.MudBlazor.Extensions.Docs.Wasm/CodeBeam.MudBlazor.Extensions.Docs.Wasm.csproj @@ -1,25 +1,21 @@  - - net10.0 - enable - enable - MudExtensions.Docs.Wasm - - - - - - - - - - - - - + + net10.0 + enable + enable + MudExtensions.Docs.Wasm + + + + + + + + + + + @@ -35,10 +31,10 @@ - - true - PreserveNewest - - + + true + PreserveNewest + + diff --git a/docs/CodeBeam.MudBlazor.Extensions.Docs.Wasm/wwwroot/CodeBeam.MudBlazor.Extensions.Qr.xml b/docs/CodeBeam.MudBlazor.Extensions.Docs.Wasm/wwwroot/CodeBeam.MudBlazor.Extensions.Qr.xml new file mode 100644 index 00000000..dc7a14c0 --- /dev/null +++ b/docs/CodeBeam.MudBlazor.Extensions.Docs.Wasm/wwwroot/CodeBeam.MudBlazor.Extensions.Qr.xml @@ -0,0 +1,146 @@ + + + + CodeBeam.MudBlazor.Extensions.Qr + + + + + The result that used in MudBarcode component to show one of the barcode formats. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Determines which barcode format will shown. Default is QR Code. + + + + + The outer div classname. + + + + + The outer div classname. + + + + + The image source shows when the value is null. + + + + + If true, it goes to specified href when click. + + + + + The error content. + + + + + The width value as integer. + + + + + The height value as integer. + + + + + Use this value on not square sized barcode formats like UPC_A and UPC_E. + + + + + Increase the stroke width if readers can not read the barcode easily. + + + + + Determines how user go to href content. Default is open in a new tab. + + + + + The value of the barcode format. + + + + + The color of the barcode as string. Accepts all kinds of CSS property values. (ex. #123456) Default is "black". + + + + + The background color of the barcode as string. Accepts all kinds of CSS property values. (ex. #123456) Default is "white". + + + + + Fires when value changed. + + + + + Shows a component inside the barcode. + + + + + Barcode process that returns BarcodeResult. Returns null if value is also null or empty. + + + + + diff --git a/docs/CodeBeam.MudBlazor.Extensions.Docs/Components/DocsApiTable.razor b/docs/CodeBeam.MudBlazor.Extensions.Docs/Components/DocsApiTable.razor index 4d9ff7b8..a7704863 100644 --- a/docs/CodeBeam.MudBlazor.Extensions.Docs/Components/DocsApiTable.razor +++ b/docs/CodeBeam.MudBlazor.Extensions.Docs/Components/DocsApiTable.razor @@ -22,7 +22,7 @@ @value - @(_docReader?.GetSummary(context) ?? "—") + @(GetDocReaderFor(context.DeclaringType!)?.GetSummary(context) ?? "—") @@ -40,7 +40,7 @@ @context.Name @DocUtilities.GetFriendlyTypeName(context.PropertyType) - @(_docReader?.GetSummary(context) ?? "—") + @(GetDocReaderFor(context.DeclaringType!)?.GetSummary(context) ?? "—") No specific items. @@ -57,7 +57,7 @@ @context.Name @DocUtilities.GetFriendlyTypeName(context.ReturnType) - @(_docReader?.GetSummary(context) ?? "—") + @(GetDocReaderFor(context.DeclaringType!)?.GetSummary(context) ?? "—") No specific items. @@ -79,7 +79,8 @@ [Parameter] public Type? Type { get; set; } [Parameter] public MudExtensionComponentInfo? Component { get; set; } - private SimpleXmlDocReader? _docReader; + // private SimpleXmlDocReader? _docReader; + private readonly Dictionary _docReaders = new(); private List _excludedMethods = new() { @@ -122,24 +123,55 @@ } } - protected override async Task OnInitializedAsync() + private SimpleXmlDocReader? GetDocReaderFor(Type memberType) { - string? xmlContent = null; + var asmName = memberType.Assembly.GetName().Name; - if (OperatingSystem.IsBrowser()) + return asmName switch { - xmlContent = await Http.GetStringAsync("CodeBeam.MudBlazor.Extensions.xml"); - } - else + "CodeBeam.MudBlazor.Extensions" => _docReaders.GetValueOrDefault("CodeBeam.MudBlazor.Extensions.xml"), + "CodeBeam.MudBlazor.Extensions.Qr" => _docReaders.GetValueOrDefault("CodeBeam.MudBlazor.Extensions.Qr.xml"), + "CodeBeam.MudBlazor.Extensions.CsvMapper" => _docReaders.GetValueOrDefault("CodeBeam.MudBlazor.Extensions.CsvMapper.xml"), + _ => null + }; + } + + protected override async Task OnInitializedAsync() + { + var xmlFiles = new[] { - var xmlPath = Path.Combine(AppContext.BaseDirectory, "CodeBeam.MudBlazor.Extensions.xml"); - if (File.Exists(xmlPath)) - xmlContent = await File.ReadAllTextAsync(xmlPath); - } + "CodeBeam.MudBlazor.Extensions.xml", + "CodeBeam.MudBlazor.Extensions.Qr.xml", + "CodeBeam.MudBlazor.Extensions.CsvMapper.xml" + }; - if (!string.IsNullOrEmpty(xmlContent)) + foreach (var file in xmlFiles) { - _docReader = new SimpleXmlDocReader(xmlContent); + string? xmlContent = null; + + try + { + if (OperatingSystem.IsBrowser()) + { + xmlContent = await Http.GetStringAsync(file); + } + else + { + var xmlPath = Path.Combine(AppContext.BaseDirectory, file); + if (File.Exists(xmlPath)) + xmlContent = await File.ReadAllTextAsync(xmlPath); + } + + if (!string.IsNullOrWhiteSpace(xmlContent)) + { + _docReaders[file] = new SimpleXmlDocReader(xmlContent); + } + } + catch + { + + } } } + } diff --git a/src/CodeBeam.MudBlazor.Extensions.CsvMapper/CodeBeam.MudBlazor.Extensions.CsvMapper.csproj b/src/CodeBeam.MudBlazor.Extensions.CsvMapper/CodeBeam.MudBlazor.Extensions.CsvMapper.csproj index 3ec6f317..c1019f25 100644 --- a/src/CodeBeam.MudBlazor.Extensions.CsvMapper/CodeBeam.MudBlazor.Extensions.CsvMapper.csproj +++ b/src/CodeBeam.MudBlazor.Extensions.CsvMapper/CodeBeam.MudBlazor.Extensions.CsvMapper.csproj @@ -5,7 +5,7 @@ enable enable MIT - 1.0.0 + 9.0.0-preview.1 CodeBeam.MudBlazor.Extensions.Csv CodeBeam CodeBeam @@ -20,10 +20,11 @@ https://github.com/CodeBeamOrg/CodeBeam.MudBlazor.Extensions Blazor; MudBlazor; Component; Extension; Csv; Mapper; MudExtensions + True - + diff --git a/src/CodeBeam.MudBlazor.Extensions.CsvMapper/Components/CsvMapper/MudCsvMapper.razor.cs b/src/CodeBeam.MudBlazor.Extensions.CsvMapper/Components/CsvMapper/MudCsvMapper.razor.cs index b10bc88a..0541c0ff 100644 --- a/src/CodeBeam.MudBlazor.Extensions.CsvMapper/Components/CsvMapper/MudCsvMapper.razor.cs +++ b/src/CodeBeam.MudBlazor.Extensions.CsvMapper/Components/CsvMapper/MudCsvMapper.razor.cs @@ -1,5 +1,4 @@ -using System.ComponentModel; -using CsvHelper; +using CsvHelper; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Forms; using MudBlazor; @@ -315,7 +314,7 @@ private async Task OnImport() await using (var csv = new CsvWriter(writer, config)) { var dynamicContent = CsvContent?.Cast(); - await csv.WriteRecordsAsync(dynamicContent); + await csv.WriteRecordsAsync(dynamicContent ?? Enumerable.Empty()); var str = writer.ToString(); FileContentByte = Encoding.UTF8.GetBytes(str); diff --git a/src/CodeBeam.MudBlazor.Extensions.Qr/CodeBeam.MudBlazor.Extensions.Qr.csproj b/src/CodeBeam.MudBlazor.Extensions.Qr/CodeBeam.MudBlazor.Extensions.Qr.csproj index 13fdff1c..24d16b21 100644 --- a/src/CodeBeam.MudBlazor.Extensions.Qr/CodeBeam.MudBlazor.Extensions.Qr.csproj +++ b/src/CodeBeam.MudBlazor.Extensions.Qr/CodeBeam.MudBlazor.Extensions.Qr.csproj @@ -5,7 +5,7 @@ enable enable MIT - 1.0.0 + 9.0.0-preview.1 CodeBeam.MudBlazor.Extensions.Qr CodeBeam CodeBeam @@ -30,7 +30,7 @@ - + @@ -39,16 +39,6 @@ - - - - - - - - - - True From 52ca9f81b9f74357187626519db73c906929c1cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Can=20Karag=C3=B6z?= Date: Sat, 27 Dec 2025 00:49:56 +0300 Subject: [PATCH 2/3] Add Csv Helper Test --- .../Components/CsvMapperTests.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 tests/CodeBeam.MudBlazor.Extensions.UnitTests/Components/CsvMapperTests.cs diff --git a/tests/CodeBeam.MudBlazor.Extensions.UnitTests/Components/CsvMapperTests.cs b/tests/CodeBeam.MudBlazor.Extensions.UnitTests/Components/CsvMapperTests.cs new file mode 100644 index 00000000..13e82828 --- /dev/null +++ b/tests/CodeBeam.MudBlazor.Extensions.UnitTests/Components/CsvMapperTests.cs @@ -0,0 +1,25 @@ + +using AwesomeAssertions; +using Bunit; + +namespace MudExtensions.UnitTests.Components +{ + [TestFixture] + public class CsvMapperTests : BunitTest + { + [Test] + public void MudCsvMapper_Should_Render_With_Minimal_Parameters() + { + var expectedHeaders = new List + { + new("Id", required: true), + new("Name"), + }; + + var cut = Context.Render(parameters => parameters.Add(p => p.ExpectedHeaders, expectedHeaders)); + + cut.Markup.Should().NotBeNullOrWhiteSpace(); + cut.FindAll("input").Count.Should().BeGreaterThan(0); + } + } +} From c4b56f410321fb54ef12f9a71c447021aab8a3e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Can=20Karag=C3=B6z?= Date: Sat, 27 Dec 2025 01:04:28 +0300 Subject: [PATCH 3/3] Add More Tests --- .../CsvMapper/MudCsvMapper.razor.cs | 12 +- .../Components/CsvMapperTests.cs | 167 +++++++++++++++++- 2 files changed, 176 insertions(+), 3 deletions(-) diff --git a/src/CodeBeam.MudBlazor.Extensions.CsvMapper/Components/CsvMapper/MudCsvMapper.razor.cs b/src/CodeBeam.MudBlazor.Extensions.CsvMapper/Components/CsvMapper/MudCsvMapper.razor.cs index 0541c0ff..98bbb3a6 100644 --- a/src/CodeBeam.MudBlazor.Extensions.CsvMapper/Components/CsvMapper/MudCsvMapper.razor.cs +++ b/src/CodeBeam.MudBlazor.Extensions.CsvMapper/Components/CsvMapper/MudCsvMapper.razor.cs @@ -10,9 +10,19 @@ namespace MudExtensions { - internal class ConfirmedDefaultValue + /// + /// + /// + public class ConfirmedDefaultValue { + /// + /// + /// public string? DefaultValue { get; set; } + + /// + /// + /// public bool Confirmed { get; set; } } diff --git a/tests/CodeBeam.MudBlazor.Extensions.UnitTests/Components/CsvMapperTests.cs b/tests/CodeBeam.MudBlazor.Extensions.UnitTests/Components/CsvMapperTests.cs index 13e82828..4688c947 100644 --- a/tests/CodeBeam.MudBlazor.Extensions.UnitTests/Components/CsvMapperTests.cs +++ b/tests/CodeBeam.MudBlazor.Extensions.UnitTests/Components/CsvMapperTests.cs @@ -1,6 +1,6 @@ - -using AwesomeAssertions; +using AwesomeAssertions; using Bunit; +using System.Reflection; namespace MudExtensions.UnitTests.Components { @@ -21,5 +21,168 @@ public void MudCsvMapper_Should_Render_With_Minimal_Parameters() cut.Markup.Should().NotBeNullOrWhiteSpace(); cut.FindAll("input").Count.Should().BeGreaterThan(0); } + + [Test] + public void MudCsvMapper_Should_Render_With_AllowCreateExpectedHeaders() + { + var cut = Context.Render(parameters => parameters + .Add(p => p.AllowCreateExpectedHeaders, true) + .Add(p => p.ExpectedHeaders, new List()) + ); + + cut.Markup.Should().Contain("Create Header"); + } + + [Test] + public void CsvHeaders_Should_Match_ExpectedHeaders_Exactly() + { + // Arrange + var expectedHeaders = new List + { + new("Id", required: true), + new("Name", required: false) + }; + + var cut = Context.Render(p => p + .Add(x => x.ExpectedHeaders, expectedHeaders) + ); + + var csvContent = new List> + { + new Dictionary + { + ["Id"] = 1, + ["Name"] = "Test" + } + }; + + cut.Instance.GetType() + .GetField("CsvContent", BindingFlags.NonPublic | BindingFlags.Instance)! + .SetValue(cut.Instance, csvContent); + + InvokePrivate(cut.Instance, "MatchCsvHeadersWithExpectedHeaders"); + + expectedHeaders[0].MatchedFieldCount.Should().Be(1); + expectedHeaders[1].MatchedFieldCount.Should().Be(1); + } + + [Test] + public void Normalize_Should_Lowercase_And_Remove_Spaces_When_Enabled() + { + var cut = Context.Render(p => p + .Add(x => x.NormalizeHeaders, true) + ); + + var result = cut.Instance.GetType() + .GetMethod("Normalize", BindingFlags.NonPublic | BindingFlags.Instance)! + .Invoke(cut.Instance, new object[] { "My Header \"Name\"" }); + + result.Should().Be("myheadername"); + } + + [Test] + public void AddDefaultValues_Should_Add_Confirmed_Defaults() + { + var expectedHeaders = new List + { + new("Age", required: true, allowDefaultValue: true) + }; + + var cut = Context.Render(p => p + .Add(x => x.ExpectedHeaders, expectedHeaders) + ); + + var csvContent = new List> + { + new Dictionary() + }; + + SetPrivateMember(cut.Instance, "CsvContent", csvContent); + + var defaults = new Dictionary + { + ["Age"] = new ConfirmedDefaultValue + { + Confirmed = true, + DefaultValue = "18" + } + }; + + SetPrivateMember(cut.Instance, "_defaultValueHeaders", defaults); + + InvokePrivate(cut.Instance, "AddDefaultValues"); + + csvContent[0].ContainsKey("Age").Should().BeTrue(); + csvContent[0]["Age"].Should().Be("18"); + } + + + [Test] + public void RemoveUnmappedData_Should_Remove_Unmapped_Fields() + { + var cut = Context.Render(); + + var headers = new List + { + new("A", "File"), + new("B", "Mapped") + }; + + SetPrivateMember(cut.Instance, "MudCsvHeaders", headers); + + var csvContent = new List> + { + new Dictionary + { + ["A"] = 1, + ["B"] = 2 + } + }; + + SetPrivateMember(cut.Instance, "CsvContent", csvContent); + + InvokePrivate(cut.Instance, "RemoveUnmappedData"); + + csvContent[0].ContainsKey("A").Should().BeFalse(); + csvContent[0].ContainsKey("B").Should().BeTrue(); + } + + + + + private static void InvokePrivate(object instance, string methodName) + { + var method = instance.GetType() + .GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance); + + method.Should().NotBeNull(); + method!.Invoke(instance, null); + } + + private static void SetPrivateMember(object instance, string name, object value) + { + var type = instance.GetType(); + + var prop = type.GetProperty(name, + BindingFlags.NonPublic | BindingFlags.Instance); + + if (prop != null) + { + prop.SetValue(instance, value); + return; + } + + var field = type.GetField(name, + BindingFlags.NonPublic | BindingFlags.Instance); + + if (field != null) + { + field.SetValue(instance, value); + return; + } + + Assert.Fail($"Private member '{name}' not found on {type.Name}"); + } + } }