diff --git a/airbyte-webapp/src/components/connection/UpdateConnectionDataResidency/UpdateConnectionDataResidency.tsx b/airbyte-webapp/src/components/connection/UpdateConnectionDataResidency/UpdateConnectionDataResidency.tsx index 3f2cbfadb03c..bff661856ad9 100644 --- a/airbyte-webapp/src/components/connection/UpdateConnectionDataResidency/UpdateConnectionDataResidency.tsx +++ b/airbyte-webapp/src/components/connection/UpdateConnectionDataResidency/UpdateConnectionDataResidency.tsx @@ -5,6 +5,7 @@ import { DataGeographyDropdown } from "components/common/DataGeographyDropdown"; import { ControlLabels } from "components/LabeledControl"; import { Card } from "components/ui/Card"; import { Spinner } from "components/ui/Spinner"; +import { ToastType } from "components/ui/Toast"; import { Geography } from "core/request/AirbyteClient"; import { useConnectionEditService } from "hooks/services/ConnectionEdit/ConnectionEditService"; @@ -32,8 +33,8 @@ export const UpdateConnectionDataResidency: React.FC = () => { } catch (e) { registerNotification({ id: "connection.geographyUpdateError", - title: formatMessage({ id: "connection.geographyUpdateError" }), - isError: true, + text: formatMessage({ id: "connection.geographyUpdateError" }), + type: ToastType.ERROR, }); } setSelectedValue(undefined); diff --git a/airbyte-webapp/src/components/ui/Button/Button.module.scss b/airbyte-webapp/src/components/ui/Button/Button.module.scss index 1358b7fbc546..55dfdaa9362c 100644 --- a/airbyte-webapp/src/components/ui/Button/Button.module.scss +++ b/airbyte-webapp/src/components/ui/Button/Button.module.scss @@ -13,6 +13,7 @@ font-weight: 600; cursor: pointer; transition: 0.2s ease-in; + white-space: nowrap; &.full { width: 100%; diff --git a/airbyte-webapp/src/components/ui/Toast/ErrorSign.tsx b/airbyte-webapp/src/components/ui/Toast/ErrorSign.tsx deleted file mode 100644 index d0e0e7fb7842..000000000000 --- a/airbyte-webapp/src/components/ui/Toast/ErrorSign.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { faExclamation } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import React from "react"; -import styled from "styled-components"; - -const ErrorIcon = styled.div` - width: 28px; - min-width: 28px; - height: 28px; - border-radius: 50%; - margin-right: 11px; - display: flex; - justify-content: center; - align-items: center; - background: ${({ theme }) => theme.dangerColor}; - border: 1px solid ${({ theme }) => theme.mediumPrimaryColor20}; -`; - -const ExclamationLight = styled(FontAwesomeIcon)` - font-size: 16px; - color: ${({ theme }) => theme.whiteColor}; -`; - -export const ErrorSign: React.FC = () => ( - - - -); diff --git a/airbyte-webapp/src/components/ui/Toast/Toast.module.scss b/airbyte-webapp/src/components/ui/Toast/Toast.module.scss index 0bf06de4cd58..42d4ac5840fe 100644 --- a/airbyte-webapp/src/components/ui/Toast/Toast.module.scss +++ b/airbyte-webapp/src/components/ui/Toast/Toast.module.scss @@ -1,3 +1,98 @@ +@use "scss/colors"; +@use "scss/variables" as vars; +@use "scss/z-indices"; +@use "scss/mixins"; + +$toast-icon-size: 13px; +$toast-icon-container-size: 34px; +$toast-bottom-margin: 27px; + +@keyframes slide-up-animations { + 0% { + transform: translate(-50%, -100%); + bottom: -60px; + } + + 100% { + transform: translate(-50%, 0); + bottom: $toast-bottom-margin; + } +} + +@mixin type($name, $color, $background) { + &.#{$name} { + background-color: $background; + border: 1px solid $color; + + .iconContainer { + background-color: $color; + } + + .toastIcon { + color: $color; + } + } +} + +.toastContainer { + display: flex; + flex-direction: row; + align-items: flex-start; + gap: vars.$spacing-md; + max-width: vars.$width-max-notification; + position: fixed; + box-sizing: border-box; + bottom: $toast-bottom-margin; + left: 50%; + transform: translate(-50%, 0); + z-index: z-indices.$notification; + padding: vars.$spacing-md; + border-radius: vars.$border-radius-md; + animation: slide-up-animations 0.25s ease-out; + + @include mixins.shadow; + + @include type("info", colors.$blue-400, colors.$blue-50); + @include type("warning", colors.$yellow-500, colors.$yellow-50); + @include type("success", colors.$green-200, colors.$green-50); + @include type("error", colors.$red-300, colors.$red-50); +} + +.iconContainer { + width: $toast-icon-container-size; + height: $toast-icon-container-size; + max-height: $toast-icon-container-size; + min-width: $toast-icon-container-size; + padding: vars.$border-radius-md; + border-radius: vars.$border-radius-md; + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: center; +} + +.toastIcon { + width: $toast-icon-size; + height: $toast-icon-size; + background: colors.$white; + border-radius: 50%; +} + +.textContainer { + align-self: center; +} + +.text { + line-height: 17px; + text-align: left; +} + +.actionButton { + margin-top: vars.$spacing-xs; +} + .closeButton { - margin-left: 10px; + svg { + color: colors.$dark-blue-900; + } } diff --git a/airbyte-webapp/src/components/ui/Toast/Toast.tsx b/airbyte-webapp/src/components/ui/Toast/Toast.tsx index c0eec9417009..ef018b6e1869 100644 --- a/airbyte-webapp/src/components/ui/Toast/Toast.tsx +++ b/airbyte-webapp/src/components/ui/Toast/Toast.tsx @@ -1,86 +1,64 @@ -import { faTimes } from "@fortawesome/free-solid-svg-icons"; +import { faCheck, faExclamation, faTimes } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import classNames from "classnames"; import React from "react"; -import styled, { keyframes } from "styled-components"; -import { H5 } from "components/base/Titles"; +import { CrossIcon } from "components/icons/CrossIcon"; +import { Text } from "components/ui/Text"; import { Button } from "../Button"; -import { ErrorSign } from "./ErrorSign"; import styles from "./Toast.module.scss"; -interface ToastProps { - title: string | React.ReactNode; - text?: string | React.ReactNode; - hasError?: boolean; - onClose?: () => void; +export const enum ToastType { + WARNING = "warning", + SUCCESS = "success", + ERROR = "error", + INFO = "info", } -export const SlideUpAnimation = keyframes` - 0% { - translate(-50%, -100%); - bottom: -49px; - } - 100% { - translate(-50%, 0); - bottom: 49px; - } -`; - -const Singleton = styled.div<{ hasError?: boolean }>` - position: fixed; - bottom: 49px; - left: 50%; - transform: translate(-50%, 0); - z-index: 20; - - padding: 25px 25px 22px; - - background: ${({ theme, hasError }) => (hasError ? theme.lightDangerColor : theme.lightPrimaryColor)}; - border: 1px solid ${({ theme }) => theme.greyColor20}; - box-shadow: 0 1px 2px ${({ theme }) => theme.shadowColor}; - border-radius: 8px; - - display: flex; - flex-direction: row; - align-items: center; - - animation: ${SlideUpAnimation} 0.25s linear; -`; - -const Title = styled(H5)<{ hasError?: boolean }>` - color: ${({ theme, hasError }) => (hasError ? theme.dangerColor : theme.primaryColor)}; - - font-style: normal; - font-weight: bold; - font-size: 15px; - line-height: 18px; -`; +export interface ToastProps { + text: string | React.ReactNode; + type?: ToastType; + onAction?: () => void; + actionBtnText?: string; + onClose?: () => void; +} -const Text = styled.div` - color: ${({ theme }) => theme.mediumPrimaryColor}; +const ICON_MAPPING = { + [ToastType.WARNING]: faExclamation, + [ToastType.ERROR]: faTimes, + [ToastType.SUCCESS]: faCheck, + [ToastType.INFO]: faExclamation, +}; - font-style: normal; - font-weight: normal; - font-size: 14px; - line-height: 17px; - margin-top: 5px; -`; +const STYLES_BY_TYPE: Readonly> = { + [ToastType.WARNING]: styles.warning, + [ToastType.ERROR]: styles.error, + [ToastType.SUCCESS]: styles.success, + [ToastType.INFO]: styles.info, +}; -export const Toast: React.FC = (props) => ( - - {props.hasError && } -
- {props.title} - {props.text && {props.text}} +export const Toast: React.FC = ({ type = ToastType.INFO, onAction, actionBtnText, onClose, text }) => { + return ( +
+
+ +
+
+ {text && ( + + {text} + + )} +
+ {onAction && ( + + )} + {onClose && ( +
- {props.onClose && ( -