Skip to content

Commit

Permalink
Port Breadcrumbs to IComponentGenerator
Browse files Browse the repository at this point in the history
  • Loading branch information
gunndabad committed Dec 25, 2024
1 parent b827f4b commit a9c4a60
Show file tree
Hide file tree
Showing 15 changed files with 183 additions and 212 deletions.
14 changes: 7 additions & 7 deletions docs/components/breadcrumbs.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

```razor
<govuk-breadcrumbs collapse-on-mobile="true">
<govuk-breadcrumbs-item asp-controller="Home" asp-action="Index">Home</govuk-breadcrumbs-item>
<govuk-breadcrumbs-item href="#" link-target="_blank">Passports, travel and living abroad</govuk-breadcrumbs-item>
<govuk-breadcrumbs-item>Travel abroad</govuk-breadcrumbs-item>
<item asp-controller="Home" asp-action="Index">Home</item>
<item href="#" link-target="_blank">Passports, travel and living abroad</item>
<item>Travel abroad</item>
</govuk-breadcrumbs>
```

Expand All @@ -18,11 +18,11 @@

### `<govuk-breadcrumbs>`

| Attribute | Type | Description |
| --- | --- | --- |
| `collapse-on-mobile` | `bool` | When true, the breadcrumbs will collapse to the first and last item only on tablet breakpoint and below. Default is `false`. |
| Attribute | Type | Description |
| --- |---------|--------------------------------------------------------------------------------------------------------------------------------------------------|
| `collapse-on-mobile` | `bool?` | When true, the breadcrumbs will collapse to the first and last item only on tablet breakpoint and below. If not specified, `false` will be used. |

### `<govuk-breadcrumbs-item>`
### `<item>`

Content is the HTML to use within the breadcrumbs item.\
Must be inside a `<govuk-breadcrumbs>` element.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
@page

<govuk-breadcrumbs collapse-on-mobile="true">
<govuk-breadcrumbs-item asp-controller="Home" asp-action="Index">Home</govuk-breadcrumbs-item>
<govuk-breadcrumbs-item href="#" link-target="_blank">Passports, travel and living abroad</govuk-breadcrumbs-item>
<govuk-breadcrumbs-item>Travel abroad</govuk-breadcrumbs-item>
<item asp-controller="Home" asp-action="Index">Home</item>
<item href="#" link-target="_blank">Passports, travel and living abroad</item>
<item>Travel abroad</item>
</govuk-breadcrumbs>
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Html;

Expand All @@ -8,9 +9,23 @@ namespace GovUk.Frontend.AspNetCore.ComponentGeneration;
public class BreadcrumbsOptions
{
public bool? CollapseOnMobile { get; set; }
public string? Classes { get; set; }
public IHtmlContent? Classes { get; set; }
public EncodedAttributesDictionary? Attributes { get; set; }
public IReadOnlyCollection<BreadcrumbsOptionsItem>? Items { get; set; }

internal void Validate()
{
if (Items is null)
{
throw new InvalidOptionsException(GetType(), $"{nameof(Items)} must be specified.");
}

int i = 0;
foreach (var item in Items)
{
item.Validate(i++);
}
}
}

public class BreadcrumbsOptionsItem
Expand All @@ -19,4 +34,15 @@ public class BreadcrumbsOptionsItem
public string? Text { get; set; }
public IHtmlContent? Href { get; set; }
public EncodedAttributesDictionary? Attributes { get; set; }

[NonStandardParameter]
internal EncodedAttributesDictionary? ItemAttributes { get; set; }

