diff --git a/src/components/experimental/Snackbar/Snackbar.spec.tsx b/src/components/experimental/Snackbar/Snackbar.spec.tsx new file mode 100644 index 00000000..f88be8df --- /dev/null +++ b/src/components/experimental/Snackbar/Snackbar.spec.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { fireEvent, render, screen } from '@testing-library/react'; +import { Snackbar } from './Snackbar'; + +test('renders the Snackbar component with children', () => { + render( + + Test Snackbar + + ); + expect(screen.getByText('Test Snackbar')).toBeInTheDocument(); +}); + +test('renders the dismiss button when hasDismissButton is true and calls onDismiss when clicked', () => { + const onDismiss = jest.fn(); + render( + + Test Snackbar + + ); + const dismissButton = screen.getByTestId('snackbar-close-icon'); + fireEvent.click(dismissButton); + expect(onDismiss).toHaveBeenCalledTimes(1); +}); diff --git a/src/components/experimental/Snackbar/Snackbar.tsx b/src/components/experimental/Snackbar/Snackbar.tsx new file mode 100644 index 00000000..26804cf1 --- /dev/null +++ b/src/components/experimental/Snackbar/Snackbar.tsx @@ -0,0 +1,59 @@ +import React, { ReactNode, type ReactElement } from 'react'; +import styled from 'styled-components'; +import { SpaceProps, LayoutProps, PositionProps, FlexboxProps } from 'styled-system'; + +import { get } from '../../../utils/experimental/themeGet'; +import { getSemanticValue } from '../../../essentials/experimental'; +import { textStyles } from '../Text/Text'; +import { XCrossIcon } from '../../../icons'; +import { IconButton } from '../IconButton/IconButton'; + +const Container = styled.div` + position: relative; + justify-content: space-between; + + border: none; + outline: none; + border-radius: ${get('radii.4')}; + padding: ${get('space.3')} ${get('space.4')}; + color: ${getSemanticValue('inverse-on-surface')}; + background-color: ${getSemanticValue('inverse-surface')}; + + display: inline-flex; + align-items: center; + gap: ${get('space.1')}; + + ${textStyles.variants.body2} +`; + +const DismissButton = styled(IconButton)` + height: unset; + width: unset; + padding: 0; +`; + +interface SnackbarProps extends SpaceProps, LayoutProps, PositionProps, FlexboxProps { + children: ReactNode; + hasDismissButton?: boolean; + onDismiss?: () => void; +} + +const Snackbar = ({ + children, + hasDismissButton = false, + onDismiss = null, + ...restProps +}: SnackbarProps): ReactElement => ( + + {children} + {hasDismissButton && ( + } + onPress={onDismiss} + /> + )} + +); + +export { Snackbar, SnackbarProps }; diff --git a/src/components/experimental/Snackbar/docs/Snackbar.stories.tsx b/src/components/experimental/Snackbar/docs/Snackbar.stories.tsx new file mode 100644 index 00000000..3df6ccab --- /dev/null +++ b/src/components/experimental/Snackbar/docs/Snackbar.stories.tsx @@ -0,0 +1,31 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { action } from '@storybook/addon-actions'; + +import { Snackbar } from '../Snackbar'; + +const meta: Meta = { + title: 'Experimental/Components/Snackbar', + component: Snackbar, + parameters: { + layout: 'centered' + }, + argTypes: {}, + args: { + children: 'Booking successfully cancelled', + hasDismissButton: false, + onDismiss: null + } +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = {}; + +export const WithDismissIcon: Story = { + args: { + hasDismissButton: true, + onDismiss: action('Remove snackbar') + } +}; diff --git a/src/components/experimental/index.ts b/src/components/experimental/index.ts index 7d1c04f0..92ffbef8 100644 --- a/src/components/experimental/index.ts +++ b/src/components/experimental/index.ts @@ -11,6 +11,7 @@ export { Label } from './Label/Label'; export { ListBox, ListBoxItem } from './ListBox/ListBox'; export { Popover } from './Popover/Popover'; export { Select } from './Select/Select'; +export { Snackbar } from './Snackbar/Snackbar'; export { Table, Row, Cell, Skeleton, Column, TableBody, TableHeader } from './Table/Table'; export { Text } from './Text/Text'; export { TextField } from './TextField/TextField';