mirror of
https://github.com/radzenhq/radzen-blazor.git
synced 2026-07-08 18:16:08 +00:00
keyboard navigation improved
This commit is contained in:
@@ -938,6 +938,64 @@ namespace Radzen.Blazor.Tests
|
||||
Assert.Contains("2025", component.Find(".rz-calendar-title-button").TextContent);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_DrillDown_MonthGrid_Supports_Keyboard_Navigation()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<DateTime>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.NavigationMode, DatePickerNavigationMode.DrillDown);
|
||||
parameters.Add(p => p.InitialViewDate, new DateTime(2024, 5, 1));
|
||||
});
|
||||
|
||||
component.InvokeAsync(() => component.Find(".rz-calendar-title-button").Click());
|
||||
|
||||
var cells = component.FindAll(".rz-calendar-month-cell");
|
||||
Assert.Equal("0", cells[4].GetAttribute("tabindex"));
|
||||
|
||||
component.InvokeAsync(() => component.FindAll(".rz-calendar-month-cell")[4].KeyDown(new Microsoft.AspNetCore.Components.Web.KeyboardEventArgs { Code = "ArrowRight" }));
|
||||
cells = component.FindAll(".rz-calendar-month-cell");
|
||||
Assert.Equal("0", cells[5].GetAttribute("tabindex"));
|
||||
Assert.Equal("-1", cells[4].GetAttribute("tabindex"));
|
||||
|
||||
component.InvokeAsync(() => component.FindAll(".rz-calendar-month-cell")[5].KeyDown(new Microsoft.AspNetCore.Components.Web.KeyboardEventArgs { Code = "ArrowDown" }));
|
||||
Assert.Equal("0", component.FindAll(".rz-calendar-month-cell")[8].GetAttribute("tabindex"));
|
||||
|
||||
component.InvokeAsync(() => component.FindAll(".rz-calendar-month-cell")[8].KeyDown(new Microsoft.AspNetCore.Components.Web.KeyboardEventArgs { Code = "PageDown" }));
|
||||
Assert.Contains("2025", component.Find(".rz-calendar-title-button").TextContent);
|
||||
Assert.Equal("0", component.FindAll(".rz-calendar-month-cell")[8].GetAttribute("tabindex"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_DrillDown_YearGrid_Supports_Keyboard_Navigation()
|
||||
{
|
||||
using var ctx = new TestContext();
|
||||
ctx.JSInterop.Mode = JSRuntimeMode.Loose;
|
||||
ctx.JSInterop.SetupModule("_content/Radzen.Blazor/Radzen.Blazor.js");
|
||||
|
||||
var component = ctx.RenderComponent<RadzenDatePicker<DateTime>>(parameters =>
|
||||
{
|
||||
parameters.Add(p => p.NavigationMode, DatePickerNavigationMode.DrillDown);
|
||||
parameters.Add(p => p.InitialViewDate, new DateTime(2024, 5, 1));
|
||||
});
|
||||
|
||||
component.InvokeAsync(() => component.Find(".rz-calendar-title-button").Click());
|
||||
component.InvokeAsync(() => component.Find(".rz-calendar-title-button").Click());
|
||||
|
||||
var focused = component.FindAll(".rz-calendar-year-cell").Single(c => c.GetAttribute("tabindex") == "0");
|
||||
Assert.Equal("2024", focused.TextContent);
|
||||
|
||||
component.InvokeAsync(() => component.FindAll(".rz-calendar-year-cell").Single(c => c.GetAttribute("tabindex") == "0").KeyDown(new Microsoft.AspNetCore.Components.Web.KeyboardEventArgs { Code = "ArrowRight" }));
|
||||
Assert.Equal("2025", component.FindAll(".rz-calendar-year-cell").Single(c => c.GetAttribute("tabindex") == "0").TextContent);
|
||||
|
||||
component.InvokeAsync(() => component.FindAll(".rz-calendar-year-cell").Single(c => c.GetAttribute("tabindex") == "0").KeyDown(new Microsoft.AspNetCore.Components.Web.KeyboardEventArgs { Code = "PageDown" }));
|
||||
Assert.Equal("2037", component.FindAll(".rz-calendar-year-cell").Single(c => c.GetAttribute("tabindex") == "0").TextContent);
|
||||
Assert.Contains(component.FindAll(".rz-calendar-year-cell"), c => c.TextContent == "2034");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatePicker_DrillDown_Disables_Months_Outside_MinMax()
|
||||
{
|
||||
|
||||
@@ -80,7 +80,8 @@
|
||||
{
|
||||
var isActiveMonth = month.Value == GetCalendarMonth(CurrentDate);
|
||||
var monthDisabled = Disabled || IsMonthDisabled(month.Value);
|
||||
<button type="button" class="@($"rz-calendar-month-cell{(isActiveMonth ? " rz-state-active" : "")}")" disabled="@monthDisabled" aria-disabled="@(monthDisabled ? "true" : "false")" aria-pressed="@(isActiveMonth ? "true" : "false")" @onclick="@(() => SelectViewMonth(month.Value))">@month.Name</button>
|
||||
<button type="button" id="@($"{GetId()}-month-{month.Value}")" class="@($"rz-calendar-month-cell{(isActiveMonth ? " rz-state-active" : "")}")" disabled="@monthDisabled" aria-disabled="@(monthDisabled ? "true" : "false")" aria-pressed="@(isActiveMonth ? "true" : "false")" tabindex="@(isActiveMonth && !monthDisabled ? TabIndex : -1)"
|
||||
@onclick="@(() => SelectViewMonth(month.Value))" @onkeydown="@OnMonthCellKeyDown" @onkeydown:preventDefault="preventKeyPress" @onkeydown:stopPropagation="stopKeydownPropagation">@month.Name</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@@ -92,7 +93,8 @@
|
||||
@foreach (var year in YearsInView)
|
||||
{
|
||||
var isActiveYear = year.Value == GetCalendarYear(CurrentDate);
|
||||
<button type="button" class="@($"rz-calendar-year-cell{(isActiveYear ? " rz-state-active" : "")}")" disabled="@Disabled" aria-disabled="@(Disabled ? "true" : "false")" aria-pressed="@(isActiveYear ? "true" : "false")" @onclick="@(() => SelectViewYear(year.Value))">@year.Name</button>
|
||||
<button type="button" id="@($"{GetId()}-year-{year.Value}")" class="@($"rz-calendar-year-cell{(isActiveYear ? " rz-state-active" : "")}")" disabled="@Disabled" aria-disabled="@(Disabled ? "true" : "false")" aria-pressed="@(isActiveYear ? "true" : "false")" tabindex="@(isActiveYear ? TabIndex : -1)"
|
||||
@onclick="@(() => SelectViewYear(year.Value))" @onkeydown="@OnYearCellKeyDown" @onkeydown:preventDefault="preventKeyPress" @onkeydown:stopPropagation="stopKeydownPropagation">@year.Name</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -258,6 +258,104 @@ namespace Radzen.Blazor
|
||||
currentView = CalendarViewKind.Days;
|
||||
}
|
||||
|
||||
bool shouldFocusMonthCell;
|
||||
bool shouldFocusYearCell;
|
||||
|
||||
void OnMonthCellKeyDown(KeyboardEventArgs args)
|
||||
{
|
||||
var key = args.Code != null ? args.Code : args.Key;
|
||||
|
||||
var delta = key switch
|
||||
{
|
||||
"ArrowLeft" => -1,
|
||||
"ArrowRight" => 1,
|
||||
"ArrowUp" => -3,
|
||||
"ArrowDown" => 3,
|
||||
_ => 0
|
||||
};
|
||||
|
||||
if (delta != 0)
|
||||
{
|
||||
preventKeyPress = true;
|
||||
stopKeydownPropagation = true;
|
||||
NavigateMonth(delta);
|
||||
shouldFocusMonthCell = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (key == "PageUp" || key == "PageDown")
|
||||
{
|
||||
preventKeyPress = true;
|
||||
stopKeydownPropagation = true;
|
||||
NavigateYear(key == "PageUp" ? -1 : 1);
|
||||
shouldFocusMonthCell = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (key == "Home" || key == "End")
|
||||
{
|
||||
preventKeyPress = true;
|
||||
stopKeydownPropagation = true;
|
||||
SetMonth(key == "Home" ? 1 : Culture.Calendar.GetMonthsInYear(GetCalendarYear(CurrentDate)));
|
||||
shouldFocusMonthCell = true;
|
||||
return;
|
||||
}
|
||||
|
||||
preventKeyPress = false;
|
||||
stopKeydownPropagation = false;
|
||||
}
|
||||
|
||||
void OnYearCellKeyDown(KeyboardEventArgs args)
|
||||
{
|
||||
var key = args.Code != null ? args.Code : args.Key;
|
||||
|
||||
var delta = key switch
|
||||
{
|
||||
"ArrowLeft" => -1,
|
||||
"ArrowRight" => 1,
|
||||
"ArrowUp" => -3,
|
||||
"ArrowDown" => 3,
|
||||
_ => 0
|
||||
};
|
||||
|
||||
if (delta != 0)
|
||||
{
|
||||
preventKeyPress = true;
|
||||
stopKeydownPropagation = true;
|
||||
NavigateYear(delta);
|
||||
shouldFocusYearCell = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (key == "PageUp" || key == "PageDown")
|
||||
{
|
||||
preventKeyPress = true;
|
||||
stopKeydownPropagation = true;
|
||||
NavigateYearsPage(key == "PageUp" ? -1 : 1);
|
||||
shouldFocusYearCell = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (key == "Home" || key == "End")
|
||||
{
|
||||
preventKeyPress = true;
|
||||
stopKeydownPropagation = true;
|
||||
|
||||
var target = key == "Home" ? YearsPageStart : Math.Min(YearsPageStart + YearsPerPage - 1, YearTo);
|
||||
|
||||
if (target != GetCalendarYear(CurrentDate))
|
||||
{
|
||||
SetYear(target);
|
||||
}
|
||||
|
||||
shouldFocusYearCell = true;
|
||||
return;
|
||||
}
|
||||
|
||||
preventKeyPress = false;
|
||||
stopKeydownPropagation = false;
|
||||
}
|
||||
|
||||
internal void NavigateMonth(int direction)
|
||||
{
|
||||
if (Disabled)
|
||||
@@ -2511,6 +2609,26 @@ namespace Radzen.Blazor
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
if (shouldFocusMonthCell && JSRuntime != null)
|
||||
{
|
||||
shouldFocusMonthCell = false;
|
||||
try
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("Radzen.focusElement", $"{GetId()}-month-{GetCalendarMonth(CurrentDate)}");
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
if (shouldFocusYearCell && JSRuntime != null)
|
||||
{
|
||||
shouldFocusYearCell = false;
|
||||
try
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("Radzen.focusElement", $"{GetId()}-year-{GetCalendarYear(CurrentDate)}");
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidationStateChanged(object? sender, ValidationStateChangedEventArgs e)
|
||||
|
||||
@@ -225,6 +225,9 @@
|
||||
new KeyboardShortcut { Key = "Shift + PageUp on open popup", Action = "Move focus to the same day of the previous year." },
|
||||
new KeyboardShortcut { Key = "Shift + PageDown on open popup", Action = "Move focus to the same day of the next year." },
|
||||
new KeyboardShortcut { Key = "Enter or Space in an opened popup", Action = "Select the focused day and close the popup." },
|
||||
new KeyboardShortcut { Key = "Enter or Space on the calendar title (drill-down navigation)", Action = "Drill up to month selection, then to year selection." },
|
||||
new KeyboardShortcut { Key = "Arrow keys in month or year selection (drill-down navigation)", Action = "Move focus between months or years." },
|
||||
new KeyboardShortcut { Key = "PageUp or PageDown in month or year selection (drill-down navigation)", Action = "Move focus to the previous or next year or page of years." },
|
||||
new KeyboardShortcut { Key = "Esc in an opened popup", Action = "Close the popup." }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -75,6 +75,11 @@
|
||||
new KeyboardShortcut { Key = "Shift + PageUp on open popup", Action = "Move focus to the same day of the previous year." },
|
||||
new KeyboardShortcut { Key = "Shift + PageDown on open popup", Action = "Move focus to the same day of the next year." },
|
||||
new KeyboardShortcut { Key = "Enter or Space in an opened popup", Action = "Select the focused day as start or end of the range. The popup closes when the range is complete." },
|
||||
new KeyboardShortcut { Key = "Enter or Space on the calendar title", Action = "Drill up to month selection, then to year selection." },
|
||||
new KeyboardShortcut { Key = "Arrow keys in month or year selection", Action = "Move focus between months or years." },
|
||||
new KeyboardShortcut { Key = "PageUp or PageDown in month selection", Action = "Move focus to the same month of the previous or next year." },
|
||||
new KeyboardShortcut { Key = "PageUp or PageDown in year selection", Action = "Move focus to the previous or next page of years." },
|
||||
new KeyboardShortcut { Key = "Enter or Space on a month or year", Action = "Select it and drill back down." },
|
||||
new KeyboardShortcut { Key = "Esc in an opened popup", Action = "Close the popup." }
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user