react-resizable-panels 0.0.55 → 0.0.57

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 (95) hide show
  1. package/.eslintrc.cjs +26 -0
  2. package/CHANGELOG.md +238 -90
  3. package/README.md +55 -49
  4. package/dist/declarations/src/Panel.d.ts +75 -20
  5. package/dist/declarations/src/PanelGroup.d.ts +29 -25
  6. package/dist/declarations/src/PanelResizeHandle.d.ts +1 -1
  7. package/dist/declarations/src/index.d.ts +5 -6
  8. package/dist/declarations/src/types.d.ts +3 -26
  9. package/dist/declarations/src/vendor/react.d.ts +4 -4
  10. package/dist/react-resizable-panels.browser.cjs.js +1276 -1043
  11. package/dist/react-resizable-panels.browser.cjs.mjs +1 -2
  12. package/dist/react-resizable-panels.browser.development.cjs.js +1410 -1097
  13. package/dist/react-resizable-panels.browser.development.cjs.mjs +1 -2
  14. package/dist/react-resizable-panels.browser.development.esm.js +1411 -1097
  15. package/dist/react-resizable-panels.browser.esm.js +1277 -1043
  16. package/dist/react-resizable-panels.cjs.js +1276 -1043
  17. package/dist/react-resizable-panels.cjs.js.map +1 -1
  18. package/dist/react-resizable-panels.cjs.mjs +1 -2
  19. package/dist/react-resizable-panels.development.cjs.js +1415 -1102
  20. package/dist/react-resizable-panels.development.cjs.mjs +1 -2
  21. package/dist/react-resizable-panels.development.esm.js +1416 -1102
  22. package/dist/react-resizable-panels.development.node.cjs.js +1179 -947
  23. package/dist/react-resizable-panels.development.node.cjs.mjs +1 -2
  24. package/dist/react-resizable-panels.development.node.esm.js +1180 -947
  25. package/dist/react-resizable-panels.esm.js +1277 -1043
  26. package/dist/react-resizable-panels.esm.js.map +1 -1
  27. package/dist/react-resizable-panels.node.cjs.js +1068 -910
  28. package/dist/react-resizable-panels.node.cjs.mjs +1 -2
  29. package/dist/react-resizable-panels.node.esm.js +1069 -910
  30. package/jest.config.js +10 -0
  31. package/package.json +5 -1
  32. package/src/Panel.test.tsx +308 -0
  33. package/src/Panel.ts +175 -123
  34. package/src/PanelGroup.test.tsx +210 -0
  35. package/src/PanelGroup.ts +730 -667
  36. package/src/PanelGroupContext.ts +33 -0
  37. package/src/PanelResizeHandle.ts +21 -17
  38. package/src/hooks/useUniqueId.ts +1 -1
  39. package/src/hooks/useWindowSplitterBehavior.ts +9 -164
  40. package/src/hooks/useWindowSplitterPanelGroupBehavior.ts +185 -0
  41. package/src/index.ts +19 -14
  42. package/src/types.ts +3 -30
  43. package/src/utils/adjustLayoutByDelta.test.ts +1808 -0
  44. package/src/utils/adjustLayoutByDelta.ts +211 -0
  45. package/src/utils/calculateAriaValues.test.ts +111 -0
  46. package/src/utils/calculateAriaValues.ts +67 -0
  47. package/src/utils/calculateDeltaPercentage.ts +68 -0
  48. package/src/utils/calculateDragOffsetPercentage.ts +30 -0
  49. package/src/utils/calculateUnsafeDefaultLayout.test.ts +92 -0
  50. package/src/utils/calculateUnsafeDefaultLayout.ts +55 -0
  51. package/src/utils/callPanelCallbacks.ts +81 -0
  52. package/src/utils/compareLayouts.test.ts +9 -0
  53. package/src/utils/compareLayouts.ts +12 -0
  54. package/src/utils/computePanelFlexBoxStyle.ts +44 -0
  55. package/src/utils/computePercentagePanelConstraints.test.ts +98 -0
  56. package/src/utils/computePercentagePanelConstraints.ts +56 -0
  57. package/src/utils/convertPercentageToPixels.test.ts +9 -0
  58. package/src/utils/convertPercentageToPixels.ts +6 -0
  59. package/src/utils/convertPixelConstraintsToPercentages.test.ts +47 -0
  60. package/src/utils/convertPixelConstraintsToPercentages.ts +72 -0
  61. package/src/utils/convertPixelsToPercentage.test.ts +9 -0
  62. package/src/utils/convertPixelsToPercentage.ts +6 -0
  63. package/src/utils/determinePivotIndices.ts +10 -0
  64. package/src/utils/dom/calculateAvailablePanelSizeInPixels.ts +29 -0
  65. package/src/utils/dom/getAvailableGroupSizePixels.ts +29 -0
  66. package/src/utils/dom/getPanelElement.ts +7 -0
  67. package/src/utils/dom/getPanelGroupElement.ts +9 -0
  68. package/src/utils/dom/getResizeHandleElement.ts +9 -0
  69. package/src/utils/dom/getResizeHandleElementIndex.ts +12 -0
  70. package/src/utils/dom/getResizeHandleElementsForGroup.ts +9 -0
  71. package/src/utils/dom/getResizeHandlePanelIds.ts +18 -0
  72. package/src/utils/events.ts +13 -0
  73. package/src/utils/getPercentageSizeFromMixedSizes.test.ts +47 -0
  74. package/src/utils/getPercentageSizeFromMixedSizes.ts +15 -0
  75. package/src/utils/getResizeEventCursorPosition.ts +19 -0
  76. package/src/utils/initializeDefaultStorage.ts +26 -0
  77. package/src/utils/numbers/fuzzyCompareNumbers.test.ts +16 -0
  78. package/src/utils/numbers/fuzzyCompareNumbers.ts +17 -0
  79. package/src/utils/numbers/fuzzyNumbersEqual.ts +9 -0
  80. package/src/utils/resizePanel.test.ts +45 -0
  81. package/src/utils/resizePanel.ts +60 -0
  82. package/src/utils/serialization.ts +9 -4
  83. package/src/utils/shouldMonitorPixelBasedConstraints.test.ts +23 -0
  84. package/src/utils/shouldMonitorPixelBasedConstraints.ts +13 -0
  85. package/src/utils/test-utils.ts +136 -0
  86. package/src/utils/validatePanelConstraints.test.ts +151 -0
  87. package/src/utils/validatePanelConstraints.ts +103 -0
  88. package/src/utils/validatePanelGroupLayout.test.ts +233 -0
  89. package/src/utils/validatePanelGroupLayout.ts +88 -0
  90. package/src/vendor/react.ts +4 -0
  91. package/.eslintrc.json +0 -22
  92. package/dist/declarations/src/utils/group.d.ts +0 -29
  93. package/src/PanelContexts.ts +0 -22
  94. package/src/utils/coordinates.ts +0 -149
  95. package/src/utils/group.ts +0 -614
