Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
fecd7d1
feat: make calendar header btns focusable
GDamyanov Nov 21, 2025
912245d
feat: apply styles
GDamyanov Nov 24, 2025
f4f5af5
Merge branch 'main' into calendar-acc-improvements
GDamyanov Nov 24, 2025
c14faf6
fix: lint errors
GDamyanov Nov 24, 2025
3986aa6
test: start to adapt tests
GDamyanov Nov 24, 2025
a287660
Merge branch 'main' into calendar-acc-improvements
GDamyanov Nov 25, 2025
e3625d1
test: add test
GDamyanov Nov 25, 2025
51349fc
refactor: remove empty line
GDamyanov Nov 25, 2025
516b5ac
Merge branch 'main' into calendar-acc-improvements
GDamyanov Nov 25, 2025
18bd963
Merge branch 'main' into calendar-acc-improvements
GDamyanov Dec 8, 2025
418abd1
test: add assert
GDamyanov Dec 8, 2025
1fcfebf
test: fix tests
GDamyanov Dec 8, 2025
3706e3a
test:fix test
GDamyanov Dec 9, 2025
12f6a2d
test: fix test
GDamyanov Dec 9, 2025
4ebdcbb
Merge branch 'main' into calendar-acc-improvements
GDamyanov Dec 10, 2025
b8379bf
feat: focus current date if the calendar is opened from popover
GDamyanov Dec 16, 2025
7de9f22
Merge branch 'main' into calendar-acc-improvements
GDamyanov Dec 17, 2025
bfa378f
Merge branch 'main' into calendar-acc-improvements
GDamyanov Dec 17, 2025
2f3b4ac
fix: show correct tooltips for year range view
GDamyanov Dec 17, 2025
ee55271
fix: set correct tooltips and prevent action when right mouse key is …
GDamyanov Dec 17, 2025
53bc977
Merge branch 'main' into calendar-acc-improvements
GDamyanov Dec 17, 2025
dd026ce
Merge branch 'main' into calendar-acc-improvements
GDamyanov Dec 17, 2025
55127dd
fix: remove empty spaces
GDamyanov Dec 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 132 additions & 43 deletions packages/main/cypress/specs/Calendar.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,47 +51,62 @@ const getCalendarWithDisabledDates = (id, formatPattern, ranges, props = {}) =>
);

