Skip to content

Commit

Permalink
Port Tabs to IComponentGenerator
Browse files Browse the repository at this point in the history
  • Loading branch information
gunndabad committed Dec 25, 2024
1 parent 956eec6 commit b827f4b
Show file tree
Hide file tree
Showing 16 changed files with 263 additions and 391 deletions.
18 changes: 9 additions & 9 deletions docs/components/tabs.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@

```razor
<govuk-tabs>
<govuk-tabs-item id="past-day" label="Past day">
<item id="past-day" label="Past day">
<h2 class="govuk-heading-l">Past day</h2>
</govuk-tabs-item>
<govuk-tabs-item id="past-week" label="Past week">
</item>
<item id="past-week" label="Past week">
<h2 class="govuk-heading-l">Past week</h2>
</govuk-tabs-item>
<govuk-tabs-item id="past-month" label="Past month">
</item>
<item id="past-month" label="Past month">
<h2 class="govuk-heading-l">Past month</h2>
</govuk-tabs-item>
<govuk-tabs-item id="past-year" label="Past year">
</item>
<item id="past-year" label="Past year">
<h2 class="govuk-heading-l">Past year</h2>
</govuk-tabs-item>
</item>
</govuk-tabs>
```

Expand All @@ -34,7 +34,7 @@
| `id-prefix` | `string` | The prefix to use when generating IDs for the items. Required unless every item specifies the `id` attribute. |
| `title` | `string` | The title for the tabs table of contents. The default is `Contents`. |

### `<govuk-tabs-item>`
### `<item>`

The content is the HTML of the panel.\
Must be inside a `<govuk-tabs>` element.
Expand Down
16 changes: 8 additions & 8 deletions src/GovUk.Frontend.AspNetCore.DocSamples/Pages/Tabs/Tabs.cshtml
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
@page

<govuk-tabs>
<govuk-tabs-item id="past-day" label="Past day">
<item id="past-day" label="Past day">
<h2 class="govuk-heading-l">Past day</h2>
</govuk-tabs-item>
<govuk-tabs-item id="past-week" label="Past week">
</item>
<item id="past-week" label="Past week">
<h2 class="govuk-heading-l">Past week</h2>
</govuk-tabs-item>
<govuk-tabs-item id="past-month" label="Past month">
</item>
<item id="past-month" label="Past month">
<h2 class="govuk-heading-l">Past month</h2>
</govuk-tabs-item>
<govuk-tabs-item id="past-year" label="Past year">
</item>
<item id="past-year" label="Past year">
<h2 class="govuk-heading-l">Past year</h2>
</govuk-tabs-item>
</item>
</govuk-tabs>
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using System;
using System.Linq;
using Microsoft.AspNetCore.Html;

namespace GovUk.Frontend.AspNetCore.ComponentGeneration;

