Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions docs/content/docs/components/toast.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,20 @@ An action that is safe to ignore to ensure users are not expected to complete ta

When obtaining a user response is necessary, portal an ["AlertDialog"](/docs/components/alert-dialog) styled as a toast into the viewport instead.

By default, clicking `ToastAction` dismisses the toast. Set `closeOnClick` to `false` when the action should run while keeping the toast visible.

```vue
<template>
<ToastAction
alt-text="Retry sending the message"
:close-on-click="false"
@click="retry"
>
Retry
</ToastAction>
</template>
```
Comment on lines +142 to +154
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟑 Minor | ⚑ Quick win

Update keyboard-interaction docs to reflect conditional close behavior.

Line 142 correctly introduces closeOnClick, but the keyboard table still says ToastAction always closes on Space/Enter. Please qualify that behavior as conditional (closeOnClick === true) to avoid conflicting guidance.

πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/content/docs/components/toast.md` around lines 142 - 154, The
keyboard-interaction docs incorrectly state that ToastAction always closes on
Space/Enter; update the keyboard table and any related text to qualify that
closing on Space/Enter is conditional on the prop closeOnClick being true.
Locate mentions of ToastAction and the keyboard table in this doc (references:
ToastAction, closeOnClick) and change the wording to something like "closes on
Space/Enter when closeOnClick === true" or equivalent concise phrasing so the
docs consistently reflect the conditional behavior.


<!-- @include: @/meta/ToastAction.md -->

### Close
Expand Down
12 changes: 10 additions & 2 deletions docs/content/meta/ToastAction.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,20 @@
'description': '<p>The element or component this component should render as. Can be overwritten by <code>asChild</code>.</p>\n',
'type': 'AsTag | Component',
'required': false,
'default': '\'div\''
'default': '\'button\''
},
{
'name': 'asChild',
'description': '<p>Change the default rendered element for the one passed as a child, merging their props and behavior.</p>\n<p>Read our <a href=\'https://www.reka-ui.com/docs/guides/composition\'>Composition</a> guide for more details.</p>\n',
'type': 'boolean',
'required': false
},
{
'name': 'closeOnClick',
'description': '<p>Whether the action should close the toast when clicked.</p>\n',
'type': 'boolean',
'required': false,
'default': 'true'
}
]" />
</llm-exclude>
Expand All @@ -31,7 +38,8 @@
| Name | Description | Type | Required | Default |
| --- | --- | --- | --- | --- |
| `altText` | A short description for an alternate way to carry out the action. For screen reader users who will not be able to navigate to the button easily/quickly. | `string` | Yes | - |
| `as` | The element or component this component should render as. Can be overwritten by asChild. | `AsTag \| Component` | No | `"div"` |
| `as` | The element or component this component should render as. Can be overwritten by asChild. | `AsTag \| Component` | No | `"button"` |
| `asChild` | Change the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | `boolean` | No | - |
| `closeOnClick` | Whether the action should close the toast when clicked. | `boolean` | No | `true` |

</llm-only>
50 changes: 49 additions & 1 deletion packages/core/src/Toast/Toast.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,48 @@ import { findByText, fireEvent } from '@testing-library/vue'
import { mount } from '@vue/test-utils'
import { beforeEach, describe, expect, it } from 'vitest'
import { axe } from 'vitest-axe'
import { nextTick } from 'vue'
import { defineComponent, h, nextTick, ref } from 'vue'
import { ToastAction, ToastDescription, ToastProvider, ToastRoot, ToastViewport } from '.'
import Toast from './story/_Toast.vue'

const CLOSE_TEXT = 'Close'

const ToastWithPersistentAction = defineComponent({
setup() {
const open = ref(true)
const actionClicks = ref(0)

return {
open,
actionClicks,
}
},
render() {
return h(ToastProvider, null, {
default: () => [
h(ToastRoot, {
'open': this.open,
'onUpdate:open': (value: boolean) => {
this.open = value
},
}, {
default: () => [
h(ToastDescription, null, { default: () => 'Action available' }),
h(ToastAction, {
altText: 'Keep toast open after action',
closeOnClick: false,
onClick: () => {
this.actionClicks += 1
},
}, { default: () => 'Undo' }),
],
}),
h(ToastViewport),
],
})
},
})

describe('given a default Toast', () => {
let wrapper: VueWrapper<InstanceType<typeof Toast>>
let trigger: DOMWrapper<HTMLElement>
Expand Down Expand Up @@ -66,6 +103,17 @@ describe('given a default Toast', () => {
expect(text).toContain('Scheduled: Catch up')
})

it('should keep the toast open when action closeOnClick is false', async () => {
const wrapper = mount(ToastWithPersistentAction, { attachTo: document.body })

const action = await findByText(document.body, 'Undo')
await fireEvent.click(action)

expect(wrapper.vm.actionClicks).toBe(1)
expect(wrapper.vm.open).toBe(true)
expect(document.body.innerHTML).toContain('Action available')
})

describe('after clicking the trigger', () => {
beforeEach(async () => {
fireEvent.click(trigger.element)
Expand Down
23 changes: 22 additions & 1 deletion packages/core/src/Toast/ToastAction.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,25 @@ export interface ToastActionProps extends ToastCloseProps {
* @example <ToastAction altText="Undo (Alt+U)">Undo</ToastAction>
*/
altText: string
/**
* Whether the action should close the toast when clicked.
*
* @defaultValue true
*/
closeOnClick?: boolean
}
</script>

<script setup lang="ts">
import { Primitive } from '@/Primitive'
import { useForwardExpose } from '@/shared'
import ToastAnnounceExclude from './ToastAnnounceExclude.vue'
import ToastClose from './ToastClose.vue'

const props = defineProps<ToastActionProps>()
const props = withDefaults(defineProps<ToastActionProps>(), {
as: 'button',
closeOnClick: true,
})

if (!props.altText)
throw new Error('Missing prop `altText` expected on `ToastAction`')
Expand All @@ -32,11 +42,22 @@ const { forwardRef } = useForwardExpose()
as-child
>
<ToastClose
v-if="closeOnClick"
:ref="forwardRef"
:as="as"
:as-child="asChild"
>
<slot />
</ToastClose>

<Primitive
v-else
:ref="forwardRef"
:as="as"
:as-child="asChild"
:type="as === 'button' ? 'button' : undefined"
>
<slot />
</Primitive>
</ToastAnnounceExclude>
</template>
Loading