Skip to content

Commit ad5a0af

Browse files
Merge pull request #21 from IowaComputerGurus/feature/card-header-buttons
Fixes #20 by adding back the helper
2 parents 9e770d2 + 82cc2bb commit ad5a0af

File tree

4 files changed

+163
-5
lines changed

4 files changed

+163
-5
lines changed

src/AspNetCore.Utilities.Bootstrap5TagHelpers.Sample/Views/Home/Index.cshtml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -289,8 +289,8 @@
289289
<card>
290290
<card-header title="Testing All Options">
291291
<card-header-actions>
292-
<a href="https://www.google.com" class="btn btn-primary">Google</a>
293-
<a href="https://www.microsoft.com" class="btn btn-secondary">Microsoft</a>
292+
<a href="https://www.google.com" class="btn btn-sm btn-primary me-1">Google</a>
293+
<a href="https://www.microsoft.com" class="btn btn-sm btn-secondary">Microsoft</a>
294294
</card-header-actions>
295295
</card-header>
296296
<card-body>
@@ -306,8 +306,8 @@
306306
&lt;card&gt;
307307
&lt;card-header title=&quot;Testing All Options&quot;&gt;
308308
&lt;card-header-actions&gt;
309-
&lt;a href=&quot;https://www.google.com&quot; class=&quot;btn btn-primary&quot;&gt;Google&lt;/a&gt;
310-
&lt;a href=&quot;https://www.microsoft.com&quot; class=&quot;btn btn-secondary&quot;&gt;Microsoft&lt;/a&gt;
309+
&lt;a href=&quot;https://www.google.com&quot; class=&quot;btn btn-sm btn-primary me-1&quot;&gt;Google&lt;/a&gt;
310+
&lt;a href=&quot;https://www.microsoft.com&quot; class=&quot;btn btn-sm btn-secondary&quot;&gt;Microsoft&lt;/a&gt;
311311
&lt;/card-header-actions&gt;
312312
&lt;/card-header&gt;
313313
&lt;card-body&gt;
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
using ICG.AspNetCore.Utilities.Bootstrap5TagHelpers.Card;
2+
using ICG.AspNetCore.Utilities.Bootstrap5TagHelpers.Contexts;
3+
using Microsoft.AspNetCore.Razor.TagHelpers;
4+
5+
namespace ICG.AspNetCore.Utilities.Bootstrap5TagHelpers.Tests.Card;
6+
public class CardHeaderActionsTagHelperTests : AbstractTagHelperTest
7+
{
8+
[Fact]
9+
public async Task Should_ThrowException_WhenMissingContext()
10+
{
11+
//Arrange
12+
var context = MakeTagHelperContext();
13+
var output = MakeTagHelperOutput(" ");
14+
15+
//Act
16+
var helper = new CardHeaderActionsTagHelper();
17+
var exceptionResult = await Record.ExceptionAsync(() => helper.ProcessAsync(context, output));
18+
19+
Assert.NotNull(exceptionResult);
20+
Assert.IsType<KeyNotFoundException>(exceptionResult);
21+
}
22+
23+
[Fact]
24+
public async Task Should_ThrowException_WhenContextIsNull()
25+
{
26+
//Arrange
27+
var context = MakeTagHelperContext();
28+
context.Items.Add(typeof(CardContext), null);
29+
var output = MakeTagHelperOutput(" ");
30+
31+
//Act
32+
var helper = new CardHeaderActionsTagHelper();
33+
var exceptionResult = await Record.ExceptionAsync(() => helper.ProcessAsync(context, output));
34+
35+
Assert.NotNull(exceptionResult);
36+
Assert.IsType<ArgumentException>(exceptionResult);
37+
}
38+
39+
[Theory]
40+
[InlineData("d-flex")]
41+
[InlineData("flex-nowrap")]
42+
[InlineData("mt-2")]
43+
[InlineData("mt-sm-0")]
44+
public async Task Should_Render_With_ClassAdded(string expectedClass)
45+
{
46+
//Arrange
47+
var context = MakeTagHelperContext();
48+
context.Items.Add(typeof(CardContext), new CardContext());
49+
var output = MakeTagHelperOutput(" ");
50+
51+
//Act
52+
var helper = new CardHeaderActionsTagHelper();
53+
await helper.ProcessAsync(context, output);
54+
55+
//Assert
56+
var classValue = output.Attributes["class"].Value;
57+
Assert.NotNull(classValue);
58+
var classString = classValue.ToString();
59+
Assert.True(classString?.Contains(expectedClass));
60+
}
61+
62+
[Fact]
63+
public async Task Should_Render_With_ClassAdded_PreservingCustomClasses()
64+
{
65+
//Arrange
66+
var customClass = "testing-out";
67+
var expectedClass = $"{customClass} d-flex flex-nowrap mt-2 mt-sm-0";
68+
var existingAttributes = new TagHelperAttributeList(new List<TagHelperAttribute>
69+
{new("class", customClass)});
70+
var context = MakeTagHelperContext();
71+
context.Items.Add(typeof(CardContext), new CardContext());
72+
var output = MakeTagHelperOutput(" ", existingAttributes);
73+
74+
//Act
75+
var helper = new CardHeaderActionsTagHelper();
76+
await helper.ProcessAsync(context, output);
77+
78+
//Assert
79+
Assert.Equal(expectedClass, output.Attributes["class"].Value.ToString());
80+
}
81+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using ICG.AspNetCore.Utilities.Bootstrap5TagHelpers.Contexts;
2+
using Microsoft.AspNetCore.Mvc.TagHelpers;
3+
using Microsoft.AspNetCore.Razor.TagHelpers;
4+
using System.Text.Encodings.Web;
5+
using System.Threading.Tasks;
6+
using System;
7+
8+
namespace ICG.AspNetCore.Utilities.Bootstrap5TagHelpers.Card;
9+
10+
/// <summary>
11+
/// Helper for rendering actions within the header of a card
12+
/// </summary>
13+
[RestrictChildren("button", "a")]
14+
public class CardHeaderActionsTagHelper : TagHelper
15+
{
16+
/// <summary>
17+
/// Renders the control
18+
/// </summary>
19+
/// <param name="context"></param>
20+
/// <param name="output"></param>
21+
/// <returns></returns>
22+
/// <exception cref="ArgumentException"></exception>
23+
public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
24+
{
25+
//Get the context information
26+
var cardContext = context.Items[typeof(CardContext)] as CardContext;
27+
if (cardContext == null)
28+
throw new ArgumentException("CardContext is not specified in context parameter");
29+
30+
return ProcessAsyncInternal(output);
31+
}
32+
33+
/// <summary>
34+
/// Internal implementation
35+
/// </summary>
36+
/// <param name="output"></param>
37+
/// <returns></returns>
38+
private static async Task ProcessAsyncInternal(TagHelperOutput output)
39+
{
40+
output.TagName = "div";
41+
output.AddClass("d-flex", HtmlEncoder.Default);
42+
output.AddClass("flex-nowrap", HtmlEncoder.Default);
43+
output.AddClass("mt-2", HtmlEncoder.Default);
44+
output.AddClass("mt-sm-0", HtmlEncoder.Default);
45+
46+
var content = (await output.GetChildContentAsync()).GetContent();
47+
48+
output.Content.AppendHtml(content);
49+
}
50+
}

src/AspNetCore.Utilities.Bootstrap5TagHelpers/Card/CardHeaderTagHelper.cs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
using ICG.AspNetCore.Utilities.Bootstrap5TagHelpers.Contexts;
2+
using Microsoft.AspNetCore.Components.Web;
23
using Microsoft.AspNetCore.Mvc.Rendering;
34
using Microsoft.AspNetCore.Mvc.TagHelpers;
45
using Microsoft.AspNetCore.Razor.TagHelpers;
56
using System;
67
using System.Text.Encodings.Web;
8+
using System.Threading.Tasks;
79

810
namespace ICG.AspNetCore.Utilities.Bootstrap5TagHelpers.Card
911
{
@@ -18,23 +20,42 @@ public class CardHeaderTagHelper : TagHelper
1820
/// </summary>
1921
public string Title { get; set; }
2022

23+
2124
/// <summary>
2225
/// Renders the header for a bootstrap card
2326
/// </summary>
2427
/// <param name="context"></param>
2528
/// <param name="output"></param>
2629
/// <returns></returns>
2730
/// <exception cref="ArgumentException"></exception>
28-
public override void Process(TagHelperContext context, TagHelperOutput output)
31+
public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
2932
{
3033
//Get the context information
3134
if (context.Items[typeof(CardContext)] is not CardContext cardContext)
3235
throw new ArgumentException("CardContext is not specified in context parameter");
3336

37+
return ProcessAsyncInternal(output, cardContext);
38+
}
39+
40+
private async Task ProcessAsyncInternal(TagHelperOutput output, CardContext cardContext)
41+
{
3442
//Setup basic tag information
3543
output.TagName = "div";
3644
output.AddClass("card-header", HtmlEncoder.Default);
3745

46+
//Get sub controls if we need them
47+
var body = (await output.GetChildContentAsync()).GetContent();
48+
body = body.Trim();
49+
50+
if (!string.IsNullOrWhiteSpace(body))
51+
{
52+
output.AddClass("d-flex", HtmlEncoder.Default);
53+
output.AddClass("flex-column", HtmlEncoder.Default);
54+
output.AddClass("flex-sm-row", HtmlEncoder.Default);
55+
output.AddClass("align-items-sm-center", HtmlEncoder.Default);
56+
output.AddClass("justify-content-between", HtmlEncoder.Default);
57+
}
58+
3859
//If we have an id make a custom span
3960
if (!string.IsNullOrEmpty(cardContext.Id))
4061
{
@@ -47,6 +68,12 @@ public override void Process(TagHelperContext context, TagHelperOutput output)
4768
{
4869
output.Content.AppendHtml(Title);
4970
}
71+
72+
73+
//Add sub-content after our title
74+
if (!string.IsNullOrEmpty(body))
75+
output.Content.AppendHtml(body);
76+
5077
}
5178
}
5279
}

0 commit comments

Comments
 (0)