]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-22049 Align FlagMessage
authorIsmail Cherri <ismail.cherri@sonarsource.com>
Thu, 25 Apr 2024 15:36:02 +0000 (17:36 +0200)
committerMatteo Mara <matteo.mara@sonarsource.com>
Tue, 30 Apr 2024 08:59:04 +0000 (10:59 +0200)
server/sonar-web/design-system/src/components/FlagMessage.tsx [deleted file]
server/sonar-web/design-system/src/components/__tests__/FlagMessage-test.tsx [deleted file]
server/sonar-web/design-system/src/components/index.ts
server/sonar-web/design-system/src/sonar-aligned/components/FlagMessage.tsx [new file with mode: 0644]
server/sonar-web/design-system/src/sonar-aligned/components/__tests__/FlagMessage-test.tsx [new file with mode: 0644]
server/sonar-web/design-system/src/sonar-aligned/components/index.ts

diff --git a/server/sonar-web/design-system/src/components/FlagMessage.tsx b/server/sonar-web/design-system/src/components/FlagMessage.tsx
deleted file mode 100644 (file)
index 47fb9b9..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 classNames from 'classnames';
-import * as React from 'react';
-import { useIntl } from 'react-intl';
-import tw from 'twin.macro';
-import { themeBorder, themeColor, themeContrast } from '../helpers/theme';
-import { ThemeColors } from '../types/theme';
-import { InteractiveIcon } from './InteractiveIcon';
-import { CloseIcon, FlagErrorIcon, FlagInfoIcon, FlagSuccessIcon, FlagWarningIcon } from './icons';
-
-export type Variant = 'error' | 'warning' | 'success' | 'info';
-
-interface Props {
-  variant: Variant;
-}
-
-function getVariantInfo(variant: Variant) {
-  const variantList = {
-    error: {
-      icon: <FlagErrorIcon />,
-      borderColor: 'errorBorder',
-      backGroundColor: 'errorBackground',
-    },
-    warning: {
-      icon: <FlagWarningIcon />,
-      borderColor: 'warningBorder',
-      backGroundColor: 'warningBackground',
-    },
-    success: {
-      icon: <FlagSuccessIcon />,
-      borderColor: 'successBorder',
-      backGroundColor: 'successBackground',
-    },
-    info: {
-      icon: <FlagInfoIcon />,
-      borderColor: 'infoBorder',
-      backGroundColor: 'infoBackground',
-    },
-  } as const;
-
-  return variantList[variant];
-}
-
-export function FlagMessage(props: Props & React.HTMLAttributes<HTMLDivElement>) {
-  const { className, variant, ...domProps } = props;
-  const variantInfo = getVariantInfo(variant);
-
-  return (
-    <StyledFlag
-      backGroundColor={variantInfo.backGroundColor}
-      borderColor={variantInfo.borderColor}
-      className={classNames('alert', className)}
-      {...domProps}
-    >
-      {props.children && (
-        <div className="flag-inner">
-          <div className="flag-icon">{variantInfo.icon}</div>
-          <div className="flag-content">{props.children}</div>
-        </div>
-      )}
-    </StyledFlag>
-  );
-}
-
-FlagMessage.displayName = 'FlagMessage'; // so that tests don't see the obfuscated production name
-
-interface DismissableFlagMessageProps extends Props {
-  onDismiss: () => void;
-}
-
-export function DismissableFlagMessage(
-  props: DismissableFlagMessageProps & React.HTMLAttributes<HTMLDivElement>,
-) {
-  const { onDismiss, children, ...flagMessageProps } = props;
-  const intl = useIntl();
-  return (
-    <FlagMessage {...flagMessageProps}>
-      {children}
-      <DismissIcon
-        Icon={CloseIcon}
-        aria-label={intl.formatMessage({ id: 'dismiss' })}
-        className="sw-ml-3"
-        onClick={onDismiss}
-        size="small"
-      />
-    </FlagMessage>
-  );
-}
-
-DismissableFlagMessage.displayName = 'DismissableFlagMessage'; // so that tests don't see the obfuscated production name
-
-export const StyledFlag = styled.div<{
-  backGroundColor: ThemeColors;
-  borderColor: ThemeColors;
-}>`
-  ${tw`sw-inline-flex`}
-  ${tw`sw-min-h-10`}
-  ${tw`sw-rounded-1`}
-  ${tw`sw-box-border`}
-  border: ${({ borderColor }) => themeBorder('default', borderColor)};
-  background-color: ${themeColor('flagMessageBackground')};
-
-  :empty {
-    display: none;
-  }
-
-  & > .flag-inner {
-    ${tw`sw-flex sw-items-stretch`}
-    ${tw`sw-box-border`}
-  }
-
-  & .flag-icon {
-    ${tw`sw-flex sw-justify-center sw-items-center`}
-    ${tw`sw-rounded-l-1`}
-    ${tw`sw-px-3`}
-    background-color: ${({ backGroundColor }) => themeColor(backGroundColor)};
-  }
-
-  & .flag-content {
-    ${tw`sw-flex sw-flex-auto sw-items-center`}
-    ${tw`sw-overflow-auto`}
-    ${tw`sw-text-left`}
-    ${tw`sw-px-3 sw-py-2`}
-    ${tw`sw-body-sm`}
-    color: ${themeContrast('flagMessageBackground')};
-  }
-`;
-
-export const DismissIcon = styled(InteractiveIcon)`
-  --background: ${themeColor('productNews')};
-  --backgroundHover: ${themeColor('productNewsHover')};
-  --color: ${themeContrast('productNews')};
-  --colorHover: ${themeContrast('productNewsHover')};
-  --focus: ${themeColor('interactiveIconFocus', 0.2)};
-
-  height: 28px;
-`;
diff --git a/server/sonar-web/design-system/src/components/__tests__/FlagMessage-test.tsx b/server/sonar-web/design-system/src/components/__tests__/FlagMessage-test.tsx
deleted file mode 100644 (file)
index 2b2afc1..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2024 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 { IntlShape } from 'react-intl';
-import { render } from '../../helpers/testUtils';
-import { FCProps } from '../../types/misc';
-import { DismissableFlagMessage, FlagMessage, Variant } from '../FlagMessage';
-
-jest.mock(
-  'react-intl',
-  () =>
-    ({
-      ...jest.requireActual('react-intl'),
-      useIntl: () => ({
-        formatMessage: ({ id }: { id: string }, values = {}) =>
-          [id, ...Object.values(values)].join('.'),
-      }),
-    }) as IntlShape,
-);
-
-it.each([
-  ['error', '1px solid rgb(249,112,102)'],
-  ['warning', '1px solid rgb(248,205,92)'],
-  ['success', '1px solid rgb(50,213,131)'],
-  ['info', '1px solid rgb(110,185,228)'],
-])('should render properly for "%s" variant', (variant: Variant, color) => {
-  renderFlagMessage({ variant });
-
-  const item = screen.getByRole('status');
-  expect(item).toBeInTheDocument();
-  expect(item).toHaveStyle({ border: color });
-});
-
-it('should render Dismissable flag message properly', () => {
-  const dismissFunc = jest.fn();
-  render(<DismissableFlagMessage onDismiss={dismissFunc} role="status" variant="error" />);
-  const item = screen.getByRole('status');
-  expect(item).toBeInTheDocument();
-  expect(item).toHaveStyle({ border: '1px solid rgb(249,112,102)' });
-  const dismissButton = screen.getByRole('button');
-  expect(dismissButton).toBeInTheDocument();
-  dismissButton.click();
-  expect(dismissFunc).toHaveBeenCalled();
-});
-
-function renderFlagMessage(props: Partial<FCProps<typeof FlagMessage>> = {}) {
-  return render(
-    <FlagMessage role="status" variant="error" {...props}>
-      This is an error!
-    </FlagMessage>,
-  );
-}
index 35e06bdc5976a035c496ac1187ecd27db692c6b6..e94ac812c9e149b0fb1d66c845ac394e79ffbc4f 100644 (file)
@@ -39,7 +39,6 @@ export * from './FacetBox';
 export * from './FacetItem';
 export { FailedQGConditionLink } from './FailedQGConditionLink';
 export * from './FavoriteButton';