package/src/Panel.ts CHANGED
@@ -1,67 +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
- Units,
23
- } from "./types";
24
- import { getAvailableGroupSizePixels } from "./utils/group";
25
-
26
- export type PanelProps = {
27
- children?: ReactNode;
28
- className?: string;
29
- collapsedSize?: number;
30
- collapsible?: boolean;
31
- defaultSize?: number | null;
32
- id?: string | null;
33
- maxSize?: number | null;
34
- minSize?: number;
35
- onCollapse?: PanelOnCollapse | null;
36
- onResize?: PanelOnResize | null;
37
- order?: number | null;
38
- style?: CSSProperties;
39
- 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;
40
49
  };
41
50
 
42
51
  export type ImperativePanelHandle = {
43
52
  collapse: () => void;
44
53
  expand: () => void;
45
- getCollapsed(): boolean;
46
54
  getId(): string;
47
- getSize(units?: Units): number;
48
- resize: (percentage: number, units?: Units) => void;
55
+ getSize(): MixedSizes;
56
+ resize: (size: Partial<MixedSizes>) => void;
49
57
  };
50
58
 
51
- function PanelWithForwardedRef({
52
- 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,
53
81
  className: classNameFromProps = "",
54
- collapsedSize = 0,
55
- collapsible = false,
56
- defaultSize = null,
82
+ collapsedSizePercentage,
83
+ collapsedSizePixels,
84
+ collapsible,
85
+ defaultSizePercentage,
86
+ defaultSizePixels,
57
87
  forwardedRef,
58
- id: idFromProps = null,
59
- maxSize = null,
60
- minSize,
61
- onCollapse = null,
62
- onResize = null,
63
- order = null,
64
- style: styleFromProps = {},
88
+ id: idFromProps,
89
+ maxSizePercentage,
90
+ maxSizePixels,
91
+ minSizePercentage,
92
+ minSizePixels,
93
+ onCollapse,
94
+ onExpand,
95
+ onResize,
96
+ order,
97
+ style: styleFromProps,
65
98
  tagName: Type = "div",
66
99
  }: PanelProps & {
67
100
  forwardedRef: ForwardedRef<ImperativePanelHandle>;
@@ -73,123 +106,152 @@ function PanelWithForwardedRef({
73
106
  );
74
107
  }
75
108
 
76
- const panelId = useUniqueId(idFromProps);
77
-
78
109
  const {
79
110
  collapsePanel,
80
111
  expandPanel,
81
112
  getPanelSize,
82
113
  getPanelStyle,
114
+ isPanelCollapsed,
83
115
  registerPanel,
84
116
  resizePanel,
85
- units,
86
117
  unregisterPanel,
87
118
  } = context;
88
119
 
89
- if (minSize == null) {
90
- if (units === "percentages") {
91
- // Mimics legacy default value for percentage based panel groups
92
- minSize = 10;
93
- } else {
94
- // There is no meaningful minimum pixel default we can provide
95
- minSize = 0;
96
- }
97
- }
120
+ const panelId = useUniqueId(idFromProps);
98
121
 
99
- // Use a ref to guard against users passing inline props
100
- const callbacksRef = useRef<{
101
- onCollapse: PanelOnCollapse | null;
102
- onResize: PanelOnResize | null;
103
- }>({ onCollapse, onResize });
104
- useEffect(() => {
105
- callbacksRef.current.onCollapse = onCollapse;
106
- callbacksRef.current.onResize = onResize;
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,
107
142
  });
108
143
 
109
- const style = getPanelStyle(panelId, defaultSize);
110
-
111
- const committedValuesRef = useRef<{
112
- size: number;
144
+ const devWarningsRef = useRef<{
145
+ didLogMissingDefaultSizeWarning: boolean;
113
146
  }>({
114
- size: parseSizeFromStyle(style),
147
+ didLogMissingDefaultSizeWarning: false,
115
148
  });
116
149
 
117
- const panelDataRef = useRef<{
118
- callbacksRef: PanelCallbackRef;
119
- collapsedSize: number;
120
- collapsible: boolean;
121
- defaultSize: number | null;
122
- id: string;
123
- idWasAutoGenerated: boolean;
124
- maxSize: number | null;
125
- minSize: number;
126
- order: number | null;
127
- }>({
128
- callbacksRef,
129
- collapsedSize,
130
- collapsible,
131
- defaultSize,
132
- id: panelId,
133
- idWasAutoGenerated: idFromProps == null,
134
- maxSize,
135
- minSize,
136
- order,
137
- });
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
+ );
163
+ }
164
+ }
165
+ }
138
166
 
