Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-12-19 14:01:45 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-12-19 14:01:45 +0300
commit9297025d0b7ddf095eb618dfaaab2ff8f2018d8b (patch)
tree865198c01d1824a9b098127baa3ab980c9cd2c06 /app/assets/javascripts/behaviors
parent6372471f43ee03c05a7c1f8b0c6ac6b8a7431dbe (diff)
Add latest changes from gitlab-org/gitlab@16-7-stable-eev16.7.0-rc42
Diffstat (limited to 'app/assets/javascripts/behaviors')
-rw-r--r--app/assets/javascripts/behaviors/index.js3
-rw-r--r--app/assets/javascripts/behaviors/markdown/copy_as_gfm.js4
-rw-r--r--app/assets/javascripts/behaviors/shortcuts.js36
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/index.js16
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/keybindings.js5
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts.js84
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_blob.js8
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_find_file.js10
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js9
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_navigation.js9
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_network.js10
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_wiki.js10
12 files changed, 114 insertions, 90 deletions
diff --git a/app/assets/javascripts/behaviors/index.js b/app/assets/javascripts/behaviors/index.js
index 84ff8fa7f33..fe3868fdd04 100644
--- a/app/assets/javascripts/behaviors/index.js
+++ b/app/assets/javascripts/behaviors/index.js
@@ -6,9 +6,9 @@ import installGlEmojiElement from './gl_emoji';
import initCopyAsGFM from './markdown/copy_as_gfm';
import './quick_submit';
import './requires_input';
-import initPageShortcuts from './shortcuts';
import { initToastMessages } from './toasts';
import { initGlobalAlerts } from './global_alerts';
+import './shortcuts';
import './toggler_behavior';
import './preview_markdown';
@@ -17,7 +17,6 @@ installGlEmojiElement();
initCopyAsGFM();
initCopyToClipboard();
-initPageShortcuts();
initCollapseSidebarOnWindowResize();
initToastMessages();
diff --git a/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js b/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js
index 36317444af9..72aae254584 100644
--- a/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js
+++ b/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js
@@ -152,7 +152,9 @@ export class CopyAsGFM {
if (lineElements.length > 0) {
for (let i = 0; i < lineElements.length; i += 1) {
const lineElement = lineElements[i];
- codeElement.appendChild(lineElement);
+ const line = document.createElement('span');
+ line.append(...lineElement.childNodes);
+ codeElement.appendChild(line);
codeElement.appendChild(document.createTextNode('\n'));
}
} else {
diff --git a/app/assets/javascripts/behaviors/shortcuts.js b/app/assets/javascripts/behaviors/shortcuts.js
deleted file mode 100644
index 22a8be92e52..00000000000
--- a/app/assets/javascripts/behaviors/shortcuts.js
+++ /dev/null
@@ -1,36 +0,0 @@
-export default function initPageShortcuts() {
- const { page } = document.body.dataset;
- const pagesWithCustomShortcuts = [
- 'projects:activity',
- 'projects:artifacts:browse',
- 'projects:artifacts:file',
- 'projects:blame:show',
- 'projects:blob:show',
- 'projects:commit:show',
- 'projects:commits:show',
- 'projects:find_file:show',
- 'projects:issues:edit',
- 'projects:issues:index',
- 'projects:issues:new',
- 'projects:issues:show',
- 'projects:merge_requests:creations:diffs',
- 'projects:merge_requests:creations:new',
- 'projects:merge_requests:edit',
- 'projects:merge_requests:index',
- 'projects:merge_requests:show',
- 'projects:network:show',
- 'projects:show',
- 'projects:tree:show',
- 'groups:show',
- ];
-
- // the pages above have their own shortcuts sub-classes instantiated elsewhere
- // TODO: replace this whitelist with something more automated/maintainable
- // https://gitlab.com/gitlab-org/gitlab/-/issues/392845
- if (page && !pagesWithCustomShortcuts.includes(page)) {
- import(/* webpackChunkName: 'shortcutsBundle' */ './shortcuts/shortcuts')
- .then(({ default: Shortcuts }) => new Shortcuts())
- .catch(() => {});
- }
- return false;
-}
diff --git a/app/assets/javascripts/behaviors/shortcuts/index.js b/app/assets/javascripts/behaviors/shortcuts/index.js
new file mode 100644
index 00000000000..cc6d8a23f68
--- /dev/null
+++ b/app/assets/javascripts/behaviors/shortcuts/index.js
@@ -0,0 +1,16 @@
+const shortcutsPromise = import(/* webpackChunkName: 'shortcutsBundle' */ './shortcuts')
+ .then(({ default: Shortcuts }) => new Shortcuts())
+ .catch(() => {});
+
+export const addShortcutsExtension = (ShortcutExtension, ...args) =>
+ shortcutsPromise.then((shortcuts) => shortcuts.addExtension(ShortcutExtension, args));
+
+export const resetShortcutsForTests = async () => {
+ if (process.env.NODE_ENV === 'test') {
+ const { Mousetrap, clearStopCallbacksForTests } = await import('~/lib/mousetrap');
+ clearStopCallbacksForTests();
+ Mousetrap.reset();
+ const shortcuts = await shortcutsPromise;
+ shortcuts.extensions.clear();
+ }
+};
diff --git a/app/assets/javascripts/behaviors/shortcuts/keybindings.js b/app/assets/javascripts/behaviors/shortcuts/keybindings.js
index 941662635ea..15229689306 100644
--- a/app/assets/javascripts/behaviors/shortcuts/keybindings.js
+++ b/app/assets/javascripts/behaviors/shortcuts/keybindings.js
@@ -538,13 +538,10 @@ const GLOBAL_SHORTCUTS_GROUP = {
GO_TO_YOUR_TODO_LIST,
TOGGLE_PERFORMANCE_BAR,
HIDE_APPEARING_CONTENT,
+ TOGGLE_SUPER_SIDEBAR,
],
};
-if (gon.use_new_navigation) {
- GLOBAL_SHORTCUTS_GROUP.keybindings.push(TOGGLE_SUPER_SIDEBAR);
-}
-
export const EDITING_SHORTCUTS_GROUP = {
id: 'editing',
name: __('Editing'),
diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts.js
index 9514ad853b0..e05694c0907 100644
--- a/app/assets/javascripts/behaviors/shortcuts/shortcuts.js
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts.js
@@ -63,11 +63,17 @@ function getToolbarBtnToShortcutsMap($textarea) {
export default class Shortcuts {
constructor() {
+ if (process.env.NODE_ENV !== 'production' && this.constructor !== Shortcuts) {
+ // eslint-disable-next-line @gitlab/require-i18n-strings
+ throw new Error('Shortcuts cannot be subclassed.');
+ }
+
+ this.extensions = new Map();
this.onToggleHelp = this.onToggleHelp.bind(this);
this.helpModalElement = null;
this.helpModalVueInstance = null;
- this.bindCommands([
+ this.addAll([
[TOGGLE_KEYBOARD_SHORTCUTS_DIALOG, this.onToggleHelp],
[START_SEARCH, Shortcuts.focusSearch],
[FOCUS_FILTER_BAR, this.focusFilter.bind(this)],
@@ -94,16 +100,12 @@ export default class Shortcuts {
const findFileURL = document.body.dataset.findFile;
if (typeof findFileURL !== 'undefined' && findFileURL !== null) {
- this.bindCommand(GO_TO_PROJECT_FIND_FILE, () => {
+ this.add(GO_TO_PROJECT_FIND_FILE, () => {
visitUrl(findFileURL);
});
}
- const shortcutsModalTriggerEvent = 'click.shortcutsModalTrigger';
- // eslint-disable-next-line @gitlab/no-global-event-off
- $(document)
- .off(shortcutsModalTriggerEvent)
- .on(shortcutsModalTriggerEvent, '.js-shortcuts-modal-trigger', this.onToggleHelp);
+ $(document).on('click', '.js-shortcuts-modal-trigger', this.onToggleHelp);
if (shouldDisableShortcuts()) {
disableShortcuts();
@@ -111,6 +113,62 @@ export default class Shortcuts {
}
/**
+ * Instantiate a legacy shortcut extension class.
+ *
+ * NOTE: The preferred approach for adding shortcuts is described in
+ * https://docs.gitlab.com/ee/development/fe_guide/keyboard_shortcuts.html.
+ * This method is only for existing legacy shortcut classes.
+ *
+ * A shortcut extension class packages up several shortcuts and behaviors for
+ * a page or set of pages. They are considered legacy because they usually do
+ * not follow modern best practices. For instance, they may hook into the UI
+ * in brittle ways, e.g.. querySelectors.
+ *
+ * Extension classes can declare dependencies on other shortcut extension
+ * classes by listing them in a static `dependencies` property. This is
+ * essentially a reimplementation of the previous subclassing approach, but
+ * with idempotency: a shortcut extension class can now only be added at most
+ * one time.
+ *
+ * Extension classes are instantiated and given the Shortcuts singleton
+ * instance as their first argument. If the class constructor needs
+ * additional arguments, pass them via the second argument as an array.
+ *
+ * See https://gitlab.com/gitlab-org/gitlab/-/issues/392845 for more context.
+ *
+ * @param {Function} Extension The extension class to add/instantiate.
+ * @param {Array} [args] A list of additional args to pass to the extension
+ * class constructor.
+ * @param {Set} [extensionsCurrentlyLoading] For internal use only. Do not
+ * use.
+ * @returns The instantiated shortcut extension class.
+ */
+ addExtension(Extension, args = [], extensionsCurrentlyLoading = new Set()) {
+ extensionsCurrentlyLoading.add(Extension);
+
+ let instance = this.extensions.get(Extension);
+ if (!instance) {
+ for (const Dep of Extension.dependencies ?? []) {
+ if (extensionsCurrentlyLoading.has(Dep) || Dep === Shortcuts) {
+ // We've encountered a circular dependency, so stop recursing.
+ // eslint-disable-next-line no-continue
+ continue;
+ }
+
+ extensionsCurrentlyLoading.add(Dep);
+
+ this.addExtension(Dep, [], extensionsCurrentlyLoading);
+ }
+
+ instance = new Extension(this, ...args);
+ this.extensions.set(Extension, instance);
+ }
+
+ extensionsCurrentlyLoading.delete(Extension);
+ return instance;
+ }
+
+ /**
* Bind the keyboard shortcut(s) defined by the given command to the given
* callback.
*
@@ -120,7 +178,7 @@ export default class Shortcuts {
* @returns {void}
*/
// eslint-disable-next-line class-methods-use-this
- bindCommand(command, callback) {
+ add(command, callback) {
Mousetrap.bind(keysFor(command), callback);
}
@@ -132,8 +190,8 @@ export default class Shortcuts {
* command/callback pairs.
* @returns {void}
*/
- bindCommands(commandsAndCallbacks) {
- commandsAndCallbacks.forEach((commandAndCallback) => this.bindCommand(...commandAndCallback));
+ addAll(commandsAndCallbacks) {
+ commandsAndCallbacks.forEach((commandAndCallback) => this.add(...commandAndCallback));
}
onToggleHelp(e) {
@@ -198,11 +256,7 @@ export default class Shortcuts {
}
static focusSearch(e) {
- if (gon.use_new_navigation) {
- document.querySelector('#super-sidebar-search')?.click();
- } else {
- document.querySelector('#search')?.focus();
- }
+ document.querySelector('#super-sidebar-search')?.click();
if (e.preventDefault) {
e.preventDefault();
diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts_blob.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_blob.js
index 65ae67d156f..a0bfd337d10 100644
--- a/app/assets/javascripts/behaviors/shortcuts/shortcuts_blob.js
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_blob.js
@@ -7,7 +7,6 @@ import {
getShaFromUrl,
} from '~/lib/utils/url_utility';
import { updateRefPortionOfTitle } from '~/repository/utils/title';
-import Shortcuts from './shortcuts';
const defaults = {
fileBlobPermalinkUrl: null,
@@ -19,15 +18,14 @@ function eventHasModifierKeys(event) {
return event.ctrlKey || event.metaKey || event.shiftKey;
}
-export default class ShortcutsBlob extends Shortcuts {
- constructor(opts) {
+export default class ShortcutsBlob {
+ constructor(shortcuts, opts) {
const options = { ...defaults, ...opts };
- super();
this.options = options;
this.shortcircuitPermalinkButton();
- this.bindCommand(PROJECT_FILES_GO_TO_PERMALINK, this.moveToFilePermalink.bind(this));
+ shortcuts.add(PROJECT_FILES_GO_TO_PERMALINK, this.moveToFilePermalink.bind(this));
}
moveToFilePermalink() {
diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts_find_file.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_find_file.js
index f26878cf161..393d0165a07 100644
--- a/app/assets/javascripts/behaviors/shortcuts/shortcuts_find_file.js
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_find_file.js
@@ -8,10 +8,8 @@ import {
import { addStopCallback } from '~/lib/mousetrap';
import ShortcutsNavigation from './shortcuts_navigation';
-export default class ShortcutsFindFile extends ShortcutsNavigation {
- constructor(projectFindFile) {
- super();
-
+export default class ShortcutsFindFile {
+ constructor(shortcuts, projectFindFile) {
addStopCallback((e, element, combo) => {
if (
element === projectFindFile.inputElement[0] &&
@@ -28,11 +26,13 @@ export default class ShortcutsFindFile extends ShortcutsNavigation {
return undefined;
});
- this.bindCommands([
+ shortcuts.addAll([
[PROJECT_FILES_MOVE_SELECTION_UP, projectFindFile.selectRowUp],
[PROJECT_FILES_MOVE_SELECTION_DOWN, projectFindFile.selectRowDown],
[PROJECT_FILES_GO_BACK, projectFindFile.goToTree],
[PROJECT_FILES_OPEN_SELECTION, projectFindFile.goToBlob],
]);
}
+
+ static dependencies = [ShortcutsNavigation];
}
diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js
index b0e515ac19d..cde6d59b210 100644
--- a/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js
@@ -16,12 +16,9 @@ import {
MR_COPY_SOURCE_BRANCH_NAME,
ISSUABLE_COPY_REF,
} from './keybindings';
-import Shortcuts from './shortcuts';
-
-export default class ShortcutsIssuable extends Shortcuts {
- constructor() {
- super();
+export default class ShortcutsIssuable {
+ constructor(shortcuts) {
this.branchInMemoryButton = document.createElement('button');
this.branchClipboardInstance = new ClipboardJS(this.branchInMemoryButton);
this.branchClipboardInstance.on('success', () => {
@@ -40,7 +37,7 @@ export default class ShortcutsIssuable extends Shortcuts {
toast(s__('GlobalShortcuts|Unable to copy the reference at this time.'));
});
- this.bindCommands([
+ shortcuts.addAll([
[ISSUE_MR_CHANGE_ASSIGNEE, () => ShortcutsIssuable.openSidebarDropdown('assignee')],
[ISSUE_MR_CHANGE_MILESTONE, () => ShortcutsIssuable.openSidebarDropdown('milestone')],
[ISSUABLE_CHANGE_LABEL, () => ShortcutsIssuable.openSidebarDropdown('labels')],
diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts_navigation.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_navigation.js
index 4691a4228e6..bae50c02599 100644
--- a/app/assets/javascripts/behaviors/shortcuts/shortcuts_navigation.js
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_navigation.js
@@ -21,13 +21,10 @@ import {
PROJECT_FILES_GO_TO_COMPARE,
NEW_ISSUE,
} from './keybindings';
-import Shortcuts from './shortcuts';
-export default class ShortcutsNavigation extends Shortcuts {
- constructor() {
- super();
-
- this.bindCommands([
+export default class ShortcutsNavigation {
+ constructor(shortcuts) {
+ shortcuts.addAll([
[GO_TO_PROJECT_OVERVIEW, () => findAndFollowLink('.shortcuts-project')],
[GO_TO_PROJECT_ACTIVITY_FEED, () => findAndFollowLink('.shortcuts-project-activity')],
[GO_TO_PROJECT_RELEASES, () => findAndFollowLink('.shortcuts-deployments-releases')],
diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts_network.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_network.js
index 02c6af53fc2..eee8c1acf1a 100644
--- a/app/assets/javascripts/behaviors/shortcuts/shortcuts_network.js
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_network.js
@@ -8,11 +8,9 @@ import {
} from './keybindings';
import ShortcutsNavigation from './shortcuts_navigation';
-export default class ShortcutsNetwork extends ShortcutsNavigation {
- constructor(graph) {
- super();
-
- this.bindCommands([
+export default class ShortcutsNetwork {
+ constructor(shortcuts, graph) {
+ shortcuts.addAll([
[REPO_GRAPH_SCROLL_LEFT, graph.scrollLeft],
[REPO_GRAPH_SCROLL_RIGHT, graph.scrollRight],
[REPO_GRAPH_SCROLL_UP, graph.scrollUp],
@@ -21,4 +19,6 @@ export default class ShortcutsNetwork extends ShortcutsNavigation {
[REPO_GRAPH_SCROLL_BOTTOM, graph.scrollBottom],
]);
}
+
+ static dependencies = [ShortcutsNavigation];
}
diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts_wiki.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_wiki.js
index 62d612cfa6d..5f45331bf76 100644
--- a/app/assets/javascripts/behaviors/shortcuts/shortcuts_wiki.js
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_wiki.js
@@ -2,13 +2,13 @@ import findAndFollowLink from '~/lib/utils/navigation_utility';
import { EDIT_WIKI_PAGE } from './keybindings';
import ShortcutsNavigation from './shortcuts_navigation';
-export default class ShortcutsWiki extends ShortcutsNavigation {
- constructor() {
- super();
-
- this.bindCommand(EDIT_WIKI_PAGE, ShortcutsWiki.editWiki);
+export default class ShortcutsWiki {
+ constructor(shortcuts) {
+ shortcuts.add(EDIT_WIKI_PAGE, ShortcutsWiki.editWiki);
}
+ static dependencies = [ShortcutsNavigation];
+
static editWiki() {
findAndFollowLink('.js-wiki-edit');
}