Skip to content

Commit 6fa70aa

Browse files
committed
fix(mothership): let the blocked Edit button's tooltip open
A disabled button gets no hover or focus, so the tooltip explaining why the Edit button was blocked never opened. It is now aria-disabled, ignores clicks while blocked, and keeps its label for screen readers.
1 parent 48f268a commit 6fa70aa

2 files changed

Lines changed: 89 additions & 14 deletions

File tree

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/**
2+
* @vitest-environment jsdom
3+
*/
4+
import { act } from 'react'
5+
import { createRoot } from 'react-dom/client'
6+
import { afterEach, describe, expect, it, vi } from 'vitest'
7+
8+
/** The queued message renders its content through the real block registry. */
9+
vi.unmock('@/blocks/registry')
10+
11+
import { QueuedMessages } from '@/app/workspace/[workspaceId]/home/components/queued-messages/queued-messages'
12+
13+
const mounted: Array<() => void> = []
14+
15+
/** jsdom has no ResizeObserver; the queue measures its width with one. */
16+
class ResizeObserverMock {
17+
observe = vi.fn()
18+
unobserve = vi.fn()
19+
disconnect = vi.fn()
20+
}
21+
22+
function renderQueue(admissionUnknown: boolean) {
23+
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true)
24+
vi.stubGlobal('ResizeObserver', ResizeObserverMock)
25+
const container = document.createElement('div')
26+
document.body.appendChild(container)
27+
const root = createRoot(container)
28+
const onEdit = vi.fn()
29+
act(() => {
30+
root.render(
31+
<QueuedMessages
32+
messageQueue={[{ id: 'held-first', content: 'inspect the workspace', admissionUnknown }]}
33+
editingQueuedId={null}
34+
dispatchingHeadId={null}
35+
onRemove={() => {}}
36+
onSendNow={async () => {}}
37+
onEdit={onEdit}
38+
onCancelEdit={() => {}}
39+
/>
40+
)
41+
})
42+
mounted.push(() => {
43+
act(() => root.unmount())
44+
container.remove()
45+
})
46+
const editButton = container.querySelector<HTMLButtonElement>(
47+
'button[aria-label="Edit queued message"], button[aria-label^="May already be sent"]'
48+
)
49+
if (!editButton) throw new Error('Edit button not rendered')
50+
return { editButton, onEdit }
51+
}
52+
53+
describe('QueuedMessages edit button', () => {
54+
afterEach(() => {
55+
for (const unmount of mounted.splice(0)) unmount()
56+
})
57+
58+
it('keeps a message that may already be sent from being edited, and says why', () => {
59+
const { editButton, onEdit } = renderQueue(true)
60+
61+
/** Still focusable and hoverable, so its tooltip can explain why. */
62+
expect(editButton.disabled).toBe(false)
63+
expect(editButton.getAttribute('aria-disabled')).toBe('true')
64+
expect(editButton.getAttribute('aria-label')).toBe(
65+
'May already be sent; editing could send a second message'
66+
)
67+
act(() => editButton.click())
68+
expect(onEdit).not.toHaveBeenCalled()
69+
})
70+
71+
it('edits an ordinary queued message', () => {
72+
const { editButton, onEdit } = renderQueue(false)
73+
74+
act(() => editButton.click())
75+
expect(onEdit).toHaveBeenCalledWith('held-first')
76+
})
77+
})

‎apps/sim/app/workspace/[workspaceId]/home/components/queued-messages/queued-messages.tsx‎

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ export function QueuedMessages({
7575
const isEditing = msg.id === editingQueuedId
7676
const isDispatching = msg.id === dispatchingHeadId
7777
const editBlocked = isDispatching || msg.admissionUnknown === true
78+
const editLabel = isDispatching
79+
? 'Sending now'
80+
: msg.admissionUnknown
81+
? 'May already be sent; editing could send a second message'
82+
: 'Edit queued message'
7883
return (
7984
<div
8085
key={msg.id}
@@ -141,31 +146,24 @@ export function QueuedMessages({
141146
<>
142147
<Tooltip.Root>
143148
<Tooltip.Trigger asChild>
149+
{/* aria-disabled, not disabled: a disabled button gets no hover or
150+
focus, so the tooltip saying why could never open. */}
144151
<button
145-
aria-label={
146-
isDispatching
147-
? 'Sending now'
148-
: msg.admissionUnknown
149-
? 'May already be sent; editing could send a second message'
150-
: 'Edit queued message'
151-
}
152+
aria-label={editLabel}
153+
aria-disabled={editBlocked}
152154
type='button'
153-
disabled={editBlocked}
154155
onClick={(e) => {
155156
e.stopPropagation()
157+
if (editBlocked) return
156158
onEdit(msg.id)
157159
}}
158-
className='rounded-md p-[5px] text-[var(--text-icon)] transition-colors hover-hover:bg-[var(--surface-active)] hover-hover:text-[var(--text-primary)] disabled:cursor-not-allowed disabled:opacity-40 disabled:hover-hover:bg-transparent disabled:hover-hover:text-[var(--text-icon)]'
160+
className='rounded-md p-[5px] text-[var(--text-icon)] transition-colors hover-hover:bg-[var(--surface-active)] hover-hover:text-[var(--text-primary)] aria-disabled:cursor-not-allowed aria-disabled:opacity-40 aria-disabled:hover-hover:bg-transparent aria-disabled:hover-hover:text-[var(--text-icon)]'
159161
>
160162
<Pencil className='size-[13px]' />
161163
</button>
162164
</Tooltip.Trigger>
163165
<Tooltip.Content side='top' sideOffset={4}>
164-
{isDispatching
165-
? 'Sending now'
166-
: msg.admissionUnknown
167-
? 'May already be sent; editing could send a second message'
168-
: 'Edit queued message'}
166+
{editLabel}
169167
</Tooltip.Content>
170168
</Tooltip.Root>
171169

0 commit comments

Comments
 (0)