DEV: API to add keyboard shortcuts to help modal (#16075)

This commit is contained in:
Mark VanLandingham 2022-03-01 14:37:26 -06:00 committed by GitHub
parent c9dab6fd08
commit c33cf3c5e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 384 additions and 283 deletions

View File

@ -5,6 +5,8 @@ export default Component.extend({
fixed: false,
submitOnEnter: true,
dismissable: true,
attributeBindings: ["tabindex"],
tabindex: -1,
didInsertElement() {
this._super(...arguments);

View File

@ -185,9 +185,14 @@ export default Component.extend({
!autofocusedElement ||
document.activeElement !== autofocusedElement
) {
innerContainer
.querySelectorAll(focusableElements + ", button:not(.modal-close)")[0]
?.focus();
// if there's not autofocus, or the activeElement, is not the autofocusable element
// attempt to focus the first of the focusable elements or just the modal-body
// to make it possible to scroll with arrow down/up
(
innerContainer.querySelector(
focusableElements + ", button:not(.modal-close)"
) || innerContainer.querySelector(".modal-body")
)?.focus();
}
return;

View File

@ -2,17 +2,28 @@ import Controller from "@ember/controller";
import I18n from "I18n";
import { translateModKey } from "discourse/lib/utilities";
import ModalFunctionality from "discourse/mixins/modal-functionality";
import { extraKeyboardShortcutsHelp } from "discourse/lib/keyboard-shortcuts";
const KEY = "keyboard_shortcuts_help";
const SHIFT = I18n.t("shortcut_modifier_key.shift");
const ALT = translateModKey("Alt");
const META = translateModKey("Meta");
const CTRL = I18n.t("shortcut_modifier_key.ctrl");
const ENTER = I18n.t("shortcut_modifier_key.enter");
const COMMA = I18n.t(`${KEY}.shortcut_key_delimiter_comma`);
const PLUS = I18n.t(`${KEY}.shortcut_key_delimiter_plus`);
const translationForExtraShortcuts = {
shift: SHIFT,
alt: ALT,
meta: META,
ctrl: CTRL,
enter: ENTER,
comma: COMMA,
plus: PLUS,
};
function buildHTML(keys1, keys2, keysDelimiter, shortcutsDelimiter) {
const allKeys = [keys1, keys2]
.reject((keys) => keys.length === 0)
@ -59,184 +70,272 @@ export default Controller.extend(ModalFunctionality, {
},
_defineShortcuts() {
this.set("shortcuts", {
jump_to: {
home: buildShortcut("jump_to.home", { keys1: ["g", "h"] }),
latest: buildShortcut("jump_to.latest", { keys1: ["g", "l"] }),
new: buildShortcut("jump_to.new", { keys1: ["g", "n"] }),
unread: buildShortcut("jump_to.unread", { keys1: ["g", "u"] }),
categories: buildShortcut("jump_to.categories", { keys1: ["g", "c"] }),
top: buildShortcut("jump_to.top", { keys1: ["g", "t"] }),
bookmarks: buildShortcut("jump_to.bookmarks", { keys1: ["g", "b"] }),
profile: buildShortcut("jump_to.profile", { keys1: ["g", "p"] }),
messages: buildShortcut("jump_to.messages", { keys1: ["g", "m"] }),
drafts: buildShortcut("jump_to.drafts", { keys1: ["g", "d"] }),
next: buildShortcut("jump_to.next", { keys1: ["g", "j"] }),
previous: buildShortcut("jump_to.previous", { keys1: ["g", "k"] }),
},
navigation: {
back: buildShortcut("navigation.back", { keys1: ["u"] }),
jump: buildShortcut("navigation.jump", { keys1: ["#"] }),
up_down: buildShortcut("navigation.up_down", {
keys1: ["k"],
keys2: ["j"],
shortcutsDelimiter: "slash",
}),
open: buildShortcut("navigation.open", {
keys1: ["o"],
keys2: [ENTER],
}),
next_prev: buildShortcut("navigation.next_prev", {
keys1: [SHIFT, "j"],
keys2: [SHIFT, "k"],
keysDelimiter: PLUS,
shortcutsDelimiter: "slash",
}),
go_to_unread_post: buildShortcut("navigation.go_to_unread_post", {
keys1: [SHIFT, "l"],
keysDelimiter: PLUS,
}),
},
let shortcuts = {
jump_to: { shortcuts: this._buildJumpToSection() },
application: {
hamburger_menu: buildShortcut("application.hamburger_menu", {
keys1: ["="],
}),
user_profile_menu: buildShortcut("application.user_profile_menu", {
keys1: ["p"],
}),
create: buildShortcut("application.create", { keys1: ["c"] }),
show_incoming_updated_topics: buildShortcut(
"application.show_incoming_updated_topics",
{ keys1: ["."] }
),
search: buildShortcut("application.search", {
keys1: ["/"],
keys2: [CTRL, ALT, "f"],
keysDelimiter: PLUS,
}),
help: buildShortcut("application.help", { keys1: ["?"] }),
dismiss_new: buildShortcut("application.dismiss_new", {
keys1: ["x", "r"],
}),
dismiss_topics: buildShortcut("application.dismiss_topics", {
keys1: ["x", "t"],
}),
log_out: buildShortcut("application.log_out", {
keys1: [SHIFT, "z"],
keys2: [SHIFT, "z"],
keysDelimiter: PLUS,
shortcutsDelimiter: "space",
}),
},
composing: {
return: buildShortcut("composing.return", {
keys1: [SHIFT, "c"],
keysDelimiter: PLUS,
}),
fullscreen: buildShortcut("composing.fullscreen", {
keys1: [SHIFT, "F11"],
keysDelimiter: PLUS,
}),
},
bookmarks: {
enter: buildShortcut("bookmarks.enter", { keys1: [ENTER] }),
later_today: buildShortcut("bookmarks.later_today", {
keys1: ["l", "t"],
shortcutsDelimiter: "space",
}),
later_this_week: buildShortcut("bookmarks.later_this_week", {
keys1: ["l", "w"],
shortcutsDelimiter: "space",
}),
tomorrow: buildShortcut("bookmarks.tomorrow", {
keys1: ["n", "d"],
shortcutsDelimiter: "space",
}),
next_business_week: buildShortcut("bookmarks.next_business_week", {
keys1: ["n", "b", "w"],
shortcutsDelimiter: "space",
}),
next_business_day: buildShortcut("bookmarks.next_business_day", {
keys1: ["n", "b", "d"],
shortcutsDelimiter: "space",
}),
custom: buildShortcut("bookmarks.custom", {
keys1: ["c", "r"],
shortcutsDelimiter: "space",
}),
none: buildShortcut("bookmarks.none", {
keys1: ["n", "r"],
shortcutsDelimiter: "space",
}),
delete: buildShortcut("bookmarks.delete", {
keys1: ["d", "d"],
shortcutsDelimiter: "space",
}),
shortcuts: {
hamburger_menu: buildShortcut("application.hamburger_menu", {
keys1: ["="],
}),
user_profile_menu: buildShortcut("application.user_profile_menu", {
keys1: ["p"],
}),
create: buildShortcut("application.create", { keys1: ["c"] }),
show_incoming_updated_topics: buildShortcut(
"application.show_incoming_updated_topics",
{ keys1: ["."] }
),
search: buildShortcut("application.search", {
keys1: ["/"],
keys2: [CTRL, ALT, "f"],
keysDelimiter: PLUS,
}),
help: buildShortcut("application.help", { keys1: ["?"] }),
dismiss_new: buildShortcut("application.dismiss_new", {
keys1: ["x", "r"],
}),
dismiss_topics: buildShortcut("application.dismiss_topics", {
keys1: ["x", "t"],
}),
log_out: buildShortcut("application.log_out", {
keys1: [SHIFT, "z"],
keys2: [SHIFT, "z"],
keysDelimiter: PLUS,
shortcutsDelimiter: "space",
}),
},
},
actions: {
bookmark_topic: buildShortcut("actions.bookmark_topic", {
keys1: ["f"],
}),
reply_as_new_topic: buildShortcut("actions.reply_as_new_topic", {
keys1: ["t"],
}),
reply_topic: buildShortcut("actions.reply_topic", {
keys1: [SHIFT, "r"],
keysDelimiter: PLUS,
}),
reply_post: buildShortcut("actions.reply_post", { keys1: ["r"] }),
quote_post: buildShortcut("actions.quote_post", { keys1: ["q"] }),
pin_unpin_topic: buildShortcut("actions.pin_unpin_topic", {
keys1: [SHIFT, "p"],
keysDelimiter: PLUS,
}),
share_topic: buildShortcut("actions.share_topic", {
keys1: [SHIFT, "s"],
keysDelimiter: PLUS,
}),
share_post: buildShortcut("actions.share_post", { keys1: ["s"] }),
like: buildShortcut("actions.like", { keys1: ["l"] }),
flag: buildShortcut("actions.flag", { keys1: ["!"] }),
bookmark: buildShortcut("actions.bookmark", { keys1: ["b"] }),
edit: buildShortcut("actions.edit", { keys1: ["e"] }),
delete: buildShortcut("actions.delete", { keys1: ["d"] }),
mark_muted: buildShortcut("actions.mark_muted", { keys1: ["m", "m"] }),
mark_regular: buildShortcut("actions.mark_regular", {
keys1: ["m", "r"],
}),
mark_tracking: buildShortcut("actions.mark_tracking", {
keys1: ["m", "t"],
}),
mark_watching: buildShortcut("actions.mark_watching", {
keys1: ["m", "w"],
}),
print: buildShortcut("actions.print", {
keys1: [translateModKey("Meta"), "p"],
keysDelimiter: PLUS,
}),
defer: buildShortcut("actions.defer", {
keys1: [SHIFT, "u"],
keysDelimiter: PLUS,
}),
topic_admin_actions: buildShortcut("actions.topic_admin_actions", {
keys1: [SHIFT, "a"],
keysDelimiter: PLUS,
}),
shortcuts: {
bookmark_topic: buildShortcut("actions.bookmark_topic", {
keys1: ["f"],
}),
reply_as_new_topic: buildShortcut("actions.reply_as_new_topic", {
keys1: ["t"],
}),
reply_topic: buildShortcut("actions.reply_topic", {
keys1: [SHIFT, "r"],
keysDelimiter: PLUS,
}),
reply_post: buildShortcut("actions.reply_post", { keys1: ["r"] }),
quote_post: buildShortcut("actions.quote_post", { keys1: ["q"] }),
pin_unpin_topic: buildShortcut("actions.pin_unpin_topic", {
keys1: [SHIFT, "p"],
keysDelimiter: PLUS,
}),
share_topic: buildShortcut("actions.share_topic", {
keys1: [SHIFT, "s"],
keysDelimiter: PLUS,
}),
share_post: buildShortcut("actions.share_post", { keys1: ["s"] }),
like: buildShortcut("actions.like", { keys1: ["l"] }),
flag: buildShortcut("actions.flag", { keys1: ["!"] }),
bookmark: buildShortcut("actions.bookmark", { keys1: ["b"] }),
edit: buildShortcut("actions.edit", { keys1: ["e"] }),
delete: buildShortcut("actions.delete", { keys1: ["d"] }),
mark_muted: buildShortcut("actions.mark_muted", {
keys1: ["m", "m"],
}),
mark_regular: buildShortcut("actions.mark_regular", {
keys1: ["m", "r"],
}),
mark_tracking: buildShortcut("actions.mark_tracking", {
keys1: ["m", "t"],
}),
mark_watching: buildShortcut("actions.mark_watching", {
keys1: ["m", "w"],
}),
print: buildShortcut("actions.print", {
keys1: [META, "p"],
keysDelimiter: PLUS,
}),
defer: buildShortcut("actions.defer", {
keys1: [SHIFT, "u"],
keysDelimiter: PLUS,
}),
topic_admin_actions: buildShortcut("actions.topic_admin_actions", {
keys1: [SHIFT, "a"],
keysDelimiter: PLUS,
}),
},
},
navigation: {
shortcuts: {
back: buildShortcut("navigation.back", { keys1: ["u"] }),
jump: buildShortcut("navigation.jump", { keys1: ["#"] }),
up_down: buildShortcut("navigation.up_down", {
keys1: ["k"],
keys2: ["j"],
shortcutsDelimiter: "slash",
}),
open: buildShortcut("navigation.open", {
keys1: ["o"],
keys2: [ENTER],
}),
next_prev: buildShortcut("navigation.next_prev", {
keys1: [SHIFT, "j"],
keys2: [SHIFT, "k"],
keysDelimiter: PLUS,
shortcutsDelimiter: "slash",
}),
go_to_unread_post: buildShortcut("navigation.go_to_unread_post", {
keys1: [SHIFT, "l"],
keysDelimiter: PLUS,
}),
},
},
composing: {
shortcuts: {
return: buildShortcut("composing.return", {
keys1: [SHIFT, "c"],
keysDelimiter: PLUS,
}),
fullscreen: buildShortcut("composing.fullscreen", {
keys1: [SHIFT, "F11"],
keysDelimiter: PLUS,
}),
},
},
bookmarks: {
shortcuts: {
enter: buildShortcut("bookmarks.enter", { keys1: [ENTER] }),
later_today: buildShortcut("bookmarks.later_today", {
keys1: ["l", "t"],
shortcutsDelimiter: "space",
}),
later_this_week: buildShortcut("bookmarks.later_this_week", {
keys1: ["l", "w"],
shortcutsDelimiter: "space",
}),
tomorrow: buildShortcut("bookmarks.tomorrow", {
keys1: ["n", "d"],
shortcutsDelimiter: "space",
}),
next_business_week: buildShortcut("bookmarks.next_business_week", {
keys1: ["n", "b", "w"],
shortcutsDelimiter: "space",
}),
next_business_day: buildShortcut("bookmarks.next_business_day", {
keys1: ["n", "b", "d"],
shortcutsDelimiter: "space",
}),
custom: buildShortcut("bookmarks.custom", {
keys1: ["c", "r"],
shortcutsDelimiter: "space",
}),
none: buildShortcut("bookmarks.none", {
keys1: ["n", "r"],
shortcutsDelimiter: "space",
}),
delete: buildShortcut("bookmarks.delete", {
keys1: ["d", "d"],
shortcutsDelimiter: "space",
}),
},
},
search_menu: {
prev_next: buildShortcut("search_menu.prev_next", {
keys1: ["↑"],
keys2: ["↓"],
shortcutsDelimiter: "slash",
}),
insert_url: buildShortcut("search_menu.insert_url", {
keys1: ["a"],
}),
full_page_search: buildShortcut("search_menu.full_page_search", {
keys1: [translateModKey("Meta"), "Enter"],
keysDelimiter: PLUS,
}),
shortcuts: {
prev_next: buildShortcut("search_menu.prev_next", {
keys1: ["↑"],
keys2: ["↓"],
shortcutsDelimiter: "slash",
}),
insert_url: buildShortcut("search_menu.insert_url", {
keys1: ["a"],
}),
full_page_search: buildShortcut("search_menu.full_page_search", {
keys1: [META, "Enter"],
keysDelimiter: PLUS,
}),
},
},
};
this._buildExtraShortcuts(shortcuts);
this._addCountsToShortcutCategories(shortcuts);
this.set("shortcuts", shortcuts);
},
_buildExtraShortcuts(shortcuts) {
for (const [category, helps] of Object.entries(
extraKeyboardShortcutsHelp
)) {
helps.forEach((help) => {
if (!shortcuts[category]) {
shortcuts[category] = {};
}
if (!shortcuts[category].shortcuts) {
shortcuts[category].shortcuts = {};
}
shortcuts[category].shortcuts[help.name] = buildShortcut(
help.name,
this._transformExtraDefinition(help.definition)
);
});
}
},
_addCountsToShortcutCategories(shortcuts) {
for (const [category, shortcutCategory] of Object.entries(shortcuts)) {
shortcuts[category].count = Object.keys(
shortcutCategory.shortcuts
).length;
}
},
_transformExtraDefinition(definition) {
if (definition.keys1) {
definition.keys1 = definition.keys1.map((key) =>
this._translateKeys(key)
);
}
if (definition.keys2) {
definition.keys2 = definition.keys2.map((key) =>
this._translateKeys(key)
);
}
if (definition.keysDelimiter) {
definition.keysDelimiter = this._translateKeys(definition.keysDelimiter);
}
if (definition.shortcutsDelimiter) {
definition.shortcutsDelimiter = this._translateKeys(
definition.shortcutsDelimiter
);
}
return definition;
},
_translateKeys(string) {
for (const [matcher, replacement] of Object.entries(
translationForExtraShortcuts
)) {
string = string.replace(matcher, replacement);
}
return string;
},
_buildJumpToSection() {
const shortcuts = {
home: buildShortcut("jump_to.home", { keys1: ["g", "h"] }),
latest: buildShortcut("jump_to.latest", { keys1: ["g", "l"] }),
new: buildShortcut("jump_to.new", { keys1: ["g", "n"] }),
unread: buildShortcut("jump_to.unread", { keys1: ["g", "u"] }),
categories: buildShortcut("jump_to.categories", { keys1: ["g", "c"] }),
top: buildShortcut("jump_to.top", { keys1: ["g", "t"] }),
bookmarks: buildShortcut("jump_to.bookmarks", { keys1: ["g", "b"] }),
profile: buildShortcut("jump_to.profile", { keys1: ["g", "p"] }),
};
if (this.siteSettings.enable_personal_messages) {
shortcuts.messages = buildShortcut("jump_to.messages", {
keys1: ["g", "m"],
});
}
Object.assign(shortcuts, {
drafts: buildShortcut("jump_to.drafts", { keys1: ["g", "d"] }),
next: buildShortcut("jump_to.next", { keys1: ["g", "j"] }),
previous: buildShortcut("jump_to.previous", { keys1: ["g", "k"] }),
});
return shortcuts;
},
});

View File

@ -13,6 +13,24 @@ import { INPUT_DELAY } from "discourse-common/config/environment";
import { ajax } from "discourse/lib/ajax";
import { headerOffset } from "discourse/lib/offset-calculator";
let extraKeyboardShortcutsHelp = {};
function addExtraKeyboardShortcutHelp(help) {
const category = help.category;
if (extraKeyboardShortcutsHelp[category]) {
extraKeyboardShortcutsHelp[category] = extraKeyboardShortcutsHelp[
category
].concat([help]);
} else {
extraKeyboardShortcutsHelp[category] = [help];
}
}
export function clearExtraKeyboardShortcutHelp() {
extraKeyboardShortcutsHelp = {};
}
export { extraKeyboardShortcutsHelp as extraKeyboardShortcutsHelp };
const DEFAULT_BINDINGS = {
"!": { postAction: "showFlags" },
"#": { handler: "goToPost", anonymous: true },
@ -209,6 +227,16 @@ export default {
* - path - a specific path to limit the shortcut to .e.g /latest
* - postAction - binds the shortcut to fire the specified post action when a
* post is selected
* - help - adds the shortcut to the keyboard shortcuts modal. `help` is an object
* with key/value pairs
* {
* category: String,
* name: String,
* definition: (See function `buildShortcut` in
* app/assets/javascripts/discourse/app/controllers/keyboard-shortcuts-help.js
* for definition structure)
* }
*
* - click - allows to provide a selector on which a click event
* will be triggered, eg: { click: ".topic.last .title" }
**/
@ -218,6 +246,9 @@ export default {
shortcut = shortcut.trim();
let newBinding = Object.assign({ handler: callback }, opts);
this.bindKey(shortcut, newBinding);
if (opts.help) {
addExtraKeyboardShortcutHelp(opts.help);
}
},
// unbinds all the shortcuts in a key binding object e.g.

View File

@ -1,107 +1,12 @@
{{#d-modal-body id="keyboard-shortcuts-help"}}
<div class="column">
<section>
<h4>{{i18n "keyboard_shortcuts_help.jump_to.title"}}</h4>
{{#each-in shortcuts as |category shortcutCategory|}}
<section class="shortcut-category span-{{shortcutCategory.count}} shortcut-category-{{category}}">
<h4>{{i18n (concat "keyboard_shortcuts_help." category ".title")}}</h4>
<ul>
<li>{{html-safe shortcuts.jump_to.home}}</li>
<li>{{html-safe shortcuts.jump_to.latest}}</li>
<li>{{html-safe shortcuts.jump_to.new}}</li>
<li>{{html-safe shortcuts.jump_to.unread}}</li>
<li>{{html-safe shortcuts.jump_to.categories}}</li>
<li>{{html-safe shortcuts.jump_to.top}}</li>
<li>{{html-safe shortcuts.jump_to.bookmarks}}</li>
<li>{{html-safe shortcuts.jump_to.profile}}</li>
{{#if siteSettings.enable_personal_messages}}
<li>{{html-safe shortcuts.jump_to.messages}}</li>
{{/if}}
<li>{{html-safe shortcuts.jump_to.drafts}}</li>
<li>{{html-safe shortcuts.jump_to.next}}</li>
<li>{{html-safe shortcuts.jump_to.previous}}</li>
{{#each-in shortcutCategory.shortcuts as |name shortcut|}}
<li>{{html-safe shortcut}}</li>
{{/each-in}}
</ul>
</section>
<section>
<h4>{{i18n "keyboard_shortcuts_help.navigation.title"}}</h4>
<ul>
<li>{{html-safe shortcuts.navigation.back}}</li>
<li>{{html-safe shortcuts.navigation.jump}}</li>
<li>{{html-safe shortcuts.navigation.up_down}}</li>
<li>{{html-safe shortcuts.navigation.open}}</li>
<li>{{html-safe shortcuts.navigation.next_prev}}</li>
<li>{{html-safe shortcuts.navigation.go_to_unread_post}}</li>
</ul>
</section>
</div>
<div class="column">
<section>
<h4>{{i18n "keyboard_shortcuts_help.application.title"}}</h4>
<ul>
<li>{{html-safe shortcuts.application.hamburger_menu}}</li>
<li>{{html-safe shortcuts.application.user_profile_menu}}</li>
<li>{{html-safe shortcuts.application.show_incoming_updated_topics}}</li>
<li>{{html-safe shortcuts.application.search}}</li>
<li>{{html-safe shortcuts.application.help}}</li>
<li>{{html-safe shortcuts.application.dismiss_new}}</li>
<li>{{html-safe shortcuts.application.dismiss_topics}}</li>
<li>{{html-safe shortcuts.application.log_out}}</li>
</ul>
</section>
<section>
<h4>{{i18n "keyboard_shortcuts_help.composing.title"}}</h4>
<ul>
<li>{{html-safe shortcuts.composing.return}}</li>
<li>{{html-safe shortcuts.composing.fullscreen}}</li>
<li>{{html-safe shortcuts.application.create}}</li>
<li>{{html-safe shortcuts.actions.reply_as_new_topic}}</li>
<li>{{html-safe shortcuts.actions.reply_topic}}</li>
<li>{{html-safe shortcuts.actions.reply_post}}</li>
<li>{{html-safe shortcuts.actions.quote_post}}</li>
</ul>
</section>
<section class="keyboard-shortcuts-bookmark-section">
<h4>{{i18n "keyboard_shortcuts_help.bookmarks.title"}}</h4>
<ul>
<li>{{html-safe shortcuts.bookmarks.enter}}</li>
<li>{{html-safe shortcuts.bookmarks.later_today}}</li>
<li>{{html-safe shortcuts.bookmarks.later_this_week}}</li>
<li>{{html-safe shortcuts.bookmarks.tomorrow}}</li>
<li>{{html-safe shortcuts.bookmarks.next_month}}</li>
<li>{{html-safe shortcuts.bookmarks.next_business_week}}</li>
<li>{{html-safe shortcuts.bookmarks.next_business_day}}</li>
<li>{{html-safe shortcuts.bookmarks.custom}}</li>
<li>{{html-safe shortcuts.bookmarks.none}}</li>
<li>{{html-safe shortcuts.bookmarks.delete}}</li>
</ul>
</section>
</div>
<div class="column">
<section>
<h4>{{i18n "keyboard_shortcuts_help.actions.title"}}</h4>
<ul>
<li>{{html-safe shortcuts.actions.bookmark_topic}}</li>
<li>{{html-safe shortcuts.actions.pin_unpin_topic}}</li>
<li>{{html-safe shortcuts.actions.share_topic}}</li>
<li>{{html-safe shortcuts.actions.share_post}}</li>
<li>{{html-safe shortcuts.actions.like}}</li>
<li>{{html-safe shortcuts.actions.flag}}</li>
<li>{{html-safe shortcuts.actions.bookmark}}</li>
<li>{{html-safe shortcuts.actions.edit}}</li>
<li>{{html-safe shortcuts.actions.delete}}</li>
<li>{{html-safe shortcuts.actions.mark_muted}}</li>
<li>{{html-safe shortcuts.actions.mark_regular}}</li>
<li>{{html-safe shortcuts.actions.mark_tracking}}</li>
<li>{{html-safe shortcuts.actions.mark_watching}}</li>
<li>{{html-safe shortcuts.actions.defer}}</li>
<li>{{html-safe shortcuts.actions.print}}</li>
<li>{{html-safe shortcuts.actions.topic_admin_actions}}</li>
</ul>
</section>
<section>
<h4>{{i18n "keyboard_shortcuts_help.search_menu.title"}}</h4>
<ul>
<li>{{html-safe shortcuts.search_menu.prev_next}}</li>
<li>{{html-safe shortcuts.search_menu.insert_url}}</li>
<li>{{html-safe shortcuts.search_menu.full_page_search}}</li>
</ul>
</section>
</div>
{{/each-in}}
{{/d-modal-body}}

View File

@ -1,6 +1,10 @@
import { triggerKeyEvent, visit } from "@ember/test-helpers";
import { click, triggerKeyEvent, visit } from "@ember/test-helpers";
import KeyboardShortcuts from "discourse/lib/keyboard-shortcuts";
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
import {
acceptance,
count,
exists,
} from "discourse/tests/helpers/qunit-helpers";
import sinon from "sinon";
import { test } from "qunit";
import { withPluginApi } from "discourse/lib/plugin-api";
@ -48,4 +52,50 @@ acceptance("Plugin Keyboard Shortcuts - Anonymous", function () {
"bindToPath is called due to options provided"
);
});
test("a plugin can add a shortcut and create a new category in the shortcut help modal", async function (assert) {
withPluginApi("0.8.38", (api) => {
api.addKeyboardShortcut("meta+]", () => {}, {
help: {
category: "new_category",
name: "new_category.test",
definition: {
keys1: ["meta", "]"],
keys2: ["meta", "["],
keysDelimiter: "plus",
shortcutsDelimiter: "slash",
},
},
});
});
await visit("/");
await triggerKeyEvent(document, "keypress", "?".charCodeAt(0));
assert.ok(exists(".shortcut-category-new_category"));
assert.strictEqual(count(".shortcut-category-new_category li"), 1);
});
test("a plugin can add a shortcut to and existing category in the shortcut help modal", async function (assert) {
await visit("/");
await triggerKeyEvent(document, "keypress", "?".charCodeAt(0));
const countBefore = count(".shortcut-category-application li");
await click(".modal-close");
withPluginApi("0.8.38", (api) => {
api.addKeyboardShortcut("meta+]", () => {}, {
help: {
category: "application",
name: "application.test",
definition: {
keys1: ["]"],
},
},
});
});
await triggerKeyEvent(document, "keypress", "?".charCodeAt(0));
assert.strictEqual(
count(".shortcut-category-application li"),
countBefore + 1
);
});
});

View File

@ -45,6 +45,7 @@ import {
} from "discourse/lib/topic-list-tracker";
import sinon from "sinon";
import siteFixtures from "discourse/tests/fixtures/site-fixtures";
import { clearExtraKeyboardShortcutHelp } from "discourse/lib/keyboard-shortcuts";
import { clearResolverOptions } from "discourse-common/resolver";
import { clearNavItems } from "discourse/models/nav-item";
import {
@ -168,6 +169,7 @@ function testCleanup(container, app) {
resetComposerCustomizations();
resetQuickSearchRandomTips();
resetPostMenuExtraButtons();
clearExtraKeyboardShortcutHelp();
clearNavItems();
setTopicList(null);
_clearSnapshots();

View File

@ -36,10 +36,17 @@
}
#keyboard-shortcuts-help {
display: flex;
display: grid;
grid-gap: 0.5em 1.25em;
grid-template-columns: repeat(3, 1fr);
&:focus {
outline: none;
}
.column {
width: 33.3%;
@for $i from 1 through 25 {
.span-#{$i} {
grid-row-end: span $i;
}
}
ul {