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