@purpurds/table 8.6.0 → 8.8.0
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.
- package/dist/LICENSE.txt +16 -16
- package/dist/styles.css +1 -1
- package/dist/table-column-header-cell.d.ts.map +1 -1
- package/dist/table-content.d.ts +16 -27
- package/dist/table-content.d.ts.map +1 -1
- package/dist/table.cjs.js +65 -65
- package/dist/table.cjs.js.map +1 -1
- package/dist/table.d.ts.map +1 -1
- package/dist/table.es.js +4191 -4249
- package/dist/table.es.js.map +1 -1
- package/dist/use-drag-handle.hook.d.ts +2 -6
- package/dist/use-drag-handle.hook.d.ts.map +1 -1
- package/dist/utils/custom-keyboard-coordinates.d.ts +3 -2
- package/dist/utils/custom-keyboard-coordinates.d.ts.map +1 -1
- package/package.json +25 -25
- package/src/cell-types/date-cell.tsx +1 -1
- package/src/table-column-header-cell.tsx +9 -5
- package/src/table-content-drag.test.tsx +43 -430
- package/src/table-content.tsx +62 -118
- package/src/table-settings-drawer.tsx +1 -1
- package/src/table.stories.tsx +11 -0
- package/src/table.tsx +100 -139
- package/src/use-drag-handle.hook.tsx +7 -26
- package/src/use-drag-handle.test.tsx +21 -174
- package/src/utils/custom-keyboard-coordinates.ts +27 -66
|
@@ -1,102 +1,12 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
3
|
import userEvent from "@testing-library/user-event";
|
|
4
4
|
import { axe } from "vitest-axe";
|
|
5
5
|
|
|
6
|
-
import { TableColumnDragHandle
|
|
7
|
-
|
|
8
|
-
// Test component to test the hook
|
|
9
|
-
function TestDragHandleComponent() {
|
|
10
|
-
const { mouseDownActive, handleMouseDown } = useDragHandle();
|
|
11
|
-
|
|
12
|
-
return (
|
|
13
|
-
<div>
|
|
14
|
-
<span data-testid="mouse-down-state">{mouseDownActive ? "active" : "inactive"}</span>
|
|
15
|
-
<button onClick={handleMouseDown} data-testid="trigger-button">
|
|
16
|
-
Trigger Mouse Down
|
|
17
|
-
</button>
|
|
18
|
-
</div>
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
describe("useDragHandle Hook", () => {
|
|
23
|
-
beforeEach(() => {
|
|
24
|
-
// Clean up any event listeners
|
|
25
|
-
vi.clearAllMocks();
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
afterEach(() => {
|
|
29
|
-
// Clean up global event listeners
|
|
30
|
-
const events = ["mouseup"];
|
|
31
|
-
events.forEach((event) => {
|
|
32
|
-
window.removeEventListener(event, () => {});
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it("should initialize with mouseDownActive as false", () => {
|
|
37
|
-
render(<TestDragHandleComponent />);
|
|
38
|
-
|
|
39
|
-
const stateElement = screen.getByTestId("mouse-down-state");
|
|
40
|
-
expect(stateElement).toHaveTextContent("inactive");
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("should set mouseDownActive to true when handleMouseDown is called", async () => {
|
|
44
|
-
const user = userEvent.setup();
|
|
45
|
-
render(<TestDragHandleComponent />);
|
|
46
|
-
|
|
47
|
-
const triggerButton = screen.getByTestId("trigger-button");
|
|
48
|
-
const stateElement = screen.getByTestId("mouse-down-state");
|
|
49
|
-
|
|
50
|
-
expect(stateElement).toHaveTextContent("inactive");
|
|
51
|
-
|
|
52
|
-
await user.click(triggerButton);
|
|
53
|
-
|
|
54
|
-
expect(stateElement).toHaveTextContent("active");
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it("should reset mouseDownActive to false when mouse up event occurs", async () => {
|
|
58
|
-
const user = userEvent.setup();
|
|
59
|
-
render(<TestDragHandleComponent />);
|
|
60
|
-
|
|
61
|
-
const triggerButton = screen.getByTestId("trigger-button");
|
|
62
|
-
const stateElement = screen.getByTestId("mouse-down-state");
|
|
63
|
-
|
|
64
|
-
// Trigger mouse down
|
|
65
|
-
await user.click(triggerButton);
|
|
66
|
-
expect(stateElement).toHaveTextContent("active");
|
|
67
|
-
|
|
68
|
-
// Simulate mouse up on window
|
|
69
|
-
fireEvent.mouseUp(window);
|
|
70
|
-
|
|
71
|
-
expect(stateElement).toHaveTextContent("inactive");
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it("should add and remove mouseup event listener correctly", async () => {
|
|
75
|
-
const addEventListenerSpy = vi.spyOn(window, "addEventListener");
|
|
76
|
-
const removeEventListenerSpy = vi.spyOn(window, "removeEventListener");
|
|
77
|
-
|
|
78
|
-
const user = userEvent.setup();
|
|
79
|
-
render(<TestDragHandleComponent />);
|
|
80
|
-
|
|
81
|
-
const triggerButton = screen.getByTestId("trigger-button");
|
|
82
|
-
|
|
83
|
-
await user.click(triggerButton);
|
|
84
|
-
|
|
85
|
-
expect(addEventListenerSpy).toHaveBeenCalledWith("mouseup", expect.any(Function), {
|
|
86
|
-
once: true,
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
// Trigger mouseup to remove listener
|
|
90
|
-
fireEvent.mouseUp(window);
|
|
91
|
-
|
|
92
|
-
addEventListenerSpy.mockRestore();
|
|
93
|
-
removeEventListenerSpy.mockRestore();
|
|
94
|
-
});
|
|
95
|
-
});
|
|
6
|
+
import { TableColumnDragHandle } from "./use-drag-handle.hook";
|
|
96
7
|
|
|
97
8
|
describe("TableColumnDragHandle Component", () => {
|
|
98
9
|
const defaultProps = {
|
|
99
|
-
onMouseDown: vi.fn(),
|
|
100
10
|
overlayActive: false,
|
|
101
11
|
isFirstColumn: false,
|
|
102
12
|
isLastColumn: false,
|
|
@@ -197,81 +107,31 @@ describe("TableColumnDragHandle Component", () => {
|
|
|
197
107
|
});
|
|
198
108
|
|
|
199
109
|
describe("Event Handling", () => {
|
|
200
|
-
it("should
|
|
201
|
-
const
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
const dragHandle = screen.getByRole("button");
|
|
207
|
-
await user.click(dragHandle);
|
|
208
|
-
|
|
209
|
-
expect(onMouseDownMock).toHaveBeenCalledTimes(1);
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
it("should call onMouseDown when mouse down event is triggered", () => {
|
|
213
|
-
const onMouseDownMock = vi.fn();
|
|
214
|
-
|
|
215
|
-
render(<TableColumnDragHandle {...defaultProps} onMouseDown={onMouseDownMock} />);
|
|
216
|
-
|
|
217
|
-
const dragHandle = screen.getByRole("button");
|
|
218
|
-
fireEvent.mouseDown(dragHandle);
|
|
110
|
+
it("should pass through dragListeners props", () => {
|
|
111
|
+
const mockListener = vi.fn();
|
|
112
|
+
const dragListeners = {
|
|
113
|
+
onMouseDown: mockListener,
|
|
114
|
+
onTouchStart: mockListener,
|
|
115
|
+
};
|
|
219
116
|
|
|
220
|
-
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
it("should call onMouseDown when Enter key is pressed", async () => {
|
|
224
|
-
const user = userEvent.setup();
|
|
225
|
-
const onMouseDownMock = vi.fn();
|
|
226
|
-
|
|
227
|
-
render(<TableColumnDragHandle {...defaultProps} onMouseDown={onMouseDownMock} />);
|
|
228
|
-
|
|
229
|
-
const dragHandle = screen.getByRole("button");
|
|
230
|
-
dragHandle.focus();
|
|
231
|
-
await user.keyboard("{Enter}");
|
|
232
|
-
|
|
233
|
-
expect(onMouseDownMock).toHaveBeenCalledTimes(1);
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
it("should call onMouseDown when Space key is pressed", async () => {
|
|
237
|
-
const user = userEvent.setup();
|
|
238
|
-
const onMouseDownMock = vi.fn();
|
|
239
|
-
|
|
240
|
-
render(<TableColumnDragHandle {...defaultProps} onMouseDown={onMouseDownMock} />);
|
|
117
|
+
render(<TableColumnDragHandle {...defaultProps} {...dragListeners} />);
|
|
241
118
|
|
|
242
119
|
const dragHandle = screen.getByRole("button");
|
|
243
|
-
dragHandle.focus();
|
|
244
|
-
await user.keyboard(" ");
|
|
245
|
-
|
|
246
|
-
expect(onMouseDownMock).toHaveBeenCalledTimes(1);
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
it("should not call onMouseDown for other key presses", async () => {
|
|
250
|
-
const user = userEvent.setup();
|
|
251
|
-
const onMouseDownMock = vi.fn();
|
|
252
120
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const dragHandle = screen.getByRole("button");
|
|
256
|
-
dragHandle.focus();
|
|
257
|
-
await user.keyboard("{Escape}");
|
|
258
|
-
|
|
259
|
-
expect(onMouseDownMock).not.toHaveBeenCalled();
|
|
121
|
+
// Verify the listeners are attached
|
|
122
|
+
expect(dragHandle).toHaveAttribute("role", "button");
|
|
260
123
|
});
|
|
261
124
|
|
|
262
|
-
it("should
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
await user.click(dragHandle);
|
|
273
|
-
|
|
274
|
-
expect(onMouseDownMock).toHaveBeenCalledTimes(3);
|
|
125
|
+
it("should not throw when receiving additional props", () => {
|
|
126
|
+
expect(() => {
|
|
127
|
+
render(
|
|
128
|
+
<TableColumnDragHandle
|
|
129
|
+
{...defaultProps}
|
|
130
|
+
data-testid="custom-test-id"
|
|
131
|
+
customProp="value"
|
|
132
|
+
/>
|
|
133
|
+
);
|
|
134
|
+
}).not.toThrow();
|
|
275
135
|
});
|
|
276
136
|
});
|
|
277
137
|
|
|
@@ -337,19 +197,6 @@ describe("TableColumnDragHandle Component", () => {
|
|
|
337
197
|
});
|
|
338
198
|
|
|
339
199
|
describe("Props Validation", () => {
|
|
340
|
-
it("should handle undefined onMouseDown gracefully", () => {
|
|
341
|
-
expect(() => {
|
|
342
|
-
render(
|
|
343
|
-
<TableColumnDragHandle
|
|
344
|
-
{...defaultProps}
|
|
345
|
-
onMouseDown={
|
|
346
|
-
undefined as unknown as Parameters<typeof TableColumnDragHandle>[0]["onMouseDown"]
|
|
347
|
-
}
|
|
348
|
-
/>
|
|
349
|
-
);
|
|
350
|
-
}).not.toThrow();
|
|
351
|
-
});
|
|
352
|
-
|
|
353
200
|
it("should handle empty aria label", () => {
|
|
354
201
|
render(<TableColumnDragHandle {...defaultProps} columnDragAriaLabel="" />);
|
|
355
202
|
|
|
@@ -1,83 +1,44 @@
|
|
|
1
|
-
import {
|
|
2
|
-
closestCorners,
|
|
3
|
-
type DroppableContainer,
|
|
4
|
-
getFirstCollision,
|
|
5
|
-
KeyboardCode,
|
|
6
|
-
type KeyboardCoordinateGetter,
|
|
7
|
-
} from "@dnd-kit/core";
|
|
1
|
+
import { KeyboardCode, type KeyboardCoordinateGetter } from "@dnd-kit/core";
|
|
8
2
|
|
|
9
3
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
4
|
+
* Simple keyboard coordinate handler that moves the drag overlay
|
|
5
|
+
* left or right incrementally and lets collision detection determine
|
|
6
|
+
* which column we're over.
|
|
12
7
|
*/
|
|
13
8
|
export const enhancedColumnKeyboardCoordinates: KeyboardCoordinateGetter = (
|
|
14
9
|
event,
|
|
15
|
-
{ context: { active,
|
|
10
|
+
{ currentCoordinates, context: { active, droppableRects } }
|
|
16
11
|
) => {
|
|
17
|
-
if (!active || !
|
|
12
|
+
if (!active || !currentCoordinates) {
|
|
18
13
|
return;
|
|
19
14
|
}
|
|
20
15
|
|
|
21
|
-
// Only handle left/right movements
|
|
22
|
-
if (event.code
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const filteredContainers: DroppableContainer[] = [];
|
|
26
|
-
|
|
27
|
-
droppableContainers.getEnabled().forEach((container) => {
|
|
28
|
-
if (container?.disabled) return;
|
|
29
|
-
|
|
30
|
-
const rect = droppableRects.get(container.id);
|
|
31
|
-
if (!rect) return;
|
|
32
|
-
|
|
33
|
-
// Filter based on direction
|
|
34
|
-
if (event.code === KeyboardCode.Right && collisionRect.left < rect.left) {
|
|
35
|
-
filteredContainers.push(container);
|
|
36
|
-
} else if (event.code === KeyboardCode.Left && collisionRect.left > rect.left) {
|
|
37
|
-
filteredContainers.push(container);
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
// Sort containers by distance (nearest first) for left movement
|
|
42
|
-
if (event.code === KeyboardCode.Left && filteredContainers.length > 0) {
|
|
43
|
-
filteredContainers.sort((a, b) => {
|
|
44
|
-
const rectA = droppableRects.get(a.id);
|
|
45
|
-
const rectB = droppableRects.get(b.id);
|
|
46
|
-
if (!rectA || !rectB) return 0;
|
|
47
|
-
|
|
48
|
-
// Sort by closest to furthest from the current position (for left movement)
|
|
49
|
-
return rectB.left - rectA.left;
|
|
50
|
-
});
|
|
51
|
-
}
|
|
16
|
+
// Only handle left/right movements
|
|
17
|
+
if (event.code !== KeyboardCode.Right && event.code !== KeyboardCode.Left) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
52
20
|
|
|
53
|
-
|
|
54
|
-
const collisions = closestCorners({
|
|
55
|
-
active,
|
|
56
|
-
collisionRect,
|
|
57
|
-
droppableRects,
|
|
58
|
-
droppableContainers: filteredContainers,
|
|
59
|
-
pointerCoordinates: null,
|
|
60
|
-
});
|
|
21
|
+
event.preventDefault();
|
|
61
22
|
|
|
62
|
-
|
|
23
|
+
// Get the active column's width to determine appropriate step size
|
|
24
|
+
const activeRect = droppableRects.get(active.id);
|
|
25
|
+
if (!activeRect) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
63
28
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
targetId = collisions[1].id;
|
|
67
|
-
}
|
|
29
|
+
// Move by half the width of the active column or max 100px
|
|
30
|
+
const stepSize = Math.min(activeRect.width * 0.5, 100);
|
|
68
31
|
|
|
69
|
-
|
|
70
|
-
const targetRect = droppableRects.get(targetId);
|
|
32
|
+
let newX: number;
|
|
71
33
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
}
|
|
34
|
+
if (event.code === KeyboardCode.Right) {
|
|
35
|
+
newX = currentCoordinates.x + stepSize;
|
|
36
|
+
} else {
|
|
37
|
+
newX = currentCoordinates.x - stepSize;
|
|
79
38
|
}
|
|
80
39
|
|
|
81
|
-
|
|
82
|
-
|
|
40
|
+
return {
|
|
41
|
+
x: newX,
|
|
42
|
+
y: currentCoordinates.y,
|
|
43
|
+
};
|
|
83
44
|
};
|