Browse Source

Make AppendAriaDescribedby case-insensitive and split on all HTML whitespace

- Look up the existing aria-describedby attribute with OrdinalIgnoreCase to match the casing rules used by HTML and TagHelperAttributeList
- Tokenize the existing value on all ASCII whitespace (space, tab, newline, carriage return, form feed) instead of just the literal space
- Cover the whitespace-separated case with a new test
pull/25352/head
maliming 3 weeks ago
parent
commit
dd3e9697d4
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 11
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperOutputExtensions.cs
  2. 18
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Tests/Volo/Abp/AspNetCore/Mvc/UI/Bootstrap/TagHelpers/Form/AbpSelectTagHelperService_Tests.cs

11
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Extensions/TagHelperOutputExtensions.cs

@ -8,6 +8,10 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions;
public static class TagHelperOutputExtensions
{
// ASCII whitespace per the HTML5 spec, used to tokenize space-separated
// attribute values such as aria-describedby.
private static readonly char[] HtmlAsciiWhitespace = { ' ', '\t', '\n', '\r', '\f' };
public static string Render(this TagHelperOutput output, HtmlEncoder htmlEncoder)
{
using (var writer = new StringWriter())
@ -30,15 +34,16 @@ public static class TagHelperOutputExtensions
}
var existing = output.Attributes
.FirstOrDefault(a => a.Name == "aria-describedby")?.Value?.ToString();
.FirstOrDefault(a => string.Equals(a.Name, "aria-describedby", StringComparison.OrdinalIgnoreCase))
?.Value?.ToString();
if (string.IsNullOrEmpty(existing))
if (string.IsNullOrWhiteSpace(existing))
{
output.Attributes.SetAttribute("aria-describedby", token);
return;
}
var tokens = existing.Split(' ', StringSplitOptions.RemoveEmptyEntries);
var tokens = existing.Split(HtmlAsciiWhitespace, StringSplitOptions.RemoveEmptyEntries);
if (tokens.Any(t => t == token))
{
return;

18
framework/test/Volo.Abp.AspNetCore.Mvc.UI.Tests/Volo/Abp/AspNetCore/Mvc/UI/Bootstrap/TagHelpers/Form/AbpSelectTagHelperService_Tests.cs

@ -97,6 +97,24 @@ public class AbpSelectTagHelperService_Tests
service.LastGroupHtml.ShouldContain("aria-describedby=\"custom-id TestSelectInfoText\"");
}
[Fact]
public async Task Aria_describedby_should_split_on_html_whitespace_separators()
{
var service = new TestAbpSelectTagHelperService(existingAriaDescribedby: "id1\tid2");
var tagHelper = new AbpSelectTagHelper(service)
{
AspFor = CreateModelExpression(),
InfoText = "Description"
};
var output = CreateOutput();
await tagHelper.ProcessAsync(CreateContext(), output);
service.LastSelectTag.ShouldNotBeNull();
service.LastSelectTag!.Attributes["aria-describedby"].Value.ToString().ShouldBe("id1\tid2 TestSelectInfoText");
}
[Fact]
public async Task InputInfoText_attribute_should_render_info_text_with_single_aria_describedby()
{

Loading…
Cancel
Save