-export { DismissableFlagMessage, FlagMessage } from './FlagMessage';
 export * from './FlowStep';
 export * from './HighlightRing';
 export * from './HighlightedSection';
diff --git a/server/sonar-web/design-system/src/sonar-aligned/components/FlagMessage.tsx b/server/sonar-web/design-system/src/sonar-aligned/components/FlagMessage.tsx
new file mode 100644 (file)
index 0000000..6b05991
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 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 classNames from 'classnames';
+import * as React from 'react';
+import { useIntl } from 'react-intl';
+import tw from 'twin.macro';
+import { ThemeColors } from '~types/theme';
+import { InteractiveIcon } from '../../components/InteractiveIcon';
+import {
+  CloseIcon,
+  FlagErrorIcon,
+  FlagInfoIcon,
+  FlagSuccessIcon,
+  FlagWarningIcon,
+} from '../../components/icons';
+import { themeBorder, themeColor, themeContrast } from '../../helpers/theme';
+
+export type Variant = 'error' | 'warning' | 'success' | 'info';
+
+interface Props {
+  variant: Variant;
+}
+
+function getVariantInfo(variant: Variant) {
+  const variantList = {
+    error: {
+      icon: <FlagErrorIcon />,
+      borderColor: 'errorBorder',
+      backGroundColor: 'errorBackground',
+    },
+    warning: {
+      icon: <FlagWarningIcon />,
+      borderColor: 'warningBorder',
+      backGroundColor: 'warningBackground',
+    },
+    success: {
+      icon: <FlagSuccessIcon />,
+      borderColor: 'successBorder',
+      backGroundColor: 'successBackground',
+    },
+    info: {
+      icon: <FlagInfoIcon />,
+      borderColor: 'infoBorder',
+      backGroundColor: 'infoBackground',
+    },
+  } as const;
+
+  return variantList[variant];
+}
+
+export function FlagMessage(props: Props & React.HTMLAttributes<HTMLDivElement>) {
+  const { className, variant, ...domProps } = props;
+  const variantInfo = getVariantInfo(variant);
+
+  return (
+    <StyledFlag
+      backGroundColor={variantInfo.backGroundColor}
+      borderColor={variantInfo.borderColor}
+      className={classNames('alert', className)}
+      {...domProps}
+    >
+      {props.children && (
+        <div className="flag-inner">
+          <div className="flag-icon">{variantInfo.icon}</div>
+          <div className="flag-content">{props.children}</div>
+        </div>
+      )}
+    </StyledFlag>
+  );
+}
+
+FlagMessage.displayName = 'FlagMessage'; // so that tests don't see the obfuscated production name
+
+interface DismissableFlagMessageProps extends Props {
+  onDismiss: () => void;
+}
+
+export function DismissableFlagMessage(
+  props: DismissableFlagMessageProps & React.HTMLAttributes<HTMLDivElement>,
+) {
+  const { onDismiss, children, ...flagMessageProps } = props;
+  const intl = useIntl();
+  return (
+    <FlagMessage {...flagMessageProps}>
+      {children}
+      <DismissIcon
+        Icon={CloseIcon}
+        aria-label={intl.formatMessage({ id: 'dismiss' })}
+        className="sw-ml-3"
+        onClick={onDismiss}
+        size="small"
+      />
+    </FlagMessage>
+  );
+}
+
+DismissableFlagMessage.displayName = 'DismissableFlagMessage'; // so that tests don't see the obfuscated production name
+
+export const StyledFlag = styled.div<{
+  backGroundColor: ThemeColors;
+  borderColor: ThemeColors;
+}>`
+  ${tw`sw-inline-flex`}
+  ${tw`sw-min-h-10`}
+  ${tw`sw-rounded-1`}
+  ${tw`sw-box-border`}
+  border: ${({ borderColor }) => themeBorder('default', borderColor)};
+  background-color: ${themeColor('flagMessageBackground')};
+
+  :empty {
+    display: none;
+  }
+
+  & > .flag-inner {
+    ${tw`sw-flex sw-items-stretch`}
+    ${tw`sw-box-border`}
+  }
+
+  & .flag-icon {
+    ${tw`sw-flex sw-justify-center sw-items-center`}
+    ${tw`sw-rounded-l-1`}
+    ${tw`sw-px-3`}
+    background-color: ${({ backGroundColor }) => themeColor(backGroundColor)};
+  }
+
+  & .flag-content {
+    ${tw`sw-flex sw-flex-auto sw-items-center`}
+    ${tw`sw-overflow-auto`}
+    ${tw`sw-text-left`}
+    ${tw`sw-px-3 sw-py-2`}
+    ${tw`sw-body-sm`}
+    color: ${themeContrast('flagMessageBackground')};
+  }
+`;
+
+export const DismissIcon = styled(InteractiveIcon)`
+  --background: ${themeColor('productNews')};
+  --backgroundHover: ${themeColor('productNewsHover')};
+  --color: ${themeContrast('productNews')};
+  --colorHover: ${themeContrast('productNewsHover')};
+  --focus: ${themeColor('interactiveIconFocus', 0.2)};
+
+  height: 28px;
+`;
diff --git a/server/sonar-web/design-system/src/sonar-aligned/components/__tests__/FlagMessage-test.tsx b/server/sonar-web/design-system/src/sonar-aligned/components/__tests__/FlagMessage-test.tsx
new file mode 100644 (file)
index 0000000..6772436
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2024 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 { IntlShape } from 'react-intl';
+import { FCProps } from '~types/misc';
+import { render } from '../../../helpers/testUtils';
+import { DismissableFlagMessage, FlagMessage, Variant } from '../FlagMessage';
+
+jest.mock(
+  'react-intl',
+  () =>
+    ({
+      ...jest.requireActual('react-intl'),
+      useIntl: () => ({
+        formatMessage: ({ id }: { id: string }, values = {}) =>
+          [id, ...Object.values(values)].join('.'),
+      }),
+    }) as IntlShape,
+);
+
+it.each([
+  ['error', '1px solid rgb(249,112,102)'],
+  ['warning', '1px solid rgb(248,205,92)'],
+  ['success', '1px solid rgb(50,213,131)'],
+  ['info', '1px solid rgb(110,185,228)'],
+])('should render properly for "%s" variant', (variant: Variant, color) => {
+  renderFlagMessage({ variant });
+
+  const item = screen.getByRole('status');
+  expect(item).toBeInTheDocument();
+  expect(item).toHaveStyle({ border: color });
+});
+
+it('should render Dismissable flag message properly', () => {
+  const dismissFunc = jest.fn();
+  render(<DismissableFlagMessage onDismiss={dismissFunc} role="status" variant="error" />);
+  const item = screen.getByRole('status');
+  expect(item).toBeInTheDocument();
+  expect(item).toHaveStyle({ border: '1px solid rgb(249,112,102)' });
+  const dismissButton = screen.getByRole('button');
+  expect(dismissButton).toBeInTheDocument();
+  dismissButton.click();
+  expect(dismissFunc).toHaveBeenCalled();
+});
+
+function renderFlagMessage(props: Partial<FCProps<typeof FlagMessage>> = {}) {
+  return render(
+    <FlagMessage role="status" variant="error" {...props}>
+      This is an error!
+    </FlagMessage>,
+  );
+}
index 459ba9d1655fd51a94f23f55e3f924674fb2d0b5..9ffeb585803a97c6b619f43e2efeb94d05a4b1e2 100644 (file)
@@ -19,6 +19,7 @@
  */
 
 export * from './Card';
+export { DismissableFlagMessage, FlagMessage } from './FlagMessage';
 export * from './MetricsRatingBadge';
 export * from './Table';
 export * from './buttons';