react-resizable-panels 0.0.55 → 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 (93) hide show
  1. package/.eslintrc.cjs +26 -0
  2. package/CHANGELOG.md +234 -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 +1241 -1035
  11. package/dist/react-resizable-panels.browser.cjs.mjs +1 -2
  12. package/dist/react-resizable-panels.browser.development.cjs.js +1367 -1081
  13. package/dist/react-resizable-panels.browser.development.cjs.mjs +1 -2
  14. package/dist/react-resizable-panels.browser.development.esm.js +1368 -1081
  15. package/dist/react-resizable-panels.browser.esm.js +1242 -1035
  16. package/dist/react-resizable-panels.cjs.js +1241 -1035
  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 +1370 -1084
  20. package/dist/react-resizable-panels.development.cjs.mjs +1 -2
  21. package/dist/react-resizable-panels.development.esm.js +1371 -1084
  22. package/dist/react-resizable-panels.development.node.cjs.js +1151 -940
  23. package/dist/react-resizable-panels.development.node.cjs.mjs +1 -2
  24. package/dist/react-resizable-panels.development.node.esm.js +1152 -940
  25. package/dist/react-resizable-panels.esm.js +1242 -1035
  26. package/dist/react-resizable-panels.esm.js.map +1 -1
  27. package/dist/react-resizable-panels.node.cjs.js +1049 -912
  28. package/dist/react-resizable-panels.node.cjs.mjs +1 -2
  29. package/dist/react-resizable-panels.node.esm.js +1050 -912
  30. package/jest.config.js +10 -0
  31. package/package.json +3 -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 -669
  36. package/src/PanelGroupContext.ts +33 -0
  37. package/src/PanelResizeHandle.ts +13 -8
  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 +71 -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.ts +55 -0
  60. package/src/utils/convertPixelsToPercentage.test.ts +9 -0
  61. package/src/utils/convertPixelsToPercentage.ts +6 -0
  62. package/src/utils/determinePivotIndices.ts +10 -0
  63. package/src/utils/dom/calculateAvailablePanelSizeInPixels.ts +29 -0
  64. package/src/utils/dom/getAvailableGroupSizePixels.ts +29 -0
  65. package/src/utils/dom/getPanelElement.ts +7 -0
  66. package/src/utils/dom/getPanelGroupElement.ts +7 -0
  67. package/src/utils/dom/getResizeHandleElement.ts +9 -0
  68. package/src/utils/dom/getResizeHandleElementIndex.ts +12 -0
  69. package/src/utils/dom/getResizeHandleElementsForGroup.ts +9 -0
  70. package/src/utils/dom/getResizeHandlePanelIds.ts +18 -0
  71. package/src/utils/events.ts +13 -0
  72. package/src/utils/getPercentageSizeFromMixedSizes.test.ts +47 -0
  73. package/src/utils/getPercentageSizeFromMixedSizes.ts +15 -0
  74. package/src/utils/getResizeEventCursorPosition.ts +19 -0
  75. package/src/utils/initializeDefaultStorage.ts +26 -0
  76. package/src/utils/numbers/fuzzyCompareNumbers.test.ts +16 -0
  77. package/src/utils/numbers/fuzzyCompareNumbers.ts +17 -0
  78. package/src/utils/numbers/fuzzyNumbersEqual.ts +9 -0
  79. package/src/utils/resizePanel.ts +41 -0
  80. package/src/utils/serialization.ts +9 -4
  81. package/src/utils/shouldMonitorPixelBasedConstraints.test.ts +23 -0
  82. package/src/utils/shouldMonitorPixelBasedConstraints.ts +13 -0
  83. package/src/utils/test-utils.ts +136 -0
  84. package/src/utils/validatePanelConstraints.test.ts +151 -0
  85. package/src/utils/validatePanelConstraints.ts +103 -0
  86. package/src/utils/validatePanelGroupLayout.test.ts +233 -0
  87. package/src/utils/validatePanelGroupLayout.ts +88 -0
  88. package/src/vendor/react.ts +4 -0
  89. package/.eslintrc.json +0 -22
  90. package/dist/declarations/src/utils/group.d.ts +0 -29
  91. package/src/PanelContexts.ts +0 -22
  92. package/src/utils/coordinates.ts +0 -149
  93. package/src/utils/group.ts +0 -614
