feat: Add close button option to toast component

- Add `closeButton` prop to BaseToastProps interface
- Update dismissable prop description for clarity
- Add close button styling to classNames
- Pass closeButton prop to external toast configuration
- Update Storybook stories to include closeButton control
- Rename "Dismissable Control" story to "Close Button Control"
This commit is contained in:
icarus 2026-01-05 21:55:18 +08:00
parent a8801e58f3
commit d89fa3cb54
No known key found for this signature in database
GPG Key ID: D4AF089AAEC25D18
3 changed files with 27 additions and 14 deletions

View File

@ -18,12 +18,14 @@ interface BaseToastProps {
colored?: boolean
/** Duration in milliseconds before auto-dismissal */
duration?: number
/** Whether the toast can be manually dismissed */
/** If 'false', it'll prevent the user from dismissing the toast. Defaults to false. */
dismissable?: boolean
/** Callback function when toast is dismissed */
onDismiss?: () => void
/** Action button or custom React node */
button?: Action | ReactNode
/** Whether to show a close button. Defaults to false */
closeButton?: boolean
/** Custom class names for toast sub-components */
classNames?: ToastClassnames
}

View File

@ -316,12 +316,14 @@ interface BaseToastProps {
colored?: boolean
/** Duration in milliseconds before auto-dismissal */
duration?: number
/** Whether the toast can be manually dismissed */
/** If 'false', it'll prevent the user from dismissing the toast. Defaults to false. */
dismissable?: boolean
/** Callback function when toast is dismissed */
onDismiss?: () => void
/** Action button or custom React node */
button?: Action | ReactNode
/** Whether to show a close button. Defaults to false */
closeButton?: boolean
/** Custom class names for toast sub-components */
classNames?: ToastClassnames
}
@ -388,7 +390,8 @@ function toast(props: ToastProps) {
props.classNames?.actionButton
),
icon: cn('size-6 min-w-6', props.description && 'self-start'),
loader: cn('!static ![--size:24px]')
loader: cn('!static ![--size:24px]'),
closeButton: cn('absolute size-5 min-w-5 top-[5px] right-1.5 [&_svg]:size-5')
}
const { classNames: externalClassNames, ...rest } = props
delete externalClassNames?.toast
@ -400,7 +403,8 @@ function toast(props: ToastProps) {
duration: rest.duration,
action: rest.button,
dismissible: rest.dismissable,
onDismiss: rest.onDismiss
onDismiss: rest.onDismiss,
closeButton: rest.closeButton
} satisfies ExternalToast
switch (props.type) {
default:

View File

@ -10,6 +10,7 @@ interface PlaygroundArgs {
colored: boolean
duration: number
dismissable: boolean
closeButton: boolean
withButton: boolean
buttonLabel: string
}
@ -49,6 +50,7 @@ export const Playground: StoryObj<PlaygroundArgs> = {
colored: false,
duration: 4000,
dismissable: true,
closeButton: false,
withButton: false,
buttonLabel: 'Action'
},
@ -76,7 +78,11 @@ export const Playground: StoryObj<PlaygroundArgs> = {
},
dismissable: {
control: 'boolean',
description: 'Whether the toast can be manually dismissed'
description: 'Whether the toast can be dismissed by user interaction (click, swipe)'
},
closeButton: {
control: 'boolean',
description: 'Whether to show a close button'
},
withButton: {
control: 'boolean',
@ -95,6 +101,7 @@ export const Playground: StoryObj<PlaygroundArgs> = {
colored: args.colored,
duration: args.duration,
dismissable: args.dismissable,
closeButton: args.closeButton,
...(args.withButton && {
button: {
label: args.buttonLabel || 'Action',
@ -608,30 +615,30 @@ export const CustomToast: Story = {
}
}
// Dismissable Control
export const DismissableControl: Story = {
// Close Button Control
export const CloseButtonControl: Story = {
render: () => {
return (
<div className="flex flex-wrap gap-2">
<Button
onClick={() =>
toast.info('Dismissable toast', {
description: 'You can close this manually',
dismissable: true,
toast.info('With close button', {
description: 'Click the X to close this toast',
closeButton: true,
duration: Number.POSITIVE_INFINITY
})
}>
Dismissable (Default)
With Close Button
</Button>
<Button
onClick={() =>
toast.warning('Non-dismissable toast', {
toast.warning('Without close button', {
description: 'This will auto-close after 3 seconds',
dismissable: false,
closeButton: false,
duration: 3000
})
}>
Non-dismissable
Without Close Button (Default)
</Button>
</div>
)