From 82cc2bb627387c36dd067f16214f6f7bab1f9718 Mon Sep 17 00:00:00 2001 From: Mitchel Sellers Date: Thu, 3 Aug 2023 17:08:56 -0500 Subject: [PATCH] Fixes #20 by adding back the helper --- .../Views/Home/Index.cshtml | 8 +- .../Card/CardHeaderActionsTagHelperTests.cs | 81 +++++++++++++++++++ .../Card/CardHeaderActionsTagHelper.cs | 50 ++++++++++++ .../Card/CardHeaderTagHelper.cs | 29 ++++++- 4 files changed, 163 insertions(+), 5 deletions(-) create mode 100644 src/AspNetCore.Utilities.Bootstrap5TagHelpers.Tests/Card/CardHeaderActionsTagHelperTests.cs create mode 100644 src/AspNetCore.Utilities.Bootstrap5TagHelpers/Card/CardHeaderActionsTagHelper.cs diff --git a/src/AspNetCore.Utilities.Bootstrap5TagHelpers.Sample/Views/Home/Index.cshtml b/src/AspNetCore.Utilities.Bootstrap5TagHelpers.Sample/Views/Home/Index.cshtml index 4b0ec6a..f5d3623 100644 --- a/src/AspNetCore.Utilities.Bootstrap5TagHelpers.Sample/Views/Home/Index.cshtml +++ b/src/AspNetCore.Utilities.Bootstrap5TagHelpers.Sample/Views/Home/Index.cshtml @@ -289,8 +289,8 @@ - Google - Microsoft + Google + Microsoft @@ -306,8 +306,8 @@ <card> <card-header title="Testing All Options"> <card-header-actions> - <a href="https://www.google.com" class="btn btn-primary">Google</a> - <a href="https://www.microsoft.com" class="btn btn-secondary">Microsoft</a> + <a href="https://www.google.com" class="btn btn-sm btn-primary me-1">Google</a> + <a href="https://www.microsoft.com" class="btn btn-sm btn-secondary">Microsoft</a> </card-header-actions> </card-header> <card-body> diff --git a/src/AspNetCore.Utilities.Bootstrap5TagHelpers.Tests/Card/CardHeaderActionsTagHelperTests.cs b/src/AspNetCore.Utilities.Bootstrap5TagHelpers.Tests/Card/CardHeaderActionsTagHelperTests.cs new file mode 100644 index 0000000..c51e0b2 --- /dev/null +++ b/src/AspNetCore.Utilities.Bootstrap5TagHelpers.Tests/Card/CardHeaderActionsTagHelperTests.cs @@ -0,0 +1,81 @@ +using ICG.AspNetCore.Utilities.Bootstrap5TagHelpers.Card; +using ICG.AspNetCore.Utilities.Bootstrap5TagHelpers.Contexts; +using Microsoft.AspNetCore.Razor.TagHelpers; + +namespace ICG.AspNetCore.Utilities.Bootstrap5TagHelpers.Tests.Card; +public class CardHeaderActionsTagHelperTests : AbstractTagHelperTest +{ + [Fact] + public async Task Should_ThrowException_WhenMissingContext() + { + //Arrange + var context = MakeTagHelperContext(); + var output = MakeTagHelperOutput(" "); + + //Act + var helper = new CardHeaderActionsTagHelper(); + var exceptionResult = await Record.ExceptionAsync(() => helper.ProcessAsync(context, output)); + + Assert.NotNull(exceptionResult); + Assert.IsType(exceptionResult); + } + + [Fact] + public async Task Should_ThrowException_WhenContextIsNull() + { + //Arrange + var context = MakeTagHelperContext(); + context.Items.Add(typeof(CardContext), null); + var output = MakeTagHelperOutput(" "); + + //Act + var helper = new CardHeaderActionsTagHelper(); + var exceptionResult = await Record.ExceptionAsync(() => helper.ProcessAsync(context, output)); + + Assert.NotNull(exceptionResult); + Assert.IsType(exceptionResult); + } + + [Theory] + [InlineData("d-flex")] + [InlineData("flex-nowrap")] + [InlineData("mt-2")] + [InlineData("mt-sm-0")] + public async Task Should_Render_With_ClassAdded(string expectedClass) + { + //Arrange + var context = MakeTagHelperContext(); + context.Items.Add(typeof(CardContext), new CardContext()); + var output = MakeTagHelperOutput(" "); + + //Act + var helper = new CardHeaderActionsTagHelper(); + await helper.ProcessAsync(context, output); + + //Assert + var classValue = output.Attributes["class"].Value; + Assert.NotNull(classValue); + var classString = classValue.ToString(); + Assert.True(classString?.Contains(expectedClass)); + } + + [Fact] + public async Task Should_Render_With_ClassAdded_PreservingCustomClasses() + { + //Arrange + var customClass = "testing-out"; + var expectedClass = $"{customClass} d-flex flex-nowrap mt-2 mt-sm-0"; + var existingAttributes = new TagHelperAttributeList(new List + {new("class", customClass)}); + var context = MakeTagHelperContext(); + context.Items.Add(typeof(CardContext), new CardContext()); + var output = MakeTagHelperOutput(" ", existingAttributes); + + //Act + var helper = new CardHeaderActionsTagHelper(); + await helper.ProcessAsync(context, output); + + //Assert + Assert.Equal(expectedClass, output.Attributes["class"].Value.ToString()); + } +} \ No newline at end of file diff --git a/src/AspNetCore.Utilities.Bootstrap5TagHelpers/Card/CardHeaderActionsTagHelper.cs b/src/AspNetCore.Utilities.Bootstrap5TagHelpers/Card/CardHeaderActionsTagHelper.cs new file mode 100644 index 0000000..b49567e --- /dev/null +++ b/src/AspNetCore.Utilities.Bootstrap5TagHelpers/Card/CardHeaderActionsTagHelper.cs @@ -0,0 +1,50 @@ +using ICG.AspNetCore.Utilities.Bootstrap5TagHelpers.Contexts; +using Microsoft.AspNetCore.Mvc.TagHelpers; +using Microsoft.AspNetCore.Razor.TagHelpers; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using System; + +namespace ICG.AspNetCore.Utilities.Bootstrap5TagHelpers.Card; + +/// +/// Helper for rendering actions within the header of a card +/// +[RestrictChildren("button", "a")] +public class CardHeaderActionsTagHelper : TagHelper +{ + /// + /// Renders the control + /// + /// + /// + /// + /// + public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output) + { + //Get the context information + var cardContext = context.Items[typeof(CardContext)] as CardContext; + if (cardContext == null) + throw new ArgumentException("CardContext is not specified in context parameter"); + + return ProcessAsyncInternal(output); + } + + /// + /// Internal implementation + /// + /// + /// + private static async Task ProcessAsyncInternal(TagHelperOutput output) + { + output.TagName = "div"; + output.AddClass("d-flex", HtmlEncoder.Default); + output.AddClass("flex-nowrap", HtmlEncoder.Default); + output.AddClass("mt-2", HtmlEncoder.Default); + output.AddClass("mt-sm-0", HtmlEncoder.Default); + + var content = (await output.GetChildContentAsync()).GetContent(); + + output.Content.AppendHtml(content); + } +} \ No newline at end of file diff --git a/src/AspNetCore.Utilities.Bootstrap5TagHelpers/Card/CardHeaderTagHelper.cs b/src/AspNetCore.Utilities.Bootstrap5TagHelpers/Card/CardHeaderTagHelper.cs index 18f7d5f..ed756b1 100644 --- a/src/AspNetCore.Utilities.Bootstrap5TagHelpers/Card/CardHeaderTagHelper.cs +++ b/src/AspNetCore.Utilities.Bootstrap5TagHelpers/Card/CardHeaderTagHelper.cs @@ -1,9 +1,11 @@ using ICG.AspNetCore.Utilities.Bootstrap5TagHelpers.Contexts; +using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.TagHelpers; using Microsoft.AspNetCore.Razor.TagHelpers; using System; using System.Text.Encodings.Web; +using System.Threading.Tasks; namespace ICG.AspNetCore.Utilities.Bootstrap5TagHelpers.Card { @@ -18,6 +20,7 @@ public class CardHeaderTagHelper : TagHelper /// public string Title { get; set; } + /// /// Renders the header for a bootstrap card /// @@ -25,16 +28,34 @@ public class CardHeaderTagHelper : TagHelper /// /// /// - public override void Process(TagHelperContext context, TagHelperOutput output) + public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { //Get the context information if (context.Items[typeof(CardContext)] is not CardContext cardContext) throw new ArgumentException("CardContext is not specified in context parameter"); + return ProcessAsyncInternal(output, cardContext); + } + + private async Task ProcessAsyncInternal(TagHelperOutput output, CardContext cardContext) + { //Setup basic tag information output.TagName = "div"; output.AddClass("card-header", HtmlEncoder.Default); + //Get sub controls if we need them + var body = (await output.GetChildContentAsync()).GetContent(); + body = body.Trim(); + + if (!string.IsNullOrWhiteSpace(body)) + { + output.AddClass("d-flex", HtmlEncoder.Default); + output.AddClass("flex-column", HtmlEncoder.Default); + output.AddClass("flex-sm-row", HtmlEncoder.Default); + output.AddClass("align-items-sm-center", HtmlEncoder.Default); + output.AddClass("justify-content-between", HtmlEncoder.Default); + } + //If we have an id make a custom span if (!string.IsNullOrEmpty(cardContext.Id)) { @@ -47,6 +68,12 @@ public override void Process(TagHelperContext context, TagHelperOutput output) { output.Content.AppendHtml(Title); } + + + //Add sub-content after our title + if (!string.IsNullOrEmpty(body)) + output.Content.AppendHtml(body); + } } }