@marimo-team/islands 0.24.1-dev79 → 0.24.1-dev80

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 (25) hide show
  1. package/dist/{chat-ui-DM12Y9is.js → chat-ui-Dcb8pQhM.js} +8 -8
  2. package/dist/click-outside-container-D_URcm3D.js +167 -0
  3. package/dist/{common-DuuFqqGa.js → common-C8LWIius.js} +571 -571
  4. package/dist/{data-grid-overlay-editor-D-p0q7YM.js → data-grid-overlay-editor-DZM4BAaI.js} +49 -48
  5. package/dist/{click-outside-container-CVJZE0x6.js → data-grid-types-BnZwWG8z.js} +150 -312
  6. package/dist/{dropdown-menu-C8N-kZzD.js → dropdown-menu-B39I9ARA.js} +20 -31
  7. package/dist/{glide-data-editor-64-L_DON.js → glide-data-editor-DEJqXbly.js} +2598 -2780
  8. package/dist/glide-utils-C0dRuWI_.js +201 -0
  9. package/dist/{html-to-image-B7RQgQQy.js → html-to-image-CvCnIqTI.js} +4476 -4465
  10. package/dist/main.js +5971 -5913
  11. package/dist/{process-output-BCqcNNll.js → process-output-C2joqP7d.js} +1 -1
  12. package/dist/{reveal-component-BDQwrD89.js → reveal-component-CdEcs6WV.js} +3 -3
  13. package/package.json +1 -1
  14. package/src/plugins/impl/DataEditorPlugin.tsx +38 -70
  15. package/src/plugins/impl/__tests__/DataEditorPlugin.test.ts +152 -1
  16. package/src/plugins/impl/data-editor/__tests__/data-utils.test.ts +73 -78
  17. package/src/plugins/impl/data-editor/__tests__/editor-state.test.ts +113 -0
  18. package/src/plugins/impl/data-editor/__tests__/glide-data-editor.test.tsx +49 -13
  19. package/src/plugins/impl/data-editor/__tests__/glide-utils.test.ts +44 -64
  20. package/src/plugins/impl/data-editor/data-utils.ts +76 -53
  21. package/src/plugins/impl/data-editor/editor-state.ts +108 -0
  22. package/src/plugins/impl/data-editor/glide-data-editor.tsx +40 -223
  23. package/src/plugins/impl/data-editor/glide-utils.ts +71 -31
  24. package/src/plugins/impl/data-editor/types.ts +30 -16
  25. package/dist/types-eyYyHvz7.js +0 -72
