react-resizable-panels 0.0.54 → 0.0.56

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/.eslintrc.cjs +26 -0
  2. package/CHANGELOG.md +253 -80
  3. package/README.md +55 -49
  4. package/dist/declarations/src/Panel.d.ts +76 -20
  5. package/dist/declarations/src/PanelGroup.d.ts +29 -21
  6. package/dist/declarations/src/PanelResizeHandle.d.ts +1 -1
  7. package/dist/declarations/src/index.d.ts +5 -5
  8. package/dist/declarations/src/types.d.ts +3 -25
  9. package/dist/declarations/src/vendor/react.d.ts +4 -4
  10. package/dist/react-resizable-panels.browser.cjs.js +1279 -796
  11. package/dist/react-resizable-panels.browser.development.cjs.js +1404 -809
  12. package/dist/react-resizable-panels.browser.development.esm.js +1398 -803
  13. package/dist/react-resizable-panels.browser.esm.js +1279 -796
  14. package/dist/react-resizable-panels.cjs.js +1279 -796
  15. package/dist/react-resizable-panels.cjs.js.map +1 -0
  16. package/dist/react-resizable-panels.development.cjs.js +1399 -804
  17. package/dist/react-resizable-panels.development.esm.js +1400 -805
  18. package/dist/react-resizable-panels.development.node.cjs.js +1172 -755
  19. package/dist/react-resizable-panels.development.node.esm.js +1173 -756
  20. package/dist/react-resizable-panels.esm.js +1279 -796
  21. package/dist/react-resizable-panels.esm.js.map +1 -0
  22. package/dist/react-resizable-panels.node.cjs.js +1064 -749
  23. package/dist/react-resizable-panels.node.esm.js +1065 -750
  24. package/jest.config.js +10 -0
  25. package/package.json +3 -1
  26. package/src/Panel.test.tsx +308 -0
  27. package/src/Panel.ts +179 -127
  28. package/src/PanelGroup.test.tsx +210 -0
  29. package/src/PanelGroup.ts +751 -580
  30. package/src/PanelGroupContext.ts +33 -0
  31. package/src/PanelResizeHandle.ts +13 -8
  32. package/src/hooks/useUniqueId.ts +1 -1
  33. package/src/hooks/useWindowSplitterBehavior.ts +9 -161
  34. package/src/hooks/useWindowSplitterPanelGroupBehavior.ts +185 -0
  35. package/src/index.ts +24 -11
  36. package/src/types.ts +3 -29
  37. package/src/utils/adjustLayoutByDelta.test.ts +1808 -0
  38. package/src/utils/adjustLayoutByDelta.ts +211 -0
  39. package/src/utils/calculateAriaValues.test.ts +111 -0
  40. package/src/utils/calculateAriaValues.ts +67 -0
  41. package/src/utils/calculateDeltaPercentage.ts +68 -0
  42. package/src/utils/calculateDragOffsetPercentage.ts +30 -0
  43. package/src/utils/calculateUnsafeDefaultLayout.test.ts +92 -0
  44. package/src/utils/calculateUnsafeDefaultLayout.ts +55 -0
  45. package/src/utils/callPanelCallbacks.ts +81 -0
  46. package/src/utils/compareLayouts.test.ts +9 -0
  47. package/src/utils/compareLayouts.ts +12 -0
  48. package/src/utils/computePanelFlexBoxStyle.ts +44 -0
  49. package/src/utils/computePercentagePanelConstraints.test.ts +71 -0
  50. package/src/utils/computePercentagePanelConstraints.ts +56 -0
  51. package/src/utils/convertPercentageToPixels.test.ts +9 -0
  52. package/src/utils/convertPercentageToPixels.ts +6 -0
  53. package/src/utils/convertPixelConstraintsToPercentages.ts +55 -0
  54. package/src/utils/convertPixelsToPercentage.test.ts +9 -0
  55. package/src/utils/convertPixelsToPercentage.ts +6 -0
  56. package/src/utils/determinePivotIndices.ts +10 -0
  57. package/src/utils/dom/calculateAvailablePanelSizeInPixels.ts +29 -0
  58. package/src/utils/dom/getAvailableGroupSizePixels.ts +29 -0
  59. package/src/utils/dom/getPanelElement.ts +7 -0
  60. package/src/utils/dom/getPanelGroupElement.ts +7 -0
  61. package/src/utils/dom/getResizeHandleElement.ts +9 -0
  62. package/src/utils/dom/getResizeHandleElementIndex.ts +12 -0
  63. package/src/utils/dom/getResizeHandleElementsForGroup.ts +9 -0
  64. package/src/utils/dom/getResizeHandlePanelIds.ts +18 -0
  65. package/src/utils/events.ts +13 -0
  66. package/src/utils/getPercentageSizeFromMixedSizes.test.ts +47 -0
  67. package/src/utils/getPercentageSizeFromMixedSizes.ts +15 -0
  68. package/src/utils/getResizeEventCursorPosition.ts +19 -0
  69. package/src/utils/initializeDefaultStorage.ts +26 -0
  70. package/src/utils/numbers/fuzzyCompareNumbers.test.ts +16 -0
  71. package/src/utils/numbers/fuzzyCompareNumbers.ts +17 -0
  72. package/src/utils/numbers/fuzzyNumbersEqual.ts +9 -0
  73. package/src/utils/resizePanel.ts +41 -0
  74. package/src/utils/serialization.ts +9 -4
  75. package/src/utils/shouldMonitorPixelBasedConstraints.test.ts +23 -0
  76. package/src/utils/shouldMonitorPixelBasedConstraints.ts +13 -0
  77. package/src/utils/test-utils.ts +136 -0
  78. package/src/utils/validatePanelConstraints.test.ts +151 -0
  79. package/src/utils/validatePanelConstraints.ts +103 -0
  80. package/src/utils/validatePanelGroupLayout.test.ts +233 -0
  81. package/src/utils/validatePanelGroupLayout.ts +88 -0
  82. package/src/vendor/react.ts +4 -0
  83. package/.eslintrc.json +0 -22
  84. package/src/PanelContexts.ts +0 -20
  85. package/src/utils/coordinates.ts +0 -149
  86. package/src/utils/group.ts +0 -315
