Browse Source

Fix aria-describedby wiring and align abp-input/abp-select form-text rendering

- Move inputTag.Render after GetInfoAsHtml so aria-describedby reaches the final HTML
- Replace Attributes.Add with Attributes.SetAttribute for aria-describedby to avoid duplicates when [InputInfoText] and info="..." are both present
- Apply the same fixes to AbpInputTagHelperService for consistency
- Cover the [InputInfoText] attribute path with an additional test
pull/25352/head
maliming 3 weeks ago
parent
commit
f1df827ec9
No known key found for this signature in database GPG Key ID: A646B9CB645ECEA4
  1. 6
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs
  2. 6
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs
  3. 42
      framework/test/Volo.Abp.AspNetCore.Mvc.UI.Tests/Volo/Abp/AspNetCore/Mvc/UI/Bootstrap/TagHelpers/Form/AbpSelectTagHelperService_Tests.cs

6
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpInputTagHelperService.cs

@ -88,10 +88,10 @@ public class AbpInputTagHelperService : AbpTagHelperService<AbpInputTagHelper>
var (inputTag, isCheckBox) = await GetInputTagHelperOutputAsync(context, output);
context.Items[nameof(IsOutputHidden)] = IsOutputHidden(inputTag);
var inputHtml = inputTag.Render(_encoder);
var label = await GetLabelAsHtmlAsync(context, output, inputTag, isCheckBox);
var info = GetInfoAsHtml(context, output, inputTag, isCheckBox);
var validation = isCheckBox ? "" : await GetValidationAsHtmlAsync(context, output, inputTag);
var inputHtml = inputTag.Render(_encoder);
return (GetContent(context, output, label, inputHtml, validation, info, isCheckBox), isCheckBox);
}
@ -267,7 +267,7 @@ public class AbpInputTagHelperService : AbpTagHelperService<AbpInputTagHelper>
var infoText = _tagHelperLocalizer.GetLocalizedText(idAttr.Value + "InfoText", TagHelper.AspFor.ModelExplorer);
inputTagHelperOutput.Attributes.Add("aria-describedby", infoText);
inputTagHelperOutput.Attributes.SetAttribute("aria-describedby", infoText);
}
protected virtual bool IsInputCheckbox(TagHelperContext context, TagHelperOutput output, TagHelperAttributeList attributes)
@ -363,7 +363,7 @@ public class AbpInputTagHelperService : AbpTagHelperService<AbpInputTagHelper>
div.AddCssClass("form-text");
div.InnerHtml.Append(localizedText);
inputTag.Attributes.Add("aria-describedby", idAttr?.Value + "InfoText");
inputTag.Attributes.SetAttribute("aria-describedby", idAttr?.Value + "InfoText");
return div.ToHtmlString();
}

6
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpSelectTagHelperService.cs

@ -73,10 +73,10 @@ public class AbpSelectTagHelperService : AbpTagHelperService<AbpSelectTagHelper>
protected virtual async Task<string> GetFormInputGroupAsHtmlAsync(TagHelperContext context, TagHelperOutput output, TagHelperContent childContent)
{
var selectTag = await GetSelectTagAsync(context, output, childContent);
var selectAsHtml = selectTag.Render(_encoder);
var label = await GetLabelAsHtmlAsync(context, output, selectTag);
var validation = await GetValidationAsHtmlAsync(context, output, selectTag);
var infoText = GetInfoAsHtml(context, output, selectTag);
var selectAsHtml = selectTag.Render(_encoder);
return TagHelper.FloatingLabel ? selectAsHtml + Environment.NewLine + label + Environment.NewLine + infoText + Environment.NewLine + validation :
label + Environment.NewLine + selectAsHtml + Environment.NewLine + infoText + Environment.NewLine + validation;
@ -224,7 +224,7 @@ public class AbpSelectTagHelperService : AbpTagHelperService<AbpSelectTagHelper>
var infoText = _tagHelperLocalizer.GetLocalizedText(idAttr.Value + "InfoText", TagHelper.AspFor.ModelExplorer);
inputTagHelperOutput.Attributes.Add("aria-describedby", infoText);
inputTagHelperOutput.Attributes.SetAttribute("aria-describedby", infoText);
}
protected virtual string GetInfoAsHtml(TagHelperContext context, TagHelperOutput output, TagHelperOutput inputTag)
@ -256,7 +256,7 @@ public class AbpSelectTagHelperService : AbpTagHelperService<AbpSelectTagHelper>
div.AddCssClass("form-text");
div.InnerHtml.Append(localizedText);
inputTag.Attributes.Add("aria-describedby", idAttr?.Value + "InfoText");
inputTag.Attributes.SetAttribute("aria-describedby", idAttr?.Value + "InfoText");
return div.ToHtmlString();
}

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

@ -1,6 +1,7 @@
#nullable enable
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
@ -51,6 +52,30 @@ public class AbpSelectTagHelperService_Tests
service.LastSelectTag.ShouldNotBeNull();
service.LastSelectTag!.Attributes.ContainsName("aria-describedby").ShouldBeTrue();
service.LastSelectTag.Attributes["aria-describedby"].Value.ToString().ShouldBe("TestSelectInfoText");
service.LastGroupHtml.ShouldContain("aria-describedby=\"TestSelectInfoText\"");
}
[Fact]
public async Task InputInfoText_attribute_should_render_info_text_with_single_aria_describedby()
{
var service = new TestAbpSelectTagHelperService();
var tagHelper = new AbpSelectTagHelper(service)
{
AspFor = CreateModelExpressionWithInputInfoText()
};
var output = CreateOutput();
await tagHelper.ProcessAsync(CreateContext(), output);
service.LastGroupHtml.ShouldContain("<div class=\"form-text\"");
service.LastGroupHtml.ShouldContain("Description from attribute");
service.LastGroupHtml.ShouldNotContain("<small");
service.LastSelectTag.ShouldNotBeNull();
var ariaDescribedby = service.LastSelectTag!.Attributes.Where(a => a.Name == "aria-describedby").ToList();
ariaDescribedby.Count.ShouldBe(1);
ariaDescribedby[0].Value.ToString().ShouldBe("TestSelectInfoText");
}
private static TagHelperContext CreateContext()
@ -77,6 +102,21 @@ public class AbpSelectTagHelperService_Tests
metadataProvider.GetModelExplorerForType(typeof(string), null));
}
private static ModelExpression CreateModelExpressionWithInputInfoText()
{
var metadataProvider = new EmptyModelMetadataProvider();
var modelExplorer = metadataProvider
.GetModelExplorerForType(typeof(TestModelWithInputInfoText), null)
.GetExplorerForProperty(nameof(TestModelWithInputInfoText.TestSelect));
return new ModelExpression(nameof(TestModelWithInputInfoText.TestSelect), modelExplorer);
}
private class TestModelWithInputInfoText
{
[InputInfoText("Description from attribute")]
public string TestSelect { get; set; } = string.Empty;
}
private sealed class TestAbpSelectTagHelperService : AbpSelectTagHelperService
{
public string LastGroupHtml { get; private set; } = string.Empty;
@ -98,6 +138,8 @@ public class AbpSelectTagHelperService_Tests
TagMode = TagMode.StartTagAndEndTag
};
AddInfoTextId(LastSelectTag);
return Task.FromResult(LastSelectTag);
}

Loading…
Cancel
Save