diff --git a/apps/web/playwright/snapshots/composer/CIDER.spec.ts/emoji-autocomplete-linux.png b/apps/web/playwright/snapshots/composer/CIDER.spec.ts/emoji-autocomplete-linux.png
index 3c3aae1e6f..e4b06f18fa 100644
Binary files a/apps/web/playwright/snapshots/composer/CIDER.spec.ts/emoji-autocomplete-linux.png and b/apps/web/playwright/snapshots/composer/CIDER.spec.ts/emoji-autocomplete-linux.png differ
diff --git a/apps/web/playwright/snapshots/composer/CIDER.spec.ts/mention-linux.png b/apps/web/playwright/snapshots/composer/CIDER.spec.ts/mention-linux.png
index 008e450ca3..de157ac749 100644
Binary files a/apps/web/playwright/snapshots/composer/CIDER.spec.ts/mention-linux.png and b/apps/web/playwright/snapshots/composer/CIDER.spec.ts/mention-linux.png differ
diff --git a/apps/web/playwright/snapshots/composer/CIDER.spec.ts/narrow-linux.png b/apps/web/playwright/snapshots/composer/CIDER.spec.ts/narrow-linux.png
index 366271b095..ad6104ee09 100644
Binary files a/apps/web/playwright/snapshots/composer/CIDER.spec.ts/narrow-linux.png and b/apps/web/playwright/snapshots/composer/CIDER.spec.ts/narrow-linux.png differ
diff --git a/apps/web/playwright/snapshots/composer/RTE.spec.ts/narrow-linux.png b/apps/web/playwright/snapshots/composer/RTE.spec.ts/narrow-linux.png
index 455917ebe2..6fffd4825d 100644
Binary files a/apps/web/playwright/snapshots/composer/RTE.spec.ts/narrow-linux.png and b/apps/web/playwright/snapshots/composer/RTE.spec.ts/narrow-linux.png differ
diff --git a/apps/web/playwright/snapshots/crypto/history-sharing.spec.ts/shared-history-invite-accepted-linux.png b/apps/web/playwright/snapshots/crypto/history-sharing.spec.ts/shared-history-invite-accepted-linux.png
index 460b54b845..88e258952d 100644
Binary files a/apps/web/playwright/snapshots/crypto/history-sharing.spec.ts/shared-history-invite-accepted-linux.png and b/apps/web/playwright/snapshots/crypto/history-sharing.spec.ts/shared-history-invite-accepted-linux.png differ
diff --git a/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-collapse.spec.ts/room-list-collapse-default-linux.png b/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-collapse.spec.ts/room-list-collapse-default-linux.png
index e9e4462317..5d4c860653 100644
Binary files a/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-collapse.spec.ts/room-list-collapse-default-linux.png and b/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-collapse.spec.ts/room-list-collapse-default-linux.png differ
diff --git a/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-filter-sort.spec.ts/room-panel-empty-room-list-linux.png b/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-filter-sort.spec.ts/room-panel-empty-room-list-linux.png
index 124cdc674d..2c931bc4e4 100644
Binary files a/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-filter-sort.spec.ts/room-panel-empty-room-list-linux.png and b/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-filter-sort.spec.ts/room-panel-empty-room-list-linux.png differ
diff --git a/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-panel.spec.ts/room-list-panel-linux.png b/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-panel.spec.ts/room-list-panel-linux.png
index f19a6cb0c4..3435f26daf 100644
Binary files a/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-panel.spec.ts/room-list-panel-linux.png and b/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-panel.spec.ts/room-list-panel-linux.png differ
diff --git a/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-panel.spec.ts/room-list-panel-smallscreen-linux.png b/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-panel.spec.ts/room-list-panel-smallscreen-linux.png
index ef53f1d6fb..a635fd4824 100644
Binary files a/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-panel.spec.ts/room-list-panel-smallscreen-linux.png and b/apps/web/playwright/snapshots/left-panel/room-list-panel/room-list-panel.spec.ts/room-list-panel-smallscreen-linux.png differ
diff --git a/apps/web/res/css/views/rooms/RoomListPanel/_RoomListPanel.pcss b/apps/web/res/css/views/rooms/RoomListPanel/_RoomListPanel.pcss
index eb1f6e5fe5..91b7bc8720 100644
--- a/apps/web/res/css/views/rooms/RoomListPanel/_RoomListPanel.pcss
+++ b/apps/web/res/css/views/rooms/RoomListPanel/_RoomListPanel.pcss
@@ -8,5 +8,4 @@
.mx_RoomListPanel {
background-color: var(--cpd-color-bg-canvas-default);
height: 100%;
- border-right: 1px solid var(--cpd-color-bg-subtle-primary);
}
diff --git a/apps/web/src/viewmodels/structures/ResizerViewModel.ts b/apps/web/src/viewmodels/structures/ResizerViewModel.ts
index 7e14523590..322db045f1 100644
--- a/apps/web/src/viewmodels/structures/ResizerViewModel.ts
+++ b/apps/web/src/viewmodels/structures/ResizerViewModel.ts
@@ -47,8 +47,15 @@ export class ResizerViewModel
*/
private panelHandle?: PanelImperativeHandle;
+ /**
+ * Needed to distinguish between a drag and a click on the separator.
+ */
+ private readonly mouseClickHandler: MouseClickHandler;
+
public constructor() {
super(undefined, getInitialState());
+ // Run onSeparatorClick when the separator is clicked.
+ this.mouseClickHandler = new MouseClickHandler(this.onSeparatorClick);
}
public onLeftPanelResize = debounce((panelSize: PanelSize): void => {
@@ -79,13 +86,25 @@ export class ResizerViewModel
this.panelHandle = handle;
};
- public onSeparatorClick = (): void => {
+ private onSeparatorClick = (): void => {
if (this.panelHandle?.isCollapsed()) {
const lastSize = SettingsStore.getValue("RoomList.panelSize");
this.panelHandle.resize(`${lastSize ?? 100}%`);
}
};
+ public onPointerUp = (): void => {
+ this.mouseClickHandler.onPointerUp();
+ };
+
+ public onPointerMove = (): void => {
+ this.mouseClickHandler.onPointerMove();
+ };
+
+ public onPointerDown = (): void => {
+ this.mouseClickHandler.onPointerDown();
+ };
+
public onFocus = (): void => {
/**
* The intention here is to make the separator visible when it is focused by keyboard
@@ -108,3 +127,26 @@ export class ResizerViewModel
this.snapshot.merge({ isFocusedViaKeyboard: false });
};
}
+
+/**
+ * Dragging the separator will emit a click event.
+ * This class uses pointer event handlers to distinguish between a drag and a click
+ * on the separator.
+ */
+class MouseClickHandler {
+ public constructor(private readonly onClick: () => void) {}
+
+ private isResize = false;
+
+ public onPointerUp = (): void => {
+ if (!this.isResize) this.onClick();
+ };
+
+ public onPointerDown = (): void => {
+ this.isResize = false;
+ };
+
+ public onPointerMove = (): void => {
+ this.isResize = true;
+ };
+}
diff --git a/apps/web/test/viewmodels/structures/ResizerViewModel-test.ts b/apps/web/test/viewmodels/structures/ResizerViewModel-test.ts
index c913eb6fc3..c8f3d5251e 100644
--- a/apps/web/test/viewmodels/structures/ResizerViewModel-test.ts
+++ b/apps/web/test/viewmodels/structures/ResizerViewModel-test.ts
@@ -63,12 +63,33 @@ describe("LeftPanelResizerViewModel", () => {
});
});
- it("should noop on onSeparatorClick() when handle is not yet set", () => {
+ it("should noop on click when handle is not yet set", () => {
const vm = new ResizerViewModel();
- expect(() => vm.onSeparatorClick()).not.toThrow();
+ expect(() => {
+ // Click
+ vm.onPointerDown();
+ vm.onPointerUp();
+ }).not.toThrow();
});
- describe("should expand panel on onSeparatorClick()", () => {
+ it("should noop on mouse drag", () => {
+ const vm = new ResizerViewModel();
+ SettingsStore.setValue("RoomList.panelSize", null, SettingLevel.DEVICE, 34);
+ const mockHandle = {
+ resize: jest.fn(),
+ isCollapsed: jest.fn().mockReturnValue(true),
+ } as unknown as PanelImperativeHandle;
+ vm.setPanelHandle(mockHandle);
+
+ // Simulate drag
+ vm.onPointerDown();
+ vm.onPointerMove();
+ vm.onPointerUp();
+
+ expect(mockHandle.resize).not.toHaveBeenCalledWith("34%");
+ });
+
+ describe("should expand panel on click()", () => {
it("to last non-zero width that the user set", () => {
const vm = new ResizerViewModel();
SettingsStore.setValue("RoomList.panelSize", null, SettingLevel.DEVICE, 34);
@@ -78,7 +99,9 @@ describe("LeftPanelResizerViewModel", () => {
} as unknown as PanelImperativeHandle;
vm.setPanelHandle(mockHandle);
- vm.onSeparatorClick();
+ // Simulate click
+ vm.onPointerDown();
+ vm.onPointerUp();
expect(mockHandle.resize).toHaveBeenCalledWith("34%");
});
@@ -91,7 +114,9 @@ describe("LeftPanelResizerViewModel", () => {
} as unknown as PanelImperativeHandle;
vm.setPanelHandle(mockHandle);
- vm.onSeparatorClick();
+ // Simulate click
+ vm.onPointerDown();
+ vm.onPointerUp();
expect(mockHandle.resize).toHaveBeenCalledWith("100%");
});
diff --git a/packages/playwright-common/src/fixtures/panel.ts b/packages/playwright-common/src/fixtures/panel.ts
index b7d6d4b5da..4d6c098b86 100644
--- a/packages/playwright-common/src/fixtures/panel.ts
+++ b/packages/playwright-common/src/fixtures/panel.ts
@@ -12,7 +12,7 @@ import { test as base } from "./services.js";
* Rounding this number to a whole number would mean updating a whole
* bunch of screenshots.
*/
-const LEFT_PANEL_WIDTH = "369.6875px";
+const LEFT_PANEL_WIDTH = "368.6875px";
export const test = base.extend<{
/**
diff --git a/packages/shared-components/__vis__/linux/__baselines__/resize/separator/SeparatorView.stories.tsx/left-panel-expanded-auto.png b/packages/shared-components/__vis__/linux/__baselines__/resize/separator/SeparatorView.stories.tsx/left-panel-expanded-auto.png
index adfeaa7f5d..81f90b0ad1 100644
Binary files a/packages/shared-components/__vis__/linux/__baselines__/resize/separator/SeparatorView.stories.tsx/left-panel-expanded-auto.png and b/packages/shared-components/__vis__/linux/__baselines__/resize/separator/SeparatorView.stories.tsx/left-panel-expanded-auto.png differ
diff --git a/packages/shared-components/src/resize/separator/SeparatorView.module.css b/packages/shared-components/src/resize/separator/SeparatorView.module.css
index 17eaae0fce..ec418c398b 100644
--- a/packages/shared-components/src/resize/separator/SeparatorView.module.css
+++ b/packages/shared-components/src/resize/separator/SeparatorView.module.css
@@ -6,21 +6,25 @@
*/
.separator {
- /* Hide the separator by default */
- display: none;
/* Necessary to avoid weird focus outlines (doubled on one side, absent on one side etc...) */
outline-offset: -2px;
}
-.separator[data-separator="hover"],
+/* When type=border, we just render a 1px border */
+.separator[data-separator-type="border"] {
+ width: 0px;
+ border-right: 1px solid var(--cpd-color-bg-subtle-primary);
+}
+
+/* When type=bar, we render the 12px separator */
+.separator[data-separator-type="bar"] {
+ width: 12px;
+ display: flex;
+ align-items: center;
+ border-right: 1px solid var(--cpd-color-bg-subtle-primary);
+}
+
+.separator[data-separator-type="bar"][data-separator="hover"],
.separator:focus-visible {
background: var(--cpd-color-bg-action-tertiary-hovered);
}
-
-.visible {
- /* Show the separator when the left panel is collapsed */
- display: flex;
- align-items: center;
- width: 12px;
- border-right: 1px solid var(--cpd-color-bg-subtle-primary);
-}
diff --git a/packages/shared-components/src/resize/separator/SeparatorView.stories.tsx b/packages/shared-components/src/resize/separator/SeparatorView.stories.tsx
index a3366c591e..93a589f4b4 100644
--- a/packages/shared-components/src/resize/separator/SeparatorView.stories.tsx
+++ b/packages/shared-components/src/resize/separator/SeparatorView.stories.tsx
@@ -16,8 +16,15 @@ import { Flex } from "../../core/utils/Flex";
type SeparatorViewProps = ResizerViewSnapshot & SeparatorViewActions;
-const Wrapper = ({ onFocus, onBlur, onSeparatorClick, ...snapshot }: SeparatorViewProps): JSX.Element => {
- const vm = useMockedViewModel(snapshot, { onFocus, onBlur, onSeparatorClick });
+const Wrapper = ({
+ onFocus,
+ onBlur,
+ onPointerDown,
+ onPointerMove,
+ onPointerUp,
+ ...snapshot
+}: SeparatorViewProps): JSX.Element => {
+ const vm = useMockedViewModel(snapshot, { onFocus, onBlur, onPointerDown, onPointerMove, onPointerUp });
return ;
};
@@ -30,7 +37,9 @@ const meta = {
args: {
onFocus: fn(),
onBlur: fn(),
- onSeparatorClick: fn(),
+ onPointerUp: fn(),
+ onPointerMove: fn(),
+ onPointerDown: fn(),
isCollapsed: true,
isFocusedViaKeyboard: false,
},
diff --git a/packages/shared-components/src/resize/separator/SeparatorView.test.tsx b/packages/shared-components/src/resize/separator/SeparatorView.test.tsx
index 1887acbb57..2202e42093 100644
--- a/packages/shared-components/src/resize/separator/SeparatorView.test.tsx
+++ b/packages/shared-components/src/resize/separator/SeparatorView.test.tsx
@@ -23,7 +23,9 @@ class MockViewModel extends BaseViewModel implemen
}
public onBlur: () => void = vi.fn();
public onFocus: () => void = vi.fn();
- public onSeparatorClick: () => void = vi.fn();
+ public onPointerUp: () => void = vi.fn();
+ public onPointerMove: () => void = vi.fn();
+ public onPointerDown: () => void = vi.fn();
}
function renderPanel(initialSnapshot?: Partial): MockViewModel {
@@ -55,11 +57,12 @@ describe("", () => {
expect(container).toMatchSnapshot();
});
- it("should call onSeparatorClick() when clicked", async () => {
+ it("should call onPointerDown and onPointerUp on pointer events", async () => {
const vm = renderPanel();
const separator = screen.getByRole("separator");
await userEvent.click(separator);
- expect(vm.onSeparatorClick).toHaveBeenCalledOnce();
+ expect(vm.onPointerDown).toHaveBeenCalledOnce();
+ expect(vm.onPointerUp).toHaveBeenCalledOnce();
});
it("should call onFocus and onBlur when receiving/loosing focus", async () => {
diff --git a/packages/shared-components/src/resize/separator/SeparatorView.tsx b/packages/shared-components/src/resize/separator/SeparatorView.tsx
index c77c38764a..2d95f1940e 100644
--- a/packages/shared-components/src/resize/separator/SeparatorView.tsx
+++ b/packages/shared-components/src/resize/separator/SeparatorView.tsx
@@ -18,9 +18,19 @@ import { useI18n } from "../../core/i18n/i18nContext";
export interface SeparatorViewActions {
/**
- * onClick handler for the separator.
+ * onPointerUp handler for separator.
*/
- onSeparatorClick: () => void;
+ onPointerUp: () => void;
+
+ /**
+ * onPointerMove handler for separator.
+ */
+ onPointerMove: () => void;
+
+ /**
+ * onPointerDown handler for separator.
+ */
+ onPointerDown: () => void;
/**
* onFocus handler for the separator.
@@ -45,28 +55,39 @@ export function SeparatorView({ vm, className }: Props): React.ReactNode {
const { translate: _t } = useI18n();
const { isCollapsed, isFocusedViaKeyboard } = useViewModel(vm);
- const classes = classNames(styles.separator, className, {
- [styles.visible]: isCollapsed || isFocusedViaKeyboard,
- });
+ /**
+ * There are two types of separator:
+ * - bar: This shows a thick bar separator with a resize icon in the middle; shown when the panel is collapsed.
+ * - border: This is just a 1px wide separator; shown when the panel is expanded.
+ */
+ const type = isCollapsed || isFocusedViaKeyboard ? "bar" : "border";
+
+ const barContent = (
+
+
+
+ );
return (
-
-
-
+ {type === "bar" ? barContent : null}
);
}
diff --git a/packages/shared-components/src/resize/separator/__snapshots__/SeparatorView.test.tsx.snap b/packages/shared-components/src/resize/separator/__snapshots__/SeparatorView.test.tsx.snap
index a901029401..695dde68da 100644
--- a/packages/shared-components/src/resize/separator/__snapshots__/SeparatorView.test.tsx.snap
+++ b/packages/shared-components/src/resize/separator/__snapshots__/SeparatorView.test.tsx.snap
@@ -36,8 +36,9 @@ exports[` > renders Default story 1`] = `
aria-valuemax="99.502"
aria-valuemin="0"
aria-valuenow="0"
- class="SeparatorView-module_separator Separator SeparatorView-module_visible"
+ class="SeparatorView-module_separator Separator"
data-separator="inactive"
+ data-separator-type="bar"
data-testid="react-use-id-3"
id="react-use-id-3"
role="separator"
@@ -115,8 +116,9 @@ exports[` > renders KeyboardFocused story 1`] = `
aria-valuemax="99.502"
aria-valuemin="0"
aria-valuenow="49.751"
- class="SeparatorView-module_separator Separator SeparatorView-module_visible"
+ class="SeparatorView-module_separator Separator"
data-separator="inactive"
+ data-separator-type="bar"
data-testid="react-use-id-3"
id="react-use-id-3"
role="separator"
@@ -188,29 +190,21 @@ exports[` > renders LeftPanelExpanded story 1`] = `
+ />