react-resizable-panels 2.0.3 → 2.0.5

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 (38) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/declarations/src/PanelResizeHandle.d.ts +1 -0
  3. package/dist/declarations/src/PanelResizeHandleRegistry.d.ts +1 -2
  4. package/dist/declarations/src/index.d.ts +3 -1
  5. package/dist/declarations/src/utils/rects/getIntersectingRectangle.d.ts +2 -0
  6. package/dist/declarations/src/utils/rects/intersects.d.ts +2 -0
  7. package/dist/declarations/src/utils/rects/types.d.ts +6 -0
  8. package/dist/react-resizable-panels.browser.cjs.js +138 -56
  9. package/dist/react-resizable-panels.browser.cjs.mjs +3 -1
  10. package/dist/react-resizable-panels.browser.development.cjs.js +138 -56
  11. package/dist/react-resizable-panels.browser.development.cjs.mjs +3 -1
  12. package/dist/react-resizable-panels.browser.development.esm.js +137 -57
  13. package/dist/react-resizable-panels.browser.esm.js +137 -57
  14. package/dist/react-resizable-panels.cjs.js +138 -56
  15. package/dist/react-resizable-panels.cjs.mjs +3 -1
  16. package/dist/react-resizable-panels.development.cjs.js +138 -56
  17. package/dist/react-resizable-panels.development.cjs.mjs +3 -1
  18. package/dist/react-resizable-panels.development.esm.js +137 -57
  19. package/dist/react-resizable-panels.development.node.cjs.js +138 -56
  20. package/dist/react-resizable-panels.development.node.cjs.mjs +3 -1
  21. package/dist/react-resizable-panels.development.node.esm.js +137 -57
  22. package/dist/react-resizable-panels.esm.js +137 -57
  23. package/dist/react-resizable-panels.node.cjs.js +138 -56
  24. package/dist/react-resizable-panels.node.cjs.mjs +3 -1
  25. package/dist/react-resizable-panels.node.esm.js +137 -57
  26. package/package.json +4 -1
  27. package/src/Panel.test.tsx +63 -0
  28. package/src/PanelGroup.test.tsx +21 -1
  29. package/src/PanelResizeHandle.test.tsx +181 -22
  30. package/src/PanelResizeHandle.ts +44 -24
  31. package/src/PanelResizeHandleRegistry.ts +87 -30
  32. package/src/index.ts +4 -0
  33. package/src/utils/rects/getIntersectingRectangle.test.ts +198 -0
  34. package/src/utils/rects/getIntersectingRectangle.ts +28 -0
  35. package/src/utils/rects/intersects.test.ts +197 -0
  36. package/src/utils/rects/intersects.ts +23 -0
  37. package/src/utils/rects/types.ts +6 -0
  38. package/src/utils/test-utils.ts +39 -0
@@ -1,4 +1,5 @@
1
1
  import * as React from 'react';
2
+ import { compare } from 'stacking-order';
2
3
 
3
4
  // This module exists to work around Webpack issue https://github.com/webpack/webpack/issues/14814
4
5
 
@@ -235,6 +236,14 @@ function getInputType() {
235
236
  }
236
237
  }
237
238
 
239
+ function intersects(rectOne, rectTwo, strict) {
240
+ if (strict) {
241
+ return rectOne.x < rectTwo.x + rectTwo.width && rectOne.x + rectOne.width > rectTwo.x && rectOne.y < rectTwo.y + rectTwo.height && rectOne.y + rectOne.height > rectTwo.y;
242
+ } else {
243
+ return rectOne.x <= rectTwo.x + rectTwo.width && rectOne.x + rectOne.width >= rectTwo.x && rectOne.y <= rectTwo.y + rectTwo.height && rectOne.y + rectOne.height >= rectTwo.y;
244
+ }
245
+ }
246
+
238
247
  const EXCEEDED_HORIZONTAL_MIN = 0b0001;
239
248
  const EXCEEDED_HORIZONTAL_MAX = 0b0010;
240
249
  const EXCEEDED_VERTICAL_MIN = 0b0100;
@@ -273,12 +282,16 @@ function registerResizeHandle(resizeHandleId, element, direction, hitAreaMargins
273
282
  };
274
283
  }
