aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/components/ui/Alert.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'server/sonar-web/src/main/js/components/ui/Alert.tsx')
-rw-r--r--server/sonar-web/src/main/js/components/ui/Alert.tsx159
1 files changed, 159 insertions, 0 deletions
diff --git a/server/sonar-web/src/main/js/components/ui/Alert.tsx b/server/sonar-web/src/main/js/components/ui/Alert.tsx
new file mode 100644
index 00000000000..abd339da10d
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/ui/Alert.tsx
@@ -0,0 +1,159 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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 * as classNames from 'classnames';
+import * as React from 'react';
+import { translate } from '../../helpers/l10n';
+import AlertErrorIcon from '../icons/AlertErrorIcon';
+import AlertSuccessIcon from '../icons/AlertSuccessIcon';
+import AlertWarnIcon from '../icons/AlertWarnIcon';
+import InfoIcon from '../icons/InfoIcon';
+import { css, styled, Theme, themeColor, ThemedProps, themeSize, useTheme } from '../theme';
+import DeferredSpinner from './DeferredSpinner';
+
+type AlertDisplay = 'banner' | 'inline' | 'block';
+type AlertVariant = 'error' | 'warning' | 'success' | 'info' | 'loading';
+
+export interface AlertProps {
+ display?: AlertDisplay;
+ variant: AlertVariant;
+}
+
+interface AlertVariantInformation {
+ icon: JSX.Element;
+ color: string;
+ borderColor: string;
+ backGroundColor: string;
+}
+
+const StyledAlertIcon = styled.div<{ isBanner: boolean; variantInfo: AlertVariantInformation }>`
+ flex: 0 0 auto;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: calc(${({ isBanner }) => (isBanner ? 2 : 4)} * ${themeSize('gridSize')});
+ border-right: ${({ isBanner }) => (!isBanner ? '1px solid' : 'none')};
+ border-color: ${({ variantInfo }) => variantInfo.borderColor};
+`;
+
+const StyledAlertContent = styled.div`
+ flex: 1 1 auto;
+ overflow: auto;
+ text-align: left;
+ padding: ${themeSize('gridSize')} calc(2 * ${themeSize('gridSize')});
+`;
+
+const alertInnerIsBannerMixin = ({ theme }: ThemedProps) => css`
+ min-width: ${theme.sizes.minPageWidth};
+ max-width: ${theme.sizes.maxPageWidth};
+ margin-left: auto;
+ margin-right: auto;
+ padding-left: ${theme.sizes.pagePadding};
+ padding-right: ${theme.sizes.pagePadding};
+ box-sizing: border-box;
+`;
+
+const StyledAlertInner = styled.div<{ isBanner: boolean }>`
+ display: flex;
+ align-items: stretch;
+ ${({ isBanner }) => (isBanner ? alertInnerIsBannerMixin : null)}
+`;
+
+const StyledAlert = styled.div<{ isInline: boolean; variantInfo: AlertVariantInformation }>`
+ border: 1px solid;
+ border-radius: 2px;
+ margin-bottom: ${themeSize('gridSize')};
+ border-color: ${({ variantInfo }) => variantInfo.borderColor};
+ background-color: ${({ variantInfo }) => variantInfo.backGroundColor};
+ color: ${({ variantInfo }) => variantInfo.color};
+ display: ${({ isInline }) => (isInline ? 'inline-block' : 'block')};
+
+ :empty {
+ display: none;
+ }
+
+ a,
+ .button-link {
+ border-color: ${themeColor('darkBlue')};
+ }
+`;
+
+function getAlertVariantInfo({ colors }: Theme, variant: AlertVariant): AlertVariantInformation {
+ const variantList: T.Dict<AlertVariantInformation> = {
+ error: {
+ icon: <AlertErrorIcon fill={colors.alertIconError} />,
+ color: colors.alertTextError,
+ borderColor: colors.alertBorderError,
+ backGroundColor: colors.alertBackgroundError
+ },
+ warning: {
+ icon: <AlertWarnIcon fill={colors.alertIconWarning} />,
+ color: colors.alertTextWarning,
+ borderColor: colors.alertBorderWarning,
+ backGroundColor: colors.alertBackgroundWarning
+ },
+ success: {
+ icon: <AlertSuccessIcon fill={colors.alertIconSuccess} />,
+ color: colors.alertTextSuccess,
+ borderColor: colors.alertBorderSuccess,
+ backGroundColor: colors.alertBackgroundSuccess
+ },
+ info: {
+ icon: <InfoIcon fill={colors.alertIconInfo} />,
+ color: colors.alertTextInfo,
+ borderColor: colors.alertBorderInfo,
+ backGroundColor: colors.alertBackgroundInfo
+ },
+ loading: {
+ icon: <DeferredSpinner timeout={0} />,
+ color: colors.alertTextInfo,
+ borderColor: colors.alertBorderInfo,
+ backGroundColor: colors.alertBackgroundInfo
+ }
+ };
+
+ return variantList[variant];
+}
+
+export function Alert(props: AlertProps & React.HTMLAttributes<HTMLDivElement>) {
+ const theme = useTheme();
+ const { className, display, variant, ...domProps } = props;
+ const isInline = display === 'inline';
+ const isBanner = display === 'banner';
+ const variantInfo = getAlertVariantInfo(theme, variant);
+
+ return (
+ <StyledAlert
+ className={classNames('alert', className)}
+ isInline={isInline}
+ role="alert"
+ variantInfo={variantInfo}
+ {...domProps}>
+ <StyledAlertInner isBanner={isBanner}>
+ <StyledAlertIcon
+ aria-label={translate('alert.tooltip', variant)}
+ isBanner={isBanner}
+ variantInfo={variantInfo}>
+ {variantInfo.icon}
+ </StyledAlertIcon>
+ <StyledAlertContent className="alert-content">{props.children}</StyledAlertContent>
+ </StyledAlertInner>
+ </StyledAlert>
+ );
+}