Room list: center focus outline of room list item (#32637)

* fix: in room list item, replace bottom padding by top and bottom

Outline focus is not aligned when only using a padding bottom. Replace
by a padding-top and a padding-bottom.

The padding top is not added on the first item and the padding bottom
neither on the last item

* test: update snapshot

* test: update storybook screenshot

* test: update screenshot

* chore: use css var instead of duplicating the logic

* chore: add new stories for first and last item case

* test: add new screenshots following the new stories
This commit is contained in:
Florian Duros
2026-02-25 19:45:22 +00:00
committed by GitHub
parent 9e802ebca6
commit dd1db8c14b
40 changed files with 57 additions and 27 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

@@ -9,17 +9,20 @@
/* Remove button default style */
background: unset;
border: none;
padding: 0;
text-align: unset;
/* Needed to position the selected marker */
position: relative;
cursor: pointer;
width: 100%;
/* Gap between items, accounted for in ROOM_LIST_ITEM_HEIGHT */
padding-bottom: var(--cpd-space-2x);
/* 44px height + padding bottom */
min-height: calc(44px + var(--cpd-space-2x));
--padding-top: var(--cpd-space-1x);
--padding-bottom: var(--cpd-space-1x);
padding: var(--padding-top) 0 var(--padding-bottom) 0;
/* 44px height + padding */
min-height: calc(44px + var(--padding-top) + var(--padding-bottom));
font: var(--cpd-font-body-md-regular);
letter-spacing: var(--cpd-font-letter-spacing-body-md);
@@ -93,10 +96,9 @@
.container::before {
content: "";
position: absolute;
/* Marker height is 34px, room list item height is 44px. So position is at 5px at and bottom */
top: 5px;
/* Add the padding bottom to center */
bottom: calc(5px + var(--cpd-space-2x));
/* Marker height is 34px, room list item height is 44px. So position is at 5px at and bottom + padding */
top: calc(5px + var(--padding-top));
bottom: calc(5px + var(--padding-bottom));
left: 0;
width: 4px;
background-color: var(--cpd-color-bg-accent-rest);
@@ -113,3 +115,15 @@
.hoverMenu svg {
fill: var(--cpd-color-icon-primary);
}
/*
* Remove top padding for the first item and bottom padding for the last item to align them with the container border.
* And adjust the selected marker position accordingly.
*/
.firstItem {
--padding-top: 0px;
}
.lastItem {
--padding-bottom: 0px;
}
@@ -87,7 +87,7 @@ const meta = {
...defaultSnapshot,
isSelected: false,
isFocused: false,
roomIndex: 0,
roomIndex: 1,
roomCount: 10,
onOpenRoom: fn(),
onMarkAsRead: fn(),
@@ -260,3 +260,17 @@ export const WithZoom: Story = {
),
],
};
export const FirstItem: Story = {
args: {
roomIndex: 0,
isSelected: true,
},
};
export const LastItem: Story = {
args: {
roomIndex: 9,
isSelected: true,
},
};
@@ -160,6 +160,8 @@ export const RoomListItemView = memo(function RoomListItemView({
className={classNames(styles.roomListItem, "mx_RoomListItemView", {
[styles.selected]: isSelected,
[styles.bold]: item.isBold,
[styles.firstItem]: roomIndex === 0,
[styles.lastItem]: roomIndex === roomCount - 1,
mx_RoomListItemView_selected: isSelected,
})}
gap="var(--cpd-space-3x)"
@@ -12,7 +12,7 @@ exports[`<RoomListItemView /> > renders Bold story 1`] = `
<button
aria-haspopup="menu"
aria-label="Open room Team Updates"
aria-posinset="1"
aria-posinset="2"
aria-selected="false"
aria-setsize="10"
class="flex roomListItem mx_RoomListItemView bold"
@@ -147,7 +147,7 @@ exports[`<RoomListItemView /> > renders Default story 1`] = `
<button
aria-haspopup="menu"
aria-label="Open room General"
aria-posinset="1"
aria-posinset="2"
aria-selected="false"
aria-setsize="10"
class="flex roomListItem mx_RoomListItemView"
@@ -282,7 +282,7 @@ exports[`<RoomListItemView /> > renders Invitation story 1`] = `
<button
aria-haspopup="menu"
aria-label="Open room Secret Project invitation."
aria-posinset="1"
aria-posinset="2"
aria-selected="false"
aria-setsize="10"
class="flex roomListItem mx_RoomListItemView"
@@ -435,7 +435,7 @@ exports[`<RoomListItemView /> > renders NoMessagePreview story 1`] = `
<button
aria-haspopup="menu"
aria-label="Open room General"
aria-posinset="1"
aria-posinset="2"
aria-selected="false"
aria-setsize="10"
class="flex roomListItem mx_RoomListItemView"
@@ -564,7 +564,7 @@ exports[`<RoomListItemView /> > renders Selected story 1`] = `
<button
aria-haspopup="menu"
aria-label="Open room General"
aria-posinset="1"
aria-posinset="2"
aria-selected="true"
aria-setsize="10"
class="flex roomListItem mx_RoomListItemView selected mx_RoomListItemView_selected"
@@ -699,7 +699,7 @@ exports[`<RoomListItemView /> > renders UnsentMessage story 1`] = `
<button
aria-haspopup="menu"
aria-label="Open room General with an unsent message."
aria-posinset="1"
aria-posinset="2"
aria-selected="false"
aria-setsize="10"
class="flex roomListItem mx_RoomListItemView"
@@ -852,7 +852,7 @@ exports[`<RoomListItemView /> > renders WithHoverMenu story 1`] = `
<button
aria-haspopup="menu"
aria-label="Open room General"
aria-posinset="1"
aria-posinset="2"
aria-selected="false"
aria-setsize="10"
class="flex roomListItem mx_RoomListItemView"
@@ -987,7 +987,7 @@ exports[`<RoomListItemView /> > renders WithMention story 1`] = `
<button
aria-haspopup="menu"
aria-label="Open room General with 1 unread mention."
aria-posinset="1"
aria-posinset="2"
aria-selected="false"
aria-setsize="10"
class="flex roomListItem mx_RoomListItemView bold"
@@ -1145,7 +1145,7 @@ exports[`<RoomListItemView /> > renders WithNotification story 1`] = `
<button
aria-haspopup="menu"
aria-label="Open room General with 3 unread messages."
aria-posinset="1"
aria-posinset="2"
aria-selected="false"
aria-setsize="10"
class="flex roomListItem mx_RoomListItemView bold"
@@ -85,7 +85,7 @@ exports[`<RoomListView /> > renders Default story 1`] = `
aria-posinset="1"
aria-selected="false"
aria-setsize="20"
class="flex roomListItem mx_RoomListItemView bold"
class="flex roomListItem mx_RoomListItemView bold firstItem"
data-state="closed"
role="option"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: stretch; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
@@ -2553,7 +2553,7 @@ exports[`<RoomListView /> > renders Default story 1`] = `
aria-posinset="20"
aria-selected="false"
aria-setsize="20"
class="flex roomListItem mx_RoomListItemView"
class="flex roomListItem mx_RoomListItemView lastItem"
data-state="closed"
role="option"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: stretch; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
@@ -3411,7 +3411,7 @@ exports[`<RoomListView /> > renders LargeList story 1`] = `
aria-posinset="1"
aria-selected="false"
aria-setsize="100"
class="flex roomListItem mx_RoomListItemView bold"
class="flex roomListItem mx_RoomListItemView bold firstItem"
data-state="closed"
role="option"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: stretch; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
@@ -8365,7 +8365,7 @@ exports[`<RoomListView /> > renders SmallList story 1`] = `
aria-posinset="1"
aria-selected="false"
aria-setsize="5"
class="flex roomListItem mx_RoomListItemView bold"
class="flex roomListItem mx_RoomListItemView bold firstItem"
data-state="closed"
role="option"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: stretch; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
@@ -8886,7 +8886,7 @@ exports[`<RoomListView /> > renders SmallList story 1`] = `
aria-posinset="5"
aria-selected="false"
aria-setsize="5"
class="flex roomListItem mx_RoomListItemView"
class="flex roomListItem mx_RoomListItemView lastItem"
data-state="closed"
role="option"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: stretch; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
@@ -9094,7 +9094,7 @@ exports[`<RoomListView /> > renders WithActiveFilter story 1`] = `
aria-posinset="1"
aria-selected="false"
aria-setsize="20"
class="flex roomListItem mx_RoomListItemView bold"
class="flex roomListItem mx_RoomListItemView bold firstItem"
data-state="closed"
role="option"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: stretch; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
@@ -11562,7 +11562,7 @@ exports[`<RoomListView /> > renders WithActiveFilter story 1`] = `
aria-posinset="20"
aria-selected="false"
aria-setsize="20"
class="flex roomListItem mx_RoomListItemView"
class="flex roomListItem mx_RoomListItemView lastItem"
data-state="closed"
role="option"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: stretch; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
@@ -36,7 +36,7 @@ exports[`<VirtualizedRoomListView /> > renders Default story 1`] = `
aria-posinset="1"
aria-selected="true"
aria-setsize="10"
class="flex roomListItem mx_RoomListItemView selected bold mx_RoomListItemView_selected"
class="flex roomListItem mx_RoomListItemView selected bold firstItem mx_RoomListItemView_selected"
data-state="closed"
role="option"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: stretch; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"
@@ -1208,7 +1208,7 @@ exports[`<VirtualizedRoomListView /> > renders Default story 1`] = `
aria-posinset="10"
aria-selected="false"
aria-setsize="10"
class="flex roomListItem mx_RoomListItemView bold"
class="flex roomListItem mx_RoomListItemView bold lastItem"
data-state="closed"
role="option"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: stretch; --mx-flex-justify: start; --mx-flex-gap: var(--cpd-space-3x); --mx-flex-wrap: nowrap;"