Skip to content

Commit c85f29a

Browse files
authored
fix(MudSelectExtended): place aria-label on visible control instead of hidden input (#622)
1 parent 0b08b25 commit c85f29a

5 files changed

Lines changed: 159 additions & 3 deletions

File tree

docs/CodeBeam.MudBlazor.Extensions.Docs/Pages/Components/SelectExtended/Examples/SelectExtendedExample6.razor

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
ToStringFunc="@((TestHouse house) => $"{house.Number} - {house.Name}")"
3636
AnchorOrigin="Origin.BottomCenter"
3737
Variant="Variant.Outlined"
38+
aria-label="House Selection"
3839
HelperText="Search by number or name (e.g., '1 -' or 'Test3')"
3940
SearchBoxClearable="true" />
4041
</MudItem>

src/CodeBeam.MudBlazor.Extensions/Components/SelectExtended/MudSelectExtended.razor

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
Disabled="@Disabled"
2222
@onclick="@ToggleMenu"
2323
Required="@Required"
24-
ForId="@InputElementId">
24+
ForId="@InputElementId"
25+
@attributes="GetAriaAttributes()">
2526
<InputContent>
2627
<MudInputExtended @ref="_elementReference" InputType="InputType.Hidden" Label="@Label"
2728
Class="@InputClassname" Style="@InputStyle" Margin="@Margin" Placeholder="@Placeholder"
@@ -30,7 +31,7 @@
3031
Value="@(Strict && !IsValueInList ? null : ReadText)" Underline="@Underline"
3132
Disabled="@GetDisabledState()" ReadOnly="true" Error="@ErrorState.Value" ErrorId="@ErrorIdState.Value"
3233
Clearable="@(Clearable && !GetReadOnlyState())" OnClearButtonClick="(async (e) => await SelectClearButtonClickHandlerAsync(e))"
33-
@attributes="UserAttributes" OnBlur="@OnLostFocus" ShrinkLabel="@(AdornmentStart != null || ShrinkLabel)" Typo="@Typo"
34+
@attributes="GetUserAttributesForHiddenInput()" OnBlur="@OnLostFocus" ShrinkLabel="@(AdornmentStart != null || ShrinkLabel)" Typo="@Typo"
3435
ShowVisualiser="true" DataVisualiserStyle="min-height: 1.1876em; padding-right: 0px;">
3536

3637
<AdornmentStart>

src/CodeBeam.MudBlazor.Extensions/Components/SelectExtended/MudSelectExtended.razor.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,5 +1332,47 @@ public bool GetOpenState()
13321332
var n = ToStringFunc(input);
13331333
return ToStringFunc(input);
13341334
}
1335+
1336+
/// <summary>
1337+
/// Filters UserAttributes to exclude ARIA attributes so they don't get applied to the hidden input.
1338+
/// ARIA attributes should only be on the visible MudInputControl, not the hidden MudInputExtended.
1339+
/// </summary>
1340+
/// <returns>A dictionary of user attributes with ARIA attributes removed</returns>
1341+
protected Dictionary<string, object?> GetUserAttributesForHiddenInput()
1342+
{
1343+
if (UserAttributes == null || UserAttributes.Count == 0)
1344+
return [];
1345+
1346+
var filtered = new Dictionary<string, object?>(UserAttributes);
1347+
filtered.Remove("aria-label");
1348+
filtered.Remove("aria-labelledby");
1349+
return filtered;
1350+
}
1351+
1352+
/// <summary>
1353+
/// Extracts ARIA attributes from UserAttributes that should be applied to the visible control.
1354+
/// These attributes belong on the visible MudInputControl, not the hidden MudInputExtended.
1355+
/// </summary>
1356+
/// <returns>A dictionary containing only ARIA attributes</returns>
1357+
protected Dictionary<string, object?> GetAriaAttributes()
1358+
{
1359+
if (UserAttributes == null || UserAttributes.Count == 0)
1360+
return [];
1361+
1362+
var aria = new Dictionary<string, object?>();
1363+
1364+
// Only include aria-* attributes that should be on the visible element
1365+
var ariaKeys = new[] { "aria-label", "aria-labelledby" };
1366+
1367+
foreach (var key in ariaKeys)
1368+
{
1369+
if (UserAttributes.TryGetValue(key, out var value))
1370+
{
1371+
aria[key] = value;
1372+
}
1373+
}
1374+
1375+
return aria;
1376+
}
13351377
}
13361378
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
@namespace MudExtensions.UnitTests.TestComponents
2+
3+
<MudPopoverProvider></MudPopoverProvider>
4+
5+
<MudSelectExtended T="string" @bind-Value="value" @attributes="UserAttributes">
6+
<MudSelectItemExtended Value="@("1")"/>
7+
<MudSelectItemExtended Value="@("2")"/>
8+
<MudSelectItemExtended Value="@("3")"/>
9+
</MudSelectExtended>
10+
11+
@code {
12+
[Parameter]
13+
public Dictionary<string, object?> UserAttributes { get; set; } = new();
14+
15+
string? value = null;
16+
}

