@gradio/dataframe 0.21.7 → 0.23.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.
Files changed (128) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/Dataframe.test.ts +697 -32
  3. package/Index.svelte +107 -106
  4. package/dist/Index.svelte +107 -106
  5. package/dist/Index.svelte.d.ts +0 -1
  6. package/dist/shared/BooleanCell.svelte +12 -6
  7. package/dist/shared/BooleanCell.svelte.d.ts +4 -18
  8. package/dist/shared/CellMenu.svelte +74 -49
  9. package/dist/shared/CellMenu.svelte.d.ts +5 -19
  10. package/dist/shared/CellMenuButton.svelte +3 -3
  11. package/dist/shared/CellMenuButton.svelte.d.ts +4 -18
  12. package/dist/shared/CellMenuIcons.svelte +1 -1
  13. package/dist/shared/CellMenuIcons.svelte.d.ts +4 -18
  14. package/dist/shared/DataCell.svelte +189 -0
  15. package/dist/shared/DataCell.svelte.d.ts +44 -0
  16. package/dist/shared/EditableCell.svelte +88 -64
  17. package/dist/shared/EditableCell.svelte.d.ts +8 -26
  18. package/dist/shared/EmptyRowButton.svelte +2 -2
  19. package/dist/shared/EmptyRowButton.svelte.d.ts +4 -18
  20. package/dist/shared/FilterMenu.svelte +26 -18
  21. package/dist/shared/FilterMenu.svelte.d.ts +5 -21
  22. package/dist/shared/HeaderCell.svelte +218 -0
  23. package/dist/shared/HeaderCell.svelte.d.ts +30 -0
  24. package/dist/shared/RowNumber.svelte +4 -2
  25. package/dist/shared/RowNumber.svelte.d.ts +4 -18
  26. package/dist/shared/Table.svelte +1039 -947
  27. package/dist/shared/Table.svelte.d.ts +12 -30
  28. package/dist/shared/Toolbar.svelte +32 -22
  29. package/dist/shared/Toolbar.svelte.d.ts +6 -21
  30. package/dist/shared/column_measurement.svelte.d.ts +20 -0
  31. package/dist/shared/column_measurement.svelte.js +79 -0
  32. package/dist/shared/icons/FilterIcon.svelte +14 -6
  33. package/dist/shared/icons/FilterIcon.svelte.d.ts +14 -6
  34. package/dist/shared/icons/Padlock.svelte +6 -2
  35. package/dist/shared/icons/Padlock.svelte.d.ts +4 -25
  36. package/dist/shared/icons/SelectionButtons.svelte +23 -8
  37. package/dist/shared/icons/SelectionButtons.svelte.d.ts +4 -18
  38. package/dist/shared/icons/SortButtonDown.svelte +15 -4
  39. package/dist/shared/icons/SortButtonDown.svelte.d.ts +4 -25
  40. package/dist/shared/icons/SortButtonUp.svelte +15 -5
  41. package/dist/shared/icons/SortButtonUp.svelte.d.ts +4 -25
  42. package/dist/shared/icons/SortIcon.svelte +13 -7
  43. package/dist/shared/icons/SortIcon.svelte.d.ts +7 -21
  44. package/dist/shared/tanstack/index.d.ts +3 -0
  45. package/dist/shared/tanstack/index.js +3 -0
  46. package/dist/shared/tanstack/table.svelte.d.ts +26 -0
  47. package/dist/shared/tanstack/table.svelte.js +102 -0
  48. package/dist/shared/tanstack/virtual.svelte.d.ts +14 -0
  49. package/dist/shared/tanstack/virtual.svelte.js +76 -0
  50. package/dist/shared/types.d.ts +2 -0
  51. package/dist/shared/utils/filter.d.ts +5 -0
  52. package/dist/shared/utils/filter.js +62 -0
  53. package/dist/shared/utils/selection_utils.js +1 -1
  54. package/dist/shared/utils/table_utils.d.ts +1 -14
  55. package/dist/shared/utils/table_utils.js +0 -61
  56. package/dist/standalone/Index.svelte +71 -46
  57. package/dist/standalone/Index.svelte.d.ts +4 -23
  58. package/dist/types.d.ts +32 -0
  59. package/dist/types.js +1 -0
  60. package/package.json +12 -11
  61. package/shared/BooleanCell.svelte +12 -6
  62. package/shared/CellMenu.svelte +74 -49
  63. package/shared/CellMenuButton.svelte +3 -3
  64. package/shared/CellMenuIcons.svelte +1 -1
  65. package/shared/DataCell.svelte +189 -0
  66. package/shared/EditableCell.svelte +88 -64
  67. package/shared/EmptyRowButton.svelte +2 -2
  68. package/shared/FilterMenu.svelte +26 -18
  69. package/shared/HeaderCell.svelte +218 -0
  70. package/shared/RowNumber.svelte +4 -2
  71. package/shared/Table.svelte +1039 -947
  72. package/shared/Toolbar.svelte +32 -22
  73. package/shared/column_measurement.svelte.ts +103 -0
  74. package/shared/icons/FilterIcon.svelte +14 -6
  75. package/shared/icons/Padlock.svelte +6 -2
  76. package/shared/icons/SelectionButtons.svelte +23 -8
  77. package/shared/icons/SortButtonDown.svelte +15 -4
  78. package/shared/icons/SortButtonUp.svelte +15 -5
  79. package/shared/icons/SortIcon.svelte +13 -7
  80. package/shared/tanstack/index.ts +23 -0
  81. package/shared/tanstack/table.svelte.ts +117 -0
  82. package/shared/tanstack/virtual.svelte.ts +105 -0
  83. package/shared/types.ts +3 -0
  84. package/shared/utils/filter.ts +66 -0
  85. package/shared/utils/selection_utils.ts +1 -1
  86. package/shared/utils/table_utils.ts +1 -100
  87. package/standalone/Index.svelte +71 -46
  88. package/test/filter.test.ts +350 -0
  89. package/test/selection_utils.test.ts +416 -0
  90. package/test/table_utils.test.ts +45 -112
  91. package/types.ts +34 -0
  92. package/dist/shared/TableCell.svelte +0 -330
  93. package/dist/shared/TableCell.svelte.d.ts +0 -66
  94. package/dist/shared/TableHeader.svelte +0 -344
  95. package/dist/shared/TableHeader.svelte.d.ts +0 -61
  96. package/dist/shared/VirtualTable.svelte +0 -379
  97. package/dist/shared/VirtualTable.svelte.d.ts +0 -42
  98. package/dist/shared/context/dataframe_context.d.ts +0 -180
  99. package/dist/shared/context/dataframe_context.js +0 -412
  100. package/dist/shared/icons/SortArrowDown.svelte +0 -25
  101. package/dist/shared/icons/SortArrowDown.svelte.d.ts +0 -20
  102. package/dist/shared/icons/SortArrowUp.svelte +0 -25
  103. package/dist/shared/icons/SortArrowUp.svelte.d.ts +0 -20
  104. package/dist/shared/utils/data_processing.d.ts +0 -14
  105. package/dist/shared/utils/data_processing.js +0 -47
  106. package/dist/shared/utils/drag_utils.d.ts +0 -15
  107. package/dist/shared/utils/drag_utils.js +0 -67
  108. package/dist/shared/utils/filter_utils.d.ts +0 -29
  109. package/dist/shared/utils/filter_utils.js +0 -123
  110. package/dist/shared/utils/keyboard_utils.d.ts +0 -3
  111. package/dist/shared/utils/keyboard_utils.js +0 -236
  112. package/dist/shared/utils/menu_utils.d.ts +0 -42
  113. package/dist/shared/utils/menu_utils.js +0 -58
  114. package/dist/shared/utils/sort_utils.d.ts +0 -26
  115. package/dist/shared/utils/sort_utils.js +0 -59
  116. package/shared/TableCell.svelte +0 -330
  117. package/shared/TableHeader.svelte +0 -344
  118. package/shared/VirtualTable.svelte +0 -379
  119. package/shared/context/dataframe_context.ts +0 -701
  120. package/shared/icons/SortArrowDown.svelte +0 -25
  121. package/shared/icons/SortArrowUp.svelte +0 -25
  122. package/shared/utils/data_processing.ts +0 -77
  123. package/shared/utils/drag_utils.ts +0 -105
  124. package/shared/utils/filter_utils.ts +0 -208
  125. package/shared/utils/keyboard_utils.ts +0 -309
  126. package/shared/utils/menu_utils.ts +0 -115
  127. package/shared/utils/sort_utils.ts +0 -91
  128. package/test/sort_utils.test.ts +0 -323
