From 6066feb20372224b4160fd972fae1f8684255361 Mon Sep 17 00:00:00 2001 From: Vladimir Enchev Date: Wed, 8 Jul 2026 16:41:38 +0300 Subject: [PATCH] various accessibility fixes --- Radzen.Blazor.Tests/DateRangePickerTests.cs | 54 +++++++++++++++++++++ Radzen.Blazor/RadzenDatePicker.razor | 8 +-- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/Radzen.Blazor.Tests/DateRangePickerTests.cs b/Radzen.Blazor.Tests/DateRangePickerTests.cs index 9342b8169..22de7a656 100644 --- a/Radzen.Blazor.Tests/DateRangePickerTests.cs +++ b/Radzen.Blazor.Tests/DateRangePickerTests.cs @@ -435,6 +435,60 @@ namespace Radzen.Blazor.Tests Assert.Equal(2, component.FindAll("span.rz-calendar-month-title").Count); } + [Fact] + public void DateRangePicker_Follows_Aria_Authoring_Practices() + { + using var ctx = new TestContext(); + ctx.JSInterop.Mode = JSRuntimeMode.Loose; + ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js"); + + var component = ctx.RenderComponent(parameters => + { + parameters.Add(p => p.InitialViewDate, new DateTime(2024, 1, 1)); + parameters.Add(p => p.Value, new DateRange(new DateTime(2024, 1, 10), new DateTime(2024, 2, 5))); + }); + + var dialog = component.Find("[role=dialog]"); + Assert.False(string.IsNullOrEmpty(dialog.GetAttribute("aria-label"))); + + var trigger = component.Find(".rz-datepicker-trigger"); + Assert.Equal("dialog", trigger.GetAttribute("aria-haspopup")); + Assert.Equal(dialog.Id, trigger.GetAttribute("aria-controls")); + + var grids = component.FindAll("table[role=grid]"); + Assert.All(grids, g => Assert.False(string.IsNullOrEmpty(g.GetAttribute("aria-label")))); + + Assert.Single(component.FindAll("td[tabindex='0']")); + + var dayCells = component.FindAll("td[id]"); + Assert.All(dayCells, c => Assert.False(string.IsNullOrEmpty(c.GetAttribute("aria-label")))); + Assert.All(dayCells, c => Assert.Contains(c.GetAttribute("aria-selected"), new[] { "true", "false" })); + + Assert.All(component.FindAll("[aria-hidden='true']"), e => Assert.Null(e.GetAttribute("tabindex"))); + Assert.Empty(component.FindAll("[aria-pressed]")); + Assert.Empty(component.FindAll("button:not([aria-label])").Where(b => string.IsNullOrWhiteSpace(b.TextContent))); + + component.InvokeAsync(() => component.FindAll(".rz-calendar-title-button")[0].Click()); + + var monthsGroup = component.Find(".rz-calendar-months"); + Assert.Equal("group", monthsGroup.GetAttribute("role")); + Assert.False(string.IsNullOrEmpty(monthsGroup.GetAttribute("aria-label"))); + + var focusedMonth = component.FindAll(".rz-calendar-month-cell").Single(c => c.GetAttribute("tabindex") == "0"); + Assert.Equal("true", focusedMonth.GetAttribute("aria-current")); + Assert.Contains("2024", focusedMonth.GetAttribute("aria-label")); + + component.InvokeAsync(() => component.Find(".rz-calendar-title-button").Click()); + + var yearsGroup = component.Find(".rz-calendar-years"); + Assert.Equal("group", yearsGroup.GetAttribute("role")); + Assert.False(string.IsNullOrEmpty(yearsGroup.GetAttribute("aria-label"))); + + var focusedYear = component.FindAll(".rz-calendar-year-cell").Single(c => c.GetAttribute("tabindex") == "0"); + Assert.Equal("true", focusedYear.GetAttribute("aria-current")); + Assert.Empty(component.FindAll("[aria-pressed]")); + } + [Fact] public void DateRangePicker_Does_Not_Render_TimePicker() { diff --git a/Radzen.Blazor/RadzenDatePicker.razor b/Radzen.Blazor/RadzenDatePicker.razor index 6f97b107a..f5ef46efc 100644 --- a/Radzen.Blazor/RadzenDatePicker.razor +++ b/Radzen.Blazor/RadzenDatePicker.razor @@ -75,12 +75,12 @@ @if (currentView == CalendarViewKind.Months) {
-
+
@foreach (var month in MonthsInView) { var isActiveMonth = month.Value == GetCalendarMonth(CurrentDate); var monthDisabled = Disabled || IsMonthDisabled(month.Value); - }
@@ -89,11 +89,11 @@ else if (currentView == CalendarViewKind.Years) {
-
+
@foreach (var year in YearsInView) { var isActiveYear = year.Value == GetCalendarYear(CurrentDate); - }