@@ -0,0 +1,113 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { describe, expect, it } from "vitest";
4
+ import type { FieldTypes } from "@/components/data-table/types";
5
+ import { applyEditorEdits } from "../editor-state";
6
+
7
+ describe("applyEditorEdits", () => {
8
+ it("replays edits against an empty table", () => {
9
+ const result = applyEditorEdits(
10
+ { data: [], columnFields: new Map([["value", "unknown"]]) },
11
+ [{ rowIdx: 0, columnId: "value", value: "first" }],
12
+ );
13
+
14
+ expect(result.data).toEqual([{ value: "first" }]);
15
+ });
16
+
17
+ it("replays an append after deleting every row", () => {
18
+ const result = applyEditorEdits(
19
+ {
20
+ data: [{ A: 1, B: "a" }],
21
+ columnFields: new Map([
22
+ ["A", "number"],
23
+ ["B", "string"],
24
+ ]) as FieldTypes,
25
+ },
26
+ [
27
+ { rowIdx: 0, type: "remove" },
28
+ { rowIdx: 0, columnId: "A", value: 2 },
29
+ { rowIdx: 0, columnId: "B", value: "b" },
30
+ ],
31
+ );
32
+
33
+ expect(result.data).toEqual([{ A: 2, B: "b" }]);
34
+ });
35
+
36
+ it("uses the evolving column order", () => {
37
+ const result = applyEditorEdits(
38
+ {
39
+ data: [{ A: "a", B: "b" }],
40
+ columnFields: new Map([
41
+ ["A", "string"],
42
+ ["B", "string"],
43
+ ]) as FieldTypes,
44
+ },
45
+ [
46
+ { columnIdx: 0, type: "remove" },
47
+ { columnIdx: 0, type: "rename", newName: "D" },
48
+ ],
49
+ );
50
+
51
+ expect(result.data).toEqual([{ D: "b" }]);
52
+ expect([...result.columnFields]).toEqual([["D", "string"]]);
53
+ });
54
+
55
+ it("preserves inserted column order through later edits", () => {
56
+ const result = applyEditorEdits(
57
+ {
58
+ data: [{ A: "a", C: "c" }],
59
+ columnFields: new Map([
60
+ ["A", "string"],
61
+ ["C", "string"],
62
+ ]) as FieldTypes,
63
+ },
64
+ [
65
+ {
66
+ columnIdx: 1,
67
+ type: "insert",
68
+ newName: "B",
69
+ dataType: "boolean",
70
+ },
71
+ { columnIdx: 1, type: "rename", newName: "D" },
72
+ { columnIdx: 2, type: "remove" },
73
+ ],
74
+ );
75
+
76
+ expect(result.data).toEqual([{ A: "a", D: "" }]);
77
+ expect(Object.keys(result.data[0])).toEqual(["A", "D"]);
78
+ expect([...result.columnFields]).toEqual([
79
+ ["A", "string"],
80
+ ["D", "boolean"],
81
+ ]);
82
+ });
83
+
84
+ it("does not mutate the previous state", () => {
85
+ const state = {
86
+ data: [{ A: "before" }],
87
+ columnFields: new Map([["A", "string"]]) as FieldTypes,
88
+ };
89
+
90
+ const result = applyEditorEdits(state, [
91
+ { rowIdx: 0, columnId: "A", value: "after" },
92
+ { columnIdx: 1, type: "insert", newName: "B" },
93
+ ]);
94
+
95
+ expect(state.data).toEqual([{ A: "before" }]);
96
+ expect([...state.columnFields]).toEqual([["A", "string"]]);
97
+ expect(result.data).toEqual([{ A: "after", B: "" }]);
98
+ });
99
+
100
+ it("ignores negative row indices", () => {
101
+ const state = {
102
+ data: [{ A: "unchanged" }],
103
+ columnFields: new Map([["A", "string"]]) as FieldTypes,
104
+ };
105
+
106
+ expect(
107
+ applyEditorEdits(state, [
108
+ { rowIdx: -1, columnId: "A", value: "invalid" },
109
+ { rowIdx: -1, type: "remove" },
110
+ ]).data,
111
+ ).toEqual(state.data);
112
+ });
113
+ });
@@ -6,18 +6,31 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
6
6
  import { TooltipProvider } from "@/components/ui/tooltip";
7
7
  import { GlideDataEditor } from "../glide-data-editor";
8
8
 
