aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/design-system/src/components
diff options
context:
space:
mode:
authorstanislavh <stanislav.honcharov@sonarsource.com>2023-05-16 18:27:46 +0200
committersonartech <sonartech@sonarsource.com>2023-05-24 20:03:14 +0000
commit20c81961de9c92422410a5657ed48a3093742bde (patch)
treeea6e6ec3f3d5eb88b1d26cfd5ab4e742821c6ba1 /server/sonar-web/design-system/src/components
parent28a8a0f6d2e079e16a511d4112dbf17e66c514f7 (diff)
downloadsonarqube-20c81961de9c92422410a5657ed48a3093742bde.tar.gz
sonarqube-20c81961de9c92422410a5657ed48a3093742bde.zip
SONAR-19236 Implement new design for hotspot header
Diffstat (limited to 'server/sonar-web/design-system/src/components')
-rw-r--r--server/sonar-web/design-system/src/components/FormField.tsx74
-rw-r--r--server/sonar-web/design-system/src/components/SearchSelectDropdown.tsx14
-rw-r--r--server/sonar-web/design-system/src/components/SearchSelectDropdownControl.tsx4
-rw-r--r--server/sonar-web/design-system/src/components/Text.tsx12
-rw-r--r--server/sonar-web/design-system/src/components/__tests__/FormField-test.tsx46
-rw-r--r--server/sonar-web/design-system/src/components/buttons.tsx2
-rw-r--r--server/sonar-web/design-system/src/components/icons/RequiredIcon.tsx33
-rw-r--r--server/sonar-web/design-system/src/components/icons/index.ts1
-rw-r--r--server/sonar-web/design-system/src/components/index.ts1
9 files changed, 184 insertions, 3 deletions
diff --git a/server/sonar-web/design-system/src/components/FormField.tsx b/server/sonar-web/design-system/src/components/FormField.tsx
new file mode 100644
index 00000000000..a6bac58d5e8
--- /dev/null
+++ b/server/sonar-web/design-system/src/components/FormField.tsx
@@ -0,0 +1,74 @@
+/*
+ * 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 { ReactNode } from 'react';
+import tw from 'twin.macro';
+import { Highlight, Note } from './Text';
+import { RequiredIcon } from './icons';
+
+interface Props {
+ ariaLabel?: string;
+ children: ReactNode;
+ className?: string;
+ description?: string | ReactNode;
+ help?: ReactNode;
+ htmlFor?: string;
+ id?: string;
+ label: string | ReactNode;
+ required?: boolean;
+ title?: string;
+}
+
+export function FormField({
+ children,
+ className,
+ description,
+ help,
+ id,
+ required,
+ label,
+ htmlFor,
+ title,
+ ariaLabel,
+}: Props) {
+ return (
+ <FieldWrapper className={className} id={id}>
+ <label aria-label={ariaLabel} className="sw-mb-2" htmlFor={htmlFor} title={title}>
+ <Highlight className="sw-flex sw-items-center sw-gap-2">
+ {label}
+ {required && <RequiredIcon className="sw--ml-1" />}
+ {help}
+ </Highlight>
+ </label>
+
+ {children}
+
+ {description && <Note className="sw-mt-2">{description}</Note>}
+ </FieldWrapper>
+ );
+}
+
+const FieldWrapper = styled.div`
+ ${tw`sw-flex sw-flex-col sw-w-full`}
+
+ &:not(:last-of-type) {
+ ${tw`sw-mb-6`}
+ }
+`;
diff --git a/server/sonar-web/design-system/src/components/SearchSelectDropdown.tsx b/server/sonar-web/design-system/src/components/SearchSelectDropdown.tsx
index 67987e450bd..7493c146ea6 100644
--- a/server/sonar-web/design-system/src/components/SearchSelectDropdown.tsx
+++ b/server/sonar-web/design-system/src/components/SearchSelectDropdown.tsx
@@ -52,6 +52,7 @@ export interface SearchSelectDropdownProps<
Group extends GroupBase<Option> = GroupBase<Option>
> extends SelectProps<V, Option, IsMulti, Group>,
AsyncProps<Option, IsMulti, Group> {
+ controlAriaLabel?: string;
controlLabel?: React.ReactNode | string;
isDiscreet?: boolean;
}
@@ -62,7 +63,16 @@ export function SearchSelectDropdown<
IsMulti extends boolean = false,
Group extends GroupBase<Option> = GroupBase<Option>
>(props: SearchSelectDropdownProps<V, Option, IsMulti, Group>) {
- const { isDiscreet, value, loadOptions, controlLabel, isDisabled, minLength, ...rest } = props;
+ const {
+ isDiscreet,
+ value,
+ loadOptions,
+ controlLabel,
+ isDisabled,
+ minLength,
+ controlAriaLabel,
+ ...rest
+ } = props;
const [open, setOpen] = React.useState(false);
const [inputValue, setInputValue] = React.useState('');
@@ -112,6 +122,7 @@ export function SearchSelectDropdown<
<DropdownToggler
allowResizing={true}
className="sw-overflow-visible sw-border-none"
+ isPortal={true}
onRequestClose={() => {
toggleDropdown(false);
}}
@@ -140,6 +151,7 @@ export function SearchSelectDropdown<
}
>
<SearchSelectDropdownControl
+ ariaLabel={controlAriaLabel}
disabled={isDisabled}
isDiscreet={isDiscreet}
label={controlLabel}
diff --git a/server/sonar-web/design-system/src/components/SearchSelectDropdownControl.tsx b/server/sonar-web/design-system/src/components/SearchSelectDropdownControl.tsx
index fcb802f32ff..e0b50db401c 100644
--- a/server/sonar-web/design-system/src/components/SearchSelectDropdownControl.tsx
+++ b/server/sonar-web/design-system/src/components/SearchSelectDropdownControl.tsx
@@ -27,6 +27,7 @@ import { InputSizeKeys } from '../types/theme';
import { ChevronDownIcon } from './icons';
interface SearchSelectDropdownControlProps {
+ ariaLabel?: string;
disabled?: boolean;
isDiscreet?: boolean;
label?: React.ReactNode | string;
@@ -35,9 +36,10 @@ interface SearchSelectDropdownControlProps {
}
export function SearchSelectDropdownControl(props: SearchSelectDropdownControlProps) {
- const { disabled, label, isDiscreet, onClick, size = 'full' } = props;
+ const { disabled, label, isDiscreet, onClick, size = 'full', ariaLabel = '' } = props;
return (
<StyledControl
+ aria-label={ariaLabel}
className={classNames({ 'is-discreet': isDiscreet })}
onClick={() => {
if (!disabled) {
diff --git a/server/sonar-web/design-system/src/components/Text.tsx b/server/sonar-web/design-system/src/components/Text.tsx
index f5ac7df3a6e..aff69978ad3 100644
--- a/server/sonar-web/design-system/src/components/Text.tsx
+++ b/server/sonar-web/design-system/src/components/Text.tsx
@@ -105,3 +105,15 @@ export const LightPrimary = styled.span`
export const PageContentFontWrapper = styled.div`
color: ${themeColor('pageContent')};
`;
+
+export const Highlight = styled.strong`
+ color: ${themeColor('pageContentDark')};
+
+ ${tw`sw-body-sm-highlight`}
+`;
+
+export const Note = styled.span`
+ color: ${themeColor('pageContentLight')};
+
+ ${tw`sw-body-sm`}
+`;
diff --git a/server/sonar-web/design-system/src/components/__tests__/FormField-test.tsx b/server/sonar-web/design-system/src/components/__tests__/FormField-test.tsx
new file mode 100644
index 00000000000..ee86ad31e26
--- /dev/null
+++ b/server/sonar-web/design-system/src/components/__tests__/FormField-test.tsx
@@ -0,0 +1,46 @@
+/*
+ * 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 { FCProps } from '~types/misc';
+import { render } from '../../helpers/testUtils';
+import { FormField } from '../FormField';
+
+it('should render correctly', () => {
+ renderFormField({}, <input id="input" />);
+ expect(screen.getByLabelText('Hello')).toBeInTheDocument();
+});
+
+it('should render with required and description', () => {
+ renderFormField({ description: 'some description', required: true }, <input id="input" />);
+ expect(screen.getByText('some description')).toBeInTheDocument();
+ expect(screen.getByText('*')).toBeInTheDocument();
+});
+
+function renderFormField(
+ props: Partial<FCProps<typeof FormField>> = {},
+ children: any = <div>Fake input</div>
+) {
+ return render(
+ <FormField htmlFor="input" label="Hello" {...props}>
+ {children}
+ </FormField>
+ );
+}
diff --git a/server/sonar-web/design-system/src/components/buttons.tsx b/server/sonar-web/design-system/src/components/buttons.tsx
index 28650a777c0..c6d2ccd3ac8 100644
--- a/server/sonar-web/design-system/src/components/buttons.tsx
+++ b/server/sonar-web/design-system/src/components/buttons.tsx
@@ -38,7 +38,7 @@ export interface ButtonProps extends AllowedButtonAttributes {
disabled?: boolean;
icon?: React.ReactNode;
innerRef?: React.Ref<HTMLButtonElement>;
- onClick?: (event?: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement>) => void;
+ onClick?: (event?: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement>) => unknown;
preventDefault?: boolean;
reloadDocument?: LinkProps['reloadDocument'];
diff --git a/server/sonar-web/design-system/src/components/icons/RequiredIcon.tsx b/server/sonar-web/design-system/src/components/icons/RequiredIcon.tsx
new file mode 100644
index 00000000000..d39fbc6c197
--- /dev/null
+++ b/server/sonar-web/design-system/src/components/icons/RequiredIcon.tsx
@@ -0,0 +1,33 @@
+/*
+ * 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 } from '../../helpers/theme';
+
+export function RequiredIcon(props: React.ComponentPropsWithoutRef<'em'>) {
+ return <StyledEm {...props}>*</StyledEm>;
+}
+
+export const StyledEm = styled.em`
+ ${tw`sw-body-sm`}
+ ${tw`sw-not-italic`}
+ ${tw`sw-ml-2`}
+ color: ${themeColor('inputRequired')};
+`;
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 6f4a7a4c893..301e4c08af8 100644
--- a/server/sonar-web/design-system/src/components/icons/index.ts
+++ b/server/sonar-web/design-system/src/components/icons/index.ts
@@ -53,6 +53,7 @@ export { PencilIcon } from './PencilIcon';
export { ProjectIcon } from './ProjectIcon';
export { PullRequestIcon } from './PullRequestIcon';
export { RefreshIcon } from './RefreshIcon';
+export { RequiredIcon } from './RequiredIcon';
export { SecurityHotspotIcon } from './SecurityHotspotIcon';
export { SeparatorCircleIcon } from './SeparatorCircleIcon';
export { SeverityBlockerIcon } from './SeverityBlockerIcon';
diff --git a/server/sonar-web/design-system/src/components/index.ts b/server/sonar-web/design-system/src/components/index.ts
index edff1756a63..7e500593b08 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 './FacetItem';
export { FailedQGConditionLink } from './FailedQGConditionLink';
export { FlagMessage } from './FlagMessage';
export * from './FlowStep';
+export * from './FormField';
export * from './GenericAvatar';
export * from './HighlightedSection';
export { HotspotRating } from './HotspotRating';