Skip to content

Commit 0fe7cc1

Browse files
committed
Prepare 4.2.2 release
1 parent dbfa324 commit 0fe7cc1

8 files changed

Lines changed: 102 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
## Unreleased
44

5+
- `useDefaultLayout` hook initializes `storage` param to `localStorage` if undefined.
6+
- Fix ambiguous type for `Panel` prop `onResize` that impacted certain TypeScript versions.
7+
8+
## 4.2.1
9+
510
- [2a6b03f](https://github.com/bvaughn/react-resizable-panels/commit/2a6b03f67d7d8fea8483a6a69bcdaebbe1b18a7a): Add `displayName` property to `Group`, `Panel`, and `Separator` components for better debugging experience.
611
- [577](https://github.com/bvaughn/react-resizable-panels/pull/577): `Group` handles newly registered `Panels` + `Separators` during mount so that user code can safely call imperative APIs earlier
712

integrations/vite/src/routes/Decoder.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useLayoutEffect, useMemo, useRef, useState } from "react";
22
import { assert, Box } from "react-lib-tools";
33
import {
4+
useDefaultLayout,
45
useGroupCallbackRef,
56
useGroupRef,
67
usePanelCallbackRef,
@@ -18,6 +19,18 @@ export function Decoder() {
1819

1920
const [group, setGroup] = useGroupCallbackRef();
2021
const groupRef = useGroupRef();
22+
23+
const result = useDefaultLayout({
24+
id: "group"
25+
});
26+
27+
const defaultLayout = searchParams.has("useDefaultLayout")
28+
? result.defaultLayout
29+
: undefined;
30+
const onLayoutChange = searchParams.has("useDefaultLayout")
31+
? result.onLayoutChange
32+
: undefined;
33+
2134
const groupRefProp = searchParams.has("useGroupCallbackRef")
2235
? setGroup
2336
: searchParams.has("useGroupRef")
@@ -82,8 +95,11 @@ export function Decoder() {
8295

8396
const group = decode(encoded, {
8497
groupProps: {
98+
defaultLayout,
8599
groupRef: groupRefProp,
86100
onLayoutChange: (layout) => {
101+
onLayoutChange?.(layout);
102+
87103
setTimeout(() => {
88104
stableCallbacksRef.current.readGroupLayout();
89105
}, 0);
@@ -120,7 +136,7 @@ export function Decoder() {
120136
});
121137

122138
return group;
123-
}, [encoded, groupRefProp, panelRefProp]);
139+
}, [defaultLayout, encoded, groupRefProp, onLayoutChange, panelRefProp]);
124140

125141
// Debugging
126142
// console.group("Decoder");
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { expect, test } from "@playwright/test";
2+
import { Group, Panel } from "react-resizable-panels";
3+
import { goToUrl } from "./utils/goToUrl";
4+
import { resizeHelper } from "./utils/pointer-interactions/resizeHelper";
5+
6+
// High level tests; more nuanced scenarios are covered by unit tests
7+
test.describe("default layout", () => {
8+
for (const usePopUpWindow of [true, false]) {
9+
test.describe(usePopUpWindow ? "in a popup" : "in the main window", () => {
10+
test("remembers most recent layout", async ({ page: mainPage }) => {
11+
const page = await goToUrl(
12+
mainPage,
13+
<Group>
14+
<Panel defaultSize="30%" id="left" minSize={50} />
15+
<Panel id="right" minSize={50} />
16+
</Group>,
17+
{ useDefaultLayout: true, usePopUpWindow }
18+
);
19+
20+
await expect(page.getByText("id: left")).toContainText("30%");
21+
await expect(page.getByText("id: right")).toContainText("70%");
22+
23+
await resizeHelper(page, ["left", "right"], 100, 0);
24+
25+
await expect(page.getByText("id: left")).toContainText("40%");
26+
await expect(page.getByText("id: right")).toContainText("60%");
27+
28+
const page2 = await goToUrl(
29+
mainPage,
30+
<Group>
31+
<Panel defaultSize="30%" id="left" minSize={50} />
32+
<Panel id="right" minSize={50} />
33+
</Group>,
34+
{ useDefaultLayout: true, usePopUpWindow }
35+
);
36+
37+
await expect(page2.getByText("id: left")).toContainText("40%");
38+
await expect(page2.getByText("id: right")).toContainText("60%");
39+
});
40+
});
41+
}
42+
});

integrations/vite/tests/utils/goToUrl.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export async function goToUrl(
77
page: Page,
88
elementProp: ReactElement<unknown>,
99
config: {
10+
useDefaultLayout?: boolean | undefined;
1011
useGroupCallbackRef?: boolean | undefined;
1112
useGroupRef?: boolean | undefined;
1213
usePanelCallbackRef?: boolean | undefined;
@@ -15,6 +16,7 @@ export async function goToUrl(
1516
} = {}
1617
): Promise<Page> {
1718
const {
19+
useDefaultLayout = false,
1820
useGroupCallbackRef = false,
1921
useGroupRef = false,
2022
usePanelCallbackRef = false,
@@ -36,6 +38,7 @@ export async function goToUrl(
3638
}
3739

3840
const queryParams = [
41+
useDefaultLayout ? "useDefaultLayout" : undefined,
3942
useGroupCallbackRef ? "useGroupCallbackRef" : undefined,
4043
useGroupRef ? "useGroupRef" : undefined,
4144
usePanelCallbackRef ? "usePanelCallbackRef" : undefined,

lib/components/group/useDefaultLayout.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type { Layout, LayoutStorage, OnGroupLayoutChange } from "./types";
66
export function useDefaultLayout({
77
debounceSaveMs = 100,
88
panelIds,
9-
storage,
9+
storage = localStorage,
1010
...rest
1111
}: {
1212
/**
@@ -25,7 +25,7 @@ export function useDefaultLayout({
2525
* Storage implementation; supports localStorage, sessionStorage, and custom implementations
2626
* Refer to documentation site for example integrations.
2727
*/
28-
storage: LayoutStorage;
28+
storage?: LayoutStorage;
2929
} & (
3030
| {
3131
/**

lib/components/panel/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@ export interface PanelImperativeHandle {
8888
resize: (size: number | string) => void;
8989
}
9090

91-
export type PanelProps = HTMLAttributes<HTMLDivElement> & {
91+
type BasePanelAttributes = Omit<HTMLAttributes<HTMLDivElement>, "onResize">;
92+
93+
export type PanelProps = BasePanelAttributes & {
9294
/**
9395
* CSS class name.
9496
*

lib/components/separator/types.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ export type RegisteredSeparator = {
55
id: string;
66
};
77

8-
type BaseAttributes = Omit<HTMLAttributes<HTMLDivElement>, "role" | "tabIndex">;
8+
type BaseSeparatorAttributes = Omit<
9+
HTMLAttributes<HTMLDivElement>,
10+
"role" | "tabIndex"
11+
>;
912

10-
export type SeparatorProps = BaseAttributes & {
13+
export type SeparatorProps = BaseSeparatorAttributes & {
1114
/**
1215
* CSS class name.
1316
*

src/App.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,31 @@ const commonQuestions: CommonQuestion[] = [
157157
<Code html={VerticalGroupOverflowHTML} />
158158
</>
159159
)
160+
},
161+
{
162+
id: "local-storage-undefined",
163+
question: "ReferenceError: localStorage is not defined",
164+
answer: (
165+
<>
166+
<p>
167+
The <code>useDefaultLayout</code> hook saves layouts to{" "}
168+
<code>localStorage</code> by default. This does not work for
169+
server-rendered applications though, since <code>localStorage</code>{" "}
170+
is only defined on the client.
171+
</p>
172+
<p>
173+
Refer to the{" "}
174+
<Link to="/examples/persistent-layout/server-rendering">
175+
server rendering
176+
</Link>{" "}
177+
or{" "}
178+
<Link to="/examples/persistent-layout/server-components">
179+
server components
180+
</Link>{" "}
181+
docs for examples of how to save layouts on the server.
182+
</p>
183+
</>
184+
)
160185
}
161186
];
162187

0 commit comments

Comments
 (0)