Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## 4.6.1

- [658](https://github.com/bvaughn/react-resizable-panels/pull/658): Imperative `Panel` and `Group` APIs ignored `disabled` status when resizing panels; this is an explicit override of the _disabled_ state and is required to support conditionally disabled groups.
- [658](https://github.com/bvaughn/react-resizable-panels/pull/658): `Separator` component does not set a `cursor: not-allowed` style if the parent `Group` has cursors disabled.

## 4.6.0

- [657](https://github.com/bvaughn/react-resizable-panels/pull/657): Allow `Panel` and `Separator` components to be disabled
Expand Down
246 changes: 239 additions & 7 deletions integrations/tests/tests/cursor.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -235,33 +235,189 @@ test.describe("cursor", () => {
).toBe("auto");
});

test("disabled panel(s)", async ({ page: mainPage }) => {
test("all panels disabled", async ({ page: mainPage }) => {
const states = [
[true, true],
[true, false],
[false, true]
] satisfies [boolean, boolean][];

for (const [leftDisabled, rightDisabled] of states) {
const page = await goToUrl(
mainPage,
<Group>
<Panel disabled={leftDisabled} id="left" />
<Panel disabled={rightDisabled} id="right" />
</Group>
);

const hitAreaBox = await calculateHitArea(page, ["left", "right"]);
const { x, y } = getCenterCoordinates(hitAreaBox);

expect(
await page.evaluate(() => getComputedStyle(document.body).cursor)
).toBe("auto");

await page.mouse.move(x, y, moveConfig);

expect(
await page.evaluate(() => getComputedStyle(document.body).cursor)
).toBe("auto");
}
});

test("all but one panels disabled", async ({ page: mainPage }) => {
const states = [
[true, true, false],
[false, true, true],
[true, false, true]
] satisfies [boolean, boolean, boolean][];

for (const [leftDisabled, centerDisabled, rightDisabled] of states) {
const page = await goToUrl(
mainPage,
<Group>
<Panel disabled={leftDisabled} id="left" />
<Panel disabled={centerDisabled} id="center" />
<Panel disabled={rightDisabled} id="right" />
</Group>
);

expect(
await page.evaluate(() => getComputedStyle(document.body).cursor)
).toBe("auto");

{
const hitAreaBox = await calculateHitArea(page, ["left", "center"]);
const { x, y } = getCenterCoordinates(hitAreaBox);
await page.mouse.move(x, y, moveConfig);

expect(
await page.evaluate(() => getComputedStyle(document.body).cursor)
).toBe("auto");
}

{
const hitAreaBox = await calculateHitArea(page, ["center", "right"]);
const { x, y } = getCenterCoordinates(hitAreaBox);
await page.mouse.move(x, y, moveConfig);

expect(
await page.evaluate(() => getComputedStyle(document.body).cursor)
).toBe("auto");
}
}
});

test("only some panels disabled", async ({ page: mainPage }) => {
const states = [
[true, false, false],
[false, true, false],
[false, false, true]
] satisfies [boolean, boolean, boolean][];

for (const [leftDisabled, centerDisabled, rightDisabled] of states) {
const page = await goToUrl(
mainPage,
<Group>
<Panel disabled={leftDisabled} id="left" />
<Panel disabled={centerDisabled} id="center" />
<Panel disabled={rightDisabled} id="right" />
</Group>
);

expect(
await page.evaluate(() => getComputedStyle(document.body).cursor)
).toBe("auto");

if (!leftDisabled) {
const hitAreaBox = await calculateHitArea(page, ["left", "center"]);
const { x, y } = getCenterCoordinates(hitAreaBox);
await page.mouse.move(x, y, moveConfig);

expect(
await page.evaluate(() => getComputedStyle(document.body).cursor)
).toBe("ew-resize");
}

if (!rightDisabled) {
const hitAreaBox = await calculateHitArea(page, ["center", "right"]);
const { x, y } = getCenterCoordinates(hitAreaBox);
await page.mouse.move(x, y, moveConfig);

expect(
await page.evaluate(() => getComputedStyle(document.body).cursor)
).toBe("ew-resize");
}
}
});

test("disabled separator", async ({ page: mainPage }) => {
const page = await goToUrl(
mainPage,
<Group>
<Panel disabled id="left" />
<Panel id="left" />
<Separator disabled id="separator" />
<Panel id="right" />
</Group>
);

const hitAreaBox = await calculateHitArea(page, ["left", "right"]);
const { x, y } = getCenterCoordinates(hitAreaBox);
const separator = page.getByTestId("separator");
const boundingBox = (await separator.boundingBox())!;
const x = boundingBox.x + boundingBox.width / 2;
const y = boundingBox.y + boundingBox.height / 2;

expect(
await page.evaluate(() => getComputedStyle(document.body).cursor)
).toBe("auto");

await page.mouse.move(x, y, moveConfig);

expect(
await page.evaluate(
() =>
getComputedStyle(document.querySelector('[role="separator"]')!).cursor
)
).toBe("not-allowed");
});

test("disabled separator within a disabled group", async ({
page: mainPage
}) => {
const page = await goToUrl(
mainPage,
<Group disabled>
<Panel id="left" />
<Separator disabled id="separator" />
<Panel id="right" />
</Group>
);

const separator = page.getByTestId("separator");
const boundingBox = (await separator.boundingBox())!;
const x = boundingBox.x + boundingBox.width / 2;
const y = boundingBox.y + boundingBox.height / 2;

expect(
await page.evaluate(() => getComputedStyle(document.body).cursor)
).toBe("auto");

await page.mouse.move(x, y, moveConfig);

expect(
await page.evaluate(
() =>
getComputedStyle(document.querySelector('[role="separator"]')!).cursor
)
).toBe("not-allowed");
});

test("disabled separator", async ({ page: mainPage }) => {
test("disabled separator within a cursor-disabled group", async ({
page: mainPage
}) => {
const page = await goToUrl(
mainPage,
<Group>
<Group disableCursor>
<Panel id="left" />
<Separator disabled id="separator" />
<Panel id="right" />
Expand All @@ -284,6 +440,82 @@ test.describe("cursor", () => {
() =>
getComputedStyle(document.querySelector('[role="separator"]')!).cursor
)
).toBe("not-allowed");
).toBe("auto");
});

test("should not show a cursor at the panel's edge if all panels before or after are disabled", async ({
page: mainPage
}) => {
{
// All before
const page = await goToUrl(
mainPage,
<Group>
<Panel disabled id="left" />
<Panel id="center" />
<Panel id="right" />
</Group>
);

expect(
await page.evaluate(() => getComputedStyle(document.body).cursor)
).toBe("auto");

{
const hitAreaBox = await calculateHitArea(page, ["left", "center"]);
const { x, y } = getCenterCoordinates(hitAreaBox);
await page.mouse.move(x, y, moveConfig);

expect(
await page.evaluate(() => getComputedStyle(document.body).cursor)
).toBe("auto");
}

{
const hitAreaBox = await calculateHitArea(page, ["center", "right"]);
const { x, y } = getCenterCoordinates(hitAreaBox);
await page.mouse.move(x, y, moveConfig);

expect(
await page.evaluate(() => getComputedStyle(document.body).cursor)
).toBe("ew-resize");
}
}

{
// All before
const page = await goToUrl(
mainPage,
<Group>
<Panel id="left" />
<Panel id="center" />
<Panel disabled id="right" />
</Group>
);

expect(
await page.evaluate(() => getComputedStyle(document.body).cursor)
).toBe("auto");

{
const hitAreaBox = await calculateHitArea(page, ["left", "center"]);
const { x, y } = getCenterCoordinates(hitAreaBox);
await page.mouse.move(x, y, moveConfig);

expect(
await page.evaluate(() => getComputedStyle(document.body).cursor)
).toBe("ew-resize");
}

{
const hitAreaBox = await calculateHitArea(page, ["center", "right"]);
const { x, y } = getCenterCoordinates(hitAreaBox);
await page.mouse.move(x, y, moveConfig);

expect(
await page.evaluate(() => getComputedStyle(document.body).cursor)
).toBe("auto");
}
}
});
});
46 changes: 46 additions & 0 deletions integrations/tests/tests/pointer-interactions.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -612,4 +612,50 @@ test.describe("pointer interactions", () => {
await assertLayoutChangeCounts(mainPage, 1);
await expect(mainPage.getByText('"left": 50')).toBeVisible();
});

test("should allow a disabled panel's border to resize another panel indirectly", async ({
page: mainPage
}) => {
const page = await goToUrl(
mainPage,
<Group>
<Panel id="left" />
<Panel disabled id="center" />
<Panel id="right" />
</Group>
);

await assertLayoutChangeCounts(mainPage, 1);
await expect(mainPage.getByText('"left": 33')).toBeVisible();
await expect(mainPage.getByText('"center": 33')).toBeVisible();
await expect(mainPage.getByText('"right": 33')).toBeVisible();

await resizeHelper(page, ["left", "center"], 100, 0);

await assertLayoutChangeCounts(mainPage, 2);
await expect(mainPage.getByText('"left": 43')).toBeVisible();
await expect(mainPage.getByText('"center": 33')).toBeVisible();
await expect(mainPage.getByText('"right": 23')).toBeVisible();

await resizeHelper(page, ["center", "right"], -200, 0);

await assertLayoutChangeCounts(mainPage, 3);
await expect(mainPage.getByText('"left": 23')).toBeVisible();
await expect(mainPage.getByText('"center": 33')).toBeVisible();
await expect(mainPage.getByText('"right": 43')).toBeVisible();

await resizeHelper(page, ["center", "right"], 200, 0);

await assertLayoutChangeCounts(mainPage, 4);
await expect(mainPage.getByText('"left": 43')).toBeVisible();
await expect(mainPage.getByText('"center": 33')).toBeVisible();
await expect(mainPage.getByText('"right": 23')).toBeVisible();

await resizeHelper(page, ["center", "right"], -100, 0);

await assertLayoutChangeCounts(mainPage, 5);
await expect(mainPage.getByText('"left": 33')).toBeVisible();
await expect(mainPage.getByText('"center": 33')).toBeVisible();
await expect(mainPage.getByText('"right": 33')).toBeVisible();
});
});
1 change: 1 addition & 0 deletions lib/components/group/Group.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ describe("Group", () => {
render(<Repro />);
});
});

describe("onLayoutChange and onLayoutChanged", () => {
beforeEach(() => {
setElementBoundsFunction((element) => {
Expand Down
Loading
Loading