139
167
  useIsomorphicLayoutEffect(() => {
140
- committedValuesRef.current.size = parseSizeFromStyle(style);
168
+ const { callbacks, constraints } = panelDataRef.current;
141
169
 
142
- panelDataRef.current.callbacksRef = callbacksRef;
143
- panelDataRef.current.collapsedSize = collapsedSize;
144
- panelDataRef.current.collapsible = collapsible;
145
- panelDataRef.current.defaultSize = defaultSize;
146
170
  panelDataRef.current.id = panelId;
147
- panelDataRef.current.idWasAutoGenerated = idFromProps == null;
148
- panelDataRef.current.maxSize = maxSize;
149
- panelDataRef.current.minSize = minSize as number;
171
+ panelDataRef.current.idIsFromProps = idFromProps !== undefined;
150
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;
151
187
  });
152
188
 
153
189
  useIsomorphicLayoutEffect(() => {
154
- registerPanel(panelId, panelDataRef as PanelData);
190
+ const panelData = panelDataRef.current;
191
+
192
+ registerPanel(panelData);
155
193
 
156
194
  return () => {
157
- unregisterPanel(panelId);
195
+ unregisterPanel(panelData);
158
196
  };
159
197
  }, [order, panelId, registerPanel, unregisterPanel]);
160
198
 
161
199
  useImperativeHandle(
162
200
  forwardedRef,
163
201
  () => ({
164
- collapse: () => collapsePanel(panelId),
165
- expand: () => expandPanel(panelId),
166
- getCollapsed() {
167
- return committedValuesRef.current.size === 0;
202
+ collapse: () => {
203
+ collapsePanel(panelDataRef.current);
204
+ },
205
+ expand: () => {
206
+ expandPanel(panelDataRef.current);
168
207
  },
169
208
  getId() {
170
209
  return panelId;
171
210
  },
172
- getSize(units) {
173
- return getPanelSize(panelId, units);
211
+ getSize() {
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);
174
222
  },
175
- resize: (percentage: number, units) =>
176
- resizePanel(panelId, percentage, units),
177
223
  }),
178
- [collapsePanel, expandPanel, getPanelSize, 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
+ "data-panel-id": panelId,
247
+
248
+ // e2e test attributes
249
+ "data-panel-collapsible": isDevelopment
250
+ ? collapsible || undefined
251
+ : 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
+ });