@marimo-team/islands 0.24.1-dev3 → 0.24.1-dev31
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-BUC1MrsF.js → ConnectedDataExplorerComponent-yEsovh7C.js} +4 -4
- package/dist/arrays-Ct6XviUd.js +59 -0
- package/dist/{capabilities-BilXeZ7p.js → capabilities-DNKeBoT5.js} +1 -1
- package/dist/{chat-ui-ChBRy1iT.js → chat-ui-ChbH2I73.js} +3 -3
- package/dist/{common-BvTQu-LJ.js → common-ZoATHjIZ.js} +17 -11
- package/dist/{formats-Dz0QNUEa.js → formats-BZtMpeQe.js} +1 -1
- package/dist/{glide-data-editor-BI2i_IEE.js → glide-data-editor-aimC_hok.js} +3 -1
- package/dist/{html-to-image-BtNr4zrO.js → html-to-image-BzT-fb14.js} +2357 -2325
- package/dist/{input-BRE2a7Ay.js → input-CqV2ZPgL.js} +1 -1
- package/dist/{loader-DTnbNSVZ.js → loader-Chm6nOqy.js} +1 -1
- package/dist/main.js +1196 -1088
- package/dist/{once-Boutk_9X.js → once-D1_qYl2e.js} +1 -1
- package/dist/{process-output-Dch8KCi8.js → process-output-C8Q8G6-g.js} +1 -1
- package/dist/{react-vega-zyfTF9SY.js → react-vega-BE2Uv1cq.js} +1 -1
- package/dist/{reveal-component-qNkzuPp3.js → reveal-component-CBpmFHpU.js} +5 -5
- package/dist/{spec-BL1GSUUF.js → spec-DR1CyTOo.js} +2 -0
- package/dist/style.css +1 -1
- package/dist/{swiper-component-Cx0cRWLk.js → swiper-component-UYicacis.js} +1 -1
- package/dist/{useIframeCapabilities-CxuXXwjj.js → useIframeCapabilities-BbnoLKxG.js} +1 -1
- package/dist/{vega-component-CioKuDut.js → vega-component-xrHju6oo.js} +5 -5
- package/dist/{vega-embed-container-BRjjqRGz.js → vega-embed-container-B1tc-DoN.js} +2 -2
- package/dist/{vega-loader.browser-CGb-2Kwi.js → vega-loader.browser-VFQfHaoV.js} +1 -1
- package/package.json +1 -1
- package/src/components/app-config/optional-features.test.ts +78 -0
- package/src/components/app-config/optional-features.tsx +33 -6
- package/src/components/data-table/__tests__/column_formatting.test.ts +7 -0
- package/src/components/data-table/charts/__tests__/schemas.test.ts +17 -2
- package/src/components/data-table/charts/chart-spec/types.ts +2 -0
- package/src/components/data-table/column-formatting/feature.ts +1 -0
- package/src/components/data-table/column-formatting/types.ts +1 -0
- package/src/components/data-table/column-summary/chart-spec-model.tsx +1 -0
- package/src/components/data-table/column-summary/column-summary.tsx +1 -0
- package/src/components/data-table/columns.tsx +4 -3
- package/src/components/datasets/__tests__/icons.test.ts +10 -2
- package/src/components/datasets/icons.tsx +4 -0
- package/src/components/datasources/__tests__/components.test.tsx +14 -1
- package/src/components/datasources/datasources.tsx +2 -40
- package/src/components/editor/chrome/panels/components.tsx +35 -0
- package/src/components/editor/chrome/panels/file-explorer-panel.tsx +10 -0
- package/src/components/editor/chrome/panels/session-panel.tsx +12 -4
- package/src/components/editor/connections/__tests__/quick-add-data-sources.test.tsx +2 -0
- package/src/components/editor/connections/add-connection-dialog.tsx +5 -1
- package/src/components/editor/connections/quick-add-data-sources.tsx +11 -7
- package/src/components/editor/connections/storage/__tests__/__snapshots__/as-code.test.ts.snap +43 -0
- package/src/components/editor/connections/storage/__tests__/as-code.test.ts +127 -0
- package/src/components/editor/connections/storage/add-storage-form.tsx +11 -1
- package/src/components/editor/connections/storage/as-code.ts +30 -1
- package/src/components/editor/connections/storage/schemas.ts +63 -0
- package/src/core/codemirror/code-lens/__tests__/popover.test.ts +13 -0
- package/src/core/codemirror/code-lens/popover.tsx +11 -5
- package/src/core/codemirror/language/languages/sql/__tests__/renderers.test.tsx +15 -4
- package/src/core/codemirror/language/languages/sql/renderers.tsx +1 -0
- package/src/core/datasets/__tests__/data-source-discovery.test.ts +277 -0
- package/src/core/datasets/data-source-connections.ts +36 -0
- package/src/core/datasets/data-source-discovery.ts +92 -0
- package/src/core/kernel/messages.ts +1 -0
- package/src/core/websocket/useMarimoKernelConnection.tsx +11 -3
- package/src/hooks/useDataSourceDiscovery.ts +50 -9
- package/src/plugins/impl/MatrixPlugin.tsx +256 -35
- package/src/plugins/impl/__tests__/DataEditorPlugin.test.ts +13 -1
- package/src/plugins/impl/__tests__/MatrixPlugin.test.tsx +540 -3
- package/src/plugins/impl/data-editor/glide-data-editor.tsx +1 -0
- package/src/plugins/impl/data-editor/glide-utils.ts +1 -0
- package/src/plugins/impl/data-frames/__tests__/schema.test.ts +9 -1
- package/src/plugins/impl/data-frames/utils/__tests__/operators.test.ts +8 -0
- package/src/plugins/impl/data-frames/utils/operators.ts +1 -1
- package/src/plugins/impl/matplotlib/__tests__/matplotlib-renderer.test.ts +184 -24
- package/src/plugins/impl/matplotlib/matplotlib-renderer.ts +40 -7
- package/src/plugins/impl/vega/__tests__/vega.test.ts +22 -4
- package/src/plugins/impl/vega/utils.ts +3 -0
- package/dist/arrays-C56grfjL.js +0 -55
- package/src/hooks/__tests__/useDataSourceDiscovery.test.ts +0 -80
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
import { PlusIcon, SparklesIcon } from "lucide-react";
|
|
4
4
|
import { Tooltip, TooltipProvider } from "@/components/ui/tooltip";
|
|
5
|
-
import type {
|
|
6
|
-
|
|
5
|
+
import type {
|
|
6
|
+
DataSourceDiscoveryGroup,
|
|
7
|
+
DetectedDataSource,
|
|
8
|
+
} from "@/core/datasets/data-source-discovery";
|
|
9
|
+
import { useDetectedDataSources } from "@/hooks/useDataSourceDiscovery";
|
|
7
10
|
import { useInsertCode } from "./components";
|
|
8
11
|
import { cn } from "@/utils/cn";
|
|
9
12
|
|
|
@@ -94,19 +97,20 @@ const DetectedDataSourceDetails: React.FC<{
|
|
|
94
97
|
);
|
|
95
98
|
|
|
96
99
|
export const AutoDiscoveredDataSources: React.FC<{
|
|
97
|
-
onSubmit
|
|
100
|
+
onSubmit?: () => void;
|
|
98
101
|
className?: string;
|
|
99
|
-
|
|
102
|
+
group?: DataSourceDiscoveryGroup;
|
|
103
|
+
}> = ({ onSubmit, className, group }) => {
|
|
100
104
|
const insertCode = useInsertCode();
|
|
101
|
-
const
|
|
105
|
+
const sources = useDetectedDataSources(group);
|
|
102
106
|
|
|
103
107
|
return (
|
|
104
108
|
<QuickAddDataSources
|
|
105
109
|
className={className}
|
|
106
|
-
sources={
|
|
110
|
+
sources={sources}
|
|
107
111
|
onAdd={(source) => {
|
|
108
112
|
insertCode(source.code);
|
|
109
|
-
onSubmit();
|
|
113
|
+
onSubmit?.();
|
|
110
114
|
}}
|
|
111
115
|
/>
|
|
112
116
|
);
|
package/src/components/editor/connections/storage/__tests__/__snapshots__/as-code.test.ts.snap
CHANGED
|
@@ -117,6 +117,49 @@ exports[`generateStorageCode > GCS > without service account key (default creden
|
|
|
117
117
|
store = GCSStore("my-bucket")"
|
|
118
118
|
`;
|
|
119
119
|
|
|
120
|
+
exports[`generateStorageCode > GitHub > public repo 1`] = `
|
|
121
|
+
"from fsspec.implementations.github import GithubFileSystem
|
|
122
|
+
|
|
123
|
+
fs = GithubFileSystem(
|
|
124
|
+
org="marimo-team",
|
|
125
|
+
repo="marimo",
|
|
126
|
+
)"
|
|
127
|
+
`;
|
|
128
|
+
|
|
129
|
+
exports[`generateStorageCode > GitHub > with sha 1`] = `
|
|
130
|
+
"from fsspec.implementations.github import GithubFileSystem
|
|
131
|
+
|
|
132
|
+
fs = GithubFileSystem(
|
|
133
|
+
org="marimo-team",
|
|
134
|
+
repo="marimo",
|
|
135
|
+
sha="main",
|
|
136
|
+
)"
|
|
137
|
+
`;
|
|
138
|
+
|
|
139
|
+
exports[`generateStorageCode > GitHub > with username and token 1`] = `
|
|
140
|
+
"from fsspec.implementations.github import GithubFileSystem
|
|
141
|
+
|
|
142
|
+
fs = GithubFileSystem(
|
|
143
|
+
org="marimo-team",
|
|
144
|
+
repo="marimo",
|
|
145
|
+
username="octocat",
|
|
146
|
+
token="ghp_example",
|
|
147
|
+
)"
|
|
148
|
+
`;
|
|
149
|
+
|
|
150
|
+
exports[`generateStorageCode > GitHub > with username and token from secrets 1`] = `
|
|
151
|
+
"from fsspec.implementations.github import GithubFileSystem
|
|
152
|
+
import os
|
|
153
|
+
|
|
154
|
+
_token = os.environ.get("GITHUB_TOKEN")
|
|
155
|
+
fs = GithubFileSystem(
|
|
156
|
+
org="marimo-team",
|
|
157
|
+
repo="marimo",
|
|
158
|
+
username="octocat",
|
|
159
|
+
token=_token,
|
|
160
|
+
)"
|
|
161
|
+
`;
|
|
162
|
+
|
|
120
163
|
exports[`generateStorageCode > Google Drive > with default auth (no credentials) 1`] = `
|
|
121
164
|
"from gdrive_fsspec import GoogleDriveFileSystem
|
|
122
165
|
|
|
@@ -329,6 +329,83 @@ describe("generateStorageCode", () => {
|
|
|
329
329
|
});
|
|
330
330
|
});
|
|
331
331
|
|
|
332
|
+
describe("GitHub", () => {
|
|
333
|
+
it("public repo", () => {
|
|
334
|
+
expect(
|
|
335
|
+
generateStorageCode(
|
|
336
|
+
{ type: "github", org: "marimo-team", repo: "marimo" },
|
|
337
|
+
{ library: "fsspec" },
|
|
338
|
+
),
|
|
339
|
+
).toMatchSnapshot();
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it("with sha", () => {
|
|
343
|
+
expect(
|
|
344
|
+
generateStorageCode(
|
|
345
|
+
{
|
|
346
|
+
type: "github",
|
|
347
|
+
org: "marimo-team",
|
|
348
|
+
repo: "marimo",
|
|
349
|
+
sha: "main",
|
|
350
|
+
},
|
|
351
|
+
{ library: "fsspec" },
|
|
352
|
+
),
|
|
353
|
+
).toMatchSnapshot();
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it("escapes quotes and backslashes in sha", () => {
|
|
357
|
+
expect(
|
|
358
|
+
generateStorageCode(
|
|
359
|
+
{
|
|
360
|
+
type: "github",
|
|
361
|
+
org: "marimo-team",
|
|
362
|
+
repo: "marimo",
|
|
363
|
+
sha: 'feature/\\"new"',
|
|
364
|
+
},
|
|
365
|
+
{ library: "fsspec" },
|
|
366
|
+
),
|
|
367
|
+
).toMatchInlineSnapshot(`
|
|
368
|
+
"from fsspec.implementations.github import GithubFileSystem
|
|
369
|
+
|
|
370
|
+
fs = GithubFileSystem(
|
|
371
|
+
org="marimo-team",
|
|
372
|
+
repo="marimo",
|
|
373
|
+
sha="feature/\\\\\\"new\\"",
|
|
374
|
+
)"
|
|
375
|
+
`);
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it("with username and token", () => {
|
|
379
|
+
expect(
|
|
380
|
+
generateStorageCode(
|
|
381
|
+
{
|
|
382
|
+
type: "github",
|
|
383
|
+
org: "marimo-team",
|
|
384
|
+
repo: "marimo",
|
|
385
|
+
username: "octocat",
|
|
386
|
+
token: "ghp_example",
|
|
387
|
+
},
|
|
388
|
+
{ library: "fsspec" },
|
|
389
|
+
),
|
|
390
|
+
).toMatchSnapshot();
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
it("with username and token from secrets", () => {
|
|
394
|
+
expect(
|
|
395
|
+
generateStorageCode(
|
|
396
|
+
{
|
|
397
|
+
type: "github",
|
|
398
|
+
org: "marimo-team",
|
|
399
|
+
repo: "marimo",
|
|
400
|
+
username: "octocat",
|
|
401
|
+
token: prefixSecret("GITHUB_TOKEN"),
|
|
402
|
+
},
|
|
403
|
+
{ library: "fsspec" },
|
|
404
|
+
),
|
|
405
|
+
).toMatchSnapshot();
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
|
|
332
409
|
describe("invalid cases", () => {
|
|
333
410
|
it("throws for empty S3 bucket", () => {
|
|
334
411
|
expect(() =>
|
|
@@ -397,5 +474,55 @@ describe("generateStorageCode", () => {
|
|
|
397
474
|
),
|
|
398
475
|
).toThrow();
|
|
399
476
|
});
|
|
477
|
+
|
|
478
|
+
it("throws for empty GitHub org", () => {
|
|
479
|
+
expect(() =>
|
|
480
|
+
generateStorageCode(
|
|
481
|
+
{ type: "github", org: "", repo: "marimo" } as StorageConnection,
|
|
482
|
+
{ library: "fsspec" },
|
|
483
|
+
),
|
|
484
|
+
).toThrow();
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
it("throws for empty GitHub repo", () => {
|
|
488
|
+
expect(() =>
|
|
489
|
+
generateStorageCode(
|
|
490
|
+
{
|
|
491
|
+
type: "github",
|
|
492
|
+
org: "marimo-team",
|
|
493
|
+
repo: "",
|
|
494
|
+
} as StorageConnection,
|
|
495
|
+
{ library: "fsspec" },
|
|
496
|
+
),
|
|
497
|
+
).toThrow();
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
it("throws when GitHub username is set without token", () => {
|
|
501
|
+
expect(() =>
|
|
502
|
+
generateStorageCode(
|
|
503
|
+
{
|
|
504
|
+
type: "github",
|
|
505
|
+
org: "marimo-team",
|
|
506
|
+
repo: "marimo",
|
|
507
|
+
username: "octocat",
|
|
508
|
+
},
|
|
509
|
+
{ library: "fsspec" },
|
|
510
|
+
),
|
|
511
|
+
).toThrow();
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
it("throws when GitHub token is set without username", () => {
|
|
515
|
+
expect(() =>
|
|
516
|
+
generateStorageCode(
|
|
517
|
+
{
|
|
518
|
+
type: "github",
|
|
519
|
+
org: "marimo-team",
|
|
520
|
+
repo: "marimo",
|
|
521
|
+
token: "ghp_example",
|
|
522
|
+
},
|
|
523
|
+
{ library: "fsspec" },
|
|
524
|
+
),
|
|
525
|
+
).toThrow();
|
|
526
|
+
});
|
|
400
527
|
});
|
|
401
528
|
});
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
AzureStorageSchema,
|
|
17
17
|
CoreWeaveStorageSchema,
|
|
18
18
|
GCSStorageSchema,
|
|
19
|
+
GithubStorageSchema,
|
|
19
20
|
GoogleDriveStorageSchema,
|
|
20
21
|
HuggingfaceStorageSchema,
|
|
21
22
|
S3StorageSchema,
|
|
@@ -89,6 +90,15 @@ const STORAGE_PROVIDERS = [
|
|
|
89
90
|
preferred: "huggingface_hub",
|
|
90
91
|
},
|
|
91
92
|
},
|
|
93
|
+
{
|
|
94
|
+
name: "GitHub",
|
|
95
|
+
schema: GithubStorageSchema,
|
|
96
|
+
protocol: "github",
|
|
97
|
+
storageLibraries: {
|
|
98
|
+
libraries: ["fsspec"],
|
|
99
|
+
preferred: "fsspec",
|
|
100
|
+
},
|
|
101
|
+
},
|
|
92
102
|
] satisfies StorageProviderSchema[];
|
|
93
103
|
|
|
94
104
|
const StorageProviderSelector: React.FC<{
|
|
@@ -105,7 +115,7 @@ const StorageProviderSelector: React.FC<{
|
|
|
105
115
|
<ProtocolIcon
|
|
106
116
|
protocol={protocol as KnownStorageProtocol}
|
|
107
117
|
forceDark={true}
|
|
108
|
-
className="w-7.5 h-7.5"
|
|
118
|
+
className="w-7.5 h-7.5 text-white"
|
|
109
119
|
/>
|
|
110
120
|
}
|
|
111
121
|
onSelect={() => onSelect(schema)}
|
|
@@ -43,7 +43,7 @@ class SecretContainer {
|
|
|
43
43
|
this.secrets[privateVar] = `os.environ.get("${envVar}")`;
|
|
44
44
|
return privateVar;
|
|
45
45
|
}
|
|
46
|
-
return `"${value}"`;
|
|
46
|
+
return `"${escapePythonString(value)}"`;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
formatSecrets(): string {
|
|
@@ -260,6 +260,32 @@ function generateHuggingfaceCode(
|
|
|
260
260
|
return { imports, code: `hf = HfApi(token=${token})` };
|
|
261
261
|
}
|
|
262
262
|
|
|
263
|
+
function generateGithubCode(
|
|
264
|
+
connection: Extract<StorageConnection, { type: "github" }>,
|
|
265
|
+
secrets: SecretContainer,
|
|
266
|
+
): { imports: Set<string>; code: string } {
|
|
267
|
+
const imports = new Set([
|
|
268
|
+
"from fsspec.implementations.github import GithubFileSystem",
|
|
269
|
+
]);
|
|
270
|
+
const params: string[] = [
|
|
271
|
+
`org=${secrets.print("org", connection.org)},`,
|
|
272
|
+
`repo=${secrets.print("repo", connection.repo)},`,
|
|
273
|
+
];
|
|
274
|
+
|
|
275
|
+
if (connection.sha) {
|
|
276
|
+
params.push(`sha=${secrets.print("sha", connection.sha)},`);
|
|
277
|
+
}
|
|
278
|
+
if (connection.username && connection.token) {
|
|
279
|
+
params.push(
|
|
280
|
+
`username=${secrets.print("username", connection.username)},`,
|
|
281
|
+
`token=${secrets.print("token", connection.token)},`,
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const paramsStr = params.map((param) => ` ${param}`).join("\n");
|
|
286
|
+
return { imports, code: `fs = GithubFileSystem(\n${paramsStr}\n)` };
|
|
287
|
+
}
|
|
288
|
+
|
|
263
289
|
export function generateStorageCode(
|
|
264
290
|
connection: StorageConnection,
|
|
265
291
|
options: StorageCodeOptions,
|
|
@@ -291,6 +317,9 @@ export function generateStorageCode(
|
|
|
291
317
|
case "huggingface":
|
|
292
318
|
result = generateHuggingfaceCode(connection, secrets);
|
|
293
319
|
break;
|
|
320
|
+
case "github":
|
|
321
|
+
result = generateGithubCode(connection, secrets);
|
|
322
|
+
break;
|
|
294
323
|
default:
|
|
295
324
|
assertNever(connection);
|
|
296
325
|
}
|
|
@@ -238,6 +238,68 @@ export const HuggingfaceStorageSchema = z
|
|
|
238
238
|
})
|
|
239
239
|
.describe(FieldOptions.of({ direction: "two-columns" }));
|
|
240
240
|
|
|
241
|
+
function isFilled(value: string | undefined): boolean {
|
|
242
|
+
return value != null && value !== "";
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export const GithubStorageSchema = z
|
|
246
|
+
.object({
|
|
247
|
+
type: z.literal("github"),
|
|
248
|
+
org: z
|
|
249
|
+
.string()
|
|
250
|
+
.nonempty()
|
|
251
|
+
.describe(
|
|
252
|
+
FieldOptions.of({
|
|
253
|
+
label: "Organization or user",
|
|
254
|
+
}),
|
|
255
|
+
),
|
|
256
|
+
repo: z
|
|
257
|
+
.string()
|
|
258
|
+
.nonempty()
|
|
259
|
+
.describe(
|
|
260
|
+
FieldOptions.of({
|
|
261
|
+
label: "Repository",
|
|
262
|
+
}),
|
|
263
|
+
),
|
|
264
|
+
sha: z
|
|
265
|
+
.string()
|
|
266
|
+
.optional()
|
|
267
|
+
.describe(
|
|
268
|
+
FieldOptions.of({
|
|
269
|
+
label: "Branch, tag, or commit",
|
|
270
|
+
placeholder: "main",
|
|
271
|
+
description: "Leave empty to use the default branch",
|
|
272
|
+
}),
|
|
273
|
+
),
|
|
274
|
+
username: z
|
|
275
|
+
.string()
|
|
276
|
+
.optional()
|
|
277
|
+
.describe(
|
|
278
|
+
FieldOptions.of({
|
|
279
|
+
label: "Username",
|
|
280
|
+
description: "Set this together with access token",
|
|
281
|
+
optionRegex: "(github.?username|github.?user)",
|
|
282
|
+
}),
|
|
283
|
+
),
|
|
284
|
+
token: z
|
|
285
|
+
.string()
|
|
286
|
+
.optional()
|
|
287
|
+
.describe(
|
|
288
|
+
FieldOptions.of({
|
|
289
|
+
label: "Access Token",
|
|
290
|
+
description:
|
|
291
|
+
"Set this together with username. Required for private repos and to avoid GitHub's rate limits.",
|
|
292
|
+
inputType: "password",
|
|
293
|
+
optionRegex: "(github.?token|gh.?token|github.?pat)",
|
|
294
|
+
}),
|
|
295
|
+
),
|
|
296
|
+
})
|
|
297
|
+
.refine((value) => isFilled(value.username) === isFilled(value.token), {
|
|
298
|
+
message: "Username and access token are required together",
|
|
299
|
+
path: ["token"],
|
|
300
|
+
})
|
|
301
|
+
.describe(FieldOptions.of({ direction: "two-columns" }));
|
|
302
|
+
|
|
241
303
|
export const StorageConnectionSchema = z.discriminatedUnion("type", [
|
|
242
304
|
S3StorageSchema,
|
|
243
305
|
GCSStorageSchema,
|
|
@@ -245,6 +307,7 @@ export const StorageConnectionSchema = z.discriminatedUnion("type", [
|
|
|
245
307
|
CoreWeaveStorageSchema,
|
|
246
308
|
GoogleDriveStorageSchema,
|
|
247
309
|
HuggingfaceStorageSchema,
|
|
310
|
+
GithubStorageSchema,
|
|
248
311
|
]);
|
|
249
312
|
|
|
250
313
|
export type StorageConnection = z.infer<typeof StorageConnectionSchema>;
|
|
@@ -56,6 +56,19 @@ describe("CachePopover getCacheInfo throttling", () => {
|
|
|
56
56
|
return () => act(() => dispose());
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
it("renders content synchronously for CodeMirror's initial measurement", async () => {
|
|
60
|
+
const dom = document.createElement("div");
|
|
61
|
+
|
|
62
|
+
// Keep the mount outside `act()`. It would flush an asynchronous render
|
|
63
|
+
// and make this assertion pass without `flushSync`.
|
|
64
|
+
const dispose = mountLensPopover(dom, CACHE_SPEC);
|
|
65
|
+
|
|
66
|
+
expect(dom.textContent).toContain("my_cache");
|
|
67
|
+
await act(async () => {
|
|
68
|
+
dispose();
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
59
72
|
it("does not refetch on every hover, only after the throttle interval elapses", () => {
|
|
60
73
|
const disposeFirst = mount();
|
|
61
74
|
expect(getCacheInfoMock).toHaveBeenCalledTimes(1);
|
|
@@ -4,6 +4,7 @@ import { Provider, useAtomValue } from "jotai";
|
|
|
4
4
|
import { DatabaseZapIcon } from "lucide-react";
|
|
5
5
|
import type React from "react";
|
|
6
6
|
import { useEffect } from "react";
|
|
7
|
+
import { flushSync } from "react-dom";
|
|
7
8
|
import { createRoot } from "react-dom/client";
|
|
8
9
|
import { useLocale } from "react-aria";
|
|
9
10
|
import { ProtocolIcon } from "@/components/storage/components";
|
|
@@ -39,11 +40,16 @@ export function mountLensPopover(
|
|
|
39
40
|
spec: CodeLensSpec,
|
|
40
41
|
): () => void {
|
|
41
42
|
const root = createRoot(dom);
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
// CodeMirror measures and positions the tooltip as soon as this function
|
|
44
|
+
// returns. Render now so it measures the popover content, not an empty
|
|
45
|
+
// container that later grows over the hovered icon.
|
|
46
|
+
flushSync(() => {
|
|
47
|
+
root.render(
|
|
48
|
+
<Provider store={store}>
|
|
49
|
+
<LensPopover spec={spec} />
|
|
50
|
+
</Provider>,
|
|
51
|
+
);
|
|
52
|
+
});
|
|
47
53
|
// Defer so tearing the tooltip down from inside a React event (e.g. the
|
|
48
54
|
// icon's click handler) doesn't unmount mid-render
|
|
49
55
|
return () => queueMicrotask(() => root.unmount());
|
|
@@ -9,13 +9,18 @@ import type {
|
|
|
9
9
|
} from "@/core/kernel/messages";
|
|
10
10
|
import { renderColumnInfo, renderTableInfo } from "../renderers";
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const bogusColumn = {
|
|
13
13
|
name: "geom",
|
|
14
|
-
type: "
|
|
14
|
+
type: "bogus_type" as DataType,
|
|
15
15
|
external_type: "geometry",
|
|
16
16
|
sample_values: [],
|
|
17
17
|
} as DataTableColumn;
|
|
18
18
|
|
|
19
|
+
const geometryColumn = {
|
|
20
|
+
...bogusColumn,
|
|
21
|
+
type: "geometry" as DataType,
|
|
22
|
+
} as DataTableColumn;
|
|
23
|
+
|
|
19
24
|
describe("SQL renderers", () => {
|
|
20
25
|
it("renders table information with an unrecognized column type", () => {
|
|
21
26
|
const table: DataTable = {
|
|
@@ -26,7 +31,7 @@ describe("SQL renderers", () => {
|
|
|
26
31
|
num_rows: null,
|
|
27
32
|
num_columns: 1,
|
|
28
33
|
variable_name: null,
|
|
29
|
-
columns: [
|
|
34
|
+
columns: [bogusColumn],
|
|
30
35
|
primary_keys: [],
|
|
31
36
|
indexes: [],
|
|
32
37
|
};
|
|
@@ -37,8 +42,14 @@ describe("SQL renderers", () => {
|
|
|
37
42
|
});
|
|
38
43
|
|
|
39
44
|
it("renders column information with an unrecognized column type", () => {
|
|
40
|
-
const result = render(renderColumnInfo(
|
|
45
|
+
const result = render(renderColumnInfo(bogusColumn));
|
|
41
46
|
|
|
42
47
|
expect(result.container.querySelector("svg")).not.toBeNull();
|
|
43
48
|
});
|
|
49
|
+
|
|
50
|
+
it("renders geometry column information with the map pin icon", () => {
|
|
51
|
+
const result = render(renderColumnInfo(geometryColumn));
|
|
52
|
+
|
|
53
|
+
expect(result.container.querySelector(".lucide-map-pin")).not.toBeNull();
|
|
54
|
+
});
|
|
44
55
|
});
|
|
@@ -41,6 +41,7 @@ const DATA_TYPE_COLORS: Record<DataType, string> = {
|
|
|
41
41
|
number: "bg-(--purple-4) text-(--purple-11)",
|
|
42
42
|
integer: "bg-(--purple-4) text-(--purple-11)",
|
|
43
43
|
string: "bg-(--blue-4) text-(--blue-11)",
|
|
44
|
+
geometry: "bg-(--cyan-4) text-(--cyan-11)",
|
|
44
45
|
unknown: "bg-(--slate-4) text-(--slate-11)",
|
|
45
46
|
};
|
|
46
47
|
|