Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,12 @@ export function EditorBubbleMenu({
<>
{onAddToChat && (
<>
<ToolbarButton icon={Blimp} label='Add to Chat' onClick={onAddToChat} />
<ToolbarButton
icon={Blimp}
iconSize='compact'
label='Add to Chat'
onClick={onAddToChat}
/>
<ToolbarDivider />
</>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/** @vitest-environment jsdom */
import { act, type ReactNode } from 'react'
import { Tooltip } from '@sim/emcn'
import { Blimp, Bold } from '@sim/emcn/icons'
import { createRoot, type Root } from 'react-dom/client'
Comment thread
waleedlatif1 marked this conversation as resolved.
import { afterEach, describe, expect, it, vi } from 'vitest'
import { ToolbarButton } from '@/app/workspace/[workspaceId]/files/components/file-viewer/rich-markdown-editor/menus/toolbar-button'

describe('ToolbarButton', () => {
const rendered: Array<{ host: HTMLDivElement; root: Root }> = []

afterEach(() => {
for (const { host, root } of rendered) {
act(() => root.unmount())
host.remove()
}
rendered.length = 0
})

function renderButton(button: ReactNode): HTMLDivElement {
const host = document.createElement('div')
document.body.append(host)
const root = createRoot(host)
act(() => root.render(<Tooltip.Provider>{button}</Tooltip.Provider>))
rendered.push({ host, root })
return host
}

it('uses the canonical active button treatment for selected formatting', () => {
const host = renderButton(<ToolbarButton icon={Bold} label='Bold' isActive onClick={vi.fn()} />)
const button = host.querySelector('button[aria-label="Bold"]')

expect(button?.className).toContain('bg-[var(--surface-5)]')
expect(button?.className).toContain('border-[var(--border-1)]')
expect(button?.className).toContain('text-[var(--text-primary)]')
})

it('supports a compact glyph without reducing the button hit target', () => {
const host = renderButton(
<ToolbarButton icon={Blimp} iconSize='compact' label='Add to Chat' onClick={vi.fn()} />
)

const button = host.querySelector('button[aria-label="Add to Chat"]')
expect(button?.className).toContain('size-[28px]')
expect(button?.querySelector('svg')?.className.baseVal).toContain('size-[12px]')
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { Button, cn, Tooltip } from '@sim/emcn'
interface ToolbarButtonProps {
/** Any SVG icon component, e.g. from `@sim/emcn/icons`. */
icon: ComponentType<SVGProps<SVGSVGElement>>
/** Reduces optically dense filled glyphs while keeping the standard button hit target. */
iconSize?: 'default' | 'compact'
label: string
shortcut?: string
isActive?: boolean
Expand All @@ -14,6 +16,7 @@ interface ToolbarButtonProps {
/** A single icon button for the editor's floating toolbars (bubble menu, link hover card). */
export function ToolbarButton({
icon: Icon,
iconSize = 'default',
label,
shortcut,
isActive,
Expand All @@ -25,21 +28,19 @@ export function ToolbarButton({
<Tooltip.Trigger asChild>
<Button
type='button'
variant='ghost'
variant={isActive ? 'active' : 'ghost'}
size='icon'
aria-label={label}
aria-pressed={isActive}
disabled={disabled}
onMouseDown={(event) => event.preventDefault()}
onClick={onClick}
className={cn(
'size-[28px] focus-visible:bg-[var(--surface-hover)] [&_svg]:size-[14px]',
isActive
? 'bg-[var(--surface-active)] text-[var(--text-body)]'
: 'hover-hover:bg-[var(--surface-hover)]'
'size-[28px] focus-visible:bg-[var(--surface-hover)]',
!isActive && 'hover-hover:bg-[var(--surface-hover)]'
)}
>
<Icon />
<Icon className={iconSize === 'compact' ? 'size-[12px]' : 'size-[14px]'} />
</Button>
</Tooltip.Trigger>
<Tooltip.Content>
Expand Down
Loading