Skip to content

Commit 5656354

Browse files
committed
Port Back link to new component generation
1 parent 9ea77bc commit 5656354

File tree

8 files changed

+84
-122
lines changed

8 files changed

+84
-122
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System;
2+
using Microsoft.AspNetCore.Html;
3+
4+
namespace GovUk.Frontend.AspNetCore.ComponentGeneration;
5+
6+
public partial class DefaultComponentGenerator
7+
{
8+
internal const string BackLinkElement = "a";
9+
10+
/// <inheritdoc/>
11+
public virtual HtmlTagBuilder GenerateBackLink(BackLinkOptions options)
12+
{
13+
ArgumentNullException.ThrowIfNull(options);
14+
options.Validate();
15+
16+
return new HtmlTagBuilder(BackLinkElement)
17+
.WithAttribute("href", options.Href.NormalizeEmptyString() ?? new HtmlString("#"))
18+
.WithCssClass("govuk-back-link")
19+
.WithCssClasses(ExplodeClasses(options.Classes?.ToHtmlString()))
20+
.WithAttributes(options.Attributes ?? [])
21+
.WithAppendedHtml(GetEncodedTextOrHtml(options.Text, options.Html) ?? new HtmlString("Back"));
22+
}
23+
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ namespace GovUk.Frontend.AspNetCore.ComponentGeneration;
55
/// </summary>
66
public interface IComponentGenerator
77
{
8+
/// <summary>
9+
/// Generates a back link component.
10+
/// </summary>
11+
/// <returns>An <see cref="HtmlTagBuilder"/> with the component's HTML.</returns>
12+
HtmlTagBuilder GenerateBackLink(BackLinkOptions options);
13+
814
/// <summary>
915
/// Generates a breadcrumbs component.
1016
/// </summary>

src/GovUk.Frontend.AspNetCore/HtmlGeneration/ComponentGenerator.BackLink.cs

Lines changed: 0 additions & 25 deletions
This file was deleted.

src/GovUk.Frontend.AspNetCore/IGovUkHtmlGenerator.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ TagBuilder GenerateAccordion(
2222
string showSectionAriaLabelText,
2323
IEnumerable<AccordionItem> items);
2424

25-
TagBuilder GenerateBackLink(IHtmlContent content, AttributeDictionary attributes);
26-
2725
TagBuilder GenerateButton(
2826
bool isStartButton,
2927
bool disabled,
Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
using System.Text.Encodings.Web;
1+
using System;
22
using System.Threading.Tasks;
3-
using GovUk.Frontend.AspNetCore.HtmlGeneration;
3+
using GovUk.Frontend.AspNetCore.ComponentGeneration;
44
using Microsoft.AspNetCore.Html;
5-
using Microsoft.AspNetCore.Mvc.TagHelpers;
65
using Microsoft.AspNetCore.Razor.TagHelpers;
76

87
namespace GovUk.Frontend.AspNetCore.TagHelpers;
@@ -11,32 +10,26 @@ namespace GovUk.Frontend.AspNetCore.TagHelpers;
1110
/// Generates a GDS back link component.
1211
/// </summary>
1312
[HtmlTargetElement(TagName)]
14-
[OutputElementHint(ComponentGenerator.BackLinkElement)]
13+
[OutputElementHint(DefaultComponentGenerator.BackLinkElement)]
1514
public class BackLinkTagHelper : TagHelper
1615
{
1716
internal const string TagName = "govuk-back-link";
1817

19-
private static readonly HtmlString _defaultContent = new HtmlString(HtmlEncoder.Default.Encode(ComponentGenerator.BackLinkDefaultContent));
20-
21-
private readonly IGovUkHtmlGenerator _htmlGenerator;
18+
private readonly IComponentGenerator _componentGenerator;
2219

2320
/// <summary>
2421
/// Creates a new <see cref="BackLinkTagHelper"/>.
2522
/// </summary>
26-
public BackLinkTagHelper()
27-
: this(htmlGenerator: null)
23+
public BackLinkTagHelper(IComponentGenerator componentGenerator)
2824
{
29-
}
30-
31-
internal BackLinkTagHelper(IGovUkHtmlGenerator? htmlGenerator)
32-
{
33-
_htmlGenerator = htmlGenerator ?? new ComponentGenerator();
25+
ArgumentNullException.ThrowIfNull(componentGenerator);
26+
_componentGenerator = componentGenerator;
3427
}
3528

3629
/// <inheritdoc/>
3730
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
3831
{
39-
IHtmlContent content = _defaultContent;
32+
IHtmlContent? content = null;
4033

4134
if (output.TagMode == TagMode.StartTagAndEndTag)
4235
{
@@ -48,13 +41,18 @@ public override async Task ProcessAsync(TagHelperContext context, TagHelperOutpu
4841
content = output.Content;
4942
}
5043

51-
var tagBuilder = _htmlGenerator.GenerateBackLink(content, output.Attributes.ToAttributeDictionary());
44+
var attributes = new EncodedAttributesDictionary(output.Attributes);
45+
attributes.Remove("class", out var classes);
46+
attributes.Remove("href", out var href);
5247

53-
output.TagName = tagBuilder.TagName;
54-
output.TagMode = TagMode.StartTagAndEndTag;
48+
var component = _componentGenerator.GenerateBackLink(new BackLinkOptions()
49+
{
50+
Html = content,
51+
Href = href,
52+
Classes = classes,
53+
Attributes = attributes
54+
});
5555

56-
output.Attributes.Clear();
57-
output.MergeAttributes(tagBuilder);
58-
output.Content.SetHtmlContent(tagBuilder.InnerHtml);
56+
component.WriteTo(output);
5957
}
6058
}

tests/GovUk.Frontend.AspNetCore.ConformanceTests/ComponentTests.BackLink.cs

Lines changed: 0 additions & 31 deletions
This file was deleted.

tests/GovUk.Frontend.AspNetCore.Tests/ComponentGeneration/ComponentTests.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ public ComponentTests()
1515
_componentGenerator = new DefaultComponentGenerator();
1616
}
1717

18+
[Theory]
19+
[ComponentFixtureData("back-link", typeof(BackLinkOptions))]
20+
public void BackLink(ComponentTestCaseData<BackLinkOptions> data) =>
21+
CheckComponentHtmlMatchesExpectedHtml(
22+
data,
23+
(generator, options) => generator.GenerateBackLink(options).ToHtmlString());
24+
1825
[Theory]
1926
[ComponentFixtureData("breadcrumbs", typeof(BreadcrumbsOptions))]
2027
public void Breadcrumbs(ComponentTestCaseData<BreadcrumbsOptions> data) =>
Lines changed: 29 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,26 @@
11
using System.Collections.Generic;
2+
using System.Text.Encodings.Web;
23
using System.Threading.Tasks;
4+
using GovUk.Frontend.AspNetCore.ComponentGeneration;
35
using GovUk.Frontend.AspNetCore.TagHelpers;
4-
using GovUk.Frontend.AspNetCore.TestCommon;
6+
using Microsoft.AspNetCore.Html;
57
using Microsoft.AspNetCore.Razor.TagHelpers;
8+
using Moq;
69
using Xunit;
710

811
namespace GovUk.Frontend.AspNetCore.Tests.TagHelpers;
912

1013
public class BackLinkTagHelperTests
1114
{
1215
[Fact]
13-
public async Task ProcessAsync_WithContent_GeneratesExpectedOutput()
16+
public async Task ProcessAsync_InvokesComponentGeneratorWithExpectedOptions()
1417
{
1518
// Arrange
16-
var context = new TagHelperContext(
17-
tagName: "govuk-back-link",
18-
allAttributes: new TagHelperAttributeList(),
19-
items: new Dictionary<object, object>(),
20-
uniqueId: "test");
19+
var href = "http://foo.com";
20+
var classes = "custom-class";
21+
var dataFooAttrValue = "bar";
22+
var content = "My custom link content";
2123

22-
var output = new TagHelperOutput(
23-
"govuk-back-link",
24-
attributes: new TagHelperAttributeList()
25-
{
26-
{ "href", "http://foo.com" }
27-
},
28-
getChildContentAsync: (useCachedResult, encoder) =>
29-
{
30-
var tagHelperContent = new DefaultTagHelperContent();
31-
tagHelperContent.SetContent("My custom link content");
32-
return Task.FromResult<TagHelperContent>(tagHelperContent);
33-
});
34-
35-
var tagHelper = new BackLinkTagHelper();
36-
37-
// Act
38-
await tagHelper.ProcessAsync(context, output);
39-
40-
// Assert
41-
var expectedHtml = @"
42-
<a class=""govuk-back-link"" href=""http://foo.com"">My custom link content</a>";
43-
44-
AssertEx.HtmlEqual(@expectedHtml, output.ToHtmlString());
45-
}
46-
47-
[Fact]
48-
public async Task ProcessAsync_WithNoContent_GeneratesExpectedOutput()
49-
{
50-
// Arrange
5124
var context = new TagHelperContext(
5225
tagName: "govuk-back-link",
5326
allAttributes: new TagHelperAttributeList(),
@@ -58,24 +31,37 @@ public async Task ProcessAsync_WithNoContent_GeneratesExpectedOutput()
5831
"govuk-back-link",
5932
attributes: new TagHelperAttributeList()
6033
{
61-
{ "href", "http://foo.com" }
34+
{ "href", href },
35+
{ "class", classes },
36+
{ "data-foo", dataFooAttrValue }
6237
},
6338
getChildContentAsync: (useCachedResult, encoder) =>
6439
{
6540
var tagHelperContent = new DefaultTagHelperContent();
41+
tagHelperContent.SetHtmlContent(content);
6642
return Task.FromResult<TagHelperContent>(tagHelperContent);
6743
});
68-
output.TagMode = TagMode.SelfClosing;
6944

70-
var tagHelper = new BackLinkTagHelper();
45+
var componentGeneratorMock = new Mock<DefaultComponentGenerator>() { CallBase = true };
46+
BackLinkOptions? actualOptions = null;
47+
componentGeneratorMock.Setup(mock => mock.GenerateBackLink(It.IsAny<BackLinkOptions>())).Callback<BackLinkOptions>(o => actualOptions = o);
48+
49+
var tagHelper = new BackLinkTagHelper(componentGeneratorMock.Object);
7150

7251
// Act
7352
await tagHelper.ProcessAsync(context, output);
7453

7554
// Assert
76-
var expectedHtml = @"
77-
<a class=""govuk-back-link"" href=""http://foo.com"">Back</a>";
78-
79-
AssertEx.HtmlEqual(@expectedHtml, output.ToHtmlString());
55+
Assert.NotNull(actualOptions);
56+
Assert.Equal(content, actualOptions!.Html?.ToHtmlString());
57+
Assert.Null(actualOptions.Text);
58+
Assert.Equal(href, actualOptions.Href?.ToHtmlString());
59+
Assert.Equal(classes, actualOptions.Classes?.ToHtmlString());
60+
Assert.NotNull(actualOptions.Attributes);
61+
Assert.Collection(actualOptions.Attributes, kvp =>
62+
{
63+
Assert.Equal("data-foo", kvp.Key);
64+
Assert.Equal(dataFooAttrValue, kvp.Value);
65+
});
8066
}
8167
}

0 commit comments

Comments
 (0)