275
284
  function handlePointerDown(event) {
285
+ const {
286
+ target
287
+ } = event;
276
288
  const {
277
289
  x,
278
290
  y
279
291
  } = getResizeEventCoordinates(event);
280
292
  isPointerDown = true;
281
293
  recalculateIntersectingHandles({
294
+ target,
282
295
  x,
283
296
  y
284
297
  });
@@ -293,29 +306,32 @@ function handlePointerMove(event) {
293
306
  x,
294
307
  y
295
308
  } = getResizeEventCoordinates(event);
296
- if (isPointerDown) {
297
- intersectingHandles.forEach(data => {
298
- const {
299
- setResizeHandlerState
300
- } = data;
301
- setResizeHandlerState("move", "drag", event);
302
- });
309
+ if (!isPointerDown) {
310
+ const {
311
+ target
312
+ } = event;
303
313
 
304
- // Update cursor based on return value(s) from active handles
305
- updateCursor();
306
- } else {
314
+ // Recalculate intersecting handles whenever the pointer moves, except if it has already been pressed
315
+ // at that point, the handles may not move with the pointer (depending on constraints)
316
+ // but the same set of active handles should be locked until the pointer is released
307
317
  recalculateIntersectingHandles({
318
+ target,
308
319
  x,
309
320
  y
310
321
  });
311
- updateResizeHandlerStates("move", event);
312
- updateCursor();
313
322
  }
323
+ updateResizeHandlerStates("move", event);
324
+
325
+ // Update cursor based on return value(s) from active handles
326
+ updateCursor();
314
327
  if (intersectingHandles.length > 0) {
315
328
  event.preventDefault();
316
329
  }
317
330
  }
318
331
  function handlePointerUp(event) {
332
+ const {
333
+ target
334
+ } = event;
319
335
  const {
320
336
  x,
321
337
  y
@@ -325,33 +341,72 @@ function handlePointerUp(event) {
325
341
  if (intersectingHandles.length > 0) {
326
342
  event.preventDefault();
327
343
  }
344
+ updateResizeHandlerStates("up", event);
328
345
  recalculateIntersectingHandles({
346
+ target,
329
347
  x,
330
348
  y
331
349
  });
332
- updateResizeHandlerStates("up", event);
333
350
  updateCursor();
334
351
  updateListeners();
335
352
  }
336
353
  function recalculateIntersectingHandles({
354
+ target,
337
355
  x,
338
356
  y
339
357
  }) {
340
358
  intersectingHandles.splice(0);
359
+ let targetElement = null;
360
+ if (target instanceof HTMLElement) {
361
+ targetElement = target;
362
+ }
341
363
  registeredResizeHandlers.forEach(data => {
342
364
  const {
343
- element,
365
+ element: dragHandleElement,
344
366
  hitAreaMargins
345
367
  } = data;
368
+ const dragHandleRect = dragHandleElement.getBoundingClientRect();
346
369
  const {
347
370
  bottom,
348
371
  left,
349
372
  right,
350
373
  top
351
- } = element.getBoundingClientRect();
374
+ } = dragHandleRect;
352
375
  const margin = isCoarsePointer ? hitAreaMargins.coarse : hitAreaMargins.fine;
353
- const intersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
354
- if (intersects) {
376
+ const eventIntersects = x >= left - margin && x <= right + margin && y >= top - margin && y <= bottom + margin;
377
+ if (eventIntersects) {
378
+ // TRICKY
379
+ // We listen for pointers events at the root in order to support hit area margins
380
+ // (determining when the pointer is close enough to an element to be considered a "hit")
381
+ // Clicking on an element "above" a handle (e.g. a modal) should prevent a hit though
382
+ // so at this point we need to compare stacking order of a potentially intersecting drag handle,
383
+ // and the element that was actually clicked/touched
384
+ if (targetElement !== null && dragHandleElement !== targetElement && !dragHandleElement.contains(targetElement) && !targetElement.contains(dragHandleElement) &&
385
+ // Calculating stacking order has a cost, so we should avoid it if possible
386
+ // That is why we only check potentially intersecting handles,
387
+ // and why we skip if the event target is within the handle's DOM
388
+ compare(targetElement, dragHandleElement) > 0) {
389
+ // If the target is above the drag handle, then we also need to confirm they overlap
390
+ // If they are beside each other (e.g. a panel and its drag handle) then the handle is still interactive
391
+ //
392
+ // It's not enough to compare only the target
393
+ // The target might be a small element inside of a larger container
394
+ // (For example, a SPAN or a DIV inside of a larger modal dialog)
395
+ let currentElement = targetElement;
396
+ let didIntersect = false;
397
+ while (currentElement) {
398
+ if (currentElement.contains(dragHandleElement)) {
399
+ break;
400
+ } else if (intersects(currentElement.getBoundingClientRect(), dragHandleRect, true)) {
401
+ didIntersect = true;
402
+ break;
403
+ }
404
+ currentElement = currentElement.parentElement;
405
+ }
406
+ if (didIntersect) {
407
+ return;
408
+ }
409
+ }
355
410
  intersectingHandles.push(data);
356
411
  }
357
412
  });
@@ -443,15 +498,8 @@ function updateResizeHandlerStates(action, event) {
443
498
  const {
444
499
  setResizeHandlerState
445
500
  } = data;
446
- if (intersectingHandles.includes(data)) {
447
- if (isPointerDown) {
448
- setResizeHandlerState(action, "drag", event);
449
- } else {
450
- setResizeHandlerState(action, "hover", event);
451
- }
452
- } else {
453
- setResizeHandlerState(action, "inactive", event);
454
- }
501
+ const isActive = intersectingHandles.includes(data);
502
+ setResizeHandlerState(action, isActive, event);
455
503
  });
456
504
  }
457
505
 
@@ -1804,6 +1852,12 @@ function PanelResizeHandle({
1804
1852
  const [state, setState] = useState("inactive");
1805
1853
  const [isFocused, setIsFocused] = useState(false);
1806
1854
  const [resizeHandler, setResizeHandler] = useState(null);
1855
+ const committedValuesRef = useRef({
1856
+ state
1857
+ });
1858
+ useLayoutEffect(() => {
1859
+ committedValuesRef.current.state = state;
1860
+ });
1807
1861
  useEffect(() => {
1808
1862
  if (disabled) {
1809
1863
  setResizeHandler(null);
@@ -1819,38 +1873,47 @@ function PanelResizeHandle({
1819
1873
  }
1820
1874
  const element = elementRef.current;
1821
1875
  assert(element);
1822
- const setResizeHandlerState = (action, state, event) => {
1823
- setState(state);
1824
- switch (action) {
1825
- case "down":
1826
- {
1827
- startDragging(resizeHandleId, event);
1828
- const {
1829
- onDragging
1830
- } = callbacksRef.current;
1831
- if (onDragging) {
1832
- onDragging(true);
1876
+ const setResizeHandlerState = (action, isActive, event) => {
1877
+ if (isActive) {
1878
+ switch (action) {
1879
+ case "down":
1880
+ {
1881
+ setState("drag");
1882
+ startDragging(resizeHandleId, event);
1883
+ const {
1884
+ onDragging
1885
+ } = callbacksRef.current;
1886
+ if (onDragging) {
1887
+ onDragging(true);
1888
+ }
1889
+ break;
1833
1890
  }
1834
- break;
1835
- }
1836
- case "up":
1837
- {
1838
- stopDragging();
1839
- const {
1840
- onDragging
1841
- } = callbacksRef.current;
1842
- if (onDragging) {
1843
- onDragging(false);
1891
+ case "move":
1892
+ {
1893
+ const {
1894
+ state
1895
+ } = committedValuesRef.current;
1896
+ if (state !== "drag") {
1897
+ setState("hover");
1898
+ }
1899
+ resizeHandler(event);
1900
+ break;
1844
1901
  }
1845
- break;
1846
- }
1847
- }
1848
- switch (state) {
1849
- case "drag":
1850
- {
1851
- resizeHandler(event);
1852
- break;
1853
- }
1902
+ case "up":
1903
+ {
1904
+ setState("hover");
1905
+ stopDragging();
1906
+ const {
1907
+ onDragging
1908
+ } = callbacksRef.current;
1909
+ if (onDragging) {
1910
+ onDragging(false);
1911
+ }
1912
+ break;
1913
+ }
1914
+ }
1915
+ } else {
1916
+ setState("inactive");
1854
1917
  }
1855
1918
  };
1856
1919
  return registerResizeHandle(resizeHandleId, element, direction, {
@@ -1907,4 +1970,21 @@ function getPanelElementsForGroup(groupId, scope = document) {
1907
1970
  return Array.from(scope.querySelectorAll(`[data-panel][data-panel-group-id="${groupId}"]`));
1908
1971
  }
1909
1972
 
1910
- export { Panel, PanelGroup, PanelResizeHandle, assert, getPanelElement, getPanelElementsForGroup, getPanelGroupElement, getResizeHandleElement, getResizeHandleElementIndex, getResizeHandleElementsForGroup, getResizeHandlePanelIds };
1973
+ function getIntersectingRectangle(rectOne, rectTwo, strict) {
1974
+ if (!intersects(rectOne, rectTwo, strict)) {
1975
+ return {
1976
+ x: 0,
1977
+ y: 0,
1978
+ width: 0,
1979
+ height: 0
1980
+ };
1981
+ }
1982
+ return {
1983
+ x: Math.max(rectOne.x, rectTwo.x),
1984
+ y: Math.max(rectOne.y, rectTwo.y),
1985
+ width: Math.min(rectOne.x + rectOne.width, rectTwo.x + rectTwo.width) - Math.max(rectOne.x, rectTwo.x),
1986
+ height: Math.min(rectOne.y + rectOne.height, rectTwo.y + rectTwo.height) - Math.max(rectOne.y, rectTwo.y)
1987
+ };
1988
+ }
1989
+
1990
+ export { Panel, PanelGroup, PanelResizeHandle, assert, getIntersectingRectangle, getPanelElement, getPanelElementsForGroup, getPanelGroupElement, getResizeHandleElement, getResizeHandleElementIndex, getResizeHandleElementsForGroup, getResizeHandlePanelIds, intersects };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-resizable-panels",
3
- "version": "2.0.3",
3
+ "version": "2.0.5",
4
4
  "description": "React components for resizable panel groups/layouts",
5
5
  "author": "Brian Vaughn <brian.david.vaughn@gmail.com>",
6
6
  "license": "MIT",
@@ -66,6 +66,9 @@
66
66
  "test:watch": "jest --config=jest.config.js --watch",
67
67
  "watch": "parcel watch --port=2345"
68
68
  },
69
+ "dependencies": {
70
+ "stacking-order": "^1"
71
+ },
69
72
  "devDependencies": {
70
73
  "@babel/plugin-proposal-nullish-coalescing-operator": "7.18.6",
71
74
  "@babel/plugin-proposal-optional-chaining": "7.21.0",
@@ -5,6 +5,7 @@ import { assert } from "./utils/assert";
5
5
  import { getPanelElement } from "./utils/dom/getPanelElement";
6
6
  import {
7
7
  mockPanelGroupOffsetWidthAndHeight,
8
+ verifyAttribute,
8
9
  verifyExpandedPanelGroupLayout,
9
10
  } from "./utils/test-utils";
10
11
  import { createRef } from "./vendor/react";
@@ -675,6 +676,68 @@ describe("PanelGroup", () => {
675
676
  });
676
677
  });
677
678
 
679
+ describe("data attributes", () => {
680
+ it("should initialize with the correct props based attributes", () => {
681
+ act(() => {
682
+ root.render(
683
+ <PanelGroup direction="horizontal" id="test-group">
684
+ <Panel defaultSize={75} id="left-panel" />
685
+ <PanelResizeHandle />
686
+ <Panel collapsible id="right-panel" />
687
+ </PanelGroup>
688
+ );
689
+ });
690
+
691
+ const leftElement = getPanelElement("left-panel", container);
692
+ const rightElement = getPanelElement("right-panel", container);
693
+
694
+ assert(leftElement);
695
+ assert(rightElement);
696
+
697
+ verifyAttribute(leftElement, "data-panel", "");
698
+ verifyAttribute(leftElement, "data-panel-id", "left-panel");
699
+ verifyAttribute(leftElement, "data-panel-group-id", "test-group");
700
+ verifyAttribute(leftElement, "data-panel-size", "75.0");
701
+ verifyAttribute(leftElement, "data-panel-collapsible", null);
702
+
703
+ verifyAttribute(rightElement, "data-panel", "");
704
+ verifyAttribute(rightElement, "data-panel-id", "right-panel");
705
+ verifyAttribute(rightElement, "data-panel-group-id", "test-group");
706
+ verifyAttribute(rightElement, "data-panel-size", "25.0");
707
+ verifyAttribute(rightElement, "data-panel-collapsible", "true");
708
+ });
709
+
710
+ it("should update the data-panel-size attribute when the panel resizes", () => {
711
+ const leftPanelRef = createRef<ImperativePanelHandle>();
712
+
713
+ act(() => {
714
+ root.render(
715
+ <PanelGroup direction="horizontal" id="test-group">
716
+ <Panel defaultSize={75} id="left-panel" ref={leftPanelRef} />
717
+ <PanelResizeHandle />
718
+ <Panel collapsible id="right-panel" />
719
+ </PanelGroup>
720
+ );
721
+ });
722
+
723
+ const leftElement = getPanelElement("left-panel", container);
724
+ const rightElement = getPanelElement("right-panel", container);
725
+
726
+ assert(leftElement);
727
+ assert(rightElement);
728
+
729
+ verifyAttribute(leftElement, "data-panel-size", "75.0");
730
+ verifyAttribute(rightElement, "data-panel-size", "25.0");
731
+
732
+ act(() => {
733
+ leftPanelRef.current?.resize(30);
734
+ });
735
+
736
+ verifyAttribute(leftElement, "data-panel-size", "30.0");
737
+ verifyAttribute(rightElement, "data-panel-size", "70.0");
738
+ });
739
+ });
740
+
678
741
  describe("DEV warnings", () => {
679
742
  it("should warn about server rendered panels with no default size", () => {
680
743
  jest.resetModules();
@@ -13,7 +13,10 @@ import {
13
13
  } from ".";
14
14
  import { assert } from "./utils/assert";
15
15
  import { getPanelGroupElement } from "./utils/dom/getPanelGroupElement";
16
- import { mockPanelGroupOffsetWidthAndHeight } from "./utils/test-utils";
16
+ import {
17
+ mockPanelGroupOffsetWidthAndHeight,
18
+ verifyAttribute,
19
+ } from "./utils/test-utils";
17
20
  import { createRef } from "./vendor/react";
18
21
 
19
22
  describe("PanelGroup", () => {
@@ -256,6 +259,23 @@ describe("PanelGroup", () => {
256
259
  });
257
260
  });
258
261
 
262
+ describe("data attributes", () => {
263
+ it("should initialize with the correct props based attributes", () => {
264
+ act(() => {
265
+ root.render(
266
+ <PanelGroup direction="horizontal" id="test-group"></PanelGroup>
267
+ );
268
+ });
269
+
270
+ const element = getPanelGroupElement("test-group", container);
271
+ assert(element);
272
+
273
+ verifyAttribute(element, "data-panel-group", "");
274
+ verifyAttribute(element, "data-panel-group-direction", "horizontal");
275
+ verifyAttribute(element, "data-panel-group-id", "test-group");
276
+ });
277
+ });
278
+
259
279
  describe("DEV warnings", () => {
260
280
  it("should warn about unstable layouts without id and order props", () => {
261
281
  act(() => {
@@ -1,9 +1,14 @@
1
1
  import { Root, createRoot } from "react-dom/client";
2
2
  import { act } from "react-dom/test-utils";
3
+ import type { PanelResizeHandleProps } from "react-resizable-panels";
3
4
  import { Panel, PanelGroup, PanelResizeHandle } from ".";
4
5
  import { assert } from "./utils/assert";
5
6
  import { getResizeHandleElement } from "./utils/dom/getResizeHandleElement";
6
- import { dispatchPointerEvent } from "./utils/test-utils";
7
+ import {
8
+ dispatchPointerEvent,
9
+ mockBoundingClientRect,
10
+ verifyAttribute,
11
+ } from "./utils/test-utils";
7
12
 
8
13
  describe("PanelResizeHandle", () => {
9
14
  let expectedWarnings: string[] = [];
@@ -67,47 +72,201 @@ describe("PanelResizeHandle", () => {
67
72
  expect(element.title).toBe("bar");
68
73
  });
69
74
 
75
+ function setupMockedGroup({
76
+ leftProps = {},
77
+ rightProps = {},
78
+ }: {
79
+ leftProps?: Partial<PanelResizeHandleProps>;
80
+ rightProps?: Partial<PanelResizeHandleProps>;
81
+ } = {}) {
82
+ act(() => {
83
+ root.render(
84
+ <PanelGroup direction="horizontal" id="test-group">
85
+ <Panel />
86
+ <PanelResizeHandle id="handle-left" tabIndex={1} {...leftProps} />
87
+ <Panel />
88
+ <PanelResizeHandle id="handle-right" tabIndex={2} {...rightProps} />
89
+ <Panel />
90
+ </PanelGroup>
91
+ );
92
+ });
93
+
94
+ const leftElement = getResizeHandleElement("handle-left", container);
95
+ const rightElement = getResizeHandleElement("handle-right", container);
96
+
97
+ assert(leftElement);
98
+ assert(rightElement);
99
+
100
+ // JSDom doesn't properly handle bounding rects
101
+ mockBoundingClientRect(leftElement, {
102
+ x: 50,
103
+ y: 0,
104
+ height: 50,
105
+ width: 2,
106
+ });
107
+ mockBoundingClientRect(rightElement, {
108
+ x: 100,
109
+ y: 0,
110
+ height: 50,
111
+ width: 2,
112
+ });
113
+
114
+ return {
115
+ leftElement,
116
+ rightElement,
117
+ };
118
+ }
119
+
70
120
  describe("callbacks", () => {
71
121
  describe("onDragging", () => {
72
- it("should fire when dragging starts/stops", async () => {
122
+ it("should fire when dragging starts/stops", () => {
73
123
  const onDragging = jest.fn();
74
124
 
75
- act(() => {
76
- root.render(
77
- <PanelGroup direction="horizontal">
78
- <Panel />
79
- <PanelResizeHandle
80
- id="handle"
81
- onDragging={onDragging}
82
- tabIndex={123}
83
- title="bar"
84
- />
85
- <Panel />
86
- </PanelGroup>
87
- );
125
+ const { leftElement } = setupMockedGroup({
126
+ leftProps: { onDragging },
88
127
  });
89
128
 
90
- const handleElement = container.querySelector(
91
- '[data-panel-resize-handle-id="handle"]'
92
- ) as HTMLElement;
93
-
94
129
  act(() => {
95
- dispatchPointerEvent("mouseover", handleElement);
130
+ dispatchPointerEvent("mousemove", leftElement);
96
131
  });
97
132
  expect(onDragging).not.toHaveBeenCalled();
98
133
 
99
134
  act(() => {
100
- dispatchPointerEvent("mousedown", handleElement);
135
+ dispatchPointerEvent("mousedown", leftElement);
101
136
  });
102
137
  expect(onDragging).toHaveBeenCalledTimes(1);
103
138
  expect(onDragging).toHaveBeenCalledWith(true);
104
139
 
105
140
  act(() => {
106
- dispatchPointerEvent("mouseup", handleElement);
141
+ dispatchPointerEvent("mouseup", leftElement);
107
142
  });
108
143
  expect(onDragging).toHaveBeenCalledTimes(2);
109
144
  expect(onDragging).toHaveBeenCalledWith(false);
110
145
  });
146
+
147
+ it("should only fire for the handle that has been dragged", () => {
148
+ const onDraggingLeft = jest.fn();
149
+ const onDraggingRight = jest.fn();
150
+
151
+ const { leftElement } = setupMockedGroup({
152
+ leftProps: { onDragging: onDraggingLeft },
153
+ rightProps: { onDragging: onDraggingRight },
154
+ });
155
+
156
+ act(() => {
157
+ dispatchPointerEvent("mousemove", leftElement);
158
+ });
159
+ expect(onDraggingLeft).not.toHaveBeenCalled();
160
+ expect(onDraggingRight).not.toHaveBeenCalled();
161
+
162
+ act(() => {
163
+ dispatchPointerEvent("mousedown", leftElement);
164
+ });
165
+ expect(onDraggingLeft).toHaveBeenCalledTimes(1);
166
+ expect(onDraggingLeft).toHaveBeenCalledWith(true);
167
+ expect(onDraggingRight).not.toHaveBeenCalled();
168
+
169
+ act(() => {
170
+ dispatchPointerEvent("mouseup", leftElement);
171
+ });
172
+ expect(onDraggingLeft).toHaveBeenCalledTimes(2);
173
+ expect(onDraggingLeft).toHaveBeenCalledWith(false);
174
+ expect(onDraggingRight).not.toHaveBeenCalled();
175
+ });
176
+ });
177
+ });
178
+
179
+ describe("data attributes", () => {
180
+ it("should initialize with the correct props based attributes", () => {
181
+ const { leftElement, rightElement } = setupMockedGroup();
182
+
183
+ verifyAttribute(leftElement, "data-panel-group-id", "test-group");
184
+ verifyAttribute(leftElement, "data-resize-handle", "");
185
+ verifyAttribute(leftElement, "data-panel-group-direction", "horizontal");
186
+ verifyAttribute(leftElement, "data-panel-resize-handle-enabled", "true");
187
+ verifyAttribute(
188
+ leftElement,
189
+ "data-panel-resize-handle-id",
190
+ "handle-left"
191
+ );
192
+
193
+ verifyAttribute(rightElement, "data-panel-group-id", "test-group");
194
+ verifyAttribute(rightElement, "data-resize-handle", "");
195
+ verifyAttribute(rightElement, "data-panel-group-direction", "horizontal");
196
+ verifyAttribute(rightElement, "data-panel-resize-handle-enabled", "true");
197
+ verifyAttribute(
198
+ rightElement,
199
+ "data-panel-resize-handle-id",
200
+ "handle-right"
201
+ );
202
+ });
203
+
204
+ it("should update data-resize-handle-active and data-resize-handle-state when dragging starts/stops", () => {
205
+ const { leftElement, rightElement } = setupMockedGroup();
206
+ verifyAttribute(leftElement, "data-resize-handle-active", null);
207
+ verifyAttribute(rightElement, "data-resize-handle-active", null);
208
+ verifyAttribute(leftElement, "data-resize-handle-state", "inactive");
209
+ verifyAttribute(rightElement, "data-resize-handle-state", "inactive");
210
+
211
+ act(() => {
212
+ dispatchPointerEvent("mousemove", leftElement);
213
+ });
214
+ verifyAttribute(leftElement, "data-resize-handle-active", null);
215
+ verifyAttribute(rightElement, "data-resize-handle-active", null);
216
+ verifyAttribute(leftElement, "data-resize-handle-state", "hover");
217
+ verifyAttribute(rightElement, "data-resize-handle-state", "inactive");
218
+
219
+ act(() => {
220
+ dispatchPointerEvent("mousedown", leftElement);
221
+ });
222
+ verifyAttribute(leftElement, "data-resize-handle-active", "pointer");
223
+ verifyAttribute(rightElement, "data-resize-handle-active", null);
224
+ verifyAttribute(leftElement, "data-resize-handle-state", "drag");
225
+ verifyAttribute(rightElement, "data-resize-handle-state", "inactive");
226
+
227
+ act(() => {
228
+ dispatchPointerEvent("mousemove", leftElement);
229
+ });
230
+ verifyAttribute(leftElement, "data-resize-handle-active", "pointer");
231
+ verifyAttribute(rightElement, "data-resize-handle-active", null);
232
+ verifyAttribute(leftElement, "data-resize-handle-state", "drag");
233
+ verifyAttribute(rightElement, "data-resize-handle-state", "inactive");
234
+
235
+ act(() => {
236
+ dispatchPointerEvent("mouseup", leftElement);
237
+ });
238
+ verifyAttribute(leftElement, "data-resize-handle-active", null);
239
+ verifyAttribute(rightElement, "data-resize-handle-active", null);
240
+ verifyAttribute(leftElement, "data-resize-handle-state", "hover");
241
+ verifyAttribute(rightElement, "data-resize-handle-state", "inactive");
242
+
243
+ act(() => {
244
+ dispatchPointerEvent("mousemove", rightElement);
245
+ });
246
+ verifyAttribute(leftElement, "data-resize-handle-active", null);
247
+ verifyAttribute(rightElement, "data-resize-handle-active", null);
248
+ verifyAttribute(leftElement, "data-resize-handle-state", "inactive");
249
+ verifyAttribute(rightElement, "data-resize-handle-state", "hover");
250
+ });
251
+
252
+ it("should update data-resize-handle-active when focused", () => {
253
+ const { leftElement, rightElement } = setupMockedGroup();
254
+ verifyAttribute(leftElement, "data-resize-handle-active", null);
255
+ verifyAttribute(rightElement, "data-resize-handle-active", null);
256
+
257
+ act(() => {
258
+ leftElement.focus();
259
+ });
260
+ expect(document.activeElement).toBe(leftElement);
261
+ verifyAttribute(leftElement, "data-resize-handle-active", "keyboard");
262
+ verifyAttribute(rightElement, "data-resize-handle-active", null);
263
+
264
+ act(() => {
265
+ leftElement.blur();
266
+ });
267
+ expect(document.activeElement).not.toBe(leftElement);
268
+ verifyAttribute(leftElement, "data-resize-handle-active", null);
269
+ verifyAttribute(rightElement, "data-resize-handle-active", null);
111
270
  });
112
271
  });
113
272
  });