diff --git a/web-app/src/components/ui/__tests__/button.test.tsx b/web-app/src/components/ui/__tests__/button.test.tsx index 187bb4403d..219e7d8a18 100644 --- a/web-app/src/components/ui/__tests__/button.test.tsx +++ b/web-app/src/components/ui/__tests__/button.test.tsx @@ -1,61 +1,71 @@ +import React from 'react' import { render, screen, fireEvent } from '@testing-library/react' import { describe, it, expect, vi } from 'vitest' import userEvent from '@testing-library/user-event' +import '@testing-library/jest-dom' import { Button } from '../button' describe('Button', () => { it('renders button with children', () => { render() - + expect(screen.getByRole('button')).toBeInTheDocument() expect(screen.getByText('Click me')).toBeInTheDocument() }) it('applies default variant classes', () => { render() - + const button = screen.getByRole('button') - expect(button).toHaveClass('bg-primary', 'text-primary-fg', 'hover:bg-primary/90') + expect(button).toHaveClass( + 'bg-primary', + 'text-primary-fg', + 'hover:bg-primary/90' + ) }) it('applies destructive variant classes', () => { render() - + const button = screen.getByRole('button') - expect(button).toHaveClass('bg-destructive', 'text-destructive-fg', 'hover:bg-destructive/90') + expect(button).toHaveClass( + 'bg-destructive', + 'text-destructive-fg', + 'hover:bg-destructive/90' + ) }) it('applies link variant classes', () => { render() - + const button = screen.getByRole('button') expect(button).toHaveClass('underline-offset-4', 'hover:no-underline') }) it('applies default size classes', () => { render() - + const button = screen.getByRole('button') expect(button).toHaveClass('h-7', 'px-3', 'py-2') }) it('applies small size classes', () => { render() - + const button = screen.getByRole('button') expect(button).toHaveClass('h-6', 'px-2') }) it('applies large size classes', () => { render() - + const button = screen.getByRole('button') expect(button).toHaveClass('h-9', 'rounded-md', 'px-4') }) it('applies icon size classes', () => { render() - + const button = screen.getByRole('button') expect(button).toHaveClass('size-8') }) @@ -63,51 +73,62 @@ describe('Button', () => { it('handles click events', async () => { const handleClick = vi.fn() const user = userEvent.setup() - + render() - + await user.click(screen.getByRole('button')) - + expect(handleClick).toHaveBeenCalledTimes(1) }) it('can be disabled', () => { render() - + const button = screen.getByRole('button') expect(button).toBeDisabled() - expect(button).toHaveClass('disabled:pointer-events-none', 'disabled:opacity-50') + expect(button).toHaveClass( + 'disabled:pointer-events-none', + 'disabled:opacity-50' + ) }) it('does not trigger click when disabled', async () => { const handleClick = vi.fn() const user = userEvent.setup() - - render() - + + render( + + ) + await user.click(screen.getByRole('button')) - + expect(handleClick).not.toHaveBeenCalled() }) it('forwards ref correctly', () => { const ref = vi.fn() - + render() - + expect(ref).toHaveBeenCalledWith(expect.any(HTMLButtonElement)) }) it('accepts custom className', () => { render() - + const button = screen.getByRole('button') expect(button).toHaveClass('custom-class') }) it('accepts custom props', () => { - render() - + render( + + ) + const button = screen.getByTestId('custom-button') expect(button).toHaveAttribute('type', 'submit') }) @@ -118,15 +139,19 @@ describe('Button', () => { Link Button ) - + const link = screen.getByRole('link') expect(link).toHaveAttribute('href', '/test') expect(link).toHaveClass('bg-primary', 'text-primary-fg') // Should inherit button classes }) it('combines variant and size classes correctly', () => { - render() - + render( + + ) + const button = screen.getByRole('button') expect(button).toHaveClass('bg-destructive', 'text-destructive-fg') // destructive variant expect(button).toHaveClass('h-9', 'rounded-md', 'px-4') // large size @@ -134,35 +159,45 @@ describe('Button', () => { it('handles keyboard events', () => { const handleKeyDown = vi.fn() - + render() - + const button = screen.getByRole('button') fireEvent.keyDown(button, { key: 'Enter' }) - - expect(handleKeyDown).toHaveBeenCalledWith(expect.objectContaining({ - key: 'Enter' - })) + + expect(handleKeyDown).toHaveBeenCalledWith( + expect.objectContaining({ + key: 'Enter', + }) + ) }) it('supports focus events', () => { const handleFocus = vi.fn() const handleBlur = vi.fn() - - render() - + + render( + + ) + const button = screen.getByRole('button') fireEvent.focus(button) fireEvent.blur(button) - + expect(handleFocus).toHaveBeenCalledTimes(1) expect(handleBlur).toHaveBeenCalledTimes(1) }) it('applies focus-visible styling', () => { render() - + const button = screen.getByRole('button') - expect(button).toHaveClass('focus-visible:border-ring', 'focus-visible:ring-ring/50') + expect(button).toHaveClass( + 'focus-visible:border-primary', + 'focus-visible:ring-2', + 'focus-visible:ring-primary/60' + ) }) -}) \ No newline at end of file +}) diff --git a/web-app/src/components/ui/button.tsx b/web-app/src/components/ui/button.tsx index bface0830d..46110406f9 100644 --- a/web-app/src/components/ui/button.tsx +++ b/web-app/src/components/ui/button.tsx @@ -5,13 +5,18 @@ import { cva, type VariantProps } from 'class-variance-authority' import { cn } from '@/lib/utils' const buttonVariants = cva( - "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[0px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive cursor-pointer focus:outline-none", + cn( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none aria-invalid:ring-destructive/60 aria-invalid:border-destructive cursor-pointer", + 'focus:border-accent focus:ring-2 focus:ring-accent/50 focus:accent-[0px]', + 'focus-visible:border-accent focus-visible:ring-2 focus-visible:ring-accent/50 focus-visible:accent-[0px]' + ), { variants: { variant: { - default: 'bg-primary text-primary-fg shadow-xs hover:bg-primary/90', + default: + 'bg-primary text-primary-fg shadow-xs hover:bg-primary/90 focus-visible:ring-primary/60 focus:ring-primary/60 focus:border-primary focus-visible:border-primary', destructive: - 'bg-destructive shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 text-destructive-fg', + 'bg-destructive shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/60 text-destructive-fg focus:border-destructive focus:ring-destructive/60', link: 'underline-offset-4 hover:no-underline', }, size: { diff --git a/web-app/src/containers/ThreadContent.tsx b/web-app/src/containers/ThreadContent.tsx index 69f4402fdf..716c6bfc4b 100644 --- a/web-app/src/containers/ThreadContent.tsx +++ b/web-app/src/containers/ThreadContent.tsx @@ -104,13 +104,13 @@ const EditDialog = ({ - + {t('common:dialogs.editMessage.title')}