Commit f00e94ca authored by Lukas 'Eipi' Eipert's avatar Lukas 'Eipi' Eipert Committed by Nathan Friend

Make keyboard shortcut help dynamic

parent 9b466bb0
...@@ -375,7 +375,7 @@ export const MR_PREVIOUS_FILE_IN_DIFF = { ...@@ -375,7 +375,7 @@ export const MR_PREVIOUS_FILE_IN_DIFF = {
export const MR_GO_TO_FILE = { export const MR_GO_TO_FILE = {
id: 'mergeRequests.goToFile', id: 'mergeRequests.goToFile',
description: __('Go to file'), description: __('Go to file'),
defaultKeys: ['t', 'mod+p'], defaultKeys: ['mod+p', 't'],
customizable: false, customizable: false,
}; };
......
<script>
import { __, s__ } from '~/locale';
// Map some keys to their proper representation depending on the system
// See also: https://craig.is/killing/mice#keys
const getKeyMap = () => {
const keyMap = {
up: '',
down: '',
left: '',
right: '',
ctrl: s__('KeyboardKey|Ctrl'),
shift: s__('KeyboardKey|Shift'),
enter: s__('KeyboardKey|Enter'),
esc: s__('KeyboardKey|Esc'),
command: '',
option: window.gl?.client?.isMac ? '' : s__('KeyboardKey|Alt'),
};
// Meta and alt are aliases
keyMap.meta = keyMap.command;
keyMap.alt = keyMap.option;
// Mod is Command on Mac, and Ctrl on Windows/Linux
keyMap.mod = window.gl?.client?.isMac ? keyMap.command : keyMap.ctrl;
return keyMap;
};
export default {
functional: true,
props: {
shortcuts: {
type: Array,
required: true,
},
},
render(createElement, context) {
const keyMap = getKeyMap();
const { staticClass } = context.data;
const shortcuts = context.props.shortcuts.reduce((acc, shortcut, i) => {
if (
!window.gl?.client?.isMac &&
(shortcut.includes('command') || shortcut.includes('meta'))
) {
return acc;
}
const keys = shortcut.split(/([ +])/);
if (i !== 0 && acc.length) {
acc.push(` ${__('or')} `);
// If there are multiple alternative shortcuts,
// we keep them on the same line if they are single-key, e.g. `]` or `j`
// but if they consist of multiple keys, we insert a line break, e.g.:
// `shift` + `]` <br> or `shift` + `j`
if (keys.length > 1) {
acc.push(createElement('br'));
}
}
keys.forEach((key) => {
if (key === '+') {
acc.push(' + ');
} else if (key === ' ') {
acc.push(` ${__('then')} `);
} else {
acc.push(createElement('kbd', {}, [keyMap[key] ?? key]));
}
});
return acc;
}, []);
return createElement('div', { staticClass }, shortcuts);
},
};
</script>
...@@ -6,7 +6,7 @@ import { disableShortcuts, enableShortcuts, shouldDisableShortcuts } from './sho ...@@ -6,7 +6,7 @@ import { disableShortcuts, enableShortcuts, shouldDisableShortcuts } from './sho
export default { export default {
i18n: { i18n: {
toggleLabel: __('Keyboard shortcuts'), toggleLabel: __('Toggle shortcuts'),
}, },
components: { components: {
GlToggle, GlToggle,
...@@ -31,14 +31,12 @@ export default { ...@@ -31,14 +31,12 @@ export default {
</script> </script>
<template> <template>
<div v-if="localStorageUsable" class="d-inline-flex align-items-center js-toggle-shortcuts"> <div v-if="localStorageUsable" class="js-toggle-shortcuts">
<gl-toggle <gl-toggle
v-model="shortcutsEnabled" v-model="shortcutsEnabled"
aria-describedby="shortcutsToggle"
:label="$options.i18n.toggleLabel" :label="$options.i18n.toggleLabel"
label-position="left" label-position="left"
@change="onChange" @change="onChange"
/> />
<div id="shortcutsToggle" class="sr-only">{{ __('Enable or disable keyboard shortcuts') }}</div>
</div> </div>
</template> </template>
...@@ -105,10 +105,6 @@ hr { ...@@ -105,10 +105,6 @@ hr {
} }
} }
kbd {
display: inline-block;
}
code { code {
padding: 2px 4px; padding: 2px 4px;
color: $code-color; color: $code-color;
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
@import 'framework/flash'; @import 'framework/flash';
@import 'framework/forms'; @import 'framework/forms';
@import 'framework/gfm'; @import 'framework/gfm';
@import 'framework/kbd';
@import 'framework/header'; @import 'framework/header';
@import 'framework/highlight'; @import 'framework/highlight';
@import 'framework/issue_box'; @import 'framework/issue_box';
......
...@@ -266,15 +266,6 @@ ...@@ -266,15 +266,6 @@
} }
} }
.shortcut-mappings {
display: none;
}
&.shortcuts .shortcut-mappings {
display: inline-block;
margin-right: 5px;
}
ul { ul {
margin: 0; margin: 0;
padding: 0; padding: 0;
......
kbd {
display: inline-block;
padding: 3px 5px;
font-size: $gl-font-size-monospace-sm;
line-height: 10px;
color: var(--gray-700, $gray-700);
vertical-align: middle;
background-color: var(--gray-10, $gray-10);
border-width: 1px;
border-style: solid;
border-color: var(--gray-100, $gray-100) var(--gray-100, $gray-100) var(--gray-200, $gray-200);
border-image: none;
border-radius: 3px;
box-shadow: 0 -1px 0 var(--gray-200, $gray-200) inset;
}
...@@ -81,22 +81,6 @@ ...@@ -81,22 +81,6 @@
word-break: keep-all; word-break: keep-all;
} }
kbd {
display: inline-block;
padding: 3px 5px;
font-size: 11px;
line-height: 10px;
color: $gray-700;
vertical-align: middle;
background-color: $gray-10;
border-width: 1px;
border-style: solid;
border-color: $gray-100 $gray-100 $gray-200;
border-image: none;
border-radius: 3px;
box-shadow: 0 -1px 0 $gray-200 inset;
}
h1 { h1 {
font-size: 1.75em; font-size: 1.75em;
font-weight: $gl-font-weight-bold; font-weight: $gl-font-weight-bold;
......
.shortcut-mappings { .shortcut-help {
font-size: 12px; &-body {
color: $gray-700; height: 80vh;
overflow-y: scroll;
tbody:first-child tr:first-child {
padding-top: 0;
} }
th { &-container {
padding-top: 15px; column-count: 1;
line-height: 1.5; @include media-breakpoint-up(md) {
color: $help-shortcut-header-color; column-count: 2;
text-align: left; }
column-gap: 1rem;
} }
td { &-mapping {
padding-top: 3px; overflow: hidden;
padding-bottom: 3px; break-inside: avoid;
vertical-align: top;
line-height: 20px; &-title {
} margin-left: 40%;
}
.shortcut { kbd {
padding-right: 10px; margin: 0.1rem 0;
color: $gray-300; line-height: unset;
text-align: right; font-size: unset;
white-space: nowrap; }
} }
} }
......
---
title: "Update Keyboard shortcut help: adding search, update styling"
merge_request: 56400
author:
type: changed
...@@ -12185,9 +12185,6 @@ msgstr "" ...@@ -12185,9 +12185,6 @@ msgstr ""
msgid "Enable or disable Seat Link." msgid "Enable or disable Seat Link."
msgstr "" msgstr ""
msgid "Enable or disable keyboard shortcuts"
msgstr ""
msgid "Enable or disable the Pseudonymizer data collection." msgid "Enable or disable the Pseudonymizer data collection."
msgstr "" msgstr ""
...@@ -18798,9 +18795,30 @@ msgstr "" ...@@ -18798,9 +18795,30 @@ msgstr ""
msgid "Keyboard shortcuts" msgid "Keyboard shortcuts"
msgstr "" msgstr ""
msgid "KeyboardKey|Alt"
msgstr ""
msgid "KeyboardKey|Ctrl"
msgstr ""
msgid "KeyboardKey|Ctrl+" msgid "KeyboardKey|Ctrl+"
msgstr "" msgstr ""
msgid "KeyboardKey|Enter"
msgstr ""
msgid "KeyboardKey|Esc"
msgstr ""
msgid "KeyboardKey|Shift"
msgstr ""
msgid "KeyboardShortcuts|No shortcuts matched your search"
msgstr ""
msgid "KeyboardShortcuts|Search keyboard shortcuts"
msgstr ""
msgid "Keys" msgid "Keys"
msgstr "" msgstr ""
...@@ -33762,13 +33780,13 @@ msgstr "" ...@@ -33762,13 +33780,13 @@ msgstr ""
msgid "Toggle project select" msgid "Toggle project select"
msgstr "" msgstr ""
msgid "Toggle sidebar" msgid "Toggle shortcuts"
msgstr "" msgstr ""
msgid "Toggle the Performance Bar" msgid "Toggle sidebar"
msgstr "" msgstr ""
msgid "Toggle this dialog" msgid "Toggle the Performance Bar"
msgstr "" msgstr ""
msgid "Toggle thread" msgid "Toggle thread"
...@@ -38934,6 +38952,9 @@ msgstr "" ...@@ -38934,6 +38952,9 @@ msgstr ""
msgid "the wiki" msgid "the wiki"
msgstr "" msgstr ""
msgid "then"
msgstr ""
msgid "this document" msgid "this document"
msgstr "" msgstr ""
......
import { shallowMount } from '@vue/test-utils';
import Shortcut from '~/behaviors/shortcuts/shortcut.vue';
describe('Shortcut Vue Component', () => {
const render = (shortcuts) => shallowMount(Shortcut, { propsData: { shortcuts } }).html();
afterEach(() => {
delete window.gl.client;
});
describe.each([true, false])('With browser env isMac: %p', (isMac) => {
beforeEach(() => {
window.gl = { client: { isMac } };
});
it.each([
['up', '<kbd>↑</kbd>'],
['down', '<kbd>↓</kbd>'],
['left', '<kbd>←</kbd>'],
['right', '<kbd>→</kbd>'],
['ctrl', '<kbd>Ctrl</kbd>'],
['shift', '<kbd>Shift</kbd>'],
['enter', '<kbd>Enter</kbd>'],
['esc', '<kbd>Esc</kbd>'],
// Some normal ascii letter
['a', '<kbd>a</kbd>'],
// An umlaut letter
['ø', '<kbd>ø</kbd>'],
// A number
['5', '<kbd>5</kbd>'],
])('renders platform agnostic key %p as: %p', (key, rendered) => {
expect(render([key])).toEqual(`<div>${rendered}</div>`);
});
it('renders keys combined with plus ("+") correctly', () => {
expect(render(['shift+a+b+c'])).toEqual(
`<div><kbd>Shift</kbd> + <kbd>a</kbd> + <kbd>b</kbd> + <kbd>c</kbd></div>`,
);
});
it('renders keys combined with space (" ") correctly', () => {
expect(render(['shift a b c'])).toEqual(
`<div><kbd>Shift</kbd> then <kbd>a</kbd> then <kbd>b</kbd> then <kbd>c</kbd></div>`,
);
});
it('renders multiple shortcuts correctly', () => {
expect(render(['shift+[', 'shift+k'])).toEqual(
`<div><kbd>Shift</kbd> + <kbd>[</kbd> or <br><kbd>Shift</kbd> + <kbd>k</kbd></div>`,
);
expect(render(['[', 'k'])).toEqual(`<div><kbd>[</kbd> or <kbd>k</kbd></div>`);
});
});
describe('With browser env isMac: true', () => {
beforeEach(() => {
window.gl = { client: { isMac: true } };
});
it.each([
['mod', '<kbd>⌘</kbd>'],
['command', '<kbd>⌘</kbd>'],
['meta', '<kbd>⌘</kbd>'],
['option', '<kbd>⌥</kbd>'],
['alt', '<kbd>⌥</kbd>'],
])('renders platform specific key %p as: %p', (key, rendered) => {
expect(render([key])).toEqual(`<div>${rendered}</div>`);
});
it('does render Mac specific shortcuts', () => {
expect(render(['command+[', 'ctrl+k'])).toEqual(
`<div><kbd>⌘</kbd> + <kbd>[</kbd> or <br><kbd>Ctrl</kbd> + <kbd>k</kbd></div>`,
);
});
});
describe('With browser env isMac: false', () => {
beforeEach(() => {
window.gl = { client: { isMac: false } };
});
it.each([
['mod', '<kbd>Ctrl</kbd>'],
['command', ''],
['meta', ''],
['option', '<kbd>Alt</kbd>'],
['alt', '<kbd>Alt</kbd>'],
])('renders platform specific key %p as: %p', (key, rendered) => {
expect(render([key])).toEqual(`<div>${rendered}</div>`);
});
it('does not render Mac specific shortcuts', () => {
expect(render(['command+[', 'ctrl+k'])).toEqual(`<div><kbd>Ctrl</kbd> + <kbd>k</kbd></div>`);
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment