Skip to content

Commit 80c785f

Browse files
committed
Move Details to new component generation
1 parent fbacb90 commit 80c785f

File tree

11 files changed

+67
-56
lines changed

11 files changed

+67
-56
lines changed
Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,34 @@
11
using System;
2-
using HtmlTags;
32

43
namespace GovUk.Frontend.AspNetCore.ComponentGeneration;
4+
55
public partial class DefaultComponentGenerator
66
{
77
internal const string DetailsElement = "details";
88
internal const string DetailsSummaryElement = "summary";
99
internal const string DetailsTextElement = "div";
1010

1111
/// <inheritdoc/>
12-
public virtual HtmlTag GenerateDetails(DetailsOptions options)
12+
public virtual HtmlTagBuilder GenerateDetails(DetailsOptions options)
1313
{
1414
ArgumentNullException.ThrowIfNull(options);
1515
options.Validate();
1616

17-
return new HtmlTag(DetailsElement)
18-
.AddEncodedAttributeIfNotNull("id", options.Id)
19-
.AddClass("govuk-details")
20-
.AddClasses(ExplodeClasses(options.Classes))
21-
.AddEncodedAttributeIf(options.Open == true, "open", null)
22-
.MergeEncodedAttributes(options.Attributes)
23-
.Append(new HtmlTag(DetailsSummaryElement)
24-
.AddClass("govuk-details__summary")
25-
.MergeEncodedAttributes(options.SummaryAttributes)
26-
.Append(new HtmlTag("span")
27-
.AddClass("govuk-details__summary-text")
28-
.AppendHtml(GetEncodedTextOrHtml(options.SummaryText, options.SummaryHtml))))
29-
.Append(new HtmlTag(DetailsTextElement)
30-
.AddClass("govuk-details__text")
31-
.MergeEncodedAttributes(options.TextAttributes)
32-
.AppendHtml(GetEncodedTextOrHtml(options.Text, options.Html)));
17+
return new HtmlTagBuilder(DetailsElement)
18+
.WhenNotNull(options.Id, (id, b) => b.WithAttribute("id", id))
19+
.WithCssClass("govuk-details")
20+
.WithCssClasses(ExplodeClasses(options.Classes?.ToHtmlString()))
21+
.When(options.Open == true, b => b.WithBooleanAttribute("open"))
22+
.WithAttributes(options.Attributes)
23+
.WithAppendedHtml(new HtmlTagBuilder(DetailsSummaryElement)
24+
.WithCssClass("govuk-details__summary")
25+
.WithAttributes(options.SummaryAttributes)
26+
.WithAppendedHtml(new HtmlTagBuilder("span")
27+
.WithCssClass("govuk-details__summary-text")
28+
.WithAppendedHtml(GetEncodedTextOrHtml(options.SummaryText, options.SummaryHtml)!)))
29+
.WithAppendedHtml(new HtmlTagBuilder(DetailsTextElement)
30+
.WithCssClass("govuk-details__text")
31+
.WithAttributes(options.TextAttributes)
32+
.WithAppendedHtml(GetEncodedTextOrHtml(options.Text, options.Html)!));
3333
}
3434
}

src/GovUk.Frontend.AspNetCore/ComponentGeneration/DetailsOptions.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
1-
using System.Collections.Generic;
1+
using Microsoft.AspNetCore.Html;
22

33
namespace GovUk.Frontend.AspNetCore.ComponentGeneration;
44

55
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
66

