Skip to content

Commit 94920e4

Browse files
committed
feat: support up/down arrow navigation in the new room list
1 parent a8ca4ff commit 94920e4

File tree

3 files changed

+63
-29
lines changed

3 files changed

+63
-29
lines changed

res/css/views/rooms/RoomListPanel/_RoomListItemView.pcss

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,6 @@
1818
all: unset;
1919
cursor: pointer;
2020

21-
&:hover {
22-
background-color: var(--cpd-color-bg-action-secondary-hovered);
23-
}
24-
2521
.mx_RoomListItemView_container {
2622
padding-left: var(--cpd-space-2x);
2723
font: var(--cpd-font-body-md-regular);
@@ -56,12 +52,12 @@
5652
}
5753
}
5854

59-
.mx_RoomListItemView_menu_open {
55+
.mx_RoomListItemView_hover {
6056
background-color: var(--cpd-color-bg-action-secondary-hovered);
57+
}
6158

62-
.mx_RoomListItemView_content {
63-
padding-right: var(--cpd-space-1-5x);
64-
}
59+
.mx_RoomListItemView_menu_open .mx_RoomListItemView_content {
60+
padding-right: var(--cpd-space-1-5x);
6561
}
6662

6763
.mx_RoomListItemView_selected {

src/components/views/rooms/RoomListPanel/RoomList.tsx

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ import { AutoSizer, List, type ListRowProps } from "react-virtualized";
1111
import { type RoomListViewState } from "../../../viewmodels/roomlist/RoomListViewModel";
1212
import { _t } from "../../../../languageHandler";
1313
import { RoomListItemView } from "./RoomListItemView";
14+
import { RovingTabIndexProvider } from "../../../../accessibility/RovingTabIndex";
15+
import { getKeyBindingsManager } from "../../../../KeyBindingsManager";
16+
import { KeyBindingAction } from "../../../../accessibility/KeyboardShortcuts";
17+
import { Landmark, LandmarkNavigation } from "../../../../accessibility/LandmarkNavigation";
1418

1519
interface RoomListProps {
1620
/**
@@ -32,21 +36,45 @@ export function RoomList({ vm: { rooms, activeIndex } }: RoomListProps): JSX.Ele
3236

3337
// The first div is needed to make the virtualized list take all the remaining space and scroll correctly
3438
return (
35-
<div className="mx_RoomList" data-testid="room-list">
36-
<AutoSizer>
37-
{({ height, width }) => (
38-
<List
39-
aria-label={_t("room_list|list_title")}
40-
className="mx_RoomList_List"
41-
rowRenderer={roomRendererMemoized}
42-
rowCount={rooms.length}
43-
rowHeight={48}
44-
height={height}
45-
width={width}
46-
scrollToIndex={activeIndex ?? 0}
47-
/>
48-
)}
49-
</AutoSizer>
50-
</div>
39+
<RovingTabIndexProvider handleHomeEnd={true} handleUpDown={true}>
40+
{({ onKeyDownHandler }) => (
41+
<div
42+
className="mx_RoomList"
43+
data-testid="room-list"
44+
onKeyDown={(ev) => {
45+
const navAction = getKeyBindingsManager().getNavigationAction(ev);
46+
if (
47+
navAction === KeyBindingAction.NextLandmark ||
48+
navAction === KeyBindingAction.PreviousLandmark
49+
) {
50+
LandmarkNavigation.findAndFocusNextLandmark(
51+
Landmark.ROOM_LIST,
52+
navAction === KeyBindingAction.PreviousLandmark,
53+
);
54+
ev.stopPropagation();
55+
ev.preventDefault();
56+
return;
57+
}
58+
onKeyDownHandler(ev);
59+
}}
60+
>
61+
<AutoSizer>
62+
{({ height, width }) => (
63+
<List
64+
aria-label={_t("room_list|list_title")}
65+
className="mx_RoomList_List"
66+
rowRenderer={roomRendererMemoized}
67+
rowCount={rooms.length}
68+
rowHeight={48}
69+
height={height}
70+
width={width}
71+
scrollToIndex={activeIndex ?? 0}
72+
tabIndex={-1}
73+
/>
74+
)}
75+
</AutoSizer>
76+
</div>
77+
)}
78+
</RovingTabIndexProvider>
5179
);
5280
}

src/components/views/rooms/RoomListPanel/RoomListItemView.tsx

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { Flex } from "../../../utils/Flex";
1414
import { RoomListItemMenuView } from "./RoomListItemMenuView";
1515
import { NotificationDecoration } from "../NotificationDecoration";
1616
import { RoomAvatarView } from "../../avatars/RoomAvatarView";
17+
import { useRovingTabIndex } from "../../../../accessibility/RovingTabIndex";
1718

1819
interface RoomListItemViewProps extends React.HTMLAttributes<HTMLButtonElement> {
1920
/**
@@ -34,22 +35,27 @@ export const RoomListItemView = memo(function RoomListItemView({
3435
isSelected,
3536
...props
3637
}: RoomListItemViewProps): JSX.Element {
38+
const [onFocus, isActive, ref] = useRovingTabIndex();
3739
const vm = useRoomListItemViewModel(room);
3840

3941
const [isHover, setIsHover] = useState(false);
4042
const [isMenuOpen, setIsMenuOpen] = useState(false);
4143
// The compound menu in RoomListItemMenuView needs to be rendered when the hover menu is shown
4244
// Using display: none; and then display:flex when hovered in CSS causes the menu to be misaligned
43-
const showHoverDecoration = (isMenuOpen || isHover) && vm.showHoverMenu;
45+
const showHoverDecoration = isMenuOpen || isHover;
46+
const showHoverMenu = showHoverDecoration && vm.showHoverMenu;
4447

45-
const isNotificationDecorationVisible = !showHoverDecoration && vm.showNotificationDecoration;
48+
const isInvitation = vm.notificationState.invited;
49+
const isNotificationDecorationVisible = isInvitation || (!showHoverDecoration && vm.showNotificationDecoration);
4650

4751
return (
4852
<button
53+
ref={ref}
4954
className={classNames("mx_RoomListItemView", {
5055
mx_RoomListItemView_empty: !isNotificationDecorationVisible && !showHoverDecoration,
5156
mx_RoomListItemView_notification_decoration: isNotificationDecorationVisible,
52-
mx_RoomListItemView_menu_open: showHoverDecoration,
57+
mx_RoomListItemView_hover: showHoverDecoration,
58+
mx_RoomListItemView_menu_open: showHoverMenu,
5359
mx_RoomListItemView_selected: isSelected,
5460
mx_RoomListItemView_bold: vm.isBold,
5561
})}
@@ -59,8 +65,12 @@ export const RoomListItemView = memo(function RoomListItemView({
5965
onClick={() => vm.openRoom()}
6066
onMouseOver={() => setIsHover(true)}
6167
onMouseOut={() => setIsHover(false)}
62-
onFocus={() => setIsHover(true)}
68+
onFocus={() => {
69+
setIsHover(true);
70+
onFocus();
71+
}}
6372
onBlur={() => setIsHover(false)}
73+
tabIndex={isActive ? 0 : -1}
6474
{...props}
6575
>
6676
{/* We need this extra div between the button and the content in order to add a padding which is not messing with the virtualized list */}
@@ -79,7 +89,7 @@ export const RoomListItemView = memo(function RoomListItemView({
7989
</div>
8090
<div className="mx_RoomListItemView_messagePreview">{vm.messagePreview}</div>
8191
</div>
82-
{showHoverDecoration ? (
92+
{showHoverMenu ? (
8393
<RoomListItemMenuView
8494
room={room}
8595
setMenuOpen={(isOpen) => {

0 commit comments

Comments
 (0)