11import type { Meta , StoryObj } from '@storybook/react-vite' ;
22import { useState } from 'react' ;
33import { NotificationPopup } from './NotificationPopup' ;
4+ import { NotificationToastContainer } from './NotificationToastContainer' ;
45import { Button } from '../Button' ;
56
67const meta : Meta < typeof NotificationPopup > = {
@@ -17,36 +18,104 @@ const meta: Meta<typeof NotificationPopup> = {
1718export default meta ;
1819type Story = StoryObj < typeof NotificationPopup > ;
1920
21+ /** Interactive toast inside a NotificationToastContainer. */
2022export const Interactive : Story = {
2123 render : ( ) => {
2224 const [ visible , setVisible ] = useState ( false ) ;
2325 return (
2426 < >
2527 < Button onClick = { ( ) => setVisible ( true ) } > Show Notification</ Button >
26- < NotificationPopup
27- variant = "success"
28- title = "Changes saved"
29- visible = { visible }
30- onDismiss = { ( ) => setVisible ( false ) }
31- >
32- Your changes have been saved successfully.
33- </ NotificationPopup >
28+ < NotificationToastContainer >
29+ < NotificationPopup
30+ variant = "success"
31+ title = "Changes saved"
32+ visible = { visible }
33+ onDismiss = { ( ) => setVisible ( false ) }
34+ >
35+ Your changes have been saved successfully.
36+ </ NotificationPopup >
37+ </ NotificationToastContainer >
3438 </ >
3539 ) ;
3640 } ,
3741} ;
3842
43+ /** Persistent notification — `autoDismiss={false}` keeps it until the user dismisses. */
44+ export const Persistent : Story = {
45+ render : ( ) => {
46+ const [ visible , setVisible ] = useState ( true ) ;
47+ return (
48+ < >
49+ < Button onClick = { ( ) => setVisible ( true ) } > Show Notification</ Button >
50+ < NotificationToastContainer >
51+ < NotificationPopup
52+ variant = "warning"
53+ title = "Session expiring"
54+ visible = { visible }
55+ autoDismiss = { false }
56+ onDismiss = { ( ) => setVisible ( false ) }
57+ >
58+ Your session will expire in 5 minutes.
59+ </ NotificationPopup >
60+ </ NotificationToastContainer >
61+ </ >
62+ ) ;
63+ } ,
64+ } ;
65+
66+ /** Multiple stacked toasts — each exits with a smooth animation before being removed from the list. */
67+ export const Stacked : Story = {
68+ render : ( ) => {
69+ const [ toasts , setToasts ] = useState ( [
70+ { id : 1 , variant : 'success' as const , title : 'Saved' , body : 'Draft saved successfully.' , visible : true } ,
71+ { id : 2 , variant : 'error' as const , title : 'Upload failed' , body : 'File exceeds 10 MB limit.' , visible : true } ,
72+ { id : 3 , variant : 'info' as const , title : 'Update available' , body : 'A new version is ready.' , visible : true } ,
73+ ] ) ;
74+
75+ // Step 1: trigger exit animation by setting visible=false
76+ const dismiss = ( id : number ) =>
77+ setToasts ( ( prev ) => prev . map ( ( t ) => ( t . id === id ? { ...t , visible : false } : t ) ) ) ;
78+
79+ // Step 2: remove from list only after the animation finishes (via onExited)
80+ const remove = ( id : number ) =>
81+ setToasts ( ( prev ) => prev . filter ( ( t ) => t . id !== id ) ) ;
82+
83+ return (
84+ < NotificationToastContainer >
85+ { toasts . map ( ( t , i ) => (
86+ < NotificationPopup
87+ key = { t . id }
88+ variant = { t . variant }
89+ title = { t . title }
90+ visible = { t . visible }
91+ autoDismiss = { false }
92+ onDismiss = { ( ) => dismiss ( t . id ) }
93+ onExited = { ( ) => remove ( t . id ) }
94+ style = { {
95+ animationDelay : `${ i * 80 } ms` ,
96+ animationFillMode : 'backwards' ,
97+ } }
98+ >
99+ { t . body }
100+ </ NotificationPopup >
101+ ) ) }
102+ </ NotificationToastContainer >
103+ ) ;
104+ } ,
105+ } ;
106+
107+ /** All four variants displayed inline (no container needed for static display). */
39108export const AllVariants : Story = {
40109 render : ( ) => (
41- < div style = { { display : 'flex' , flexDirection : 'column' , gap : '1rem' , position : 'relative' } } >
110+ < div style = { { display : 'flex' , flexDirection : 'column' , gap : '1rem' } } >
42111 { ( [ 'info' , 'success' , 'warning' , 'error' ] as const ) . map ( ( v ) => (
43112 < NotificationPopup
44113 key = { v }
45114 variant = { v }
46115 title = { `${ v . charAt ( 0 ) . toUpperCase ( ) + v . slice ( 1 ) } notification` }
47116 visible
117+ autoDismiss = { false }
48118 onDismiss = { ( ) => { } }
49- style = { { position : 'static' } }
50119 >
51120 This is a { v } notification message.
52121 </ NotificationPopup >
0 commit comments