fix: restore CartSnackbar styling and 'Перейти в корзину' action button in NotificationStack

This commit is contained in:
Kirill
2026-05-27 23:16:03 +05:00
parent 599b66503d
commit 8133e0cf63
8 changed files with 92 additions and 13 deletions
+7 -1
View File
@@ -7,6 +7,8 @@ export interface Notification {
type: NotificationType
message: string
autoHideDuration?: number
actionLabel?: string
actionPath?: string
}
const MAX_VISIBLE = 3
@@ -16,18 +18,22 @@ export const addNotification = createEvent<{
type: NotificationType
message: string
autoHideDuration?: number
actionLabel?: string
actionPath?: string
}>()
export const dismissNotification = createEvent<string>()
export const dismissAll = createEvent()
export const $notifications = createStore<Notification[]>([])
.on(addNotification, (state, { type, message, autoHideDuration }) => {
.on(addNotification, (state, { type, message, autoHideDuration, actionLabel, actionPath }) => {
const notification: Notification = {
id: String(nextId++),
type,
message,
autoHideDuration: autoHideDuration ?? (type === 'error' ? 6000 : 4000),
actionLabel,
actionPath,
}
return [...state, notification].slice(-MAX_VISIBLE)
})
@@ -1,6 +1,7 @@
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import { allSettled, fork } from 'effector'
import { Provider } from 'effector-react'
import { MemoryRouter } from 'react-router-dom'
import { describe, it, expect, afterEach } from 'vitest'
import { addNotification, dismissAll, $notifications } from '../../model/notification'
import { NotificationStack } from './NotificationStack'
@@ -16,9 +17,11 @@ describe('NotificationStack', () => {
function renderWithScope(scope: ReturnType<typeof fork>) {
return render(
<Provider value={scope}>
<NotificationStack />
</Provider>,
<MemoryRouter>
<Provider value={scope}>
<NotificationStack />
</Provider>
</MemoryRouter>,
)
}
@@ -1,11 +1,13 @@
import CloseIcon from '@mui/icons-material/Close'
import { Snackbar, Alert, Stack, IconButton } from '@mui/material'
import { Snackbar, Alert, Stack, IconButton, Button } from '@mui/material'
import { useUnit } from 'effector-react'
import { useNavigate } from 'react-router-dom'
import { $notifications, dismissNotification as dismissNotificationEvent } from '../../model/notification'
export function NotificationStack() {
const notifications = useUnit($notifications)
const dismissNotification = useUnit(dismissNotificationEvent)
const navigate = useNavigate()
if (notifications.length === 0) return null
@@ -29,14 +31,72 @@ export function NotificationStack() {
autoHideDuration={n.autoHideDuration}
onClose={() => dismissNotification(n.id)}
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
sx={{
'& .MuiSnackbarContent-root': {
borderRadius: 12,
border: '1px solid',
borderColor: 'rgba(0,0,0,0.06)',
bgcolor: 'background.paper',
boxShadow: '0 4px 20px rgba(0,0,0,0.08)',
},
}}
>
<Alert
severity={n.type}
variant="filled"
variant="standard"
onClose={() => dismissNotification(n.id)}
sx={{
bgcolor: 'transparent',
border: 'none',
boxShadow: 'none',
p: 1.5,
alignItems: 'center',
'& .MuiAlert-icon': {
padding: 0,
mr: 1.5,
display: 'flex',
alignItems: 'center',
},
'& .MuiAlert-message': {
padding: 0,
fontSize: '0.875rem',
fontWeight: 600,
},
'& .MuiAlert-action': {
padding: 0,
mr: 0,
ml: 1,
},
}}
action={
<IconButton size="small" color="inherit" onClick={() => dismissNotification(n.id)}>
<CloseIcon fontSize="small" />
</IconButton>
<>
{n.actionLabel && n.actionPath && (
<Button
size="small"
variant="text"
onClick={() => {
dismissNotification(n.id)
navigate(n.actionPath!)
}}
sx={{
fontWeight: 600,
fontSize: '0.8125rem',
px: 1.5,
py: 0.5,
borderRadius: 8,
color: 'success.main',
'&:hover': {
bgcolor: 'action.hover',
},
}}
>
{n.actionLabel}
</Button>
)}
<IconButton size="small" color="inherit" onClick={() => dismissNotification(n.id)}>
<CloseIcon fontSize="small" />
</IconButton>
</>
}
>
{n.message}