77
public class DetailsOptions
88
{
9-
public string? Id { get; set; }
9+
public IHtmlContent? Id { get; set; }
1010
public bool? Open { get; set; }
11-
public string? SummaryHtml { get; set; }
11+
public IHtmlContent? SummaryHtml { get; set; }
1212
public string? SummaryText { get; set; }
13-
public string? Html { get; set; }
13+
public IHtmlContent? Html { get; set; }
1414
public string? Text { get; set; }
15-
public string? Classes { get; set; }
16-
public IReadOnlyDictionary<string, string?>? Attributes { get; set; }
15+
public IHtmlContent? Classes { get; set; }
16+
public EncodedAttributesDictionary? Attributes { get; set; }
1717

1818
[NonStandardParameter]
19-
internal IReadOnlyDictionary<string, string?>? SummaryAttributes { get; set; }
19+
internal EncodedAttributesDictionary? SummaryAttributes { get; set; }
2020
[NonStandardParameter]
21-
internal IReadOnlyDictionary<string, string?>? TextAttributes { get; set; }
21+
internal EncodedAttributesDictionary? TextAttributes { get; set; }
2222

2323
internal void Validate()
2424
{

src/GovUk.Frontend.AspNetCore/ComponentGeneration/HtmlTagBuilder.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,12 @@ public HtmlTagBuilder WithBooleanAttribute(string name)
7373

7474
/// <inheritdoc cref="EncodedAttributesDictionary.Add(EncodedAttributesDictionary)"/>
7575
/// <returns>This <see cref="HtmlTagBuilder"/> to allow calls to be chained.</returns>
76-
public HtmlTagBuilder WithAttributes(EncodedAttributesDictionary other)
76+
public HtmlTagBuilder WithAttributes(EncodedAttributesDictionary? other)
7777
{
78-
ArgumentNullException.ThrowIfNull(other);
78+
if (other is null)
79+
{
80+
return this;
81+
}
7982

8083
_attributes.Add(other);
8184

src/GovUk.Frontend.AspNetCore/ComponentGeneration/IComponentGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public interface IComponentGenerator
2929
/// Generates a details component.
3030
/// </summary>
3131
/// <returns>An <see cref="HtmlTag"/> with the component's HTML.</returns>
32-
HtmlTag GenerateDetails(DetailsOptions options);
32+
HtmlTagBuilder GenerateDetails(DetailsOptions options);
3333

3434
/// <summary>
3535
/// Generates an error message component.

src/GovUk.Frontend.AspNetCore/TagHelpers/DetailsContext.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,36 @@
11
using System;
22
using System.Collections.Immutable;
33
using System.Diagnostics.CodeAnalysis;
4+
using GovUk.Frontend.AspNetCore.ComponentGeneration;
5+
using Microsoft.AspNetCore.Html;
46

57
namespace GovUk.Frontend.AspNetCore.TagHelpers;
68

79
internal class DetailsContext
810
{
9-
internal record SummaryInfo(ImmutableDictionary<string, string?> Attributes, string Html);
11+
internal record SummaryInfo(EncodedAttributesDictionary Attributes, IHtmlContent Html);
1012

11-
internal record TextInfo(ImmutableDictionary<string, string?> Attributes, string Html);
13+
internal record TextInfo(EncodedAttributesDictionary Attributes, IHtmlContent Html);
1214

1315
// internal for testing
1416
internal SummaryInfo? Summary;
1517
internal TextInfo? Text;
1618

17-
public (ImmutableDictionary<string, string?> Attributes, string Html) GetSummaryOptions()
19+
public (EncodedAttributesDictionary Attributes, IHtmlContent Html) GetSummaryOptions()
1820
{
1921
ThrowIfNotComplete();
2022

2123
return (Summary.Attributes, Summary.Html);
2224
}
2325

24-
public (ImmutableDictionary<string, string?> Attributes, string Html) GetTextOptions()
26+
public (EncodedAttributesDictionary Attributes, IHtmlContent Html) GetTextOptions()
2527
{
2628
ThrowIfNotComplete();
2729

2830
return (Text.Attributes, Text.Html);
2931
}
3032

31-
public void SetSummary(ImmutableDictionary<string, string?> attributes, string html)
33+
public void SetSummary(EncodedAttributesDictionary attributes, IHtmlContent html)
3234
{
3335
ArgumentNullException.ThrowIfNull(attributes);
3436
ArgumentNullException.ThrowIfNull(html);
@@ -46,7 +48,7 @@ public void SetSummary(ImmutableDictionary<string, string?> attributes, string h
4648
Summary = new SummaryInfo(attributes, html);
4749
}
4850

49-
public void SetText(ImmutableDictionary<string, string?> attributes, string html)
51+
public void SetText(EncodedAttributesDictionary attributes, IHtmlContent html)
5052
{
5153
ArgumentNullException.ThrowIfNull(attributes);
5254
ArgumentNullException.ThrowIfNull(html);

src/GovUk.Frontend.AspNetCore/TagHelpers/DetailsSummaryTagHelper.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ public override async Task ProcessAsync(TagHelperContext context, TagHelperOutpu
1818
{
1919
var detailsContext = context.GetContextItem<DetailsContext>();
2020

21-
var content = await output.GetChildContentAsync();
21+
var content = (await output.GetChildContentAsync()).Snapshot();
2222

2323
if (output.Content.IsModified)
2424
{
2525
content = output.Content;
2626
}
2727

28-
detailsContext.SetSummary(output.Attributes.ToEncodedAttributeDictionary(), content.ToHtmlString());
28+
detailsContext.SetSummary(new EncodedAttributesDictionary(output.Attributes), content);
2929

3030
output.SuppressOutput();
3131
}

src/GovUk.Frontend.AspNetCore/TagHelpers/DetailsTagHelper.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Threading.Tasks;
33
using GovUk.Frontend.AspNetCore.ComponentGeneration;
4+
using Microsoft.AspNetCore.Html;
45
using Microsoft.AspNetCore.Razor.TagHelpers;
56

67
namespace GovUk.Frontend.AspNetCore.TagHelpers;
@@ -54,12 +55,12 @@ public override async Task ProcessAsync(TagHelperContext context, TagHelperOutpu
5455
var (summaryAttributes, summaryHtml) = detailsContext.GetSummaryOptions();
5556
var (textAttributes, textHtml) = detailsContext.GetTextOptions();
5657

57-
var attributes = output.Attributes.ToEncodedAttributeDictionary()
58-
.Remove("class", out var classes);
58+
var attributes = new EncodedAttributesDictionary(output.Attributes);
59+
attributes.Remove("class", out var classes);
5960

6061
var component = _componentGenerator.GenerateDetails(new DetailsOptions()
6162
{
62-
Id = Id,
63+
Id = Id is not null ? new HtmlString(Id) : null,
6364
Open = Open,
6465
SummaryHtml = summaryHtml,
6566
SummaryText = null,
@@ -71,6 +72,6 @@ public override async Task ProcessAsync(TagHelperContext context, TagHelperOutpu
7172
TextAttributes = textAttributes
7273
});
7374

74-
output.WriteComponent(component);
75+
component.WriteTo(output);
7576
}
7677
}

src/GovUk.Frontend.AspNetCore/TagHelpers/DetailsTextTagHelper.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ public override async Task ProcessAsync(TagHelperContext context, TagHelperOutpu
1818
{
1919
var detailsContext = context.GetContextItem<DetailsContext>();
2020

21-
var content = await output.GetChildContentAsync();
21+
var content = (await output.GetChildContentAsync()).Snapshot();
2222

2323
if (output.Content.IsModified)
2424
{
2525
content = output.Content;
2626
}
2727

28-
detailsContext.SetText(output.Attributes.ToEncodedAttributeDictionary(), content.ToHtmlString());
28+
detailsContext.SetText(new EncodedAttributesDictionary(output.Attributes), content);
2929

3030
output.SuppressOutput();
3131
}

tests/GovUk.Frontend.AspNetCore.Tests/TagHelpers/DetailsSummaryTagHelperTests.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
using System.Collections.Generic;
33
using System.Collections.Immutable;
44
using System.Threading.Tasks;
5+
using GovUk.Frontend.AspNetCore.ComponentGeneration;
56
using GovUk.Frontend.AspNetCore.TagHelpers;
7+
using Microsoft.AspNetCore.Html;
68
using Microsoft.AspNetCore.Razor.TagHelpers;
79
using Xunit;
810

@@ -42,15 +44,15 @@ public async Task ProcessAsync_SetsSummaryOnContext()
4244
await tagHelper.ProcessAsync(context, output);
4345

4446
// Assert
45-
Assert.Equal(summaryContent, detailsContext.Summary?.Html);
47+
Assert.Equal(summaryContent, detailsContext.Summary?.Html.ToHtmlString());
4648
}
4749

4850
[Fact]
4951
public async Task ProcessAsync_ParentAlreadyHasSummary_ThrowsInvalidOperationException()
5052
{
5153
// Arrange
5254
var detailsContext = new DetailsContext();
53-
detailsContext.SetSummary(ImmutableDictionary<string, string?>.Empty, "Existing summary");
55+
detailsContext.SetSummary(new EncodedAttributesDictionary(), new HtmlString("Existing summary"));
5456

5557
var context = new TagHelperContext(
5658
tagName: "govuk-details-summary",
@@ -86,7 +88,7 @@ public async Task ProcessAsync_ParentAlreadyHasText_ThrowsInvalidOperationExcept
8688
{
8789
// Arrange
8890
var detailsContext = new DetailsContext();
89-
detailsContext.SetText(ImmutableDictionary<string, string?>.Empty, "Existing text");
91+
detailsContext.SetText(new EncodedAttributesDictionary(), new HtmlString("Existing text"));
9092

9193
var context = new TagHelperContext(
9294
tagName: "govuk-details-summary",

tests/GovUk.Frontend.AspNetCore.Tests/TagHelpers/DetailsTagHelperTests.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Threading.Tasks;
44
using GovUk.Frontend.AspNetCore.ComponentGeneration;
55
using GovUk.Frontend.AspNetCore.TagHelpers;
6+
using Microsoft.AspNetCore.Html;
67
using Microsoft.AspNetCore.Razor.TagHelpers;
78
using Moq;
89
using Xunit;
@@ -39,9 +40,9 @@ public async Task ProcessAsync_InvokesComponentGeneratorWithExpectedOptions()
3940
{
4041
var detailsContext = context.GetContextItem<DetailsContext>();
4142

42-
detailsContext.SetSummary(ImmutableDictionary<string, string?>.Empty, summaryHtml);
43+
detailsContext.SetSummary(new EncodedAttributesDictionary(), new HtmlString(summaryHtml));
4344

44-
detailsContext.SetText(ImmutableDictionary<string, string?>.Empty, content);
45+
detailsContext.SetText(new EncodedAttributesDictionary(), new HtmlString(content));
4546

4647
var tagHelperContent = new DefaultTagHelperContent();
4748
return Task.FromResult<TagHelperContent>(tagHelperContent);
@@ -62,13 +63,13 @@ public async Task ProcessAsync_InvokesComponentGeneratorWithExpectedOptions()
6263

6364
// Assert
6465
Assert.NotNull(actualOptions);
65-
Assert.Equal(id, actualOptions!.Id);
66+
Assert.Equal(id, actualOptions!.Id?.ToHtmlString());
6667
Assert.Equal(open, actualOptions.Open);
67-
Assert.Equal(summaryHtml, actualOptions.SummaryHtml);
68+
Assert.Equal(summaryHtml, actualOptions.SummaryHtml?.ToHtmlString());
6869
Assert.Null(actualOptions.SummaryText);
69-
Assert.Equal(content, actualOptions.Html);
70+
Assert.Equal(content, actualOptions.Html?.ToHtmlString());
7071
Assert.Null(actualOptions.Text);
71-
Assert.Equal(classes, actualOptions.Classes);
72+
Assert.Equal(classes, actualOptions.Classes?.ToHtmlString());
7273
Assert.NotNull(actualOptions.Attributes);
7374
Assert.Collection(actualOptions.Attributes, kvp =>
7475
{

0 commit comments

Comments
 (0)