diff --git a/web/app/components/app/configuration/config/automatic/__tests__/automatic-btn.spec.tsx b/web/app/components/app/configuration/config/automatic/__tests__/automatic-btn.spec.tsx index b32c77ba21..218aa2732b 100644 --- a/web/app/components/app/configuration/config/automatic/__tests__/automatic-btn.spec.tsx +++ b/web/app/components/app/configuration/config/automatic/__tests__/automatic-btn.spec.tsx @@ -58,20 +58,4 @@ describe('AutomaticBtn', () => { expect(mockOnClick).toHaveBeenCalledTimes(3) }) }) - - describe('Styling', () => { - it('should have secondary-accent variant', () => { - render() - - const button = screen.getByRole('button') - expect(button.className).toContain('secondary-accent') - }) - - it('should have small size', () => { - render() - - const button = screen.getByRole('button') - expect(button.className).toContain('small') - }) - }) }) diff --git a/web/app/components/app/overview/apikey-info-panel/__tests__/cloud.spec.tsx b/web/app/components/app/overview/apikey-info-panel/__tests__/cloud.spec.tsx index 037803d355..e6abf54ac5 100644 --- a/web/app/components/app/overview/apikey-info-panel/__tests__/cloud.spec.tsx +++ b/web/app/components/app/overview/apikey-info-panel/__tests__/cloud.spec.tsx @@ -95,12 +95,6 @@ describe('APIKeyInfoPanel - Cloud Edition', () => { }) describe('Props and Styling', () => { - it('should render button with primary variant', () => { - scenarios.withAPIKeyNotSet() - const button = screen.getByRole('button') - expect(button).toHaveClass('btn-primary') - }) - it('should render panel container with correct classes', () => { const { container } = scenarios.withAPIKeyNotSet() const panel = container.firstChild as HTMLElement diff --git a/web/app/components/app/overview/apikey-info-panel/__tests__/index.spec.tsx b/web/app/components/app/overview/apikey-info-panel/__tests__/index.spec.tsx index d9c10b6ab9..7b3a12898a 100644 --- a/web/app/components/app/overview/apikey-info-panel/__tests__/index.spec.tsx +++ b/web/app/components/app/overview/apikey-info-panel/__tests__/index.spec.tsx @@ -108,12 +108,6 @@ describe('APIKeyInfoPanel - Community Edition', () => { }) describe('Props and Styling', () => { - it('should render button with primary variant', () => { - scenarios.withAPIKeyNotSet() - const button = screen.getByRole('button') - expect(button).toHaveClass('btn-primary') - }) - it('should render panel container with correct classes', () => { const { container } = scenarios.withAPIKeyNotSet() const panel = container.firstChild as HTMLElement diff --git a/web/app/components/app/overview/apikey-info-panel/apikey-info-panel.test-utils.tsx b/web/app/components/app/overview/apikey-info-panel/apikey-info-panel.test-utils.tsx index 4bab54b711..5d3c008989 100644 --- a/web/app/components/app/overview/apikey-info-panel/apikey-info-panel.test-utils.tsx +++ b/web/app/components/app/overview/apikey-info-panel/apikey-info-panel.test-utils.tsx @@ -1,7 +1,7 @@ import type { RenderOptions } from '@testing-library/react' import type { Mock, MockedFunction } from 'vitest' import type { ModalContextState } from '@/context/modal-context' -import { fireEvent, render } from '@testing-library/react' +import { fireEvent, render, screen } from '@testing-library/react' import { noop } from 'es-toolkit/function' import { defaultPlan } from '@/app/components/billing/config' import { useModalContext as actualUseModalContext } from '@/context/modal-context' @@ -81,6 +81,8 @@ type APIKeyInfoPanelRenderOptions = { mockOverrides?: MockOverrides } & Omit +const mainButtonName = /appOverview\.apiKeyInfo\.setAPIBtn/ + // Setup function to configure mocks function setupMocks(overrides: MockOverrides = {}) { mockUseProviderContext.mockReturnValue({ @@ -137,7 +139,7 @@ export const scenarios = { export const assertions = { // Should render main button shouldRenderMainButton: () => { - const button = document.querySelector('button.btn-primary') + const button = screen.getByRole('button', { name: mainButtonName }) expect(button).toBeInTheDocument() return button }, @@ -174,9 +176,8 @@ export const assertions = { export const interactions = { // Click the main button clickMainButton: () => { - const button = document.querySelector('button.btn-primary') - if (button) - fireEvent.click(button) + const button = screen.getByRole('button', { name: mainButtonName }) + fireEvent.click(button) return button }, @@ -191,6 +192,7 @@ export const interactions = { // Text content keys for assertions export const textKeys = { + button: mainButtonName, selfHost: { titleRow1: /appOverview\.apiKeyInfo\.selfHost\.title\.row1/, titleRow2: /appOverview\.apiKeyInfo\.selfHost\.title\.row2/, diff --git a/web/app/components/base/inline-delete-confirm/__tests__/index.spec.tsx b/web/app/components/base/inline-delete-confirm/__tests__/index.spec.tsx index 521988d3e6..176aa87786 100644 --- a/web/app/components/base/inline-delete-confirm/__tests__/index.spec.tsx +++ b/web/app/components/base/inline-delete-confirm/__tests__/index.spec.tsx @@ -84,49 +84,6 @@ describe('InlineDeleteConfirm', () => { }) }) - describe('Variant prop', () => { - it('should render with delete variant by default', () => { - const onConfirm = vi.fn() - const onCancel = vi.fn() - const { getByText } = render( - , - ) - - const confirmButton = getByText('Yes').closest('button') - expect(confirmButton?.className).toContain('btn-destructive-primary') - }) - - it('should render without destructive class for warning variant', () => { - const onConfirm = vi.fn() - const onCancel = vi.fn() - const { getByText } = render( - , - ) - - const confirmButton = getByText('Yes').closest('button') - expect(confirmButton?.className).not.toContain('btn-destructive-primary') - }) - - it('should render without destructive class for info variant', () => { - const onConfirm = vi.fn() - const onCancel = vi.fn() - const { getByText } = render( - , - ) - - const confirmButton = getByText('Yes').closest('button') - expect(confirmButton?.className).not.toContain('btn-destructive-primary') - }) - }) - describe('Custom className', () => { it('should apply custom className to wrapper', () => { const onConfirm = vi.fn() diff --git a/web/app/components/base/notion-connector/__tests__/index.spec.tsx b/web/app/components/base/notion-connector/__tests__/index.spec.tsx index 578ffffdca..2fb4976b29 100644 --- a/web/app/components/base/notion-connector/__tests__/index.spec.tsx +++ b/web/app/components/base/notion-connector/__tests__/index.spec.tsx @@ -22,7 +22,6 @@ describe('NotionConnector', () => { }) expect(button).toBeInTheDocument() - expect(button).toHaveClass('btn', 'btn-primary') }) it('should trigger the onSetting callback when the real button is clicked', async () => { diff --git a/web/app/components/base/ui/alert-dialog/__tests__/index.spec.tsx b/web/app/components/base/ui/alert-dialog/__tests__/index.spec.tsx index 3eec5f9f05..23fbcb19d6 100644 --- a/web/app/components/base/ui/alert-dialog/__tests__/index.spec.tsx +++ b/web/app/components/base/ui/alert-dialog/__tests__/index.spec.tsx @@ -4,7 +4,6 @@ import { AlertDialog, AlertDialogActions, AlertDialogCancelButton, - AlertDialogClose, AlertDialogConfirmButton, AlertDialogContent, AlertDialogDescription, @@ -70,14 +69,16 @@ describe('AlertDialog wrapper', () => { }) describe('User Interactions', () => { - it('should open and close dialog when trigger and close are clicked', async () => { + it('should open and close dialog when trigger and cancel button are clicked', async () => { render( Open Dialog Action Required Please confirm the action. - Cancel + + Cancel + , ) @@ -109,8 +110,7 @@ describe('AlertDialog wrapper', () => { expect(screen.getByTestId('actions')).toHaveClass('flex', 'items-start', 'justify-end', 'gap-2', 'self-stretch', 'p-6', 'custom-actions') const confirmButton = screen.getByRole('button', { name: 'Confirm' }) - expect(confirmButton).toHaveClass('btn-primary') - expect(confirmButton).toHaveClass('btn-destructive-primary') + expect(confirmButton).toHaveClass('bg-components-button-destructive-primary-bg') }) it('should keep dialog open after confirm click and close via cancel helper', async () => { diff --git a/web/app/components/base/ui/alert-dialog/index.tsx b/web/app/components/base/ui/alert-dialog/index.tsx index c8e68260c0..f922a19847 100644 --- a/web/app/components/base/ui/alert-dialog/index.tsx +++ b/web/app/components/base/ui/alert-dialog/index.tsx @@ -10,7 +10,6 @@ export const AlertDialog = BaseAlertDialog.Root export const AlertDialogTrigger = BaseAlertDialog.Trigger export const AlertDialogTitle = BaseAlertDialog.Title export const AlertDialogDescription = BaseAlertDialog.Description -export const AlertDialogClose = BaseAlertDialog.Close type AlertDialogContentProps = { children: React.ReactNode diff --git a/web/app/components/base/ui/avatar/index.tsx b/web/app/components/base/ui/avatar/index.tsx index 0842a1734d..f3bb5298b1 100644 --- a/web/app/components/base/ui/avatar/index.tsx +++ b/web/app/components/base/ui/avatar/index.tsx @@ -53,7 +53,7 @@ function AvatarImage({ }: AvatarImageProps) { return ( ) diff --git a/web/app/components/base/ui/button/__tests__/index.spec.tsx b/web/app/components/base/ui/button/__tests__/index.spec.tsx index 656c344b1a..e7b9c92c91 100644 --- a/web/app/components/base/ui/button/__tests__/index.spec.tsx +++ b/web/app/components/base/ui/button/__tests__/index.spec.tsx @@ -1,8 +1,6 @@ -import { cleanup, fireEvent, render, screen } from '@testing-library/react' +import { fireEvent, render, screen } from '@testing-library/react' import { Button } from '../index' -afterEach(cleanup) - describe('Button', () => { describe('rendering', () => { it('renders children text', () => { @@ -31,58 +29,79 @@ describe('Button', () => { expect(link).toHaveTextContent('Link') expect(link).toHaveAttribute('href', '/test') }) + + it('applies base layout classes', () => { + render() + const btn = screen.getByRole('button') + expect(btn).toHaveClass('inline-flex', 'justify-center', 'items-center', 'cursor-pointer') + }) }) describe('variants', () => { it('applies default secondary variant', () => { render() - expect(screen.getByRole('button').className).toContain('btn-secondary') + const btn = screen.getByRole('button') + expect(btn).toHaveClass('bg-components-button-secondary-bg', 'text-components-button-secondary-text') }) it.each([ - 'primary', - 'secondary', - 'secondary-accent', - 'ghost', - 'ghost-accent', - 'tertiary', - ] as const)('applies %s variant', (variant) => { + { variant: 'primary' as const, expectedClass: 'bg-components-button-primary-bg' }, + { variant: 'secondary' as const, expectedClass: 'bg-components-button-secondary-bg' }, + { variant: 'secondary-accent' as const, expectedClass: 'text-components-button-secondary-accent-text' }, + { variant: 'ghost' as const, expectedClass: 'text-components-button-ghost-text' }, + { variant: 'ghost-accent' as const, expectedClass: 'hover:bg-state-accent-hover' }, + { variant: 'tertiary' as const, expectedClass: 'bg-components-button-tertiary-bg' }, + ])('applies $variant variant', ({ variant, expectedClass }) => { render() - expect(screen.getByRole('button').className).toContain(`btn-${variant}`) + expect(screen.getByRole('button')).toHaveClass(expectedClass) }) it('applies destructive tone with default variant', () => { render() - expect(screen.getByRole('button').className).toContain('btn-destructive-secondary') + expect(screen.getByRole('button')).toHaveClass('bg-components-button-destructive-secondary-bg') }) it('applies destructive tone with primary variant', () => { render() - expect(screen.getByRole('button').className).toContain('btn-destructive-primary') + expect(screen.getByRole('button')).toHaveClass('bg-components-button-destructive-primary-bg') + }) + + it('applies destructive tone with tertiary variant', () => { + render() + expect(screen.getByRole('button')).toHaveClass('bg-components-button-destructive-tertiary-bg') + }) + + it('applies destructive tone with ghost variant', () => { + render() + expect(screen.getByRole('button')).toHaveClass('text-components-button-destructive-ghost-text') }) }) describe('sizes', () => { it('applies default medium size', () => { render() - expect(screen.getByRole('button').className).toContain('btn-medium') + expect(screen.getByRole('button')).toHaveClass('h-8', 'rounded-lg') }) - it.each(['small', 'medium', 'large'] as const)('applies %s size', (size) => { + it.each([ + { size: 'small' as const, expectedClass: 'h-6' }, + { size: 'medium' as const, expectedClass: 'h-8' }, + { size: 'large' as const, expectedClass: 'h-9' }, + ])('applies $size size', ({ size, expectedClass }) => { render() - expect(screen.getByRole('button').className).toContain(`btn-${size}`) + expect(screen.getByRole('button')).toHaveClass(expectedClass) }) }) describe('loading', () => { it('shows spinner when loading', () => { render() - expect(screen.getByRole('button').querySelector('.animate-spin')).toBeInTheDocument() + expect(screen.getByRole('button').querySelector('[aria-hidden="true"]')).toBeInTheDocument() }) it('hides spinner when not loading', () => { render() - expect(screen.getByRole('button').querySelector('.animate-spin')).not.toBeInTheDocument() + expect(screen.getByRole('button').querySelector('[aria-hidden="true"]')).not.toBeInTheDocument() }) it('auto-disables when loading', () => { @@ -137,6 +156,15 @@ describe('Button', () => { }) }) + describe('className merging', () => { + it('merges custom className with variant classes', () => { + render() + const btn = screen.getByRole('button') + expect(btn).toHaveClass('custom-class') + expect(btn).toHaveClass('inline-flex') + }) + }) + describe('ref forwarding', () => { it('forwards ref to the button element', () => { let buttonRef: HTMLButtonElement | null = null diff --git a/web/app/components/base/ui/button/index.css b/web/app/components/base/ui/button/index.css deleted file mode 100644 index eee8b7944b..0000000000 --- a/web/app/components/base/ui/button/index.css +++ /dev/null @@ -1,148 +0,0 @@ -@utility btn { - @apply inline-flex justify-center items-center cursor-pointer whitespace-nowrap - outline-hidden focus-visible:ring-2 focus-visible:ring-state-accent-solid; - - &:is(:disabled, [data-disabled]) { - @apply cursor-not-allowed; - } -} - -@utility btn-small { - @apply px-2 h-6 rounded-md text-xs font-medium; -} - -@utility btn-medium { - @apply px-3.5 h-8 rounded-lg text-[13px] leading-4 font-medium; -} - -@utility btn-large { - @apply px-4 h-9 rounded-[10px] text-sm font-semibold; -} - -@utility btn-primary { - @apply shadow - bg-components-button-primary-bg - border-components-button-primary-border - hover:bg-components-button-primary-bg-hover - hover:border-components-button-primary-border-hover - text-components-button-primary-text; - - &:is(:disabled, [data-disabled]) { - @apply shadow-none - bg-components-button-primary-bg-disabled - border-components-button-primary-border-disabled - text-components-button-primary-text-disabled; - } -} - -@utility btn-secondary { - @apply border-[0.5px] - shadow-xs - backdrop-blur-[5px] - bg-components-button-secondary-bg - border-components-button-secondary-border - hover:bg-components-button-secondary-bg-hover - hover:border-components-button-secondary-border-hover - text-components-button-secondary-text; - - &:is(:disabled, [data-disabled]) { - @apply backdrop-blur-xs - bg-components-button-secondary-bg-disabled - border-components-button-secondary-border-disabled - text-components-button-secondary-text-disabled; - } -} - -@utility btn-secondary-accent { - @apply border-[0.5px] - shadow-xs - bg-components-button-secondary-bg - border-components-button-secondary-border - hover:bg-components-button-secondary-bg-hover - hover:border-components-button-secondary-border-hover - text-components-button-secondary-accent-text; - - &:is(:disabled, [data-disabled]) { - @apply bg-components-button-secondary-bg-disabled - border-components-button-secondary-border-disabled - text-components-button-secondary-accent-text-disabled; - } -} - -@utility btn-tertiary { - @apply bg-components-button-tertiary-bg - hover:bg-components-button-tertiary-bg-hover - text-components-button-tertiary-text; - - &:is(:disabled, [data-disabled]) { - @apply bg-components-button-tertiary-bg-disabled - text-components-button-tertiary-text-disabled; - } -} - -@utility btn-ghost { - @apply hover:bg-components-button-ghost-bg-hover - text-components-button-ghost-text; - - &:is(:disabled, [data-disabled]) { - @apply text-components-button-ghost-text-disabled; - } -} - -@utility btn-ghost-accent { - @apply hover:bg-state-accent-hover - text-components-button-secondary-accent-text; - - &:is(:disabled, [data-disabled]) { - @apply text-components-button-secondary-accent-text-disabled; - } -} - -@utility btn-destructive-primary { - @apply bg-components-button-destructive-primary-bg - border-components-button-destructive-primary-border - hover:bg-components-button-destructive-primary-bg-hover - hover:border-components-button-destructive-primary-border-hover - text-components-button-destructive-primary-text; - - &:is(:disabled, [data-disabled]) { - @apply shadow-none - bg-components-button-destructive-primary-bg-disabled - border-components-button-destructive-primary-border-disabled - text-components-button-destructive-primary-text-disabled; - } -} - -@utility btn-destructive-secondary { - @apply bg-components-button-destructive-secondary-bg - border-components-button-destructive-secondary-border - hover:bg-components-button-destructive-secondary-bg-hover - hover:border-components-button-destructive-secondary-border-hover - text-components-button-destructive-secondary-text; - - &:is(:disabled, [data-disabled]) { - @apply bg-components-button-destructive-secondary-bg-disabled - border-components-button-destructive-secondary-border-disabled - text-components-button-destructive-secondary-text-disabled; - } -} - -@utility btn-destructive-tertiary { - @apply bg-components-button-destructive-tertiary-bg - hover:bg-components-button-destructive-tertiary-bg-hover - text-components-button-destructive-tertiary-text; - - &:is(:disabled, [data-disabled]) { - @apply bg-components-button-destructive-tertiary-bg-disabled - text-components-button-destructive-tertiary-text-disabled; - } -} - -@utility btn-destructive-ghost { - @apply hover:bg-components-button-destructive-ghost-bg-hover - text-components-button-destructive-ghost-text; - - &:is(:disabled, [data-disabled]) { - @apply text-components-button-destructive-ghost-text-disabled; - } -} diff --git a/web/app/components/base/ui/button/index.tsx b/web/app/components/base/ui/button/index.tsx index 00bbe3c8b7..002213486b 100644 --- a/web/app/components/base/ui/button/index.tsx +++ b/web/app/components/base/ui/button/index.tsx @@ -5,21 +5,47 @@ import { cva } from 'class-variance-authority' import { cn } from '@/utils/classnames' const buttonVariants = cva( - 'btn', + 'inline-flex cursor-pointer items-center justify-center whitespace-nowrap outline-hidden focus-visible:ring-2 focus-visible:ring-state-accent-solid data-[disabled]:cursor-not-allowed', { variants: { variant: { - 'primary': 'btn-primary', - 'secondary': 'btn-secondary', - 'secondary-accent': 'btn-secondary-accent', - 'ghost': 'btn-ghost', - 'ghost-accent': 'btn-ghost-accent', - 'tertiary': 'btn-tertiary', + 'primary': [ + 'border-components-button-primary-border bg-components-button-primary-bg text-components-button-primary-text shadow', + 'hover:border-components-button-primary-border-hover hover:bg-components-button-primary-bg-hover', + 'data-[disabled]:border-components-button-primary-border-disabled data-[disabled]:bg-components-button-primary-bg-disabled data-[disabled]:text-components-button-primary-text-disabled data-[disabled]:shadow-none', + ], + 'secondary': [ + 'border-[0.5px] shadow-xs backdrop-blur-[5px]', + 'border-components-button-secondary-border bg-components-button-secondary-bg text-components-button-secondary-text', + 'hover:border-components-button-secondary-border-hover hover:bg-components-button-secondary-bg-hover', + 'data-[disabled]:border-components-button-secondary-border-disabled data-[disabled]:bg-components-button-secondary-bg-disabled data-[disabled]:text-components-button-secondary-text-disabled data-[disabled]:backdrop-blur-xs', + ], + 'secondary-accent': [ + 'border-[0.5px] shadow-xs', + 'border-components-button-secondary-border bg-components-button-secondary-bg text-components-button-secondary-accent-text', + 'hover:border-components-button-secondary-border-hover hover:bg-components-button-secondary-bg-hover', + 'data-[disabled]:border-components-button-secondary-border-disabled data-[disabled]:bg-components-button-secondary-bg-disabled data-[disabled]:text-components-button-secondary-accent-text-disabled', + ], + 'tertiary': [ + 'bg-components-button-tertiary-bg text-components-button-tertiary-text', + 'hover:bg-components-button-tertiary-bg-hover', + 'data-[disabled]:bg-components-button-tertiary-bg-disabled data-[disabled]:text-components-button-tertiary-text-disabled', + ], + 'ghost': [ + 'text-components-button-ghost-text', + 'hover:bg-components-button-ghost-bg-hover', + 'data-[disabled]:text-components-button-ghost-text-disabled', + ], + 'ghost-accent': [ + 'text-components-button-secondary-accent-text', + 'hover:bg-state-accent-hover', + 'data-[disabled]:text-components-button-secondary-accent-text-disabled', + ], }, size: { - small: 'btn-small', - medium: 'btn-medium', - large: 'btn-large', + small: 'h-6 rounded-md px-2 text-xs font-medium', + medium: 'h-8 rounded-lg px-3.5 text-[13px] leading-4 font-medium', + large: 'h-9 rounded-[10px] px-4 text-sm font-semibold', }, tone: { default: '', @@ -27,10 +53,42 @@ const buttonVariants = cva( }, }, compoundVariants: [ - { variant: 'primary', tone: 'destructive', class: 'btn-destructive-primary' }, - { variant: 'secondary', tone: 'destructive', class: 'btn-destructive-secondary' }, - { variant: 'tertiary', tone: 'destructive', class: 'btn-destructive-tertiary' }, - { variant: 'ghost', tone: 'destructive', class: 'btn-destructive-ghost' }, + { + variant: 'primary', + tone: 'destructive', + class: [ + 'border-components-button-destructive-primary-border bg-components-button-destructive-primary-bg text-components-button-destructive-primary-text', + 'hover:border-components-button-destructive-primary-border-hover hover:bg-components-button-destructive-primary-bg-hover', + 'data-[disabled]:border-components-button-destructive-primary-border-disabled data-[disabled]:bg-components-button-destructive-primary-bg-disabled data-[disabled]:text-components-button-destructive-primary-text-disabled data-[disabled]:shadow-none', + ], + }, + { + variant: 'secondary', + tone: 'destructive', + class: [ + 'border-components-button-destructive-secondary-border bg-components-button-destructive-secondary-bg text-components-button-destructive-secondary-text', + 'hover:border-components-button-destructive-secondary-border-hover hover:bg-components-button-destructive-secondary-bg-hover', + 'data-[disabled]:border-components-button-destructive-secondary-border-disabled data-[disabled]:bg-components-button-destructive-secondary-bg-disabled data-[disabled]:text-components-button-destructive-secondary-text-disabled', + ], + }, + { + variant: 'tertiary', + tone: 'destructive', + class: [ + 'bg-components-button-destructive-tertiary-bg text-components-button-destructive-tertiary-text', + 'hover:bg-components-button-destructive-tertiary-bg-hover', + 'data-[disabled]:bg-components-button-destructive-tertiary-bg-disabled data-[disabled]:text-components-button-destructive-tertiary-text-disabled', + ], + }, + { + variant: 'ghost', + tone: 'destructive', + class: [ + 'text-components-button-destructive-ghost-text', + 'hover:bg-components-button-destructive-ghost-bg-hover', + 'data-[disabled]:text-components-button-destructive-ghost-text-disabled', + ], + }, ], defaultVariants: { variant: 'secondary', diff --git a/web/app/components/base/ui/dialog/__tests__/index.spec.tsx b/web/app/components/base/ui/dialog/__tests__/index.spec.tsx index 2e52bd547a..55f4e53288 100644 --- a/web/app/components/base/ui/dialog/__tests__/index.spec.tsx +++ b/web/app/components/base/ui/dialog/__tests__/index.spec.tsx @@ -1,15 +1,11 @@ -import { Dialog as BaseDialog } from '@base-ui/react/dialog' import { fireEvent, render, screen } from '@testing-library/react' import { describe, expect, it, vi } from 'vitest' import { Dialog, - DialogClose, DialogCloseButton, DialogContent, DialogDescription, - DialogPortal, DialogTitle, - DialogTrigger, } from '../index' describe('Dialog wrapper', () => { @@ -86,15 +82,4 @@ describe('Dialog wrapper', () => { expect(onClick).not.toHaveBeenCalled() }) }) - - describe('Exports', () => { - it('should map dialog aliases to the matching base dialog primitives', () => { - expect(Dialog).toBe(BaseDialog.Root) - expect(DialogTrigger).toBe(BaseDialog.Trigger) - expect(DialogTitle).toBe(BaseDialog.Title) - expect(DialogDescription).toBe(BaseDialog.Description) - expect(DialogClose).toBe(BaseDialog.Close) - expect(DialogPortal).toBe(BaseDialog.Portal) - }) - }) }) diff --git a/web/app/components/base/ui/dialog/index.tsx b/web/app/components/base/ui/dialog/index.tsx index 5dcebfcac2..c1802806c9 100644 --- a/web/app/components/base/ui/dialog/index.tsx +++ b/web/app/components/base/ui/dialog/index.tsx @@ -12,10 +12,10 @@ import * as React from 'react' import { cn } from '@/utils/classnames' export const Dialog = BaseDialog.Root +/** @public */ export const DialogTrigger = BaseDialog.Trigger export const DialogTitle = BaseDialog.Title export const DialogDescription = BaseDialog.Description -export const DialogClose = BaseDialog.Close export const DialogPortal = BaseDialog.Portal type DialogCloseButtonProps = Omit, 'children'> diff --git a/web/app/components/base/ui/dropdown-menu/__tests__/index.spec.tsx b/web/app/components/base/ui/dropdown-menu/__tests__/index.spec.tsx index b6772e5ad0..93ceb911e7 100644 --- a/web/app/components/base/ui/dropdown-menu/__tests__/index.spec.tsx +++ b/web/app/components/base/ui/dropdown-menu/__tests__/index.spec.tsx @@ -1,7 +1,5 @@ -import type { ComponentPropsWithoutRef, ReactNode } from 'react' import { fireEvent, render, screen, within } from '@testing-library/react' import { describe, expect, it, vi } from 'vitest' -import Link from '@/next/link' import { DropdownMenu, DropdownMenuContent, @@ -14,21 +12,6 @@ import { DropdownMenuTrigger, } from '../index' -vi.mock('@/next/link', () => ({ - default: ({ - href, - children, - ...props - }: { - href: string - children?: ReactNode - } & Omit, 'href'>) => ( - - {children} - - ), -})) - describe('dropdown-menu wrapper', () => { describe('DropdownMenuContent', () => { it('should position content at bottom-end with default placement when props are omitted', () => { @@ -295,13 +278,13 @@ describe('dropdown-menu wrapper', () => { expect(link).not.toHaveAttribute('closeOnClick') }) - it('should preserve link semantics when render prop uses a custom link component', () => { + it('should preserve link semantics when render prop uses a custom anchor element', () => { render( Open } + render={} aria-label="account link" > Account settings diff --git a/web/app/components/base/ui/number-field/__tests__/index.spec.tsx b/web/app/components/base/ui/number-field/__tests__/index.spec.tsx index f988e2b312..abfee58302 100644 --- a/web/app/components/base/ui/number-field/__tests__/index.spec.tsx +++ b/web/app/components/base/ui/number-field/__tests__/index.spec.tsx @@ -6,7 +6,6 @@ import type { NumberFieldInputProps, NumberFieldUnitProps, } from '../index' -import { NumberField as BaseNumberField } from '@base-ui/react/number-field' import { render, screen } from '@testing-library/react' import { NumberField, @@ -67,17 +66,6 @@ const renderNumberField = ({ } describe('NumberField wrapper', () => { - beforeEach(() => { - vi.clearAllMocks() - }) - - // Export mapping should stay aligned with the Base UI primitive. - describe('Exports', () => { - it('should map NumberField to the matching base primitive root', () => { - expect(NumberField).toBe(BaseNumberField.Root) - }) - }) - // Group and input wrappers should preserve the design-system variants and DOM defaults. describe('Group and input', () => { it('should apply regular group classes by default and merge custom className', () => { diff --git a/web/app/components/base/ui/popover/__tests__/index.spec.tsx b/web/app/components/base/ui/popover/__tests__/index.spec.tsx index 9d65f8c934..39fb3f3fac 100644 --- a/web/app/components/base/ui/popover/__tests__/index.spec.tsx +++ b/web/app/components/base/ui/popover/__tests__/index.spec.tsx @@ -1,12 +1,8 @@ -import { Popover as BasePopover } from '@base-ui/react/popover' import { fireEvent, render, screen } from '@testing-library/react' import { describe, expect, it, vi } from 'vitest' import { Popover, - PopoverClose, PopoverContent, - PopoverDescription, - PopoverTitle, PopoverTrigger, } from '..' @@ -93,15 +89,3 @@ describe('PopoverContent', () => { }) }) }) - -describe('Popover aliases', () => { - describe('Export mapping', () => { - it('should map aliases to the matching base popover primitives when wrapper exports are imported', () => { - expect(Popover).toBe(BasePopover.Root) - expect(PopoverTrigger).toBe(BasePopover.Trigger) - expect(PopoverClose).toBe(BasePopover.Close) - expect(PopoverTitle).toBe(BasePopover.Title) - expect(PopoverDescription).toBe(BasePopover.Description) - }) - }) -}) diff --git a/web/app/components/base/ui/popover/index.tsx b/web/app/components/base/ui/popover/index.tsx index 045d9066d3..08e3cf51e2 100644 --- a/web/app/components/base/ui/popover/index.tsx +++ b/web/app/components/base/ui/popover/index.tsx @@ -9,7 +9,9 @@ import { cn } from '@/utils/classnames' export const Popover = BasePopover.Root export const PopoverTrigger = BasePopover.Trigger export const PopoverClose = BasePopover.Close +/** @public */ export const PopoverTitle = BasePopover.Title +/** @public */ export const PopoverDescription = BasePopover.Description type PopoverContentProps = { diff --git a/web/app/components/base/ui/scroll-area/__tests__/index.spec.tsx b/web/app/components/base/ui/scroll-area/__tests__/index.spec.tsx index 4945b7f805..0d37c30712 100644 --- a/web/app/components/base/ui/scroll-area/__tests__/index.spec.tsx +++ b/web/app/components/base/ui/scroll-area/__tests__/index.spec.tsx @@ -9,7 +9,6 @@ import { ScrollAreaThumb, ScrollAreaViewport, } from '../index' -import styles from '../index.module.css' const renderScrollArea = (options: { rootClassName?: string @@ -106,7 +105,7 @@ describe('scroll-area wrapper', () => { const thumb = screen.getByTestId('scroll-area-vertical-thumb') expect(scrollbar).toHaveAttribute('data-orientation', 'vertical') - expect(scrollbar).toHaveClass(styles.scrollbar) + expect(scrollbar).toHaveAttribute('data-dify-scrollbar') expect(scrollbar).toHaveClass( 'flex', 'overflow-clip', @@ -144,7 +143,7 @@ describe('scroll-area wrapper', () => { const thumb = screen.getByTestId('scroll-area-horizontal-thumb') expect(scrollbar).toHaveAttribute('data-orientation', 'horizontal') - expect(scrollbar).toHaveClass(styles.scrollbar) + expect(scrollbar).toHaveAttribute('data-dify-scrollbar') expect(scrollbar).toHaveClass( 'flex', 'overflow-clip', diff --git a/web/app/components/base/ui/scroll-area/index.tsx b/web/app/components/base/ui/scroll-area/index.tsx index 34c5e69188..1533092b8a 100644 --- a/web/app/components/base/ui/scroll-area/index.tsx +++ b/web/app/components/base/ui/scroll-area/index.tsx @@ -3,7 +3,7 @@ import { ScrollArea as BaseScrollArea } from '@base-ui/react/scroll-area' import * as React from 'react' import { cn } from '@/utils/classnames' -import styles from './index.module.css' +import './scroll-area.css' export const ScrollAreaRoot = BaseScrollArea.Root type ScrollAreaRootProps = React.ComponentPropsWithRef @@ -25,7 +25,6 @@ type ScrollAreaProps = Omit & { } const scrollAreaScrollbarClassName = cn( - styles.scrollbar, 'flex touch-none overflow-clip p-1 opacity-100 transition-opacity select-none motion-reduce:transition-none', 'pointer-events-none data-hovering:pointer-events-auto', 'data-scrolling:pointer-events-auto', @@ -68,6 +67,7 @@ export function ScrollAreaScrollbar({ }: ScrollAreaScrollbarProps) { return ( diff --git a/web/app/components/base/ui/scroll-area/index.module.css b/web/app/components/base/ui/scroll-area/scroll-area.css similarity index 57% rename from web/app/components/base/ui/scroll-area/index.module.css rename to web/app/components/base/ui/scroll-area/scroll-area.css index a81fd3d3c2..7a33076acf 100644 --- a/web/app/components/base/ui/scroll-area/index.module.css +++ b/web/app/components/base/ui/scroll-area/scroll-area.css @@ -1,5 +1,5 @@ -.scrollbar::before, -.scrollbar::after { +[data-dify-scrollbar]::before, +[data-dify-scrollbar]::after { content: ''; position: absolute; z-index: 1; @@ -9,7 +9,7 @@ transition: opacity 150ms ease; } -.scrollbar[data-orientation='vertical']::before { +[data-dify-scrollbar][data-orientation='vertical']::before { left: 50%; top: 4px; width: 4px; @@ -18,7 +18,7 @@ background: linear-gradient(to bottom, var(--scroll-area-edge-hint-bg, var(--color-components-panel-bg)), transparent); } -.scrollbar[data-orientation='vertical']::after { +[data-dify-scrollbar][data-orientation='vertical']::after { left: 50%; bottom: 4px; width: 4px; @@ -27,7 +27,7 @@ background: linear-gradient(to top, var(--scroll-area-edge-hint-bg, var(--color-components-panel-bg)), transparent); } -.scrollbar[data-orientation='horizontal']::before { +[data-dify-scrollbar][data-orientation='horizontal']::before { top: 50%; left: 4px; width: 12px; @@ -36,7 +36,7 @@ background: linear-gradient(to right, var(--scroll-area-edge-hint-bg, var(--color-components-panel-bg)), transparent); } -.scrollbar[data-orientation='horizontal']::after { +[data-dify-scrollbar][data-orientation='horizontal']::after { top: 50%; right: 4px; width: 12px; @@ -45,31 +45,31 @@ background: linear-gradient(to left, var(--scroll-area-edge-hint-bg, var(--color-components-panel-bg)), transparent); } -.scrollbar[data-orientation='vertical']:not([data-overflow-y-start])::before { +[data-dify-scrollbar][data-orientation='vertical']:not([data-overflow-y-start])::before { opacity: 1; } -.scrollbar[data-orientation='vertical']:not([data-overflow-y-end])::after { +[data-dify-scrollbar][data-orientation='vertical']:not([data-overflow-y-end])::after { opacity: 1; } -.scrollbar[data-orientation='horizontal']:not([data-overflow-x-start])::before { +[data-dify-scrollbar][data-orientation='horizontal']:not([data-overflow-x-start])::before { opacity: 1; } -.scrollbar[data-orientation='horizontal']:not([data-overflow-x-end])::after { +[data-dify-scrollbar][data-orientation='horizontal']:not([data-overflow-x-end])::after { opacity: 1; } -.scrollbar[data-hovering] > [data-orientation], -.scrollbar[data-scrolling] > [data-orientation], -.scrollbar > [data-orientation]:active { +[data-dify-scrollbar][data-hovering] > [data-orientation], +[data-dify-scrollbar][data-scrolling] > [data-orientation], +[data-dify-scrollbar] > [data-orientation]:active { background-color: var(--scroll-area-thumb-bg-active, var(--color-state-base-handle-hover)); } @media (prefers-reduced-motion: reduce) { - .scrollbar::before, - .scrollbar::after { + [data-dify-scrollbar]::before, + [data-dify-scrollbar]::after { transition: none; } } diff --git a/web/app/components/base/ui/tooltip/__tests__/index.spec.tsx b/web/app/components/base/ui/tooltip/__tests__/index.spec.tsx index f1a96060d2..7feee4f8c3 100644 --- a/web/app/components/base/ui/tooltip/__tests__/index.spec.tsx +++ b/web/app/components/base/ui/tooltip/__tests__/index.spec.tsx @@ -1,7 +1,6 @@ -import { Tooltip as BaseTooltip } from '@base-ui/react/tooltip' import { fireEvent, render, screen } from '@testing-library/react' import { describe, expect, it, vi } from 'vitest' -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../index' +import { Tooltip, TooltipContent, TooltipTrigger } from '../index' describe('TooltipContent', () => { describe('Placement and offsets', () => { @@ -105,11 +104,3 @@ describe('TooltipContent', () => { }) }) }) - -describe('Tooltip aliases', () => { - it('should map alias exports to BaseTooltip components when wrapper exports are imported', () => { - expect(TooltipProvider).toBe(BaseTooltip.Provider) - expect(Tooltip).toBe(BaseTooltip.Root) - expect(TooltipTrigger).toBe(BaseTooltip.Trigger) - }) -}) diff --git a/web/app/components/datasets/create-from-pipeline/list/template-card/__tests__/actions.spec.tsx b/web/app/components/datasets/create-from-pipeline/list/template-card/__tests__/actions.spec.tsx index 6dbfb42ec8..0ac767667e 100644 --- a/web/app/components/datasets/create-from-pipeline/list/template-card/__tests__/actions.spec.tsx +++ b/web/app/components/datasets/create-from-pipeline/list/template-card/__tests__/actions.spec.tsx @@ -85,21 +85,6 @@ describe('Actions', () => { }) }) - // Button Variants Tests - describe('Button Variants', () => { - it('should have primary variant for choose button', () => { - render() - const chooseButton = screen.getByText(/operations\.choose/i).closest('button') - expect(chooseButton).toHaveClass('btn-primary') - }) - - it('should have secondary variant for details button', () => { - render() - const detailsButton = screen.getByText(/operations\.details/i).closest('button') - expect(detailsButton).toHaveClass('btn-secondary') - }) - }) - describe('Layout', () => { it('should have absolute positioning', () => { const { container } = render() diff --git a/web/app/components/datasets/documents/detail/batch-modal/__tests__/csv-uploader.spec.tsx b/web/app/components/datasets/documents/detail/batch-modal/__tests__/csv-uploader.spec.tsx index 38169c1537..6e3ba6ab90 100644 --- a/web/app/components/datasets/documents/detail/batch-modal/__tests__/csv-uploader.spec.tsx +++ b/web/app/components/datasets/documents/detail/batch-modal/__tests__/csv-uploader.spec.tsx @@ -160,24 +160,6 @@ describe('CSVUploader', () => { expect(mockUpdateFile).toHaveBeenCalled() }) }) - - it('should call updateFile with undefined when remove is clicked', () => { - const mockUpdateFile = vi.fn() - const mockFile: FileItem = { - fileID: 'file-1', - file: new File(['content'], 'test.csv', { type: 'text/csv' }) as CustomFile, - progress: 100, - } - const { container } = render( - , - ) - - const deleteButton = container.querySelector('.cursor-pointer') - if (deleteButton) - fireEvent.click(deleteButton) - - expect(mockUpdateFile).toHaveBeenCalledWith() - }) }) describe('Validation', () => { diff --git a/web/app/components/datasets/metadata/edit-metadata-batch/__tests__/modal.spec.tsx b/web/app/components/datasets/metadata/edit-metadata-batch/__tests__/modal.spec.tsx index c3b44ebd61..d9b88e20bb 100644 --- a/web/app/components/datasets/metadata/edit-metadata-batch/__tests__/modal.spec.tsx +++ b/web/app/components/datasets/metadata/edit-metadata-batch/__tests__/modal.spec.tsx @@ -187,10 +187,7 @@ describe('EditMetadataBatchModal', () => { }) // Find the primary save button (not the one in SelectMetadataModal) - const saveButtons = screen.getAllByText(/save/i) - const modalSaveButton = saveButtons.find(btn => btn.closest('button')?.classList.contains('btn-primary')) - if (modalSaveButton) - fireEvent.click(modalSaveButton) + fireEvent.click(screen.getByRole('button', { name: 'common.operation.save' })) expect(onSave).toHaveBeenCalled() }) @@ -443,13 +440,10 @@ describe('EditMetadataBatchModal', () => { }) // Find the primary save button - const saveButtons = screen.getAllByText(/save/i) - const saveBtn = saveButtons.find(btn => btn.closest('button')?.classList.contains('btn-primary')) - if (saveBtn) { - fireEvent.click(saveBtn) - fireEvent.click(saveBtn) - fireEvent.click(saveBtn) - } + const saveBtn = screen.getByRole('button', { name: 'common.operation.save' }) + fireEvent.click(saveBtn) + fireEvent.click(saveBtn) + fireEvent.click(saveBtn) expect(onSave).toHaveBeenCalledTimes(3) }) @@ -462,10 +456,7 @@ describe('EditMetadataBatchModal', () => { expect(screen.getByRole('dialog')).toBeInTheDocument() }) - const saveButtons = screen.getAllByText(/save/i) - const saveBtn = saveButtons.find(btn => btn.closest('button')?.classList.contains('btn-primary')) - if (saveBtn) - fireEvent.click(saveBtn) + fireEvent.click(screen.getByRole('button', { name: 'common.operation.save' })) expect(onSave).toHaveBeenCalledWith( expect.any(Array), @@ -486,10 +477,7 @@ describe('EditMetadataBatchModal', () => { if (checkboxContainer) fireEvent.click(checkboxContainer) - const saveButtons = screen.getAllByText(/save/i) - const saveBtn = saveButtons.find(btn => btn.closest('button')?.classList.contains('btn-primary')) - if (saveBtn) - fireEvent.click(saveBtn) + fireEvent.click(screen.getByRole('button', { name: 'common.operation.save' })) await waitFor(() => { expect(onSave).toHaveBeenCalledWith( @@ -511,10 +499,7 @@ describe('EditMetadataBatchModal', () => { // Remove an item fireEvent.click(screen.getByTestId('remove-1')) - const saveButtons = screen.getAllByText(/save/i) - const saveBtn = saveButtons.find(btn => btn.closest('button')?.classList.contains('btn-primary')) - if (saveBtn) - fireEvent.click(saveBtn) + fireEvent.click(screen.getByRole('button', { name: 'common.operation.save' })) expect(onSave).toHaveBeenCalled() // The first argument should not contain the deleted item (id '1') diff --git a/web/app/components/datasets/metadata/metadata-dataset/__tests__/dataset-metadata-drawer.spec.tsx b/web/app/components/datasets/metadata/metadata-dataset/__tests__/dataset-metadata-drawer.spec.tsx index 907174ab19..353a39450e 100644 --- a/web/app/components/datasets/metadata/metadata-dataset/__tests__/dataset-metadata-drawer.spec.tsx +++ b/web/app/components/datasets/metadata/metadata-dataset/__tests__/dataset-metadata-drawer.spec.tsx @@ -284,12 +284,7 @@ describe('DatasetMetadataDrawer', () => { fireEvent.change(inputs[0], { target: { value: 'renamed_field' } }) // Find and click save button - const saveBtns = screen.getAllByText(/save/i) - const primaryBtn = saveBtns.find(btn => - btn.closest('button')?.classList.contains('btn-primary'), - ) - if (primaryBtn) - fireEvent.click(primaryBtn) + fireEvent.click(screen.getByRole('button', { name: 'common.operation.save' })) await waitFor(() => { expect(onRename).toHaveBeenCalled() diff --git a/web/app/components/develop/secret-key/__tests__/secret-key-button.spec.tsx b/web/app/components/develop/secret-key/__tests__/secret-key-button.spec.tsx index 798d0dd16f..e5134b995d 100644 --- a/web/app/components/develop/secret-key/__tests__/secret-key-button.spec.tsx +++ b/web/app/components/develop/secret-key/__tests__/secret-key-button.spec.tsx @@ -139,18 +139,6 @@ describe('SecretKeyButton', () => { const button = screen.getByRole('button') expect(button.className).toContain('px-3') }) - - it('should have small size', () => { - render() - const button = screen.getByRole('button') - expect(button.className).toContain('btn-small') - }) - - it('should have ghost variant', () => { - render() - const button = screen.getByRole('button') - expect(button.className).toContain('btn-ghost') - }) }) describe('icon styling', () => { diff --git a/web/app/components/header/account-setting/model-provider-page/system-model-selector/__tests__/index.spec.tsx b/web/app/components/header/account-setting/model-provider-page/system-model-selector/__tests__/index.spec.tsx index 2cf83597ca..c1b27a2c04 100644 --- a/web/app/components/header/account-setting/model-provider-page/system-model-selector/__tests__/index.spec.tsx +++ b/web/app/components/header/account-setting/model-provider-page/system-model-selector/__tests__/index.spec.tsx @@ -120,12 +120,6 @@ describe('SystemModel', () => { expect(screen.getByRole('button', { name: /system model settings/i })).toBeDisabled() }) - it('should render the primary button variant when configuration is required', () => { - render() - - expect(screen.getByRole('button', { name: /system model settings/i })).toHaveClass('btn-primary') - }) - it('should close dialog when cancel is clicked', async () => { render() fireEvent.click(screen.getByRole('button', { name: /system model settings/i })) diff --git a/web/app/components/plugins/plugin-auth/authorize/__tests__/authorize-components.spec.tsx b/web/app/components/plugins/plugin-auth/authorize/__tests__/authorize-components.spec.tsx index acb31cee5f..c495d5501a 100644 --- a/web/app/components/plugins/plugin-auth/authorize/__tests__/authorize-components.spec.tsx +++ b/web/app/components/plugins/plugin-auth/authorize/__tests__/authorize-components.spec.tsx @@ -156,29 +156,6 @@ describe('AddApiKeyButton', () => { expect(screen.getByRole('button')).toHaveTextContent('Custom API Key') }) - - it('should apply button variant', () => { - const pluginPayload = createPluginPayload() - - render( - , - { wrapper: createWrapper() }, - ) - - expect(screen.getByRole('button').className).toContain('btn-primary') - }) - - it('should use secondary-accent variant by default', () => { - const pluginPayload = createPluginPayload() - - render(, { wrapper: createWrapper() }) - - // Verify the default button has secondary-accent variant class - expect(screen.getByRole('button').className).toContain('btn-secondary-accent') - }) }) describe('Props Testing', () => { @@ -372,25 +349,6 @@ describe('AddOAuthButton', () => { expect(screen.getByText('plugin.auth.setupOAuth')).toBeInTheDocument() }) - - it('should apply button variant to setup button', () => { - const pluginPayload = createPluginPayload() - mockGetPluginOAuthClientSchema.mockReturnValue({ - schema: [], - is_oauth_custom_client_enabled: false, - is_system_oauth_params_exists: false, - }) - - render( - , - { wrapper: createWrapper() }, - ) - - expect(screen.getByRole('button').className).toContain('btn-secondary') - }) }) describe('Rendering - Configured State', () => { diff --git a/web/app/components/rag-pipeline/components/__tests__/version-mismatch-modal.spec.tsx b/web/app/components/rag-pipeline/components/__tests__/version-mismatch-modal.spec.tsx index 038362c7ce..56ed88e2ae 100644 --- a/web/app/components/rag-pipeline/components/__tests__/version-mismatch-modal.spec.tsx +++ b/web/app/components/rag-pipeline/components/__tests__/version-mismatch-modal.spec.tsx @@ -88,23 +88,6 @@ describe('VersionMismatchModal', () => { }) }) - describe('button variants', () => { - it('should render cancel button with secondary variant', () => { - render() - - const cancelBtn = screen.getByRole('button', { name: /app\.newApp\.Cancel/ }) - expect(cancelBtn).toHaveClass('btn-secondary') - }) - - it('should render confirm button with primary destructive variant', () => { - render() - - const confirmBtn = screen.getByRole('button', { name: /app\.newApp\.Confirm/ }) - expect(confirmBtn).toHaveClass('btn-primary') - expect(confirmBtn).toHaveClass('btn-destructive-primary') - }) - }) - describe('edge cases', () => { it('should handle undefined versions gracefully', () => { render() diff --git a/web/app/components/tools/workflow-tool/confirm-modal/__tests__/index.spec.tsx b/web/app/components/tools/workflow-tool/confirm-modal/__tests__/index.spec.tsx index 2cbe98c7ab..c5bce8b663 100644 --- a/web/app/components/tools/workflow-tool/confirm-modal/__tests__/index.spec.tsx +++ b/web/app/components/tools/workflow-tool/confirm-modal/__tests__/index.spec.tsx @@ -157,16 +157,6 @@ describe('ConfirmModal', () => { // Act & Assert - This will fail the test if user.click throws an unhandled error await user.click(confirmButton) }) - - it('should have correct button variants', () => { - // Arrange & Act - renderComponent() - - // Assert - const confirmButton = screen.getByText('common.operation.confirm') - expect(confirmButton).toHaveClass('btn-primary') - expect(confirmButton).toHaveClass('btn-destructive-primary') - }) }) // Edge Cases (REQUIRED) diff --git a/web/app/styles/globals.css b/web/app/styles/globals.css index ac6475a565..ec5dc86a89 100644 --- a/web/app/styles/globals.css +++ b/web/app/styles/globals.css @@ -8,7 +8,6 @@ @import '../components/base/action-button/index.css'; @import '../components/base/badge/index.css'; -@import '../components/base/ui/button/index.css'; @import '../components/base/modal/index.css' layer(base); @import '../components/base/premium-badge/index.css'; @import '../components/base/segmented-control/index.css'; diff --git a/web/eslint-suppressions.json b/web/eslint-suppressions.json index 111bd0011d..6b28c8ada2 100644 --- a/web/eslint-suppressions.json +++ b/web/eslint-suppressions.json @@ -3314,11 +3314,6 @@ "count": 1 } }, - "app/components/base/ui/avatar/index.tsx": { - "tailwindcss/enforce-consistent-class-order": { - "count": 1 - } - }, "app/components/base/video-gallery/VideoPlayer.tsx": { "react/set-state-in-effect": { "count": 1