describe("Calendar general interaction", () => {
it("Focus goes into the current day item of the day picker", () => {
const date = new Date(Date.UTC(2000, 10, 22, 0, 0, 0));
cy.mount(getDefaultCalendar(date));

cy.ui5CalendarGetDay("#calendar1", "974851200")
.as("selectedDay");

cy.get("@selectedDay")
.realClick();

cy.get("@selectedDay")
.should("have.focus")
.realPress("Tab");
it("Focus goes into the header items and then to the current day item of the day picker", () => {
const calendarTestDate = new Date(Date.UTC(2000, 10, 22, 0, 0, 0));
cy.mount(getDefaultCalendar(calendarTestDate));

cy.get<Calendar>("#calendar1")
.shadow()
.find(".ui5-calheader")
.as("calheader");

cy.ui5CalendarGetDay("#calendar1", "974851200").as("selectedDay");

cy.get("#calendar1")
.realClick();

cy.realPress("Tab");

cy.get("@calheader")
.find("[data-ui5-cal-header-btn-month]")
.as("monthBtn");
.find("[data-ui5-cal-header-btn-prev]")
.as("prevBtn")
.should("have.attr", "tabindex", "0");

cy.get("@prevBtn")
.should("be.focused");

cy.realPress("Tab");

cy.get("@calheader")
.find("[data-ui5-cal-header-btn-month]")
.as("monthBtn")
.should("have.attr", "tabindex", "0");;

cy.get("@monthBtn")
.should("have.focus")
.realPress("Tab");
.should("be.focused");

cy.realPress("Tab");

cy.get("@calheader")
.find("[data-ui5-cal-header-btn-year]")
.as("yearBtn");

cy.get("@yearBtn")
.should("have.focus")
.realPress(["Shift", "Tab"]);
.should("be.focused");

cy.realPress("Tab");

cy.get("@monthBtn")
.should("have.focus")
.realPress(["Shift", "Tab"]);
cy.get("@calheader")
.find("[data-ui5-cal-header-btn-next]")
.as("nextBtn");

cy.get("@nextBtn")
.should("be.focused");

cy.realPress("Tab");

cy.get("@selectedDay")
.should("have.focus");
.should("be.focused");
});

it("Calendar focuses the selected year when yearpicker is opened", () => {
Expand Down Expand Up @@ -121,11 +136,32 @@ describe("Calendar general interaction", () => {
const date = new Date(Date.UTC(2000, 10, 22, 0, 0, 0));
cy.mount(getDefaultCalendar(date));

cy.ui5CalendarGetDay("#calendar1", "974851200")
cy.get<Calendar>("#calendar1")
.shadow()
.find(".ui5-calheader")
.as("calheader");

cy.ui5CalendarGetDay("#calendar1", "974851200").as("selectedDay");

cy.get("#calendar1")
.realClick();

cy.focused().realPress("Tab");
cy.focused().realPress("Space");
cy.realPress("Tab");

cy.get("@calheader")
.find("[data-ui5-cal-header-btn-prev]")
.as("prevBtn");

cy.get("@prevBtn")
.should("be.focused");

cy.realPress("Tab");

cy.get("@calheader")
.find("[data-ui5-cal-header-btn-month]")
.as("monthBtn");

cy.realPress("Space");

cy.get<Calendar>("#calendar1")
.shadow()
Expand All @@ -148,12 +184,46 @@ describe("Calendar general interaction", () => {
const date = new Date(Date.UTC(2000, 10, 22, 0, 0, 0));
cy.mount(getDefaultCalendar(date));

cy.ui5CalendarGetDay("#calendar1", "974851200")
.realClick();

cy.get<Calendar>("#calendar1")
.shadow()
.find(".ui5-calheader")
.as("calheader");

cy.focused().realPress("Tab");
cy.focused().realPress("Tab");
cy.focused().realPress("Space");
cy.ui5CalendarGetDay("#calendar1", "974851200").as("selectedDay");

cy.get("#calendar1")
.realClick();

cy.realPress("Tab");

cy.get("@calheader")
.find("[data-ui5-cal-header-btn-prev]")
.as("prevBtn");

cy.get("@prevBtn")
.should("be.focused");

cy.realPress("Tab");

cy.get("@calheader")
.find("[data-ui5-cal-header-btn-month]")
.as("monthBtn");

cy.get("@monthBtn")
.should("be.focused");


cy.realPress("Tab");

cy.get("@calheader")
.find("[data-ui5-cal-header-btn-year]")
.as("yearBtn");

cy.get("@yearBtn")
.should("be.focused");

cy.realPress("Space");

cy.get<Calendar>("#calendar1")
.shadow()
Expand Down Expand Up @@ -427,15 +497,15 @@ describe("Calendar general interaction", () => {
.should("have.focus");

cy.focused().realPress(["Shift", "F4"]);

// Wait for focus to settle before proceeding
cy.get<Calendar>("#calendar1")
.shadow()
.find("[ui5-yearpicker]")
.shadow()
.find("[tabindex='0']")
.should("have.focus");

cy.focused().realPress("PageUp");

cy.get<Calendar>("#calendar1")
Expand Down Expand Up @@ -1160,6 +1230,26 @@ describe("Calendar general interaction", () => {
});

describe("Calendar accessibility", () => {
it("Header prev/next buttons have correct title and tabindex", () => {
const date = new Date(Date.UTC(2025, 0, 15, 0, 0, 0));
cy.mount(getDefaultCalendar(date));

cy.get<Calendar>("#calendar1")
.shadow()
.find(".ui5-calheader")
.as("calheader");

cy.get("@calheader")
.find("[data-ui5-cal-header-btn-prev]")
.should("have.attr", "title")
.and("contain", "Previous Month (Pagedown)");

cy.get("@calheader")
.find("[data-ui5-cal-header-btn-next]")
.should("have.attr", "title")
.and("contain", "Next Month (Pageup)");
});

it("Should have proper aria-label attributes on header buttons", () => {
const date = new Date(Date.UTC(2000, 10, 22, 0, 0, 0));
cy.mount(getDefaultCalendar(date));
Expand Down Expand Up @@ -1435,7 +1525,7 @@ describe("Calendar accessibility", () => {
// Get the selected days and verify their aria-labels
cy.get("@selectedDays").each(($day, index) => {
cy.wrap($day).should("have.attr", "aria-label");

if (index === 0) {
// First day should contain "First date of range"
cy.wrap($day)
Expand All @@ -1457,22 +1547,22 @@ describe("Calendar accessibility", () => {
});

describe("Day Picker Tests", () => {
it.skip("Select day with Space", () => {
it.skip("Select day with Space", () => {
cy.mount(<Calendar id="calendar1"></Calendar>);

cy.get<Calendar>("#calendar1")
.shadow()
.find("[ui5-daypicker]")
.shadow()
.find(".ui5-dp-item--now")
.as("today");

cy.get("@today")
.realClick()
.should("be.focused")
.realPress("ArrowRight")
.realPress("Space");

cy.focused()
.invoke("attr", "data-sap-timestamp")
.then(timestampAttr => {
Expand All @@ -1481,7 +1571,7 @@ describe("Day Picker Tests", () => {
const expectedDate = new Date(Date.now() + 24 * 3600 * 1000).getDate();
expect(selectedDate).to.eq(expectedDate);
});

cy.get<Calendar>("#calendar1")
.should(($calendar) => {
const selectedDates = $calendar.prop("selectedDates");
Expand All @@ -1494,7 +1584,7 @@ describe("Day Picker Tests", () => {
const tomorrow = Math.floor(Date.UTC(today.getFullYear(), today.getMonth(), today.getDate() + 1, 0, 0, 0, 0) / 1000);

cy.mount(<Calendar id="calendar1"></Calendar>);

cy.get<Calendar>("#calendar1")
.shadow()
.find("[ui5-daypicker]")
Expand Down Expand Up @@ -1533,7 +1623,7 @@ describe("Day Picker Tests", () => {

it("Day names are correctly displayed", () => {
cy.mount(<Calendar id="calendar1"></Calendar>);

cy.get<Calendar>("#calendar1")
.shadow()
.find("[ui5-daypicker]")
Expand Down Expand Up @@ -1593,7 +1683,6 @@ describe("Day Picker Tests", () => {
const timestamp = parseInt(timestampAttr!);
const todayFromTimestamp = new Date(timestamp * 1000);
const actualToday = new Date();

expect(todayFromTimestamp.getDate()).to.equal(actualToday.getDate());
expect(todayFromTimestamp.getMonth()).to.equal(actualToday.getMonth());
expect(todayFromTimestamp.getFullYear()).to.equal(actualToday.getFullYear());
Expand Down
33 changes: 18 additions & 15 deletions packages/main/cypress/specs/DateTimePicker.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ describe("DateTimePicker general interaction", () => {
cy.get("[ui5-calendar]")
.shadow()
.as("calendar");

cy.realPress("Tab");
cy.realPress("Tab");

cy.get("@calendar")
.find("[ui5-daypicker]")
Expand Down Expand Up @@ -174,7 +177,7 @@ describe("DateTimePicker general interaction", () => {

cy.realPress("Tab");

// Simulate keyboard interactions
//Simulate keyboard interactions
cy.get("@dtp")
.shadow()
.find("[ui5-datetime-input]")
Expand Down Expand Up @@ -300,7 +303,7 @@ describe("DateTimePicker general interaction", () => {
.ui5DateTimePickerClose();
});

// Unstable test, needs investigation
//Unstable test, needs investigation
it("tests selection of 12:34:56 AM", () => {
setAnimationMode(AnimationMode.None);

Expand All @@ -324,8 +327,8 @@ describe("DateTimePicker general interaction", () => {

cy.get("@daypicker")
.find(".ui5-dp-item--selected")
.should("be.focused")
.realClick();
.realClick()
.should("be.focused");

cy.get("[ui5-time-selection-clocks]")
.shadow()
Expand Down Expand Up @@ -389,18 +392,18 @@ describe("DateTimePicker general interaction", () => {
.find("ui5-daypicker")
.as("daypicker");

// act: open the picker
//act: open the picker
cy.get<DateTimePicker>("@dtp")
.ui5DateTimePickerOpen();

// act: click today's date
//act: click today's date
cy.get("@daypicker")
.shadow()
.find("[data-sap-focus-ref]")
.should("be.focused")
.realClick();
.realClick()
.should("be.focused");

// act: confirm selection
//act: confirm selection
cy.get<DateTimePicker>("@dtp")
.ui5DateTimePickerGetSubmitButton()
.should("have.prop", "disabled", false);
Expand All @@ -412,7 +415,7 @@ describe("DateTimePicker general interaction", () => {
cy.get<DateTimePicker>("@dtp")
.ui5DateTimePickerExpectToBeClosed();

// assert: the value is not changed
//assert: the value is not changed
cy.get("@input")
.should("be.focused")
.and("have.attr", "value", "");
Expand Down Expand Up @@ -488,7 +491,7 @@ describe("DateTimePicker general interaction", () => {
.should("have.text", "Invalid entry");
});

// Unstable test, needs investigation
//Unstable test, needs investigation
it("tests change event is fired on submit", () => {
cy.mount(<DateTimePickerTemplate onChange={cy.stub().as("changeStub")} />);

Expand Down Expand Up @@ -525,10 +528,10 @@ describe("DateTimePicker general interaction", () => {
cy.get<DateTimePicker>("@dtp")
.ui5DateTimePickerExpectToBeClosed();

// Assert the change event was fired once
//Assert the change event was fired once
cy.get("@changeStub").should("have.been.calledOnce");

// Re-open the picker and submit without making a change
//Re-open the picker and submit without making a change
cy.get<DateTimePicker>("@dtp")
.ui5DateTimePickerOpen();

Expand All @@ -540,11 +543,11 @@ describe("DateTimePicker general interaction", () => {
.ui5DateTimePickerGetSubmitButton()
.realClick();

// Verify the picker is closed
//Verify the picker is closed
cy.get<DateTimePicker>("@dtp")
.ui5DateTimePickerExpectToBeClosed();

// The change event should not have been fired a second time.
//The change event should not have been fired a second time.
cy.get("@changeStub").should("have.been.calledOnce");
});
});
Expand Down
Loading
Loading