diff --git a/packages/components/src/Alert/Alert.stories.tsx b/packages/components/src/Alert/Alert.stories.tsx index 32dd55531..c9cec2bb3 100644 --- a/packages/components/src/Alert/Alert.stories.tsx +++ b/packages/components/src/Alert/Alert.stories.tsx @@ -13,20 +13,64 @@ export default { export const Success: StoryObj = { args: { status: 'success', - children: 'Transaction was succesful!' + children: 'Transaction was succesful!', + onClose: undefined + } +}; + +export const Info: StoryObj = { + args: { + status: 'info', + children: 'This is some useful information!', + onClose: undefined } }; export const Warning: StoryObj = { args: { status: 'warning', - children: 'This is a warning message...' + children: 'This is a warning message...', + onClose: undefined } }; export const Error: StoryObj = { args: { status: 'error', - children: 'Error happened, please contact our support.' + children: 'Error happened, please contact our support.', + onClose: undefined + } +}; + +export const Outlined: StoryObj = { + args: { + variant: 'outlined', + children: 'Transaction was succesful!', + onClose: undefined + } +}; + +export const Filled: StoryObj = { + args: { + variant: 'filled', + children: 'Transaction was succesful!', + onClose: undefined + } +}; + +export const WithTitle: StoryObj = { + args: { + status: 'success', + title: 'This is a successful alert', + children: 'Transaction was succesful!', + onClose: undefined + } +}; + +export const Closable: StoryObj = { + args: { + status: 'success', + children: 'Transaction was succesful!', + onClose: () => {} } }; diff --git a/packages/components/src/Alert/Alert.style.tsx b/packages/components/src/Alert/Alert.style.tsx index 038c5469f..1079a8dac 100644 --- a/packages/components/src/Alert/Alert.style.tsx +++ b/packages/components/src/Alert/Alert.style.tsx @@ -1,38 +1,98 @@ -import { CheckCircle, InformationCircle, Warning } from '@interlay/icons'; +import { CheckCircle, ExclamationCircle, InformationCircle, Warning } from '@interlay/icons'; +import { AlertStatus, AlertVariant, Rounded } from '@interlay/theme'; import styled, { css } from 'styled-components'; -import { AlertStatus } from '@interlay/theme'; +import { Button } from '../Button'; import { Flex } from '../Flex'; type StyledAlertProps = { $status: AlertStatus; + $variant: AlertVariant; + $rounded?: Rounded; }; -// FIXME: waiting on JAy const StyledAlert = styled(Flex)` - ${({ $status, theme }) => css` + ${({ $status, $variant, $rounded, theme }) => css` ${theme.alert.base} - ${theme.alert.status[$status]} + ${theme.alert.status[$status][$variant].base} + border-radius: ${$rounded && theme.rounded($rounded)}; + `} +`; + +const StyledAlertTitle = styled.div` + ${({ $status, $variant, theme }) => css` + ${theme.alert.title} + ${theme.alert.status[$status][$variant].title} `} `; const StyledInformationCircle = styled(InformationCircle)` - ${({ $status, theme }) => css` - ${theme.alert.status[$status]} + ${({ $status, $variant, theme }) => css` + ${theme.alert.status[$status][$variant].icon} `} `; const StyledCheckCircle = styled(CheckCircle)` - ${({ $status, theme }) => css` - ${theme.alert.base} - ${theme.alert.status[$status]} + ${({ $status, $variant, theme }) => css` + ${theme.alert.status[$status][$variant].icon} `} `; const StyledWarning = styled(Warning)` - ${({ $status, theme }) => css` - ${theme.alert.status[$status].color} + ${({ $status, $variant, theme }) => css` + ${theme.alert.status[$status][$variant].icon} + `} +`; + +const StyledExclamationCircle = styled(ExclamationCircle)` + ${({ $status, $variant, theme }) => css` + ${theme.alert.status[$status][$variant].icon} + `} +`; + +const StyledIconWrapper = styled.div` + display: flex; + + ${({ theme }) => css` + ${theme.alert.icon} `} `; -export { StyledAlert, StyledInformationCircle, StyledCheckCircle, StyledWarning }; +const StyledContent = styled.div` + min-width: 0px; + overflow: auto; + + ${({ theme }) => css` + ${theme.alert.content} + `} +`; + +const StyledButton = styled(Button)` + margin-left: auto; + color: inherit; + + ${({ theme }) => css` + ${theme.alert.closeBtn} + `}; +`; + +const StyledButtonWrapper = styled.div` + margin-left: auto; + padding-left: ${({ theme }) => theme.spacing('xl')}; + ${({ theme }) => css` + ${theme.alert.closeBtnWrapper} + `}; +`; + +export { + StyledAlert, + StyledAlertTitle, + StyledCheckCircle, + StyledContent, + StyledButtonWrapper, + StyledButton, + StyledExclamationCircle, + StyledIconWrapper, + StyledInformationCircle, + StyledWarning +}; diff --git a/packages/components/src/Alert/Alert.tsx b/packages/components/src/Alert/Alert.tsx index 9f0675a33..a6532e371 100644 --- a/packages/components/src/Alert/Alert.tsx +++ b/packages/components/src/Alert/Alert.tsx @@ -1,32 +1,72 @@ -import { AlertStatus } from '@interlay/theme'; -import { ForwardRefExoticComponent } from 'react'; +import { XMark } from '@interlay/icons'; +import { AlertStatus, AlertVariant, Rounded } from '@interlay/theme'; +import { ForwardRefExoticComponent, ReactNode } from 'react'; import { FlexProps } from '../Flex'; -import { StyledAlert, StyledCheckCircle, StyledInformationCircle, StyledWarning } from './Alert.style'; +import { + StyledAlert, + StyledAlertTitle, + StyledButton, + StyledButtonWrapper, + StyledCheckCircle, + StyledContent, + StyledExclamationCircle, + StyledIconWrapper, + StyledInformationCircle, + StyledWarning +} from './Alert.style'; const iconMap: Record> = { info: StyledInformationCircle, success: StyledCheckCircle, - error: StyledWarning, + error: StyledExclamationCircle, warning: StyledWarning }; type Props = { status?: AlertStatus; + variant?: AlertVariant; + rounded?: Rounded; + title?: ReactNode; + onClose?: () => void; }; type InheritAttrs = Omit; type AlertProps = Props & InheritAttrs; -const Alert = ({ status = 'success', children, ...props }: AlertProps): JSX.Element => { +const Alert = ({ + status = 'success', + variant = 'default', + rounded, + children, + title, + onClose, + ...props +}: AlertProps): JSX.Element => { const Icon = iconMap[status]; return ( - - -
{children}
+ + + + + + {title && ( + + {title} + + )} + {children} + + {onClose && ( + + + + + + )} ); }; diff --git a/packages/components/src/Alert/__tests__/Alert.test.tsx b/packages/components/src/Alert/__tests__/Alert.test.tsx index a1815edd1..80bd815ec 100644 --- a/packages/components/src/Alert/__tests__/Alert.test.tsx +++ b/packages/components/src/Alert/__tests__/Alert.test.tsx @@ -1,4 +1,6 @@ -import { testA11y, render } from '@interlay/test-utils'; +import { render, testA11y } from '@interlay/test-utils'; +import { screen } from '@testing-library/react'; +import { userEvent } from '@testing-library/user-event'; import { Alert } from '..'; @@ -12,4 +14,14 @@ describe('Alert', () => { it('should pass a11y', async () => { await testA11y(Alert); }); + + it.skip('should emit close event', () => { + const handleClose = jest.fn(); + + render(Alert); + + userEvent.click(screen.getByRole('button', { name: /close/i })); + + expect(handleClose).toHaveBeenCalledTimes(1); + }); }); diff --git a/packages/core/theme/src/components/alert.ts b/packages/core/theme/src/components/alert.ts index d89a3232d..8cb852a59 100644 --- a/packages/core/theme/src/components/alert.ts +++ b/packages/core/theme/src/components/alert.ts @@ -2,9 +2,19 @@ import { StyledObject } from 'styled-components'; type AlertStatus = 'info' | 'success' | 'warning' | 'error'; +type AlertVariant = 'filled' | 'outlined' | 'default'; + type AlertTheme = { base: StyledObject; - status: Record>; + icon: StyledObject; + title: StyledObject; + content: StyledObject; + closeBtn: StyledObject; + closeBtnWrapper: StyledObject; + status: Record< + AlertStatus, + Record; icon: StyledObject; title: StyledObject }> + >; }; -export type { AlertTheme, AlertStatus }; +export type { AlertTheme, AlertStatus, AlertVariant }; diff --git a/packages/core/theme/src/core/colors.ts b/packages/core/theme/src/core/colors.ts index 41157ba3a..5ebaff589 100644 --- a/packages/core/theme/src/core/colors.ts +++ b/packages/core/theme/src/core/colors.ts @@ -19,9 +19,13 @@ type Palette = { GreenColors & RedColors; -type Color = keyof Palette; +type NativeColor = 'inherit' | 'unset' | string; -const color = (colors: Palette) => (color: Color | 'inherit') => (color === 'inherit' ? color : colors[color]); +type PaletteColor = keyof Palette; + +type Color = PaletteColor | NativeColor; + +const color = (colors: Palette) => (color: Color) => colors[color as PaletteColor] || color; export { color }; -export type { Color, Palette, PrimaryColors, GreyColors, BlueColors, GreenColors, RedColors }; +export type { Color, Palette, PrimaryColors, PaletteColor, GreyColors, BlueColors, GreenColors, RedColors }; diff --git a/packages/core/theme/src/index.ts b/packages/core/theme/src/index.ts index 83686f475..b343260b5 100644 --- a/packages/core/theme/src/index.ts +++ b/packages/core/theme/src/index.ts @@ -10,6 +10,7 @@ export type { AccordionVariants, ProgressBarSize, AlertStatus, + AlertVariant, TokenInputSize, TabsSize, SwitchSize, diff --git a/packages/core/theme/src/themes/bob/alert.ts b/packages/core/theme/src/themes/bob/alert.ts index 9a3bd7542..df4ca5b7d 100644 --- a/packages/core/theme/src/themes/bob/alert.ts +++ b/packages/core/theme/src/themes/bob/alert.ts @@ -1,34 +1,189 @@ -import { rounded, spacing } from '../../core'; +import { rounded, spacing, fontSize, typography } from '../../core'; import { AlertTheme } from '../../components'; import { color } from './colors'; const alert: AlertTheme = { base: { + ...typography('s'), color: color('dark'), - padding: spacing('md'), + padding: `${spacing('md')} ${spacing('xl')}`, borderRadius: rounded('md') }, + icon: { + padding: `${spacing('s')} 0`, + marginRight: spacing('md') + }, + title: { + fontSize: fontSize('md', 'rem'), + marginBottom: spacing('s') + }, + content: { + padding: `${spacing('s')} 0` + }, + closeBtn: { + width: spacing('4xl'), + height: spacing('4xl') + }, + closeBtnWrapper: { + marginRight: `-${spacing('md')}` + }, status: { + success: { + filled: { + base: { + backgroundColor: '#1CCA87', + color: color('dark') + }, + icon: { + color: color('dark') + }, + title: { + color: color('dark') + } + }, + outlined: { + base: { + backgroundColor: '#0D120D', + border: `1px solid #1CCA87`, + color: color('light') + }, + icon: { + color: '#1CCA87' + }, + title: { + color: '#1CCA87' + } + }, + default: { + base: { + backgroundColor: '#0D120D', + color: color('light') + }, + icon: { + color: '#1CCA87' + }, + title: { + color: '#1CCA87' + } + } + }, error: { - backgroundColor: color('red-300'), - border: `1px solid ${color('red-600')}`, - color: color('red-300') + filled: { + base: { + backgroundColor: '#FF7676', + color: color('dark') + }, + icon: { + color: color('dark') + }, + title: { + color: color('dark') + } + }, + outlined: { + base: { + backgroundColor: '#150B0B', + border: `1px solid #FF7676`, + color: color('light') + }, + icon: { + color: '#FF7676' + }, + title: { + color: '#FF7676' + } + }, + default: { + base: { + backgroundColor: '#150B0B', + color: color('light') + }, + icon: { + color: '#FF7676' + }, + title: { + color: '#FF7676' + } + } }, info: { - backgroundColor: color('blue-300'), - border: `1px solid ${color('blue-600')}`, - color: color('red-300') - }, - success: { - backgroundColor: color('green-200'), - border: `1px solid ${color('green-600')}`, - color: color('red-300') + filled: { + base: { + backgroundColor: '#57ADDD', + color: color('dark') + }, + icon: { + color: color('dark') + }, + title: { + color: color('dark') + } + }, + outlined: { + base: { + backgroundColor: '#161B26', + border: `1px solid #57ADDD`, + color: color('light') + }, + icon: { + color: '#57ADDD' + }, + title: { + color: '#57ADDD' + } + }, + default: { + base: { + backgroundColor: '#161B26', + color: color('light') + }, + icon: { + color: '#57ADDD' + }, + title: { + color: '#57ADDD' + } + } }, warning: { - backgroundColor: color('red-300'), - border: `1px solid ${color('red-600')}`, - color: color('red-300') + filled: { + base: { + backgroundColor: '#FFF27C', + color: color('dark') + }, + icon: { + color: color('dark') + }, + title: { + color: color('dark') + } + }, + outlined: { + base: { + backgroundColor: '#181208', + border: `1px solid #FFF27C`, + color: color('light') + }, + icon: { + color: '#FFF27C' + }, + title: { + color: '#FFF27C' + } + }, + default: { + base: { + backgroundColor: '#181208', + color: color('light') + }, + icon: { + color: '#FFF27C' + }, + title: { + color: '#FFF27C' + } + } } } }; diff --git a/packages/icons/src/CheckCircle.tsx b/packages/icons/src/CheckCircle.tsx index 21f2846ce..b733a0fbf 100644 --- a/packages/icons/src/CheckCircle.tsx +++ b/packages/icons/src/CheckCircle.tsx @@ -4,13 +4,13 @@ import { Icon, IconProps } from './core'; const CheckCircle = forwardRef((props, ref) => ( ((props, ref) => ( + + + +)); + +ExclamationCircle.displayName = 'ExclamationCircle'; + +export { ExclamationCircle }; diff --git a/packages/icons/src/Warning.tsx b/packages/icons/src/Warning.tsx index 2224fcb68..750254652 100644 --- a/packages/icons/src/Warning.tsx +++ b/packages/icons/src/Warning.tsx @@ -4,13 +4,13 @@ import { Icon, IconProps } from './core'; const Warning = forwardRef((props, ref) => (