9
- const capturedPortalRef = vi.hoisted(() => ({
9
+ const capturedDataEditor = vi.hoisted(() => ({
10
10
  ref: undefined as React.RefObject<HTMLElement> | undefined,
11
+ onCellEdited: undefined as
12
+ | ((cell: [number, number], value: { data: unknown }) => void)
13
+ | undefined,
14
+ onRowAppended: undefined as (() => void) | undefined,
11
15
  }));
12
16
 
13
17
  vi.mock("@glideapps/glide-data-grid", async () => {
14
18
  const React = await import("react");
15
19
  return {
16
20
  default: React.forwardRef(function MockDataEditor(
17
- props: { portalElementRef?: React.RefObject<HTMLElement> },
21
+ props: {
22
+ portalElementRef?: React.RefObject<HTMLElement>;
23
+ onCellEdited?: (
24
+ cell: [number, number],
25
+ value: { data: unknown },
26
+ ) => void;
27
+ onRowAppended?: () => void;
28
+ },
18
29
  _ref: React.Ref<HTMLDivElement>,
19
30
  ) {
20
- capturedPortalRef.ref = props.portalElementRef;
31
+ capturedDataEditor.ref = props.portalElementRef;
32
+ capturedDataEditor.onCellEdited = props.onCellEdited;
33
+ capturedDataEditor.onRowAppended = props.onRowAppended;
21
34
  return <div data-testid="mock-data-editor" />;
22
35
  }),
23
36
  CompactSelection: {
@@ -40,23 +53,16 @@ vi.mock("@/theme/useTheme", () => ({
40
53
 
41
54
  const editorProps = {
42
55
  data: [{ name: "alice" }],
43
- setData: vi.fn(),
44
56
  columnFields: new Map([["name", "string"]]) as Map<string, "string">,
45
- setColumnFields: vi.fn(),
46
57
  editableColumns: "all" as const,
47
- edits: [],
48
58
  onAddEdits: vi.fn(),
49
- onAddRows: vi.fn(),
50
- onDeleteRows: vi.fn(),
51
- onRenameColumn: vi.fn(),
52
- onDeleteColumn: vi.fn(),
53
- onAddColumn: vi.fn(),
54
59
  };
55
60
 
56
61
  describe("GlideDataEditor portal", () => {
57
62
  let fullscreenElement: Element | null;
58
63
 
59
64
  beforeEach(() => {
65
+ vi.clearAllMocks();
60
66
  fullscreenElement = null;
61
67
  Object.defineProperty(document, "fullscreenElement", {
62
68
  get: () => fullscreenElement,
@@ -79,12 +85,42 @@ describe("GlideDataEditor portal", () => {
79
85
  const portal = screen.getByTestId("glide-data-editor-portal");
80
86
  expect(portal.parentElement).toBe(document.body);
81
87
  expect(container.contains(portal)).toBe(false);
82
- expect(capturedPortalRef.ref).toBeDefined();
88
+ expect(capturedDataEditor.ref).toBeDefined();
83
89
  await waitFor(() => {
84
- expect(capturedPortalRef.ref?.current).toBe(portal);
90
+ expect(capturedDataEditor.ref?.current).toBe(portal);
85
91
  });
86
92
  });
87
93
 
94
+ it("emits cell edits", () => {
95
+ const onAddEdits = vi.fn();
96
+ render(
97
+ <TooltipProvider>
98
+ <GlideDataEditor {...editorProps} onAddEdits={onAddEdits} />
99
+ </TooltipProvider>,
100
+ );
101
+
102
+ act(() => capturedDataEditor.onCellEdited?.([0, 0], { data: "bob" }));
103
+
104
+ expect(onAddEdits).toHaveBeenCalledWith([
105
+ { rowIdx: 0, columnId: "name", value: "bob" },
106
+ ]);
107
+ });
108
+
109
+ it("emits one positional edit per cell when appending a row", () => {
110
+ const onAddEdits = vi.fn();
111
+ render(
112
+ <TooltipProvider>
113
+ <GlideDataEditor {...editorProps} onAddEdits={onAddEdits} />
114
+ </TooltipProvider>,
115
+ );
116
+
117
+ act(() => capturedDataEditor.onRowAppended?.());
118
+
119
+ expect(onAddEdits).toHaveBeenCalledWith([
120
+ { rowIdx: 1, columnId: "name", value: "" },
121
+ ]);
122
+ });
123
+
88
124
  it("mounts into the fullscreen element when fullscreen is already active", () => {
89
125
  const fullscreenContainer = document.createElement("div");
90
126
  document.body.appendChild(fullscreenContainer);
@@ -3,7 +3,7 @@
3
3
  import type { GridSelection } from "@glideapps/glide-data-grid";
4
4
  import { CompactSelection, GridCellKind } from "@glideapps/glide-data-grid";
5
5
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
6
- import { pasteCells } from "../glide-utils";
6
+ import { isValidCellValue, pasteCells } from "../glide-utils";
7
7
  import type { ModifiedGridColumn } from "../types";
8
8
 
9
9
  // Mock navigator.clipboard
@@ -16,6 +16,26 @@ Object.defineProperty(navigator, "clipboard", {
16
16
  writable: true,
17
17
  });
18
18
 
19
+ describe("isValidCellValue", () => {
20
+ it("accepts only finite numbers", () => {
21
+ expect(isValidCellValue("number", 3.5)).toBe(true);
22
+ expect(isValidCellValue("number", Number.POSITIVE_INFINITY)).toBe(false);
23
+ expect(isValidCellValue("number", Number.NEGATIVE_INFINITY)).toBe(false);
24
+ expect(isValidCellValue("number", "Infinity")).toBe(false);
25
+ expect(isValidCellValue("number", Number.NaN)).toBe(false);
26
+ });
27
+
28
+ it("preserves integer precision", () => {
29
+ expect(isValidCellValue("integer", 3)).toBe(true);
30
+ expect(isValidCellValue("integer", 3.5)).toBe(false);
31
+ expect(isValidCellValue("integer", "9007199254740993")).toBe(true);
32
+ expect(isValidCellValue("integer", "9007199254740993.0")).toBe(true);
33
+ expect(isValidCellValue("integer", "9007199254740993.1")).toBe(false);
34
+ expect(isValidCellValue("integer", "1.0000000000000001")).toBe(false);
35
+ expect(isValidCellValue("integer", "1e-324")).toBe(false);
36
+ });
37
+ });
38
+
19
39
  describe("pasteCells", () => {
20
40
  beforeEach(() => {
21
41
  vi.clearAllMocks();
@@ -76,13 +96,11 @@ describe("pasteCells", () => {
76
96
  it("should handle empty clipboard text", async () => {
77
97
  mockClipboard.readText.mockResolvedValue("");
78
98
 
79
- const mockSetLocalData = vi.fn();
80
99
  const mockOnAddEdits = vi.fn();
81
100
 
82
101
  pasteCells({
83
102
  selection: createMockSelection(0, 0),
84
103
  data: createMockData(),
85
- setData: mockSetLocalData,
86
104
  columns: createMockColumns(),
87
105
  editableColumns: "all",
88
106
  onAddEdits: mockOnAddEdits,
@@ -91,20 +109,17 @@ describe("pasteCells", () => {
91
109
  // Wait for the async operation
92
110
  await vi.waitFor(() => {
93
111
  expect(mockOnAddEdits).not.toHaveBeenCalled();
94
- expect(mockSetLocalData).not.toHaveBeenCalled();
95
112
  });
96
113
  });
97
114
 
98
115
  it("should handle whitespace-only clipboard text", async () => {
99
116
  mockClipboard.readText.mockResolvedValue(" \n\t ");
100
117
 
101
- const mockSetLocalData = vi.fn();
102
118
  const mockOnAddEdits = vi.fn();
103
119
 
104
120
  pasteCells({
105
121
  selection: createMockSelection(0, 0),
106
122
  data: createMockData(),
107
- setData: mockSetLocalData,
108
123
  columns: createMockColumns(),
109
124
  editableColumns: "all",
110
125
  onAddEdits: mockOnAddEdits,
@@ -112,20 +127,17 @@ describe("pasteCells", () => {
112
127
 
113
128
  await vi.waitFor(() => {
114
129
  expect(mockOnAddEdits).not.toHaveBeenCalled();
115
- expect(mockSetLocalData).not.toHaveBeenCalled();
116
130
  });
117
131
  });
118
132
 
119
133
  it("should paste string data correctly", async () => {
120
134
  mockClipboard.readText.mockResolvedValue("David\t40\ttrue");
121
135
 
122
- const mockSetLocalData = vi.fn();
123
136
  const mockOnAddEdits = vi.fn();
124
137
 
125
138
  pasteCells({
126
139
  selection: createMockSelection(0, 0),
127
140
  data: createMockData(),
128
- setData: mockSetLocalData,
129
141
  columns: createMockColumns(),
130
142
  editableColumns: "all",
131
143
  onAddEdits: mockOnAddEdits,
@@ -140,16 +152,14 @@ describe("pasteCells", () => {
140
152
  });
141
153
  });
142
154
 
143
- it("should convert number values correctly", async () => {
155
+ it("should skip fractional values for integer columns", async () => {
144
156
  mockClipboard.readText.mockResolvedValue("Eve\t25.5\tfalse");
145
157
 
146
- const mockSetLocalData = vi.fn();
147
158
  const mockOnAddEdits = vi.fn();
148
159
 
149
160
  pasteCells({
150
161
  selection: createMockSelection(0, 0),
151
162
  data: createMockData(),
152
- setData: mockSetLocalData,
153
163
  columns: createMockColumns(),
154
164
  editableColumns: "all",
155
165
  onAddEdits: mockOnAddEdits,
@@ -158,7 +168,6 @@ describe("pasteCells", () => {
158
168
  await vi.waitFor(() => {
159
169
  expect(mockOnAddEdits).toHaveBeenCalledWith([
160
170
  { rowIdx: 0, columnId: "name", value: "Eve" },
161
- { rowIdx: 0, columnId: "age", value: 25.5 },
162
171
  { rowIdx: 0, columnId: "active", value: false },
163
172
  ]);
164
173
  });
@@ -167,13 +176,11 @@ describe("pasteCells", () => {
167
176
  it("should handle boolean conversion with different values", async () => {
168
177
  mockClipboard.readText.mockResolvedValue("Frank\t30\t1");
169
178
 
170
- const mockSetLocalData = vi.fn();
171
179
  const mockOnAddEdits = vi.fn();
172
180
 
173
181
  pasteCells({
174
182
  selection: createMockSelection(0, 0),
175
183
  data: createMockData(),
176
- setData: mockSetLocalData,
177
184
  columns: createMockColumns(),
178
185
  editableColumns: "all",
179
186
  onAddEdits: mockOnAddEdits,
@@ -191,13 +198,11 @@ describe("pasteCells", () => {
191
198
  it("should skip invalid number values", async () => {
192
199
  mockClipboard.readText.mockResolvedValue("Grace\tinvalid\tfalse");
193
200
 
194
- const mockSetLocalData = vi.fn();
195
201
  const mockOnAddEdits = vi.fn();
196
202
 
197
203
  pasteCells({
198
204
  selection: createMockSelection(0, 0),
199
205
  data: createMockData(),
200
- setData: mockSetLocalData,
201
206
  columns: createMockColumns(),
202
207
  editableColumns: "all",
203
208
  onAddEdits: mockOnAddEdits,
@@ -211,16 +216,36 @@ describe("pasteCells", () => {
211
216
  });
212
217
  });
213
218
 
219
+ it("should preserve unsafe integer precision", async () => {
220
+ mockClipboard.readText.mockResolvedValue("Grace\t9007199254740993\tfalse");
221
+
222
+ const mockOnAddEdits = vi.fn();
223
+
224
+ pasteCells({
225
+ selection: createMockSelection(0, 0),
226
+ data: createMockData(),
227
+ columns: createMockColumns(),
228
+ editableColumns: "all",
229
+ onAddEdits: mockOnAddEdits,
230
+ });
231
+
232
+ await vi.waitFor(() => {
233
+ expect(mockOnAddEdits).toHaveBeenCalledWith([
234
+ { rowIdx: 0, columnId: "name", value: "Grace" },
235
+ { rowIdx: 0, columnId: "age", value: "9007199254740993" },
236
+ { rowIdx: 0, columnId: "active", value: false },
237
+ ]);
238
+ });
239
+ });
240
+
214
241
  it("should handle multiple rows", async () => {
215
242
  mockClipboard.readText.mockResolvedValue("Hank\t40\ttrue\nIvy\t35\tfalse");
216
243
 
217
- const mockSetLocalData = vi.fn();
218
244
  const mockOnAddEdits = vi.fn();
219
245
 
220
246
  pasteCells({
221
247
  selection: createMockSelection(0, 0),
222
248
  data: createMockData(),
223
- setData: mockSetLocalData,
224
249
  columns: createMockColumns(),
225
250
  editableColumns: "all",
226
251
  onAddEdits: mockOnAddEdits,
@@ -243,13 +268,11 @@ describe("pasteCells", () => {
243
268
  "Jack\t45\ttrue\nKate\t50\tfalse\nLiam\t55\ttrue",
244
269
  );
245
270
 
246
- const mockSetLocalData = vi.fn();
247
271
  const mockOnAddEdits = vi.fn();
248
272
 
249
273
  pasteCells({
250
274
  selection: createMockSelection(0, 0),
251
275
  data: createMockData(), // Only 3 rows
252
- setData: mockSetLocalData,
253
276
  columns: createMockColumns(),
254
277
  editableColumns: "all",
255
278
  onAddEdits: mockOnAddEdits,
@@ -273,13 +296,11 @@ describe("pasteCells", () => {
273
296
  it("should respect column bounds - not exceed column count", async () => {
274
297
  mockClipboard.readText.mockResolvedValue("Mia\t30\ttrue\textra");
275
298
 
276
- const mockSetLocalData = vi.fn();
277
299
  const mockOnAddEdits = vi.fn();
278
300
 
279
301
  pasteCells({
280
302
  selection: createMockSelection(0, 0),
281
303
  data: createMockData(),
282
- setData: mockSetLocalData,
283
304
  columns: createMockColumns(), // Only 3 columns
284
305
  editableColumns: "all",
285
306
  onAddEdits: mockOnAddEdits,
@@ -297,13 +318,11 @@ describe("pasteCells", () => {
297
318
  it("should handle starting position offset", async () => {
298
319
  mockClipboard.readText.mockResolvedValue("Noah\t25\ttrue");
299
320
 
300
- const mockSetLocalData = vi.fn();
301
321
  const mockOnAddEdits = vi.fn();
302
322
 
303
323
  pasteCells({
304
324
  selection: createMockSelection(1, 1), // Start at column 1, row 1
305
325
  data: createMockData(),
306
- setData: mockSetLocalData,
307
326
  columns: createMockColumns(),
308
327
  editableColumns: "all",
309
328
  onAddEdits: mockOnAddEdits,
@@ -317,7 +336,6 @@ describe("pasteCells", () => {
317
336
  });
318
337
 
319
338
  it("should handle no selection", async () => {
320
- const mockSetLocalData = vi.fn();
321
339
  const mockOnAddEdits = vi.fn();
322
340
 
323
341
  pasteCells({
@@ -327,7 +345,6 @@ describe("pasteCells", () => {
327
345
  columns: CompactSelection.empty(),
328
346
  },
329
347
  data: createMockData(),
330
- setData: mockSetLocalData,
331
348
  columns: createMockColumns(),
332
349
  editableColumns: "all",
333
350
  onAddEdits: mockOnAddEdits,
@@ -335,7 +352,6 @@ describe("pasteCells", () => {
335
352
 
336
353
  await vi.waitFor(() => {
337
354
  expect(mockOnAddEdits).not.toHaveBeenCalled();
338
- expect(mockSetLocalData).not.toHaveBeenCalled();
339
355
  });
340
356
  });
341
357
 
@@ -344,7 +360,6 @@ describe("pasteCells", () => {
344
360
  new Error("Clipboard access denied"),
345
361
  );
346
362
 
347
- const mockSetLocalData = vi.fn();
348
363
  const mockOnAddEdits = vi.fn();
349
364
  const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {
350
365
  // Do nothing
@@ -353,7 +368,6 @@ describe("pasteCells", () => {
353
368
  pasteCells({
354
369
  selection: createMockSelection(0, 0),
355
370
  data: createMockData(),
356
- setData: mockSetLocalData,
357
371
  columns: createMockColumns(),
358
372
  editableColumns: "all",
359
373
  onAddEdits: mockOnAddEdits,
@@ -361,53 +375,21 @@ describe("pasteCells", () => {
361
375
 
362
376
  await vi.waitFor(() => {
363
377
  expect(mockOnAddEdits).not.toHaveBeenCalled();
364
- expect(mockSetLocalData).not.toHaveBeenCalled();
365
378
  });
366
379
 
367
380
  consoleSpy.mockRestore();
368
381
  });
369
382
 
370
- it("should update local data when edits are applied", async () => {
371
- mockClipboard.readText.mockResolvedValue("Olivia\t28\ttrue");
372
-
373
- const mockSetLocalData = vi.fn();
374
- const mockOnAddEdits = vi.fn();
375
-
376
- pasteCells({
377
- selection: createMockSelection(0, 0),
378
- data: createMockData(),
379
- setData: mockSetLocalData,
380
- columns: createMockColumns(),
381
- editableColumns: "all",
382
- onAddEdits: mockOnAddEdits,
383
- });
384
-
385
- await vi.waitFor(() => {
386
- expect(mockSetLocalData).toHaveBeenCalled();
387
-
388
- // Verify the updater function is called
389
- const updater = mockSetLocalData.mock.calls[0][0];
390
- const originalData = createMockData();
391
- const updatedData = updater(originalData);
392
-
393
- expect(updatedData[0].name).toBe("Olivia");
394
- expect(updatedData[0].age).toBe(28);
395
- expect(updatedData[0].active).toBe(true);
396
- });
397
- });
398
-
399
383
  it("should handle empty rows in clipboard data", async () => {
400
384
  mockClipboard.readText.mockResolvedValue(
401
385
  "Paul\t30\ttrue\n\nRachel\t25\tfalse",
402
386
  );
403
387
 
404
- const mockSetLocalData = vi.fn();
405
388
  const mockOnAddEdits = vi.fn();
406
389
 
407
390
  pasteCells({
408
391
  selection: createMockSelection(0, 0),
409
392
  data: createMockData(),
410
- setData: mockSetLocalData,
411
393
  columns: createMockColumns(),
412
394
  editableColumns: "all",
413
395
  onAddEdits: mockOnAddEdits,
@@ -430,13 +412,11 @@ describe("pasteCells", () => {
430
412
  "Olivia\t28\ttrue\nWilliams\t35\tfalse",
431
413
  );
432
414
 
433
- const mockSetLocalData = vi.fn();
434
415
  const mockOnAddEdits = vi.fn();
435
416
 
436
417
  pasteCells({
437
418
  selection: createMockSelection(0, 0),
438
419
  data: createMockData(),
439
- setData: mockSetLocalData,
440
420
  columns: createMockColumns(),
441
421
  editableColumns: ["age"],
442
422
  onAddEdits: mockOnAddEdits,