Room list: fix keyboard navigation on sections (#33809)
* fix: keyboard navigation on sections * test: add e2e test for keyboard navigation * fix: add more keyboard navigation * test: add e2e tests * chore: remove useless check
This commit is contained in:
@@ -290,4 +290,131 @@ test.describe("Room list sections", () => {
|
||||
await expect(roomList.getByRole("row", { name: "no unread room" })).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Section keyboard navigation", () => {
|
||||
test.beforeEach(async ({ app }) => {
|
||||
// A favourite room forces section mode and gives us a non-trivial first section.
|
||||
const favouriteId = await app.client.createRoom({ name: "favourite room" });
|
||||
await app.client.evaluate(async (client, roomId) => {
|
||||
await client.setRoomTag(roomId, "m.favourite");
|
||||
}, favouriteId);
|
||||
|
||||
// A chats-section room so we have a second section to navigate to.
|
||||
await app.client.createRoom({ name: "chat room" });
|
||||
});
|
||||
|
||||
test("Arrow Down/Up move focus through sections and rooms", async ({ page }) => {
|
||||
const roomList = getRoomList(page);
|
||||
const favouritesHeader = getSectionHeader(page, "Favourites");
|
||||
// In treegrid mode, a room renders as <div role="row"><div role="gridcell"><button …></button></div></div>.
|
||||
// Only the inner <button> is focusable, so target it by role for focus assertions.
|
||||
const favRoomButton = roomList.getByRole("button", { name: "Open room favourite room" });
|
||||
const chatsHeader = getSectionHeader(page, "Chats");
|
||||
|
||||
await expect(favouritesHeader).toBeVisible();
|
||||
await expect(favRoomButton).toBeVisible();
|
||||
await expect(chatsHeader).toBeVisible();
|
||||
|
||||
await favouritesHeader.focus();
|
||||
await expect(favouritesHeader).toBeFocused();
|
||||
|
||||
// Down moves into the favourite section's room.
|
||||
await page.keyboard.press("ArrowDown");
|
||||
await expect(favRoomButton).toBeFocused();
|
||||
|
||||
// Down again jumps to the next section header.
|
||||
await page.keyboard.press("ArrowDown");
|
||||
await expect(chatsHeader).toBeFocused();
|
||||
|
||||
// Up reverses the traversal.
|
||||
await page.keyboard.press("ArrowUp");
|
||||
await expect(favRoomButton).toBeFocused();
|
||||
|
||||
await page.keyboard.press("ArrowUp");
|
||||
await expect(favouritesHeader).toBeFocused();
|
||||
});
|
||||
|
||||
test("Arrow Right expands a collapsed section", async ({ page }) => {
|
||||
const favouritesHeader = getSectionHeader(page, "Favourites");
|
||||
const favRoom = getRoomList(page).getByRole("row", { name: "Open room favourite room" });
|
||||
|
||||
// Collapse the section via click so we know we start expanded=false.
|
||||
await favouritesHeader.click();
|
||||
await expect(favouritesHeader).toHaveAttribute("aria-expanded", "false");
|
||||
await expect(favRoom).not.toBeVisible();
|
||||
|
||||
await favouritesHeader.focus();
|
||||
await page.keyboard.press("ArrowRight");
|
||||
|
||||
await expect(favouritesHeader).toHaveAttribute("aria-expanded", "true");
|
||||
await expect(favRoom).toBeVisible();
|
||||
});
|
||||
|
||||
test("Arrow Right is a no-op on an already-expanded section", async ({ page }) => {
|
||||
const favouritesHeader = getSectionHeader(page, "Favourites");
|
||||
await expect(favouritesHeader).toHaveAttribute("aria-expanded", "true");
|
||||
|
||||
await favouritesHeader.focus();
|
||||
await page.keyboard.press("ArrowRight");
|
||||
|
||||
await expect(favouritesHeader).toHaveAttribute("aria-expanded", "true");
|
||||
});
|
||||
|
||||
test("Arrow Left collapses an expanded section", async ({ page }) => {
|
||||
const favouritesHeader = getSectionHeader(page, "Favourites");
|
||||
const favRoom = getRoomList(page).getByRole("row", { name: "Open room favourite room" });
|
||||
|
||||
await expect(favouritesHeader).toHaveAttribute("aria-expanded", "true");
|
||||
await expect(favRoom).toBeVisible();
|
||||
|
||||
await favouritesHeader.focus();
|
||||
await page.keyboard.press("ArrowLeft");
|
||||
|
||||
await expect(favouritesHeader).toHaveAttribute("aria-expanded", "false");
|
||||
await expect(favRoom).not.toBeVisible();
|
||||
});
|
||||
|
||||
test("Arrow Left is a no-op on an already-collapsed section", async ({ page }) => {
|
||||
const favouritesHeader = getSectionHeader(page, "Favourites");
|
||||
|
||||
await favouritesHeader.click();
|
||||
await expect(favouritesHeader).toHaveAttribute("aria-expanded", "false");
|
||||
|
||||
await favouritesHeader.focus();
|
||||
await page.keyboard.press("ArrowLeft");
|
||||
|
||||
await expect(favouritesHeader).toHaveAttribute("aria-expanded", "false");
|
||||
});
|
||||
|
||||
test("Arrow Right on an expanded section with rooms moves focus to its first room", async ({ page }) => {
|
||||
const favouritesHeader = getSectionHeader(page, "Favourites");
|
||||
const favRoomButton = getRoomList(page).getByRole("button", { name: "Open room favourite room" });
|
||||
|
||||
await expect(favouritesHeader).toHaveAttribute("aria-expanded", "true");
|
||||
|
||||
await favouritesHeader.focus();
|
||||
await expect(favouritesHeader).toBeFocused();
|
||||
|
||||
await page.keyboard.press("ArrowRight");
|
||||
|
||||
// Focus must move to the first room in the section, not the next section header.
|
||||
await expect(favRoomButton).toBeFocused();
|
||||
// The section should remain expanded.
|
||||
await expect(favouritesHeader).toHaveAttribute("aria-expanded", "true");
|
||||
});
|
||||
|
||||
test("Arrow Left on the first room of a section moves focus back to the section header", async ({ page }) => {
|
||||
const favouritesHeader = getSectionHeader(page, "Favourites");
|
||||
const favRoomButton = getRoomList(page).getByRole("button", { name: "Open room favourite room" });
|
||||
|
||||
await expect(favouritesHeader).toHaveAttribute("aria-expanded", "true");
|
||||
|
||||
await favRoomButton.focus();
|
||||
await expect(favRoomButton).toBeFocused();
|
||||
|
||||
await page.keyboard.press("ArrowLeft");
|
||||
|
||||
await expect(favouritesHeader).toBeFocused();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user