feat(web): toast anchoring and model-selection rejection banner
The toast sits 120px from the viewport top and centers over its anchor — the composer card, so it centers on the chat column rather than the window; a rejected model selection (e.g. picking a text-only model while the session holds images) announces through the same banner while the in-menu strip with Retry stays the catalog-load surface. The attachment rail consumes every wheel tick with a vertical component: a diagonal pan keeps its horizontal intent and nothing scrolls the conversation behind the composer.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { cleanup, render } from '@testing-library/react'
|
||||
import { cleanup, fireEvent, render } from '@testing-library/react'
|
||||
import { Toast } from '../src/Toast.tsx'
|
||||
|
||||
afterEach(cleanup)
|
||||
@@ -24,6 +24,23 @@ describe('Toast', () => {
|
||||
}
|
||||
})
|
||||
|
||||
it('centers over its anchor and re-measures on window resize', () => {
|
||||
vi.useFakeTimers()
|
||||
try {
|
||||
const anchor = document.createElement('div')
|
||||
document.body.appendChild(anchor)
|
||||
anchor.getBoundingClientRect = () => ({ left: 100, width: 400 }) as DOMRect
|
||||
const view = render(<Toast text="anchored" anchor={anchor} onDone={vi.fn()} />)
|
||||
expect(view.getByRole('alert').style.left).toBe('300px')
|
||||
anchor.getBoundingClientRect = () => ({ left: 200, width: 400 }) as DOMRect
|
||||
fireEvent(window, new Event('resize'))
|
||||
expect(view.getByRole('alert').style.left).toBe('400px')
|
||||
anchor.remove()
|
||||
} finally {
|
||||
vi.useRealTimers()
|
||||
}
|
||||
})
|
||||
|
||||
it('renders without an icon and cancels its timer on unmount', () => {
|
||||
vi.useFakeTimers()
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user