Skip to content

Commit 47dd6fc

Browse files
committed
Added form button tag helpers, including bootstrap 3 attributes (optional)
1 parent 4745f44 commit 47dd6fc

16 files changed

+340
-25
lines changed

ChameleonForms.AcceptanceTests/IntegrationTests/TagHelperTests.Should_render_correctly_when_used_via_form_or_section_and_when_used_for_top_level_property_or_sub_property.approved.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ <h1>ChameleonForms TagHelpers example</h1>
3737
</form-message>
3838

3939
<form-navigation>
40-
<button type="submit">Signup</button>
40+
<form-submit label="Signup" emphasis-style="Primary" />
4141
</form-navigation>
4242
</chameleon-form>
4343

@@ -142,7 +142,7 @@ <h3>Confirm the Terms &amp; Conditions</h3>
142142

143143
<div class="form_navigation">
144144

145-
<button type="submit">Signup</button>
145+
<button class="btn-primary" type="submit">Signup</button>
146146
</div>
147147

148148
</form>

ChameleonForms.Core/HtmlAttributes.cs

+35-7
Original file line numberDiff line numberDiff line change
@@ -160,11 +160,16 @@ public HtmlAttributes Attr(string key, object value)
160160
public HtmlAttributes Attr(Func<object, object> attribute)
161161
{
162162
var item = attribute(null);
163-
_tagBuilder.MergeAttribute(attribute.Method.GetParameters()[0].Name.Replace("_", "-").ToLower(), item == null ? string.Empty : item.ToString(), true);
163+
_tagBuilder.MergeAttribute(GetAttributeName(attribute), item == null ? string.Empty : item.ToString(), true);
164164

165165
return this;
166166
}
167167

168+
private string GetAttributeName(Func<object, object> attribute)
169+
{
170+
return attribute.Method.GetParameters()[0].Name.Replace("_", "-").ToLower();
171+
}
172+
168173
/// <summary>
169174
/// Adds or updates a set of HTML attributes using lambda methods to express the attributes.
170175
/// </summary>
@@ -173,7 +178,12 @@ public HtmlAttributes Attr(Func<object, object> attribute)
173178
public HtmlAttributes Attrs(params Func<object, object>[] attributes)
174179
{
175180
foreach (var func in attributes)
176-
Attr(func);
181+
{
182+
if (GetAttributeName(func) == "class")
183+
AddClass(func(null) as string);
184+
else
185+
Attr(func);
186+
}
177187

178188
return this;
179189
}
@@ -185,9 +195,14 @@ public HtmlAttributes Attrs(params Func<object, object>[] attributes)
185195
/// <returns>The <see cref="HtmlAttributes"/> attribute to allow for method chaining</returns>
186196
public HtmlAttributes Attrs(IDictionary<string, object> attributes)
187197
{
188-
attributes = attributes.ToDictionary(d => d.Key.ToLower(), d => d.Value);
198+
var attributesToMerge = attributes
199+
.Where(x => x.Key != "class")
200+
.ToDictionary(d => d.Key.ToLower(), d => d.Value);
189201

190-
_tagBuilder.MergeAttributes(attributes, true);
202+
_tagBuilder.MergeAttributes(attributesToMerge, true);
203+
204+
if (attributes.ContainsKey("class"))
205+
AddClass(attributes["class"] as string);
191206

192207
return this;
193208
}
@@ -199,7 +214,14 @@ public HtmlAttributes Attrs(IDictionary<string, object> attributes)
199214
/// <returns>The <see cref="HtmlAttributes"/> attribute to allow for method chaining</returns>
200215
public HtmlAttributes Attrs(IDictionary<string, string> attributes)
201216
{
202-
_tagBuilder.MergeAttributes(attributes, true);
217+
var attributesToMerge = attributes
218+
.Where(k => k.Key != "class")
219+
.ToDictionary(x => x.Key.ToLower(), x => x.Value);
220+
221+
_tagBuilder.MergeAttributes(attributesToMerge, true);
222+
223+
if (attributes.ContainsKey("class"))
224+
AddClass(attributes["class"]);
203225

204226
return this;
205227
}
@@ -211,9 +233,15 @@ public HtmlAttributes Attrs(IDictionary<string, string> attributes)
211233
/// <returns>The <see cref="HtmlAttributes"/> attribute to allow for method chaining</returns>
212234
public HtmlAttributes Attrs(object attributes)
213235
{
214-
var attrs = HtmlHelper.AnonymousObjectToHtmlAttributes(attributes)
236+
var attrs = HtmlHelper.AnonymousObjectToHtmlAttributes(attributes);
237+
238+
var attrsToMerge = attrs
239+
.Where(x => x.Key != "class")
215240
.ToDictionary(d => d.Key.ToLower(), d => d.Value);
216-
_tagBuilder.MergeAttributes(attrs, true);
241+
_tagBuilder.MergeAttributes(attrsToMerge, true);
242+
243+
if (attrs.ContainsKey("class"))
244+
AddClass(attrs["class"] as string);
217245

218246
return this;
219247
}