@@ -0,0 +1,416 @@
1
+ import { describe, test, expect } from "vitest";
2
+ import {
3
+ is_cell_in_selection,
4
+ is_cell_selected,
5
+ get_range_selection,
6
+ handle_selection,
7
+ handle_delete_key,
8
+ should_show_cell_menu,
9
+ get_next_cell_coordinates,
10
+ move_cursor,
11
+ get_current_indices,
12
+ select_column,
13
+ select_row
14
+ } from "../shared/utils/selection_utils";
15
+ import type { CellCoordinate } from "../shared/types";
16
+
17
+ // helper: create a grid of CellData for move_cursor / get_next_cell_coordinates
18
+ function make_grid(rows: number, cols: number) {
19
+ return Array.from({ length: rows }, (_, r) =>
20
+ Array.from({ length: cols }, (_, c) => ({
21
+ id: `cell-${r}-${c}`,
22
+ value: `${r},${c}`
23
+ }))
24
+ );
25
+ }
26
+
27
+ // helper: create a minimal KeyboardEvent-like object
28
+ function make_key_event(
29
+ key: string,
30
+ opts: { metaKey?: boolean; ctrlKey?: boolean; shiftKey?: boolean } = {}
31
+ ) {
32
+ return {
33
+ key,
34
+ metaKey: opts.metaKey ?? false,
35
+ ctrlKey: opts.ctrlKey ?? false,
36
+ shiftKey: opts.shiftKey ?? false
37
+ } as KeyboardEvent;
38
+ }
39
+
40
+ describe("is_cell_in_selection", () => {
41
+ test("returns true when cell is in selection", () => {
42
+ expect(
43
+ is_cell_in_selection(
44
+ [1, 2],
45
+ [
46
+ [0, 0],
47
+ [1, 2],
48
+ [3, 4]
49
+ ]
50
+ )
51
+ ).toBe(true);
52
+ });
53
+
54
+ test("returns false when cell is not in selection", () => {
55
+ expect(
56
+ is_cell_in_selection(
57
+ [1, 2],
58
+ [
59
+ [0, 0],
60
+ [3, 4]
61
+ ]
62
+ )
63
+ ).toBe(false);
64
+ });
65
+
66
+ test("handles empty selection", () => {
67
+ expect(is_cell_in_selection([0, 0], [])).toBe(false);
68
+ });
69
+ });
70
+
71
+ describe("is_cell_selected", () => {
72
+ test("returns empty string when cell not selected", () => {
73
+ expect(is_cell_selected([0, 0], [[1, 1]])).toBe("");
74
+ });
75
+
76
+ test("returns 'cell-selected' for isolated cell", () => {
77
+ expect(is_cell_selected([1, 1], [[1, 1]])).toBe("cell-selected");
78
+ });
79
+
80
+ test("adds 'no-top' when neighbor above is selected", () => {
81
+ const result = is_cell_selected(
82
+ [1, 0],
83
+ [
84
+ [0, 0],
85
+ [1, 0]
86
+ ]
87
+ );
88
+ expect(result).toContain("cell-selected");
89
+ expect(result).toContain("no-top");
90
+ });
91
+
92
+ test("adds 'no-bottom' when neighbor below is selected", () => {
93
+ const result = is_cell_selected(
94
+ [0, 0],
95
+ [
96
+ [0, 0],
97
+ [1, 0]
98
+ ]
99
+ );
100
+ expect(result).toContain("cell-selected");
101
+ expect(result).toContain("no-bottom");
102
+ });
103
+
104
+ test("adds 'no-left' when neighbor left is selected", () => {
105
+ const result = is_cell_selected(
106
+ [0, 1],
107
+ [
108
+ [0, 0],
109
+ [0, 1]
110
+ ]
111
+ );
112
+ expect(result).toContain("cell-selected");
113
+ expect(result).toContain("no-left");
114
+ });
115
+
116
+ test("adds 'no-right' when neighbor right is selected", () => {
117
+ const result = is_cell_selected(
118
+ [0, 0],
119
+ [
120
+ [0, 0],
121
+ [0, 1]
122
+ ]
123
+ );
124
+ expect(result).toContain("cell-selected");
125
+ expect(result).toContain("no-right");
126
+ });
127
+
128
+ test("combines multiple neighbor flags for block selection", () => {
129
+ // 2x2 block: center-ish cell [1,1] in a block [[0,0],[0,1],[1,0],[1,1]]
130
+ const selection: CellCoordinate[] = [
131
+ [0, 0],
132
+ [0, 1],
133
+ [1, 0],
134
+ [1, 1]
135
+ ];
136
+ const result = is_cell_selected([1, 1], selection);
137
+ expect(result).toContain("cell-selected");
138
+ expect(result).toContain("no-top");
139
+ expect(result).toContain("no-left");
140
+ // no neighbor below or right
141
+ expect(result).not.toContain("no-bottom");
142
+ expect(result).not.toContain("no-right");
143
+ });
144
+ });
145
+
146
+ describe("get_range_selection", () => {
147
+ test("returns rectangular range with anchor first", () => {
148
+ const result = get_range_selection([0, 0], [2, 2]);
149
+ // anchor [0,0] should be first
150
+ expect(result[0]).toEqual([0, 0]);
151
+ // should contain all 9 cells in the 3x3 range
152
+ expect(result).toHaveLength(9);
153
+ expect(result).toContainEqual([1, 1]);
154
+ expect(result).toContainEqual([2, 2]);
155
+ });
156
+
157
+ test("handles reversed direction (end before start)", () => {
158
+ const result = get_range_selection([2, 2], [0, 0]);
159
+ expect(result[0]).toEqual([2, 2]);
160
+ expect(result).toHaveLength(9);
161
+ expect(result).toContainEqual([0, 0]);
162
+ });
163
+
164
+ test("single cell range returns just the anchor", () => {
165
+ const result = get_range_selection([1, 1], [1, 1]);
166
+ expect(result).toEqual([[1, 1]]);
167
+ });
168
+ });
169
+
170
+ describe("handle_selection", () => {
171
+ test("plain click returns single cell", () => {
172
+ const result = handle_selection([2, 3], [[0, 0]], {
173
+ shiftKey: false,
174
+ metaKey: false,
175
+ ctrlKey: false
176
+ });
177
+ expect(result).toEqual([[2, 3]]);
178
+ });
179
+
180
+ test("shift+click returns range from last selected", () => {
181
+ const result = handle_selection([2, 2], [[0, 0]], {
182
+ shiftKey: true,
183
+ metaKey: false,
184
+ ctrlKey: false
185
+ });
186
+ // range from [0,0] to [2,2] = 9 cells
187
+ expect(result).toHaveLength(9);
188
+ });
189
+
190
+ test("cmd/ctrl+click adds cell to selection", () => {
191
+ const result = handle_selection([1, 1], [[0, 0]], {
192
+ shiftKey: false,
193
+ metaKey: true,
194
+ ctrlKey: false
195
+ });
196
+ expect(result).toEqual([
197
+ [0, 0],
198
+ [1, 1]
199
+ ]);
200
+ });
201
+
202
+ test("cmd/ctrl+click removes already-selected cell", () => {
203
+ const result = handle_selection(
204
+ [0, 0],
205
+ [
206
+ [0, 0],
207
+ [1, 1]
208
+ ],
209
+ {
210
+ shiftKey: false,
211
+ metaKey: true,
212
+ ctrlKey: false
213
+ }
214
+ );
215
+ expect(result).toEqual([[1, 1]]);
216
+ });
217
+ });
218
+
219
+ describe("handle_delete_key", () => {
220
+ test("clears values of selected cells", () => {
221
+ const data = [
222
+ [
223
+ { id: "0-0", value: "a" },
224
+ { id: "0-1", value: "b" }
225
+ ],
226
+ [
227
+ { id: "1-0", value: "c" },
228
+ { id: "1-1", value: "d" }
229
+ ]
230
+ ];
231
+ const result = handle_delete_key(data, [
232
+ [0, 0],
233
+ [1, 1]
234
+ ]);
235
+ expect(result[0][0].value).toBe("");
236
+ expect(result[1][1].value).toBe("");
237
+ });
238
+
239
+ test("leaves unselected cells unchanged", () => {
240
+ const data = [
241
+ [
242
+ { id: "0-0", value: "a" },
243
+ { id: "0-1", value: "b" }
244
+ ]
245
+ ];
246
+ const result = handle_delete_key(data, [[0, 0]]);
247
+ expect(result[0][1].value).toBe("b");
248
+ });
249
+
250
+ test("handles out-of-bounds gracefully", () => {
251
+ const data = [
252
+ [
253
+ { id: "0-0", value: "a" },
254
+ { id: "0-1", value: "b" }
255
+ ]
256
+ ];
257
+ // should not throw
258
+ const result = handle_delete_key(data, [[5, 5]]);
259
+ expect(result[0][0].value).toBe("a");
260
+ expect(result[0][1].value).toBe("b");
261
+ });
262
+ });
263
+
264
+ describe("should_show_cell_menu", () => {
265
+ test("true when editable, single cell selected, and coords match", () => {
266
+ expect(should_show_cell_menu([1, 2], [[1, 2]], true)).toBe(true);
267
+ });
268
+
269
+ test("false when not editable", () => {
270
+ expect(should_show_cell_menu([1, 2], [[1, 2]], false)).toBe(false);
271
+ });
272
+
273
+ test("false when multiple cells selected", () => {
274
+ expect(
275
+ should_show_cell_menu(
276
+ [1, 2],
277
+ [
278
+ [1, 2],
279
+ [1, 3]
280
+ ],
281
+ true
282
+ )
283
+ ).toBe(false);
284
+ });
285
+
286
+ test("false when coords don't match", () => {
287
+ expect(should_show_cell_menu([0, 0], [[1, 2]], true)).toBe(false);
288
+ });
289
+ });
290
+
291
+ describe("get_next_cell_coordinates", () => {
292
+ const grid = make_grid(3, 3);
293
+
294
+ test("moves right to next column", () => {
295
+ expect(get_next_cell_coordinates([0, 0], grid, false)).toEqual([0, 1]);
296
+ });
297
+
298
+ test("wraps to next row at end of row", () => {
299
+ expect(get_next_cell_coordinates([0, 2], grid, false)).toEqual([1, 0]);
300
+ });
301
+
302
+ test("shift moves left to previous column", () => {
303
+ expect(get_next_cell_coordinates([0, 1], grid, true)).toEqual([0, 0]);
304
+ });
305
+
306
+ test("shift wraps to previous row at start of row", () => {
307
+ expect(get_next_cell_coordinates([1, 0], grid, true)).toEqual([0, 2]);
308
+ });
309
+
310
+ test("returns false at grid boundary", () => {
311
+ // forward past last cell
312
+ expect(get_next_cell_coordinates([2, 2], grid, false)).toBe(false);
313
+ // backward past first cell
314
+ expect(get_next_cell_coordinates([0, 0], grid, true)).toBe(false);
315
+ });
316
+ });
317
+
318
+ describe("move_cursor", () => {
319
+ const grid = make_grid(4, 4);
320
+
321
+ test("ArrowRight moves one column right", () => {
322
+ expect(move_cursor(make_key_event("ArrowRight"), [1, 1], grid)).toEqual([
323
+ 1, 2
324
+ ]);
325
+ });
326
+
327
+ test("ArrowLeft moves one column left", () => {
328
+ expect(move_cursor(make_key_event("ArrowLeft"), [1, 1], grid)).toEqual([
329
+ 1, 0
330
+ ]);
331
+ });
332
+
333
+ test("ArrowDown moves one row down", () => {
334
+ expect(move_cursor(make_key_event("ArrowDown"), [1, 1], grid)).toEqual([
335
+ 2, 1
336
+ ]);
337
+ });
338
+
339
+ test("ArrowUp moves one row up", () => {
340
+ expect(move_cursor(make_key_event("ArrowUp"), [1, 1], grid)).toEqual([
341
+ 0, 1
342
+ ]);
343
+ });
344
+
345
+ test("Cmd+ArrowRight jumps to last column", () => {
346
+ expect(
347
+ move_cursor(make_key_event("ArrowRight", { metaKey: true }), [1, 0], grid)
348
+ ).toEqual([1, 3]);
349
+ });
350
+
351
+ test("Cmd+ArrowLeft jumps to first column", () => {
352
+ expect(
353
+ move_cursor(make_key_event("ArrowLeft", { metaKey: true }), [1, 3], grid)
354
+ ).toEqual([1, 0]);
355
+ });
356
+
357
+ test("Cmd+ArrowDown jumps to last row", () => {
358
+ expect(
359
+ move_cursor(make_key_event("ArrowDown", { metaKey: true }), [0, 1], grid)
360
+ ).toEqual([3, 1]);
361
+ });
362
+
363
+ test("Cmd+ArrowUp jumps to first row", () => {
364
+ expect(
365
+ move_cursor(make_key_event("ArrowUp", { metaKey: true }), [3, 1], grid)
366
+ ).toEqual([0, 1]);
367
+ });
368
+
369
+ test("returns false at grid boundary", () => {
370
+ expect(move_cursor(make_key_event("ArrowUp"), [0, 0], grid)).toBe(false);
371
+ expect(move_cursor(make_key_event("ArrowLeft"), [0, 0], grid)).toBe(false);
372
+ expect(move_cursor(make_key_event("ArrowDown"), [3, 3], grid)).toBe(false);
373
+ expect(move_cursor(make_key_event("ArrowRight"), [3, 3], grid)).toBe(false);
374
+ });
375
+ });
376
+
377
+ describe("get_current_indices", () => {
378
+ const grid = make_grid(3, 3);
379
+
380
+ test("finds cell by ID", () => {
381
+ expect(get_current_indices("cell-1-2", grid)).toEqual([1, 2]);
382
+ });
383
+
384
+ test("returns [-1, -1] when ID not found", () => {
385
+ expect(get_current_indices("nonexistent", grid)).toEqual([-1, -1]);
386
+ });
387
+ });
388
+
389
+ describe("select_column", () => {
390
+ test("selects all rows for given column", () => {
391
+ const data = [
392
+ [1, 2, 3],
393
+ [4, 5, 6],
394
+ [7, 8, 9]
395
+ ];
396
+ expect(select_column(data, 1)).toEqual([
397
+ [0, 1],
398
+ [1, 1],
399
+ [2, 1]
400
+ ]);
401
+ });
402
+ });
403
+
404
+ describe("select_row", () => {
405
+ test("selects all columns for given row", () => {
406
+ const data = [
407
+ [1, 2, 3],
408
+ [4, 5, 6]
409
+ ];
410
+ expect(select_row(data, 0)).toEqual([
411
+ [0, 0],
412
+ [0, 1],
413
+ [0, 2]
414
+ ]);
415
+ });
416
+ });
@@ -1,12 +1,12 @@
1
1
  import { describe, test, expect } from "vitest";
