Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/olive-snakes-wink.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@stackoverflow/stacks-editor": patch
---

Actually register the Snippets open command (rather than just showing it in the tooltip)
25 changes: 14 additions & 11 deletions plugins/official/stack-snippets/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,13 @@ export function openSnippetModal(options?: StackSnippetOptions): MenuCommand {
};
}

/**
* Snippets are comprised of a container around customized codeblocks. Some of the default behaviour for key-binds makes them behave
* very strangely.
*
* In these cases, we override the command to (contextually) do nothing if the current context is a snippet
* This is possible because returning truthy consumes the event.
* **/
const swallowSnippetCommand = (state: EditorState): boolean => {
const fromNodeType = state.selection.$from.node().type.name;

Expand All @@ -129,18 +136,14 @@ const swallowSnippetCommand = (state: EditorState): boolean => {
}
};

export const swallowedCommandList = {
export const OPEN_SNIPPET_SHORTCUT = "Mod-9";

export const commandList = (opts?: StackSnippetOptions) => ({
"Mod-Enter": swallowSnippetCommand,
"Shift-Enter": swallowSnippetCommand,
"Mod-r": swallowSnippetCommand,
};
[OPEN_SNIPPET_SHORTCUT]: openSnippetModal(opts),
});

/**
* Snippets are comprised of a container around customized codeblocks. Some of the default behaviour for key-binds makes them behave
* very strangely.
*
* In these cases, we override the command to (contextually) do nothing if the current context is a snippet
* This is possible because returning truthy consumes the event.
* **/
export const stackSnippetCommandRedactor =
caseNormalizeKeymap(swallowedCommandList);
export const stackSnippetCommandShortcuts = (opts?: StackSnippetOptions) =>
caseNormalizeKeymap(commandList(opts));
7 changes: 5 additions & 2 deletions plugins/official/stack-snippets/src/stackSnippetPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { EditorView } from "prosemirror-view";
import { StackSnippetView } from "./snippet-view";
import { StackSnippetOptions } from "./common";
import { stackSnippetPasteHandler } from "./paste-handler";
import { openSnippetModal, stackSnippetCommandRedactor } from "./commands";
import { openSnippetModal, stackSnippetCommandShortcuts } from "./commands";

/**
* Build the StackSnippet plugin using hoisted options that can be specified at runtime
Expand All @@ -30,7 +30,10 @@ export const stackSnippetPlugin: (opts?: StackSnippetOptions) => EditorPlugin =
return new StackSnippetView(node, view, getPos, opts);
},
},
plugins: [stackSnippetPasteHandler, stackSnippetCommandRedactor],
plugins: [
stackSnippetPasteHandler,
stackSnippetCommandShortcuts(opts),
],
},
extendSchema: (schema) => {
schema.nodes = schema.nodes.append(stackSnippetRichTextNodeSpec);
Expand Down
71 changes: 59 additions & 12 deletions plugins/official/stack-snippets/test/commands.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,17 +227,28 @@ describe("commands", () => {
);
});

describe("redactor", () => {
//Note: we're testing this functionality once with a command that is universal across Macs and PC.
// In the pipeline this is likely using a Linux environment, in which case "Mod" means "Ctrl" too, but
// the main concern is on other development environments.
describe("shortcuts", () => {
//Stolen eagerly from the Prosemirror-keymap git https://github.com/ProseMirror/prosemirror-keymap/blob/9df35bd441aa60b3ad620da66e0e3f75cd774075/src/keymap.ts#L5
const mac =
typeof navigator != "undefined"
? /Mac|iP(hone|[oa]d)/.test(navigator.platform)
: false;

it("should swallow commands when in a Snippet context", () => {
const view = richView(`${validBegin}${validJs}${validEnd}`);
const expectedHTML = view.editorView.dom.innerHTML;
const event = new KeyboardEvent("keydown", {
ctrlKey: true,
key: "Enter",
});
let event: KeyboardEvent;
if (mac) {
event = new KeyboardEvent("keydown", {
metaKey: true,
key: "Enter",
});
} else {
event = new KeyboardEvent("keydown", {
ctrlKey: true,
key: "Enter",
});
}

view.editorView.someProp("handleKeyDown", (f) =>
f(view.editorView, event)
Expand All @@ -250,10 +261,18 @@ describe("commands", () => {
it("should not swallow commands when in a non-Snippet context", () => {
const view = richView("```javascript\nconsole.log('test');\n```");
const expectedHTML = view.editorView.dom.innerHTML;
const event = new KeyboardEvent("keydown", {
ctrlKey: true,
key: "Enter",
});
let event: KeyboardEvent;
if (mac) {
event = new KeyboardEvent("keydown", {
metaKey: true,
key: "Enter",
});
} else {
event = new KeyboardEvent("keydown", {
ctrlKey: true,
key: "Enter",
});
}

view.editorView.someProp("handleKeyDown", (f) =>
f(view.editorView, event)
Expand All @@ -262,5 +281,33 @@ describe("commands", () => {
//The Dom is not the same - a change has occured
expect(view.editorView.dom.innerHTML).not.toBe(expectedHTML);
});

it("should trigger the openModal event when shortcut pressed", () => {
let openSnippetTriggered = false;
const view = richView("```javascript\nconsole.log('test');\n```", {
openSnippetsModal: () => {
openSnippetTriggered = true;
},
renderer: () => Promise.resolve(null),
});
let event: KeyboardEvent;
if (mac) {
event = new KeyboardEvent("keydown", {
metaKey: true,
key: "9",
});
} else {
event = new KeyboardEvent("keydown", {
ctrlKey: true,
key: "9",
});
}

view.editorView.someProp("handleKeyDown", (f) =>
f(view.editorView, event)
);

expect(openSnippetTriggered).toBe(true);
});
});
});
Loading