@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.
Files changed (44) hide show
  1. package/dist/{ConnectedDataExplorerComponent-CJaUI-S-.js → ConnectedDataExplorerComponent-f9vLjQiF.js} +13 -13
  2. package/dist/{ErrorBoundary-DXlxsrTF.js → ErrorBoundary-DhLqwSWA.js} +3 -3
  3. package/dist/{any-language-editor-P9tjkj4n.js → any-language-editor-DifE-fl7.js} +5 -5
  4. package/dist/{button-BcSml68y.js → button-BSm8IXRU.js} +2 -4
  5. package/dist/{capabilities-BYLFYTYv.js → capabilities-C6OQ5YBO.js} +1 -1
  6. package/dist/{chat-ui-BdSDiVjk.js → chat-ui-JeekqoMH.js} +16 -16
  7. package/dist/{check-Cs-KV5XY.js → check-DP7U0H0G.js} +1 -1
  8. package/dist/{code-visibility-CfBftiVM.js → code-visibility-z1wi6InA.js} +21 -21
  9. package/dist/{copy-Cs3yt6Vt.js → copy-DNq8q3zw.js} +2 -2
  10. package/dist/{dist-B96tMAst.js → dist-DIlxLFXG.js} +2 -2
  11. package/dist/{error-banner-DR7o3CbX.js → error-banner-B_GQfxxD.js} +2 -2
  12. package/dist/{esm-k8PIvuZW.js → esm-DzZVaNH4.js} +1 -1
  13. package/dist/{extends-Cq2JYHKy.js → extends-w2_S44bV.js} +4 -4
  14. package/dist/{formats-BFhkZJwd.js → formats-CO8cn5_K.js} +3 -3
  15. package/dist/{glide-data-editor--CaN-f27.js → glide-data-editor-B9K6-MJe.js} +9 -9
  16. package/dist/{html-to-image-DDENewl2.js → html-to-image-Bif4utQZ.js} +15 -15
  17. package/dist/{input-DStQIuqD.js → input-C5K4VBvB.js} +7 -7
  18. package/dist/{label-DIk0QoFe.js → label-DjoYwqmj.js} +2 -2
  19. package/dist/{loader-CTHCI-qe.js → loader-DX-IDS7n.js} +1 -1
  20. package/dist/main.js +56 -55
  21. package/dist/{mermaid-CdSvixW2.js → mermaid-hv6IckVw.js} +5 -5
  22. package/dist/{process-output-2QPcgBQg.js → process-output-TAzy-GUk.js} +3 -3
  23. package/dist/{reveal-component-BclLkYnV.js → reveal-component-kTeHzMBu.js} +11 -11
  24. package/dist/{spec-CnaxwDqy.js → spec-CQKL_D3i.js} +4 -4
  25. package/dist/{strings-BYAhZnQi.js → strings-DIy9NAmg.js} +4 -4
  26. package/dist/{swiper-component-DE94711u.js → swiper-component-CEdAYjb7.js} +2 -2
  27. package/dist/{toDate-B3IRfHhw.js → toDate-CmR_xR3P.js} +3 -3
  28. package/dist/{tooltip-B-qh7UPB.js → tooltip-f-qR0iUx.js} +3 -3
  29. package/dist/{types-DQl7jVYK.js → types-DicH9Oi0.js} +1 -1
  30. package/dist/{useAsyncData-CvO8WUy3.js → useAsyncData-CYOQb1EW.js} +1 -1
  31. package/dist/{useDateFormatter-BSoGim-G.js → useDateFormatter-Bt2qr_XS.js} +2 -2
  32. package/dist/{useDeepCompareMemoize-J3MKRdA4.js → useDeepCompareMemoize-CSHQeQEI.js} +1 -1
  33. package/dist/{useIframeCapabilities-POcFXHbw.js → useIframeCapabilities-kA09ykNq.js} +1 -1
  34. package/dist/{useLifecycle-DqPxHgAN.js → useLifecycle-CMJGqBaY.js} +3 -3
  35. package/dist/{useTheme-XMq9mNf_.js → useTheme-DYfby65G.js} +2 -2
  36. package/dist/{vega-component-CrFoZT1S.js → vega-component-DphdxaJ_.js} +10 -10
  37. package/dist/{zod-D0yOqoGe.js → zod-BUcyXSdR.js} +1 -1
  38. package/package.json +1 -1
  39. package/src/components/ui/input.tsx +1 -1
  40. package/src/core/codemirror/keymaps/__tests__/keymaps.test.ts +31 -2
  41. package/src/core/codemirror/keymaps/keymaps.ts +12 -0
  42. package/src/core/hotkeys/hotkeys.ts +0 -2
  43. package/src/plugins/impl/FormPlugin.tsx +1 -1
  44. 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 { defaultKeymap as originalDefaultKeymap } from "@codemirror/commands";
3
+ import {
4
+ copyLineDown,
5
+ copyLineUp,
6
+ defaultKeymap as originalDefaultKeymap,
7
+ } from "@codemirror/commands";
4
8
  import { describe, expect, it } from "vitest";
5
- import { HotkeyProvider } from "@/core/hotkeys/hotkeys";
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
@@ -148,7 +148,7 @@ export const FormWrapper = <T,>({
148
148
  if (evt.key === "Enter" && (evt.ctrlKey || evt.metaKey)) {
149
149
  evt.preventDefault();
150
150
  evt.stopPropagation();
151
- setValue(newValue);
151
+ formDiv.current?.requestSubmit();
152
152
  }
153
153
  }}
154
154
  >
@@ -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
+ });