@marimo-team/islands 0.25.3-dev322 → 0.25.3-dev348
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-DkXBUBf3.js → ConnectedDataExplorerComponent-xliyyXNO.js} +16 -16
- package/dist/{ErrorBoundary-CYa6lyKz.js → ErrorBoundary-CIimOpqs.js} +7 -7
- package/dist/{abnfDiagram-VCTEODGH-BPS-pTU2.js → abnfDiagram-VCTEODGH-Dl9Nrtbf.js} +2 -2
- package/dist/{any-language-editor-DIS7IdV1.js → any-language-editor-CBHhdCC0.js} +2 -2
- package/dist/{architectureDiagram-5GKGNRK7-DB2MHOb7.js → architectureDiagram-5GKGNRK7-DTg032fQ.js} +1 -1
- package/dist/{assertNever-HbUt8DyH.js → assertNever-ie77PjMn.js} +13 -13
- package/dist/{chat-ui-85k50jaj.js → chat-ui-BV0qyfru.js} +51 -51
- package/dist/{check-CApDh1-n.js → check-DTIv5ZZ3.js} +1 -1
- package/dist/{chunk-IMKFNOWR-CjFCedjx.js → chunk-IMKFNOWR-CsFB-2LW.js} +1 -1
- package/dist/{chunk-SHT3W25Y-Dptltg0_.js → chunk-SHT3W25Y-ByuJHeBL.js} +1 -1
- package/dist/{chunk-SVP7TREG-2jBN760m.js → chunk-SVP7TREG-9oBZhbrr.js} +1 -1
- package/dist/{chunk-TICWLB2K-CS8BE-b9.js → chunk-TICWLB2K-Bx4m0cJ1.js} +1 -1
- package/dist/{classDiagram-ZZMXUADV-HtQWp-Cm.js → classDiagram-ZZMXUADV-Cms37-C9.js} +2 -2
- package/dist/{classDiagram-v2-VYDZK3BY-DA9LETN-.js → classDiagram-v2-VYDZK3BY-qAom4Bi7.js} +2 -2
- package/dist/{code-visibility-CGcIJMGj.js → code-visibility-_-8-dvb4.js} +20 -20
- package/dist/{common-B-Ng3J3K.js → common-CNXuhphD.js} +620 -620
- package/dist/{config-B171m9zn.js → config-CvBnR945.js} +3 -3
- package/dist/{copy-icon-k2_Fx5Qv.js → copy-icon-DmtNidSa.js} +2 -2
- package/dist/{createReducer-BPPI11pP.js → createReducer-BxBCTvKn.js} +5 -5
- package/dist/{cynefinDiagram-5FMLGOSQ-eyocrg_E.js → cynefinDiagram-5FMLGOSQ-BjJZwMEz.js} +1 -1
- package/dist/{dagre-GXQ25YYZ-Dtm96Nvx.js → dagre-GXQ25YYZ-Bohu7_09.js} +1 -1
- package/dist/{data-grid-overlay-editor-CUD6F0Vj.js → data-grid-overlay-editor--4Gk_rwA.js} +1 -1
- package/dist/{diagram-S7CK7UJ4-C09TrdNO.js → diagram-S7CK7UJ4-Chl0n835.js} +1 -1
- package/dist/{diagram-UQ7AKVKN-vHUsSML6.js → diagram-UQ7AKVKN-DHx8nzy7.js} +1 -1
- package/dist/{diagram-VX7I27RA-Ch4mRgz8.js → diagram-VX7I27RA-CKdpv1U4.js} +1 -1
- package/dist/{diagram-Z3DM3KII-DyFQNdAL.js → diagram-Z3DM3KII-sY-sI--2.js} +1 -1
- package/dist/{dialog-C2Muh059.js → dialog-Cqu5Hp5U.js} +21 -21
- package/dist/{dropdown-menu-BGbNnuFq.js → dropdown-menu-DXEYk99S.js} +707 -707
- package/dist/{ebnfDiagram-PWID7BFC-N3SvlrCs.js → ebnfDiagram-PWID7BFC-CD0P2xeH.js} +2 -2
- package/dist/{erDiagram-RLTQ6QDP-RR2q2xtd.js → erDiagram-RLTQ6QDP-BaKQuYxC.js} +1 -1
- package/dist/{error-banner-3_4Lc93Y.js → error-banner-DUB_qUeL.js} +58 -58
- package/dist/{error-entries-BhNAZWU1.js → error-entries-CTb_cxXY.js} +310 -310
- package/dist/{feedback-button-DK6zUzAk.js → feedback-button-BaA4r2dl.js} +7 -7
- package/dist/{flowDiagram-HODETNUW-nxd7WhlW.js → flowDiagram-HODETNUW-BcCpHh7-.js} +2 -2
- package/dist/{formats-aUcw6MrS.js → formats-hF6fETr6.js} +1 -1
- package/dist/{glide-data-editor-O-94UN5R.js → glide-data-editor-CRn3KXyR.js} +3129 -3119
- package/dist/{glide-utils-D4UP8TGv.js → glide-utils-Bnd_k48H.js} +2 -2
- package/dist/{globals-BVjYePCn.js → globals-_FmD97Ui.js} +3 -3
- package/dist/{html-to-image-DOOtP-4-.js → html-to-image-Bbl0PeC7.js} +478 -478
- package/dist/{infoDiagram-27XIBGKW-CUYXQKGf.js → infoDiagram-27XIBGKW-DWltseIa.js} +1 -1
- package/dist/{ishikawaDiagram-5VMMS53U-v6gsCrlK.js → ishikawaDiagram-5VMMS53U-DHgepBPo.js} +1 -1
- package/dist/{kanban-definition-UXKFOSKX-z236-0Ok.js → kanban-definition-UXKFOSKX-B2_BIjBq.js} +1 -1
- package/dist/main.js +6712 -6590
- package/dist/{mermaid-C7lQ_2LJ.js → mermaid--2K0cmt9.js} +34 -34
- package/dist/{mindmap-definition-YA3MSWOX--pZF7OCY.js → mindmap-definition-YA3MSWOX-DNebycIP.js} +1 -1
- package/dist/{number-overlay-editor-jkzzCN4e.js → number-overlay-editor-Bcg25qrp.js} +1 -1
- package/dist/{pegDiagram-XKGWAZYB-Bgw0Zz_6.js → pegDiagram-XKGWAZYB-CH4U3II-.js} +2 -2
- package/dist/{pencil-RXFY-Ek8.js → pencil-MMnw1YQh.js} +1 -1
- package/dist/{pieDiagram-E7YTZNPT-B27tDYvK.js → pieDiagram-E7YTZNPT-pfcrdUCa.js} +1 -1
- package/dist/{process-output-CiX_Dl4L.js → process-output-Dw_sI4Xp.js} +4 -4
- package/dist/{railroadDiagram-O6MQD6OU-CBjPhGpj.js → railroadDiagram-O6MQD6OU-BZKpAbNG.js} +2 -2
- package/dist/{requirementDiagram-BXWQKSXE-DDDrLEw9.js → requirementDiagram-BXWQKSXE-DnyfIHe3.js} +1 -1
- package/dist/{reveal-component-CvPmNI5C.js → reveal-component-BVC491am.js} +18 -18
- package/dist/{select-Cv6lR39D.js → select-suaAxFuo.js} +286 -286
- package/dist/{sequenceDiagram-WJ2MYXX4-DQPaMygQ.js → sequenceDiagram-WJ2MYXX4-BeYuWX7N.js} +1 -1
- package/dist/{spec-BoCLWa-M.js → spec-BQNx2-D7.js} +4 -4
- package/dist/{stateDiagram-D77RDMKH-BDoDVVnq.js → stateDiagram-D77RDMKH-B8FxwAM2.js} +2 -2
- package/dist/{stateDiagram-v2-MP3YSRHH-KNko46uJ.js → stateDiagram-v2-MP3YSRHH-CzWLBqm1.js} +2 -2
- package/dist/{static-state-DgdOadIH.js → static-state-Al_hmf1n.js} +6 -6
- package/dist/style.css +1 -1
- package/dist/{swimlanes-42K2YHIH-boFjOuiI.js → swimlanes-42K2YHIH-CzKmGMts.js} +1 -1
- package/dist/{swimlanesDiagram-VR7AAH4N-BC4FLNwY.js → swimlanesDiagram-VR7AAH4N-DnWIUN_9.js} +2 -2
- package/dist/{timeline-definition-24CTP7MA-CLVPYA-v.js → timeline-definition-24CTP7MA-Czf2QN2R.js} +1 -1
- package/dist/{toast-CUuX7uXg.js → toast-CsJa1nGj.js} +66 -66
- package/dist/{tooltip-DBv8QUvS.js → tooltip-BVbJPkcl.js} +49 -49
- package/dist/{use-restore-focus-BxJh5Zfa.js → use-restore-focus-tPmARo1Q.js} +1035 -1022
- package/dist/{useDateFormatter-2k4mHRBq.js → useDateFormatter-B4ygYuUU.js} +2 -2
- package/dist/{useDeepCompareMemoize-Cb2GRQIS.js → useDeepCompareMemoize-Bcu0DkCS.js} +1 -1
- package/dist/{useLifecycle-B2fV3c8N.js → useLifecycle-MTdtnR3-.js} +1 -1
- package/dist/{vega-component-do3stf0K.js → vega-component-BhBbP4S7.js} +6 -6
- package/dist/{vennDiagram-4TSXK5OY-BLq7sNjb.js → vennDiagram-4TSXK5OY-BrHqWB1U.js} +1 -1
- package/dist/{wardleyDiagram-VM6X3IG4-oyJEWWtb.js → wardleyDiagram-VM6X3IG4-Cms1K_3v.js} +1 -1
- package/dist/{xychartDiagram-S5SC5T6Z-DMjWgQPm.js → xychartDiagram-S5SC5T6Z-_kUiODzS.js} +1 -1
- package/package.json +1 -1
- package/src/__mocks__/requests.ts +0 -2
- package/src/components/chat/__tests__/notebook-context.test.ts +199 -0
- package/src/components/chat/chat-panel.tsx +33 -15
- package/src/components/chat/notebook-context.ts +80 -0
- package/src/components/editor/cell/StopButton.tsx +3 -3
- package/src/components/editor/cell/cell-context-menu.tsx +18 -2
- package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +38 -1
- package/src/components/editor/cell/code/cell-editor.tsx +2 -1
- package/src/components/editor/cell/code/language-toggle.tsx +6 -4
- package/src/components/editor/chrome/wrapper/__tests__/document-sync-status.test.tsx +9 -6
- package/src/components/slides/slide-cell-view.tsx +2 -1
- package/src/components/ui/__tests__/fullscreen.test.tsx +56 -1
- package/src/components/ui/fullscreen.tsx +18 -2
- package/src/components/ui/slider.tsx +6 -2
- package/src/core/codemirror/copilot/getCodes.ts +1 -1
- package/src/core/edit-app.tsx +5 -4
- package/src/core/executions/__tests__/input.test.ts +1 -1
- package/src/core/executions/__tests__/interrupt.test.ts +200 -0
- package/src/core/executions/__tests__/run.test.ts +3 -3
- package/src/core/executions/index.ts +1 -0
- package/src/core/executions/interrupt.ts +49 -0
- package/src/core/islands/bridge.ts +0 -2
- package/src/core/network/requests-network.ts +0 -15
- package/src/core/network/requests-static.ts +0 -2
- package/src/core/network/requests-toasting.tsx +0 -2
- package/src/core/network/types.ts +0 -3
- package/src/core/notebook/document/__tests__/model.test.ts +4 -4
- package/src/core/notebook/document/__tests__/test-host.test.ts +6 -6
- package/src/core/notebook/document/__tests__/test-host.ts +42 -46
- package/src/core/notebook/document/__tests__/test-notebook.ts +5 -4
- package/src/core/notebook/document/sync.ts +12 -5
- package/src/core/notebook/replica/__tests__/fixtures.ts +8 -0
- package/src/core/notebook/replica/backend.ts +1 -1
- package/src/core/saving/__tests__/document.test.tsx +1 -1
- package/src/core/transports/__tests__/static.test.ts +1 -1
- package/src/core/transports/__tests__/wasm.test.ts +1 -1
- package/src/core/transports/static.ts +8 -2
- package/src/core/transports/wasm.ts +2 -4
- package/src/core/wasm/__tests__/bridge.test.ts +170 -26
- package/src/core/wasm/bridge.ts +64 -19
- package/src/core/wasm/worker/types.ts +0 -1
- package/src/plugins/impl/DataEditorPlugin.tsx +32 -21
- package/src/plugins/impl/SliderPlugin.tsx +35 -3
- package/src/plugins/impl/__test__/slider-labels.test.tsx +30 -0
- package/src/plugins/impl/__tests__/DataEditorPlugin.test.ts +103 -12
- package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +40 -0
- package/src/plugins/impl/data-editor/__tests__/glide-data-editor.test.tsx +159 -4
- package/src/plugins/impl/data-editor/glide-data-editor.tsx +30 -10
- package/src/plugins/impl/data-editor/glide-portal.tsx +5 -3
- package/src/plugins/layout/AccordionPlugin.tsx +2 -1
- package/src/plugins/layout/__tests__/AccordionPlugin.test.tsx +21 -0
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import React from "react";
|
|
4
|
+
import {
|
|
5
|
+
CompactSelection,
|
|
6
|
+
type GridColumn,
|
|
7
|
+
type GridSelection,
|
|
8
|
+
type Rectangle,
|
|
9
|
+
} from "@glideapps/glide-data-grid";
|
|
4
10
|
import { act, render, screen, waitFor } from "@testing-library/react";
|
|
5
11
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
6
12
|
import { TooltipProvider } from "@/components/ui/tooltip";
|
|
@@ -13,8 +19,13 @@ interface MockDataEditorProps {
|
|
|
13
19
|
value: { data: unknown; kind?: unknown },
|
|
14
20
|
) => void;
|
|
15
21
|
onRowAppended?: () => void;
|
|
16
|
-
getCellContent?: (cell: [number, number]) => Record<string, unknown>;
|
|
22
|
+
getCellContent?: (cell: readonly [number, number]) => Record<string, unknown>;
|
|
17
23
|
customRenderers?: unknown[];
|
|
24
|
+
columns?: readonly GridColumn[];
|
|
25
|
+
gridSelection?: GridSelection;
|
|
26
|
+
onGridSelectionChange?: (selection: GridSelection) => void;
|
|
27
|
+
onHeaderMenuClick?: (col: number, bounds: Rectangle) => void;
|
|
28
|
+
onColumnResize?: (column: GridColumn, newSize: number) => void;
|
|
18
29
|
}
|
|
19
30
|
|
|
20
31
|
const captureDataEditorProps = vi.hoisted(() =>
|
|
@@ -41,17 +52,21 @@ const capturedDataEditor = {
|
|
|
41
52
|
|
|
42
53
|
vi.mock("@glideapps/glide-data-grid", async () => {
|
|
43
54
|
const React = await import("react");
|
|
55
|
+
const { CompactSelection } = await vi.importActual<
|
|
56
|
+
typeof import("@glideapps/glide-data-grid")
|
|
57
|
+
>("@glideapps/glide-data-grid");
|
|
44
58
|
return {
|
|
45
59
|
default: React.forwardRef(function MockDataEditor(
|
|
46
60
|
props: MockDataEditorProps,
|
|
47
61
|
_ref: React.Ref<HTMLDivElement>,
|
|
48
62
|
) {
|
|
49
63
|
captureDataEditorProps(props);
|
|
64
|
+
if (props.gridSelection?.current) {
|
|
65
|
+
props.getCellContent?.(props.gridSelection.current.cell);
|
|
66
|
+
}
|
|
50
67
|
return <div data-testid="mock-data-editor" />;
|
|
51
68
|
}),
|
|
52
|
-
CompactSelection
|
|
53
|
-
empty: () => ({}),
|
|
54
|
-
},
|
|
69
|
+
CompactSelection,
|
|
55
70
|
GridCellKind: {
|
|
56
71
|
Text: "text",
|
|
57
72
|
Number: "number",
|
|
@@ -132,6 +147,111 @@ describe("GlideDataEditor portal", () => {
|
|
|
132
147
|
]);
|
|
133
148
|
});
|
|
134
149
|
|
|
150
|
+
it("keeps the grid and selection when cell data changes with the same schema", () => {
|
|
151
|
+
const { rerender } = render(<GlideDataEditor {...editorProps} />);
|
|
152
|
+
const grid = screen.getByTestId("mock-data-editor");
|
|
153
|
+
const selection: GridSelection = {
|
|
154
|
+
columns: CompactSelection.empty(),
|
|
155
|
+
rows: CompactSelection.empty(),
|
|
156
|
+
current: {
|
|
157
|
+
cell: [0, 0],
|
|
158
|
+
range: { x: 0, y: 0, width: 1, height: 1 },
|
|
159
|
+
rangeStack: [],
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
act(() =>
|
|
163
|
+
captureDataEditorProps.mock.lastCall?.[0].onGridSelectionChange?.(
|
|
164
|
+
selection,
|
|
165
|
+
),
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
rerender(
|
|
169
|
+
<GlideDataEditor
|
|
170
|
+
{...editorProps}
|
|
171
|
+
data={[{ name: "bob" }]}
|
|
172
|
+
columnFields={new Map(editorProps.columnFields)}
|
|
173
|
+
/>,
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
expect(screen.getByTestId("mock-data-editor")).toBe(grid);
|
|
177
|
+
expect(captureDataEditorProps.mock.lastCall?.[0].gridSelection).toEqual(
|
|
178
|
+
selection,
|
|
179
|
+
);
|
|
180
|
+
expect(capturedDataEditor.getCellContent?.([0, 0])).toMatchObject({
|
|
181
|
+
data: "bob",
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it.each([
|
|
186
|
+
{ operation: "remove", names: ["name"] },
|
|
187
|
+
{ operation: "rename", names: ["name", "weekday"] },
|
|
188
|
+
{ operation: "insert", names: ["name", "day", "extra"] },
|
|
189
|
+
{ operation: "reorder", names: ["day", "name"] },
|
|
190
|
+
])(
|
|
191
|
+
"preserves widths and resets selection, menu, and grid on column $operation",
|
|
192
|
+
({ names }) => {
|
|
193
|
+
const columnFields = new Map<string, "string">([
|
|
194
|
+
["name", "string"],
|
|
195
|
+
["day", "string"],
|
|
196
|
+
]);
|
|
197
|
+
const props = {
|
|
198
|
+
...editorProps,
|
|
199
|
+
columnFields,
|
|
200
|
+
data: [{ name: "alice", day: "Friday" }],
|
|
201
|
+
};
|
|
202
|
+
const { rerender } = render(<GlideDataEditor {...props} />);
|
|
203
|
+
const grid = screen.getByTestId("mock-data-editor");
|
|
204
|
+
act(() => {
|
|
205
|
+
captureDataEditorProps.mock.lastCall?.[0].onColumnResize?.(
|
|
206
|
+
{ title: "name", width: 100 },
|
|
207
|
+
240,
|
|
208
|
+
);
|
|
209
|
+
});
|
|
210
|
+
expect(
|
|
211
|
+
captureDataEditorProps.mock.lastCall?.[0].columns?.[0],
|
|
212
|
+
).toMatchObject({ width: 240 });
|
|
213
|
+
act(() => {
|
|
214
|
+
captureDataEditorProps.mock.lastCall?.[0].onGridSelectionChange?.({
|
|
215
|
+
columns: CompactSelection.fromSingleSelection(1),
|
|
216
|
+
rows: CompactSelection.fromSingleSelection(0),
|
|
217
|
+
current: {
|
|
218
|
+
cell: [1, 0],
|
|
219
|
+
range: { x: 1, y: 0, width: 1, height: 1 },
|
|
220
|
+
rangeStack: [],
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
captureDataEditorProps.mock.lastCall?.[0].onHeaderMenuClick?.(1, {
|
|
224
|
+
x: 0,
|
|
225
|
+
y: 0,
|
|
226
|
+
width: 100,
|
|
227
|
+
height: 30,
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
expect(screen.getByText("Copy column name")).toBeInTheDocument();
|
|
231
|
+
captureDataEditorProps.mockClear();
|
|
232
|
+
|
|
233
|
+
const nextFields = new Map<string, "string">(
|
|
234
|
+
names.map((name) => [name, "string"]),
|
|
235
|
+
);
|
|
236
|
+
rerender(<GlideDataEditor {...props} columnFields={nextFields} />);
|
|
237
|
+
|
|
238
|
+
expect(screen.getByTestId("mock-data-editor")).not.toBe(grid);
|
|
239
|
+
expect(screen.queryByText("Copy column name")).toBeNull();
|
|
240
|
+
for (const [rendered] of captureDataEditorProps.mock.calls) {
|
|
241
|
+
expect(rendered.gridSelection).toEqual({
|
|
242
|
+
columns: CompactSelection.empty(),
|
|
243
|
+
rows: CompactSelection.empty(),
|
|
244
|
+
});
|
|
245
|
+
expect(rendered.columns?.map((column) => column.title)).toEqual([
|
|
246
|
+
...nextFields.keys(),
|
|
247
|
+
]);
|
|
248
|
+
expect(
|
|
249
|
+
rendered.columns?.find((column) => column.title === "name"),
|
|
250
|
+
).toMatchObject({ width: 240 });
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
);
|
|
254
|
+
|
|
135
255
|
it("creates a restricted dropdown cell and registers only its renderer", () => {
|
|
136
256
|
render(
|
|
137
257
|
<TooltipProvider>
|
|
@@ -303,6 +423,41 @@ describe("GlideDataEditor portal", () => {
|
|
|
303
423
|
fullscreenContainer.remove();
|
|
304
424
|
});
|
|
305
425
|
|
|
426
|
+
it("moves the portal into a fullscreen element inside a shadow root", async () => {
|
|
427
|
+
const host = document.createElement("div");
|
|
428
|
+
host.append(document.createElement("div"));
|
|
429
|
+
const shadowRoot = host.attachShadow({ mode: "open" });
|
|
430
|
+
const target = document.createElement("div");
|
|
431
|
+
shadowRoot.append(target);
|
|
432
|
+
document.body.append(host);
|
|
433
|
+
Object.defineProperty(shadowRoot, "fullscreenElement", {
|
|
434
|
+
configurable: true,
|
|
435
|
+
get: () => target,
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
render(
|
|
439
|
+
<TooltipProvider>
|
|
440
|
+
<GlideDataEditor {...editorProps} />
|
|
441
|
+
</TooltipProvider>,
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
act(() => {
|
|
445
|
+
// The browser retargets document.fullscreenElement to the shadow host.
|
|
446
|
+
fullscreenElement = host;
|
|
447
|
+
document.dispatchEvent(new Event("fullscreenchange"));
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
await waitFor(() => {
|
|
451
|
+
const portal = target.querySelector(
|
|
452
|
+
"[data-testid='glide-data-editor-portal']",
|
|
453
|
+
);
|
|
454
|
+
expect(portal?.parentElement).toBe(target);
|
|
455
|
+
});
|
|
456
|
+
expect(host.children).toHaveLength(1);
|
|
457
|
+
|
|
458
|
+
host.remove();
|
|
459
|
+
});
|
|
460
|
+
|
|
306
461
|
it("unmounts cleanly while fullscreen is active", async () => {
|
|
307
462
|
const fullscreenContainer = document.createElement("div");
|
|
308
463
|
document.body.appendChild(fullscreenContainer);
|
|
@@ -72,13 +72,41 @@ interface GlideDataEditorProps {
|
|
|
72
72
|
onAddEdits: (edits: Edits["edits"]) => void;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
export const GlideDataEditor = ({
|
|
75
|
+
export const GlideDataEditor = (props: GlideDataEditorProps) => {
|
|
76
|
+
const [columnWidths, setColumnWidths] = useState<Record<string, number>>({});
|
|
77
|
+
const onColumnResize = useCallback((column: GridColumn, newSize: number) => {
|
|
78
|
+
setColumnWidths((prev) => ({
|
|
79
|
+
...prev,
|
|
80
|
+
[column.title]: newSize,
|
|
81
|
+
}));
|
|
82
|
+
}, []);
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
// Changing columns can leave the selection pointing to a removed column.
|
|
86
|
+
// Remount the grid to clear that selection before rendering the new schema.
|
|
87
|
+
<GlideDataEditorInner
|
|
88
|
+
key={JSON.stringify([...props.columnFields])}
|
|
89
|
+
{...props}
|
|
90
|
+
columnWidths={columnWidths}
|
|
91
|
+
onColumnResize={onColumnResize}
|
|
92
|
+
/>
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
interface GlideDataEditorInnerProps extends GlideDataEditorProps {
|
|
97
|
+
columnWidths: Readonly<Record<string, number>>;
|
|
98
|
+
onColumnResize: (column: GridColumn, newSize: number) => void;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const GlideDataEditorInner = ({
|
|
76
102
|
data,
|
|
77
103
|
columnFields,
|
|
78
104
|
columnTypes,
|
|
79
105
|
editableColumns,
|
|
80
106
|
onAddEdits,
|
|
81
|
-
|
|
107
|
+
columnWidths,
|
|
108
|
+
onColumnResize,
|
|
109
|
+
}: GlideDataEditorInnerProps) => {
|
|
82
110
|
const { theme } = useTheme();
|
|
83
111
|
const dataEditorRef = useRef<DataEditorRef>(null);
|
|
84
112
|
const portalElementRef = useRef<HTMLDivElement>(null);
|
|
@@ -90,7 +118,6 @@ export const GlideDataEditor = ({
|
|
|
90
118
|
rows: CompactSelection.empty(),
|
|
91
119
|
});
|
|
92
120
|
|
|
93
|
-
const [columnWidths, setColumnWidths] = useState<Record<string, number>>({});
|
|
94
121
|
const editableColumnSet = useMemo(
|
|
95
122
|
() => (editableColumns === "all" ? null : new Set(editableColumns)),
|
|
96
123
|
[editableColumns],
|
|
@@ -215,13 +242,6 @@ export const GlideDataEditor = ({
|
|
|
215
242
|
[columns, onAddEdits],
|
|
216
243
|
);
|
|
217
244
|
|
|
218
|
-
const onColumnResize = useCallback((column: GridColumn, newSize: number) => {
|
|
219
|
-
setColumnWidths((prev) => ({
|
|
220
|
-
...prev,
|
|
221
|
-
[column.title]: newSize,
|
|
222
|
-
}));
|
|
223
|
-
}, []);
|
|
224
|
-
|
|
225
245
|
// Only called when user edits a cell, not deletes
|
|
226
246
|
const validateCell = useCallback(
|
|
227
247
|
(cell: Item, newValue: EditableGridCell, _prevValue: GridCell): boolean => {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import React, { useEffect, useState } from "react";
|
|
4
4
|
import { createPortal } from "react-dom";
|
|
5
|
+
import { getFullScreenElement } from "@/components/ui/fullscreen";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Dismiss any open Glide Data Grid overlay by firing Escape on it.
|
|
@@ -18,7 +19,7 @@ export function dismissGlideOverlay() {
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
function getGlidePortalContainer(): Element {
|
|
21
|
-
return
|
|
22
|
+
return getFullScreenElement() ?? document.body;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
function useGlidePortalContainer(): Element {
|
|
@@ -26,8 +27,9 @@ function useGlidePortalContainer(): Element {
|
|
|
26
27
|
|
|
27
28
|
useEffect(() => {
|
|
28
29
|
const handleFullscreenChange = () => {
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
const fullScreenElement = getFullScreenElement();
|
|
31
|
+
if (fullScreenElement) {
|
|
32
|
+
setContainer(fullScreenElement);
|
|
31
33
|
} else {
|
|
32
34
|
dismissGlideOverlay();
|
|
33
35
|
setContainer(document.body);
|
|
@@ -57,7 +57,8 @@ const AccordionComponent = ({
|
|
|
57
57
|
return (
|
|
58
58
|
<AccordionItem key={index} value={index.toString()}>
|
|
59
59
|
<AccordionTrigger className="py-2 text-md">
|
|
60
|
-
{
|
|
60
|
+
{/* The host's light DOM can gain a child that has no label. */}
|
|
61
|
+
{renderHTML({ html: labels[index] ?? "" })}
|
|
61
62
|
</AccordionTrigger>
|
|
62
63
|
<AccordionContent className="text-md">{child}</AccordionContent>
|
|
63
64
|
</AccordionItem>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { render, screen } from "@testing-library/react";
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
import { AccordionPlugin } from "../AccordionPlugin";
|
|
6
|
+
|
|
7
|
+
describe("AccordionPlugin", () => {
|
|
8
|
+
it("renders a child that has no label", () => {
|
|
9
|
+
const plugin = new AccordionPlugin();
|
|
10
|
+
render(
|
|
11
|
+
plugin.render({
|
|
12
|
+
host: document.createElement("div"),
|
|
13
|
+
data: { labels: ["First"], multiple: false, expanded: [] },
|
|
14
|
+
children: [<p key="first">first</p>, <p key="extra">extra</p>],
|
|
15
|
+
}),
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
expect(screen.getByText("First")).toBeInTheDocument();
|
|
19
|
+
expect(screen.getAllByRole("button")).toHaveLength(2);
|
|
20
|
+
});
|
|
21
|
+
});
|