From f7cfaa0150d0a7898a3ebe9a7e8925f21e23d7a8 Mon Sep 17 00:00:00 2001 From: Kevin Silva Date: Fri, 5 May 2023 16:26:46 +0200 Subject: [PATCH] SONAR-19172 Issues page: create a "discreet select" component for the issue details header --- server/sonar-web/design-system/package.json | 1 + .../design-system/src/components/Checkbox.tsx | 2 +- .../src/components/DiscreetSelect.tsx | 118 +++++++++++ .../src/components/DropdownMenu.tsx | 6 +- .../src/components/InputSelect.tsx | 191 ++++++++++++++++++ .../src/components/RadioButton.tsx | 2 +- .../__tests__/DiscreetSelect-test.tsx | 59 ++++++ .../components/__tests__/InputSelect-test.tsx | 58 ++++++ .../design-system/src/components/index.ts | 2 + .../issue/components/IssueSeverity.tsx | 2 +- server/sonar-web/yarn.lock | 1 + 11 files changed, 436 insertions(+), 6 deletions(-) create mode 100644 server/sonar-web/design-system/src/components/DiscreetSelect.tsx create mode 100644 server/sonar-web/design-system/src/components/InputSelect.tsx create mode 100644 server/sonar-web/design-system/src/components/__tests__/DiscreetSelect-test.tsx create mode 100644 server/sonar-web/design-system/src/components/__tests__/InputSelect-test.tsx diff --git a/server/sonar-web/design-system/package.json b/server/sonar-web/design-system/package.json index 8e280e6794e..4e07b7d40ce 100644 --- a/server/sonar-web/design-system/package.json +++ b/server/sonar-web/design-system/package.json @@ -59,6 +59,7 @@ "react-helmet-async": "1.3.0", "react-intl": "6.2.5", "react-router-dom": "6.10.0", + "react-select": "5.7.2", "tailwindcss": "3.3.1" }, "babelMacros": { diff --git a/server/sonar-web/design-system/src/components/Checkbox.tsx b/server/sonar-web/design-system/src/components/Checkbox.tsx index 2968443b597..648fd6af48c 100644 --- a/server/sonar-web/design-system/src/components/Checkbox.tsx +++ b/server/sonar-web/design-system/src/components/Checkbox.tsx @@ -41,7 +41,7 @@ interface Props { title?: string; } -export default function Checkbox({ +export function Checkbox({ checked, disabled, children, diff --git a/server/sonar-web/design-system/src/components/DiscreetSelect.tsx b/server/sonar-web/design-system/src/components/DiscreetSelect.tsx new file mode 100644 index 00000000000..7d68321d666 --- /dev/null +++ b/server/sonar-web/design-system/src/components/DiscreetSelect.tsx @@ -0,0 +1,118 @@ +/* + * 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 { themeBorder, themeColor, themeContrast } from '../helpers/theme'; +import { InputSizeKeys } from '../types/theme'; +import { InputSelect, LabelValueSelectOption } from './InputSelect'; + +interface Props { + className?: string; + customValue?: JSX.Element; + options: LabelValueSelectOption[]; + setValue: ({ value }: LabelValueSelectOption) => void; + size?: InputSizeKeys; + value: string; +} + +export function DiscreetSelect({ + className, + customValue, + options, + size = 'small', + setValue, + value, + ...props +}: Props) { + return ( + item.value === value)} + {...props} + /> + ); +} + +const StyledSelect = styled(InputSelect)` + & { + width: inherit !important; + } + + & .react-select__dropdown-indicator { + ${tw`sw-p-0 sw-py-1`}; + } + + & .react-select__value-container { + ${tw`sw-p-0`}; + } + + & .react-select__menu { + margin: 0; + } + + & .react-select__control { + height: auto; + color: ${themeContrast('discreetBackground')}; + background: none; + outline: inherit; + box-shadow: none; + + ${tw`sw-border-none`}; + ${tw`sw-p-0`}; + ${tw`sw-cursor-pointer`}; + ${tw`sw-flex sw-items-center`}; + ${tw`sw-body-sm`}; + ${tw`sw-select-none`}; + + &:hover { + ${tw`sw-border-none`}; + outline: none; + color: ${themeColor('discreetButtonHover')}; + border-color: inherit; + box-shadow: none; + + & .react-select__single-value, + & .react-select__dropdown-indicator, + & .react-select__placeholder { + color: ${themeColor('discreetButtonHover')}; + } + } + + &:focus { + ${tw`sw-rounded-1`}; + color: ${themeColor('discreetButtonHover')}; + background: ${themeColor('discreetBackground')}; + outline: ${themeBorder('focus', 'discreetFocusBorder')}; + outline: none; + border-color: inherit; + box-shadow: none; + } + } + + & .react-select__control--is-focused, + & .react-select__control--menu-is-open { + ${tw`sw-border-none`}; + outline: none; + } +`; diff --git a/server/sonar-web/design-system/src/components/DropdownMenu.tsx b/server/sonar-web/design-system/src/components/DropdownMenu.tsx index d16011785b9..b9e430d7623 100644 --- a/server/sonar-web/design-system/src/components/DropdownMenu.tsx +++ b/server/sonar-web/design-system/src/components/DropdownMenu.tsx @@ -26,12 +26,12 @@ import { INPUT_SIZES } from '../helpers/constants'; import { translate } from '../helpers/l10n'; import { themeBorder, themeColor, themeContrast } from '../helpers/theme'; import { InputSizeKeys, ThemedProps } from '../types/theme'; -import Checkbox from './Checkbox'; -import { ClipboardBase } from './clipboard'; +import { Checkbox } from './Checkbox'; import { BaseLink, LinkProps } from './Link'; import NavLink from './NavLink'; -import RadioButton from './RadioButton'; +import { RadioButton } from './RadioButton'; import Tooltip from './Tooltip'; +import { ClipboardBase } from './clipboard'; interface Props extends React.HtmlHTMLAttributes { children?: React.ReactNode; diff --git a/server/sonar-web/design-system/src/components/InputSelect.tsx b/server/sonar-web/design-system/src/components/InputSelect.tsx new file mode 100644 index 00000000000..2563d3c9c15 --- /dev/null +++ b/server/sonar-web/design-system/src/components/InputSelect.tsx @@ -0,0 +1,191 @@ +/* + * 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 { useTheme as themeInfo } from '@emotion/react'; +import classNames from 'classnames'; +import { omit } from 'lodash'; +import { ReactNode } from 'react'; +import ReactSelect, { + GroupBase, + Props as NamedProps, + OptionProps, + StylesConfig, + components, +} from 'react-select'; +import { INPUT_SIZES } from '../helpers'; +import { themeBorder, themeColor, themeContrast } from '../helpers/theme'; +import { InputSizeKeys } from '../types/theme'; +import { ChevronDownIcon } from './icons'; + +export interface LabelValueSelectOption { + Icon?: ReactNode; + label: string; + value: V; +} + +interface StyleExtensionProps { + size?: InputSizeKeys; +} + +type SelectProps< + Option = LabelValueSelectOption, + IsMulti extends boolean = boolean, + Group extends GroupBase