@marimo-team/frontend 0.25.2-dev53 → 0.25.2-dev61
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/assets/{CellStatus-Q5giBgg0.js → CellStatus-CRFBVGjI.js} +1 -1
- package/dist/assets/ConnectedDataExplorerComponent-C5UW0p9I.js +1 -0
- package/dist/assets/{ErrorBoundary-CowFEmQK.js → ErrorBoundary-BKmN7zye.js} +2 -2
- package/dist/assets/JsonOutput-nqqzL-n0.js +11 -0
- package/dist/assets/{MarimoErrorOutput-p5jt_udI.js → MarimoErrorOutput-DCu6fm2y.js} +1 -1
- package/dist/assets/{RenderHTML-CqDY0F9e.js → RenderHTML-DRMkieKn.js} +1 -1
- package/dist/assets/RunButton-CPZWjCL9.js +1 -0
- package/dist/assets/{add-cell-with-ai-BKqOYjHI.js → add-cell-with-ai-sH3HbLJO.js} +1 -1
- package/dist/assets/{add-connection-dialog-CL3bFsGA.js → add-connection-dialog-pQcdKIlO.js} +2 -2
- package/dist/assets/{agent-panel-q9X3jIj0.js → agent-panel-Bkcf1sED.js} +1 -1
- package/dist/assets/ai-model-dropdown-D-51nbXZ.js +5 -0
- package/dist/assets/{app-config-button-Bylx9JDv.js → app-config-button-Dr1EwwIk.js} +1 -1
- package/dist/assets/{blob-BTk5Q8u9.js → blob-BIYte8oT.js} +1 -1
- package/dist/assets/cache-panel-BNPF4X5e.js +1 -0
- package/dist/assets/{cell-editor-DfRhvA75.js → cell-editor-Cmqe3QmM.js} +3 -3
- package/dist/assets/{cell-link-DRuyhIKm.js → cell-link-BeZAiE1D.js} +1 -1
- package/dist/assets/{cells-CuKxjgjJ.js → cells-h5u00Nz8.js} +3 -3
- package/dist/assets/{chat-display-BT4wYvSi.js → chat-display-CuZ8niXg.js} +1 -1
- package/dist/assets/chat-panel-CHPPwqcP.js +4 -0
- package/dist/assets/{chat-ui-DsHB8FDP.js → chat-ui-BJAcq-Ln.js} +1 -1
- package/dist/assets/column-preview-DtrgJzVI.js +1 -0
- package/dist/assets/{command-palette-DLjm6e2f.js → command-palette-DUPyVGsq.js} +1 -1
- package/dist/assets/{common-BHV00wKC.js → common-BMeq0Bqi.js} +1 -1
- package/dist/assets/{components-DzAIJKU7.js → components-CWcf8098.js} +1 -1
- package/dist/assets/{config-BaEZVOk2.js → config-CR2d-XsP.js} +1 -1
- package/dist/assets/{datasource-D0736MyK.js → datasource-BLHGFSsT.js} +1 -1
- package/dist/assets/{dependency-graph-panel-B6fAc_9C.js → dependency-graph-panel-C8X3Nn8W.js} +1 -1
- package/dist/assets/{documentation-panel-DQAQba71.js → documentation-panel-C8wrSKlX.js} +1 -1
- package/dist/assets/{download-D-MTWucM.js → download-CUgiCx7s.js} +1 -1
- package/dist/assets/edit-page-DMsRAPsr.js +9 -0
- package/dist/assets/{error-entries-CWxVwEPG.js → error-entries-DQD4A9cx.js} +1 -1
- package/dist/assets/{error-panel-DISYRsce.js → error-panel-BJYkwYEY.js} +1 -1
- package/dist/assets/{feedback-button-B1bYxCrZ.js → feedback-button-DbTjGwcX.js} +2 -2
- package/dist/assets/feedback-button-JqvnTSfN.js +1 -0
- package/dist/assets/file-explorer-panel-BQi88S1o.js +1 -0
- package/dist/assets/{file-name-input-BVXW4Ihy.js → file-name-input-DPJSxW_X.js} +1 -1
- package/dist/assets/{floating-outline-BhY3Wt-J.js → floating-outline-3PlTr0K1.js} +1 -1
- package/dist/assets/{gallery-page-Be8AbY36.js → gallery-page-BqvVmwde.js} +1 -1
- package/dist/assets/{glide-data-editor-D1Fip07J.js → glide-data-editor-CAa14cIr.js} +1 -1
- package/dist/assets/home-page-B_NxFh87.js +4 -0
- package/dist/assets/{hooks-BgpsvfLA.js → hooks-BHoInJWN.js} +1 -1
- package/dist/assets/index-C968xQuz.js +29 -0
- package/dist/assets/index-CF8Gfz1R.css +2 -0
- package/dist/assets/input-DPd57MDP.js +7 -0
- package/dist/assets/{layout-BAJKEADQ.js → layout-CzNq5YtR.js} +5 -5
- package/dist/assets/{logs-panel-CjHa5qIw.js → logs-panel-IKL6F2OX.js} +1 -1
- package/dist/assets/{markdown-renderer-quSCxU-_.js → markdown-renderer-DHvAXF8E.js} +1 -1
- package/dist/assets/{name-cell-input-Cba1m8_c.js → name-cell-input-j4eGLLyd.js} +1 -1
- package/dist/assets/{outline-panel-CWsF1QII.js → outline-panel-CprDWmxn.js} +1 -1
- package/dist/assets/{packages-panel-C-XEdHNc.js → packages-panel-BGqHk9nr.js} +1 -1
- package/dist/assets/{panel-accordion-state-u7avi-RV.js → panel-accordion-state-8xXVhawu.js} +1 -1
- package/dist/assets/{panels-B5iuV0lM.js → panels-Dsf6yEsA.js} +1 -1
- package/dist/assets/{process-output-DbjZ_y5T.js → process-output-DOmeJ1VR.js} +1 -1
- package/dist/assets/{readonly-code-display-Ctjk_xXb.js → readonly-code-display-xvH6MyT6.js} +1 -1
- package/dist/assets/{readonly-python-code-Ctej1jSH.js → readonly-python-code-D4YGmSvB.js} +1 -1
- package/dist/assets/{reveal-component-YHvm3T44.js → reveal-component-CJxdWpbj.js} +1 -1
- package/dist/assets/{run-page-BkMc69OJ.js → run-page-DCu4YGwW.js} +1 -1
- package/dist/assets/{sandbox-panel-fEQNl_1C.js → sandbox-panel-D5bRunTM.js} +1 -1
- package/dist/assets/{scratchpad-panel-CZ1PriSX.js → scratchpad-panel-BSlnM0TK.js} +1 -1
- package/dist/assets/{secrets-panel-Yc5ollBX.js → secrets-panel-DT1Nansx.js} +1 -1
- package/dist/assets/{select-Ww_82o86.js → select-BwRFbZty.js} +1 -1
- package/dist/assets/session-panel-BdN43Lzi.js +1 -0
- package/dist/assets/{snippets-panel-BFXHSCEq.js → snippets-panel-5_jR08Zd.js} +1 -1
- package/dist/assets/spec-D9d9nT0z.js +1 -0
- package/dist/assets/{state-Do1H9fl6.js → state-7os84dnI.js} +1 -1
- package/dist/assets/{state-CdP1z8ku.js → state-BxJ_FX3k.js} +1 -1
- package/dist/assets/switch-C-83e2fG.js +1 -0
- package/dist/assets/{list-filter-CTg19675.js → table-B7KYyTxw.js} +1 -1
- package/dist/assets/{toast-CL5SyrsO.js → toast-BRwEbWb6.js} +1 -1
- package/dist/assets/{tracing-DBfpxL3C.js → tracing-CRkNpBaH.js} +1 -1
- package/dist/assets/tracing-panel-DV2-2TUI.js +2 -0
- package/dist/assets/{tree-actions-DdvPjaze.js → tree-actions-CShqSonp.js} +1 -1
- package/dist/assets/{useAddCell-BbK54Wma.js → useAddCell-DesMfnE4.js} +1 -1
- package/dist/assets/{useCellActionButton-BEZZV5mI.js → useCellActionButton-BO5SFMxA.js} +1 -1
- package/dist/assets/useDateFormatter-CTYCgKB-.js +1 -0
- package/dist/assets/{useDeleteCell-BG8LgAEA.js → useDeleteCell-DSbUjpSC.js} +1 -1
- package/dist/assets/{useDependencyPanelTab-BEjMLLcY.js → useDependencyPanelTab-JIFiQrr2.js} +1 -1
- package/dist/assets/{useInstallPackage-DnijI-KS.js → useInstallPackage-CCQcFc6U.js} +1 -1
- package/dist/assets/{useNotebookActions-Cnznz5jV.js → useNotebookActions-BDD321Hs.js} +1 -1
- package/dist/assets/useNumberFormatter-BP99Zbyy.js +1 -0
- package/dist/assets/{useRunCells-DTPuvtCQ.js → useRunCells-Bx3wXUWO.js} +1 -1
- package/dist/assets/{useSplitCell-BuTWivoM.js → useSplitCell-BRmQP02b.js} +1 -1
- package/dist/assets/{write-secret-modal-DknTu1O1.js → write-secret-modal-BcosCmZy.js} +2 -2
- package/dist/index.html +32 -32
- package/package.json +3 -3
- package/src/components/data-table/TableTopBar.tsx +4 -0
- package/src/components/data-table/__tests__/export-actions.test.tsx +1111 -14
- package/src/components/data-table/__tests__/schemas.test.ts +57 -0
- package/src/components/data-table/data-table.tsx +3 -0
- package/src/components/data-table/export-actions.tsx +365 -81
- package/src/components/data-table/schemas.ts +90 -14
- package/src/components/editor/navigation/__tests__/navigation.test.ts +8 -8
- package/src/plugins/impl/DataTablePlugin.tsx +23 -2
- package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +65 -8
- package/src/plugins/impl/data-frames/DataFramePlugin.tsx +15 -3
- package/src/stories/dataframe.stories.tsx +7 -0
- package/dist/assets/ConnectedDataExplorerComponent-D5gfkAKK.js +0 -1
- package/dist/assets/JsonOutput-c1E0SCoN.js +0 -11
- package/dist/assets/RunButton-TcDo218m.js +0 -1
- package/dist/assets/ai-model-dropdown-CK8y46Fn.js +0 -5
- package/dist/assets/cache-panel-CKzoUIhe.js +0 -1
- package/dist/assets/chat-panel-C3Bu_7u5.js +0 -4
- package/dist/assets/column-preview-BltuXodG.js +0 -1
- package/dist/assets/edit-page-CMJrCnfU.js +0 -9
- package/dist/assets/feedback-button-BW1-NBBf.js +0 -1
- package/dist/assets/file-explorer-panel-VIG5c9ac.js +0 -1
- package/dist/assets/home-page-Dv2fEKyO.js +0 -4
- package/dist/assets/index-BGdpHNhZ.css +0 -2
- package/dist/assets/index-BdrTweFz.js +0 -29
- package/dist/assets/input-DnyzXg4C.js +0 -7
- package/dist/assets/session-panel-DD5LfQGb.js +0 -1
- package/dist/assets/spec-SOFn9xEK.js +0 -1
- package/dist/assets/switch-CPh_C6Mu.js +0 -1
- package/dist/assets/tracing-panel-CS9W1a0x.js +0 -2
- package/dist/assets/useDateFormatter-CjgScv5a.js +0 -1
- package/dist/assets/useNumberFormatter-C4-_TnkF.js +0 -1
- /package/dist/assets/{context-BFOBKCJB.js → I18nProvider-BFOBKCJB.js} +0 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import z from "zod";
|
|
4
4
|
import { rpc } from "@/plugins/core/rpc";
|
|
5
5
|
|
|
6
|
-
export type DownloadFormat = "csv" | "json" | "parquet" | "tsv";
|
|
6
|
+
export type DownloadFormat = "csv" | "json" | "parquet" | "tsv" | "geojson";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Per-request export options. Each field is optional. A missing field keeps
|
|
@@ -18,6 +18,7 @@ export interface DownloadAsOptions {
|
|
|
18
18
|
export interface DownloadAsRequest {
|
|
19
19
|
format: DownloadFormat;
|
|
20
20
|
options?: DownloadAsOptions;
|
|
21
|
+
geometry_column?: string | null;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
export type DownloadAsArgs = (req: DownloadAsRequest) => Promise<{
|
|
@@ -25,26 +26,101 @@ export type DownloadAsArgs = (req: DownloadAsRequest) => Promise<{
|
|
|
25
26
|
filename: string;
|
|
26
27
|
error?: string | null;
|
|
27
28
|
missing_packages?: string[] | null;
|
|
29
|
+
code?:
|
|
30
|
+
| "geometry_required"
|
|
31
|
+
| "invalid_geometry"
|
|
32
|
+
| "invalid_metadata"
|
|
33
|
+
| "unsupported_representation"
|
|
34
|
+
| "unsupported_version"
|
|
35
|
+
| "missing_packages"
|
|
36
|
+
| "missing_crs"
|
|
37
|
+
| "conversion_failed"
|
|
38
|
+
| null;
|
|
39
|
+
column?: string | null;
|
|
28
40
|
}>;
|
|
29
41
|
|
|
42
|
+
const DownloadRequestSchema = z.object({
|
|
43
|
+
geometry_column: z.string().nullish(),
|
|
44
|
+
options: z
|
|
45
|
+
.object({
|
|
46
|
+
separator: z.string().optional(),
|
|
47
|
+
encoding: z.string().optional(),
|
|
48
|
+
ensure_ascii: z.boolean().optional(),
|
|
49
|
+
})
|
|
50
|
+
.optional(),
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const DownloadResponseSchema = z.object({
|
|
54
|
+
url: z.string(),
|
|
55
|
+
filename: z.string(),
|
|
56
|
+
error: z.string().nullish(),
|
|
57
|
+
missing_packages: z.array(z.string()).nullish(),
|
|
58
|
+
code: z
|
|
59
|
+
.enum([
|
|
60
|
+
"geometry_required",
|
|
61
|
+
"invalid_geometry",
|
|
62
|
+
"invalid_metadata",
|
|
63
|
+
"unsupported_representation",
|
|
64
|
+
"unsupported_version",
|
|
65
|
+
"missing_packages",
|
|
66
|
+
"conversion_failed",
|
|
67
|
+
])
|
|
68
|
+
.nullish(),
|
|
69
|
+
column: z.string().nullish(),
|
|
70
|
+
});
|
|
71
|
+
|
|
30
72
|
export const DownloadAsSchema = rpc
|
|
31
73
|
.input(
|
|
32
|
-
|
|
74
|
+
DownloadRequestSchema.extend({
|
|
33
75
|
format: z.enum(["csv", "json", "parquet", "tsv"]),
|
|
34
|
-
options: z
|
|
35
|
-
.object({
|
|
36
|
-
separator: z.string().optional(),
|
|
37
|
-
encoding: z.string().optional(),
|
|
38
|
-
ensure_ascii: z.boolean().optional(),
|
|
39
|
-
})
|
|
40
|
-
.optional(),
|
|
41
76
|
}),
|
|
42
77
|
)
|
|
78
|
+
.output(DownloadResponseSchema);
|
|
79
|
+
|
|
80
|
+
export const DownloadGeoJSONSchema = rpc
|
|
81
|
+
.input(DownloadRequestSchema.extend({ format: z.literal("geojson") }))
|
|
43
82
|
.output(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
83
|
+
DownloadResponseSchema.extend({
|
|
84
|
+
code: z.union([
|
|
85
|
+
DownloadResponseSchema.shape.code,
|
|
86
|
+
z.literal("missing_crs"),
|
|
87
|
+
]),
|
|
49
88
|
}),
|
|
50
89
|
);
|
|
90
|
+
|
|
91
|
+
export type DownloadAsFunction = (
|
|
92
|
+
req: z.infer<typeof DownloadAsSchema.input>,
|
|
93
|
+
) => Promise<z.infer<typeof DownloadAsSchema.output>>;
|
|
94
|
+
|
|
95
|
+
export type DownloadGeoJSON = (
|
|
96
|
+
req: z.infer<typeof DownloadGeoJSONSchema.input>,
|
|
97
|
+
) => Promise<z.infer<typeof DownloadGeoJSONSchema.output>>;
|
|
98
|
+
|
|
99
|
+
const ExportMetadataSchema = z.object({
|
|
100
|
+
geometry_columns: z.array(
|
|
101
|
+
z.object({
|
|
102
|
+
name: z.string(),
|
|
103
|
+
encoding: z.enum(["objects", "wkb", "wkt", "other"]),
|
|
104
|
+
crs: z.union([z.string(), z.record(z.string(), z.unknown())]).nullable(),
|
|
105
|
+
}),
|
|
106
|
+
),
|
|
107
|
+
primary_geometry_column: z.string().nullable(),
|
|
108
|
+
default_geometry_column: z.string().nullable(),
|
|
109
|
+
formats: z.record(
|
|
110
|
+
z.string(),
|
|
111
|
+
z.object({
|
|
112
|
+
available: z.boolean(),
|
|
113
|
+
reason: z.string().nullable(),
|
|
114
|
+
missing_packages: z.array(z.string()),
|
|
115
|
+
}),
|
|
116
|
+
),
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
export type ExportMetadata = z.infer<typeof ExportMetadataSchema>;
|
|
120
|
+
export type GetExportMetadata = (
|
|
121
|
+
opts: Record<string, never>,
|
|
122
|
+
) => Promise<ExportMetadata>;
|
|
123
|
+
|
|
124
|
+
export const GetExportMetadataSchema = rpc
|
|
125
|
+
.input(z.object({}))
|
|
126
|
+
.output(ExportMetadataSchema);
|
|
@@ -703,7 +703,7 @@ describe("useCellNavigationProps", () => {
|
|
|
703
703
|
});
|
|
704
704
|
|
|
705
705
|
expect(mockCopyCell).not.toHaveBeenCalled();
|
|
706
|
-
expect(mockEvent.
|
|
706
|
+
expect(mockEvent.stopPropagation).not.toHaveBeenCalled();
|
|
707
707
|
});
|
|
708
708
|
});
|
|
709
709
|
|
|
@@ -714,7 +714,7 @@ describe("useCellNavigationProps", () => {
|
|
|
714
714
|
);
|
|
715
715
|
|
|
716
716
|
const mockEvent = Mocks.keyboardEvent({
|
|
717
|
-
key: "
|
|
717
|
+
key: "F24",
|
|
718
718
|
target: document.createElement("div"),
|
|
719
719
|
});
|
|
720
720
|
|
|
@@ -722,7 +722,7 @@ describe("useCellNavigationProps", () => {
|
|
|
722
722
|
result.current.onKeyDown?.(mockEvent);
|
|
723
723
|
});
|
|
724
724
|
|
|
725
|
-
expect(mockEvent.
|
|
725
|
+
expect(mockEvent.stopPropagation).not.toHaveBeenCalled();
|
|
726
726
|
});
|
|
727
727
|
});
|
|
728
728
|
|
|
@@ -1749,7 +1749,7 @@ describe("useCellEditorNavigationProps", () => {
|
|
|
1749
1749
|
});
|
|
1750
1750
|
|
|
1751
1751
|
expect(focusCell).toHaveBeenCalledWith(mockCellId);
|
|
1752
|
-
expect(mockEvent.
|
|
1752
|
+
expect(mockEvent.stopPropagation).not.toHaveBeenCalled();
|
|
1753
1753
|
});
|
|
1754
1754
|
|
|
1755
1755
|
it("should clear text selection when Escape is pressed with selection", () => {
|
|
@@ -1901,7 +1901,7 @@ describe("useCellEditorNavigationProps", () => {
|
|
|
1901
1901
|
});
|
|
1902
1902
|
|
|
1903
1903
|
expect(focusCell).not.toHaveBeenCalled();
|
|
1904
|
-
expect(mockEvent.
|
|
1904
|
+
expect(mockEvent.stopPropagation).not.toHaveBeenCalled();
|
|
1905
1905
|
});
|
|
1906
1906
|
});
|
|
1907
1907
|
|
|
@@ -1932,7 +1932,7 @@ describe("useCellEditorNavigationProps", () => {
|
|
|
1932
1932
|
});
|
|
1933
1933
|
|
|
1934
1934
|
expect(focusCell).toHaveBeenCalledWith(mockCellId);
|
|
1935
|
-
expect(mockEvent.
|
|
1935
|
+
expect(mockEvent.stopPropagation).not.toHaveBeenCalled();
|
|
1936
1936
|
});
|
|
1937
1937
|
});
|
|
1938
1938
|
|
|
@@ -1954,7 +1954,7 @@ describe("useCellEditorNavigationProps", () => {
|
|
|
1954
1954
|
});
|
|
1955
1955
|
|
|
1956
1956
|
expect(focusCell).toHaveBeenCalledWith(mockCellId);
|
|
1957
|
-
expect(mockEvent.
|
|
1957
|
+
expect(mockEvent.stopPropagation).not.toHaveBeenCalled();
|
|
1958
1958
|
});
|
|
1959
1959
|
});
|
|
1960
1960
|
|
|
@@ -1979,7 +1979,7 @@ describe("useCellEditorNavigationProps", () => {
|
|
|
1979
1979
|
});
|
|
1980
1980
|
|
|
1981
1981
|
expect(focusCell).toHaveBeenCalledWith(mockCellId);
|
|
1982
|
-
expect(mockEvent.
|
|
1982
|
+
expect(mockEvent.stopPropagation).not.toHaveBeenCalled();
|
|
1983
1983
|
});
|
|
1984
1984
|
});
|
|
1985
1985
|
});
|
|
@@ -36,7 +36,12 @@ import { usePanelOwnership } from "@/components/data-table/hooks/use-panel-owner
|
|
|
36
36
|
import { LoadingTable } from "@/components/data-table/loading-table";
|
|
37
37
|
import {
|
|
38
38
|
type DownloadAsArgs,
|
|
39
|
+
type DownloadAsFunction,
|
|
39
40
|
DownloadAsSchema,
|
|
41
|
+
type DownloadGeoJSON,
|
|
42
|
+
DownloadGeoJSONSchema,
|
|
43
|
+
type GetExportMetadata,
|
|
44
|
+
GetExportMetadataSchema,
|
|
40
45
|
} from "@/components/data-table/schemas";
|
|
41
46
|
import { TableExplorerPanel } from "@/components/data-table/table-explorer-panel/table-explorer-panel";
|
|
42
47
|
import {
|
|
@@ -209,7 +214,9 @@ interface Data<T> {
|
|
|
209
214
|
|
|
210
215
|
// oxlint-disable-next-line typescript/consistent-type-definitions
|
|
211
216
|
type DataTableFunctions = {
|
|
212
|
-
download_as:
|
|
217
|
+
download_as: DownloadAsFunction;
|
|
218
|
+
download_geojson: DownloadGeoJSON;
|
|
219
|
+
get_export_metadata: GetExportMetadata;
|
|
213
220
|
get_column_summaries: <T>(opts: {}) => Promise<ColumnSummaries<T>>;
|
|
214
221
|
search: <T>(req: {
|
|
215
222
|
sort?: {
|
|
@@ -302,6 +309,8 @@ export const DataTablePlugin = createPlugin<S>("marimo-table")
|
|
|
302
309
|
)
|
|
303
310
|
.withFunctions<DataTableFunctions>({
|
|
304
311
|
download_as: DownloadAsSchema,
|
|
312
|
+
download_geojson: DownloadGeoJSONSchema,
|
|
313
|
+
get_export_metadata: GetExportMetadataSchema,
|
|
305
314
|
get_column_summaries: rpc.input(z.looseObject({})).output(
|
|
306
315
|
z.object({
|
|
307
316
|
data: z.union([z.string(), z.array(z.looseObject({}))]).nullable(),
|
|
@@ -839,7 +848,9 @@ const DataTableComponent = ({
|
|
|
839
848
|
fieldTypes,
|
|
840
849
|
paginationState,
|
|
841
850
|
setPaginationState,
|
|
842
|
-
download_as
|
|
851
|
+
download_as,
|
|
852
|
+
download_geojson,
|
|
853
|
+
get_export_metadata: getExportMetadata,
|
|
843
854
|
columnSummaries,
|
|
844
855
|
className,
|
|
845
856
|
setValue,
|
|
@@ -880,6 +891,15 @@ const DataTableComponent = ({
|
|
|
880
891
|
sizeBytesIsLoading?: boolean;
|
|
881
892
|
}): JSX.Element => {
|
|
882
893
|
const id = useId();
|
|
894
|
+
const downloadAs = useCallback<DownloadAsArgs>(
|
|
895
|
+
(request) => {
|
|
896
|
+
const { format, ...options } = request;
|
|
897
|
+
return format === "geojson"
|
|
898
|
+
? download_geojson({ format, ...options })
|
|
899
|
+
: download_as({ format, ...options });
|
|
900
|
+
},
|
|
901
|
+
[download_as, download_geojson],
|
|
902
|
+
);
|
|
883
903
|
const { locale } = useLocale();
|
|
884
904
|
const [viewedRowIdx, setViewedRowIdx] = useState(0);
|
|
885
905
|
const { isPanelOpen, isAnyPanelOpen, togglePanel, panelType, setPanelType } =
|
|
@@ -1168,6 +1188,7 @@ const DataTableComponent = ({
|
|
|
1168
1188
|
hoverTemplate={hoverTemplate}
|
|
1169
1189
|
cellHoverTexts={cellHoverTexts}
|
|
1170
1190
|
downloadAs={showDownload ? downloadAs : undefined}
|
|
1191
|
+
getExportMetadata={showDownload ? getExportMetadata : undefined}
|
|
1171
1192
|
showSearch={showSearch}
|
|
1172
1193
|
searchQuery={searchQuery}
|
|
1173
1194
|
onSearchQueryChange={setSearchQuery}
|
|
@@ -4,11 +4,17 @@ import { Tooltip } from "radix-ui";
|
|
|
4
4
|
|
|
5
5
|
const TooltipProvider = Tooltip.Provider;
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
act,
|
|
9
|
+
fireEvent,
|
|
10
|
+
render,
|
|
11
|
+
screen,
|
|
12
|
+
waitFor,
|
|
13
|
+
} from "@testing-library/react";
|
|
8
14
|
import { Provider } from "jotai";
|
|
9
15
|
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
|
10
16
|
import { SetupMocks } from "@/__mocks__/common";
|
|
11
|
-
import type {
|
|
17
|
+
import type { DownloadAsFunction } from "@/components/data-table/schemas";
|
|
12
18
|
import type { FieldTypesWithExternalType } from "@/components/data-table/types";
|
|
13
19
|
import { store } from "@/core/state/jotai";
|
|
14
20
|
import {
|
|
@@ -19,6 +25,10 @@ import {
|
|
|
19
25
|
|
|
20
26
|
// Default to normal (non-static) mode; individual tests flip this on.
|
|
21
27
|
const mockIsStatic = vi.fn().mockReturnValue(false);
|
|
28
|
+
vi.mock("@/utils/download", () => ({
|
|
29
|
+
downloadByURL: vi.fn(),
|
|
30
|
+
withLoadingToast: vi.fn(async (_title, callback) => callback()),
|
|
31
|
+
}));
|
|
22
32
|
vi.mock("@/core/static/static-state", async (importOriginal) => {
|
|
23
33
|
const actual =
|
|
24
34
|
await importOriginal<typeof import("@/core/static/static-state")>();
|
|
@@ -30,9 +40,15 @@ beforeAll(() => {
|
|
|
30
40
|
});
|
|
31
41
|
|
|
32
42
|
describe("LoadingDataTableComponent", () => {
|
|
33
|
-
it("keeps data columns
|
|
43
|
+
it("keeps data columns and routes each export to its RPC", async () => {
|
|
34
44
|
const host = document.createElement("div");
|
|
35
45
|
const data = JSON.stringify([{ index: "first", value: 1 }]);
|
|
46
|
+
const downloadAs = vi
|
|
47
|
+
.fn()
|
|
48
|
+
.mockResolvedValue({ url: "export", filename: "table.csv" });
|
|
49
|
+
const downloadGeoJSON = vi
|
|
50
|
+
.fn()
|
|
51
|
+
.mockResolvedValue({ url: "export", filename: "table.geojson" });
|
|
36
52
|
|
|
37
53
|
render(
|
|
38
54
|
<Provider store={store}>
|
|
@@ -43,7 +59,7 @@ describe("LoadingDataTableComponent", () => {
|
|
|
43
59
|
pagination={false}
|
|
44
60
|
pageSize={10}
|
|
45
61
|
selection={null}
|
|
46
|
-
showDownload={
|
|
62
|
+
showDownload={true}
|
|
47
63
|
showFilters={false}
|
|
48
64
|
showColumnSummaries={false}
|
|
49
65
|
showDataTypes={false}
|
|
@@ -68,7 +84,22 @@ describe("LoadingDataTableComponent", () => {
|
|
|
68
84
|
cell_styles: null,
|
|
69
85
|
cell_hover_texts: null,
|
|
70
86
|
})}
|
|
71
|
-
download_as={
|
|
87
|
+
download_as={downloadAs}
|
|
88
|
+
download_geojson={downloadGeoJSON}
|
|
89
|
+
get_export_metadata={vi.fn().mockResolvedValue({
|
|
90
|
+
geometry_columns: [
|
|
91
|
+
{ name: "value", encoding: "objects", crs: "EPSG:4326" },
|
|
92
|
+
],
|
|
93
|
+
primary_geometry_column: "value",
|
|
94
|
+
default_geometry_column: "value",
|
|
95
|
+
formats: {
|
|
96
|
+
geojson: {
|
|
97
|
+
available: true,
|
|
98
|
+
reason: null,
|
|
99
|
+
missing_packages: [],
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
})}
|
|
72
103
|
get_column_summaries={vi.fn()}
|
|
73
104
|
get_data_url={vi.fn() as GetDataUrl}
|
|
74
105
|
get_row_ids={vi.fn() as GetRowIds}
|
|
@@ -83,6 +114,26 @@ describe("LoadingDataTableComponent", () => {
|
|
|
83
114
|
screen.getByRole("columnheader", { name: "value" }),
|
|
84
115
|
).toBeInTheDocument();
|
|
85
116
|
});
|
|
117
|
+
fireEvent.click(screen.getByTestId("export-button"));
|
|
118
|
+
await screen.findByRole("dialog", { name: "Export table" });
|
|
119
|
+
await waitFor(() =>
|
|
120
|
+
expect(
|
|
121
|
+
screen.getByRole("button", { name: "Download GeoJSON" }),
|
|
122
|
+
).toBeEnabled(),
|
|
123
|
+
);
|
|
124
|
+
fireEvent.click(screen.getByRole("button", { name: "Download GeoJSON" }));
|
|
125
|
+
await waitFor(() =>
|
|
126
|
+
expect(downloadGeoJSON).toHaveBeenCalledWith({
|
|
127
|
+
format: "geojson",
|
|
128
|
+
geometry_column: "value",
|
|
129
|
+
}),
|
|
130
|
+
);
|
|
131
|
+
expect(downloadAs).not.toHaveBeenCalled();
|
|
132
|
+
fireEvent.click(screen.getByRole("button", { name: "Download CSV" }));
|
|
133
|
+
await waitFor(() =>
|
|
134
|
+
expect(downloadAs).toHaveBeenCalledWith({ format: "csv" }),
|
|
135
|
+
);
|
|
136
|
+
expect(downloadGeoJSON).toHaveBeenCalledTimes(1);
|
|
86
137
|
});
|
|
87
138
|
|
|
88
139
|
it("does not log duplicate keys when a pivot becomes empty", async () => {
|
|
@@ -122,7 +173,9 @@ describe("LoadingDataTableComponent", () => {
|
|
|
122
173
|
showSearch: false,
|
|
123
174
|
value: [] as (number | string | { rowId: string; columnName?: string })[],
|
|
124
175
|
setValue: vi.fn(),
|
|
125
|
-
download_as: vi.fn() as
|
|
176
|
+
download_as: vi.fn() as DownloadAsFunction,
|
|
177
|
+
download_geojson: vi.fn(),
|
|
178
|
+
get_export_metadata: vi.fn(),
|
|
126
179
|
get_column_summaries: vi.fn(),
|
|
127
180
|
get_data_url: vi.fn() as GetDataUrl,
|
|
128
181
|
get_row_ids: vi.fn() as GetRowIds,
|
|
@@ -263,7 +316,9 @@ describe("LoadingDataTableComponent", () => {
|
|
|
263
316
|
showSearch: true,
|
|
264
317
|
value: [] as (number | string | { rowId: string; columnName?: string })[],
|
|
265
318
|
setValue,
|
|
266
|
-
download_as: vi.fn() as
|
|
319
|
+
download_as: vi.fn() as DownloadAsFunction,
|
|
320
|
+
download_geojson: vi.fn(),
|
|
321
|
+
get_export_metadata: vi.fn(),
|
|
267
322
|
get_column_summaries: vi.fn().mockResolvedValue({
|
|
268
323
|
data: null,
|
|
269
324
|
stats: {},
|
|
@@ -379,7 +434,9 @@ describe("static notebook control suppression", () => {
|
|
|
379
434
|
cell_styles: null,
|
|
380
435
|
cell_hover_texts: null,
|
|
381
436
|
}),
|
|
382
|
-
download_as: vi.fn() as
|
|
437
|
+
download_as: vi.fn() as DownloadAsFunction,
|
|
438
|
+
download_geojson: vi.fn(),
|
|
439
|
+
get_export_metadata: vi.fn(),
|
|
383
440
|
get_column_summaries: vi.fn().mockResolvedValue({
|
|
384
441
|
data: null,
|
|
385
442
|
stats: {},
|
|
@@ -12,8 +12,12 @@ import {
|
|
|
12
12
|
} from "react";
|
|
13
13
|
import { z } from "zod";
|
|
14
14
|
import {
|
|
15
|
-
type
|
|
15
|
+
type DownloadAsFunction,
|
|
16
16
|
DownloadAsSchema,
|
|
17
|
+
type DownloadGeoJSON,
|
|
18
|
+
DownloadGeoJSONSchema,
|
|
19
|
+
type GetExportMetadata,
|
|
20
|
+
GetExportMetadataSchema,
|
|
17
21
|
} from "@/components/data-table/schemas";
|
|
18
22
|
import type { FieldTypesWithExternalType } from "@/components/data-table/types";
|
|
19
23
|
import { ReadonlyCode } from "@/components/editor/code/readonly-python-code";
|
|
@@ -82,7 +86,9 @@ type PluginFunctions = {
|
|
|
82
86
|
data: TableData<T>;
|
|
83
87
|
total_rows: number;
|
|
84
88
|
}>;
|
|
85
|
-
download_as:
|
|
89
|
+
download_as: DownloadAsFunction;
|
|
90
|
+
download_geojson: DownloadGeoJSON;
|
|
91
|
+
get_export_metadata: GetExportMetadata;
|
|
86
92
|
get_size_bytes: (opts: Record<string, never>) => Promise<{
|
|
87
93
|
size_bytes?: number | null;
|
|
88
94
|
}>;
|
|
@@ -153,6 +159,8 @@ export const DataFramePlugin = createPlugin<S>("marimo-dataframe")
|
|
|
153
159
|
}),
|
|
154
160
|
),
|
|
155
161
|
download_as: DownloadAsSchema,
|
|
162
|
+
download_geojson: DownloadGeoJSONSchema,
|
|
163
|
+
get_export_metadata: GetExportMetadataSchema,
|
|
156
164
|
get_size_bytes: rpc
|
|
157
165
|
.input(z.object({}))
|
|
158
166
|
.output(z.object({ size_bytes: z.number().nullish() })),
|
|
@@ -174,7 +182,7 @@ interface DataTableProps extends Data, PluginFunctions {
|
|
|
174
182
|
setValue: (value: S) => void;
|
|
175
183
|
host: HTMLElement;
|
|
176
184
|
showDownload: boolean;
|
|
177
|
-
download_as:
|
|
185
|
+
download_as: DownloadAsFunction;
|
|
178
186
|
}
|
|
179
187
|
|
|
180
188
|
const EMPTY: Transformations = {
|
|
@@ -194,6 +202,8 @@ export const DataFrameComponent = memo(
|
|
|
194
202
|
get_column_values,
|
|
195
203
|
search,
|
|
196
204
|
download_as,
|
|
205
|
+
download_geojson,
|
|
206
|
+
get_export_metadata,
|
|
197
207
|
get_size_bytes,
|
|
198
208
|
host,
|
|
199
209
|
}: DataTableProps): JSX.Element => {
|
|
@@ -337,6 +347,8 @@ export const DataFrameComponent = memo(
|
|
|
337
347
|
rowHeaders={row_headers || Arrays.EMPTY}
|
|
338
348
|
showDownload={showDownload}
|
|
339
349
|
download_as={download_as}
|
|
350
|
+
download_geojson={download_geojson}
|
|
351
|
+
get_export_metadata={get_export_metadata}
|
|
340
352
|
get_size_bytes={get_size_bytes}
|
|
341
353
|
showSearch={false}
|
|
342
354
|
showFilters={false}
|
|
@@ -42,6 +42,13 @@ export const DataFrame: StoryObj = {
|
|
|
42
42
|
host={document.body}
|
|
43
43
|
showDownload={false}
|
|
44
44
|
download_as={async () => ({ url: "", filename: "" })}
|
|
45
|
+
download_geojson={async () => ({ url: "", filename: "" })}
|
|
46
|
+
get_export_metadata={async () => ({
|
|
47
|
+
geometry_columns: [],
|
|
48
|
+
primary_geometry_column: null,
|
|
49
|
+
default_geometry_column: null,
|
|
50
|
+
formats: {},
|
|
51
|
+
})}
|
|
45
52
|
get_size_bytes={async () => ({ size_bytes: null })}
|
|
46
53
|
lazy={false}
|
|
47
54
|
/>
|