@marimo-team/islands 0.25.3-dev4 → 0.25.3-dev40
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/{ConnectedDataExplorerComponent-CNSxdKyJ.js → ConnectedDataExplorerComponent-COS3f8io.js} +14 -14
- package/dist/{ErrorBoundary-CuU5CuVr.js → ErrorBoundary-C7__jAZL.js} +9 -9
- package/dist/{abnfDiagram-VCTEODGH-DQDjNXt3.js → abnfDiagram-VCTEODGH-DQ4bKxYa.js} +2 -2
- package/dist/any-language-editor-DQItQSua.js +75 -0
- package/dist/{architectureDiagram-5GKGNRK7-Nmls7Wxn.js → architectureDiagram-5GKGNRK7-BA7-SI7Q.js} +1 -1
- package/dist/{arrays-CN1djC4k.js → arrays-R6JsImiT.js} +8 -0
- package/dist/{button-CvjkwdSk.js → button-BxB3RDjN.js} +1 -1
- package/dist/{capabilities-CQ5IjhzL.js → capabilities-BgR0osfN.js} +2 -2
- package/dist/{chat-ui-BDpymeoe.js → chat-ui-DGpBSln7.js} +1152 -1153
- package/dist/{check-EDwSwP89.js → check-C209OH-W.js} +1 -1
- package/dist/{chunk-IMKFNOWR-CoIFEo-1.js → chunk-IMKFNOWR-DPY3kqOV.js} +1 -1
- package/dist/{chunk-SHT3W25Y-lHiGx3ZC.js → chunk-SHT3W25Y-C14Ikodf.js} +1 -1
- package/dist/{chunk-SVP7TREG-BvsHIrY_.js → chunk-SVP7TREG-BKu0HO4I.js} +1 -1
- package/dist/{chunk-TICWLB2K-BC8YIm_n.js → chunk-TICWLB2K-iJDDWGtE.js} +1 -1
- package/dist/{chunk-YOKDWASO-DPZk8vhS.js → chunk-YOKDWASO-DKplTrBY.js} +20 -20
- package/dist/{classDiagram-ZZMXUADV-Bt98lkkv.js → classDiagram-ZZMXUADV-HuJXQI0F.js} +2 -2
- package/dist/{classDiagram-v2-VYDZK3BY-D9bfbcC3.js → classDiagram-v2-VYDZK3BY-CpmmpTbC.js} +2 -2
- package/dist/{code-visibility-Lu0dkrFO.js → code-visibility-DO4MlViW.js} +17 -17
- package/dist/{common-DiWGWDIM.js → common-Ci7TC3e0.js} +6683 -6454
- package/dist/{copy-icon-Ft7t9bqz.js → copy-icon-L-mFk59e.js} +3 -3
- package/dist/{cynefinDiagram-5FMLGOSQ-CgWra7wt.js → cynefinDiagram-5FMLGOSQ-uh0puAW2.js} +1 -1
- package/dist/{dagre-GXQ25YYZ-CFZ8-35c.js → dagre-GXQ25YYZ-4LBeAAMz.js} +1 -1
- package/dist/{data-grid-overlay-editor-ga7ytgbj.js → data-grid-overlay-editor-DpH_AKdC.js} +5 -5
- package/dist/{data-grid-types-BH_r7g7l.js → data-grid-types-0PJ-9N5n.js} +1 -1
- package/dist/{diagram-S7CK7UJ4-lCNQrW7d.js → diagram-S7CK7UJ4-BXPmZ2sX.js} +1 -1
- package/dist/{diagram-UQ7AKVKN-CQPQ6pP_.js → diagram-UQ7AKVKN--Xz9UNpC.js} +1 -1
- package/dist/{diagram-VX7I27RA-Cf3b0llz.js → diagram-VX7I27RA-rt8VIRNT.js} +1 -1
- package/dist/{diagram-Z3DM3KII-Bt4QLrpn.js → diagram-Z3DM3KII-Cs1GqfDD.js} +1 -1
- package/dist/{dialog-CEBFZNyc.js → dialog-CDbPtWUD.js} +25 -25
- package/dist/{dist-DpBpA_n1.js → dist-Lm-D-Vr6.js} +3 -3
- package/dist/{dropdown-menu-CP_qCYHC.js → dropdown-menu-ugNoZmKx.js} +828 -828
- package/dist/{ebnfDiagram-PWID7BFC-D3NAyPFY.js → ebnfDiagram-PWID7BFC-CgTsgbmZ.js} +2 -2
- package/dist/{erDiagram-RLTQ6QDP-Tlu09_Nm.js → erDiagram-RLTQ6QDP-CFoADGzT.js} +1 -1
- package/dist/{error-banner-Da600cwz.js → error-banner-DMIrHA-R.js} +27 -22
- package/dist/{cells-C8iCYvLU.js → error-entries-D576fzV9.js} +1378 -1322
- package/dist/{feedback-button-DQA-pzxi.js → feedback-button-BV5AJ3nl.js} +122 -123
- package/dist/{flowDiagram-HODETNUW-CAYbpJmV.js → flowDiagram-HODETNUW-BM2lvKuH.js} +2 -2
- package/dist/{formats-BGh2thRc.js → formats-DIVMVjeV.js} +3 -3
- package/dist/{ganttDiagram-EL5Y4UJY-BQT16JSc.js → ganttDiagram-EL5Y4UJY-Dh_4cBJZ.js} +2 -2
- package/dist/{glide-data-editor-Bv75MH6g.js → glide-data-editor-CRK-O0KE.js} +370 -368
- package/dist/{glide-utils-DJyqh0EM.js → glide-utils-Q2ZsuA3h.js} +16 -16
- package/dist/{globals-EDQGFIvi.js → globals-H_ILLmr7.js} +3 -3
- package/dist/{highlighted-body-KPVGNVTW-zwjq21tf.js → highlighted-body-KPVGNVTW-3Ziwfz-V.js} +2 -2
- package/dist/{html-to-image-CZY1pIZH.js → html-to-image-D3m5Ob5H.js} +69 -69
- package/dist/{infoDiagram-27XIBGKW-XrDxhr0x.js → infoDiagram-27XIBGKW-Crwjiywp.js} +1 -1
- package/dist/{ishikawaDiagram-5VMMS53U-CD1muzov.js → ishikawaDiagram-5VMMS53U-D4US2ShV.js} +1 -1
- package/dist/{kanban-definition-UXKFOSKX-Dg9W142_.js → kanban-definition-UXKFOSKX-Bk097CUX.js} +1 -1
- package/dist/{label-BDKF3Afc.js → label-CuEjBqYP.js} +2 -2
- package/dist/{linear-Cnaq3Tfb.js → linear-DWERh7eI.js} +121 -73
- package/dist/{loader-BQOfjTIJ.js → loader-Bm8cy3e8.js} +12 -12
- package/dist/main.js +2696 -2646
- package/dist/{mermaid-CiAXjLpA.js → mermaid-6EkeBlhr.js} +38 -38
- package/dist/mermaid-HWGCJPDP-Du3-HPQA.js +2 -0
- package/dist/{mindmap-definition-YA3MSWOX-DgCidgYJ.js → mindmap-definition-YA3MSWOX-DmORzfXQ.js} +1 -1
- package/dist/{number-overlay-editor-BCP3MxhC.js → number-overlay-editor-BQ2UqdED.js} +3 -3
- package/dist/{objects-CzUgoMIF.js → objects-DCmMvZtn.js} +1 -3
- package/dist/{once-BtE-wKRL.js → once-eG8Vmvjd.js} +1 -1
- package/dist/{pegDiagram-XKGWAZYB-coOYYKB6.js → pegDiagram-XKGWAZYB-DT5TL02S.js} +2 -2
- package/dist/{pieDiagram-E7YTZNPT-BnXTc3bj.js → pieDiagram-E7YTZNPT-B1NwGcuH.js} +1 -1
- package/dist/precisionRound-DhAzeRcm.js +49 -0
- package/dist/{process-output-e4jj9Ijt.js → process-output-C6qc7hPS.js} +5 -5
- package/dist/{quadrantDiagram-AXDQQJYC-Ccw0xMjN.js → quadrantDiagram-AXDQQJYC-Blrm7V7_.js} +1 -1
- package/dist/{railroadDiagram-O6MQD6OU-B28-u83n.js → railroadDiagram-O6MQD6OU-BxoycdEu.js} +2 -2
- package/dist/{react-vega-BED0DjiB.js → react-vega-C4T9Rb_w.js} +12284 -12270
- package/dist/{requirementDiagram-BXWQKSXE-CdYCTMu0.js → requirementDiagram-BXWQKSXE-DHq_eRrm.js} +1 -1
- package/dist/{reveal-component-DO5BJB9l.js → reveal-component-Cs-0GwxY.js} +10 -10
- package/dist/{select-BButFi-d.js → select-D2iqeJYk.js} +200 -200
- package/dist/{sequenceDiagram-WJ2MYXX4-CM3aRAe_.js → sequenceDiagram-WJ2MYXX4-Ba11BAOu.js} +1 -1
- package/dist/{spec-BwarBpUJ.js → spec-BCCcvWPS.js} +4 -4
- package/dist/{stateDiagram-D77RDMKH-DOZUMVj8.js → stateDiagram-D77RDMKH-OHQbvkbP.js} +2 -2
- package/dist/{stateDiagram-v2-MP3YSRHH-CISYxma6.js → stateDiagram-v2-MP3YSRHH-BlIquvFJ.js} +2 -2
- package/dist/style.css +1 -1
- package/dist/{swimlanes-42K2YHIH-BkSppxp8.js → swimlanes-42K2YHIH-DSlitS4s.js} +1 -1
- package/dist/{swimlanesDiagram-VR7AAH4N-c8G5pGxC.js → swimlanesDiagram-VR7AAH4N-ClwuzFNO.js} +2 -2
- package/dist/{swiper-component-DKntyn8p.js → swiper-component-DfhRvAFi.js} +2 -2
- package/dist/{time-BieKwdYS.js → time-BaMbJs4z.js} +18 -18
- package/dist/{timeline-definition-24CTP7MA-CaMQayqK.js → timeline-definition-24CTP7MA-hAzc5VkT.js} +1 -1
- package/dist/toDate-CSY-j1Fi.js +1185 -0
- package/dist/{toast-B9UMEi8u.js → toast-DxVfYlCp.js} +222 -222
- package/dist/{tooltip-BO1REbqs.js → tooltip-BCntm-jf.js} +52 -52
- package/dist/{use-restore-focus-BEM7sFWl.js → use-restore-focus-BhXhsNLV.js} +2 -2
- package/dist/{useAsyncData-Deh5kszT.js → useAsyncData-BbuzBAUp.js} +15 -14
- package/dist/{useDateFormatter-D8XruNGG.js → useDateFormatter-6AHbIR-J.js} +2 -2
- package/dist/{useDeepCompareMemoize-DXwzRINI.js → useDeepCompareMemoize-da5A3c-Y.js} +1 -1
- package/dist/{useIframeCapabilities-DBETvvPl.js → useIframeCapabilities-BkdeVdXC.js} +1 -1
- package/dist/{useLifecycle-j7D9uD1o.js → useLifecycle-C3nT1kwI.js} +3 -3
- package/dist/{useTheme-CRnjkku0.js → useTheme-BC8Lof0B.js} +87 -87
- package/dist/{vega-component-DG29TAB7.js → vega-component-Df45z17m.js} +147 -147
- package/dist/{vega-embed-container-BNHE18G9.js → vega-embed-container--LYZqguO.js} +2 -2
- package/dist/{vega-loader.browser-DNST6uVf.js → vega-loader.browser-B9Pz0EGP.js} +298 -507
- package/dist/{vennDiagram-4TSXK5OY-CF8pfaK3.js → vennDiagram-4TSXK5OY-CINKaJgr.js} +1 -1
- package/dist/{wardleyDiagram-VM6X3IG4-dGPu39d-.js → wardleyDiagram-VM6X3IG4-KG2tlXg_.js} +1 -1
- package/dist/{xychartDiagram-S5SC5T6Z-CP6-9q56.js → xychartDiagram-S5SC5T6Z-XIH5cwgI.js} +2 -2
- package/package.json +4 -2
- package/src/__tests__/fixtures/{pair-preview.ts → pair-config.ts} +3 -2
- package/src/__tests__/mount.test.ts +28 -8
- package/src/components/app-config/ai-config.tsx +5 -1
- package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
- package/src/components/data-table/__tests__/export-actions.test.tsx +329 -6
- package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
- package/src/components/data-table/columns.tsx +2 -1
- package/src/components/data-table/date-popover.tsx +6 -4
- package/src/components/data-table/export-actions.tsx +192 -116
- package/src/components/data-table/row-viewer-panel/row-viewer.tsx +2 -2
- package/src/components/data-table/schemas.ts +14 -36
- package/src/components/editor/Output.tsx +9 -2
- package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
- package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +80 -252
- package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +186 -34
- package/src/components/editor/actions/pair-with-agent-commands.ts +42 -51
- package/src/components/editor/actions/pair-with-agent-modal.tsx +96 -36
- package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
- package/src/components/editor/chrome/panels/error-panel.tsx +1 -0
- package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +366 -0
- package/src/components/editor/chrome/wrapper/footer-item.tsx +32 -24
- package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +237 -0
- package/src/components/editor/chrome/wrapper/footer.tsx +2 -0
- package/src/components/editor/connections/components.tsx +1 -1
- package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
- package/src/components/editor/controls/command-palette.tsx +22 -10
- package/src/components/editor/dynamic-favicon.tsx +21 -40
- package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +537 -0
- package/src/components/editor/errors/send-error-report-button.tsx +69 -0
- package/src/components/editor/output/MarimoErrorOutput.tsx +21 -5
- package/src/components/editor/output/MarimoTracebackOutput.tsx +36 -21
- package/src/components/editor/output/__tests__/ansi-reduce.test.ts +18 -0
- package/src/components/editor/output/ansi-reduce.ts +6 -0
- package/src/components/pages/home-page.tsx +4 -4
- package/src/components/tracing/tracing.tsx +2 -2
- package/src/core/attachments/__tests__/toast.test.tsx +58 -0
- package/src/core/attachments/state.ts +18 -0
- package/src/core/attachments/toast.ts +15 -0
- package/src/core/cells/__tests__/cells.test.ts +52 -0
- package/src/core/cells/__tests__/error-report.test.ts +139 -0
- package/src/core/cells/__tests__/notebook-outline.test.ts +113 -0
- package/src/core/cells/__tests__/session.test.ts +50 -1
- package/src/core/cells/cells.ts +2 -1
- package/src/core/cells/error-report.ts +63 -0
- package/src/core/cells/pending-delete-service.ts +2 -6
- package/src/core/cells/session.ts +28 -1
- package/src/core/config/pair.ts +5 -3
- package/src/core/datasets/__tests__/data-source-discovery.test.ts +4 -4
- package/src/core/datasets/data-source-discovery.ts +4 -6
- package/src/core/edit-app.tsx +2 -2
- package/src/core/host/AGENTS.md +5 -0
- package/src/core/host/__tests__/attachments.test.ts +57 -0
- package/src/core/host/__tests__/document.test.ts +124 -0
- package/src/core/host/__tests__/events.test.ts +46 -0
- package/src/core/host/__tests__/fixtures.ts +39 -0
- package/src/core/host/__tests__/notebook-client.test.ts +433 -0
- package/src/core/host/__tests__/protocol.test.ts +15 -0
- package/src/core/host/__tests__/stream.test.ts +209 -0
- package/src/core/host/connection.ts +81 -0
- package/src/core/host/events.ts +61 -0
- package/src/core/host/notebook-client.ts +290 -0
- package/src/core/host/protocol.ts +11 -6
- package/src/core/host/resources/attachments.ts +19 -0
- package/src/core/host/resources/document.ts +101 -0
- package/src/core/host/resources/resource.ts +47 -0
- package/src/core/host/stream.ts +211 -0
- package/src/core/hotkeys/actions.ts +2 -4
- package/src/core/network/connection.ts +4 -2
- package/src/core/run-app.tsx +2 -2
- package/src/core/runtime/__tests__/runtime.test.ts +55 -11
- package/src/core/runtime/runtime.ts +18 -5
- package/src/core/runtime/types.ts +2 -0
- package/src/core/websocket/__tests__/{useMarimoKernelConnection.test.ts → connection-state.test.ts} +15 -17
- package/src/core/websocket/__tests__/{useWebSocket.test.ts → createConnectionTransport.test.ts} +1 -1
- package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +170 -0
- package/src/core/websocket/__tests__/{useMarimoKernelConnection.hook.test.tsx → useNotebookSession.hook.test.tsx} +181 -65
- package/src/core/websocket/connection-state.ts +70 -0
- package/src/core/websocket/state.ts +7 -0
- package/src/core/websocket/transports/__tests__/sse.test.ts +1 -4
- package/src/core/websocket/transports/create.ts +31 -0
- package/src/core/websocket/transports/transport.ts +12 -23
- package/src/core/websocket/types.ts +9 -39
- package/src/core/websocket/useNotebookClient.ts +88 -0
- package/src/core/websocket/{useMarimoKernelConnection.tsx → useNotebookSession.tsx} +59 -225
- package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
- package/src/hooks/useAsyncData.ts +3 -3
- package/src/hooks/useDataSourceDiscovery.ts +6 -4
- package/src/hooks/useElapsedTime.ts +3 -3
- package/src/mount.tsx +7 -3
- package/src/plugins/core/registerReactComponent.tsx +1 -1
- package/src/plugins/impl/DropdownPlugin.tsx +2 -6
- package/src/plugins/impl/FormPlugin.tsx +1 -1
- package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +84 -0
- package/src/plugins/impl/code/any-language-editor.tsx +2 -2
- package/src/plugins/impl/data-editor/glide-data-editor.tsx +8 -5
- package/src/plugins/impl/data-editor/glide-utils.ts +3 -1
- package/src/plugins/impl/vega/make-selectable.ts +3 -6
- package/src/utils/__tests__/arrays.test.ts +35 -0
- package/src/utils/__tests__/numbers.test.ts +48 -1
- package/src/utils/__tests__/objects.test.ts +21 -0
- package/src/utils/arrays.ts +10 -0
- package/src/utils/numbers.ts +28 -17
- package/src/utils/objects.ts +3 -7
- package/src/utils/once.ts +19 -19
- package/dist/any-language-editor-B9REGlD9.js +0 -76
- package/dist/error-entries-CzoYkIQZ.js +0 -56
- package/dist/mermaid-HWGCJPDP-ByCsZnYF.js +0 -2
- package/dist/precisionRound-DMIVQrt2.js +0 -97
- package/dist/toDate-BNJf01u1.js +0 -703
- package/src/core/websocket/useWebSocket.tsx +0 -88
|
@@ -3,7 +3,7 @@ import { t } from "./src-CA_m4ONl.js";
|
|
|
3
3
|
import { n } from "./chunk-Y2CYZVJY-DKKS1VyV.js";
|
|
4
4
|
import { $ as r, H as i, K as a, U as o, a as s, b as c, c as l, et as u, f as d, nt as f, tt as p, v as m, w as h, y as g } from "./chunk-DU6HZSFF-BwiyaNpg.js";
|
|
5
5
|
import { t as _ } from "./rough.esm-CtV9AnV1.js";
|
|
6
|
-
import { p as v } from "./mermaid-
|
|
6
|
+
import { p as v } from "./mermaid-6EkeBlhr.js";
|
|
7
7
|
//#region ../node_modules/.pnpm/khroma@2.1.0/node_modules/khroma/dist/methods/transparentize.js
|
|
8
8
|
var y = (e, t) => p(e, "a", -t), b = 1e-10;
|
|
9
9
|
function x(e, t) {
|
|
@@ -2,7 +2,7 @@ import { i as e } from "./chunk-75Z2AOVW-D7gxIs2Q.js";
|
|
|
2
2
|
import { n as t } from "./src-CA_m4ONl.js";
|
|
3
3
|
import { n } from "./chunk-Y2CYZVJY-DKKS1VyV.js";
|
|
4
4
|
import { D as r, H as i, K as a, U as o, a as s, b as c, c as l, v as u, w as d, x as f, y as p } from "./chunk-DU6HZSFF-BwiyaNpg.js";
|
|
5
|
-
import { p as m } from "./mermaid-
|
|
5
|
+
import { p as m } from "./mermaid-6EkeBlhr.js";
|
|
6
6
|
import { n as h } from "./mermaid-parser.core-DxuSfYwJ.js";
|
|
7
7
|
import { t as g } from "./chunk-JWPE2WC7-DJvv1t8U.js";
|
|
8
8
|
//#region ../node_modules/.pnpm/mermaid@11.17.2/node_modules/mermaid/dist/chunks/mermaid.core/wardleyDiagram-VM6X3IG4.mjs
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as e } from "./linear-
|
|
1
|
+
import { t as e } from "./linear-DWERh7eI.js";
|
|
2
2
|
import { n as t } from "./ordinal-c7efuCSS.js";
|
|
3
3
|
import { t as n } from "./range-BAIvbVHp.js";
|
|
4
4
|
import { q as r } from "./cubic-OaEBhQPK.js";
|
|
@@ -9,7 +9,7 @@ import { n as s } from "./chunk-Y2CYZVJY-DKKS1VyV.js";
|
|
|
9
9
|
import { D as c, H as l, K as u, U as d, a as f, b as p, c as m, f as h, v as g, w as _, y as v, z as y } from "./chunk-DU6HZSFF-BwiyaNpg.js";
|
|
10
10
|
import "./chunk-PWAF6VOD-CQw5qATZ.js";
|
|
11
11
|
import { t as b } from "./chunk-GMAD6QVW-BhaJx0X3.js";
|
|
12
|
-
import { p as x } from "./mermaid-
|
|
12
|
+
import { p as x } from "./mermaid-6EkeBlhr.js";
|
|
13
13
|
//#region ../node_modules/.pnpm/d3-scale@4.0.2/node_modules/d3-scale/src/band.js
|
|
14
14
|
function S() {
|
|
15
15
|
var e = t().unknown(void 0), r = e.domain, i = e.range, o = 0, s = 1, c, l, u = !1, d = 0, f = 0, p = .5;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marimo-team/islands",
|
|
3
|
-
"version": "0.25.3-
|
|
3
|
+
"version": "0.25.3-dev40",
|
|
4
4
|
"main": "dist/main.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -152,6 +152,7 @@
|
|
|
152
152
|
"typescript-memoize": "^1.1.1",
|
|
153
153
|
"use-acp": "0.2.6",
|
|
154
154
|
"use-resize-observer": "^9.1.0",
|
|
155
|
+
"vega": "6.2.0",
|
|
155
156
|
"vega-lite": "6.4.3",
|
|
156
157
|
"vega-loader": "^5.1.3",
|
|
157
158
|
"vega-parser": "^7.1.3",
|
|
@@ -185,7 +186,8 @@
|
|
|
185
186
|
"build-storybook": "storybook build",
|
|
186
187
|
"sync-katex-css": "node --experimental-strip-types scripts/sync-katex-css.ts",
|
|
187
188
|
"codegen": "node --experimental-strip-types scripts/generate-host-protocol.ts",
|
|
188
|
-
"codegen:check": "node --experimental-strip-types scripts/generate-host-protocol.ts --check"
|
|
189
|
+
"codegen:check": "node --experimental-strip-types scripts/generate-host-protocol.ts --check",
|
|
190
|
+
"doctor": "npx react-doctor@latest"
|
|
189
191
|
},
|
|
190
192
|
"peerDependencies": {
|
|
191
193
|
"react": "^17 || ^18 || ^19",
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import {
|
|
5
|
+
import { pairConfigSchema } from "@/core/config/pair";
|
|
6
6
|
|
|
7
7
|
// Read the Python-owned resource only in tests to check the shared contract.
|
|
8
|
-
export const
|
|
8
|
+
export const PAIR_CONFIG = pairConfigSchema.parse({
|
|
9
9
|
command: "uvx marimo@latest",
|
|
10
|
+
hostEnabled: true,
|
|
10
11
|
templates: JSON.parse(
|
|
11
12
|
readFileSync(
|
|
12
13
|
path.resolve(
|
|
@@ -6,10 +6,11 @@ import { initialLayoutState, layoutStateAtom } from "@/core/layout/state";
|
|
|
6
6
|
import { kioskModeAtom } from "@/core/mode";
|
|
7
7
|
import { connectionAtom } from "@/core/network/connection";
|
|
8
8
|
import { store } from "@/core/state/jotai";
|
|
9
|
+
import { runtimeConfigAtom } from "@/core/runtime/config";
|
|
9
10
|
import { isStaticNotebook } from "@/core/static/static-state";
|
|
10
11
|
import { ConnectionState } from "@/core/websocket/types";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
12
|
+
import { pairConfigAtom } from "@/core/config/pair";
|
|
13
|
+
import { PAIR_CONFIG } from "./fixtures/pair-config";
|
|
13
14
|
import { mount, visibleForTesting } from "../mount";
|
|
14
15
|
|
|
15
16
|
// Mock React DOM
|
|
@@ -84,23 +85,42 @@ describe("mount", () => {
|
|
|
84
85
|
mockElement,
|
|
85
86
|
);
|
|
86
87
|
|
|
87
|
-
it("hydrates the server's Pair
|
|
88
|
-
const error = mountRead({
|
|
88
|
+
it("hydrates the server's Pair configuration", () => {
|
|
89
|
+
const error = mountRead({ pairConfig: PAIR_CONFIG });
|
|
89
90
|
|
|
90
91
|
expect(error).toBeUndefined();
|
|
91
|
-
expect(store.get(
|
|
92
|
+
expect(store.get(pairConfigAtom)).toEqual(PAIR_CONFIG);
|
|
92
93
|
});
|
|
93
94
|
|
|
94
|
-
it("
|
|
95
|
-
store.set(
|
|
95
|
+
it("clears Pair configuration when the server omits it", () => {
|
|
96
|
+
store.set(pairConfigAtom, PAIR_CONFIG);
|
|
96
97
|
|
|
97
98
|
const error = mountRead();
|
|
98
99
|
|
|
99
100
|
expect(error).toBeUndefined();
|
|
100
|
-
expect(store.get(
|
|
101
|
+
expect(store.get(pairConfigAtom)).toBeUndefined();
|
|
101
102
|
});
|
|
102
103
|
|
|
103
104
|
describe("connection state initialization", () => {
|
|
105
|
+
it("passes the host notebook identity to the local runtime", () => {
|
|
106
|
+
mount({ ...baseOptions, notebookId: "nb_local" }, mockElement);
|
|
107
|
+
expect(store.get(runtimeConfigAtom).notebookId).toBe("nb_local");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("uses a remote runtime's own notebook identity", () => {
|
|
111
|
+
mount(
|
|
112
|
+
{
|
|
113
|
+
...baseOptions,
|
|
114
|
+
notebookId: "nb_local",
|
|
115
|
+
runtimeConfig: [
|
|
116
|
+
{ url: "https://remote.example", notebookId: "nb_remote" },
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
mockElement,
|
|
120
|
+
);
|
|
121
|
+
expect(store.get(runtimeConfigAtom).notebookId).toBe("nb_remote");
|
|
122
|
+
});
|
|
123
|
+
|
|
104
124
|
it("should set connection to CONNECTING when runtimeConfig has lazy=false", () => {
|
|
105
125
|
mount(
|
|
106
126
|
{
|
|
@@ -48,6 +48,7 @@ import { DEFAULT_AI_MODEL, type UserConfig } from "@/core/config/config-schema";
|
|
|
48
48
|
import { isWasm } from "@/core/wasm/utils";
|
|
49
49
|
import { cn } from "@/utils/cn";
|
|
50
50
|
import { Events } from "@/utils/events";
|
|
51
|
+
import { Objects } from "@/utils/objects";
|
|
51
52
|
import { Strings } from "@/utils/strings";
|
|
52
53
|
import { AIModelDropdown, getProviderLabel } from "../ai/ai-model-dropdown";
|
|
53
54
|
import {
|
|
@@ -681,7 +682,10 @@ export const CustomProvidersConfig: React.FC<AiConfigProps> = ({
|
|
|
681
682
|
};
|
|
682
683
|
|
|
683
684
|
const removeProvider = (providerName: string) => {
|
|
684
|
-
const
|
|
685
|
+
const rest = Objects.filter(
|
|
686
|
+
customProviders,
|
|
687
|
+
(_, name) => name !== providerName,
|
|
688
|
+
);
|
|
685
689
|
// Reset to clear nested dirty state, then set new value
|
|
686
690
|
form.resetField("ai.custom_providers");
|
|
687
691
|
form.setValue("ai.custom_providers", rest, { shouldDirty: true });
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
import { render, screen } from "@testing-library/react";
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
import { I18nProvider } from "react-aria";
|
|
5
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
6
|
+
import { DatePopover } from "../date-popover";
|
|
7
|
+
|
|
8
|
+
vi.mock("@/components/ui/tooltip", () => ({
|
|
9
|
+
Tooltip: ({ content }: { content: ReactNode }) => content,
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
vi.restoreAllMocks();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe("DatePopover", () => {
|
|
17
|
+
it.each([0, 123])(
|
|
18
|
+
"updates the local timezone and time on rerender with %i milliseconds",
|
|
19
|
+
(milliseconds) => {
|
|
20
|
+
const NativeDateTimeFormat = Intl.DateTimeFormat;
|
|
21
|
+
let systemTimezone = "America/Los_Angeles";
|
|
22
|
+
vi.spyOn(Intl, "DateTimeFormat").mockImplementation(
|
|
23
|
+
function (locales, options) {
|
|
24
|
+
return new NativeDateTimeFormat(locales, {
|
|
25
|
+
...options,
|
|
26
|
+
timeZone: options?.timeZone ?? systemTimezone,
|
|
27
|
+
});
|
|
28
|
+
},
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const date = new Date("2026-01-15T12:00:00Z");
|
|
32
|
+
date.setUTCMilliseconds(milliseconds);
|
|
33
|
+
const content = () => (
|
|
34
|
+
<I18nProvider locale="en-US">
|
|
35
|
+
<DatePopover date={date} type="datetime">
|
|
36
|
+
timestamp
|
|
37
|
+
</DatePopover>
|
|
38
|
+
</I18nProvider>
|
|
39
|
+
);
|
|
40
|
+
const { rerender } = render(content());
|
|
41
|
+
const previousTime =
|
|
42
|
+
screen.getByText(systemTimezone).nextElementSibling?.textContent;
|
|
43
|
+
const utcTime = screen.getByText("UTC").nextElementSibling?.textContent;
|
|
44
|
+
|
|
45
|
+
systemTimezone = "Asia/Tokyo";
|
|
46
|
+
rerender(content());
|
|
47
|
+
|
|
48
|
+
const expected = new NativeDateTimeFormat("en-US", {
|
|
49
|
+
timeZone: systemTimezone,
|
|
50
|
+
...(milliseconds
|
|
51
|
+
? {
|
|
52
|
+
year: "numeric",
|
|
53
|
+
month: "2-digit",
|
|
54
|
+
day: "2-digit",
|
|
55
|
+
hour: "2-digit",
|
|
56
|
+
minute: "2-digit",
|
|
57
|
+
second: "2-digit",
|
|
58
|
+
fractionalSecondDigits: 3,
|
|
59
|
+
}
|
|
60
|
+
: { dateStyle: "long", timeStyle: "medium" }),
|
|
61
|
+
}).format(date);
|
|
62
|
+
expect(screen.queryByText("America/Los_Angeles")).toBeNull();
|
|
63
|
+
expect(
|
|
64
|
+
screen.getByText(systemTimezone).nextElementSibling?.textContent,
|
|
65
|
+
).toBe(expected);
|
|
66
|
+
expect(expected).not.toBe(previousTime);
|
|
67
|
+
expect(screen.getByText("UTC").nextElementSibling?.textContent).toBe(
|
|
68
|
+
utcTime,
|
|
69
|
+
);
|
|
70
|
+
},
|
|
71
|
+
);
|
|
72
|
+
});
|
|
@@ -54,6 +54,7 @@ function renderExportActions(
|
|
|
54
54
|
props: Partial<ExportActionProps> = {},
|
|
55
55
|
sizeLimit: { limitBytes: number; unavailableMessage: string } | null = null,
|
|
56
56
|
mode: AppMode = "edit",
|
|
57
|
+
strictMode = false,
|
|
57
58
|
) {
|
|
58
59
|
const store = createStore();
|
|
59
60
|
store.set(downloadSizeLimitAtom, sizeLimit);
|
|
@@ -66,7 +67,7 @@ function renderExportActions(
|
|
|
66
67
|
</TooltipProvider>
|
|
67
68
|
</Provider>
|
|
68
69
|
);
|
|
69
|
-
const result = render(view(props));
|
|
70
|
+
const result = render(view(props), { reactStrictMode: strictMode });
|
|
70
71
|
return {
|
|
71
72
|
...result,
|
|
72
73
|
rerenderExportActions: (nextProps: Partial<ExportActionProps>) =>
|
|
@@ -284,7 +285,7 @@ describe("ExportActions dialog", () => {
|
|
|
284
285
|
await waitFor(() => {
|
|
285
286
|
expect(downloadByURL).toHaveBeenCalled();
|
|
286
287
|
});
|
|
287
|
-
expect(getExportMetadata).toHaveBeenCalledTimes(
|
|
288
|
+
expect(getExportMetadata).toHaveBeenCalledTimes(3);
|
|
288
289
|
expect(
|
|
289
290
|
screen.queryByText("GeoParquet export requires pyarrow."),
|
|
290
291
|
).not.toBeInTheDocument();
|
|
@@ -310,10 +311,10 @@ describe("ExportActions dialog", () => {
|
|
|
310
311
|
mocks.handleInstallPackages.mock.calls[0][1]();
|
|
311
312
|
});
|
|
312
313
|
await waitFor(() => {
|
|
313
|
-
expect(getExportMetadata).toHaveBeenCalledTimes(
|
|
314
|
+
expect(getExportMetadata).toHaveBeenCalledTimes(3);
|
|
314
315
|
});
|
|
315
316
|
expect(
|
|
316
|
-
screen.
|
|
317
|
+
await screen.findByRole("button", { name: "Install pyarrow" }),
|
|
317
318
|
).toBeInTheDocument();
|
|
318
319
|
expect(
|
|
319
320
|
screen.getByRole("button", { name: "Download GeoParquet" }),
|
|
@@ -530,6 +531,328 @@ describe("ExportActions dialog", () => {
|
|
|
530
531
|
expect(downloadByURL).not.toHaveBeenCalled();
|
|
531
532
|
});
|
|
532
533
|
|
|
534
|
+
async function startGeoParquetInstall(strictMode = false) {
|
|
535
|
+
const getExportMetadata = vi.fn().mockResolvedValue(geometryMetadata);
|
|
536
|
+
downloadAs.mockResolvedValueOnce({
|
|
537
|
+
url: "",
|
|
538
|
+
filename: "",
|
|
539
|
+
error: "GeoParquet export requires pyarrow.",
|
|
540
|
+
missing_packages: ["pyarrow"],
|
|
541
|
+
});
|
|
542
|
+
const { rerenderExportActions, unmount } = renderExportActions(
|
|
543
|
+
{ getExportMetadata, metadataSource: "first" },
|
|
544
|
+
null,
|
|
545
|
+
"edit",
|
|
546
|
+
strictMode,
|
|
547
|
+
);
|
|
548
|
+
const dialog = await openDialog();
|
|
549
|
+
fireEvent.click(
|
|
550
|
+
await screen.findByRole("button", { name: "Download GeoParquet" }),
|
|
551
|
+
);
|
|
552
|
+
fireEvent.click(
|
|
553
|
+
await screen.findByRole("button", { name: "Install pyarrow" }),
|
|
554
|
+
);
|
|
555
|
+
return { dialog, getExportMetadata, rerenderExportActions, unmount };
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
it("does not retry with a geometry choice after the final geometry disappears", async () => {
|
|
559
|
+
const { getExportMetadata } = await startGeoParquetInstall();
|
|
560
|
+
getExportMetadata.mockResolvedValue({
|
|
561
|
+
geometry_columns: [],
|
|
562
|
+
primary_geometry_column: null,
|
|
563
|
+
default_geometry_column: null,
|
|
564
|
+
formats: {},
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
await act(async () => mocks.handleInstallPackages.mock.calls[0][1]());
|
|
568
|
+
|
|
569
|
+
expect(downloadAs).toHaveBeenCalledTimes(1);
|
|
570
|
+
expect(downloadByURL).not.toHaveBeenCalled();
|
|
571
|
+
expect(
|
|
572
|
+
await screen.findByText(
|
|
573
|
+
"The selected geometry is no longer available. Choose another geometry to export.",
|
|
574
|
+
),
|
|
575
|
+
).toBeInTheDocument();
|
|
576
|
+
fireEvent.click(screen.getByRole("button", { name: "Download Parquet" }));
|
|
577
|
+
await waitFor(() => expect(downloadByURL).toHaveBeenCalledTimes(1));
|
|
578
|
+
expect(downloadAs).toHaveBeenLastCalledWith({ format: "parquet" });
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
it("does not export when installation finishes after the widget unmounts", async () => {
|
|
582
|
+
const { getExportMetadata, unmount } = await startGeoParquetInstall();
|
|
583
|
+
unmount();
|
|
584
|
+
|
|
585
|
+
await act(async () => mocks.handleInstallPackages.mock.calls[0][1]());
|
|
586
|
+
|
|
587
|
+
expect(getExportMetadata).toHaveBeenCalledTimes(1);
|
|
588
|
+
expect(downloadAs).toHaveBeenCalledTimes(1);
|
|
589
|
+
expect(downloadByURL).not.toHaveBeenCalled();
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
it("does not export when post-install metadata resolves after unmount", async () => {
|
|
593
|
+
const { getExportMetadata, unmount } = await startGeoParquetInstall();
|
|
594
|
+
let resolveCheck: (value: ExportMetadata) => void = () => undefined;
|
|
595
|
+
getExportMetadata.mockImplementationOnce(
|
|
596
|
+
() =>
|
|
597
|
+
new Promise<ExportMetadata>((resolve) => {
|
|
598
|
+
resolveCheck = resolve;
|
|
599
|
+
}),
|
|
600
|
+
);
|
|
601
|
+
await act(async () => mocks.handleInstallPackages.mock.calls[0][1]());
|
|
602
|
+
expect(getExportMetadata).toHaveBeenCalledTimes(2);
|
|
603
|
+
unmount();
|
|
604
|
+
|
|
605
|
+
await act(async () => resolveCheck(geometryMetadata));
|
|
606
|
+
|
|
607
|
+
expect(downloadAs).toHaveBeenCalledTimes(1);
|
|
608
|
+
expect(downloadByURL).not.toHaveBeenCalled();
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
it("does not deliver a retried export that resolves after unmount", async () => {
|
|
612
|
+
const { unmount } = await startGeoParquetInstall();
|
|
613
|
+
let resolveDownload: (value: {
|
|
614
|
+
url: string;
|
|
615
|
+
filename: string;
|
|
616
|
+
}) => void = () => undefined;
|
|
617
|
+
downloadAs.mockImplementationOnce(
|
|
618
|
+
() =>
|
|
619
|
+
new Promise((resolve) => {
|
|
620
|
+
resolveDownload = resolve;
|
|
621
|
+
}),
|
|
622
|
+
);
|
|
623
|
+
await act(async () => mocks.handleInstallPackages.mock.calls[0][1]());
|
|
624
|
+
expect(downloadAs).toHaveBeenCalledTimes(2);
|
|
625
|
+
unmount();
|
|
626
|
+
|
|
627
|
+
await act(async () =>
|
|
628
|
+
resolveDownload({
|
|
629
|
+
url: "https://example.test/export",
|
|
630
|
+
filename: "table",
|
|
631
|
+
}),
|
|
632
|
+
);
|
|
633
|
+
|
|
634
|
+
expect(downloadByURL).not.toHaveBeenCalled();
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
it("retries after installation under StrictMode effect replay", async () => {
|
|
638
|
+
await startGeoParquetInstall(true);
|
|
639
|
+
|
|
640
|
+
await act(async () => mocks.handleInstallPackages.mock.calls[0][1]());
|
|
641
|
+
|
|
642
|
+
await waitFor(() => expect(downloadByURL).toHaveBeenCalledTimes(1));
|
|
643
|
+
expect(downloadAs).toHaveBeenLastCalledWith({
|
|
644
|
+
format: "parquet",
|
|
645
|
+
geometry_column: "location",
|
|
646
|
+
});
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
it("does not export when installation finishes after the dialog closes", async () => {
|
|
650
|
+
const { dialog, getExportMetadata } = await startGeoParquetInstall();
|
|
651
|
+
fireEvent.click(within(dialog).getByRole("button", { name: "Close" }));
|
|
652
|
+
await waitFor(() => {
|
|
653
|
+
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
await act(async () => mocks.handleInstallPackages.mock.calls[0][1]());
|
|
657
|
+
|
|
658
|
+
expect(getExportMetadata).toHaveBeenCalledTimes(1);
|
|
659
|
+
expect(downloadAs).toHaveBeenCalledTimes(1);
|
|
660
|
+
expect(downloadByURL).not.toHaveBeenCalled();
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
it("does not export when installation finishes after the source changes", async () => {
|
|
664
|
+
const { getExportMetadata, rerenderExportActions } =
|
|
665
|
+
await startGeoParquetInstall();
|
|
666
|
+
rerenderExportActions({ getExportMetadata, metadataSource: "second" });
|
|
667
|
+
await waitFor(() => {
|
|
668
|
+
expect(getExportMetadata).toHaveBeenCalledTimes(2);
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
await act(async () => mocks.handleInstallPackages.mock.calls[0][1]());
|
|
672
|
+
|
|
673
|
+
expect(getExportMetadata).toHaveBeenCalledTimes(2);
|
|
674
|
+
expect(downloadAs).toHaveBeenCalledTimes(1);
|
|
675
|
+
expect(downloadByURL).not.toHaveBeenCalled();
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
it("does not export a non-geographic retry after the dialog closes", async () => {
|
|
679
|
+
downloadAs.mockResolvedValueOnce({
|
|
680
|
+
url: "",
|
|
681
|
+
filename: "",
|
|
682
|
+
error: "Parquet export requires pyarrow.",
|
|
683
|
+
missing_packages: ["pyarrow"],
|
|
684
|
+
});
|
|
685
|
+
renderExportActions();
|
|
686
|
+
const dialog = await openDialog();
|
|
687
|
+
fireEvent.click(screen.getByRole("button", { name: "Download Parquet" }));
|
|
688
|
+
fireEvent.click(
|
|
689
|
+
await screen.findByRole("button", { name: "Install pyarrow" }),
|
|
690
|
+
);
|
|
691
|
+
fireEvent.click(within(dialog).getByRole("button", { name: "Close" }));
|
|
692
|
+
|
|
693
|
+
await act(async () => mocks.handleInstallPackages.mock.calls[0][1]());
|
|
694
|
+
|
|
695
|
+
expect(downloadAs).toHaveBeenCalledTimes(1);
|
|
696
|
+
expect(downloadByURL).not.toHaveBeenCalled();
|
|
697
|
+
});
|
|
698
|
+
|
|
699
|
+
it("retries after installation when an earlier export succeeded", async () => {
|
|
700
|
+
const getExportMetadata = vi.fn().mockResolvedValue({
|
|
701
|
+
...geometryMetadata,
|
|
702
|
+
formats: {
|
|
703
|
+
parquet: {
|
|
704
|
+
available: false,
|
|
705
|
+
reason: "GeoParquet export requires pyarrow.",
|
|
706
|
+
missing_packages: ["pyarrow"],
|
|
707
|
+
},
|
|
708
|
+
},
|
|
709
|
+
});
|
|
710
|
+
renderExportActions({ getExportMetadata });
|
|
711
|
+
await openDialog();
|
|
712
|
+
fireEvent.click(screen.getByRole("button", { name: "Download CSV" }));
|
|
713
|
+
await waitFor(() => expect(downloadByURL).toHaveBeenCalledTimes(1));
|
|
714
|
+
fireEvent.click(
|
|
715
|
+
await screen.findByRole("button", { name: "Install pyarrow" }),
|
|
716
|
+
);
|
|
717
|
+
getExportMetadata.mockResolvedValue(geometryMetadata);
|
|
718
|
+
|
|
719
|
+
await act(async () => mocks.handleInstallPackages.mock.calls[0][1]());
|
|
720
|
+
|
|
721
|
+
await waitFor(() => expect(downloadByURL).toHaveBeenCalledTimes(2));
|
|
722
|
+
expect(downloadAs).toHaveBeenLastCalledWith({
|
|
723
|
+
format: "parquet",
|
|
724
|
+
geometry_column: "location",
|
|
725
|
+
});
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
it("waits for the post-install metadata check before exporting", async () => {
|
|
729
|
+
let resolveCheck: (value: ExportMetadata) => void = () => undefined;
|
|
730
|
+
const getExportMetadata = vi.fn().mockResolvedValue({
|
|
731
|
+
...geometryMetadata,
|
|
732
|
+
formats: {
|
|
733
|
+
parquet: {
|
|
734
|
+
available: false,
|
|
735
|
+
reason: "GeoParquet export requires pyarrow.",
|
|
736
|
+
missing_packages: ["pyarrow"],
|
|
737
|
+
},
|
|
738
|
+
},
|
|
739
|
+
});
|
|
740
|
+
renderExportActions({ getExportMetadata });
|
|
741
|
+
await openDialog();
|
|
742
|
+
fireEvent.click(
|
|
743
|
+
await screen.findByRole("button", { name: "Install pyarrow" }),
|
|
744
|
+
);
|
|
745
|
+
getExportMetadata.mockImplementationOnce(
|
|
746
|
+
() =>
|
|
747
|
+
new Promise((resolve) => {
|
|
748
|
+
resolveCheck = resolve;
|
|
749
|
+
}),
|
|
750
|
+
);
|
|
751
|
+
|
|
752
|
+
await act(async () => mocks.handleInstallPackages.mock.calls[0][1]());
|
|
753
|
+
expect(downloadAs).not.toHaveBeenCalled();
|
|
754
|
+
|
|
755
|
+
getExportMetadata.mockResolvedValue(geometryMetadata);
|
|
756
|
+
await act(async () => resolveCheck(geometryMetadata));
|
|
757
|
+
await waitFor(() => expect(downloadByURL).toHaveBeenCalled());
|
|
758
|
+
expect(downloadAs).toHaveBeenCalledTimes(1);
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
it("uses the new source eligibility after a settled package failure", async () => {
|
|
762
|
+
const getExportMetadata = vi.fn().mockResolvedValue(geometryMetadata);
|
|
763
|
+
downloadAs.mockResolvedValueOnce({
|
|
764
|
+
url: "",
|
|
765
|
+
filename: "",
|
|
766
|
+
error: "GeoParquet export requires pyarrow.",
|
|
767
|
+
missing_packages: ["pyarrow"],
|
|
768
|
+
});
|
|
769
|
+
const { rerenderExportActions } = renderExportActions({
|
|
770
|
+
getExportMetadata,
|
|
771
|
+
metadataSource: "first",
|
|
772
|
+
});
|
|
773
|
+
await openDialog();
|
|
774
|
+
fireEvent.click(
|
|
775
|
+
await screen.findByRole("button", { name: "Download GeoParquet" }),
|
|
776
|
+
);
|
|
777
|
+
await screen.findByRole("button", { name: "Install pyarrow" });
|
|
778
|
+
|
|
779
|
+
rerenderExportActions({ getExportMetadata, metadataSource: "second" });
|
|
780
|
+
|
|
781
|
+
await waitFor(() =>
|
|
782
|
+
expect(
|
|
783
|
+
screen.getByRole("button", { name: "Download GeoParquet" }),
|
|
784
|
+
).toBeEnabled(),
|
|
785
|
+
);
|
|
786
|
+
expect(
|
|
787
|
+
screen.queryByRole("button", { name: "Install pyarrow" }),
|
|
788
|
+
).not.toBeInTheDocument();
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
it("ignores metadata for an earlier source that resolves late", async () => {
|
|
792
|
+
let resolveFirst: (value: ExportMetadata) => void = () => undefined;
|
|
793
|
+
const first = vi.fn(
|
|
794
|
+
() =>
|
|
795
|
+
new Promise<ExportMetadata>((resolve) => {
|
|
796
|
+
resolveFirst = resolve;
|
|
797
|
+
}),
|
|
798
|
+
);
|
|
799
|
+
const second = vi.fn().mockResolvedValue({
|
|
800
|
+
...geometryMetadata,
|
|
801
|
+
geometry_columns: [
|
|
802
|
+
{ name: "region", encoding: "objects", crs: "EPSG:3857" },
|
|
803
|
+
],
|
|
804
|
+
primary_geometry_column: "region",
|
|
805
|
+
default_geometry_column: "region",
|
|
806
|
+
});
|
|
807
|
+
const { rerenderExportActions } = renderExportActions({
|
|
808
|
+
getExportMetadata: first,
|
|
809
|
+
metadataSource: "first",
|
|
810
|
+
});
|
|
811
|
+
await openDialog();
|
|
812
|
+
rerenderExportActions({
|
|
813
|
+
getExportMetadata: second,
|
|
814
|
+
metadataSource: "second",
|
|
815
|
+
});
|
|
816
|
+
await waitFor(() =>
|
|
817
|
+
expect(screen.getByTestId("export-summary-parquet")).toHaveTextContent(
|
|
818
|
+
"region",
|
|
819
|
+
),
|
|
820
|
+
);
|
|
821
|
+
|
|
822
|
+
await act(async () => resolveFirst(geometryMetadata));
|
|
823
|
+
|
|
824
|
+
expect(screen.getByTestId("export-summary-parquet")).toHaveTextContent(
|
|
825
|
+
"region",
|
|
826
|
+
);
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
it("does not use earlier metadata when the new source request fails", async () => {
|
|
830
|
+
const getExportMetadata = vi.fn().mockResolvedValue(geometryMetadata);
|
|
831
|
+
const { rerenderExportActions } = renderExportActions({
|
|
832
|
+
getExportMetadata,
|
|
833
|
+
metadataSource: "first",
|
|
834
|
+
});
|
|
835
|
+
await openDialog();
|
|
836
|
+
await waitFor(() =>
|
|
837
|
+
expect(
|
|
838
|
+
screen.getByRole("button", { name: "Download GeoParquet" }),
|
|
839
|
+
).toBeEnabled(),
|
|
840
|
+
);
|
|
841
|
+
getExportMetadata.mockRejectedValueOnce(new Error("Connection lost"));
|
|
842
|
+
|
|
843
|
+
rerenderExportActions({ getExportMetadata, metadataSource: "second" });
|
|
844
|
+
|
|
845
|
+
expect(
|
|
846
|
+
await screen.findByText(
|
|
847
|
+
"Could not load geometry options: Connection lost",
|
|
848
|
+
),
|
|
849
|
+
).toBeInTheDocument();
|
|
850
|
+
expect(
|
|
851
|
+
screen.getByRole("button", { name: "Download Parquet" }),
|
|
852
|
+
).toBeDisabled();
|
|
853
|
+
expect(screen.getByRole("button", { name: "Download CSV" })).toBeEnabled();
|
|
854
|
+
});
|
|
855
|
+
|
|
533
856
|
it("shows formats in the approved order with an options toggle where supported", async () => {
|
|
534
857
|
renderExportActions();
|
|
535
858
|
const dialog = await openDialog();
|
|
@@ -1412,7 +1735,7 @@ describe("ExportActions GeoJSON", () => {
|
|
|
1412
1735
|
);
|
|
1413
1736
|
await act(async () => mocks.handleInstallPackages.mock.calls[0][1]());
|
|
1414
1737
|
await waitFor(() => expect(downloadByURL).toHaveBeenCalled());
|
|
1415
|
-
expect(getExportMetadata).toHaveBeenCalledTimes(
|
|
1738
|
+
expect(getExportMetadata).toHaveBeenCalledTimes(3);
|
|
1416
1739
|
expect(downloadAs).toHaveBeenLastCalledWith({
|
|
1417
1740
|
format: "geojson",
|
|
1418
1741
|
geometry_column: "boundary",
|
|
@@ -1685,7 +2008,7 @@ describe("ExportActions GeoJSON clipboard", () => {
|
|
|
1685
2008
|
format: "geojson",
|
|
1686
2009
|
geometry_column: "boundary",
|
|
1687
2010
|
});
|
|
1688
|
-
expect(getExportMetadata).toHaveBeenCalledTimes(
|
|
2011
|
+
expect(getExportMetadata).toHaveBeenCalledTimes(3);
|
|
1689
2012
|
expect(downloadByURL).not.toHaveBeenCalled();
|
|
1690
2013
|
});
|
|
1691
2014
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import type { Table } from "@tanstack/react-table";
|
|
8
8
|
import { Columns3Icon, EyeIcon, EyeOffIcon, ScanEyeIcon } from "lucide-react";
|
|
9
|
-
import React
|
|
9
|
+
import React from "react";
|
|
10
10
|
import { ColumnName } from "@/components/datasources/components";
|
|
11
11
|
import { Button } from "@/components/ui/button";
|
|
12
12
|
import {
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
} from "@/components/ui/popover";
|
|
25
25
|
import { type BulkAction, useSelectList } from "@/components/ui/select-core";
|
|
26
26
|
import type { DataType } from "@/core/kernel/messages";
|
|
27
|
+
import { Arrays } from "@/utils/arrays";
|
|
27
28
|
import { cn } from "@/utils/cn";
|
|
28
29
|
import { Events } from "@/utils/events";
|
|
29
30
|
import { smartMatchFilter } from "@/utils/smartMatch";
|
|
@@ -67,9 +68,8 @@ export const ColumnVisibilityDropdown = <TData,>({
|
|
|
67
68
|
const hideableIds = userColumns
|
|
68
69
|
.filter((column) => column.getCanHide())
|
|
69
70
|
.map((column) => column.id);
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
).length;
|
|
71
|
+
const visibleHideableColumnIds = Arrays.difference(hideableIds, hiddenIds);
|
|
72
|
+
const visibleHideableCount = visibleHideableColumnIds.length;
|
|
73
73
|
|
|
74
74
|
const applyHidden = (next: string[] | string | null) => {
|
|
75
75
|
const hidden = new Set(Array.isArray(next) ? next : []);
|
|
@@ -106,15 +106,10 @@ export const ColumnVisibilityDropdown = <TData,>({
|
|
|
106
106
|
.filter((option) => !option.disabled)
|
|
107
107
|
.map((option) => option.value)
|
|
108
108
|
: [];
|
|
109
|
-
const showOnlyColumnState =
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
),
|
|
114
|
-
hideableColumnIdSet: new Set(hideableIds),
|
|
115
|
-
}),
|
|
116
|
-
[hideableIds, hiddenIds],
|
|
117
|
-
);
|
|
109
|
+
const showOnlyColumnState = {
|
|
110
|
+
visibleHideableColumnIds,
|
|
111
|
+
hideableColumnIdSet: new Set(hideableIds),
|
|
112
|
+
};
|
|
118
113
|
const hasSearchBulkActions =
|
|
119
114
|
showOnlyMatchIds.length > 0 || matchingActions.length > 0;
|
|
120
115
|
|