Skip to content

Commit 42048fd

Browse files
authored
Room list: collapse sections when a room is dragged (#34290)
* Collapse sections when a room is dragged * Update vm to follow the drag methods renaming * Update e2e tests * Update screenshots
1 parent 064616b commit 42048fd

9 files changed

Lines changed: 42 additions & 41 deletions

File tree

apps/web/playwright/e2e/left-panel/room-list-panel/utils.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,22 +60,27 @@ export async function assertRoomInSection(page: Page, sectionName: string, roomN
6060
export async function dragRoomToSection(page: Page, roomName: string, sectionName: string): Promise<void> {
6161
const sourceRow = getRoomList(page).getByRole("row", { name: `Open room ${roomName}` });
6262
const source = sourceRow.locator("button").first();
63-
const target = getSectionHeader(page, sectionName);
6463

6564
await expect(sourceRow).toBeVisible();
6665
await expect(source).toBeVisible();
67-
await expect(target).toBeVisible();
6866

67+
// The source is safe to cache because it is grabbed before the sections collapse.
6968
const sourceBox = await getBoundingBox(source, `room ${roomName}`);
70-
const targetBox = await getBoundingBox(target, `section ${sectionName}`);
71-
7269
const sourceX = sourceBox.x + sourceBox.width / 2;
7370
const sourceY = sourceBox.y + sourceBox.height / 2;
74-
const targetY = targetBox.y + targetBox.height / 2;
7571

7672
// Grab the room
7773
await page.mouse.move(sourceX, sourceY);
7874
await page.mouse.down();
75+
// Move past the 5px PointerSensor activation threshold so the drag actually starts.
76+
// This triggers onSectionOrRoomDragStart, which collapses all sections.
77+
await page.mouse.move(sourceX, sourceY + 10, { steps: 5 });
78+
79+
// Re-query the target now that the sections have collapsed and the layout reflowed.
80+
const target = getSectionHeader(page, sectionName);
81+
const targetBox = await getBoundingBox(target, `section ${sectionName}`);
82+
const targetY = targetBox.y + targetBox.height / 2;
83+
7984
// Move the room on the section header
8085
await page.mouse.move(sourceX, targetY, { steps: 10 });
8186
// Drop the room
2.21 KB
Loading

apps/web/src/viewmodels/room-list/RoomListViewModel.test.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1716,7 +1716,7 @@ describe("RoomListViewModel", () => {
17161716
it("should collapse every section on drag start", () => {
17171717
expect(viewModel.getSectionHeaderViewModel(DefaultTagID.Favourite).isExpanded).toBe(true);
17181718

1719-
viewModel.onSectionDragStart();
1719+
viewModel.onSectionOrRoomDragStart();
17201720

17211721
expect(viewModel.getSectionHeaderViewModel(DefaultTagID.Favourite).isExpanded).toBe(false);
17221722
expect(viewModel.getSectionHeaderViewModel(CHATS_TAG).isExpanded).toBe(false);
@@ -1731,8 +1731,8 @@ describe("RoomListViewModel", () => {
17311731
// Collapse Favourite before the drag; other sections remain expanded
17321732
viewModel.getSectionHeaderViewModel(DefaultTagID.Favourite).onClick();
17331733

1734-
viewModel.onSectionDragStart();
1735-
viewModel.onSectionDragEnd();
1734+
viewModel.onSectionOrRoomDragStart();
1735+
viewModel.onSectionOrRoomDragEnd();
17361736

17371737
expect(viewModel.getSectionHeaderViewModel(DefaultTagID.Favourite).isExpanded).toBe(false);
17381738
expect(viewModel.getSectionHeaderViewModel(CHATS_TAG).isExpanded).toBe(true);
@@ -1752,13 +1752,13 @@ describe("RoomListViewModel", () => {
17521752
it("should re-snapshot expansion state on each drag start", () => {
17531753
// First cycle: Favourite is collapsed before the drag
17541754
viewModel.getSectionHeaderViewModel(DefaultTagID.Favourite).onClick();
1755-
viewModel.onSectionDragStart();
1756-
viewModel.onSectionDragEnd();
1755+
viewModel.onSectionOrRoomDragStart();
1756+
viewModel.onSectionOrRoomDragEnd();
17571757

17581758
// Between cycles: collapse CHATS_TAG as well
17591759
viewModel.getSectionHeaderViewModel(CHATS_TAG).onClick();
1760-
viewModel.onSectionDragStart();
1761-
viewModel.onSectionDragEnd();
1760+
viewModel.onSectionOrRoomDragStart();
1761+
viewModel.onSectionOrRoomDragEnd();
17621762

17631763
// The second drag end must restore the state captured at the second drag start
17641764
// (Favourite collapsed, CHATS_TAG collapsed, LowPriority expanded), not the first cycle's snapshot.
@@ -1768,7 +1768,7 @@ describe("RoomListViewModel", () => {
17681768
});
17691769

17701770
it("should be a no-op when drag end is called without drag start", () => {
1771-
viewModel.onSectionDragEnd();
1771+
viewModel.onSectionOrRoomDragEnd();
17721772

17731773
expect(viewModel.getSectionHeaderViewModel(DefaultTagID.Favourite).isExpanded).toBe(true);
17741774
expect(viewModel.getSectionHeaderViewModel(CHATS_TAG).isExpanded).toBe(true);

apps/web/src/viewmodels/room-list/RoomListViewModel.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -961,7 +961,7 @@ export class RoomListViewModel
961961
void this.updateRoomListData(false, null, sourceTag);
962962
};
963963

964-
public onSectionDragStart = (): void => {
964+
public onSectionOrRoomDragStart = (): void => {
965965
this.savedExpansionStates.clear();
966966
for (const [tag, sectionVM] of this.roomSectionHeaderViewModels) {
967967
this.savedExpansionStates.set(tag, sectionVM.isExpanded);
@@ -970,7 +970,7 @@ export class RoomListViewModel
970970
void this.updateRoomListData();
971971
};
972972

973-
public onSectionDragEnd = (): void => {
973+
public onSectionOrRoomDragEnd = (): void => {
974974
for (const [tag, expanded] of this.savedExpansionStates) {
975975
const sectionVM = this.roomSectionHeaderViewModels.get(tag);
976976
if (sectionVM) sectionVM.isExpanded = expanded;

packages/shared-components/src/room-list/RoomListView/RoomListView.stories.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ const RoomListViewWrapperImpl = ({
4545
setScrollToIndex,
4646
changeRoomSection,
4747
changeSectionOrder,
48-
onSectionDragStart,
49-
onSectionDragEnd,
48+
onSectionOrRoomDragStart,
49+
onSectionOrRoomDragEnd,
5050
...rest
5151
}: RoomListViewProps): JSX.Element => {
5252
const vm = useMockedViewModel(rest, {
@@ -62,8 +62,8 @@ const RoomListViewWrapperImpl = ({
6262
setScrollToIndex,
6363
changeRoomSection,
6464
changeSectionOrder,
65-
onSectionDragStart,
66-
onSectionDragEnd,
65+
onSectionOrRoomDragStart,
66+
onSectionOrRoomDragEnd,
6767
});
6868
return <RoomListView vm={vm} renderAvatar={renderAvatarProp} />;
6969
};
@@ -121,8 +121,8 @@ const meta = {
121121
setScrollToIndex: fn(),
122122
changeRoomSection: fn(),
123123
changeSectionOrder: fn(),
124-
onSectionDragStart: fn(),
125-
onSectionDragEnd: fn(),
124+
onSectionOrRoomDragStart: fn(),
125+
onSectionOrRoomDragEnd: fn(),
126126
},
127127
parameters: {
128128
design: {

packages/shared-components/src/room-list/RoomListView/RoomListView.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,9 @@ export interface RoomListViewActions {
101101
/** Called to change the order of sections */
102102
changeSectionOrder: (sourceTag: string, targetTag: string) => void;
103103
/** Called when a section drag starts — collapses all sections */
104-
onSectionDragStart: () => void;
104+
onSectionOrRoomDragStart: () => void;
105105
/** Called when a section drag ends (drop or cancel) — restores expansion states */
106-
onSectionDragEnd: () => void;
106+
onSectionOrRoomDragEnd: () => void;
107107
}
108108

109109
/**

packages/shared-components/src/room-list/VirtualizedRoomListView/VirtualizedRoomListView.stories.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ const RoomListWrapperImpl = ({
4141
renderAvatar: renderAvatarProp,
4242
changeRoomSection,
4343
changeSectionOrder,
44-
onSectionDragStart,
45-
onSectionDragEnd,
44+
onSectionOrRoomDragStart,
45+
onSectionOrRoomDragEnd,
4646
...rest
4747
}: RoomListStoryProps): JSX.Element => {
4848
const vm = useMockedViewModel(rest, {
@@ -58,8 +58,8 @@ const RoomListWrapperImpl = ({
5858
setScrollToIndex,
5959
changeRoomSection,
6060
changeSectionOrder,
61-
onSectionDragStart,
62-
onSectionDragEnd,
61+
onSectionOrRoomDragStart,
62+
onSectionOrRoomDragEnd,
6363
});
6464

6565
return (
@@ -104,8 +104,8 @@ const meta = {
104104
setScrollToIndex: fn(),
105105
changeRoomSection: fn(),
106106
changeSectionOrder: fn(),
107-
onSectionDragStart: fn(),
108-
onSectionDragEnd: fn(),
107+
onSectionOrRoomDragStart: fn(),
108+
onSectionOrRoomDragEnd: fn(),
109109
},
110110
parameters: {
111111
design: {

packages/shared-components/src/room-list/VirtualizedRoomListView/VirtualizedRoomListView.test.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ describe("<VirtualizedRoomListView />", () => {
9494
// reach them, so explicitly reset call history for the spies under test.
9595
(Sections.args.changeRoomSection as any).mockClear?.();
9696
(Sections.args.changeSectionOrder as any).mockClear?.();
97-
(Sections.args.onSectionDragStart as any).mockClear?.();
98-
(Sections.args.onSectionDragEnd as any).mockClear?.();
97+
(Sections.args.onSectionOrRoomDragStart as any).mockClear?.();
98+
(Sections.args.onSectionOrRoomDragEnd as any).mockClear?.();
9999
});
100100

101101
it("should call changeRoomSection when drag ends successfully", async () => {
@@ -192,8 +192,8 @@ describe("<VirtualizedRoomListView />", () => {
192192
await waitFor(() => {
193193
expect(Sections.args.changeSectionOrder).toHaveBeenCalledWith("favourites", "low-priority");
194194
});
195-
expect(Sections.args.onSectionDragStart).toHaveBeenCalled();
196-
expect(Sections.args.onSectionDragEnd).toHaveBeenCalled();
195+
expect(Sections.args.onSectionOrRoomDragStart).toHaveBeenCalled();
196+
expect(Sections.args.onSectionOrRoomDragEnd).toHaveBeenCalled();
197197
});
198198
});
199199

packages/shared-components/src/room-list/VirtualizedRoomListView/VirtualizedRoomListView.tsx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -581,17 +581,13 @@ export function VirtualizedRoomListView({ vm, renderAvatar, onKeyDown }: Virtual
581581
return (
582582
<DragDropProvider<RoomListDragData>
583583
onDragStart={(event) => {
584-
const { source } = event.operation;
585-
// Changing the state of sections (collapsed/expanded) while dragging a section header causes a double readback for the a11y announcement.
586-
if (isSectionDragData(source?.data)) {
587-
vm.onSectionDragStart();
588-
}
584+
// Changing the state of sections (collapsed/expanded) while dragging a section header or a room causes a double readback for the a11y announcement.
585+
vm.onSectionOrRoomDragStart();
589586
}}
590587
onDragEnd={(event) => {
591588
const { source, target } = event.operation;
592-
if (isSectionDragData(source?.data)) {
593-
vm.onSectionDragEnd();
594-
}
589+
vm.onSectionOrRoomDragEnd();
590+
595591
if (event.canceled || !source || !target) return;
596592
if (isSectionDragData(source.data)) {
597593
vm.changeSectionOrder(String(source.id), String(target.id));

0 commit comments

Comments
 (0)