Skip to content

Commit

Permalink
Move Details to new component generation
Browse files Browse the repository at this point in the history
  • Loading branch information
gunndabad committed Dec 22, 2024
1 parent 888bfb0 commit 2397a5d
Show file tree
Hide file tree
Showing 11 changed files with 67 additions and 56 deletions.
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
using System;
using HtmlTags;

namespace GovUk.Frontend.AspNetCore.ComponentGeneration;

public partial class DefaultComponentGenerator
{
internal const string DetailsElement = "details";
internal const string DetailsSummaryElement = "summary";
internal const string DetailsTextElement = "div";

/// <inheritdoc/>
public virtual HtmlTag GenerateDetails(DetailsOptions options)
public virtual HtmlTagBuilder GenerateDetails(DetailsOptions options)
{
ArgumentNullException.ThrowIfNull(options);
options.Validate();

return new HtmlTag(DetailsElement)
.AddEncodedAttributeIfNotNull("id", options.Id)
.AddClass("govuk-details")
.AddClasses(ExplodeClasses(options.Classes))
.AddEncodedAttributeIf(options.Open == true, "open", null)
.MergeEncodedAttributes(options.Attributes)
.Append(new HtmlTag(DetailsSummaryElement)
.AddClass("govuk-details__summary")
.MergeEncodedAttributes(options.SummaryAttributes)
.Append(new HtmlTag("span")
.AddClass("govuk-details__summary-text")
.AppendHtml(GetEncodedTextOrHtml(options.SummaryText, options.SummaryHtml))))
.Append(new HtmlTag(DetailsTextElement)
.AddClass("govuk-details__text")
.MergeEncodedAttributes(options.TextAttributes)
.AppendHtml(GetEncodedTextOrHtml(options.Text, options.Html)));
return new HtmlTagBuilder(DetailsElement)
.WhenNotNull(options.Id, (id, b) => b.WithAttribute("id", id))
.WithCssClass("govuk-details")
.WithCssClasses(ExplodeClasses(options.Classes?.ToHtmlString()))
.When(options.Open == true, b => b.WithBooleanAttribute("open"))
.WithAttributes(options.Attributes)
.WithAppendedHtml(new HtmlTagBuilder(DetailsSummaryElement)
.WithCssClass("govuk-details__summary")
.WithAttributes(options.SummaryAttributes)
.WithAppendedHtml(new HtmlTagBuilder("span")
.WithCssClass("govuk-details__summary-text")
.WithAppendedHtml(GetEncodedTextOrHtml(options.SummaryText, options.SummaryHtml)!)))
.WithAppendedHtml(new HtmlTagBuilder(DetailsTextElement)
.WithCssClass("govuk-details__text")
.WithAttributes(options.TextAttributes)
.WithAppendedHtml(GetEncodedTextOrHtml(options.Text, options.Html)!));
}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Html;

namespace GovUk.Frontend.AspNetCore.ComponentGeneration;

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

public class DetailsOptions
{
public string? Id { get; set; }
public IHtmlContent? Id { get; set; }
public bool? Open { get; set; }
public string? SummaryHtml { get; set; }
public IHtmlContent? SummaryHtml { get; set; }
public string? SummaryText { get; set; }
public string? Html { get; set; }
public IHtmlContent? Html { get; set; }
public string? Text { get; set; }
public string? Classes { get; set; }
public IReadOnlyDictionary<string, string?>? Attributes { get; set; }
public IHtmlContent? Classes { get; set; }
public EncodedAttributesDictionary? Attributes { get; set; }

[NonStandardParameter]
internal IReadOnlyDictionary<string, string?>? SummaryAttributes { get; set; }
internal EncodedAttributesDictionary? SummaryAttributes { get; set; }
[NonStandardParameter]
internal IReadOnlyDictionary<string, string?>? TextAttributes { get; set; }
internal EncodedAttributesDictionary? TextAttributes { get; set; }

internal void Validate()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,12 @@ public HtmlTagBuilder WithBooleanAttribute(string name)

/// <inheritdoc cref="EncodedAttributesDictionary.Add(EncodedAttributesDictionary)"/>
/// <returns>This <see cref="HtmlTagBuilder"/> to allow calls to be chained.</returns>
public HtmlTagBuilder WithAttributes(EncodedAttributesDictionary other)
public HtmlTagBuilder WithAttributes(EncodedAttributesDictionary? other)
{
ArgumentNullException.ThrowIfNull(other);
if (other is null)
{
return this;
}

_attributes.Add(other);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public interface IComponentGenerator
/// Generates a details component.
/// </summary>
/// <returns>An <see cref="HtmlTag"/> with the component's HTML.</returns>
HtmlTag GenerateDetails(DetailsOptions options);
HtmlTagBuilder GenerateDetails(DetailsOptions options);

/// <summary>
/// Generates an error message component.
Expand Down
14 changes: 8 additions & 6 deletions src/GovUk.Frontend.AspNetCore/TagHelpers/DetailsContext.cs
Original file line number Diff line number Diff line change
@@ -1,34 +1,36 @@
using System;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using GovUk.Frontend.AspNetCore.ComponentGeneration;
using Microsoft.AspNetCore.Html;

namespace GovUk.Frontend.AspNetCore.TagHelpers;

internal class DetailsContext
{
internal record SummaryInfo(ImmutableDictionary<string, string?> Attributes, string Html);
internal record SummaryInfo(EncodedAttributesDictionary Attributes, IHtmlContent Html);

internal record TextInfo(ImmutableDictionary<string, string?> Attributes, string Html);
internal record TextInfo(EncodedAttributesDictionary Attributes, IHtmlContent Html);

// internal for testing
internal SummaryInfo? Summary;
internal TextInfo? Text;

public (ImmutableDictionary<string, string?> Attributes, string Html) GetSummaryOptions()
public (EncodedAttributesDictionary Attributes, IHtmlContent Html) GetSummaryOptions()
{
ThrowIfNotComplete();

return (Summary.Attributes, Summary.Html);
}

public (ImmutableDictionary<string, string?> Attributes, string Html) GetTextOptions()
public (EncodedAttributesDictionary Attributes, IHtmlContent Html) GetTextOptions()
{
ThrowIfNotComplete();

return (Text.Attributes, Text.Html);
}

public void SetSummary(ImmutableDictionary<string, string?> attributes, string html)
public void SetSummary(EncodedAttributesDictionary attributes, IHtmlContent html)
{
ArgumentNullException.ThrowIfNull(attributes);
ArgumentNullException.ThrowIfNull(html);
Expand All @@ -46,7 +48,7 @@ public void SetSummary(ImmutableDictionary<string, string?> attributes, string h
Summary = new SummaryInfo(attributes, html);
}

public void SetText(ImmutableDictionary<string, string?> attributes, string html)
public void SetText(EncodedAttributesDictionary attributes, IHtmlContent html)
{
ArgumentNullException.ThrowIfNull(attributes);
ArgumentNullException.ThrowIfNull(html);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ public override async Task ProcessAsync(TagHelperContext context, TagHelperOutpu
{
var detailsContext = context.GetContextItem<DetailsContext>();

var content = await output.GetChildContentAsync();
var content = (await output.GetChildContentAsync()).Snapshot();

if (output.Content.IsModified)
{
content = output.Content;
}

detailsContext.SetSummary(output.Attributes.ToEncodedAttributeDictionary(), content.ToHtmlString());
detailsContext.SetSummary(new EncodedAttributesDictionary(output.Attributes), content);

output.SuppressOutput();
}
Expand Down
9 changes: 5 additions & 4 deletions src/GovUk.Frontend.AspNetCore/TagHelpers/DetailsTagHelper.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Threading.Tasks;
using GovUk.Frontend.AspNetCore.ComponentGeneration;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Razor.TagHelpers;

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

var attributes = output.Attributes.ToEncodedAttributeDictionary()
.Remove("class", out var classes);
var attributes = new EncodedAttributesDictionary(output.Attributes);
attributes.Remove("class", out var classes);

var component = _componentGenerator.GenerateDetails(new DetailsOptions()
{
Id = Id,
Id = Id is not null ? new HtmlString(Id) : null,
Open = Open,
SummaryHtml = summaryHtml,
SummaryText = null,
Expand All @@ -71,6 +72,6 @@ public override async Task ProcessAsync(TagHelperContext context, TagHelperOutpu
TextAttributes = textAttributes
});

output.WriteComponent(component);
component.WriteTo(output);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ public override async Task ProcessAsync(TagHelperContext context, TagHelperOutpu
{
var detailsContext = context.GetContextItem<DetailsContext>();

var content = await output.GetChildContentAsync();
var content = (await output.GetChildContentAsync()).Snapshot();

if (output.Content.IsModified)
{
content = output.Content;
}

detailsContext.SetText(output.Attributes.ToEncodedAttributeDictionary(), content.ToHtmlString());
detailsContext.SetText(new EncodedAttributesDictionary(output.Attributes), content);

output.SuppressOutput();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading.Tasks;
using GovUk.Frontend.AspNetCore.ComponentGeneration;
using GovUk.Frontend.AspNetCore.TagHelpers;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Xunit;

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

// Assert
Assert.Equal(summaryContent, detailsContext.Summary?.Html);
Assert.Equal(summaryContent, detailsContext.Summary?.Html.ToHtmlString());
}

[Fact]
public async Task ProcessAsync_ParentAlreadyHasSummary_ThrowsInvalidOperationException()
{
// Arrange
var detailsContext = new DetailsContext();
detailsContext.SetSummary(ImmutableDictionary<string, string?>.Empty, "Existing summary");
detailsContext.SetSummary(new EncodedAttributesDictionary(), new HtmlString("Existing summary"));

var context = new TagHelperContext(
tagName: "govuk-details-summary",
Expand Down Expand Up @@ -86,7 +88,7 @@ public async Task ProcessAsync_ParentAlreadyHasText_ThrowsInvalidOperationExcept
{
// Arrange
var detailsContext = new DetailsContext();
detailsContext.SetText(ImmutableDictionary<string, string?>.Empty, "Existing text");
detailsContext.SetText(new EncodedAttributesDictionary(), new HtmlString("Existing text"));

var context = new TagHelperContext(
tagName: "govuk-details-summary",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Threading.Tasks;
using GovUk.Frontend.AspNetCore.ComponentGeneration;
using GovUk.Frontend.AspNetCore.TagHelpers;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Moq;
using Xunit;
Expand Down Expand Up @@ -39,9 +40,9 @@ public async Task ProcessAsync_InvokesComponentGeneratorWithExpectedOptions()
{
var detailsContext = context.GetContextItem<DetailsContext>();

detailsContext.SetSummary(ImmutableDictionary<string, string?>.Empty, summaryHtml);
detailsContext.SetSummary(new EncodedAttributesDictionary(), new HtmlString(summaryHtml));

detailsContext.SetText(ImmutableDictionary<string, string?>.Empty, content);
detailsContext.SetText(new EncodedAttributesDictionary(), new HtmlString(content));

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

// Assert
Assert.NotNull(actualOptions);
Assert.Equal(id, actualOptions!.Id);
Assert.Equal(id, actualOptions!.Id?.ToHtmlString());
Assert.Equal(open, actualOptions.Open);
Assert.Equal(summaryHtml, actualOptions.SummaryHtml);
Assert.Equal(summaryHtml, actualOptions.SummaryHtml?.ToHtmlString());
Assert.Null(actualOptions.SummaryText);
Assert.Equal(content, actualOptions.Html);
Assert.Equal(content, actualOptions.Html?.ToHtmlString());
Assert.Null(actualOptions.Text);
Assert.Equal(classes, actualOptions.Classes);
Assert.Equal(classes, actualOptions.Classes?.ToHtmlString());
Assert.NotNull(actualOptions.Attributes);
Assert.Collection(actualOptions.Attributes, kvp =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading.Tasks;
using GovUk.Frontend.AspNetCore.ComponentGeneration;
using GovUk.Frontend.AspNetCore.TagHelpers;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Xunit;

Expand All @@ -15,7 +17,7 @@ public async Task ProcessAsync_SetsContentOnContext()
{
// Arrange
var detailsContext = new DetailsContext();
detailsContext.SetSummary(ImmutableDictionary<string, string?>.Empty, "The summary");
detailsContext.SetSummary(new EncodedAttributesDictionary(), new HtmlString("The summary"));
var textContent = "The text";

var context = new TagHelperContext(
Expand Down Expand Up @@ -43,16 +45,16 @@ public async Task ProcessAsync_SetsContentOnContext()
await tagHelper.ProcessAsync(context, output);

// Assert
Assert.Equal(textContent, detailsContext.Text?.Html);
Assert.Equal(textContent, detailsContext.Text?.Html.ToHtmlString());
}

[Fact]
public async Task ProcessAsync_ParentAlreadyHasText_ThrowsInvalidOperationException()
{
// Arrange
var detailsContext = new DetailsContext();
detailsContext.SetSummary(ImmutableDictionary<string, string?>.Empty, "The summary");
detailsContext.SetText(ImmutableDictionary<string, string?>.Empty, "Existing text");
detailsContext.SetSummary(new EncodedAttributesDictionary(), new HtmlString("The summary"));
detailsContext.SetText(new EncodedAttributesDictionary(), new HtmlString("Existing text"));

var context = new TagHelperContext(
tagName: "govuk-details-text",
Expand Down

0 comments on commit 2397a5d

Please sign in to comment.