public partial class DefaultComponentGenerator
{
internal const string TabsDefaultTitle = "Contents";
internal const string TabsElement = "div";
internal const string TabsItemPanelElement = "div";

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

HtmlTagBuilder TabListItem(TabsOptionsItem item, int index)
{
var tabPanelId = item.Id?.NormalizeEmptyString() is IHtmlContent id ? id : new HtmlString($"{options.IdPrefix}-{index}");

return new HtmlTagBuilder("li")
.WithCssClass("govuk-tabs__list-item")
.When(index == 1, b => b.WithCssClass("govuk-tabs__list-item--selected"))
.WithAppendedHtml(new HtmlTagBuilder("a")
.WithCssClass("govuk-tabs__tab")
.WithAttribute("href", $"#{tabPanelId.ToHtmlString()}", encodeValue: false)
.WithAttributes(item.Attributes)
.WithAppendedHtml(item.Label!));
}

HtmlTagBuilder TabPanel(TabsOptionsItem item, int index)
{
var tabPanelId = item.Id?.NormalizeEmptyString() is IHtmlContent id ? id : new HtmlString($"{options.IdPrefix}-{index}");

return new HtmlTagBuilder(TabsItemPanelElement)
.WithCssClass("govuk-tabs__panel")
.When(index > 1, b => b.WithCssClass("govuk-tabs__panel--hidden"))
.WithAttribute("id", tabPanelId)
.WithAttributes(item.Panel?.Attributes)
.When(
GetEncodedTextOrHtml(item.Panel?.Text, item.Panel?.Html) is not null,
b => b.WithAppendedHtml(
item.Panel?.Html.NormalizeEmptyString() is IHtmlContent html
? html
: new HtmlTagBuilder("p").WithCssClass("govuk-body").WithAppendedText(item.Panel!.Text!)));
}

return new HtmlTagBuilder(TabsElement)
.WhenNotNull(options.Id, (id, b) => b.WithAttribute("id", id))
.WithCssClass("govuk-tabs")
.WithCssClasses(ExplodeClasses(options.Classes?.ToHtmlString()))
.WithAttributes(options.Attributes)
.WithAttribute("data-module", "govuk-tabs", encodeValue: false)
.WithAppendedHtml(new HtmlTagBuilder("h2")
.WithCssClass("govuk-tabs__title")
.WithAppendedHtml(options.Title.NormalizeEmptyString() ?? new HtmlString(TabsDefaultTitle)))
.When(
options.Items?.Count > 0,
b => b
.WithAppendedHtml(new HtmlTagBuilder("ul")
.WithCssClass("govuk-tabs__list")
.WithAppendedHtml(options.Items!.Select((item, index) => TabListItem(item, index + 1))))
.WithAppendedHtml(options.Items!.Select((item, index) => TabPanel(item, index + 1))));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ public interface IComponentGenerator
/// <returns>An <see cref="HtmlTagBuilder"/> with the component's HTML.</returns>
HtmlTagBuilder GenerateSkipLink(SkipLinkOptions options);

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

/// <summary>
/// Generates a tag component.
/// </summary>
Expand Down
27 changes: 27 additions & 0 deletions src/GovUk.Frontend.AspNetCore/ComponentGeneration/TabsOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,20 @@ public class TabsOptions
public IReadOnlyCollection<TabsOptionsItem>? Items { get; set; }
public IHtmlContent? Classes { get; set; }
public EncodedAttributesDictionary? Attributes { get; set; }

internal void Validate()
{
var gotIdPrefix = IdPrefix.NormalizeEmptyString() is not null;

if (Items is not null)
{
int itemIndex = 0;
foreach (var item in Items)
{
item.Validate(itemIndex++, gotIdPrefix);
}
}
}
}

public class TabsOptionsItem
Expand All @@ -21,6 +35,19 @@ public class TabsOptionsItem
public IHtmlContent? Label { get; set; }
public EncodedAttributesDictionary? Attributes { get; set; }
public TabsOptionsItemPanel? Panel { get; set; }

internal void Validate(int itemIndex, bool gotIdPrefix)
{
if (Label is null)
{
throw new InvalidOptionsException(GetType(), $"{nameof(Label)} must be specified on item {itemIndex}.");
}

if (!gotIdPrefix && Id.NormalizeEmptyString() is null)
{
throw new InvalidOptionsException(GetType(), $"{nameof(Id)} must be specified on item {itemIndex} when the parent {nameof(TabsOptions.IdPrefix)} is null.");
}
}
}

public class TabsOptionsItemPanel
Expand Down

This file was deleted.

13 changes: 0 additions & 13 deletions src/GovUk.Frontend.AspNetCore/HtmlGeneration/TabsItem.cs

This file was deleted.

7 changes: 0 additions & 7 deletions src/GovUk.Frontend.AspNetCore/IGovUkHtmlGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,6 @@ TagBuilder GenerateSelect(

TagBuilder GenerateSummaryList(IEnumerable<SummaryListRow> rows, AttributeDictionary attributes);

TagBuilder GenerateTabs(
string id,
string idPrefix,
string title,
AttributeDictionary attributes,
IEnumerable<TabsItem> items);

TagBuilder GenerateTextArea(
bool haveError,
string id,
Expand Down
11 changes: 6 additions & 5 deletions src/GovUk.Frontend.AspNetCore/TagHelpers/TabsContext.cs
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
using System;
using System.Collections.Generic;
using GovUk.Frontend.AspNetCore.ComponentGeneration;
using GovUk.Frontend.AspNetCore.HtmlGeneration;

namespace GovUk.Frontend.AspNetCore.TagHelpers;

internal class TabsContext
{
private readonly List<TabsItem> _items;
private readonly List<TabsOptionsItem> _items;
private readonly bool _haveIdPrefix;

public TabsContext(bool haveIdPrefix)
{
_items = new List<TabsItem>();
_items = new List<TabsOptionsItem>();
_haveIdPrefix = haveIdPrefix;
}

public IReadOnlyList<TabsItem> Items => _items;
public IReadOnlyList<TabsOptionsItem> Items => _items;

public void AddItem(TabsItem item)
public void AddItem(TabsOptionsItem item)
{
Guard.ArgumentNotNull(nameof(item), item);
ArgumentNullException.ThrowIfNull(item);

if (item.Id == null && !_haveIdPrefix)
{
Expand Down
Loading

0 comments on commit b827f4b

Please sign in to comment.