Prevent toast disappearing when hovered - #227
Conversation
There was a problem hiding this comment.
Pull request overview
Addresses issue #226 by preventing the command-line/toast overlay from disappearing while the user is hovering it, shifting toast dismissal responsibility to the Toast component so it can pause/resume timers based on hover state.
Changes:
- Moved toast auto-dismiss logic out of
DesignWeb.showToastinto theToastcomponent (centralized timer management). - Added hover handling to pause dismissal on
mouseenterand re-schedule dismissal onmouseleave. - Enabled pointer interaction on the toast element so hover events can be received.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/js/DesignWeb.js |
Removes inline setTimeout dismissal and exposes removeToast(id); passes it into Toast. |
src/js/components/toast.js |
Implements per-toast timers, hover pause/resume behavior, and cleanup on unmount/update. |
src/css/Toast.css |
Allows toast elements to receive pointer events (required for hover). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const DISMISS_DELAY = 3000; | ||
| const HOVER_DISMISS_DELAY = 1000; | ||
|
|
||
| export default class Toast extends Component { | ||
| constructor(props) { | ||
| super(props); | ||
| this.timers = new Map(); | ||
| } | ||
|
|
||
| componentDidMount() { | ||
| this.startTimersForNewToasts([], this.props.toasts); | ||
| } | ||
|
|
||
| componentDidUpdate(prevProps) { | ||
| this.startTimersForNewToasts(prevProps.toasts, this.props.toasts); | ||
| // Clear timers for toasts that were removed externally | ||
| prevProps.toasts.forEach((toast) => { | ||
| if (!this.props.toasts.find((t) => t.id === toast.id)) { | ||
| clearTimeout(this.timers.get(toast.id)); | ||
| this.timers.delete(toast.id); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| componentWillUnmount() { | ||
| this.timers.forEach((timerId) => clearTimeout(timerId)); | ||
| this.timers.clear(); | ||
| } | ||
|
|
||
| startTimersForNewToasts(prevToasts, nextToasts) { | ||
| nextToasts.forEach((toast) => { | ||
| if (!this.timers.has(toast.id) && !prevToasts.find((t) => t.id === toast.id)) { | ||
| this.scheduleDismiss(toast.id, DISMISS_DELAY); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| scheduleDismiss(id, delay) { | ||
| const timerId = setTimeout(() => { | ||
| this.timers.delete(id); | ||
| this.props.removeToast(id); | ||
| }, delay); | ||
| this.timers.set(id, timerId); | ||
| } | ||
|
|
||
| handleMouseEnter(id) { | ||
| clearTimeout(this.timers.get(id)); | ||
| this.timers.delete(id); | ||
| } | ||
|
|
||
| handleMouseLeave(id) { | ||
| this.scheduleDismiss(id, HOVER_DISMISS_DELAY); | ||
| } |
There was a problem hiding this comment.
New hover-pausing / rescheduled dismissal behavior is implemented here, but there’s no automated test covering it. Consider adding a React Testing Library/Jest test that (1) verifies a toast does not dismiss while hovered when timers are advanced and (2) dismisses after mouse leave once timers are advanced again, to prevent regressions for issue #226.
Fixes #226