diff --git a/client/src/shared/ui/CartSnackbar.tsx b/client/src/shared/ui/CartSnackbar.tsx new file mode 100644 index 0000000..f2eb5a4 --- /dev/null +++ b/client/src/shared/ui/CartSnackbar.tsx @@ -0,0 +1,46 @@ +import { useEffect } from 'react' +import Alert from '@mui/material/Alert' +import Button from '@mui/material/Button' +import Snackbar from '@mui/material/Snackbar' +import { useNavigate } from 'react-router-dom' +import { useUnit } from 'effector-react' +import { $cartSnackOpen, cartDismissed } from '@/shared/model/cart-notifications' + +export function CartSnackbar() { + const open = useUnit($cartSnackOpen) + const navigate = useNavigate() + + useEffect(() => { + if (!open) return + const timer = setTimeout(() => cartDismissed(), 4000) + return () => clearTimeout(timer) + }, [open]) + + const handleClose = () => cartDismissed() + + const handleGoToCart = () => { + cartDismissed() + navigate('/cart') + } + + return ( + + + Перейти в корзину + + } + > + Товар добавлен в корзину + + + ) +} diff --git a/client/src/shared/ui/__tests__/CartSnackbar.test.tsx b/client/src/shared/ui/__tests__/CartSnackbar.test.tsx new file mode 100644 index 0000000..9b708d9 --- /dev/null +++ b/client/src/shared/ui/__tests__/CartSnackbar.test.tsx @@ -0,0 +1,55 @@ +import { render, screen, fireEvent, act } from '@testing-library/react' +import { describe, it, expect, vi } from 'vitest' +import { MemoryRouter } from 'react-router-dom' +import { cartAdded, cartDismissed } from '@/shared/model/cart-notifications' +import { CartSnackbar } from '@/shared/ui/CartSnackbar' + +function renderWithRouter() { + render( + + + , + ) +} + +describe('CartSnackbar', () => { + it('is hidden when store is false', () => { + cartDismissed() + renderWithRouter() + expect(screen.queryByText(/товар добавлен/i)).not.toBeInTheDocument() + }) + + it('shows snackbar when cartAdded is fired', () => { + renderWithRouter() + cartAdded() + expect(screen.getByText(/товар добавлен/i)).toBeInTheDocument() + expect(screen.getByRole('button', { name: /перейти в корзину/i })).toBeInTheDocument() + }) + + it('closes on dismiss button click', () => { + renderWithRouter() + cartAdded() + const closeBtn = screen.getByLabelText(/закрыть/i) + fireEvent.click(closeBtn) + expect(screen.queryByText(/товар добавлен/i)).not.toBeInTheDocument() + }) + + it('auto-closes after 4 seconds', () => { + vi.useFakeTimers() + renderWithRouter() + cartAdded() + act(() => { + vi.advanceTimersByTime(4000) + }) + expect(screen.queryByText(/товар добавлен/i)).not.toBeInTheDocument() + vi.useRealTimers() + }) + + it('navigates to /cart and closes on "Перейти в корзину" click', () => { + renderWithRouter() + cartAdded() + const goBtn = screen.getByRole('button', { name: /перейти в корзину/i }) + fireEvent.click(goBtn) + expect(screen.queryByText(/товар добавлен/i)).not.toBeInTheDocument() + }) +})