--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import styled from '@emotion/styled';
+import tw from 'twin.macro';
+import { themeContrast } from '../helpers';
+import { Key } from '../helpers/keyboard';
+import { KeyboardHintKeys } from './KeyboardHintKeys';
+
+interface Props {
+ command: string;
+ title?: string;
+}
+
+export function KeyboardHint({ title, command }: Props) {
+ const normalizedCommand = command
+ .replace(Key.Control, isMacOS() ? 'Command' : 'Control')
+ .replace(Key.Alt, isMacOS() ? 'Option' : 'Alt');
+
+ return (
+ <Body>
+ {title && <span className="sw-truncate">{title}</span>}
+ <KeyboardHintKeys command={normalizedCommand} />
+ </Body>
+ );
+}
+
+const Body = styled.div`
+ ${tw`sw-flex sw-gap-2 sw-justify-center`}
+ flex-wrap: wrap;
+ color: ${themeContrast('pageContentLight')};
+`;
+
+function isMacOS() {
+ return navigator.userAgent.toLocaleLowerCase().includes('mac os');
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import styled from '@emotion/styled';
+import tw from 'twin.macro';
+import { themeColor, themeContrast } from '../helpers';
+import { Key } from '../helpers/keyboard';
+import { TriangleDownIcon, TriangleLeftIcon, TriangleRightIcon, TriangleUpIcon } from './icons';
+
+const COMMAND = '⌘';
+const CTRL = 'Ctrl';
+const OPTION = '⌥';
+const ALT = 'Alt';
+const NON_KEY_SYMBOLS = ['+', ' '];
+
+export function KeyboardHintKeys({ command }: { command: string }) {
+ const keys = command
+ .trim()
+ .split(' ')
+ .map((key, index) => {
+ const uniqueKey = `${key}-${index}`;
+ if (NON_KEY_SYMBOLS.includes(key)) {
+ return <span key={uniqueKey}>{key}</span>;
+ }
+
+ return <KeyBox key={uniqueKey}>{getKey(key)}</KeyBox>;
+ });
+
+ return <div className="sw-flex sw-gap-1">{keys}</div>;
+}
+
+export const KeyBox = styled.span`
+ ${tw`sw-flex sw-items-center sw-justify-center`}
+ ${tw`sw-px-1/2`}
+ ${tw`sw-rounded-1/2`}
+
+ color: ${themeContrast('keyboardHintKey')};
+ background-color: ${themeColor('keyboardHintKey')};
+`;
+
+function getKey(key: string) {
+ switch (key) {
+ case Key.Control:
+ return CTRL;
+ case Key.Command:
+ return COMMAND;
+ case Key.Alt:
+ return ALT;
+ case Key.Option:
+ return OPTION;
+ case Key.ArrowUp:
+ return <TriangleUpIcon />;
+ case Key.ArrowDown:
+ return <TriangleDownIcon />;
+ case Key.ArrowLeft:
+ return <TriangleLeftIcon />;
+ case Key.ArrowRight:
+ return <TriangleRightIcon />;
+ default:
+ return key;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { Key } from '../../helpers/keyboard';
+import { render } from '../../helpers/testUtils';
+import { FCProps } from '../../types/misc';
+import { KeyboardHint } from '../KeyboardHint';
+
+afterEach(() => {
+ jest.clearAllMocks();
+});
+
+it('renders without title', () => {
+ const { container } = setupWithProps();
+ expect(container).toMatchSnapshot();
+});
+
+it('renders with title', () => {
+ const { container } = setupWithProps({ title: 'title' });
+ expect(container).toMatchSnapshot();
+});
+
+it('renders with command', () => {
+ const { container } = setupWithProps({ command: 'command' });
+ expect(container).toMatchSnapshot();
+});
+
+it('renders on mac', () => {
+ Object.defineProperty(navigator, 'userAgent', {
+ configurable: true,
+ value: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4)',
+ });
+ const { container } = setupWithProps({ command: `${Key.Control} ${Key.Alt}` });
+ expect(container).toMatchSnapshot();
+});
+
+it('renders on windows', () => {
+ Object.defineProperty(navigator, 'userAgent', {
+ configurable: true,
+ value: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
+ });
+ const { container } = setupWithProps({ command: `${Key.Control} ${Key.Alt}` });
+ expect(container).toMatchSnapshot();
+});
+
+function setupWithProps(props: Partial<FCProps<typeof KeyboardHint>> = {}) {
+ return render(<KeyboardHint command="click" {...props} />);
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+import { Key } from '../../helpers/keyboard';
+import { render } from '../../helpers/testUtils';
+import { FCProps } from '../../types/misc';
+import { KeyboardHintKeys } from '../KeyboardHintKeys';
+
+it.each([
+ Key.Control,
+ Key.Command,
+ Key.Alt,
+ Key.Option,
+ Key.ArrowUp,
+ Key.ArrowDown,
+ Key.ArrowLeft,
+ Key.ArrowRight,
+])('should render %s', (key) => {
+ const { container } = setupWithProps({ command: key });
+ expect(container).toMatchSnapshot();
+});
+
+it('should render multiple keys', () => {
+ const { container } = setupWithProps({ command: `${Key.ArrowUp} ${Key.ArrowDown}` });
+ expect(container).toMatchSnapshot();
+});
+
+it('should render multiple keys with non-key symbols', () => {
+ const { container } = setupWithProps({
+ command: `${Key.Control} + ${Key.ArrowDown} ${Key.ArrowUp}`,
+ });
+ expect(container).toMatchSnapshot();
+});
+
+it('should render a default text if no keys match', () => {
+ const { container } = setupWithProps({ command: `${Key.Control} + click` });
+ expect(container).toMatchSnapshot();
+});
+
+function setupWithProps(props: Partial<FCProps<typeof KeyboardHintKeys>> = {}) {
+ return render(<KeyboardHintKeys command={`${Key.ArrowUp}`} {...props} />);
+}
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders on mac 1`] = `
+.emotion-0 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-box-pack: center;
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ gap: 0.5rem;
+ -webkit-box-flex-wrap: wrap;
+ -webkit-flex-wrap: wrap;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ color: rgb(106,117,144);
+}
+
+.emotion-2 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-box-pack: center;
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ padding-left: 0.125rem;
+ padding-right: 0.125rem;
+ border-radius: 0.125rem;
+ color: rgb(62,67,87);
+ background-color: rgb(225,230,243);
+}
+
+<div>
+ <div
+ class="emotion-0 emotion-1"
+ >
+ <div
+ class="sw-flex sw-gap-1"
+ >
+ <span
+ class="emotion-2 emotion-3"
+ >
+ ⌘
+ </span>
+ <span
+ class="emotion-2 emotion-3"
+ >
+ ⌥
+ </span>
+ </div>
+ </div>
+</div>
+`;
+
+exports[`renders on windows 1`] = `
+.emotion-0 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-box-pack: center;
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ gap: 0.5rem;
+ -webkit-box-flex-wrap: wrap;
+ -webkit-flex-wrap: wrap;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ color: rgb(106,117,144);
+}
+
+.emotion-2 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-box-pack: center;
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ padding-left: 0.125rem;
+ padding-right: 0.125rem;
+ border-radius: 0.125rem;
+ color: rgb(62,67,87);
+ background-color: rgb(225,230,243);
+}
+
+<div>
+ <div
+ class="emotion-0 emotion-1"
+ >
+ <div
+ class="sw-flex sw-gap-1"
+ >
+ <span
+ class="emotion-2 emotion-3"
+ >
+ Ctrl
+ </span>
+ <span
+ class="emotion-2 emotion-3"
+ >
+ Alt
+ </span>
+ </div>
+ </div>
+</div>
+`;
+
+exports[`renders with command 1`] = `
+.emotion-0 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-box-pack: center;
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ gap: 0.5rem;
+ -webkit-box-flex-wrap: wrap;
+ -webkit-flex-wrap: wrap;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ color: rgb(106,117,144);
+}
+
+.emotion-2 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-box-pack: center;
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ padding-left: 0.125rem;
+ padding-right: 0.125rem;
+ border-radius: 0.125rem;
+ color: rgb(62,67,87);
+ background-color: rgb(225,230,243);
+}
+
+<div>
+ <div
+ class="emotion-0 emotion-1"
+ >
+ <div
+ class="sw-flex sw-gap-1"
+ >
+ <span
+ class="emotion-2 emotion-3"
+ >
+ command
+ </span>
+ </div>
+ </div>
+</div>
+`;
+
+exports[`renders with title 1`] = `
+.emotion-0 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-box-pack: center;
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ gap: 0.5rem;
+ -webkit-box-flex-wrap: wrap;
+ -webkit-flex-wrap: wrap;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ color: rgb(106,117,144);
+}
+
+.emotion-2 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-box-pack: center;
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ padding-left: 0.125rem;
+ padding-right: 0.125rem;
+ border-radius: 0.125rem;
+ color: rgb(62,67,87);
+ background-color: rgb(225,230,243);
+}
+
+<div>
+ <div
+ class="emotion-0 emotion-1"
+ >
+ <span
+ class="sw-truncate"
+ >
+ title
+ </span>
+ <div
+ class="sw-flex sw-gap-1"
+ >
+ <span
+ class="emotion-2 emotion-3"
+ >
+ click
+ </span>
+ </div>
+ </div>
+</div>
+`;
+
+exports[`renders without title 1`] = `
+.emotion-0 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-box-pack: center;
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ gap: 0.5rem;
+ -webkit-box-flex-wrap: wrap;
+ -webkit-flex-wrap: wrap;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ color: rgb(106,117,144);
+}
+
+.emotion-2 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-box-pack: center;
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ padding-left: 0.125rem;
+ padding-right: 0.125rem;
+ border-radius: 0.125rem;
+ color: rgb(62,67,87);
+ background-color: rgb(225,230,243);
+}
+
+<div>
+ <div
+ class="emotion-0 emotion-1"
+ >
+ <div
+ class="sw-flex sw-gap-1"
+ >
+ <span
+ class="emotion-2 emotion-3"
+ >
+ click
+ </span>
+ </div>
+ </div>
+</div>
+`;
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render Alt 1`] = `
+.emotion-0 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-box-pack: center;
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ padding-left: 0.125rem;
+ padding-right: 0.125rem;
+ border-radius: 0.125rem;
+ color: rgb(62,67,87);
+ background-color: rgb(225,230,243);
+}
+
+<div>
+ <div
+ class="sw-flex sw-gap-1"
+ >
+ <span
+ class="emotion-0 emotion-1"
+ >
+ Alt
+ </span>
+ </div>
+</div>
+`;
+
+exports[`should render ArrowDown 1`] = `
+.emotion-0 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-box-pack: center;
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ padding-left: 0.125rem;
+ padding-right: 0.125rem;
+ border-radius: 0.125rem;
+ color: rgb(62,67,87);
+ background-color: rgb(225,230,243);
+}
+
+<div>
+ <div
+ class="sw-flex sw-gap-1"
+ >
+ <span
+ class="emotion-0 emotion-1"
+ >
+ <svg
+ aria-hidden="true"
+ class="octicon octicon-triangle-down"
+ fill="currentColor"
+ focusable="false"
+ height="16"
+ role="img"
+ style="display: inline-block; user-select: none; vertical-align: middle; overflow: visible;"
+ viewBox="0 0 16 16"
+ width="16"
+ >
+ <path
+ d="m4.427 7.427 3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.396 7H4.604a.25.25 0 0 0-.177.427Z"
+ />
+ </svg>
+ </span>
+ </div>
+</div>
+`;
+
+exports[`should render ArrowLeft 1`] = `
+.emotion-0 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-box-pack: center;
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ padding-left: 0.125rem;
+ padding-right: 0.125rem;
+ border-radius: 0.125rem;
+ color: rgb(62,67,87);
+ background-color: rgb(225,230,243);
+}
+
+<div>
+ <div
+ class="sw-flex sw-gap-1"
+ >
+ <span
+ class="emotion-0 emotion-1"
+ >
+ <svg
+ aria-hidden="true"
+ class="octicon octicon-triangle-left"
+ fill="currentColor"
+ focusable="false"
+ height="16"
+ role="img"
+ style="display: inline-block; user-select: none; vertical-align: middle; overflow: visible;"
+ viewBox="0 0 16 16"
+ width="16"
+ >
+ <path
+ d="M9.573 4.427 6.177 7.823a.25.25 0 0 0 0 .354l3.396 3.396a.25.25 0 0 0 .427-.177V4.604a.25.25 0 0 0-.427-.177Z"
+ />
+ </svg>
+ </span>
+ </div>
+</div>
+`;
+
+exports[`should render ArrowRight 1`] = `
+.emotion-0 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-box-pack: center;
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ padding-left: 0.125rem;
+ padding-right: 0.125rem;
+ border-radius: 0.125rem;
+ color: rgb(62,67,87);
+ background-color: rgb(225,230,243);
+}
+
+<div>
+ <div
+ class="sw-flex sw-gap-1"
+ >
+ <span
+ class="emotion-0 emotion-1"
+ >
+ <svg
+ aria-hidden="true"
+ class="octicon octicon-triangle-right"
+ fill="currentColor"
+ focusable="false"
+ height="16"
+ role="img"
+ style="display: inline-block; user-select: none; vertical-align: middle; overflow: visible;"
+ viewBox="0 0 16 16"
+ width="16"
+ >
+ <path
+ d="m6.427 4.427 3.396 3.396a.25.25 0 0 1 0 .354l-3.396 3.396A.25.25 0 0 1 6 11.396V4.604a.25.25 0 0 1 .427-.177Z"
+ />
+ </svg>
+ </span>
+ </div>
+</div>
+`;
+
+exports[`should render ArrowUp 1`] = `
+.emotion-0 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-box-pack: center;
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ padding-left: 0.125rem;
+ padding-right: 0.125rem;
+ border-radius: 0.125rem;
+ color: rgb(62,67,87);
+ background-color: rgb(225,230,243);
+}
+
+<div>
+ <div
+ class="sw-flex sw-gap-1"
+ >
+ <span
+ class="emotion-0 emotion-1"
+ >
+ <svg
+ aria-hidden="true"
+ class="octicon octicon-triangle-up"
+ fill="currentColor"
+ focusable="false"
+ height="16"
+ role="img"
+ style="display: inline-block; user-select: none; vertical-align: middle; overflow: visible;"
+ viewBox="0 0 16 16"
+ width="16"
+ >
+ <path
+ d="m4.427 9.573 3.396-3.396a.25.25 0 0 1 .354 0l3.396 3.396a.25.25 0 0 1-.177.427H4.604a.25.25 0 0 1-.177-.427Z"
+ />
+ </svg>
+ </span>
+ </div>
+</div>
+`;
+
+exports[`should render Command 1`] = `
+.emotion-0 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-box-pack: center;
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ padding-left: 0.125rem;
+ padding-right: 0.125rem;
+ border-radius: 0.125rem;
+ color: rgb(62,67,87);
+ background-color: rgb(225,230,243);
+}
+
+<div>
+ <div
+ class="sw-flex sw-gap-1"
+ >
+ <span
+ class="emotion-0 emotion-1"
+ >
+ ⌘
+ </span>
+ </div>
+</div>
+`;
+
+exports[`should render Control 1`] = `
+.emotion-0 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-box-pack: center;
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ padding-left: 0.125rem;
+ padding-right: 0.125rem;
+ border-radius: 0.125rem;
+ color: rgb(62,67,87);
+ background-color: rgb(225,230,243);
+}
+
+<div>
+ <div
+ class="sw-flex sw-gap-1"
+ >
+ <span
+ class="emotion-0 emotion-1"
+ >
+ Ctrl
+ </span>
+ </div>
+</div>
+`;
+
+exports[`should render Option 1`] = `
+.emotion-0 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-box-pack: center;
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ padding-left: 0.125rem;
+ padding-right: 0.125rem;
+ border-radius: 0.125rem;
+ color: rgb(62,67,87);
+ background-color: rgb(225,230,243);
+}
+
+<div>
+ <div
+ class="sw-flex sw-gap-1"
+ >
+ <span
+ class="emotion-0 emotion-1"
+ >
+ ⌥
+ </span>
+ </div>
+</div>
+`;
+
+exports[`should render a default text if no keys match 1`] = `
+.emotion-0 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-box-pack: center;
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ padding-left: 0.125rem;
+ padding-right: 0.125rem;
+ border-radius: 0.125rem;
+ color: rgb(62,67,87);
+ background-color: rgb(225,230,243);
+}
+
+<div>
+ <div
+ class="sw-flex sw-gap-1"
+ >
+ <span
+ class="emotion-0 emotion-1"
+ >
+ Ctrl
+ </span>
+ <span>
+ +
+ </span>
+ <span
+ class="emotion-0 emotion-1"
+ >
+ click
+ </span>
+ </div>
+</div>
+`;
+
+exports[`should render multiple keys 1`] = `
+.emotion-0 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-box-pack: center;
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ padding-left: 0.125rem;
+ padding-right: 0.125rem;
+ border-radius: 0.125rem;
+ color: rgb(62,67,87);
+ background-color: rgb(225,230,243);
+}
+
+<div>
+ <div
+ class="sw-flex sw-gap-1"
+ >
+ <span
+ class="emotion-0 emotion-1"
+ >
+ <svg
+ aria-hidden="true"
+ class="octicon octicon-triangle-up"
+ fill="currentColor"
+ focusable="false"
+ height="16"
+ role="img"
+ style="display: inline-block; user-select: none; vertical-align: middle; overflow: visible;"
+ viewBox="0 0 16 16"
+ width="16"
+ >
+ <path
+ d="m4.427 9.573 3.396-3.396a.25.25 0 0 1 .354 0l3.396 3.396a.25.25 0 0 1-.177.427H4.604a.25.25 0 0 1-.177-.427Z"
+ />
+ </svg>
+ </span>
+ <span
+ class="emotion-0 emotion-1"
+ >
+ <svg
+ aria-hidden="true"
+ class="octicon octicon-triangle-down"
+ fill="currentColor"
+ focusable="false"
+ height="16"
+ role="img"
+ style="display: inline-block; user-select: none; vertical-align: middle; overflow: visible;"
+ viewBox="0 0 16 16"
+ width="16"
+ >
+ <path
+ d="m4.427 7.427 3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.396 7H4.604a.25.25 0 0 0-.177.427Z"
+ />
+ </svg>
+ </span>
+ </div>
+</div>
+`;
+
+exports[`should render multiple keys with non-key symbols 1`] = `
+.emotion-0 {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-align-items: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-box-pack: center;
+ -ms-flex-pack: center;
+ -webkit-justify-content: center;
+ justify-content: center;
+ padding-left: 0.125rem;
+ padding-right: 0.125rem;
+ border-radius: 0.125rem;
+ color: rgb(62,67,87);
+ background-color: rgb(225,230,243);
+}
+
+<div>
+ <div
+ class="sw-flex sw-gap-1"
+ >
+ <span
+ class="emotion-0 emotion-1"
+ >
+ Ctrl
+ </span>
+ <span>
+ +
+ </span>
+ <span
+ class="emotion-0 emotion-1"
+ >
+ <svg
+ aria-hidden="true"
+ class="octicon octicon-triangle-down"
+ fill="currentColor"
+ focusable="false"
+ height="16"
+ role="img"
+ style="display: inline-block; user-select: none; vertical-align: middle; overflow: visible;"
+ viewBox="0 0 16 16"
+ width="16"
+ >
+ <path
+ d="m4.427 7.427 3.396 3.396a.25.25 0 0 0 .354 0l3.396-3.396A.25.25 0 0 0 11.396 7H4.604a.25.25 0 0 0-.177.427Z"
+ />
+ </svg>
+ </span>
+ <span
+ class="emotion-0 emotion-1"
+ >
+ <svg
+ aria-hidden="true"
+ class="octicon octicon-triangle-up"
+ fill="currentColor"
+ focusable="false"
+ height="16"
+ role="img"
+ style="display: inline-block; user-select: none; vertical-align: middle; overflow: visible;"
+ viewBox="0 0 16 16"
+ width="16"
+ >
+ <path
+ d="m4.427 9.573 3.396-3.396a.25.25 0 0 1 .354 0l3.396 3.396a.25.25 0 0 1-.177.427H4.604a.25.25 0 0 1-.177-.427Z"
+ />
+ </svg>
+ </span>
+ </div>
+</div>
+`;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { TriangleDownIcon as Octicon } from '@primer/octicons-react';
+import { OcticonHoc } from './Icon';
+
+export const TriangleDownIcon = OcticonHoc(Octicon);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { TriangleLeftIcon as Octicon } from '@primer/octicons-react';
+import { OcticonHoc } from './Icon';
+
+export const TriangleLeftIcon = OcticonHoc(Octicon);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { TriangleRightIcon as Octicon } from '@primer/octicons-react';
+import { OcticonHoc } from './Icon';
+
+export const TriangleRightIcon = OcticonHoc(Octicon);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { TriangleUpIcon as Octicon } from '@primer/octicons-react';
+import { OcticonHoc } from './Icon';
+
+export const TriangleUpIcon = OcticonHoc(Octicon);
export { StatusOpenIcon } from './StatusOpenIcon';
export { StatusReopenedIcon } from './StatusReopenedIcon';
export { StatusResolvedIcon } from './StatusResolvedIcon';
+export { TriangleDownIcon } from './TriangleDownIcon';
+export { TriangleLeftIcon } from './TriangleLeftIcon';
+export { TriangleRightIcon } from './TriangleRightIcon';
+export { TriangleUpIcon } from './TriangleUpIcon';
export { UnfoldDownIcon } from './UnfoldDownIcon';
export { UnfoldIcon } from './UnfoldIcon';
export { UnfoldUpIcon } from './UnfoldUpIcon';
export { InputSearch } from './InputSearch';
export * from './InputSelect';
export * from './InteractiveIcon';
+export * from './KeyboardHint';
export * from './Link';
export { StandoutLink as Link } from './Link';
export * from './MainAppBar';
ArrowDown = 'ArrowDown',
Alt = 'Alt',
+ Option = 'Option',
Backspace = 'Backspace',
CapsLock = 'CapsLock',
Meta = 'Meta',
Control = 'Control',
+ Command = 'Command',
Delete = 'Delete',
End = 'End',
Enter = 'Enter',
// project analyse page
almCardBorder: COLORS.grey[100],
+
+ // Keyboard hint
+ keyboardHintKey: COLORS.blueGrey[100],
},
// contrast colors to be used for text when using a color background with the same name
newsTag: COLORS.blueGrey[500],
roadmap: COLORS.blueGrey[600],
roadmapContent: COLORS.blueGrey[500],
+
+ // Keyboard hint
+ keyboardHintKey: COLORS.blueGrey[500],
},
// predefined shadows