ChameleonForms.Example/Views/Comparison/ChameleonForms.cshtml

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@using Microsoft.AspNetCore.Mvc.Razor
2+
@using ChameleonForms.Templates.TwitterBootstrap3
23
@model ComparisonController.SignupViewModel
34

45
@{
@@ -42,6 +43,6 @@
4243

4344
using (var n = f.BeginNavigation())
4445
{
45-
@n.Submit("Signup")
46+
@n.Submit("Signup").WithStyle(EmphasisStyle.Primary)
4647
}
4748
}

ChameleonForms.Example/Views/Comparison/ChameleonFormsTH.cshtml

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,6 @@
3535
</form-message>
3636

3737
<form-navigation>
38-
<button type="submit">Signup</button>
38+
<form-submit label="Signup" emphasis-style="Primary" />
3939
</form-navigation>
4040
</chameleon-form>

ChameleonForms.Example/Views/Comparison/EditorTemplates.cshtml

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,6 @@
4242
</div>
4343

4444
<div class="form_navigation">
45-
<button type="submit">Signup</button>
45+
<button type="submit" class="btn btn-primary">Signup</button>
4646
</div>
4747
}

ChameleonForms.Example/Views/Comparison/HtmlHelpers.cshtml

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,6 @@
6060
</div>
6161

6262
<div class="form_navigation">
63-
<button type="submit">Signup</button>
63+
<button type="submit" class="btn btn-primary">Signup</button>
6464
</div>
6565
}

ChameleonForms.Example/Views/ExampleForms/Form2.cshtml

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
@model ViewModelExample
1+
@using ChameleonForms.Templates.TwitterBootstrap3
2+
@model ViewModelExample
23

34
@{
45
ViewBag.Title = "Example Form 2";
@@ -38,13 +39,12 @@
3839
}
3940
@* Navigation *@
4041
<form-navigation>
41-
@{ var n = Html.GetChameleonFormsNavigation();}
42-
@n.Submit("asdf")
43-
@n.Reset("Reset")
42+
<form-submit label="Submit" size="Large" emphasis-style="Info" icon="calendar" />
43+
<form-button label="Button" fluent-attrs='b => b.WithStyle(EmphasisStyle.Primary)' />
44+
<form-reset size="ExtraSmall" icon="person"><strong>Hello</strong> World! Reset</form-reset>
4445
</form-navigation>
4546
</chameleon-form>
4647

47-
4848
@* Not possible - compile error, model has to be the page model with tag helpers
4949
<chameleon-form enctype="EncType.UrlEncoded" action="@Url.Action("ChameleonForms", "Comparison")" method="Post" model="new SignupViewModel()">
5050
<section heading="asdf">

ChameleonForms.Example/Views/_ViewImports.cshtml

+1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@
1010

1111
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
1212
@addTagHelper ChameleonForms.TagHelpers.*, ChameleonForms
13+
@addTagHelper ChameleonForms.Templates.TwitterBootstrap3.*, ChameleonForms
1314
@addTagHelper *, ChameleonForms.Example