package/src/Panel.ts CHANGED
@@ -1,64 +1,100 @@
1
+ import { isBrowser } from "#is-browser";
2
+ import { isDevelopment } from "#is-development";
3
+ import { PanelGroupContext } from "./PanelGroupContext";
1
4
  import useIsomorphicLayoutEffect from "./hooks/useIsomorphicEffect";
2
5
  import useUniqueId from "./hooks/useUniqueId";
6
+ import { MixedSizes } from "./types";
3
7
  import {
4
- createElement,
5
- CSSProperties,
6
8
  ElementType,
7
9
  ForwardedRef,
10
+ PropsWithChildren,
11
+ createElement,
8
12
  forwardRef,
9
- ReactNode,
10
13
  useContext,
11
- useEffect,
12
14
  useImperativeHandle,
13
15
  useRef,
14
16
  } from "./vendor/react";
15
17
 
16
- import { PanelGroupContext } from "./PanelContexts";
17
- import {
18
- PanelCallbackRef,
19
- PanelData,
20
- PanelOnCollapse,
21
- PanelOnResize,
22
- } from "./types";
23
-
24
- export type PanelProps = {
25
- children?: ReactNode;
26
- className?: string;
27
- collapsedSize?: number;
28
- collapsible?: boolean;
29
- defaultSize?: number | null;
30
- id?: string | null;
31
- maxSize?: number;
32
- minSize?: number;
33
- onCollapse?: PanelOnCollapse | null;
34
- onResize?: PanelOnResize | null;
35
- order?: number | null;
36
- style?: CSSProperties;
37
- tagName?: ElementType;
18
+ export type PanelOnCollapse = () => void;
19
+ export type PanelOnExpand = () => void;
20
+ export type PanelOnResize = (
21
+ mixedSizes: MixedSizes,
22
+ prevMixedSizes: MixedSizes
23
+ ) => void;
24
+
25
+ export type PanelCallbacks = {
26
+ onCollapse?: PanelOnCollapse;
27
+ onExpand?: PanelOnExpand;
28
+ onResize?: PanelOnResize;
29
+ };
30
+
31
+ export type PanelConstraints = {
32
+ collapsedSizePercentage?: number | undefined;
33
+ collapsedSizePixels?: number | undefined;
34
+ collapsible?: boolean | undefined;
35
+ defaultSizePercentage?: number | undefined;
36
+ defaultSizePixels?: number | undefined;
37
+ maxSizePercentage?: number | undefined;
38
+ maxSizePixels?: number | undefined;
39
+ minSizePercentage?: number | undefined;
40
+ minSizePixels?: number | undefined;
41
+ };
42
+
43
+ export type PanelData = {
44
+ callbacks: PanelCallbacks;
45
+ constraints: PanelConstraints;
46
+ id: string;
47
+ idIsFromProps: boolean;
48
+ order: number | undefined;
38
49
  };
39
50
 
40
51
  export type ImperativePanelHandle = {
41
52
  collapse: () => void;
42
53
  expand: () => void;
43
- getCollapsed(): boolean;
44
- getSize(): number;
45
- resize: (percentage: number) => void;
54
+ getId(): string;
55
+ getSize(): MixedSizes;
56
+ resize: (size: Partial<MixedSizes>) => void;
46
57
  };
47
58
 
48
- function PanelWithForwardedRef({
49
- children = null,
59
+ export type PanelProps = PropsWithChildren<{
60
+ className?: string;
61
+ collapsedSizePercentage?: number | undefined;
62
+ collapsedSizePixels?: number | undefined;
63
+ collapsible?: boolean | undefined;
64
+ defaultSizePercentage?: number | undefined;
65
+ defaultSizePixels?: number | undefined;
66
+ id?: string;
67
+ maxSizePercentage?: number | undefined;
68
+ maxSizePixels?: number | undefined;
69
+ minSizePercentage?: number | undefined;
70
+ minSizePixels?: number | undefined;
71
+ onCollapse?: PanelOnCollapse;
72
+ onExpand?: PanelOnExpand;
73
+ onResize?: PanelOnResize;
74
+ order?: number;
75
+ style?: object;
76
+ tagName?: ElementType;
77
+ }>;
78
+
79
+ export function PanelWithForwardedRef({
80
+ children,
50
81
  className: classNameFromProps = "",
51
- collapsedSize = 0,
52
- collapsible = false,
53
- defaultSize = null,
82
+ collapsedSizePercentage,
83
+ collapsedSizePixels,
84
+ collapsible,
85
+ defaultSizePercentage,
86
+ defaultSizePixels,
54
87
  forwardedRef,
55
- id: idFromProps = null,
56
- maxSize = 100,
57
- minSize = 10,
58
- onCollapse = null,
59
- onResize = null,
60
- order = null,
61
- style: styleFromProps = {},
88
+ id: idFromProps,
89
+ maxSizePercentage,
90
+ maxSizePixels,
91
+ minSizePercentage,
92
+ minSizePixels,
93
+ onCollapse,
94
+ onExpand,
95
+ onResize,
96
+ order,
97
+ style: styleFromProps,
62
98
  tagName: Type = "div",
63
99
  }: PanelProps & {
64
100
  forwardedRef: ForwardedRef<ImperativePanelHandle>;
@@ -70,126 +106,152 @@ function PanelWithForwardedRef({
70
106
  );
71
107
  }
72
108
 
73
- const panelId = useUniqueId(idFromProps);
74
-
75
109
  const {
76
110
  collapsePanel,
77
111
  expandPanel,
112
+ getPanelSize,
78
113
  getPanelStyle,
114
+ isPanelCollapsed,
79
115
  registerPanel,
80
116
  resizePanel,
81
117
  unregisterPanel,
82
118
  } = context;
83
119
 
84
- // Use a ref to guard against users passing inline props
85
- const callbacksRef = useRef<{
86
- onCollapse: PanelOnCollapse | null;
87
- onResize: PanelOnResize | null;
88
- }>({ onCollapse, onResize });
89
- useEffect(() => {
90
- callbacksRef.current.onCollapse = onCollapse;
91
- callbacksRef.current.onResize = onResize;
120
+ const panelId = useUniqueId(idFromProps);
121
+
122
+ const panelDataRef = useRef<PanelData>({
123
+ callbacks: {
124
+ onCollapse,
125
+ onExpand,
126
+ onResize,
127
+ },
128
+ constraints: {
129
+ collapsedSizePercentage,
130
+ collapsedSizePixels,
131
+ collapsible,
132
+ defaultSizePercentage,
133
+ defaultSizePixels,
134
+ maxSizePercentage,
135
+ maxSizePixels,
136
+ minSizePercentage,
137
+ minSizePixels,
138
+ },
139
+ id: panelId,
140
+ idIsFromProps: idFromProps !== undefined,
141
+ order,
92
142
  });
93
143
 
94
- // Basic props validation
95
- if (minSize < 0 || minSize > 100) {
96
- throw Error(`Panel minSize must be between 0 and 100, but was ${minSize}`);
97
- } else if (maxSize < 0 || maxSize > 100) {
98
- throw Error(`Panel maxSize must be between 0 and 100, but was ${maxSize}`);
99
- } else {
100
- if (defaultSize !== null) {
101
- if (defaultSize < 0 || defaultSize > 100) {
102
- throw Error(
103
- `Panel defaultSize must be between 0 and 100, but was ${defaultSize}`
104
- );
105
- } else if (minSize > defaultSize && !collapsible) {
106
- console.error(
107
- `Panel minSize ${minSize} cannot be greater than defaultSize ${defaultSize}`
108
- );
144
+ const devWarningsRef = useRef<{
145
+ didLogMissingDefaultSizeWarning: boolean;
146
+ }>({
147
+ didLogMissingDefaultSizeWarning: false,
148
+ });
109
149
 
110
- defaultSize = minSize;
150
+ // Normally we wouldn't log a warning during render,
151
+ // but effects don't run on the server, so we can't do it there
152
+ if (isDevelopment) {
153
+ if (!devWarningsRef.current.didLogMissingDefaultSizeWarning) {
154
+ if (
155
+ !isBrowser &&
156
+ defaultSizePercentage == null &&
157
+ defaultSizePixels == null
158
+ ) {
159
+ devWarningsRef.current.didLogMissingDefaultSizeWarning = true;
160
+ console.warn(
161
+ `WARNING: Panel defaultSizePercentage or defaultSizePixels prop recommended to avoid layout shift after server rendering`
162
+ );
111
163
  }
112
164
  }
113
165
  }
114
166
 
115
- const style = getPanelStyle(panelId, defaultSize);
116
-
117
- const committedValuesRef = useRef<{
118
- size: number;
119
- }>({
120
- size: parseSizeFromStyle(style),
121
- });
122
- const panelDataRef = useRef<{
123
- callbacksRef: PanelCallbackRef;
124
- collapsedSize: number;
125
- collapsible: boolean;
126
- defaultSize: number | null;
127
- id: string;
128
- idWasAutoGenerated: boolean;
129
- maxSize: number;
130
- minSize: number;
131
- order: number | null;
132
- }>({
133
- callbacksRef,
134
- collapsedSize,
135
- collapsible,
136
- defaultSize,
137
- id: panelId,
138
- idWasAutoGenerated: idFromProps == null,
139
- maxSize,
140
- minSize,
141
- order,
142
- });
143
167
  useIsomorphicLayoutEffect(() => {
144
- committedValuesRef.current.size = parseSizeFromStyle(style);
168
+ const { callbacks, constraints } = panelDataRef.current;
145
169
 
146
- panelDataRef.current.callbacksRef = callbacksRef;
147
- panelDataRef.current.collapsedSize = collapsedSize;
148
- panelDataRef.current.collapsible = collapsible;
149
- panelDataRef.current.defaultSize = defaultSize;
150
170
  panelDataRef.current.id = panelId;
151
- panelDataRef.current.idWasAutoGenerated = idFromProps == null;
152
- panelDataRef.current.maxSize = maxSize;
153
- panelDataRef.current.minSize = minSize;
171
+ panelDataRef.current.idIsFromProps = idFromProps !== undefined;
154
172
  panelDataRef.current.order = order;
173
+
174
+ callbacks.onCollapse = onCollapse;
175
+ callbacks.onExpand = onExpand;
176
+ callbacks.onResize = onResize;
177
+
178
+ constraints.collapsedSizePercentage = collapsedSizePercentage;
179
+ constraints.collapsedSizePixels = collapsedSizePixels;
180
+ constraints.collapsible = collapsible;
181
+ constraints.defaultSizePercentage = defaultSizePercentage;
182
+ constraints.defaultSizePixels = defaultSizePixels;
183
+ constraints.maxSizePercentage = maxSizePercentage;
184
+ constraints.maxSizePixels = maxSizePixels;
185
+ constraints.minSizePercentage = minSizePercentage;
186
+ constraints.minSizePixels = minSizePixels;
155
187
  });
156
188
 
157
189
  useIsomorphicLayoutEffect(() => {
158
- registerPanel(panelId, panelDataRef as PanelData);
190
+ const panelData = panelDataRef.current;
191
+
192
+ registerPanel(panelData);
159
193
 
160
194
  return () => {
161
- unregisterPanel(panelId);
195
+ unregisterPanel(panelData);
162
196
  };
163
197
  }, [order, panelId, registerPanel, unregisterPanel]);
164
198
 
165
199
  useImperativeHandle(
166
200
  forwardedRef,
167
201
  () => ({
168
- collapse: () => collapsePanel(panelId),
169
- expand: () => expandPanel(panelId),
170
- getCollapsed() {
171
- return committedValuesRef.current.size === 0;
202
+ collapse: () => {
203
+ collapsePanel(panelDataRef.current);
204
+ },
205
+ expand: () => {
206
+ expandPanel(panelDataRef.current);
207
+ },
208
+ getId() {
209
+ return panelId;
172
210
  },
173
211
  getSize() {
174
- return committedValuesRef.current.size;
212
+ return getPanelSize(panelDataRef.current);
213
+ },
214
+ isCollapsed() {
215
+ return isPanelCollapsed(panelDataRef.current);
216
+ },
217
+ isExpanded() {
218
+ return !isPanelCollapsed(panelDataRef.current);
219
+ },
220
+ resize: (mixedSizes: Partial<MixedSizes>) => {
221
+ resizePanel(panelDataRef.current, mixedSizes);
175
222
  },
176
- resize: (percentage: number) => resizePanel(panelId, percentage),
177
223
  }),
178
- [collapsePanel, expandPanel, panelId, resizePanel]
224
+ [
225
+ collapsePanel,
226
+ expandPanel,
227
+ getPanelSize,
228
+ isPanelCollapsed,
229
+ panelId,
230
+ resizePanel,
231
+ ]
179
232
  );
180
233
 
234
+ const style = getPanelStyle(panelDataRef.current);
235
+
181
236
  return createElement(Type, {
182
237
  children,
183
238
  className: classNameFromProps,
184
- "data-panel": "",
185
- "data-panel-collapsible": collapsible || undefined,
186
- "data-panel-id": panelId,
187
- "data-panel-size": parseFloat("" + style.flexGrow).toFixed(1),
188
- id: `data-panel-id-${panelId}`,
189
239
  style: {
190
240
  ...style,
191
241
  ...styleFromProps,
192
242
  },
243
+
244
+ // CSS selectors
245
+ "data-panel": "",
246
+
247
+ // e2e test attributes
248
+ "data-panel-collapsible": isDevelopment
249
+ ? collapsible || undefined
250
+ : undefined,
251
+ "data-panel-id": isDevelopment ? panelId : undefined,
252
+ "data-panel-size": isDevelopment
253
+ ? parseFloat("" + style.flexGrow).toFixed(1)
254
+ : undefined,
193
255
  });
194
256
  }
195
257
 
@@ -200,13 +262,3 @@ export const Panel = forwardRef<ImperativePanelHandle, PanelProps>(
200
262
 
201
263
  PanelWithForwardedRef.displayName = "Panel";
202
264
  Panel.displayName = "forwardRef(Panel)";
203
-
204
- // HACK
205
- function parseSizeFromStyle(style: CSSProperties): number {
206
- const { flexGrow } = style;
207
- if (typeof flexGrow === "string") {
208
- return parseFloat(flexGrow);
209
- } else {
210
- return flexGrow as number;
211
- }
212
- }
@@ -0,0 +1,210 @@
1
+ import { Root, createRoot } from "react-dom/client";
2
+ import { act } from "react-dom/test-utils";
3
+ import {
4
+ ImperativePanelGroupHandle,
5
+ MixedSizes,
6
+ Panel,
7
+ PanelGroup,
8
+ PanelResizeHandle,
9
+ } from ".";
10
+ import { mockPanelGroupOffsetWidthAndHeight } from "./utils/test-utils";
11
+ import { createRef } from "./vendor/react";
12
+
13
+ describe("PanelGroup", () => {
14
+ let expectedWarnings: string[] = [];
15
+ let root: Root;
16
+ let uninstallMockOffsetWidthAndHeight: () => void;
17
+
18
+ function expectWarning(expectedMessage: string) {
19
+ expectedWarnings.push(expectedMessage);
20
+ }
21
+
22
+ beforeEach(() => {
23
+ // @ts-expect-error
24
+ global.IS_REACT_ACT_ENVIRONMENT = true;
25
+
26
+ // JSDom doesn't support element sizes
27
+ uninstallMockOffsetWidthAndHeight = mockPanelGroupOffsetWidthAndHeight();
28
+
29
+ const container = document.createElement("div");
30
+ document.body.appendChild(container);
31
+
32
+ expectedWarnings = [];
33
+ root = createRoot(container);
34
+
35
+ jest.spyOn(console, "warn").mockImplementation((actualMessage: string) => {
36
+ const match = expectedWarnings.findIndex((expectedMessage) => {
37
+ return actualMessage.includes(expectedMessage);
38
+ });
39
+
40
+ if (match >= 0) {
41
+ expectedWarnings.splice(match, 1);
42
+ return;
43
+ }
44
+
45
+ throw Error(`Unexpected warning: ${actualMessage}`);
46
+ });
47
+ });
48
+
49
+ afterEach(() => {
50
+ uninstallMockOffsetWidthAndHeight();
51
+
52
+ jest.clearAllMocks();
53
+ jest.resetModules();
54
+
55
+ act(() => {
56
+ root.unmount();
57
+ });
58
+
59
+ expect(expectedWarnings).toHaveLength(0);
60
+ });
61
+
62
+ describe("imperative handle API", () => {
63
+ it("should report the most recently rendered group id", () => {
64
+ const ref = createRef<ImperativePanelGroupHandle>();
65
+
66
+ act(() => {
67
+ root.render(<PanelGroup direction="horizontal" id="one" ref={ref} />);
68
+ });
69
+
70
+ expect(ref.current!.getId()).toBe("one");
71
+
72
+ act(() => {
73
+ root.render(<PanelGroup direction="horizontal" id="two" ref={ref} />);
74
+ });
75
+
76
+ expect(ref.current!.getId()).toBe("two");
77
+ });
78
+
79
+ it("should get and set layouts", () => {
80
+ const ref = createRef<ImperativePanelGroupHandle>();
81
+
82
+ let mostRecentLayout: MixedSizes[] | null = null;
83
+
84
+ const onLayout = (layout: MixedSizes[]) => {
85
+ mostRecentLayout = layout;
86
+ };
87
+
88
+ act(() => {
89
+ root.render(
90
+ <PanelGroup direction="horizontal" onLayout={onLayout} ref={ref}>
91
+ <Panel defaultSizePercentage={50} id="a" />
92
+ <PanelResizeHandle />
93
+ <Panel defaultSizePercentage={50} id="b" />
94
+ </PanelGroup>
95
+ );
96
+ });
97
+
98
+ expect(mostRecentLayout).toEqual([
99
+ {
100
+ sizePercentage: 50,
101
+ sizePixels: 500,
102
+ },
103
+ {
104
+ sizePercentage: 50,
105
+ sizePixels: 500,
106
+ },
107
+ ]);
108
+
109
+ act(() => {
110
+ ref.current!.setLayout([
111
+ { sizePercentage: 25 },
112
+ { sizePercentage: 75 },
113
+ ]);
114
+ });
115
+
116
+ expect(mostRecentLayout).toEqual([
117
+ {
118
+ sizePercentage: 25,
119
+ sizePixels: 250,
120
+ },
121
+ {
122
+ sizePercentage: 75,
123
+ sizePixels: 750,
124
+ },
125
+ ]);
126
+ });
127
+ });
128
+
129
+ describe("DEV warnings", () => {
130
+ it("should warn about unstable layouts without id and order props", () => {
131
+ act(() => {
132
+ root.render(
133
+ <PanelGroup direction="horizontal">
134
+ <Panel defaultSizePercentage={100} id="a" />
135
+ </PanelGroup>
136
+ );
137
+ });
138
+
139
+ expectWarning(
140
+ "Panel id and order props recommended when panels are dynamically rendered"
141
+ );
142
+
143
+ act(() => {
144
+ root.render(
145
+ <PanelGroup direction="horizontal">
146
+ <Panel defaultSizePercentage={50} id="a" />
147
+ <PanelResizeHandle />
148
+ <Panel defaultSizePercentage={50} id="b" />
149
+ </PanelGroup>
150
+ );
151
+ });
152
+ });
153
+
154
+ it("should warn about missing resize handles", () => {
155
+ expectWarning(
156
+ 'Missing resize handle for PanelGroup "group-without-handle"'
157
+ );
158
+
159
+ act(() => {
160
+ root.render(
161
+ <PanelGroup direction="horizontal" id="group-without-handle">
162
+ <Panel />
163
+ <Panel />
164
+ </PanelGroup>
165
+ );
166
+ });
167
+ });
168
+
169
+ it("should warn about an invalid declarative layout", () => {
170
+ expectWarning("Invalid layout total size: 60%, 80%");
171
+
172
+ act(() => {
173
+ root.render(
174
+ <PanelGroup direction="horizontal" id="group-without-handle">
175
+ <Panel defaultSizePercentage={60} />
176
+ <PanelResizeHandle />
177
+ <Panel defaultSizePercentage={80} />
178
+ </PanelGroup>
179
+ );
180
+ });
181
+ });
182
+
183
+ it("should warn about an invalid layout set via the imperative api", () => {
184
+ const ref = createRef<ImperativePanelGroupHandle>();
185
+
186
+ act(() => {
187
+ root.render(
188
+ <PanelGroup
189
+ direction="horizontal"
190
+ id="group-without-handle"
191
+ ref={ref}
192
+ >
193
+ <Panel defaultSizePercentage={30} />
194
+ <PanelResizeHandle />
195
+ <Panel defaultSizePercentage={70} />
196
+ </PanelGroup>
197
+ );
198
+ });
199
+
200
+ expectWarning("Invalid layout total size: 60%, 80%");
201
+
202
+ act(() => {
203
+ ref.current!.setLayout([
204
+ { sizePercentage: 60 },
205
+ { sizePercentage: 80 },
206
+ ]);
207
+ });
208
+ });
209
+ });
210
+ });