Skip to content

Commit cd5303d

Browse files
authored
Remove aria-invalid when validation state changes to valid (#28854)
The original logic for input validation was adding aria-invalid attribute when validation state changed to invalid. However, as is reported in the below referred issue, the attribute didn't get removed, when state later changed to valid. This change addresses that issue. Also removing blazor.webassembly.js per review comment. Addresses #28823
1 parent 3af6c9a commit cd5303d

File tree

4 files changed

+63
-7
lines changed

4 files changed

+63
-7
lines changed

src/Components/Web.JS/dist/Release/blazor.server.js

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Components/Web.JS/dist/Release/blazor.webassembly.js

-1
This file was deleted.

src/Components/Web/src/Forms/InputBase.cs

+24-4
Original file line numberDiff line numberDiff line change
@@ -226,24 +226,25 @@ public override Task SetParametersAsync(ParameterView parameters)
226226
$"{nameof(Forms.EditContext)} dynamically.");
227227
}
228228

229-
SetAdditionalAttributesIfValidationFailed();
229+
UpdateAdditionalValidationAttributes();
230230

231231
// For derived components, retain the usual lifecycle with OnInit/OnParametersSet/etc.
232232
return base.SetParametersAsync(ParameterView.Empty);
233233
}
234234

235235
private void OnValidateStateChanged(object? sender, ValidationStateChangedEventArgs eventArgs)
236236
{
237-
SetAdditionalAttributesIfValidationFailed();
237+
UpdateAdditionalValidationAttributes();
238238

239239
StateHasChanged();
240240
}
241241

242-
private void SetAdditionalAttributesIfValidationFailed()
242+
private void UpdateAdditionalValidationAttributes()
243243
{
244+
var hasAriaInvalidAttribute = AdditionalAttributes != null && AdditionalAttributes.ContainsKey("aria-invalid");
244245
if (EditContext.GetValidationMessages(FieldIdentifier).Any())
245246
{
246-
if (AdditionalAttributes != null && AdditionalAttributes.ContainsKey("aria-invalid"))
247+
if (hasAriaInvalidAttribute)
247248
{
248249
// Do not overwrite the attribute value
249250
return;
@@ -258,6 +259,25 @@ private void SetAdditionalAttributesIfValidationFailed()
258259
// we will automatically render the `aria-invalid` attribute when the validation fails
259260
additionalAttributes["aria-invalid"] = true;
260261
}
262+
else if (hasAriaInvalidAttribute)
263+
{
264+
// No validation errors. Need to remove `aria-invalid` if it was rendered already
265+
266+
if (AdditionalAttributes!.Count == 1)
267+
{
268+
// Only aria-invalid argument is present which we don't need any more
269+
AdditionalAttributes = null;
270+
}
271+
else
272+
{
273+
if (ConvertToDictionary(AdditionalAttributes, out var additionalAttributes))
274+
{
275+
AdditionalAttributes = additionalAttributes;
276+
}
277+
278+
additionalAttributes.Remove("aria-invalid");
279+
}
280+
}
261281
}
262282

263283
/// <summary>

src/Components/Web/test/Forms/InputBaseTest.cs

+37
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,43 @@ public async Task UserSpecifiedAriaValueIsNotChangedIfInvalid()
468468
Assert.Equal("userSpecifiedValue", component.AdditionalAttributes["aria-invalid"]);
469469
}
470470

471+
[Fact]
472+
public async Task AriaAttributeRemovedWhenStateChangesToValidFromInvalid()
473+
{
474+
// Arrange
475+
var model = new TestModel();
476+
var rootComponent = new TestInputHostComponent<string, TestInputComponent<string>>
477+
{
478+
EditContext = new EditContext(model),
479+
ValueExpression = () => model.StringProperty
480+
};
481+
var fieldIdentifier = FieldIdentifier.Create(() => model.StringProperty);
482+
var renderer = new TestRenderer();
483+
var messageStore = new ValidationMessageStore(rootComponent.EditContext);
484+
messageStore.Add(fieldIdentifier, "Artificial error message");
485+
var rootComponentId = renderer.AssignRootComponentId(rootComponent);
486+
await renderer.RenderRootComponentAsync(rootComponentId);
487+
488+
// Initally, it rendered one batch and is invalid
489+
var batch1 = renderer.Batches.Single();
490+
var componentFrame1 = batch1.GetComponentFrames<TestInputComponent<string>>().Single();
491+
var inputComponentId = componentFrame1.ComponentId;
492+
var component = (TestInputComponent<string>)componentFrame1.Component;
493+
Assert.Equal("invalid", component.CssClass);
494+
Assert.NotNull(component.AdditionalAttributes);
495+
Assert.True(component.AdditionalAttributes.ContainsKey("aria-invalid"));
496+
497+
// Act: update the field state in the EditContext and notify
498+
messageStore.Clear(fieldIdentifier);
499+
await renderer.Dispatcher.InvokeAsync(rootComponent.EditContext.NotifyValidationStateChanged);
500+
501+
// Assert: The input component rendered itself again and now has the new class
502+
var batch2 = renderer.Batches.Skip(1).Single();
503+
Assert.Equal(inputComponentId, batch2.DiffsByComponentId.Keys.Single());
504+
Assert.Equal("valid", component.CssClass);
505+
Assert.Null(component.AdditionalAttributes);
506+
}
507+
471508
class TestModel
472509
{
473510
public string StringProperty { get; set; }

0 commit comments

Comments
 (0)