2
- import { make_cell_id, make_header_id } from "../shared/utils/table_utils";
3
- import { process_data, make_headers } from "../shared/utils/data_processing";
2
+ import {
3
+ make_cell_id,
4
+ make_header_id,
5
+ guess_delimiter,
6
+ data_uri_to_blob
7
+ } from "../shared/utils/table_utils";
4
8
  import { cast_value_to_type } from "../shared/utils/utils";
5
9
 
6
- function make_id(): string {
7
- return Math.random().toString(36).substring(2, 15);
8
- }
9
-
10
10
  describe("table_utils", () => {
11
11
  describe("id generation", () => {
12
12
  test("generates unique cell ids", () => {
@@ -27,112 +27,6 @@ describe("table_utils", () => {
27
27
  expect(id_set.size).toBe(10);
28
28
  });
29
29
  });
30
-
31
- describe("process_data", () => {
32
- const element_refs: Record<string, { cell: null; input: null }> = {};
33
- const data_binding: Record<string, { value: string | number; id: string }> =
34
- {};
35
-
36
- test("processes data with row numbers", () => {
37
- const test_result = process_data(
38
- [
39
- ["1", "2"],
40
- ["3", "4"]
41
- ],
42
- element_refs,
43
- data_binding,
44
- make_id
45
- );
46
-
47
- expect(test_result[0].map((item) => item.value)).toEqual(["1", "2"]);
48
- expect(test_result).toHaveLength(2);
49
- });
50
-
51
- test("processes data without row numbers", () => {
52
- const test_result = process_data(
53
- [
54
- ["1", "2"],
55
- ["3", "4"]
56
- ],
57
- element_refs,
58
- data_binding,
59
- make_id
60
- );
61
-
62
- expect(test_result.length).toBe(2);
63
- expect(test_result[0].length).toBe(2);
64
- expect(test_result[0][0].value).toBe("1");
65
- expect(test_result[0][1].value).toBe("2");
66
- });
67
-
68
- test("handles empty data", () => {
69
- const test_result = process_data([], element_refs, data_binding, make_id);
70
-
71
- expect(test_result.length).toBe(0);
72
- });
73
- });
74
-
75
- describe("make_headers", () => {
76
- test("creates headers with ids when headers are provided", () => {
77
- const els = {};
78
- const headers = ["Name", "Age", "City"];
79
- const col_count: [number, "fixed" | "dynamic"] = [3, "fixed"];
80
-
81
- const result = make_headers(headers, col_count, els, make_id);
82
-
83
- expect(result.length).toBe(3);
84
- expect(result[0].value).toBe("Name");
85
- expect(result[1].value).toBe("Age");
86
- expect(result[2].value).toBe("City");
87
- expect(result[0].id).toBeDefined();
88
- expect(result[1].id).toBeDefined();
89
- expect(result[2].id).toBeDefined();
90
- expect(Object.keys(els).length).toBe(3);
91
- });
92
-
93
- test("fills missing headers when col_count is fixed", () => {
94
- const els = {};
95
- const headers = ["Name", "Age"];
96
- const col_count: [number, "fixed" | "dynamic"] = [4, "fixed"];
97
-
98
- const result = make_headers(headers, col_count, els, make_id);
99
-
100
- expect(result.length).toBe(4);
101
- expect(result[0].value).toBe("Name");
102
- expect(result[1].value).toBe("Age");
103
- expect(result[2].value).toBe("2");
104
- expect(result[3].value).toBe("3");
105
- expect(Object.keys(els).length).toBe(4);
106
- });
107
-
108
- test("creates default headers when no headers are provided", () => {
109
- const els = {};
110
- const headers = [];
111
- const col_count: [number, "fixed" | "dynamic"] = [3, "fixed"];
112
-
113
- const result = make_headers(headers, col_count, els, make_id);
114
-
115
- expect(result.length).toBe(3);
116
- expect(result[0].value).toBe("0");
117
- expect(result[1].value).toBe("1");
118
- expect(result[2].value).toBe("2");
119
- expect(Object.keys(els).length).toBe(3);
120
- });
121
-
122
- test("handles null values in headers", () => {
123
- const els = {};
124
- const headers = ["Name", null, "City"] as any;
125
- const col_count: [number, "fixed" | "dynamic"] = [3, "fixed"];
126
-
127
- const result = make_headers(headers, col_count, els, make_id);
128
-
129
- expect(result.length).toBe(3);
130
- expect(result[0].value).toBe("Name");
131
- expect(result[1].value).toBe("");
132
- expect(result[2].value).toBe("City");
133
- expect(Object.keys(els).length).toBe(3);
134
- });
135
- });
136
30
  });
137
31
 
138
32
  describe("cast_value_to_type", () => {
@@ -174,3 +68,42 @@ describe("cast_value_to_type", () => {
174
68
  expect(cast_value_to_type(undefined, "str")).toBe(undefined);
175
69
  });
176
70
  });
71
+
72
+ describe("guess_delimiter", () => {
73
+ test("detects comma delimiter", () => {
74
+ const csv = "a,b,c\n1,2,3\n4,5,6";
75
+ expect(guess_delimiter(csv, [",", "\t"])).toContain(",");
76
+ });
77
+
78
+ test("detects tab delimiter", () => {
79
+ const tsv = "a\tb\tc\n1\t2\t3\n4\t5\t6";
80
+ expect(guess_delimiter(tsv, [",", "\t"])).toContain("\t");
81
+ });
82
+
83
+ test("returns empty array when no consistent delimiter", () => {
84
+ const text = "abc\ndef\nghi";
85
+ expect(guess_delimiter(text, [",", "\t"])).toEqual([]);
86
+ });
87
+
88
+ test("handles single-line input", () => {
89
+ // single line with commas — cache set once, always matches
90
+ const text = "a,b,c";
91
+ expect(guess_delimiter(text, [",", "\t"])).toContain(",");
92
+ });
93
+ });
94
+
95
+ describe("data_uri_to_blob", () => {
96
+ test("converts data URI to Blob with correct MIME type", () => {
97
+ const data_uri = "data:text/plain;base64,SGVsbG8=";
98
+ const blob = data_uri_to_blob(data_uri);
99
+ expect(blob.type).toBe("text/plain");
100
+ });
101
+
102
+ test("converts data URI to Blob with correct content", async () => {
103
+ // "Hello" in base64 is "SGVsbG8="
104
+ const data_uri = "data:text/plain;base64,SGVsbG8=";
105
+ const blob = data_uri_to_blob(data_uri);
106
+ const text = await blob.text();
107
+ expect(text).toBe("Hello");
108
+ });
109
+ });
package/types.ts ADDED
@@ -0,0 +1,34 @@
1
+ import type { SelectData } from "@gradio/utils";
2
+ import type { LoadingStatus } from "@gradio/statustracker";
3
+ import type { DataframeValue, Datatype, EditData } from "./shared/utils/utils";
4
+
5
+ export interface DataframeEvents {
6
+ change: DataframeValue;
7
+ input: never;
8
+ select: SelectData;
9
+ edit: EditData;
10
+ clear_status: LoadingStatus;
11
+ }
12
+
13
+ export interface DataframeProps {
14
+ value: DataframeValue;
15
+ col_count: [number, "fixed" | "dynamic"];
16
+ row_count: [number, "fixed" | "dynamic"];
17
+ wrap: boolean;
18
+ datatype: Datatype | Datatype[];
19
+ line_breaks: boolean;
20
+ column_widths: string[];
21
+ latex_delimiters: {
22
+ left: string;
23
+ right: string;
24
+ display: boolean;
25
+ }[];
26
+ max_height: number;
27
+ buttons: string[] | null;
28
+ max_chars: number | undefined;
29
+ show_row_numbers: boolean;
30
+ show_search: "none" | "search" | "filter";
31
+ pinned_columns: number;
32
+ static_columns: (string | number)[];
33
+ fullscreen: boolean;
34
+ }