Skip to content

Commit

Permalink
BugFix: onDragging prop only called for active handle(s) (#289)
Browse files Browse the repository at this point in the history
  • Loading branch information
bvaughn authored Feb 10, 2024
1 parent a63f61d commit 5afa01c
Show file tree
Hide file tree
Showing 7 changed files with 343 additions and 71 deletions.
4 changes: 4 additions & 0 deletions packages/react-resizable-panels/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 2.0.4

- Fixed `PanelResizeHandle` `onDragging` prop to only be called for the handle being dragged (#289)

## 2.0.3

- Fix resize handle onDragging callback (#278)
Expand Down
63 changes: 63 additions & 0 deletions packages/react-resizable-panels/src/Panel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { assert } from "./utils/assert";
import { getPanelElement } from "./utils/dom/getPanelElement";
import {
mockPanelGroupOffsetWidthAndHeight,
verifyAttribute,
verifyExpandedPanelGroupLayout,
} from "./utils/test-utils";
import { createRef } from "./vendor/react";
Expand Down Expand Up @@ -675,6 +676,68 @@ describe("PanelGroup", () => {
});
});

describe("data attributes", () => {
it("should initialize with the correct props based attributes", () => {
act(() => {
root.render(
<PanelGroup direction="horizontal" id="test-group">
<Panel defaultSize={75} id="left-panel" />
<PanelResizeHandle />
<Panel collapsible id="right-panel" />
</PanelGroup>
);
});

const leftElement = getPanelElement("left-panel", container);
const rightElement = getPanelElement("right-panel", container);

assert(leftElement);
assert(rightElement);

verifyAttribute(leftElement, "data-panel", "");
verifyAttribute(leftElement, "data-panel-id", "left-panel");
verifyAttribute(leftElement, "data-panel-group-id", "test-group");
verifyAttribute(leftElement, "data-panel-size", "75.0");
verifyAttribute(leftElement, "data-panel-collapsible", null);

verifyAttribute(rightElement, "data-panel", "");
verifyAttribute(rightElement, "data-panel-id", "right-panel");
verifyAttribute(rightElement, "data-panel-group-id", "test-group");
verifyAttribute(rightElement, "data-panel-size", "25.0");
verifyAttribute(rightElement, "data-panel-collapsible", "true");
});

it("should update the data-panel-size attribute when the panel resizes", () => {
const leftPanelRef = createRef<ImperativePanelHandle>();

act(() => {
root.render(
<PanelGroup direction="horizontal" id="test-group">
<Panel defaultSize={75} id="left-panel" ref={leftPanelRef} />
<PanelResizeHandle />
<Panel collapsible id="right-panel" />
</PanelGroup>
);
});

const leftElement = getPanelElement("left-panel", container);
const rightElement = getPanelElement("right-panel", container);

assert(leftElement);
assert(rightElement);

verifyAttribute(leftElement, "data-panel-size", "75.0");
verifyAttribute(rightElement, "data-panel-size", "25.0");

act(() => {
leftPanelRef.current?.resize(30);
});

verifyAttribute(leftElement, "data-panel-size", "30.0");
verifyAttribute(rightElement, "data-panel-size", "70.0");
});
});

describe("DEV warnings", () => {
it("should warn about server rendered panels with no default size", () => {
jest.resetModules();
Expand Down
22 changes: 21 additions & 1 deletion packages/react-resizable-panels/src/PanelGroup.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ import {
} from ".";
import { assert } from "./utils/assert";
import { getPanelGroupElement } from "./utils/dom/getPanelGroupElement";
import { mockPanelGroupOffsetWidthAndHeight } from "./utils/test-utils";
import {
mockPanelGroupOffsetWidthAndHeight,
verifyAttribute,
} from "./utils/test-utils";
import { createRef } from "./vendor/react";

describe("PanelGroup", () => {
Expand Down Expand Up @@ -256,6 +259,23 @@ describe("PanelGroup", () => {
});
});

describe("data attributes", () => {
it("should initialize with the correct props based attributes", () => {
act(() => {
root.render(
<PanelGroup direction="horizontal" id="test-group"></PanelGroup>
);
});

const element = getPanelGroupElement("test-group", container);
assert(element);

verifyAttribute(element, "data-panel-group", "");
verifyAttribute(element, "data-panel-group-direction", "horizontal");
verifyAttribute(element, "data-panel-group-id", "test-group");
});
});

describe("DEV warnings", () => {
it("should warn about unstable layouts without id and order props", () => {
act(() => {
Expand Down
195 changes: 173 additions & 22 deletions packages/react-resizable-panels/src/PanelResizeHandle.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { Root, createRoot } from "react-dom/client";
import { act } from "react-dom/test-utils";
import type { PanelResizeHandleProps } from "react-resizable-panels";
import { Panel, PanelGroup, PanelResizeHandle } from ".";
import { assert } from "./utils/assert";
import { getResizeHandleElement } from "./utils/dom/getResizeHandleElement";
import { dispatchPointerEvent } from "./utils/test-utils";
import {
dispatchPointerEvent,
mockBoundingClientRect,
verifyAttribute,
} from "./utils/test-utils";

describe("PanelResizeHandle", () => {
let expectedWarnings: string[] = [];
Expand Down Expand Up @@ -67,47 +72,193 @@ describe("PanelResizeHandle", () => {
expect(element.title).toBe("bar");
});

function setupMockedGroup({
leftProps = {},
rightProps = {},
}: {
leftProps?: Partial<PanelResizeHandleProps>;
rightProps?: Partial<PanelResizeHandleProps>;
} = {}) {
act(() => {
root.render(
<PanelGroup direction="horizontal" id="test-group">
<Panel />
<PanelResizeHandle id="handle-left" tabIndex={1} {...leftProps} />
<Panel />
<PanelResizeHandle id="handle-right" tabIndex={2} {...rightProps} />
<Panel />
</PanelGroup>
);
});

const leftElement = getResizeHandleElement("handle-left", container);
const rightElement = getResizeHandleElement("handle-right", container);

assert(leftElement);
assert(rightElement);

// JSDom doesn't properly handle bounding rects
mockBoundingClientRect(leftElement, {
x: 50,
y: 0,
height: 50,
width: 2,
});
mockBoundingClientRect(rightElement, {
x: 100,
y: 0,
height: 50,
width: 2,
});

return {
leftElement,
rightElement,
};
}

describe("callbacks", () => {
describe("onDragging", () => {
it("should fire when dragging starts/stops", async () => {
it("should fire when dragging starts/stops", () => {
const onDragging = jest.fn();

act(() => {
root.render(
<PanelGroup direction="horizontal">
<Panel />
<PanelResizeHandle
id="handle"
onDragging={onDragging}
tabIndex={123}
title="bar"
/>
<Panel />
</PanelGroup>
);
const { leftElement } = setupMockedGroup({
leftProps: { onDragging },
});

const handleElement = container.querySelector(
'[data-panel-resize-handle-id="handle"]'
) as HTMLElement;

act(() => {
dispatchPointerEvent("mouseover", handleElement);
dispatchPointerEvent("mousemove", leftElement);
});
expect(onDragging).not.toHaveBeenCalled();

act(() => {
dispatchPointerEvent("mousedown", handleElement);
dispatchPointerEvent("mousedown", leftElement);
});
expect(onDragging).toHaveBeenCalledTimes(1);
expect(onDragging).toHaveBeenCalledWith(true);

act(() => {
dispatchPointerEvent("mouseup", handleElement);
dispatchPointerEvent("mouseup", leftElement);
});
expect(onDragging).toHaveBeenCalledTimes(2);
expect(onDragging).toHaveBeenCalledWith(false);
});

it("should only fire for the handle that has been dragged", () => {
const onDraggingLeft = jest.fn();
const onDraggingRight = jest.fn();

const { leftElement } = setupMockedGroup({
leftProps: { onDragging: onDraggingLeft },
rightProps: { onDragging: onDraggingRight },
});

act(() => {
dispatchPointerEvent("mousemove", leftElement);
});
expect(onDraggingLeft).not.toHaveBeenCalled();
expect(onDraggingRight).not.toHaveBeenCalled();

act(() => {
dispatchPointerEvent("mousedown", leftElement);
});
expect(onDraggingLeft).toHaveBeenCalledTimes(1);
expect(onDraggingLeft).toHaveBeenCalledWith(true);
expect(onDraggingRight).not.toHaveBeenCalled();

act(() => {
dispatchPointerEvent("mouseup", leftElement);
});
expect(onDraggingLeft).toHaveBeenCalledTimes(2);
expect(onDraggingLeft).toHaveBeenCalledWith(false);
expect(onDraggingRight).not.toHaveBeenCalled();
});
});
});

describe("data attributes", () => {
it("should initialize with the correct props based attributes", () => {
const { leftElement, rightElement } = setupMockedGroup();

verifyAttribute(leftElement, "data-panel-group-id", "test-group");
verifyAttribute(leftElement, "data-resize-handle", "");
verifyAttribute(leftElement, "data-panel-group-direction", "horizontal");
verifyAttribute(leftElement, "data-panel-resize-handle-enabled", "true");
verifyAttribute(
leftElement,
"data-panel-resize-handle-id",
"handle-left"
);

verifyAttribute(rightElement, "data-panel-group-id", "test-group");
verifyAttribute(rightElement, "data-resize-handle", "");
verifyAttribute(rightElement, "data-panel-group-direction", "horizontal");
verifyAttribute(rightElement, "data-panel-resize-handle-enabled", "true");
verifyAttribute(
rightElement,
"data-panel-resize-handle-id",
"handle-right"
);
});

it("should update data-resize-handle-active and data-resize-handle-state when dragging starts/stops", () => {
const { leftElement, rightElement } = setupMockedGroup();
verifyAttribute(leftElement, "data-resize-handle-active", null);
verifyAttribute(rightElement, "data-resize-handle-active", null);
verifyAttribute(leftElement, "data-resize-handle-state", "inactive");
verifyAttribute(rightElement, "data-resize-handle-state", "inactive");

act(() => {
dispatchPointerEvent("mousemove", leftElement);
});
verifyAttribute(leftElement, "data-resize-handle-active", null);
verifyAttribute(rightElement, "data-resize-handle-active", null);
verifyAttribute(leftElement, "data-resize-handle-state", "hover");
verifyAttribute(rightElement, "data-resize-handle-state", "inactive");

act(() => {
dispatchPointerEvent("mousedown", leftElement);
});
verifyAttribute(leftElement, "data-resize-handle-active", "pointer");
verifyAttribute(rightElement, "data-resize-handle-active", null);
verifyAttribute(leftElement, "data-resize-handle-state", "drag");
verifyAttribute(rightElement, "data-resize-handle-state", "inactive");

act(() => {
dispatchPointerEvent("mouseup", leftElement);
});
verifyAttribute(leftElement, "data-resize-handle-active", null);
verifyAttribute(rightElement, "data-resize-handle-active", null);
verifyAttribute(leftElement, "data-resize-handle-state", "hover");
verifyAttribute(rightElement, "data-resize-handle-state", "inactive");

act(() => {
dispatchPointerEvent("mousemove", rightElement);
});
verifyAttribute(leftElement, "data-resize-handle-active", null);
verifyAttribute(rightElement, "data-resize-handle-active", null);
verifyAttribute(leftElement, "data-resize-handle-state", "inactive");
verifyAttribute(rightElement, "data-resize-handle-state", "hover");
});

it("should update data-resize-handle-active when focused", () => {
const { leftElement, rightElement } = setupMockedGroup();
verifyAttribute(leftElement, "data-resize-handle-active", null);
verifyAttribute(rightElement, "data-resize-handle-active", null);

act(() => {
leftElement.focus();
});
expect(document.activeElement).toBe(leftElement);
verifyAttribute(leftElement, "data-resize-handle-active", "keyboard");
verifyAttribute(rightElement, "data-resize-handle-active", null);

act(() => {
leftElement.blur();
});
expect(document.activeElement).not.toBe(leftElement);
verifyAttribute(leftElement, "data-resize-handle-active", null);
verifyAttribute(rightElement, "data-resize-handle-active", null);
});
});
});
Loading

0 comments on commit 5afa01c

Please sign in to comment.