@marimo-team/islands 0.23.16-dev6 → 0.23.16-dev8
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-CJaUI-S-.js → ConnectedDataExplorerComponent-f9vLjQiF.js} +13 -13
- package/dist/{ErrorBoundary-DXlxsrTF.js → ErrorBoundary-DhLqwSWA.js} +3 -3
- package/dist/{any-language-editor-P9tjkj4n.js → any-language-editor-DifE-fl7.js} +5 -5
- package/dist/{button-BcSml68y.js → button-BSm8IXRU.js} +2 -4
- package/dist/{capabilities-BYLFYTYv.js → capabilities-C6OQ5YBO.js} +1 -1
- package/dist/{chat-ui-BdSDiVjk.js → chat-ui-JeekqoMH.js} +16 -16
- package/dist/{check-Cs-KV5XY.js → check-DP7U0H0G.js} +1 -1
- package/dist/{code-visibility-CfBftiVM.js → code-visibility-z1wi6InA.js} +21 -21
- package/dist/{copy-Cs3yt6Vt.js → copy-DNq8q3zw.js} +2 -2
- package/dist/{dist-B96tMAst.js → dist-DIlxLFXG.js} +2 -2
- package/dist/{error-banner-DR7o3CbX.js → error-banner-B_GQfxxD.js} +2 -2
- package/dist/{esm-k8PIvuZW.js → esm-DzZVaNH4.js} +1 -1
- package/dist/{extends-Cq2JYHKy.js → extends-w2_S44bV.js} +4 -4
- package/dist/{formats-BFhkZJwd.js → formats-CO8cn5_K.js} +3 -3
- package/dist/{glide-data-editor--CaN-f27.js → glide-data-editor-B9K6-MJe.js} +9 -9
- package/dist/{html-to-image-DDENewl2.js → html-to-image-Bif4utQZ.js} +15 -15
- package/dist/{input-DStQIuqD.js → input-C5K4VBvB.js} +7 -7
- package/dist/{label-DIk0QoFe.js → label-DjoYwqmj.js} +2 -2
- package/dist/{loader-CTHCI-qe.js → loader-DX-IDS7n.js} +1 -1
- package/dist/main.js +56 -55
- package/dist/{mermaid-CdSvixW2.js → mermaid-hv6IckVw.js} +5 -5
- package/dist/{process-output-2QPcgBQg.js → process-output-TAzy-GUk.js} +3 -3
- package/dist/{reveal-component-BclLkYnV.js → reveal-component-kTeHzMBu.js} +11 -11
- package/dist/{spec-CnaxwDqy.js → spec-CQKL_D3i.js} +4 -4
- package/dist/{strings-BYAhZnQi.js → strings-DIy9NAmg.js} +4 -4
- package/dist/{swiper-component-DE94711u.js → swiper-component-CEdAYjb7.js} +2 -2
- package/dist/{toDate-B3IRfHhw.js → toDate-CmR_xR3P.js} +3 -3
- package/dist/{tooltip-B-qh7UPB.js → tooltip-f-qR0iUx.js} +3 -3
- package/dist/{types-DQl7jVYK.js → types-DicH9Oi0.js} +1 -1
- package/dist/{useAsyncData-CvO8WUy3.js → useAsyncData-CYOQb1EW.js} +1 -1
- package/dist/{useDateFormatter-BSoGim-G.js → useDateFormatter-Bt2qr_XS.js} +2 -2
- package/dist/{useDeepCompareMemoize-J3MKRdA4.js → useDeepCompareMemoize-CSHQeQEI.js} +1 -1
- package/dist/{useIframeCapabilities-POcFXHbw.js → useIframeCapabilities-kA09ykNq.js} +1 -1
- package/dist/{useLifecycle-DqPxHgAN.js → useLifecycle-CMJGqBaY.js} +3 -3
- package/dist/{useTheme-XMq9mNf_.js → useTheme-DYfby65G.js} +2 -2
- package/dist/{vega-component-CrFoZT1S.js → vega-component-DphdxaJ_.js} +10 -10
- package/dist/{zod-D0yOqoGe.js → zod-BUcyXSdR.js} +1 -1
- package/package.json +1 -1
- package/src/components/ui/input.tsx +1 -1
- package/src/core/codemirror/keymaps/__tests__/keymaps.test.ts +31 -2
- package/src/core/codemirror/keymaps/keymaps.ts +12 -0
- package/src/core/hotkeys/hotkeys.ts +0 -2
- package/src/plugins/impl/FormPlugin.tsx +1 -1
- package/src/plugins/impl/__tests__/FormPlugin.test.tsx +162 -0
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
copyLineDown,
|
|
5
|
+
copyLineUp,
|
|
6
|
+
defaultKeymap as originalDefaultKeymap,
|
|
7
|
+
} from "@codemirror/commands";
|
|
4
8
|
import { describe, expect, it } from "vitest";
|
|
5
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
HotkeyProvider,
|
|
11
|
+
OverridingHotkeyProvider,
|
|
12
|
+
} from "@/core/hotkeys/hotkeys";
|
|
6
13
|
import { visibleForTesting } from "../keymaps";
|
|
7
14
|
|
|
8
15
|
const { defaultKeymap, defaultVimKeymap, overrideKeymap, OVERRIDDEN_COMMANDS } =
|
|
@@ -40,4 +47,26 @@ describe("keymaps", () => {
|
|
|
40
47
|
expect(keys.some((k) => k.run === command)).toBe(true);
|
|
41
48
|
}
|
|
42
49
|
});
|
|
50
|
+
|
|
51
|
+
it.each([
|
|
52
|
+
["cell.copyLineUp", copyLineUp, "Alt-Shift-ArrowUp"],
|
|
53
|
+
["cell.copyLineDown", copyLineDown, "Alt-Shift-ArrowDown"],
|
|
54
|
+
] as const)(
|
|
55
|
+
"%s is bound to the configured hotkey, not CodeMirror's default",
|
|
56
|
+
(action, command, defaultKey) => {
|
|
57
|
+
// Without an override, the binding matches CodeMirror's default key.
|
|
58
|
+
expect(
|
|
59
|
+
overrideKeymap(HotkeyProvider.create()).find((k) => k.run === command)
|
|
60
|
+
?.key,
|
|
61
|
+
).toBe(defaultKey);
|
|
62
|
+
|
|
63
|
+
// With an override, the binding follows the user's configured key.
|
|
64
|
+
// This is what `editable: true` on these hotkeys promises.
|
|
65
|
+
expect(
|
|
66
|
+
overrideKeymap(
|
|
67
|
+
new OverridingHotkeyProvider({ [action]: "Ctrl-d" }),
|
|
68
|
+
).find((k) => k.run === command)?.key,
|
|
69
|
+
).toBe("Ctrl-d");
|
|
70
|
+
},
|
|
71
|
+
);
|
|
43
72
|
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
+
copyLineDown,
|
|
5
|
+
copyLineUp,
|
|
4
6
|
cursorCharLeft,
|
|
5
7
|
cursorCharRight,
|
|
6
8
|
cursorLineDown,
|
|
@@ -94,6 +96,8 @@ export function keymapBundle(
|
|
|
94
96
|
const OVERRIDDEN_COMMANDS = new Set<Command | undefined>([
|
|
95
97
|
toggleComment,
|
|
96
98
|
toggleBlockComment,
|
|
99
|
+
copyLineUp,
|
|
100
|
+
copyLineDown,
|
|
97
101
|
]);
|
|
98
102
|
|
|
99
103
|
const defaultKeymap = once(() => {
|
|
@@ -111,6 +115,14 @@ const overrideKeymap = (keymap: HotkeyProvider): readonly KeyBinding[] => {
|
|
|
111
115
|
key: keymap.getHotkey("cell.toggleBlockComment").key,
|
|
112
116
|
run: toggleBlockComment,
|
|
113
117
|
},
|
|
118
|
+
{
|
|
119
|
+
key: keymap.getHotkey("cell.copyLineUp").key,
|
|
120
|
+
run: copyLineUp,
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
key: keymap.getHotkey("cell.copyLineDown").key,
|
|
124
|
+
run: copyLineDown,
|
|
125
|
+
},
|
|
114
126
|
];
|
|
115
127
|
};
|
|
116
128
|
|
|
@@ -256,13 +256,11 @@ const DEFAULT_HOT_KEY = {
|
|
|
256
256
|
name: "Copy line(s) up",
|
|
257
257
|
group: "Editing",
|
|
258
258
|
key: "Alt-Shift-ArrowUp",
|
|
259
|
-
editable: false,
|
|
260
259
|
},
|
|
261
260
|
"cell.copyLineDown": {
|
|
262
261
|
name: "Copy line(s) down",
|
|
263
262
|
group: "Editing",
|
|
264
263
|
key: "Alt-Shift-ArrowDown",
|
|
265
|
-
editable: false,
|
|
266
264
|
},
|
|
267
265
|
|
|
268
266
|
// Markdown
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
4
|
+
import { describe, expect, it, vi } from "vitest";
|
|
5
|
+
import { FormWrapper } from "../FormPlugin";
|
|
6
|
+
|
|
7
|
+
describe("FormPlugin", () => {
|
|
8
|
+
it("submits the form on Ctrl+Enter and calls setValue", async () => {
|
|
9
|
+
const setValue = vi.fn();
|
|
10
|
+
const validate = vi.fn().mockResolvedValue(null);
|
|
11
|
+
|
|
12
|
+
render(
|
|
13
|
+
<FormWrapper
|
|
14
|
+
currentValue="old-val"
|
|
15
|
+
newValue="new-val"
|
|
16
|
+
setValue={setValue}
|
|
17
|
+
validate={validate}
|
|
18
|
+
shouldValidate={false}
|
|
19
|
+
clearOnSubmit={false}
|
|
20
|
+
submitButtonLabel="Submit"
|
|
21
|
+
bordered={true}
|
|
22
|
+
loading={false}
|
|
23
|
+
submitButtonDisabled={false}
|
|
24
|
+
showClearButton={false}
|
|
25
|
+
clearButtonLabel="Clear"
|
|
26
|
+
label={null}
|
|
27
|
+
>
|
|
28
|
+
<input data-testid="input" />
|
|
29
|
+
</FormWrapper>,
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const input = screen.getByTestId("input");
|
|
33
|
+
|
|
34
|
+
// Simulate Ctrl+Enter on input
|
|
35
|
+
fireEvent.keyDown(input, { key: "Enter", ctrlKey: true });
|
|
36
|
+
|
|
37
|
+
await waitFor(() => {
|
|
38
|
+
expect(setValue).toHaveBeenCalledWith("new-val");
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("submits the form on Ctrl+Enter and runs validation when shouldValidate is true", async () => {
|
|
43
|
+
const setValue = vi.fn();
|
|
44
|
+
const validate = vi.fn().mockResolvedValue("Validation failed message");
|
|
45
|
+
|
|
46
|
+
render(
|
|
47
|
+
<FormWrapper
|
|
48
|
+
currentValue="old-val"
|
|
49
|
+
newValue="new-val"
|
|
50
|
+
setValue={setValue}
|
|
51
|
+
validate={validate}
|
|
52
|
+
shouldValidate={true}
|
|
53
|
+
clearOnSubmit={false}
|
|
54
|
+
submitButtonLabel="Submit"
|
|
55
|
+
bordered={true}
|
|
56
|
+
loading={false}
|
|
57
|
+
submitButtonDisabled={false}
|
|
58
|
+
showClearButton={false}
|
|
59
|
+
clearButtonLabel="Clear"
|
|
60
|
+
label={null}
|
|
61
|
+
>
|
|
62
|
+
<input data-testid="input" />
|
|
63
|
+
</FormWrapper>,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const input = screen.getByTestId("input");
|
|
67
|
+
|
|
68
|
+
// Simulate Ctrl+Enter on input
|
|
69
|
+
fireEvent.keyDown(input, { key: "Enter", ctrlKey: true });
|
|
70
|
+
|
|
71
|
+
await waitFor(() => {
|
|
72
|
+
expect(validate).toHaveBeenCalledWith({ value: "new-val" });
|
|
73
|
+
// should not submit (setValue not called) due to validation failure
|
|
74
|
+
expect(setValue).not.toHaveBeenCalled();
|
|
75
|
+
// error message displayed
|
|
76
|
+
expect(screen.getByText("Validation failed message")).toBeInTheDocument();
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("clears inputs on submission when clearOnSubmit is true", async () => {
|
|
81
|
+
const setValue = vi.fn();
|
|
82
|
+
const validate = vi.fn().mockResolvedValue(null);
|
|
83
|
+
|
|
84
|
+
let mockUIElement: (HTMLElement & { reset?: () => void }) | null = null;
|
|
85
|
+
const mockReset = vi.fn();
|
|
86
|
+
|
|
87
|
+
render(
|
|
88
|
+
<FormWrapper
|
|
89
|
+
currentValue="old-val"
|
|
90
|
+
newValue="new-val"
|
|
91
|
+
setValue={setValue}
|
|
92
|
+
validate={validate}
|
|
93
|
+
shouldValidate={false}
|
|
94
|
+
clearOnSubmit={true}
|
|
95
|
+
submitButtonLabel="Submit"
|
|
96
|
+
bordered={true}
|
|
97
|
+
loading={false}
|
|
98
|
+
submitButtonDisabled={false}
|
|
99
|
+
showClearButton={false}
|
|
100
|
+
clearButtonLabel="Clear"
|
|
101
|
+
label={null}
|
|
102
|
+
>
|
|
103
|
+
<div
|
|
104
|
+
ref={(el) => {
|
|
105
|
+
if (el && !mockUIElement) {
|
|
106
|
+
mockUIElement = document.createElement("marimo-ui-element");
|
|
107
|
+
mockUIElement.setAttribute("data-testid", "mock-ui-element");
|
|
108
|
+
mockUIElement.reset = mockReset;
|
|
109
|
+
const inputEl = document.createElement("input");
|
|
110
|
+
inputEl.type = "text";
|
|
111
|
+
mockUIElement.appendChild(inputEl);
|
|
112
|
+
el.appendChild(mockUIElement);
|
|
113
|
+
}
|
|
114
|
+
}}
|
|
115
|
+
/>
|
|
116
|
+
</FormWrapper>,
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const input = screen.getByRole("textbox");
|
|
120
|
+
|
|
121
|
+
// Simulate Ctrl+Enter on input
|
|
122
|
+
fireEvent.keyDown(input, { key: "Enter", ctrlKey: true });
|
|
123
|
+
|
|
124
|
+
await waitFor(() => {
|
|
125
|
+
expect(setValue).toHaveBeenCalledWith("new-val");
|
|
126
|
+
expect(mockReset).toHaveBeenCalled();
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("does not submit on Shift+Enter", async () => {
|
|
131
|
+
const setValue = vi.fn();
|
|
132
|
+
const validate = vi.fn().mockResolvedValue(null);
|
|
133
|
+
|
|
134
|
+
render(
|
|
135
|
+
<FormWrapper
|
|
136
|
+
currentValue="old-val"
|
|
137
|
+
newValue="new-val"
|
|
138
|
+
setValue={setValue}
|
|
139
|
+
validate={validate}
|
|
140
|
+
shouldValidate={false}
|
|
141
|
+
clearOnSubmit={false}
|
|
142
|
+
submitButtonLabel="Submit"
|
|
143
|
+
bordered={true}
|
|
144
|
+
loading={false}
|
|
145
|
+
submitButtonDisabled={false}
|
|
146
|
+
showClearButton={false}
|
|
147
|
+
clearButtonLabel="Clear"
|
|
148
|
+
label={null}
|
|
149
|
+
>
|
|
150
|
+
<input data-testid="input" />
|
|
151
|
+
</FormWrapper>,
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
const input = screen.getByTestId("input");
|
|
155
|
+
|
|
156
|
+
// Simulate Shift+Enter on input
|
|
157
|
+
fireEvent.keyDown(input, { key: "Enter", shiftKey: true });
|
|
158
|
+
|
|
159
|
+
expect(setValue).not.toHaveBeenCalled();
|
|
160
|
+
expect(validate).not.toHaveBeenCalled();
|
|
161
|
+
});
|
|
162
|
+
});
|