diff options
Diffstat (limited to 'server/sonar-web')
14 files changed, 1162 insertions, 0 deletions
diff --git a/server/sonar-web/design-system/src/components/KeyboardHint.tsx b/server/sonar-web/design-system/src/components/KeyboardHint.tsx new file mode 100644 index 00000000000..cbfa57434c3 --- /dev/null +++ b/server/sonar-web/design-system/src/components/KeyboardHint.tsx @@ -0,0 +1,52 @@ +/* + * 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'); +} diff --git a/server/sonar-web/design-system/src/components/KeyboardHintKeys.tsx b/server/sonar-web/design-system/src/components/KeyboardHintKeys.tsx new file mode 100644 index 00000000000..7aa37f175d3 --- /dev/null +++ b/server/sonar-web/design-system/src/components/KeyboardHintKeys.tsx @@ -0,0 +1,78 @@ +/* + * 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; + } +} diff --git a/server/sonar-web/design-system/src/components/__tests__/KeyboardHint-test.tsx b/server/sonar-web/design-system/src/components/__tests__/KeyboardHint-test.tsx new file mode 100644 index 00000000000..95afb94442e --- /dev/null +++ b/server/sonar-web/design-system/src/components/__tests__/KeyboardHint-test.tsx @@ -0,0 +1,64 @@ +/* + * 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} />); +} diff --git a/server/sonar-web/design-system/src/components/__tests__/KeyboardHintKeys-test.tsx b/server/sonar-web/design-system/src/components/__tests__/KeyboardHintKeys-test.tsx new file mode 100644 index 00000000000..4d1ff44cede --- /dev/null +++ b/server/sonar-web/design-system/src/components/__tests__/KeyboardHintKeys-test.tsx @@ -0,0 +1,59 @@ +/* + * 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} />); +} diff --git a/server/sonar-web/design-system/src/components/__tests__/__snapshots__/KeyboardHint-test.tsx.snap b/server/sonar-web/design-system/src/components/__tests__/__snapshots__/KeyboardHint-test.tsx.snap new file mode 100644 index 00000000000..081af387a40 --- /dev/null +++ b/server/sonar-web/design-system/src/components/__tests__/__snapshots__/KeyboardHint-test.tsx.snap @@ -0,0 +1,291 @@ +// 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> +`; diff --git a/server/sonar-web/design-system/src/components/__tests__/__snapshots__/KeyboardHintKeys-test.tsx.snap b/server/sonar-web/design-system/src/components/__tests__/__snapshots__/KeyboardHintKeys-test.tsx.snap new file mode 100644 index 00000000000..907e3994ef0 --- /dev/null +++ b/server/sonar-web/design-system/src/components/__tests__/__snapshots__/KeyboardHintKeys-test.tsx.snap @@ -0,0 +1,513 @@ +// 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> +`; diff --git a/server/sonar-web/design-system/src/components/icons/TriangleDownIcon.tsx b/server/sonar-web/design-system/src/components/icons/TriangleDownIcon.tsx new file mode 100644 index 00000000000..44c8cc5c630 --- /dev/null +++ b/server/sonar-web/design-system/src/components/icons/TriangleDownIcon.tsx @@ -0,0 +1,23 @@ +/* + * 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); diff --git a/server/sonar-web/design-system/src/components/icons/TriangleLeftIcon.tsx b/server/sonar-web/design-system/src/components/icons/TriangleLeftIcon.tsx new file mode 100644 index 00000000000..2b2e0f29619 --- /dev/null +++ b/server/sonar-web/design-system/src/components/icons/TriangleLeftIcon.tsx @@ -0,0 +1,23 @@ +/* + * 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); diff --git a/server/sonar-web/design-system/src/components/icons/TriangleRightIcon.tsx b/server/sonar-web/design-system/src/components/icons/TriangleRightIcon.tsx new file mode 100644 index 00000000000..fd8dc00aa1a --- /dev/null +++ b/server/sonar-web/design-system/src/components/icons/TriangleRightIcon.tsx @@ -0,0 +1,23 @@ +/* + * 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); diff --git a/server/sonar-web/design-system/src/components/icons/TriangleUpIcon.tsx b/server/sonar-web/design-system/src/components/icons/TriangleUpIcon.tsx new file mode 100644 index 00000000000..9f192daf12b --- /dev/null +++ b/server/sonar-web/design-system/src/components/icons/TriangleUpIcon.tsx @@ -0,0 +1,23 @@ +/* + * 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); diff --git a/server/sonar-web/design-system/src/components/icons/index.ts b/server/sonar-web/design-system/src/components/icons/index.ts index 114132d4710..3b23d108df4 100644 --- a/server/sonar-web/design-system/src/components/icons/index.ts +++ b/server/sonar-web/design-system/src/components/icons/index.ts @@ -61,6 +61,10 @@ export { StatusConfirmedIcon } from './StatusConfirmedIcon'; 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'; diff --git a/server/sonar-web/design-system/src/components/index.ts b/server/sonar-web/design-system/src/components/index.ts index 83f42811301..1859acdfaba 100644 --- a/server/sonar-web/design-system/src/components/index.ts +++ b/server/sonar-web/design-system/src/components/index.ts @@ -40,6 +40,7 @@ export { HotspotRating } from './HotspotRating'; 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'; diff --git a/server/sonar-web/design-system/src/helpers/keyboard.ts b/server/sonar-web/design-system/src/helpers/keyboard.ts index 42bc6bdf52e..37b26e3cb2e 100644 --- a/server/sonar-web/design-system/src/helpers/keyboard.ts +++ b/server/sonar-web/design-system/src/helpers/keyboard.ts @@ -24,10 +24,12 @@ export enum Key { ArrowDown = 'ArrowDown', Alt = 'Alt', + Option = 'Option', Backspace = 'Backspace', CapsLock = 'CapsLock', Meta = 'Meta', Control = 'Control', + Command = 'Command', Delete = 'Delete', End = 'End', Enter = 'Enter', diff --git a/server/sonar-web/design-system/src/theme/light.ts b/server/sonar-web/design-system/src/theme/light.ts index 6a2f8bd5e95..24cd71f3ea3 100644 --- a/server/sonar-web/design-system/src/theme/light.ts +++ b/server/sonar-web/design-system/src/theme/light.ts @@ -472,6 +472,9 @@ export const lightTheme = { // 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 @@ -657,6 +660,9 @@ export const lightTheme = { newsTag: COLORS.blueGrey[500], roadmap: COLORS.blueGrey[600], roadmapContent: COLORS.blueGrey[500], + + // Keyboard hint + keyboardHintKey: COLORS.blueGrey[500], }, // predefined shadows |