From: Jeremy Davis Date: Wed, 12 Apr 2023 12:54:18 +0000 (+0200) Subject: SONAR-19025 Add ToggleButton component X-Git-Tag: 10.1.0.73491~444 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=ca5d9265153d9186dd86251b3c1043af277ee9d8;p=sonarqube.git SONAR-19025 Add ToggleButton component --- diff --git a/server/sonar-web/design-system/src/components/ToggleButton.tsx b/server/sonar-web/design-system/src/components/ToggleButton.tsx new file mode 100644 index 00000000000..82dcc07b304 --- /dev/null +++ b/server/sonar-web/design-system/src/components/ToggleButton.tsx @@ -0,0 +1,115 @@ +/* + * 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 Badge from './Badge'; +import { ButtonSecondary } from './buttons'; + +type ToggleButtonValueType = string | number | boolean; + +export interface ToggleButtonsOption { + counter?: number; + disabled?: boolean; + label: string; + value: T; +} + +export interface ButtonToggleProps { + disabled?: boolean; + label?: string; + onChange: (value: T) => void; + options: Array>; + value?: T; +} + +export function ToggleButton(props: ButtonToggleProps) { + const { disabled = false, label, options, value } = props; + + return ( + + {options.map((option) => ( + { + if (option.value !== value) { + props.onChange(option.value); + } + }} + role="radio" + selected={option.value === value} + > + {option.label} + {option.counter ? ( + + {option.counter} + + ) : null} + + ))} + + ); +} + +const Wrapper = styled.div` + border: ${themeBorder('default', 'toggleBorder')}; + + ${tw`sw-inline-flex`} + ${tw`sw-h-control`} + ${tw`sw-box-border`} + ${tw`sw-font-semibold`} + ${tw`sw-rounded-2`} +`; + +const OptionButton = styled(ButtonSecondary)<{ selected: boolean }>` + background: ${(props) => (props.selected ? themeColor('toggleHover') : themeColor('toggle'))}; + color: ${(props) => (props.selected ? themeContrast('toggleHover') : themeContrast('toggle'))}; + border: none; + height: auto; + overflow: hidden; + ${tw`sw-rounded-0`}; + + &:first-of-type { + ${tw`sw-rounded-l-2`}; + } + + &:last-of-type { + ${tw`sw-rounded-r-2`}; + } + + &:not(:last-of-type) { + border-right: ${themeBorder('default', 'toggleBorder')}; + } + + &:hover { + background: ${themeColor('toggleHover')}; + color: ${themeContrast('toggleHover')}; + } + + &:focus, + &:active { + outline: ${themeBorder('focus', 'toggleFocus')}; + z-index: 1; + } +`; diff --git a/server/sonar-web/design-system/src/components/__tests__/ToggleButton-test.tsx b/server/sonar-web/design-system/src/components/__tests__/ToggleButton-test.tsx new file mode 100644 index 00000000000..d42cd875eee --- /dev/null +++ b/server/sonar-web/design-system/src/components/__tests__/ToggleButton-test.tsx @@ -0,0 +1,48 @@ +/* + * 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 { screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import { render } from '../../helpers/testUtils'; +import { FCProps } from '../../types/misc'; +import { ToggleButton, ToggleButtonsOption } from '../ToggleButton'; + +it('should render all options', async () => { + const user = userEvent.setup(); + + const onChange = jest.fn(); + + const options: Array> = [ + { value: 1, label: 'first' }, + { value: 2, label: 'disabled', disabled: true }, + { value: 3, label: 'has counter', counter: 7 }, + ]; + + renderToggleButtons({ onChange, options, value: 1 }); + + expect(screen.getAllByRole('radio')).toHaveLength(3); + + await user.click(screen.getByText('has counter')); + + expect(onChange).toHaveBeenCalledWith(3); +}); + +function renderToggleButtons(props: Partial> = {}) { + return render(); +} diff --git a/server/sonar-web/design-system/src/components/buttons.tsx b/server/sonar-web/design-system/src/components/buttons.tsx index ab0fcf9fb0c..b939dbbe4c0 100644 --- a/server/sonar-web/design-system/src/components/buttons.tsx +++ b/server/sonar-web/design-system/src/components/buttons.tsx @@ -29,7 +29,7 @@ import { BaseLink, LinkProps } from './Link'; type AllowedButtonAttributes = Pick< React.ButtonHTMLAttributes, - 'aria-label' | 'autoFocus' | 'id' | 'name' | 'style' | 'title' | 'type' + 'aria-label' | 'autoFocus' | 'id' | 'name' | 'role' | 'style' | 'title' | 'type' >; export interface ButtonProps extends AllowedButtonAttributes { @@ -52,8 +52,6 @@ class Button extends React.PureComponent { const { disabled, onClick, stopPropagation = false, type } = this.props; const { preventDefault = type !== 'submit' } = this.props; - event.currentTarget.blur(); - if (preventDefault || disabled) { event.preventDefault(); } diff --git a/server/sonar-web/design-system/src/components/index.ts b/server/sonar-web/design-system/src/components/index.ts index 1a9eb75d615..a9e95d2a208 100644 --- a/server/sonar-web/design-system/src/components/index.ts +++ b/server/sonar-web/design-system/src/components/index.ts @@ -39,6 +39,7 @@ export * from './NavBarTabs'; export { default as QualityGateIndicator } from './QualityGateIndicator'; export * from './SonarQubeLogo'; export * from './Text'; +export { ToggleButton } from './ToggleButton'; export { TopBar } from './TopBar'; export * from './buttons'; export * from './icons';