From 57b50365718a783e11e8a90a4f0ca4d9a45974de Mon Sep 17 00:00:00 2001 From: Philippe Matray Date: Thu, 26 Feb 2026 23:57:26 +0100 Subject: [PATCH] feat: add password input type support and AddPasswordField builder method Fixes #55 --- .../FormContainer/FormCraftComponent.razor.cs | 37 +++++++++++++++++-- .../Builders/FieldBuilderTests.cs | 28 ++++++++++++++ .../FluentFormBuilderExtensionsTests.cs | 1 + .../Extensions/FluentFormBuilderExtensions.cs | 3 +- 4 files changed, 65 insertions(+), 4 deletions(-) diff --git a/FormCraft.ForMudBlazor/Features/FormContainer/FormCraftComponent.razor.cs b/FormCraft.ForMudBlazor/Features/FormContainer/FormCraftComponent.razor.cs index ef69c58..78f75d0 100644 --- a/FormCraft.ForMudBlazor/Features/FormContainer/FormCraftComponent.razor.cs +++ b/FormCraft.ForMudBlazor/Features/FormContainer/FormCraftComponent.razor.cs @@ -182,23 +182,54 @@ private void RenderTextField(RenderTreeBuilder builder, IFieldConfiguration(this, newValue => UpdateFieldValue(field.FieldName, newValue))); - + // Add Lines attribute for text area support if (field.AdditionalAttributes.TryGetValue("Lines", out var linesObj) && linesObj is int lines) { builder.AddAttribute(10, "Lines", lines); } - + // Add MaxLength attribute if present if (field.AdditionalAttributes.TryGetValue("MaxLength", out var maxLengthObj) && maxLengthObj is int maxLength) { builder.AddAttribute(11, "MaxLength", maxLength); } - + builder.AddAttribute(12, "Immediate", true); + + // Add InputType attribute if present + if (field.InputType != null) + { + builder.AddAttribute(13, "InputType", GetInputType(field.InputType)); + } + + // Add Adornment attributes for password toggle support + if (field.AdditionalAttributes.TryGetValue("Adornment", out var adornmentObj) && adornmentObj is Adornment adornment) + { + builder.AddAttribute(14, "Adornment", adornment); + } + + if (field.AdditionalAttributes.TryGetValue("AdornmentIcon", out var adornmentIconObj) && adornmentIconObj is string adornmentIcon) + { + builder.AddAttribute(15, "AdornmentIcon", adornmentIcon); + } + builder.CloseComponent(); } + private static InputType GetInputType(string inputType) + { + return inputType.ToLowerInvariant() switch + { + "email" => InputType.Email, + "password" => InputType.Password, + "tel" or "telephone" => InputType.Telephone, + "url" => InputType.Url, + "search" => InputType.Search, + _ => InputType.Text + }; + } + private void RenderNumericField(RenderTreeBuilder builder, IFieldConfiguration field, T value) where T : struct { diff --git a/FormCraft.UnitTests/Builders/FieldBuilderTests.cs b/FormCraft.UnitTests/Builders/FieldBuilderTests.cs index 2ec2712..159fd46 100644 --- a/FormCraft.UnitTests/Builders/FieldBuilderTests.cs +++ b/FormCraft.UnitTests/Builders/FieldBuilderTests.cs @@ -346,6 +346,34 @@ public void Build_Should_Return_FormConfiguration() result.Fields.First().IsRequired.ShouldBeTrue(); } + [Fact] + public void WithInputType_Should_Set_InputType_To_Password() + { + // Arrange & Act + var config = FormBuilder.Create() + .AddField(x => x.Name, field => field + .WithInputType("password")) + .Build(); + + // Assert + var field = config.Fields.First(f => f.FieldName == "Name"); + field.InputType.ShouldBe("password"); + } + + [Fact] + public void WithInputType_Should_Set_InputType_To_Email() + { + // Arrange & Act + var config = FormBuilder.Create() + .AddField(x => x.Email, field => field + .WithInputType("email")) + .Build(); + + // Assert + var field = config.Fields.First(f => f.FieldName == "Email"); + field.InputType.ShouldBe("email"); + } + [Fact] public void Multiple_Fluent_Calls_Should_Chain_Correctly() { diff --git a/FormCraft.UnitTests/Extensions/FluentFormBuilderExtensionsTests.cs b/FormCraft.UnitTests/Extensions/FluentFormBuilderExtensionsTests.cs index d8cf637..afa9edb 100644 --- a/FormCraft.UnitTests/Extensions/FluentFormBuilderExtensionsTests.cs +++ b/FormCraft.UnitTests/Extensions/FluentFormBuilderExtensionsTests.cs @@ -227,6 +227,7 @@ public void AddPasswordField_Should_Configure_Password_Field_With_MinLength() var field = config.Fields.First(f => f.FieldName == "Password"); field.Label.ShouldBe("Password"); field.IsRequired.ShouldBeTrue(); + field.InputType.ShouldBe("password"); field.Validators.Count.ShouldBe(3); // Required + MinLength + Special chars } diff --git a/FormCraft/Forms/Extensions/FluentFormBuilderExtensions.cs b/FormCraft/Forms/Extensions/FluentFormBuilderExtensions.cs index 8c401be..cdfde32 100644 --- a/FormCraft/Forms/Extensions/FluentFormBuilderExtensions.cs +++ b/FormCraft/Forms/Extensions/FluentFormBuilderExtensions.cs @@ -312,9 +312,10 @@ public static FormBuilder AddPasswordField( int minLength = 8, bool requireSpecialChars = true) where TModel : new() { - return builder.AddField(expression, field => + return builder.AddField(expression, field => { field.WithLabel(label) + .WithInputType("password") .Required($"{label} is required") .WithMinLength(minLength, $"Must be at least {minLength} characters");