ChameleonForms.Templates/TwitterBootstrap3/ButtonHtmlAttributesExtensions.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public static ButtonHtmlAttributes WithIcon(this ButtonHtmlAttributes attrs, str
3636
/// <returns>The Html Attribute object so other methods can be chained off of it</returns>
3737
public static ButtonHtmlAttributes WithStyle(this ButtonHtmlAttributes attrs, EmphasisStyle style)
3838
{
39-
attrs.AddClass(string.Format("btn-{0}", style.ToString().ToLower()));
39+
attrs.AddClass($"btn-{style.ToString().ToLower()}");
4040
return attrs;
4141
}
4242

@@ -51,8 +51,8 @@ public static ButtonHtmlAttributes WithStyle(this ButtonHtmlAttributes attrs, Em
5151
/// <returns>The Html Attribute object so other methods can be chained off of it</returns>
5252
public static ButtonHtmlAttributes WithSize(this ButtonHtmlAttributes attrs, ButtonSize size)
5353
{
54-
if (size != ButtonSize.Default)
55-
attrs.AddClass(string.Format("btn-{0}", size.Humanize()));
54+
if (size != ButtonSize.Default && size != ButtonSize.NoneSpecified)
55+
attrs.AddClass($"btn-{size.Humanize()}");
5656
return attrs;
5757
}
5858
}

ChameleonForms.Templates/TwitterBootstrap3/ButtonSize.cs

+5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ namespace ChameleonForms.Templates.TwitterBootstrap3
77
/// </summary>
88
public enum ButtonSize
99
{
10+
/// <summary>
11+
/// None specified.
12+
/// </summary>
13+
[Description("")]
14+
NoneSpecified,
1015
/// <summary>
1116
/// Extra small button size.
1217
/// </summary>

ChameleonForms.Tests/PublicApiTests.ChameleonFormsPublicApi_ShouldNotChange_WithoutApproval.approved.txt

+50-4
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,13 @@ namespace ChameleonForms.TagHelpers
793793
public FieldValidationTagHelper() { }
794794
public override System.Threading.Tasks.Task ProcessUsingModelPropertyAsync<TModel, TProperty>(Microsoft.AspNetCore.Razor.TagHelpers.TagHelperContext context, Microsoft.AspNetCore.Razor.TagHelpers.TagHelperOutput output, System.Linq.Expressions.Expression<System.Func<TModel, TProperty>> modelProperty) { }
795795
}
796+
public class FormButtonTagHelper : ChameleonForms.TagHelpers.ModelAwareTagHelper
797+
{
798+
public FormButtonTagHelper() { }
799+
public System.Func<ChameleonForms.Component.ButtonHtmlAttributes, ChameleonForms.Component.ButtonHtmlAttributes> FluentAttrs { get; set; }
800+
public string Label { get; set; }
801+
public override System.Threading.Tasks.Task ProcessWhileAwareOfModelTypeAsync<TModel>(Microsoft.AspNetCore.Razor.TagHelpers.TagHelperContext context, Microsoft.AspNetCore.Razor.TagHelpers.TagHelperOutput output) { }
802+
}
796803
public class FormMessageTagHelper : ChameleonForms.TagHelpers.ModelAwareTagHelper
797804
{
798805
public FormMessageTagHelper() { }
@@ -818,6 +825,13 @@ namespace ChameleonForms.TagHelpers
818825
public string Name { get; set; }
819826
public override System.Threading.Tasks.Task ProcessUsingModelPropertyAsync<TModel, TProperty>(Microsoft.AspNetCore.Razor.TagHelpers.TagHelperContext context, Microsoft.AspNetCore.Razor.TagHelpers.TagHelperOutput output, System.Linq.Expressions.Expression<System.Func<TModel, TProperty>> modelProperty) { }
820827
}
828+
public class FormResetTagHelper : ChameleonForms.TagHelpers.ModelAwareTagHelper
829+
{
830+
public FormResetTagHelper() { }
831+
public System.Func<ChameleonForms.Component.ButtonHtmlAttributes, ChameleonForms.Component.ButtonHtmlAttributes> FluentAttrs { get; set; }
832+
public string Label { get; set; }
833+
public override System.Threading.Tasks.Task ProcessWhileAwareOfModelTypeAsync<TModel>(Microsoft.AspNetCore.Razor.TagHelpers.TagHelperContext context, Microsoft.AspNetCore.Razor.TagHelpers.TagHelperOutput output) { }
834+
}
821835
public class FormSectionTagHelper : ChameleonForms.TagHelpers.ModelAwareTagHelper
822836
{
823837
public FormSectionTagHelper() { }
@@ -838,6 +852,15 @@ namespace ChameleonForms.TagHelpers
838852
public Microsoft.AspNetCore.Html.IHtmlContent LeadingHtmlContent { get; set; }
839853
public override System.Threading.Tasks.Task ProcessWhileAwareOfModelTypeAsync<TModel>(Microsoft.AspNetCore.Razor.TagHelpers.TagHelperContext context, Microsoft.AspNetCore.Razor.TagHelpers.TagHelperOutput output) { }
840854
}
855+
public class FormSubmitTagHelper : ChameleonForms.TagHelpers.ModelAwareTagHelper
856+
{
857+
public FormSubmitTagHelper() { }
858+
public System.Func<ChameleonForms.Component.ButtonHtmlAttributes, ChameleonForms.Component.ButtonHtmlAttributes> FluentAttrs { get; set; }
859+
public string Label { get; set; }
860+
public string Name { get; set; }
861+
public string Value { get; set; }
862+
public override System.Threading.Tasks.Task ProcessWhileAwareOfModelTypeAsync<TModel>(Microsoft.AspNetCore.Razor.TagHelpers.TagHelperContext context, Microsoft.AspNetCore.Razor.TagHelpers.TagHelperOutput output) { }
863+
}
841864
public class MessageParagraphTagHelper : ChameleonForms.TagHelpers.ModelAwareTagHelper
842865
{
843866
public MessageParagraphTagHelper() { }
@@ -862,6 +885,27 @@ namespace ChameleonForms.TagHelpers
862885
public override System.Threading.Tasks.Task ProcessAsync(Microsoft.AspNetCore.Razor.TagHelpers.TagHelperContext context, Microsoft.AspNetCore.Razor.TagHelpers.TagHelperOutput output) { }
863886
public abstract System.Threading.Tasks.Task ProcessUsingModelPropertyAsync<TModel, TProperty>(Microsoft.AspNetCore.Razor.TagHelpers.TagHelperContext context, Microsoft.AspNetCore.Razor.TagHelpers.TagHelperOutput output, System.Linq.Expressions.Expression<System.Func<TModel, TProperty>> modelProperty);
864887
}
888+
public static class TagHelperContextExtensions
889+
{
890+
public const string FieldConfigurationItemsKey = "ChameleonForms:FieldConfiguration";
891+
public static ChameleonForms.Component.Config.IFieldConfiguration GetFieldConfiguration(this Microsoft.AspNetCore.Razor.TagHelpers.TagHelperContext context) { }
892+
public static ChameleonForms.HtmlAttributes GetHtmlAttributes(this Microsoft.AspNetCore.Razor.TagHelpers.TagHelperContext context) { }
893+
}
894+
}
895+
namespace ChameleonForms.Templates.TwitterBootstrap3
896+
{
897+
[Microsoft.AspNetCore.Razor.TagHelpers.HtmlTargetElement("form-button")]
898+
[Microsoft.AspNetCore.Razor.TagHelpers.HtmlTargetElement("form-reset")]
899+
[Microsoft.AspNetCore.Razor.TagHelpers.HtmlTargetElement("form-submit")]
900+
public class TwitterBootstrap3SubmitButtonTagHelper : Microsoft.AspNetCore.Razor.TagHelpers.TagHelper
901+
{
902+
public TwitterBootstrap3SubmitButtonTagHelper() { }
903+
public ChameleonForms.Templates.TwitterBootstrap3.EmphasisStyle EmphasisStyle { get; set; }
904+
public string Icon { get; set; }
905+
public override int Order { get; }
906+
public ChameleonForms.Templates.TwitterBootstrap3.ButtonSize Size { get; set; }
907+
public override System.Threading.Tasks.Task ProcessAsync(Microsoft.AspNetCore.Razor.TagHelpers.TagHelperContext context, Microsoft.AspNetCore.Razor.TagHelpers.TagHelperOutput output) { }
908+
}
865909
}
866910
namespace ChameleonForms.Utils
867911
{
@@ -1034,13 +1078,15 @@ namespace ChameleonForms.Templates.TwitterBootstrap3
10341078
}
10351079
public enum ButtonSize
10361080
{
1081+
[System.ComponentModel.Description("")]
1082+
NoneSpecified = 0,
10371083
[System.ComponentModel.Description("xs")]
1038-
ExtraSmall = 0,
1084+
ExtraSmall = 1,
10391085
[System.ComponentModel.Description("sm")]
1040-
Small = 1,
1041-
Default = 2,
1086+
Small = 2,
1087+
Default = 3,
10421088
[System.ComponentModel.Description("lg")]
1043-
Large = 3,
1089+
Large = 4,
10441090
}
10451091
public enum EmphasisStyle
10461092
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System;
2+
using System.Text.Encodings.Web;
3+
using System.Threading.Tasks;
4+
using ChameleonForms.Component;
5+
using ChameleonForms.Utils;
6+
using Microsoft.AspNetCore.Razor.TagHelpers;
7+
8+
namespace ChameleonForms.TagHelpers
9+
{
10+
/// <summary>
11+
/// Creates a ChameleonForms form button, use within a ChameleonForm form navigation context.
12+
/// </summary>
13+
public class FormButtonTagHelper : ModelAwareTagHelper
14+
{
15+
/// <summary>
16+
/// Button label (if text, otherwise make it the content within the button)
17+
/// </summary>
18+
public string Label { get; set; }
19+
20+
public Func<ButtonHtmlAttributes, ButtonHtmlAttributes> FluentAttrs { get; set; }
21+
22+
/// <inheritdoc />
23+
public override async Task ProcessWhileAwareOfModelTypeAsync<TModel>(TagHelperContext context, TagHelperOutput output)
24+
{
25+
var helper = ViewContext.GetHtmlHelper<TModel>();
26+
var n = helper.GetChameleonFormsNavigation();
27+
output.TagMode = TagMode.StartTagAndEndTag;
28+
output.TagName = null;
29+
var childContent = Label?.ToHtml() ?? await output.GetChildContentAsync(HtmlEncoder.Default);
30+
var buttonOutput = n.Button(childContent);
31+
32+
FluentAttrs?.Invoke(buttonOutput);
33+
34+
var finalOutput = buttonOutput.Attrs(context.GetHtmlAttributes().Attributes);
35+
output.Content.SetHtmlContent(finalOutput);
36+
}
37+
}
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System;
2+
using System.Text.Encodings.Web;
3+
using System.Threading.Tasks;
4+
using ChameleonForms.Component;
5+
using ChameleonForms.Utils;
6+
using Microsoft.AspNetCore.Razor.TagHelpers;
7+
8+
namespace ChameleonForms.TagHelpers
9+
{
10+
/// <summary>
11+
/// Creates a ChameleonForms form reset button, use within a ChameleonForm form navigation context.
12+
/// </summary>
13+
public class FormResetTagHelper : ModelAwareTagHelper
14+
{
15+
/// <summary>
16+
/// Button label (if text, otherwise make it the content within the button)
17+
/// </summary>
18+
public string Label { get; set; }
19+
20+
public Func<ButtonHtmlAttributes, ButtonHtmlAttributes> FluentAttrs { get; set; }
21+
22+
/// <inheritdoc />
23+
public override async Task ProcessWhileAwareOfModelTypeAsync<TModel>(TagHelperContext context, TagHelperOutput output)
24+
{
25+
var helper = ViewContext.GetHtmlHelper<TModel>();
26+
var n = helper.GetChameleonFormsNavigation();
27+
output.TagMode = TagMode.StartTagAndEndTag;
28+
output.TagName = null;
29+
var childContent = Label?.ToHtml() ?? await output.GetChildContentAsync(HtmlEncoder.Default);
30+
var buttonOutput = n.Reset(childContent);
31+
32+
FluentAttrs?.Invoke(buttonOutput);
33+
34+
var finalOutput = buttonOutput.Attrs(context.GetHtmlAttributes().Attributes);
35+
output.Content.SetHtmlContent(finalOutput);
36+
}
37+
}
38+
}

0 commit comments

Comments
 (0)