mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-10 07:19:02 +08:00
feat: Update Sonner stories with new features and examples
- Add dismissable control to playground - Add custom toast example with JSX rendering - Add dismissable control showcase - Add custom class names example - Update component description for clarity - Import CheckIcon for custom toast example
This commit is contained in:
parent
ad9451c2a1
commit
833fa467ba
@ -1,6 +1,7 @@
|
|||||||
import { Button } from '@cherrystudio/ui'
|
import { Button } from '@cherrystudio/ui'
|
||||||
import { toast, Toaster } from '@cherrystudio/ui'
|
import { toast, Toaster } from '@cherrystudio/ui'
|
||||||
import type { Meta, StoryObj } from '@storybook/react'
|
import type { Meta, StoryObj } from '@storybook/react'
|
||||||
|
import { CheckIcon } from 'lucide-react'
|
||||||
|
|
||||||
interface PlaygroundArgs {
|
interface PlaygroundArgs {
|
||||||
type: 'info' | 'success' | 'warning' | 'error' | 'loading'
|
type: 'info' | 'success' | 'warning' | 'error' | 'loading'
|
||||||
@ -8,6 +9,7 @@ interface PlaygroundArgs {
|
|||||||
description: string
|
description: string
|
||||||
colored: boolean
|
colored: boolean
|
||||||
duration: number
|
duration: number
|
||||||
|
dismissable: boolean
|
||||||
withButton: boolean
|
withButton: boolean
|
||||||
buttonLabel: string
|
buttonLabel: string
|
||||||
}
|
}
|
||||||
@ -20,7 +22,7 @@ const meta: Meta<typeof Toaster> = {
|
|||||||
docs: {
|
docs: {
|
||||||
description: {
|
description: {
|
||||||
component:
|
component:
|
||||||
'A custom toast notification component built on sonner. Features custom icons, action buttons, links, and support for info, success, warning, error, and loading states.'
|
'A toast notification component built on Sonner with custom icons and styling. Supports info, success, warning, error, loading, and custom toast types with discriminated union types for type-safe APIs.'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -46,6 +48,7 @@ export const Playground: StoryObj<PlaygroundArgs> = {
|
|||||||
description: 'This is a description that provides more details about the notification.',
|
description: 'This is a description that provides more details about the notification.',
|
||||||
colored: false,
|
colored: false,
|
||||||
duration: 4000,
|
duration: 4000,
|
||||||
|
dismissable: true,
|
||||||
withButton: false,
|
withButton: false,
|
||||||
buttonLabel: 'Action'
|
buttonLabel: 'Action'
|
||||||
},
|
},
|
||||||
@ -65,12 +68,16 @@ export const Playground: StoryObj<PlaygroundArgs> = {
|
|||||||
},
|
},
|
||||||
colored: {
|
colored: {
|
||||||
control: 'boolean',
|
control: 'boolean',
|
||||||
description: 'Enable colored background'
|
description: 'Enable colored background with backdrop blur'
|
||||||
},
|
},
|
||||||
duration: {
|
duration: {
|
||||||
control: { type: 'number', min: 1000, max: 10000, step: 1000 },
|
control: { type: 'number', min: 1000, max: 10000, step: 1000 },
|
||||||
description: 'Duration in milliseconds (use Infinity for persistent)'
|
description: 'Duration in milliseconds (use Infinity for persistent)'
|
||||||
},
|
},
|
||||||
|
dismissable: {
|
||||||
|
control: 'boolean',
|
||||||
|
description: 'Whether the toast can be manually dismissed'
|
||||||
|
},
|
||||||
withButton: {
|
withButton: {
|
||||||
control: 'boolean',
|
control: 'boolean',
|
||||||
description: 'Show action button'
|
description: 'Show action button'
|
||||||
@ -83,26 +90,17 @@ export const Playground: StoryObj<PlaygroundArgs> = {
|
|||||||
},
|
},
|
||||||
render: (args: PlaygroundArgs) => {
|
render: (args: PlaygroundArgs) => {
|
||||||
const handleToast = () => {
|
const handleToast = () => {
|
||||||
const toastOptions: {
|
const toastOptions = {
|
||||||
description?: string
|
|
||||||
colored: boolean
|
|
||||||
duration: number
|
|
||||||
button?: {
|
|
||||||
label: string
|
|
||||||
onClick: () => void
|
|
||||||
}
|
|
||||||
promise?: Promise<void>
|
|
||||||
} = {
|
|
||||||
description: args.description || undefined,
|
description: args.description || undefined,
|
||||||
colored: args.colored,
|
colored: args.colored,
|
||||||
duration: args.duration
|
duration: args.duration,
|
||||||
}
|
dismissable: args.dismissable,
|
||||||
|
...(args.withButton && {
|
||||||
if (args.withButton) {
|
button: {
|
||||||
toastOptions.button = {
|
label: args.buttonLabel || 'Action',
|
||||||
label: args.buttonLabel || 'Action',
|
onClick: () => toast.info('Button clicked!')
|
||||||
onClick: () => toast.info('Button clicked!')
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (args.type) {
|
switch (args.type) {
|
||||||
@ -572,3 +570,96 @@ export const RealWorldExamples: Story = {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Custom Toast with JSX
|
||||||
|
export const CustomToast: Story = {
|
||||||
|
render: () => {
|
||||||
|
const showCustomToast = () => {
|
||||||
|
toast.custom({
|
||||||
|
jsx: (id) => (
|
||||||
|
<div className="flex items-center gap-4 rounded-xs bg-gradient-to-r from-purple-500 to-pink-500 p-4 text-white shadow-lg">
|
||||||
|
<CheckIcon className="size-6" />
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<div className="text-md font-medium leading-4.5">Custom Design</div>
|
||||||
|
<div className="text-xs leading-3.5">This is a fully customized toast with JSX</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => toast.dismiss(id)}
|
||||||
|
className="ml-auto rounded-3xs bg-white/20 px-2 py-1 text-xs hover:bg-white/30">
|
||||||
|
Dismiss
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
data: {
|
||||||
|
duration: 5000
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
<Button onClick={showCustomToast}>Show Custom Toast</Button>
|
||||||
|
<div className="text-sm text-muted-foreground max-w-md">
|
||||||
|
Custom toasts allow you to render any JSX content with full styling control.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dismissable Control
|
||||||
|
export const DismissableControl: Story = {
|
||||||
|
render: () => {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
<Button
|
||||||
|
onClick={() =>
|
||||||
|
toast.info('Dismissable toast', {
|
||||||
|
description: 'You can close this manually',
|
||||||
|
dismissable: true,
|
||||||
|
duration: Number.POSITIVE_INFINITY
|
||||||
|
})
|
||||||
|
}>
|
||||||
|
Dismissable (Default)
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() =>
|
||||||
|
toast.warning('Non-dismissable toast', {
|
||||||
|
description: 'This will auto-close after 3 seconds',
|
||||||
|
dismissable: false,
|
||||||
|
duration: 3000
|
||||||
|
})
|
||||||
|
}>
|
||||||
|
Non-dismissable
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// With Custom Class Names
|
||||||
|
export const WithCustomClassNames: Story = {
|
||||||
|
render: () => {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
<Button
|
||||||
|
onClick={() =>
|
||||||
|
toast.success('Custom Styled Toast', {
|
||||||
|
description: 'This toast has custom class names applied',
|
||||||
|
classNames: {
|
||||||
|
toast: 'border-2 border-green-500',
|
||||||
|
title: 'text-lg font-bold',
|
||||||
|
description: 'text-green-700 italic'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}>
|
||||||
|
Custom Class Names
|
||||||
|
</Button>
|
||||||
|
<div className="text-sm text-muted-foreground max-w-md">
|
||||||
|
You can customize specific parts of the toast using the classNames prop.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user