Implement new separator design (#33599)

* Remove `onFocus` and `onBlur` handlers

react-resizable-panels fixed the issue where focus-visible was not
working as expected, so we no longer need this bit of code.

* Implement new hover/focus separator

* Collapse the panel when clicking on the new focus separator

* Remove `isFocusedViaKeyboard` from snapshot

* Update tests and storybook

* Expand panel only on double click

* Add animation

* Fix tests

* Don't render border when type is bar

* Single click to expand, double click to collapse

* Change focus separator color
This commit is contained in:
R Midhun Suresh
2026-06-03 12:29:49 +00:00
committed by GitHub
parent 47f76c7d7a
commit 2c8fafdec4
14 changed files with 325 additions and 90 deletions
@@ -15,7 +15,6 @@ import {
type ResizerViewSnapshot, type ResizerViewSnapshot,
} from "@element-hq/web-shared-components"; } from "@element-hq/web-shared-components";
import { debounce } from "lodash"; import { debounce } from "lodash";
import whatInput from "what-input";
import SettingsStore from "../../settings/SettingsStore"; import SettingsStore from "../../settings/SettingsStore";
import { SettingLevel } from "../../settings/SettingLevel"; import { SettingLevel } from "../../settings/SettingLevel";
@@ -25,13 +24,11 @@ function getInitialState(): ResizerViewSnapshot {
return { return {
isCollapsed: true, isCollapsed: true,
initialSize: 0, initialSize: 0,
isFocusedViaKeyboard: false,
}; };
} }
return { return {
isCollapsed: false, isCollapsed: false,
initialSize: SettingsStore.getValue("RoomList.panelSize") ?? undefined, initialSize: SettingsStore.getValue("RoomList.panelSize") ?? undefined,
isFocusedViaKeyboard: false,
}; };
} }
@@ -87,12 +84,18 @@ export class ResizerViewModel
}; };
private onSeparatorClick = (): void => { private onSeparatorClick = (): void => {
// When panel is collapsed, single click should expand the panel.
if (this.panelHandle?.isCollapsed()) { if (this.panelHandle?.isCollapsed()) {
const lastSize = SettingsStore.getValue("RoomList.panelSize"); const lastSize = SettingsStore.getValue("RoomList.panelSize");
this.panelHandle.resize(`${lastSize ?? 100}%`); this.panelHandle.resize(`${lastSize ?? 100}%`);
} }
}; };
public onDoubleClick = (): void => {
// When the panel is expanded, double click should collapse.
if (!this.panelHandle?.isCollapsed()) this.panelHandle?.collapse();
};
public onPointerUp = (): void => { public onPointerUp = (): void => {
this.mouseClickHandler.onPointerUp(); this.mouseClickHandler.onPointerUp();
}; };
@@ -104,28 +107,6 @@ export class ResizerViewModel
public onPointerDown = (): void => { public onPointerDown = (): void => {
this.mouseClickHandler.onPointerDown(); this.mouseClickHandler.onPointerDown();
}; };
public onFocus = (): void => {
/**
* The intention here is to make the separator visible when it is focused by keyboard
* navigation i.e tabbing through the app.
*
* There's a good reason to take this approach instead of just relying on the focus-visible
* selector:
* When exactly an element gets focus-visible is determined by browser heuristics and usually
* interacting with the mouse will not give an element focus-visible.
* However with this separator on chrome, mouse interaction occasionally gives it focus-visible.
* The leads to flakey separator behaviour.
*/
const currentNavigation = whatInput.ask();
if (currentNavigation === "keyboard") {
this.snapshot.merge({ isFocusedViaKeyboard: true });
}
};
public onBlur = (): void => {
this.snapshot.merge({ isFocusedViaKeyboard: false });
};
} }
/** /**
@@ -7,7 +7,6 @@
import { waitFor } from "jest-matrix-react"; import { waitFor } from "jest-matrix-react";
import { type PanelImperativeHandle } from "@element-hq/web-shared-components"; import { type PanelImperativeHandle } from "@element-hq/web-shared-components";
import whatInput from "what-input";
import { ResizerViewModel } from "../../../src/viewmodels/structures/ResizerViewModel"; import { ResizerViewModel } from "../../../src/viewmodels/structures/ResizerViewModel";
import SettingsStore from "../../../src/settings/SettingsStore"; import SettingsStore from "../../../src/settings/SettingsStore";
@@ -27,7 +26,6 @@ describe("LeftPanelResizerViewModel", () => {
expect(vm.getSnapshot()).toStrictEqual({ expect(vm.getSnapshot()).toStrictEqual({
isCollapsed: true, isCollapsed: true,
initialSize: 0, initialSize: 0,
isFocusedViaKeyboard: false,
}); });
}); });
@@ -37,7 +35,6 @@ describe("LeftPanelResizerViewModel", () => {
expect(vm.getSnapshot()).toStrictEqual({ expect(vm.getSnapshot()).toStrictEqual({
isCollapsed: false, isCollapsed: false,
initialSize: 34, initialSize: 34,
isFocusedViaKeyboard: false,
}); });
}); });
@@ -46,7 +43,6 @@ describe("LeftPanelResizerViewModel", () => {
expect(vm.getSnapshot()).toStrictEqual({ expect(vm.getSnapshot()).toStrictEqual({
isCollapsed: false, isCollapsed: false,
initialSize: undefined, initialSize: undefined,
isFocusedViaKeyboard: false,
}); });
}); });
}); });
@@ -89,7 +85,7 @@ describe("LeftPanelResizerViewModel", () => {
expect(mockHandle.resize).not.toHaveBeenCalledWith("34%"); expect(mockHandle.resize).not.toHaveBeenCalledWith("34%");
}); });
describe("should expand panel on click()", () => { describe("should expand panel on double click when panel is collapsed", () => {
it("to last non-zero width that the user set", () => { it("to last non-zero width that the user set", () => {
const vm = new ResizerViewModel(); const vm = new ResizerViewModel();
SettingsStore.setValue("RoomList.panelSize", null, SettingLevel.DEVICE, 34); SettingsStore.setValue("RoomList.panelSize", null, SettingLevel.DEVICE, 34);
@@ -98,11 +94,9 @@ describe("LeftPanelResizerViewModel", () => {
isCollapsed: jest.fn().mockReturnValue(true), isCollapsed: jest.fn().mockReturnValue(true),
} as unknown as PanelImperativeHandle; } as unknown as PanelImperativeHandle;
vm.setPanelHandle(mockHandle); vm.setPanelHandle(mockHandle);
// Simulate click // Simulate click
vm.onPointerDown(); vm.onPointerDown();
vm.onPointerUp(); vm.onPointerUp();
expect(mockHandle.resize).toHaveBeenCalledWith("34%"); expect(mockHandle.resize).toHaveBeenCalledWith("34%");
}); });
@@ -113,22 +107,23 @@ describe("LeftPanelResizerViewModel", () => {
isCollapsed: jest.fn().mockReturnValue(true), isCollapsed: jest.fn().mockReturnValue(true),
} as unknown as PanelImperativeHandle; } as unknown as PanelImperativeHandle;
vm.setPanelHandle(mockHandle); vm.setPanelHandle(mockHandle);
// Simulate click // Simulate click
vm.onPointerDown(); vm.onPointerDown();
vm.onPointerUp(); vm.onPointerUp();
expect(mockHandle.resize).toHaveBeenCalledWith("100%"); expect(mockHandle.resize).toHaveBeenCalledWith("100%");
}); });
}); });
it("should set isFocusedViaKeyboard state correctly", () => { it("should collapse panel on click when panel is expanded", () => {
whatInput.ask = jest.fn().mockReturnValue("keyboard");
const vm = new ResizerViewModel(); const vm = new ResizerViewModel();
vm.onFocus(); const mockHandle = {
expect(vm.getSnapshot().isFocusedViaKeyboard).toStrictEqual(true); collapse: jest.fn(),
vm.onBlur(); isCollapsed: jest.fn().mockReturnValue(false),
expect(vm.getSnapshot().isFocusedViaKeyboard).toStrictEqual(false); } as unknown as PanelImperativeHandle;
vm.setPanelHandle(mockHandle);
vm.onDoubleClick();
expect(mockHandle.collapse).toHaveBeenCalled();
}); });
it("should resize to nearest whole number", () => { it("should resize to nearest whole number", () => {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

@@ -27,11 +27,6 @@ export interface ResizerViewSnapshot {
* This is the initial size of the panel if available; should be interpreted as percentage. * This is the initial size of the panel if available; should be interpreted as percentage.
*/ */
initialSize?: number; initialSize?: number;
/**
* Whether the separator is currently focused by navigating
* to it using keyboard input.
*/
isFocusedViaKeyboard: boolean;
} }
/** /**
@@ -41,14 +41,9 @@ const meta = {
component: LeftResizablePanelViewWrapper, component: LeftResizablePanelViewWrapper,
// This is a structural component, so nothing to visually test. // This is a structural component, so nothing to visually test.
tags: ["autodocs", "!snapshot"], tags: ["autodocs", "!snapshot"],
argTypes: {
// This snapshot state is not relevant for this View.
isFocusedViaKeyboard: { table: { disable: true } },
},
args: { args: {
initialSize: 20, initialSize: 20,
isCollapsed: false, isCollapsed: false,
isFocusedViaKeyboard: false,
onLeftPanelResize: fn(), onLeftPanelResize: fn(),
setPanelHandle: fn(), setPanelHandle: fn(),
}, },
@@ -8,12 +8,33 @@
.separator { .separator {
/* Necessary to avoid weird focus outlines (doubled on one side, absent on one side etc...) */ /* Necessary to avoid weird focus outlines (doubled on one side, absent on one side etc...) */
outline-offset: -2px; outline-offset: -2px;
/* We set the separator container to relative so that the child container (activeSeparatorContainer)
can be absolutely positioned */
position: relative;
display: flex;
justify-content: center;
} }
/* When type=border, we just render a 1px border */ /* When type=border, we just render a 1px border */
.separator[data-separator-type="border"] { .separator[data-separator-type="border"] {
width: 0px; /*
border-right: 1px solid var(--cpd-color-bg-subtle-primary); We reserve 1px of space even though the actual separator is absolute positioned.
This keeps the panel looking the same as when the border was part of the panel.
*/
width: 1px;
/* We can disable the browser focus ring since we show a thicker separator on focus */
outline: none;
.activeSeparatorContainer {
.activeSeparator {
width: 0px;
border-right: 1px solid var(--cpd-color-bg-subtle-primary);
outline: none;
}
}
} }
/* When type=bar, we render the 12px separator */ /* When type=bar, we render the 12px separator */
@@ -28,3 +49,36 @@
.separator:focus-visible { .separator:focus-visible {
background: var(--cpd-color-bg-action-tertiary-hovered); background: var(--cpd-color-bg-action-tertiary-hovered);
} }
/* When type=border and the user hovers/focuses the separator, we make the separator thicker */
.separator[data-separator-type="border"]:is([data-separator="hover"], [data-separator="active"]),
.separator[data-separator-type="border"]:focus-visible {
.activeSeparatorContainer {
.activeSeparator {
width: 0px;
border-right: 2px solid var(--cpd-color-border-interactive-secondary);
outline: none;
}
}
}
/* Container for the separator when the left panel is expanded */
.activeSeparatorContainer {
display: flex;
justify-content: center;
/*
By making this container absolutely positioned an wide enough to cover the entire hit region,
we're able to intercept all the pointer events.
Without this, we'd only capture the events within a narrow 2px/1px border.
*/
position: absolute;
width: 20px;
z-index: 100;
height: 100%;
.activeSeparator {
/* Fade in/ fade out the border */
transition: border 0.15s ease-in-out 0.3s;
}
}
@@ -17,14 +17,13 @@ import { Flex } from "../../core/utils/Flex";
type SeparatorViewProps = ResizerViewSnapshot & SeparatorViewActions; type SeparatorViewProps = ResizerViewSnapshot & SeparatorViewActions;
const Wrapper = ({ const Wrapper = ({
onFocus,
onBlur,
onPointerDown, onPointerDown,
onPointerMove, onPointerMove,
onPointerUp, onPointerUp,
onDoubleClick,
...snapshot ...snapshot
}: SeparatorViewProps): JSX.Element => { }: SeparatorViewProps): JSX.Element => {
const vm = useMockedViewModel(snapshot, { onFocus, onBlur, onPointerDown, onPointerMove, onPointerUp }); const vm = useMockedViewModel(snapshot, { onPointerDown, onPointerMove, onPointerUp, onDoubleClick });
return <SeparatorView className="Separator" vm={vm} />; return <SeparatorView className="Separator" vm={vm} />;
}; };
@@ -35,13 +34,11 @@ const meta = {
component: SeparatorViewWrapper, component: SeparatorViewWrapper,
tags: ["autodocs"], tags: ["autodocs"],
args: { args: {
onFocus: fn(),
onBlur: fn(),
onPointerUp: fn(), onPointerUp: fn(),
onPointerMove: fn(), onPointerMove: fn(),
onPointerDown: fn(), onPointerDown: fn(),
onDoubleClick: fn(),
isCollapsed: true, isCollapsed: true,
isFocusedViaKeyboard: false,
}, },
parameters: { parameters: {
design: { design: {
@@ -118,16 +115,17 @@ export const KeyboardFocused: Story = {
decorators: [(Story) => commonDecorator(Story)], decorators: [(Story) => commonDecorator(Story)],
args: { args: {
isCollapsed: false, isCollapsed: false,
isFocusedViaKeyboard: true,
}, },
play: async ({ canvas, canvasElement }) => { play: async ({ canvas, canvasElement }) => {
const separator = canvas.getByRole("separator"); const separator = canvas.getByRole("separator");
separator.focus(); separator.focus();
// Wait for animations to finish
await new Promise((r) => setTimeout(r, 500));
await expect(canvasElement).toMatchImageSnapshot(); await expect(canvasElement).toMatchImageSnapshot();
}, },
}; };
export const Hover: Story = { export const HoverWhenCollapsed: Story = {
// We'll manually take a screenshot for this story // We'll manually take a screenshot for this story
tags: ["autodocs", "!snapshot"], tags: ["autodocs", "!snapshot"],
decorators: [ decorators: [
@@ -159,3 +157,47 @@ export const Hover: Story = {
await expect(canvasElement).toMatchImageSnapshot(); await expect(canvasElement).toMatchImageSnapshot();
}, },
}; };
export const HoverWhenExpanded: Story = {
// We'll manually take a screenshot for this story
tags: ["autodocs", "!snapshot"],
args: {
isCollapsed: false,
},
decorators: [
(Story) => (
<div
style={{
height: "600px",
}}
>
<ResizableGroup>
<div
style={{
height: "600px",
width: "200px",
}}
/>
<Panel collapsible defaultSize="0" minSize="200px" maxSize="400px">
<Flex tabIndex={0} align="center" justify="center">
LEFT CONTENT
</Flex>
</Panel>
<Story />
<Panel>
<Flex align="center" justify="center">
MAIN CONTENT
</Flex>
</Panel>
</ResizableGroup>
</div>
),
],
play: async ({ canvas, canvasElement }) => {
const separator = canvas.getByRole("separator");
separator.dataset.separator = "hover";
// Wait for animations to finish
await new Promise((r) => setTimeout(r, 500));
await expect(canvasElement).toMatchImageSnapshot();
},
};
@@ -15,17 +15,16 @@ import * as stories from "./SeparatorView.stories";
import { BaseViewModel } from "../../core/viewmodel"; import { BaseViewModel } from "../../core/viewmodel";
import { ResizableGroup, Panel, type ResizerViewSnapshot, SeparatorView, type SeparatorViewActions } from ".."; import { ResizableGroup, Panel, type ResizerViewSnapshot, SeparatorView, type SeparatorViewActions } from "..";
const { Default, LeftPanelExpanded, KeyboardFocused } = composeStories(stories); const { Default, LeftPanelExpanded, KeyboardFocused, HoverWhenCollapsed, HoverWhenExpanded } = composeStories(stories);
class MockViewModel extends BaseViewModel<ResizerViewSnapshot, unknown> implements SeparatorViewActions { class MockViewModel extends BaseViewModel<ResizerViewSnapshot, unknown> implements SeparatorViewActions {
public constructor(snapshot: ResizerViewSnapshot) { public constructor(snapshot: ResizerViewSnapshot) {
super(undefined, snapshot); super(undefined, snapshot);
} }
public onBlur: () => void = vi.fn();
public onFocus: () => void = vi.fn();
public onPointerUp: () => void = vi.fn(); public onPointerUp: () => void = vi.fn();
public onPointerMove: () => void = vi.fn(); public onPointerMove: () => void = vi.fn();
public onPointerDown: () => void = vi.fn(); public onPointerDown: () => void = vi.fn();
public onDoubleClick: () => void = vi.fn();
} }
function renderPanel(initialSnapshot?: Partial<ResizerViewSnapshot>): MockViewModel { function renderPanel(initialSnapshot?: Partial<ResizerViewSnapshot>): MockViewModel {
@@ -57,6 +56,16 @@ describe("<SeparatorView />", () => {
expect(container).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
it("renders HoverWhenCollapsed story", () => {
const { container } = render(<HoverWhenCollapsed />);
expect(container).toMatchSnapshot();
});
it("renders HoverWhenExpanded story", () => {
const { container } = render(<HoverWhenExpanded />);
expect(container).toMatchSnapshot();
});
it("should call onPointerDown and onPointerUp on pointer events", async () => { it("should call onPointerDown and onPointerUp on pointer events", async () => {
const vm = renderPanel(); const vm = renderPanel();
const separator = screen.getByRole("separator"); const separator = screen.getByRole("separator");
@@ -65,12 +74,10 @@ describe("<SeparatorView />", () => {
expect(vm.onPointerUp).toHaveBeenCalledOnce(); expect(vm.onPointerUp).toHaveBeenCalledOnce();
}); });
it("should call onFocus and onBlur when receiving/loosing focus", async () => { it("should call onDoubleClick on double click", async () => {
const vm = renderPanel(); const vm = renderPanel();
const separator = screen.getByRole("separator"); const separator = screen.getByRole("separator");
separator.focus(); await userEvent.dblClick(separator);
expect(vm.onFocus).toHaveBeenCalled(); expect(vm.onDoubleClick).toHaveBeenCalledOnce();
separator.blur();
expect(vm.onBlur).toHaveBeenCalled();
}); });
}); });
@@ -33,14 +33,9 @@ export interface SeparatorViewActions {
onPointerDown: () => void; onPointerDown: () => void;
/** /**
* onFocus handler for the separator. * onDoubleClick handler for the separator.
*/ */
onFocus: () => void; onDoubleClick: () => void;
/**
* onBlur handler for the separator.
*/
onBlur: () => void;
} }
interface Props { interface Props {
@@ -53,14 +48,14 @@ interface Props {
*/ */
export function SeparatorView({ vm, className }: Props): React.ReactNode { export function SeparatorView({ vm, className }: Props): React.ReactNode {
const { translate: _t } = useI18n(); const { translate: _t } = useI18n();
const { isCollapsed, isFocusedViaKeyboard } = useViewModel(vm); const { isCollapsed } = useViewModel(vm);
/** /**
* There are two types of separator: * 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. * - 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. * - border: This is just a thin separator; shown when the panel is expanded.
*/ */
const type = isCollapsed || isFocusedViaKeyboard ? "bar" : "border"; const type = isCollapsed ? "bar" : "border";
const barContent = ( const barContent = (
<Tooltip description={_t("left_panel|separator_label")} placement="right"> <Tooltip description={_t("left_panel|separator_label")} placement="right">
@@ -75,19 +70,29 @@ export function SeparatorView({ vm, className }: Props): React.ReactNode {
</Tooltip> </Tooltip>
); );
/**
* This border is:
* - a 1px border that separates the left panel and main content.
* - a 2px border when the panel is expanded and the user is interacting with the separator.
*/
const border = (
<div className={styles.activeSeparatorContainer}>
<div className={styles.activeSeparator} />
</div>
);
return ( return (
<Separator <Separator
className={classNames(styles.separator, className)} className={classNames(styles.separator, className)}
onPointerUp={vm.onPointerUp} onPointerUp={vm.onPointerUp}
onPointerMove={vm.onPointerMove} onPointerMove={vm.onPointerMove}
onPointerDown={vm.onPointerDown} onPointerDown={vm.onPointerDown}
onFocus={vm.onFocus}
onBlur={vm.onBlur}
aria-label={_t("left_panel|separator_label")} aria-label={_t("left_panel|separator_label")}
data-separator-type={type} data-separator-type={type}
onDoubleClick={vm.onDoubleClick}
disableDoubleClick disableDoubleClick
> >
{type === "bar" ? barContent : null} {type === "bar" ? barContent : border}
</Separator> </Separator>
); );
} }
@@ -80,7 +80,7 @@ exports[`<SeparatorView /> > renders Default story 1`] = `
</div> </div>
`; `;
exports[`<SeparatorView /> > renders KeyboardFocused story 1`] = ` exports[`<SeparatorView /> > renders HoverWhenCollapsed story 1`] = `
<div> <div>
<div <div
style="height: 600px;" style="height: 600px;"
@@ -95,7 +95,7 @@ exports[`<SeparatorView /> > renders KeyboardFocused story 1`] = `
data-panel="true" data-panel="true"
data-testid="react-use-id-2" data-testid="react-use-id-2"
id="react-use-id-2" id="react-use-id-2"
style="min-height: 0px; max-height: 100%; height: auto; min-width: 0px; max-width: 100%; width: auto; border: 0px; padding: 0px; margin: 0px; display: flex; flex: 49.751 1 0px; overflow: visible;" style="min-height: 0px; max-height: 100%; height: auto; min-width: 0px; max-width: 100%; width: auto; border: 0px; padding: 0px; margin: 0px; display: flex; flex: 0 1 0px; overflow: visible;"
> >
<div <div
style="max-height: 100%; max-width: 100%; flex-grow: 1; overflow: auto; touch-action: pan-y;" style="max-height: 100%; max-width: 100%; flex-grow: 1; overflow: auto; touch-action: pan-y;"
@@ -115,7 +115,7 @@ exports[`<SeparatorView /> > renders KeyboardFocused story 1`] = `
aria-orientation="vertical" aria-orientation="vertical"
aria-valuemax="99.502" aria-valuemax="99.502"
aria-valuemin="0" aria-valuemin="0"
aria-valuenow="49.751" aria-valuenow="0"
class="SeparatorView-module_separator Separator" class="SeparatorView-module_separator Separator"
data-separator="inactive" data-separator="inactive"
data-separator-type="bar" data-separator-type="bar"
@@ -142,7 +142,160 @@ exports[`<SeparatorView /> > renders KeyboardFocused story 1`] = `
data-panel="true" data-panel="true"
data-testid="react-use-id-4" data-testid="react-use-id-4"
id="react-use-id-4" id="react-use-id-4"
style="min-height: 0px; max-height: 100%; height: auto; min-width: 0px; max-width: 100%; width: auto; border: 0px; padding: 0px; margin: 0px; display: flex; flex: 50.249 1 0px; overflow: visible;" style="min-height: 0px; max-height: 100%; height: auto; min-width: 0px; max-width: 100%; width: auto; border: 0px; padding: 0px; margin: 0px; display: flex; flex: 100 1 0px; overflow: visible;"
>
<div
style="max-height: 100%; max-width: 100%; flex-grow: 1; overflow: auto; touch-action: pan-y;"
>
<div
class="Flex-module_flex"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: center; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
>
MAIN CONTENT
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`<SeparatorView /> > renders HoverWhenExpanded story 1`] = `
<div>
<div
style="height: 600px;"
>
<div
data-group="true"
data-testid="react-use-id-1"
id="react-use-id-1"
style="height: 100%; width: 100%; overflow: hidden; display: flex; flex-flow: row; touch-action: pan-y;"
>
<div
style="height: 600px; width: 200px;"
/>
<div
data-panel="true"
data-testid="react-use-id-2"
id="react-use-id-2"
style="min-height: 0px; max-height: 100%; height: auto; min-width: 0px; max-width: 100%; width: auto; border: 0px; padding: 0px; margin: 0px; display: flex; flex: 0 1 0px; overflow: visible;"
>
<div
style="max-height: 100%; max-width: 100%; flex-grow: 1; overflow: auto; touch-action: pan-y;"
>
<div
class="Flex-module_flex"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: center; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
tabindex="0"
>
LEFT CONTENT
</div>
</div>
</div>
<div
aria-controls="react-use-id-2"
aria-label="Click or drag to expand"
aria-orientation="vertical"
aria-valuemax="100"
aria-valuemin="0"
aria-valuenow="0"
class="SeparatorView-module_separator Separator"
data-separator="inactive"
data-separator-type="border"
data-testid="react-use-id-3"
id="react-use-id-3"
role="separator"
style="flex: 0 0 auto; touch-action: none;"
tabindex="0"
>
<div
class="SeparatorView-module_activeSeparatorContainer"
>
<div
class="SeparatorView-module_activeSeparator"
/>
</div>
</div>
<div
data-panel="true"
data-testid="react-use-id-4"
id="react-use-id-4"
style="min-height: 0px; max-height: 100%; height: auto; min-width: 0px; max-width: 100%; width: auto; border: 0px; padding: 0px; margin: 0px; display: flex; flex: 100 1 0px; overflow: visible;"
>
<div
style="max-height: 100%; max-width: 100%; flex-grow: 1; overflow: auto; touch-action: pan-y;"
>
<div
class="Flex-module_flex"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: center; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
>
MAIN CONTENT
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`<SeparatorView /> > renders KeyboardFocused story 1`] = `
<div>
<div
style="height: 600px;"
>
<div
data-group="true"
data-testid="react-use-id-1"
id="react-use-id-1"
style="height: 100%; width: 100%; overflow: hidden; display: flex; flex-flow: row; touch-action: pan-y;"
>
<div
data-panel="true"
data-testid="react-use-id-2"
id="react-use-id-2"
style="min-height: 0px; max-height: 100%; height: auto; min-width: 0px; max-width: 100%; width: auto; border: 0px; padding: 0px; margin: 0px; display: flex; flex: 48.426 1 0px; overflow: visible;"
>
<div
style="max-height: 100%; max-width: 100%; flex-grow: 1; overflow: auto; touch-action: pan-y;"
>
<div
class="Flex-module_flex"
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: center; --mx-flex-gap: 0; --mx-flex-wrap: nowrap;"
tabindex="0"
>
LEFT CONTENT
</div>
</div>
</div>
<div
aria-controls="react-use-id-2"
aria-label="Click or drag to expand"
aria-orientation="vertical"
aria-valuemax="96.852"
aria-valuemin="0"
aria-valuenow="48.426"
class="SeparatorView-module_separator Separator"
data-separator="inactive"
data-separator-type="border"
data-testid="react-use-id-3"
id="react-use-id-3"
role="separator"
style="flex: 0 0 auto; touch-action: none;"
tabindex="0"
>
<div
class="SeparatorView-module_activeSeparatorContainer"
>
<div
class="SeparatorView-module_activeSeparator"
/>
</div>
</div>
<div
data-panel="true"
data-testid="react-use-id-4"
id="react-use-id-4"
style="min-height: 0px; max-height: 100%; height: auto; min-width: 0px; max-width: 100%; width: auto; border: 0px; padding: 0px; margin: 0px; display: flex; flex: 51.574 1 0px; overflow: visible;"
> >
<div <div
style="max-height: 100%; max-width: 100%; flex-grow: 1; overflow: auto; touch-action: pan-y;" style="max-height: 100%; max-width: 100%; flex-grow: 1; overflow: auto; touch-action: pan-y;"
@@ -175,7 +328,7 @@ exports[`<SeparatorView /> > renders LeftPanelExpanded story 1`] = `
data-panel="true" data-panel="true"
data-testid="react-use-id-2" data-testid="react-use-id-2"
id="react-use-id-2" id="react-use-id-2"
style="min-height: 0px; max-height: 100%; height: auto; min-width: 0px; max-width: 100%; width: auto; border: 0px; padding: 0px; margin: 0px; display: flex; flex: 48.309 1 0px; overflow: visible;" style="min-height: 0px; max-height: 100%; height: auto; min-width: 0px; max-width: 100%; width: auto; border: 0px; padding: 0px; margin: 0px; display: flex; flex: 48.426 1 0px; overflow: visible;"
> >
<div <div
style="max-height: 100%; max-width: 100%; flex-grow: 1; overflow: auto; touch-action: pan-y;" style="max-height: 100%; max-width: 100%; flex-grow: 1; overflow: auto; touch-action: pan-y;"
@@ -193,9 +346,9 @@ exports[`<SeparatorView /> > renders LeftPanelExpanded story 1`] = `
aria-controls="react-use-id-2" aria-controls="react-use-id-2"
aria-label="Click or drag to expand" aria-label="Click or drag to expand"
aria-orientation="vertical" aria-orientation="vertical"
aria-valuemax="96.618" aria-valuemax="96.852"
aria-valuemin="0" aria-valuemin="0"
aria-valuenow="48.309" aria-valuenow="48.426"
class="SeparatorView-module_separator Separator" class="SeparatorView-module_separator Separator"
data-separator="inactive" data-separator="inactive"
data-separator-type="border" data-separator-type="border"
@@ -204,12 +357,20 @@ exports[`<SeparatorView /> > renders LeftPanelExpanded story 1`] = `
role="separator" role="separator"
style="flex: 0 0 auto; touch-action: none;" style="flex: 0 0 auto; touch-action: none;"
tabindex="0" tabindex="0"
/> >
<div
class="SeparatorView-module_activeSeparatorContainer"
>
<div
class="SeparatorView-module_activeSeparator"
/>
</div>
</div>
<div <div
data-panel="true" data-panel="true"
data-testid="react-use-id-4" data-testid="react-use-id-4"
id="react-use-id-4" id="react-use-id-4"
style="min-height: 0px; max-height: 100%; height: auto; min-width: 0px; max-width: 100%; width: auto; border: 0px; padding: 0px; margin: 0px; display: flex; flex: 51.691 1 0px; overflow: visible;" style="min-height: 0px; max-height: 100%; height: auto; min-width: 0px; max-width: 100%; width: auto; border: 0px; padding: 0px; margin: 0px; display: flex; flex: 51.574 1 0px; overflow: visible;"
> >
<div <div
style="max-height: 100%; max-width: 100%; flex-grow: 1; overflow: auto; touch-action: pan-y;" style="max-height: 100%; max-width: 100%; flex-grow: 1; overflow: auto; touch-action: pan-y;"