internal void Validate(int itemIndex)
{
if (Html.NormalizeEmptyString() is null && Text.NormalizeEmptyString() is null)
{
throw new InvalidOptionsException(GetType(), $"{nameof(Html)} or {nameof(Text)} must be specified on item {itemIndex}.");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System;
using System.Linq;

namespace GovUk.Frontend.AspNetCore.ComponentGeneration;

public partial class DefaultComponentGenerator
{
internal const string BreadcrumbsElement = "div";
internal const bool BreadcrumbsDefaultCollapseOnMobile = false;
internal const string BreadcrumbsItemElement = "li";

/// <inheritdoc/>
public virtual HtmlTagBuilder GenerateBreadcrumbs(BreadcrumbsOptions options)
{
ArgumentNullException.ThrowIfNull(options);
options.Validate();

return new HtmlTagBuilder(BreadcrumbsElement)
.WithCssClass("govuk-breadcrumbs")
.WithCssClasses(ExplodeClasses(options.Classes?.ToHtmlString()))
.When(options.CollapseOnMobile == true, b => b.WithCssClass("govuk-breadcrumbs--collapse-on-mobile"))
.WithAttributes(options.Attributes)
.WithAppendedHtml(new HtmlTagBuilder("ol")
.WithCssClass("govuk-breadcrumbs__list")
.WithAppendedHtml(options.Items!.Select(item =>
{
var content = GetEncodedTextOrHtml(item.Text, item.Html)!;
var gotLink = item.Href.NormalizeEmptyString() is not null;

return new HtmlTagBuilder(BreadcrumbsItemElement)
.WithCssClass("govuk-breadcrumbs__list-item")
.WithAttributes(item.ItemAttributes)
.When(
!gotLink,
b => b
.WithAttribute("aria-current", "page", encodeValue: false)
.WithAppendedHtml(content))
.When(
gotLink,
b => b
.WithAppendedHtml(new HtmlTagBuilder("a")
.WithCssClass("govuk-breadcrumbs__link")
.WithAttribute("href", item.Href!)
.WithAttributes(item.Attributes)
.WithAppendedHtml(content)));
})));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ public interface IComponentGenerator
/// <returns>An <see cref="HtmlTagBuilder"/> with the component's HTML.</returns>
HtmlTagBuilder GenerateBackLink(BackLinkOptions options);

/// <summary>
/// Generates a breadcrumbs component.
/// </summary>
/// <returns>An <see cref="HtmlTagBuilder"/> with the component's HTML.</returns>
HtmlTagBuilder GenerateBreadcrumbs(BreadcrumbsOptions options);

/// <summary>
/// Generates a button component.
/// </summary>
Expand Down
12 changes: 0 additions & 12 deletions src/GovUk.Frontend.AspNetCore/HtmlGeneration/BreadcrumbsItem.cs

This file was deleted.

This file was deleted.

5 changes: 0 additions & 5 deletions src/GovUk.Frontend.AspNetCore/IGovUkHtmlGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,6 @@ TagBuilder GenerateAccordion(
string showSectionAriaLabelText,
IEnumerable<AccordionItem> items);

TagBuilder GenerateBreadcrumbs(
bool collapseOnMobile,
AttributeDictionary attributes,
IEnumerable<BreadcrumbsItem> items);

TagBuilder GenerateCharacterCount(
string textAreaId,
int? maxLength,
Expand Down
13 changes: 7 additions & 6 deletions src/GovUk.Frontend.AspNetCore/TagHelpers/BreadcrumbsContext.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
using System;
using System.Collections.Generic;
using GovUk.Frontend.AspNetCore.HtmlGeneration;
using GovUk.Frontend.AspNetCore.ComponentGeneration;

namespace GovUk.Frontend.AspNetCore.TagHelpers;

internal class BreadcrumbsContext
{
private readonly List<BreadcrumbsItem> _items;
private readonly List<BreadcrumbsOptionsItem> _items;

public BreadcrumbsContext()
{
_items = new List<BreadcrumbsItem>();
_items = new List<BreadcrumbsOptionsItem>();
}

public IReadOnlyCollection<BreadcrumbsItem> Items => _items;
public IReadOnlyCollection<BreadcrumbsOptionsItem> Items => _items;

public void AddItem(BreadcrumbsItem item)
public void AddItem(BreadcrumbsOptionsItem item)
{
Guard.ArgumentNotNull(nameof(item), item);
ArgumentNullException.ThrowIfNull(item);

_items.Add(item);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using GovUk.Frontend.AspNetCore.ComponentGeneration;
using GovUk.Frontend.AspNetCore.HtmlGeneration;
using Microsoft.AspNetCore.Razor.TagHelpers;

Expand All @@ -9,10 +10,12 @@ namespace GovUk.Frontend.AspNetCore.TagHelpers;
/// Represents an item in a GDS breadcrumbs component.
/// </summary>
[HtmlTargetElement(TagName, ParentTag = BreadcrumbsTagHelper.TagName)]
[HtmlTargetElement(ShortTagName, ParentTag = BreadcrumbsTagHelper.TagName)]
//[OutputElementHint(ComponentGenerator.BreadcrumbsItemElement)] // Omitted since it produces intellisense warnings
public class BreadcrumbsItemTagHelper : TagHelper
{
internal const string TagName = "govuk-breadcrumbs-item";
internal const string ShortTagName = ShortTagNames.Item;

private const string LinkAttributesPrefix = "link-";

Expand All @@ -27,27 +30,22 @@ public override async Task ProcessAsync(TagHelperContext context, TagHelperOutpu
{
var breadcrumbsContext = context.GetContextItem<BreadcrumbsContext>();

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

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

string? href = null;
var attributes = new EncodedAttributesDictionary(output.Attributes);
attributes.Remove("href", out var href);

if (output.Attributes.TryGetAttribute("href", out var hrefAttribute))
breadcrumbsContext.AddItem(new BreadcrumbsOptionsItem()
{
href = hrefAttribute.Value.ToString();
output.Attributes.Remove(hrefAttribute);
}

breadcrumbsContext.AddItem(new BreadcrumbsItem()
{
Attributes = output.Attributes.ToAttributeDictionary(),
ItemAttributes = attributes,
Href = href,
LinkAttributes = LinkAttributes.ToAttributeDictionary(),
Content = childContent.Snapshot()
Attributes = EncodedAttributesDictionary.FromDictionaryWithEncodedValues(LinkAttributes),
Html = childContent
});

output.SuppressOutput();
Expand Down
Loading

0 comments on commit a9c4a60

Please sign in to comment.