tests/CodeBeam.MudBlazor.Extensions.UnitTests/Components/SelectExtendedTests.cs

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1354,5 +1354,101 @@ public void Select_Should_NotOpen_WhenParentDisabled()
13541354
// The menu should not open
13551355
comp.Find("div.mud-popover").ClassList.Should().NotContain("mud-popover-open");
13561356
}
1357+
1358+
[Test]
1359+
public void Select_AriaLabel_ShouldBeOnVisibleControl_NotHiddenInput()
1360+
{
1361+
var comp = Context.Render<SelectWithAriaLabelTest>(parameters =>
1362+
{
1363+
parameters.Add(p => p.UserAttributes, new Dictionary<string, object?> { { "aria-label", "Test Dropdown" } });
1364+
});
1365+
1366+
var visibleControl = comp.Find("div.mud-input-control");
1367+
var hiddenInput = comp.Find("input[type='hidden']");
1368+
1369+
// aria-label should be on the visible MudInputControl
1370+
visibleControl.GetAttribute("aria-label").Should().Be("Test Dropdown");
1371+
1372+
// aria-label should NOT be on the hidden input
1373+
hiddenInput.GetAttribute("aria-label").Should().BeNull();
1374+
}
1375+
1376+
[Test]
1377+
public void Select_AriaLabelledBy_ShouldBeOnVisibleControl_NotHiddenInput()
1378+
{
1379+
var comp = Context.Render<SelectWithAriaLabelTest>(parameters =>
1380+
{
1381+
parameters.Add(p => p.UserAttributes, new Dictionary<string, object?> { { "aria-labelledby", "my-label-id" } });
1382+
});
1383+
1384+
var visibleControl = comp.Find("div.mud-input-control");
1385+
var hiddenInput = comp.Find("input[type='hidden']");
1386+
1387+
// aria-labelledby should be on the visible MudInputControl
1388+
visibleControl.GetAttribute("aria-labelledby").Should().Be("my-label-id");
1389+
1390+
// aria-labelledby should NOT be on the hidden input
1391+
hiddenInput.GetAttribute("aria-labelledby").Should().BeNull();
1392+
}
1393+
1394+
[Test]
1395+
public void Select_OtherUserAttributes_ShouldBePreservedOnHiddenInput()
1396+
{
1397+
var comp = Context.Render<SelectWithAriaLabelTest>(parameters =>
1398+
{
1399+
parameters.Add(p => p.UserAttributes, new Dictionary<string, object?>
1400+
{
1401+
{ "aria-label", "Test Dropdown" },
1402+
{ "data-testid", "my-select" },
1403+
{ "custom-attr", "custom-value" }
1404+
});
1405+
});
1406+
1407+
var hiddenInput = comp.Find("input[type='hidden']");
1408+
1409+
// aria-label should be filtered out
1410+
hiddenInput.GetAttribute("aria-label").Should().BeNull();
1411+
1412+
// Other attributes should be preserved
1413+
hiddenInput.GetAttribute("data-testid").Should().Be("my-select");
1414+
hiddenInput.GetAttribute("custom-attr").Should().Be("custom-value");
1415+
}
1416+
1417+
[Test]
1418+
public void Select_BothAriaAttributes_ShouldBeFiltered()
1419+
{
1420+
var comp = Context.Render<SelectWithAriaLabelTest>(parameters =>
1421+
{
1422+
parameters.Add(p => p.UserAttributes, new Dictionary<string, object?>
1423+
{
1424+
{ "aria-label", "Test Dropdown" },
1425+
{ "aria-labelledby", "my-label-id" }
1426+
});
1427+
});
1428+
1429+
var visibleControl = comp.Find("div.mud-input-control");
1430+
var hiddenInput = comp.Find("input[type='hidden']");
1431+
1432+
// Both aria attributes should be on visible control
1433+
visibleControl.GetAttribute("aria-label").Should().Be("Test Dropdown");
1434+
visibleControl.GetAttribute("aria-labelledby").Should().Be("my-label-id");
1435+
1436+
// Neither should be on hidden input
1437+
hiddenInput.GetAttribute("aria-label").Should().BeNull();
1438+
hiddenInput.GetAttribute("aria-labelledby").Should().BeNull();
1439+
}
1440+
1441+
[Test]
1442+
public void Select_NoUserAttributes_ShouldWorkCorrectly()
1443+
{
1444+
var comp = Context.Render<SelectWithAriaLabelTest>();
1445+
1446+
var visibleControl = comp.Find("div.mud-input-control");
1447+
var hiddenInput = comp.Find("input[type='hidden']");
1448+
1449+
// Neither element should have aria attributes
1450+
visibleControl.GetAttribute("aria-label").Should().BeNull();
1451+
hiddenInput.GetAttribute("aria-label").Should().BeNull();
1452+
}
13571453
}
1358-
}
1454+
}

0 commit comments

Comments
 (0)