Skip to content

Commit

Permalink
Add method for generating Character count to DefaultComponentGenerator
Browse files Browse the repository at this point in the history
  • Loading branch information
gunndabad committed Dec 31, 2024
1 parent ba42235 commit 087b8d3
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,24 @@ public record CharacterCountOptions
public CharacterCountOptionsLocalizedText? WordsUnderLimitText { get; set; }
public IHtmlContent? WordsAtLimitText { get; set; }
public CharacterCountOptionsLocalizedText? WordsOverLimitText { get; set; }

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

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

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

public record CharacterCountCountOptionsMessage
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System;
using Microsoft.AspNetCore.Html;

namespace GovUk.Frontend.AspNetCore.ComponentGeneration;

public partial class DefaultComponentGenerator
{
internal const string CharacterCountElement = "div";

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

var hasNoLimit = options.MaxLength is null && options.MaxWords is null;

return new HtmlTagBuilder(CharacterCountElement)
.WithCssClass("govuk-character-count")
.WithAttribute("data-module", "govuk-character-count", encodeValue: false)
.WhenNotNull(options.MaxLength,
(maxLength, b) => b.WithAttribute("data-maxlength", maxLength.ToString()!, encodeValue: false))
.WhenNotNull(options.Threshold,
(threshold, b) => b.WithAttribute("data-threshold", threshold.ToString()!, encodeValue: false))
.WhenNotNull(options.MaxWords,
(maxWords, b) => b.WithAttribute("data-maxwords", maxWords.ToString()!, encodeValue: false))
.When(
hasNoLimit && options.TextareaDescriptionText.NormalizeEmptyString() is not null,
b => b.WithAttribute("data-i18n.textarea-description.other", options.TextareaDescriptionText!))
.WithAttributeWhenNotNull(options.CharactersUnderLimitText?.One, "data-i18n.characters-under-limit.one")
.WithAttributeWhenNotNull(options.CharactersUnderLimitText?.Other, "data-i18n.characters-under-limit.other")
.WithAttributeWhenNotNull(options.CharactersAtLimitText, "data-i18n.characters-at-limit")
.WithAttributeWhenNotNull(options.CharactersOverLimitText?.One, "data-i18n.characters-over-limit.one")
.WithAttributeWhenNotNull(options.CharactersOverLimitText?.Other, "data-i18n.characters-over-limit.other")
.WithAttributeWhenNotNull(options.WordsUnderLimitText?.One, "data-i18n.words-under-limit.one")
.WithAttributeWhenNotNull(options.WordsUnderLimitText?.Other, "data-i18n.words-under-limit.other")
.WithAttributeWhenNotNull(options.WordsAtLimitText, "data-i18n.words-at-limit")
.WithAttributeWhenNotNull(options.WordsOverLimitText?.One, "data-i18n.words-over-limit.one")
.WithAttributeWhenNotNull(options.WordsOverLimitText?.Other, "data-i18n.words-over-limit.other")
.WithAppendedHtml(GenerateTextarea(new TextareaOptions
{
Id = options.Id,
Name = options.Name,
DescribedBy = new HtmlString($"{options.Id!.ToHtmlString()}-info"),
Rows = options.Rows,
Spellcheck = options.Spellcheck,
Value = options.Value,
FormGroup = options.FormGroup,
Classes = new HtmlString($"govuk-js-character-count {options.Classes?.ToHtmlString()}".TrimEnd()),
Label = (options.Label ?? new LabelOptions()) with { For = options.Id },
Hint = options.Hint,
ErrorMessage = options.ErrorMessage,
Attributes = options.Attributes
}))
.WithAppendedHtml(() =>
{
IHtmlContent? content = null;

if (!hasNoLimit)
{
var textareaDescriptionLength = options.MaxWords ?? options.MaxLength;

content = new HtmlString(
(options.TextareaDescriptionText.NormalizeEmptyString()?.ToHtmlString() ??
$"You can enter up to %{{count}} {(options.MaxWords is not null ? "words" : "characters")}")
.Replace("%{count}", textareaDescriptionLength.ToString()));
}

return GenerateHint(
new HintOptions()
{
Html = content,
Id = new HtmlString($"{options.Id!.ToHtmlString()}-info"),
Classes = new HtmlString(
$"govuk-character-count__message {options.CountMessage?.Classes?.ToHtmlString()}".TrimEnd())
},
allowMissingContent: true);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@ public partial class DefaultComponentGenerator
internal const string HintElement = "div";

/// <inheritdoc/>
public virtual HtmlTagBuilder GenerateHint(HintOptions options)
protected virtual HtmlTagBuilder GenerateHint(HintOptions options) =>
GenerateHint(options, allowMissingContent: false);

private HtmlTagBuilder GenerateHint(HintOptions options, bool allowMissingContent = false)
{
ArgumentNullException.ThrowIfNull(options);
options.Validate();
options.Validate(allowMissingContent);

return new HtmlTagBuilder(HintElement)
.WithCssClass("govuk-hint")
.WithCssClasses(ExplodeClasses(options.Classes?.ToHtmlString()))
.WithAttributeWhenNotNull(options.Id, "id")
.WithAttributes(options.Attributes)
.WithAppendedHtml(GetEncodedTextOrHtml(options.Text, options.Html)!);
.WhenNotNull(GetEncodedTextOrHtml(options.Text, options.Html), (content, b) => b.WithAppendedHtml(content));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ public record HintOptions
public IHtmlContent? Classes { get; set; }
public EncodedAttributesDictionary? Attributes { get; set; }

internal void Validate()
internal void Validate(bool allowMissingContent = false)
{
if (Html.NormalizeEmptyString() is null && Text.NormalizeEmptyString() is null)
if (!allowMissingContent && Html.NormalizeEmptyString() is null && Text.NormalizeEmptyString() is null)
{
throw new InvalidOptionsException(GetType(), $"{nameof(Html)} or {nameof(Text)} must be specified.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ public ComponentTests()
_componentGenerator = new DefaultComponentGenerator();
}

[Theory]
[ComponentFixtureData("character-count", typeof(CharacterCountOptions))]
public void CharacterCount(ComponentTestCaseData<CharacterCountOptions> data) =>
CheckComponentHtmlMatchesExpectedHtml(
data,
(generator, options) => generator.GenerateCharacterCount(options).ToHtmlString());

[Theory]
[ComponentFixtureData("textarea", typeof(TextareaOptions))]
public void Textarea(ComponentTestCaseData<TextareaOptions> data) =>
Expand Down

0 comments on commit 087b8d3

Please sign in to comment.