package/jest.config.js ADDED
@@ -0,0 +1,10 @@
1
+ /** @type {import('ts-jest').JestConfigWithTsJest} */
2
+ module.exports = {
3
+ testEnvironment: "jsdom",
4
+ preset: "ts-jest",
5
+ prettierPath: null,
6
+ testEnvironmentOptions: {
7
+ customExportConditions: ["development"],
8
+ },
9
+ testMatch: ["**/*.test.{ts,tsx}"],
10
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-resizable-panels",
3
- "version": "0.0.55",
3
+ "version": "0.0.56",
4
4
  "description": "React components for resizable panel groups/layouts",
5
5
  "author": "Brian Vaughn <brian.david.vaughn@gmail.com>",
6
6
  "license": "MIT",
@@ -62,6 +62,8 @@
62
62
  "types": "dist/react-resizable-panels.cjs.d.ts",
63
63
  "scripts": {
64
64
  "lint": "eslint \"src/**/*.{ts,tsx}\"",
65
+ "test": "jest --config=jest.config.js",
66
+ "test:watch": "jest --config=jest.config.js --watch",
65
67
  "watch": "parcel watch --port=2345"
66
68
  },
67
69
  "devDependencies": {
@@ -0,0 +1,308 @@
1
+ import { Root, createRoot } from "react-dom/client";
2
+ import { act } from "react-dom/test-utils";
3
+ import {
4
+ ImperativePanelHandle,
5
+ MixedSizes,
6
+ Panel,
7
+ PanelGroup,
8
+ PanelResizeHandle,
9
+ } from ".";
10
+ import {
11
+ mockPanelGroupOffsetWidthAndHeight,
12
+ verifyExpandedPanelGroupLayout,
13
+ } from "./utils/test-utils";
14
+ import { createRef } from "./vendor/react";
15
+
16
+ describe("PanelGroup", () => {
17
+ let expectedWarnings: string[] = [];
18
+ let root: Root;
19
+ let uninstallMockOffsetWidthAndHeight: () => void;
20
+
21
+ function expectWarning(expectedMessage: string) {
22
+ expectedWarnings.push(expectedMessage);
23
+ }
24
+
25
+ beforeEach(() => {
26
+ // @ts-expect-error
27
+ global.IS_REACT_ACT_ENVIRONMENT = true;
28
+
29
+ uninstallMockOffsetWidthAndHeight = mockPanelGroupOffsetWidthAndHeight();
30
+
31
+ const container = document.createElement("div");
32
+ document.body.appendChild(container);
33
+
34
+ expectedWarnings = [];
35
+ root = createRoot(container);
36
+
37
+ jest.spyOn(console, "warn").mockImplementation((actualMessage: string) => {
38
+ const match = expectedWarnings.findIndex((expectedMessage) => {
39
+ return actualMessage.includes(expectedMessage);
40
+ });
41
+
42
+ if (match >= 0) {
43
+ expectedWarnings.splice(match, 1);
44
+ return;
45
+ }
46
+
47
+ throw Error(`Unexpected warning: ${actualMessage}`);
48
+ });
49
+ });
50
+
51
+ afterEach(() => {
52
+ uninstallMockOffsetWidthAndHeight();
53
+
54
+ jest.clearAllMocks();
55
+ jest.resetModules();
56
+
57
+ act(() => {
58
+ root.unmount();
59
+ });
60
+
61
+ expect(expectedWarnings).toHaveLength(0);
62
+ });
63
+
64
+ describe("imperative handle API", () => {
65
+ describe("collapse and expand", () => {
66
+ let leftPanelRef = createRef<ImperativePanelHandle>();
67
+ let rightPanelRef = createRef<ImperativePanelHandle>();
68
+
69
+ let mostRecentLayout: MixedSizes[] | null;
70
+
71
+ beforeEach(() => {
72
+ leftPanelRef = createRef<ImperativePanelHandle>();
73
+ rightPanelRef = createRef<ImperativePanelHandle>();
74
+
75
+ mostRecentLayout = null;
76
+
77
+ const onLayout = (layout: MixedSizes[]) => {
78
+ mostRecentLayout = layout;
79
+ };
80
+
81
+ act(() => {
82
+ root.render(
83
+ <PanelGroup direction="horizontal" onLayout={onLayout}>
84
+ <Panel
85
+ collapsible
86
+ defaultSizePercentage={50}
87
+ ref={leftPanelRef}
88
+ />
89
+ <PanelResizeHandle />
90
+ <Panel
91
+ collapsible
92
+ defaultSizePercentage={50}
93
+ ref={rightPanelRef}
94
+ />
95
+ </PanelGroup>
96
+ );
97
+ });
98
+ });
99
+
100
+ it("should expand and collapse the first panel in a group", () => {
101
+ verifyExpandedPanelGroupLayout(mostRecentLayout!, [50, 50]);
102
+ act(() => {
103
+ leftPanelRef.current!.collapse();
104
+ });
105
+ verifyExpandedPanelGroupLayout(mostRecentLayout!, [0, 100]);
106
+ act(() => {
107
+ leftPanelRef.current!.expand();
108
+ });
109
+ verifyExpandedPanelGroupLayout(mostRecentLayout!, [50, 50]);
110
+ });
111
+
112
+ it("should expand and collapse the last panel in a group", () => {
113
+ verifyExpandedPanelGroupLayout(mostRecentLayout!, [50, 50]);
114
+ act(() => {
115
+ rightPanelRef.current!.collapse();
116
+ });
117
+ verifyExpandedPanelGroupLayout(mostRecentLayout!, [100, 0]);
118
+ act(() => {
119
+ rightPanelRef.current!.expand();
120
+ });
121
+ verifyExpandedPanelGroupLayout(mostRecentLayout!, [50, 50]);
122
+ });
123
+
124
+ it("should re-expand to the most recent size before collapsing", () => {
125
+ verifyExpandedPanelGroupLayout(mostRecentLayout!, [50, 50]);
126
+ act(() => {
127
+ leftPanelRef.current!.resize({ sizePercentage: 30 });
128
+ });
129
+ verifyExpandedPanelGroupLayout(mostRecentLayout!, [30, 70]);
130
+ act(() => {
131
+ leftPanelRef.current!.collapse();
132
+ });
133
+ verifyExpandedPanelGroupLayout(mostRecentLayout!, [0, 100]);
134
+ act(() => {
135
+ leftPanelRef.current!.expand();
136
+ });
137
+ verifyExpandedPanelGroupLayout(mostRecentLayout!, [30, 70]);
138
+ });
139
+ });
140
+
141
+ describe("resize", () => {
142
+ let leftPanelRef = createRef<ImperativePanelHandle>();
143
+ let middlePanelRef = createRef<ImperativePanelHandle>();
144
+ let rightPanelRef = createRef<ImperativePanelHandle>();
145
+
146
+ let mostRecentLayout: MixedSizes[] | null;
147
+
148
+ beforeEach(() => {
149
+ leftPanelRef = createRef<ImperativePanelHandle>();
150
+ middlePanelRef = createRef<ImperativePanelHandle>();
151
+ rightPanelRef = createRef<ImperativePanelHandle>();
152
+
153
+ mostRecentLayout = null;
154
+
155
+ const onLayout = (layout: MixedSizes[]) => {
156
+ mostRecentLayout = layout;
157
+ };
158
+
159
+ act(() => {
160
+ root.render(
161
+ <PanelGroup direction="horizontal" onLayout={onLayout}>
162
+ <Panel defaultSizePercentage={20} ref={leftPanelRef} />
163
+ <PanelResizeHandle />
164
+ <Panel defaultSizePercentage={60} ref={middlePanelRef} />
165
+ <PanelResizeHandle />
166
+ <Panel defaultSizePercentage={20} ref={rightPanelRef} />
167
+ </PanelGroup>
168
+ );
169
+ });
170
+ });
171
+
172
+ it("should resize the first panel in a group", () => {
173
+ verifyExpandedPanelGroupLayout(mostRecentLayout!, [20, 60, 20]);
174
+ act(() => {
175
+ leftPanelRef.current!.resize({ sizePercentage: 40 });
176
+ });
177
+ verifyExpandedPanelGroupLayout(mostRecentLayout!, [40, 40, 20]);
178
+ });
179
+
180
+ it("should resize the middle panel in a group", () => {
181
+ verifyExpandedPanelGroupLayout(mostRecentLayout!, [20, 60, 20]);
182
+ act(() => {
183
+ middlePanelRef.current!.resize({ sizePercentage: 40 });
184
+ });
185
+ verifyExpandedPanelGroupLayout(mostRecentLayout!, [20, 40, 40]);
186
+ });
187
+
188
+ it("should resize the last panel in a group", () => {
189
+ verifyExpandedPanelGroupLayout(mostRecentLayout!, [20, 60, 20]);
190
+ act(() => {
191
+ rightPanelRef.current!.resize({ sizePercentage: 40 });
192
+ });
193
+ verifyExpandedPanelGroupLayout(mostRecentLayout!, [20, 40, 40]);
194
+ });
195
+ });
196
+ });
197
+
198
+ describe("invariants", () => {
199
+ beforeEach(() => {
200
+ jest.spyOn(console, "error").mockImplementation(() => {
201
+ // Noop
202
+ });
203
+ });
204
+
205
+ it("should throw if default size is less than 0 or greater than 100", () => {
206
+ expect(() => {
207
+ act(() => {
208
+ root.render(
209
+ <PanelGroup direction="horizontal">
210
+ <Panel defaultSizePercentage={-1} />
211
+ </PanelGroup>
212
+ );
213
+ });
214
+ }).toThrow("Invalid layout");
215
+
216
+ expect(() => {
217
+ act(() => {
218
+ root.render(
219
+ <PanelGroup direction="horizontal">
220
+ <Panel defaultSizePercentage={101} />
221
+ </PanelGroup>
222
+ );
223
+ });
224
+ }).toThrow("Invalid layout");
225
+ });
226
+
227
+ it("should throw if rendered outside of a PanelGroup", () => {
228
+ expect(() => {
229
+ act(() => {
230
+ root.render(<Panel />);
231
+ });
232
+ }).toThrow(
233
+ "Panel components must be rendered within a PanelGroup container"
234
+ );
235
+ });
236
+ });
237
+
238
+ describe("DEV warnings", () => {
239
+ it("should warn about server rendered panels with no default size", () => {
240
+ jest.resetModules();
241
+ jest.mock("#is-browser", () => ({ isBrowser: false }));
242
+
243
+ const { TextEncoder } = require("util");
244
+ global.TextEncoder = TextEncoder;
245
+
246
+ const { renderToStaticMarkup } = require("react-dom/server.browser");
247
+ const { act } = require("react-dom/test-utils");
248
+ const Panel = require("./Panel").Panel;
249
+ const PanelGroup = require("./PanelGroup").PanelGroup;
250
+ const PanelResizeHandle =
251
+ require("./PanelResizeHandle").PanelResizeHandle;
252
+
253
+ act(() => {
254
+ // No warning expected if default sizes provided
255
+ renderToStaticMarkup(
256
+ <PanelGroup direction="horizontal">
257
+ <Panel defaultSizePercentage={100} />
258
+ <PanelResizeHandle />
259
+ <Panel defaultSizePixels={1_000} />
260
+ </PanelGroup>
261
+ );
262
+ });
263
+
264
+ expectWarning(
265
+ "Panel defaultSizePercentage or defaultSizePixels prop recommended to avoid layout shift after server rendering"
266
+ );
267
+
268
+ act(() => {
269
+ renderToStaticMarkup(
270
+ <PanelGroup direction="horizontal">
271
+ <Panel id="one" />
272
+ </PanelGroup>
273
+ );
274
+ });
275
+ });
276
+
277
+ it("should warn if both pixel and percentage units are specified", () => {
278
+ // We just spot check this here; validatePanelConstraints() has its own in-depth tests
279
+ expectWarning(
280
+ "should not specify both percentage and pixel units for: min size"
281
+ );
282
+
283
+ expectWarning("Pixel based constraints require ResizeObserver");
284
+
285
+ act(() => {
286
+ root.render(
287
+ <PanelGroup direction="horizontal" key="minSize">
288
+ <Panel minSizePercentage={100} minSizePixels={1_000} />
289
+ </PanelGroup>
290
+ );
291
+ });
292
+ });
293
+
294
+ it("should warn if invalid sizes are specified declaratively", () => {
295
+ expectWarning("default size should not be less than 0");
296
+
297
+ act(() => {
298
+ root.render(
299
+ <PanelGroup direction="horizontal" key="collapsedSize">
300
+ <Panel defaultSizePercentage={-1} />
301
+ <PanelResizeHandle />
302
+ <Panel />
303
+ </PanelGroup>
304
+ );
305
+ });
306
+ });
307
+ });
308
+ });