mirror of
https://github.com/langgenius/dify.git
synced 2026-04-19 18:27:27 +08:00
refactor: replace deprecated Confirm component with new ConfirmDialog across multiple files
This commit is contained in:
@ -10,7 +10,7 @@
|
||||
* - Access mode icons
|
||||
*/
|
||||
import type { App } from '@/types/app'
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import { fireEvent, render, screen, waitFor, within } from '@testing-library/react'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import AppCard from '@/app/components/apps/app-card'
|
||||
import { AccessMode } from '@/models/access-control'
|
||||
@ -22,26 +22,7 @@ let mockSystemFeatures = {
|
||||
branding: { enabled: false },
|
||||
webapp_auth: { enabled: false },
|
||||
}
|
||||
|
||||
const toastMocks = vi.hoisted(() => ({
|
||||
mockNotify: vi.fn(),
|
||||
dismiss: vi.fn(),
|
||||
update: vi.fn(),
|
||||
promise: vi.fn(),
|
||||
}))
|
||||
const mockRouterPush = vi.fn()
|
||||
|
||||
vi.mock('@/app/components/base/ui/toast', () => ({
|
||||
toast: {
|
||||
success: (message: string, options?: Record<string, unknown>) => toastMocks.mockNotify({ type: 'success', message, ...options }),
|
||||
error: (message: string, options?: Record<string, unknown>) => toastMocks.mockNotify({ type: 'error', message, ...options }),
|
||||
warning: (message: string, options?: Record<string, unknown>) => toastMocks.mockNotify({ type: 'warning', message, ...options }),
|
||||
info: (message: string, options?: Record<string, unknown>) => toastMocks.mockNotify({ type: 'info', message, ...options }),
|
||||
dismiss: toastMocks.dismiss,
|
||||
update: toastMocks.update,
|
||||
promise: toastMocks.promise,
|
||||
},
|
||||
}))
|
||||
const mockOnPlanInfoChanged = vi.fn()
|
||||
const mockDeleteAppMutation = vi.fn().mockResolvedValue(undefined)
|
||||
let mockDeleteMutationPending = false
|
||||
@ -207,20 +188,6 @@ vi.mock('@/app/components/app/switch-app-modal', () => ({
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/confirm', () => ({
|
||||
default: ({ isShow, onConfirm, onCancel, title }: Record<string, unknown>) => {
|
||||
if (!isShow)
|
||||
return null
|
||||
return (
|
||||
<div data-testid="confirm-delete-modal">
|
||||
<span>{title as string}</span>
|
||||
<button data-testid="confirm-delete" onClick={onConfirm as () => void}>Delete</button>
|
||||
<button data-testid="cancel-delete" onClick={onCancel as () => void}>Cancel</button>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/workflow/dsl-export-confirm-modal', () => ({
|
||||
default: ({ onConfirm, onClose }: Record<string, unknown>) => (
|
||||
<div data-testid="dsl-export-confirm-modal">
|
||||
@ -342,14 +309,15 @@ describe('App Card Operations Flow', () => {
|
||||
fireEvent.click(deleteBtn)
|
||||
})
|
||||
|
||||
const confirmBtn = screen.queryByTestId('confirm-delete')
|
||||
if (confirmBtn) {
|
||||
fireEvent.click(confirmBtn)
|
||||
const dialog = await screen.findByRole('alertdialog')
|
||||
fireEvent.change(within(dialog).getByRole('textbox', { name: 'deleteAppConfirmInputLabel' }), {
|
||||
target: { value: 'Deletable App' },
|
||||
})
|
||||
fireEvent.click(within(dialog).getByRole('button', { name: 'common.operation.confirm' }))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockDeleteAppMutation).toHaveBeenCalledWith('app-to-delete')
|
||||
})
|
||||
}
|
||||
await waitFor(() => {
|
||||
expect(mockDeleteAppMutation).toHaveBeenCalledWith('app-to-delete')
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@ -7,7 +7,7 @@ import type { Collection } from '@/app/components/tools/types'
|
||||
* Verifies that different provider types render correctly and
|
||||
* handle auth/edit/delete flows.
|
||||
*/
|
||||
import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import { cleanup, fireEvent, render, screen, waitFor, within } from '@testing-library/react'
|
||||
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { CollectionType } from '@/app/components/tools/types'
|
||||
@ -133,36 +133,6 @@ vi.mock('@/app/components/base/drawer', () => ({
|
||||
),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/confirm', () => ({
|
||||
default: ({ title, isShow, onConfirm, onCancel }: {
|
||||
title: string
|
||||
content: string
|
||||
isShow: boolean
|
||||
onConfirm: () => void
|
||||
onCancel: () => void
|
||||
}) => (
|
||||
isShow
|
||||
? (
|
||||
<div data-testid="confirm-dialog">
|
||||
<span>{title}</span>
|
||||
<button data-testid="confirm-ok" onClick={onConfirm}>Confirm</button>
|
||||
<button data-testid="confirm-cancel" onClick={onCancel}>Cancel</button>
|
||||
</div>
|
||||
)
|
||||
: null
|
||||
),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/ui/toast', () => ({
|
||||
default: { notify: vi.fn() },
|
||||
toast: {
|
||||
success: vi.fn(),
|
||||
error: vi.fn(),
|
||||
warning: vi.fn(),
|
||||
info: vi.fn(),
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/icons/src/vender/line/general', () => ({
|
||||
LinkExternal02: () => <span data-testid="link-icon" />,
|
||||
Settings01: () => <span data-testid="settings-icon" />,
|
||||
@ -464,12 +434,10 @@ describe('Tool Provider Detail Flow Integration', () => {
|
||||
})
|
||||
|
||||
fireEvent.click(screen.getByTestId('custom-modal-remove'))
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('confirm-dialog')).toBeInTheDocument()
|
||||
expect(screen.getByText('Delete Tool')).toBeInTheDocument()
|
||||
})
|
||||
const dialog = await screen.findByRole('alertdialog')
|
||||
expect(within(dialog).getByText('Delete Tool')).toBeInTheDocument()
|
||||
|
||||
fireEvent.click(screen.getByTestId('confirm-ok'))
|
||||
fireEvent.click(within(dialog).getAllByRole('button').at(-1)!)
|
||||
await waitFor(() => {
|
||||
expect(mockRemoveCustomCollection).toHaveBeenCalledWith('test_collection')
|
||||
expect(mockOnRefreshData).toHaveBeenCalled()
|
||||
@ -526,11 +494,9 @@ describe('Tool Provider Detail Flow Integration', () => {
|
||||
})
|
||||
|
||||
fireEvent.click(screen.getByTestId('wf-modal-remove'))
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('confirm-dialog')).toBeInTheDocument()
|
||||
})
|
||||
const dialog = await screen.findByRole('alertdialog')
|
||||
|
||||
fireEvent.click(screen.getByTestId('confirm-ok'))
|
||||
fireEvent.click(within(dialog).getAllByRole('button').at(-1)!)
|
||||
await waitFor(() => {
|
||||
expect(mockDeleteWorkflowTool).toHaveBeenCalledWith('test-collection')
|
||||
expect(mockOnRefreshData).toHaveBeenCalled()
|
||||
|
||||
@ -6,7 +6,6 @@ import * as React from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import { LinkExternal02 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { Lock01 } from '@/app/components/base/icons/src/vender/solid/security'
|
||||
@ -14,6 +13,7 @@ import {
|
||||
PortalToFollowElem,
|
||||
PortalToFollowElemContent,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import { toast } from '@/app/components/base/ui/toast'
|
||||
import { addTracingConfig, removeTracingConfig, updateTracingConfig } from '@/service/apps'
|
||||
import { docURL } from './config'
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { App, AppSSO } from '@/types/app'
|
||||
import { act, render, screen, waitFor } from '@testing-library/react'
|
||||
import { act, render, screen, waitFor, within } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import * as React from 'react'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
@ -36,24 +36,6 @@ vi.mock('@/app/components/app/duplicate-modal', () => ({
|
||||
),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/confirm', () => ({
|
||||
default: ({ isShow, title, onConfirm, onCancel }: {
|
||||
isShow: boolean
|
||||
title: string
|
||||
onConfirm: () => void
|
||||
onCancel: () => void
|
||||
}) => (
|
||||
isShow
|
||||
? (
|
||||
<div data-testid="confirm-modal" data-title={title}>
|
||||
<button type="button" onClick={onConfirm}>Confirm</button>
|
||||
<button type="button" onClick={onCancel}>Cancel</button>
|
||||
</div>
|
||||
)
|
||||
: null
|
||||
),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/workflow/update-dsl-modal', () => ({
|
||||
default: ({ onCancel, onBackup }: { onCancel: () => void, onBackup: () => void }) => (
|
||||
<div data-testid="import-dsl-modal">
|
||||
@ -113,7 +95,7 @@ describe('AppInfoModals', () => {
|
||||
render(<AppInfoModals {...defaultProps} activeModal={null} />)
|
||||
})
|
||||
expect(screen.queryByTestId('switch-modal')).not.toBeInTheDocument()
|
||||
expect(screen.queryByTestId('confirm-modal')).not.toBeInTheDocument()
|
||||
expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render SwitchAppModal when activeModal is switch', async () => {
|
||||
@ -148,9 +130,9 @@ describe('AppInfoModals', () => {
|
||||
render(<AppInfoModals {...defaultProps} activeModal="delete" />)
|
||||
})
|
||||
await waitFor(() => {
|
||||
const confirm = screen.getByTestId('confirm-modal')
|
||||
expect(confirm).toBeInTheDocument()
|
||||
expect(confirm).toHaveAttribute('data-title', 'app.deleteAppConfirmTitle')
|
||||
const dialog = screen.getByRole('alertdialog')
|
||||
expect(dialog).toBeInTheDocument()
|
||||
expect(within(dialog).getByText('app.deleteAppConfirmTitle')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
@ -168,9 +150,9 @@ describe('AppInfoModals', () => {
|
||||
render(<AppInfoModals {...defaultProps} activeModal="exportWarning" />)
|
||||
})
|
||||
await waitFor(() => {
|
||||
const confirm = screen.getByTestId('confirm-modal')
|
||||
expect(confirm).toBeInTheDocument()
|
||||
expect(confirm).toHaveAttribute('data-title', 'workflow.sidebar.exportWarning')
|
||||
const dialog = screen.getByRole('alertdialog')
|
||||
expect(dialog).toBeInTheDocument()
|
||||
expect(within(dialog).getByText('workflow.sidebar.exportWarning')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
@ -202,8 +184,8 @@ describe('AppInfoModals', () => {
|
||||
render(<AppInfoModals {...defaultProps} activeModal="delete" />)
|
||||
})
|
||||
|
||||
await waitFor(() => expect(screen.getByText('Cancel')).toBeInTheDocument())
|
||||
await user.click(screen.getByText('Cancel'))
|
||||
const dialog = await screen.findByRole('alertdialog')
|
||||
await user.click(within(dialog).getByRole('button', { name: 'common.operation.cancel' }))
|
||||
|
||||
expect(defaultProps.closeModal).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
@ -214,8 +196,9 @@ describe('AppInfoModals', () => {
|
||||
render(<AppInfoModals {...defaultProps} activeModal="delete" />)
|
||||
})
|
||||
|
||||
await waitFor(() => expect(screen.getByText('Confirm')).toBeInTheDocument())
|
||||
await user.click(screen.getByText('Confirm'))
|
||||
await user.type(screen.getByRole('textbox'), 'Test App')
|
||||
const dialog = screen.getByRole('alertdialog')
|
||||
await user.click(within(dialog).getAllByRole('button').at(-1)!)
|
||||
|
||||
expect(defaultProps.onConfirmDelete).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
@ -226,8 +209,8 @@ describe('AppInfoModals', () => {
|
||||
render(<AppInfoModals {...defaultProps} activeModal="exportWarning" />)
|
||||
})
|
||||
|
||||
await waitFor(() => expect(screen.getByText('Confirm')).toBeInTheDocument())
|
||||
await user.click(screen.getByText('Confirm'))
|
||||
const dialog = await screen.findByRole('alertdialog')
|
||||
await user.click(within(dialog).getAllByRole('button').at(-1)!)
|
||||
|
||||
expect(defaultProps.handleConfirmExport).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
@ -11,7 +11,7 @@ import dynamic from '@/next/dynamic'
|
||||
const SwitchAppModal = dynamic(() => import('@/app/components/app/switch-app-modal'), { ssr: false })
|
||||
const CreateAppModal = dynamic(() => import('@/app/components/explore/create-app-modal'), { ssr: false })
|
||||
const DuplicateAppModal = dynamic(() => import('@/app/components/app/duplicate-modal'), { ssr: false })
|
||||
const Confirm = dynamic(() => import('@/app/components/base/confirm'), { ssr: false })
|
||||
const Confirm = dynamic(() => import('@/app/components/base/ui/confirm-dialog'), { ssr: false })
|
||||
const UpdateDSLModal = dynamic(() => import('@/app/components/workflow/update-dsl-modal'), { ssr: false })
|
||||
const DSLExportConfirmModal = dynamic(() => import('@/app/components/workflow/dsl-export-confirm-modal'), { ssr: false })
|
||||
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import type { DataSet } from '@/models/datasets'
|
||||
import { render, screen, waitFor } from '@testing-library/react'
|
||||
import { render, screen, waitFor, within } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import * as React from 'react'
|
||||
import * as toastModule from '@/app/components/base/ui/toast'
|
||||
import {
|
||||
ChunkingMode,
|
||||
DatasetPermission,
|
||||
@ -112,10 +113,6 @@ vi.mock('@/service/datasets', () => ({
|
||||
deleteDataset: (...args: unknown[]) => mockDeleteDataset(...args),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/ui/toast', () => ({
|
||||
toast: (...args: unknown[]) => mockToast(...args),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/datasets/rename-modal', () => ({
|
||||
default: ({
|
||||
show,
|
||||
@ -137,33 +134,6 @@ vi.mock('@/app/components/datasets/rename-modal', () => ({
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/confirm', () => ({
|
||||
default: ({
|
||||
isShow,
|
||||
onConfirm,
|
||||
onCancel,
|
||||
title,
|
||||
content,
|
||||
}: {
|
||||
isShow: boolean
|
||||
onConfirm: () => void
|
||||
onCancel: () => void
|
||||
title: string
|
||||
content: string
|
||||
}) => {
|
||||
if (!isShow)
|
||||
return null
|
||||
return (
|
||||
<div data-testid="confirm-dialog">
|
||||
<span>{title}</span>
|
||||
<span>{content}</span>
|
||||
<button type="button" onClick={onConfirm}>confirm</button>
|
||||
<button type="button" onClick={onCancel}>cancel</button>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/portal-to-follow-elem', () => ({
|
||||
PortalToFollowElem: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
||||
PortalToFollowElemTrigger: ({ children, onClick }: { children: React.ReactNode, onClick?: () => void }) => (
|
||||
@ -172,6 +142,11 @@ vi.mock('@/app/components/base/portal-to-follow-elem', () => ({
|
||||
PortalToFollowElemContent: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
|
||||
}))
|
||||
|
||||
vi.spyOn(toastModule, 'toast').mockImplementation((...args) => {
|
||||
mockToast(...args)
|
||||
return 'toast-id'
|
||||
})
|
||||
|
||||
describe('Dropdown callback coverage', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
@ -220,14 +195,11 @@ describe('Dropdown callback coverage', () => {
|
||||
await user.click(screen.getByTestId('portal-trigger'))
|
||||
await user.click(screen.getByText('common.operation.delete'))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('confirm-dialog')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
await user.click(screen.getByText('cancel'))
|
||||
const dialog = await screen.findByRole('alertdialog')
|
||||
await user.click(within(dialog).getByRole('button', { name: 'common.operation.cancel' }))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId('confirm-dialog')).not.toBeInTheDocument()
|
||||
expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
@ -273,6 +245,6 @@ describe('Dropdown callback coverage', () => {
|
||||
await waitFor(() => {
|
||||
expect(mockToast).toHaveBeenCalledWith('check failed', { type: 'error' })
|
||||
})
|
||||
expect(screen.queryByTestId('confirm-dialog')).not.toBeInTheDocument()
|
||||
expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
@ -14,8 +14,8 @@ import { useExportPipelineDSL } from '@/service/use-pipeline'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { downloadBlob } from '@/utils/download'
|
||||
import ActionButton from '../../base/action-button'
|
||||
import Confirm from '../../base/confirm'
|
||||
import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '../../base/portal-to-follow-elem'
|
||||
import Confirm from '../../base/ui/confirm-dialog'
|
||||
import RenameDatasetModal from '../../datasets/rename-modal'
|
||||
import Menu from './menu'
|
||||
|
||||
|
||||
@ -3,8 +3,8 @@ import { RiDeleteBinLine } from '@remixicon/react'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
const i18nPrefix = 'batchAction'
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
import type { FC } from 'react'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
|
||||
type Props = {
|
||||
isShow: boolean
|
||||
|
||||
@ -3,9 +3,9 @@ import type { FC } from 'react'
|
||||
import * as React from 'react'
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import Drawer from '@/app/components/base/drawer-plus'
|
||||
import { MessageCheckRemove } from '@/app/components/base/icons/src/vender/line/communication'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import { toast } from '@/app/components/base/ui/toast'
|
||||
import AnnotationFull from '@/app/components/billing/annotation-full'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
import type { FC } from 'react'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
|
||||
type Props = {
|
||||
isShow: boolean
|
||||
|
||||
@ -5,11 +5,11 @@ import * as React from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Badge from '@/app/components/base/badge'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import Drawer from '@/app/components/base/drawer-plus'
|
||||
import { MessageCheckRemove } from '@/app/components/base/icons/src/vender/line/communication'
|
||||
import Pagination from '@/app/components/base/pagination'
|
||||
import TabSlider from '@/app/components/base/tab-slider-plain'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import { APP_PAGE_LIMIT } from '@/config'
|
||||
import useTimestamp from '@/hooks/use-timestamp'
|
||||
import { fetchHitHistoryList } from '@/service/annotation'
|
||||
|
||||
@ -6,9 +6,9 @@ import * as React from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import AppPublisher from '@/app/components/app/app-publisher'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks'
|
||||
import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import { SupportUploadFileTypes } from '@/app/components/workflow/types'
|
||||
import { Resolution } from '@/types/app'
|
||||
|
||||
|
||||
@ -11,8 +11,8 @@ import { useCallback, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { ReactSortable } from 'react-sortablejs'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import { toast } from '@/app/components/base/ui/toast'
|
||||
import { InputVarType } from '@/app/components/workflow/types'
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
|
||||
@ -20,10 +20,10 @@ import * as React from 'react'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import { Generator } from '@/app/components/base/icons/src/vender/other'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
|
||||
import { toast } from '@/app/components/base/ui/toast'
|
||||
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
|
||||
@ -11,10 +11,10 @@ import * as React from 'react'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import { Generator } from '@/app/components/base/icons/src/vender/other'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import { toast } from '@/app/components/base/ui/toast'
|
||||
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
|
||||
@ -9,10 +9,10 @@ import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Checkbox from '@/app/components/base/checkbox'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback'
|
||||
import Input from '@/app/components/base/input'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import { toast } from '@/app/components/base/ui/toast'
|
||||
import AppsFull from '@/app/components/billing/apps-full-in-dialog'
|
||||
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
|
||||
|
||||
@ -5,7 +5,7 @@ import ActionButton from '@/app/components/base/action-button'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import InputsFormContent from '@/app/components/base/chat/chat-with-history/inputs-form/content'
|
||||
import RenameModal from '@/app/components/base/chat/chat-with-history/sidebar/rename-modal'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import { useChatWithHistoryContext } from './context'
|
||||
import MobileOperationDropdown from './header/mobile-operation-dropdown'
|
||||
import Operation from './header/operation'
|
||||
|
||||
@ -10,8 +10,8 @@ import ActionButton, { ActionButtonState } from '@/app/components/base/action-bu
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import ViewFormDropdown from '@/app/components/base/chat/chat-with-history/inputs-form/view-form-dropdown'
|
||||
import RenameModal from '@/app/components/base/chat/chat-with-history/sidebar/rename-modal'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import {
|
||||
useChatWithHistoryContext,
|
||||
|
||||
@ -106,22 +106,6 @@ vi.mock('@/app/components/base/modal', () => ({
|
||||
},
|
||||
}))
|
||||
|
||||
// Mock Confirm
|
||||
vi.mock('@/app/components/base/confirm', () => ({
|
||||
default: ({ onCancel, onConfirm, title, content, isShow }: { onCancel: () => void, onConfirm: () => void, title: string, content?: React.ReactNode, isShow: boolean }) => {
|
||||
if (!isShow)
|
||||
return null
|
||||
return (
|
||||
<div data-testid="confirm-dialog">
|
||||
<div data-testid="confirm-title">{title}</div>
|
||||
<button data-testid="confirm-cancel" onClick={onCancel}>Cancel</button>
|
||||
<div data-testid="confirm-content">{content}</div>
|
||||
<button data-testid="confirm-confirm" onClick={onConfirm}>Confirm</button>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
}))
|
||||
|
||||
describe('Sidebar Index', () => {
|
||||
const mockContextValue = {
|
||||
isInstalledApp: false,
|
||||
@ -475,8 +459,9 @@ describe('Sidebar Index', () => {
|
||||
render(<Sidebar />)
|
||||
|
||||
await user.click(screen.getByTestId('delete-1'))
|
||||
expect(screen.getByTestId('confirm-dialog')).toBeInTheDocument()
|
||||
expect(screen.getByTestId('confirm-title')).toBeInTheDocument()
|
||||
const dialog = screen.getByRole('alertdialog')
|
||||
expect(dialog).toBeInTheDocument()
|
||||
expect(within(dialog).getByText('share.chat.deleteConversation.title')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should call handleDeleteConversation when confirm is clicked', async () => {
|
||||
@ -490,7 +475,8 @@ describe('Sidebar Index', () => {
|
||||
render(<Sidebar />)
|
||||
|
||||
await user.click(screen.getByTestId('delete-1'))
|
||||
await user.click(screen.getByTestId('confirm-confirm'))
|
||||
const dialog = screen.getByRole('alertdialog')
|
||||
await user.click(within(dialog).getAllByRole('button').at(-1)!)
|
||||
|
||||
expect(handleDeleteConversation).toHaveBeenCalledWith('1', expect.objectContaining({
|
||||
onSuccess: expect.any(Function),
|
||||
@ -502,11 +488,12 @@ describe('Sidebar Index', () => {
|
||||
render(<Sidebar />)
|
||||
|
||||
await user.click(screen.getByTestId('delete-1'))
|
||||
expect(screen.getByTestId('confirm-dialog')).toBeInTheDocument()
|
||||
const dialog = screen.getByRole('alertdialog')
|
||||
expect(dialog).toBeInTheDocument()
|
||||
|
||||
await user.click(screen.getByTestId('confirm-cancel'))
|
||||
await user.click(within(dialog).getByRole('button', { name: 'common.operation.cancel' }))
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId('confirm-dialog')).not.toBeInTheDocument()
|
||||
expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
@ -525,7 +512,8 @@ describe('Sidebar Index', () => {
|
||||
render(<Sidebar />)
|
||||
|
||||
await user.click(screen.getByTestId('delete-1'))
|
||||
await user.click(screen.getByTestId('confirm-confirm'))
|
||||
const dialog = screen.getByRole('alertdialog')
|
||||
await user.click(within(dialog).getAllByRole('button').at(-1)!)
|
||||
|
||||
expect(handleDeleteConversation).toHaveBeenCalledWith('1', expect.any(Object))
|
||||
})
|
||||
@ -837,7 +825,8 @@ describe('Sidebar Index', () => {
|
||||
|
||||
// Delete it
|
||||
await user.click(screen.getByTestId('delete-1'))
|
||||
await user.click(screen.getByTestId('confirm-confirm'))
|
||||
const dialog = await screen.findByRole('alertdialog')
|
||||
await user.click(within(dialog).getByRole('button', { name: 'common.operation.confirm' }))
|
||||
expect(handleDeleteConversation).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
@ -901,8 +890,9 @@ describe('Sidebar Index', () => {
|
||||
try {
|
||||
render(<Sidebar />)
|
||||
await user.click(screen.getByTestId('delete-1'))
|
||||
expect(screen.getByTestId('confirm-dialog')).toBeInTheDocument()
|
||||
expect(screen.getByTestId('confirm-content')).toBeEmptyDOMElement()
|
||||
const dialog = screen.getByRole('alertdialog')
|
||||
expect(dialog).toBeInTheDocument()
|
||||
expect(dialog).not.toHaveTextContent('share.chat.deleteConversation.content')
|
||||
}
|
||||
finally {
|
||||
useTranslationSpy.mockRestore()
|
||||
|
||||
@ -14,8 +14,8 @@ import AppIcon from '@/app/components/base/app-icon'
|
||||
import Button from '@/app/components/base/button'
|
||||
import List from '@/app/components/base/chat/chat-with-history/sidebar/list'
|
||||
import RenameModal from '@/app/components/base/chat/chat-with-history/sidebar/rename-modal'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import DifyLogo from '@/app/components/base/logo/dify-logo'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import MenuDropdown from '@/app/components/share/text-generation/menu-dropdown'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
@ -1,117 +0,0 @@
|
||||
import { act, fireEvent, render, screen } from '@testing-library/react'
|
||||
import Confirm from '..'
|
||||
|
||||
vi.mock('react-dom', async () => {
|
||||
const actual = await vi.importActual<typeof import('react-dom')>('react-dom')
|
||||
|
||||
return {
|
||||
...actual,
|
||||
createPortal: (children: React.ReactNode) => children,
|
||||
}
|
||||
})
|
||||
|
||||
const onCancel = vi.fn()
|
||||
const onConfirm = vi.fn()
|
||||
|
||||
describe('Confirm Component', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('Rendering', () => {
|
||||
it('renders confirm correctly', () => {
|
||||
render(<Confirm isShow={true} title="test title" onCancel={onCancel} onConfirm={onConfirm} />)
|
||||
expect(screen.getByText('test title')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('does not render on isShow false', () => {
|
||||
const { container } = render(<Confirm isShow={false} title="test title" onCancel={onCancel} onConfirm={onConfirm} />)
|
||||
expect(container.firstChild).toBeNull()
|
||||
})
|
||||
|
||||
it('hides after delay when isShow changes to false', () => {
|
||||
vi.useFakeTimers()
|
||||
const { rerender } = render(<Confirm isShow={true} title="test title" onCancel={onCancel} onConfirm={onConfirm} />)
|
||||
expect(screen.getByText('test title')).toBeInTheDocument()
|
||||
|
||||
rerender(<Confirm isShow={false} title="test title" onCancel={onCancel} onConfirm={onConfirm} />)
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(200)
|
||||
})
|
||||
expect(screen.queryByText('test title')).not.toBeInTheDocument()
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
it('renders content when provided', () => {
|
||||
render(<Confirm isShow={true} title="title" content="some description" onCancel={onCancel} onConfirm={onConfirm} />)
|
||||
expect(screen.getByText('some description')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Props', () => {
|
||||
it('showCancel prop works', () => {
|
||||
render(<Confirm isShow={true} title="test title" onCancel={onCancel} onConfirm={onConfirm} showCancel={false} />)
|
||||
expect(screen.getByRole('button', { name: 'common.operation.confirm' })).toBeInTheDocument()
|
||||
expect(screen.queryByRole('button', { name: 'common.operation.cancel' })).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('showConfirm prop works', () => {
|
||||
render(<Confirm isShow={true} title="test title" onCancel={onCancel} onConfirm={onConfirm} showConfirm={false} />)
|
||||
expect(screen.getByRole('button', { name: 'common.operation.cancel' })).toBeInTheDocument()
|
||||
expect(screen.queryByRole('button', { name: 'common.operation.confirm' })).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('renders custom confirm and cancel text', () => {
|
||||
render(<Confirm isShow={true} title="title" confirmText="Yes" cancelText="No" onCancel={onCancel} onConfirm={onConfirm} />)
|
||||
expect(screen.getByRole('button', { name: 'Yes' })).toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: 'No' })).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('disables confirm button when isDisabled is true', () => {
|
||||
render(<Confirm isShow={true} title="title" isDisabled={true} onCancel={onCancel} onConfirm={onConfirm} />)
|
||||
expect(screen.getByRole('button', { name: 'common.operation.confirm' })).toBeDisabled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('User Interactions', () => {
|
||||
it('clickAway is handled properly', () => {
|
||||
render(<Confirm isShow={true} title="test title" onCancel={onCancel} onConfirm={onConfirm} />)
|
||||
const overlay = screen.getByTestId('confirm-overlay') as HTMLElement
|
||||
expect(overlay).toBeTruthy()
|
||||
fireEvent.mouseDown(overlay)
|
||||
expect(onCancel).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('overlay click stops propagation', () => {
|
||||
render(<Confirm isShow={true} title="test title" onCancel={onCancel} onConfirm={onConfirm} />)
|
||||
const overlay = screen.getByTestId('confirm-overlay') as HTMLElement
|
||||
const clickEvent = new MouseEvent('click', { bubbles: true, cancelable: true })
|
||||
const preventDefaultSpy = vi.spyOn(clickEvent, 'preventDefault')
|
||||
const stopPropagationSpy = vi.spyOn(clickEvent, 'stopPropagation')
|
||||
overlay.dispatchEvent(clickEvent)
|
||||
expect(preventDefaultSpy).toHaveBeenCalled()
|
||||
expect(stopPropagationSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('does not close on click away when maskClosable is false', () => {
|
||||
render(<Confirm isShow={true} title="test title" maskClosable={false} onCancel={onCancel} onConfirm={onConfirm} />)
|
||||
const overlay = screen.getByTestId('confirm-overlay') as HTMLElement
|
||||
fireEvent.mouseDown(overlay)
|
||||
expect(onCancel).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('escape keyboard event works', () => {
|
||||
render(<Confirm isShow={true} title="test title" onCancel={onCancel} onConfirm={onConfirm} />)
|
||||
fireEvent.keyDown(document, { key: 'Escape' })
|
||||
expect(onCancel).toHaveBeenCalledTimes(1)
|
||||
expect(onConfirm).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('Enter keyboard event works', () => {
|
||||
render(<Confirm isShow={true} title="test title" onCancel={onCancel} onConfirm={onConfirm} />)
|
||||
fireEvent.keyDown(document, { key: 'Enter' })
|
||||
expect(onConfirm).toHaveBeenCalledTimes(1)
|
||||
expect(onCancel).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1,216 +0,0 @@
|
||||
import type { Meta, StoryObj } from '@storybook/nextjs-vite'
|
||||
import { useState } from 'react'
|
||||
import Confirm from '.'
|
||||
import Button from '../button'
|
||||
|
||||
const meta = {
|
||||
title: 'Base/Feedback/Confirm',
|
||||
component: Confirm,
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
docs: {
|
||||
description: {
|
||||
component: 'Confirmation dialog component that supports warning and info types, with customizable button text and behavior.',
|
||||
},
|
||||
},
|
||||
},
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
type: {
|
||||
control: 'select',
|
||||
options: ['info', 'warning'],
|
||||
description: 'Dialog type',
|
||||
},
|
||||
isShow: {
|
||||
control: 'boolean',
|
||||
description: 'Whether to show the dialog',
|
||||
},
|
||||
title: {
|
||||
control: 'text',
|
||||
description: 'Dialog title',
|
||||
},
|
||||
content: {
|
||||
control: 'text',
|
||||
description: 'Dialog content',
|
||||
},
|
||||
confirmText: {
|
||||
control: 'text',
|
||||
description: 'Confirm button text',
|
||||
},
|
||||
cancelText: {
|
||||
control: 'text',
|
||||
description: 'Cancel button text',
|
||||
},
|
||||
isLoading: {
|
||||
control: 'boolean',
|
||||
description: 'Confirm button loading state',
|
||||
},
|
||||
isDisabled: {
|
||||
control: 'boolean',
|
||||
description: 'Confirm button disabled state',
|
||||
},
|
||||
showConfirm: {
|
||||
control: 'boolean',
|
||||
description: 'Whether to show confirm button',
|
||||
},
|
||||
showCancel: {
|
||||
control: 'boolean',
|
||||
description: 'Whether to show cancel button',
|
||||
},
|
||||
maskClosable: {
|
||||
control: 'boolean',
|
||||
description: 'Whether clicking mask closes dialog',
|
||||
},
|
||||
},
|
||||
args: {
|
||||
onConfirm: () => {
|
||||
console.log('✅ User clicked confirm')
|
||||
},
|
||||
onCancel: () => {
|
||||
console.log('❌ User clicked cancel')
|
||||
},
|
||||
},
|
||||
} satisfies Meta<typeof Confirm>
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
// Interactive demo wrapper
|
||||
const ConfirmDemo = (args: any) => {
|
||||
const [isShow, setIsShow] = useState(false)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Button variant="primary" onClick={() => setIsShow(true)}>
|
||||
Open Dialog
|
||||
</Button>
|
||||
<Confirm
|
||||
{...args}
|
||||
isShow={isShow}
|
||||
onConfirm={() => {
|
||||
console.log('✅ User clicked confirm')
|
||||
setIsShow(false)
|
||||
}}
|
||||
onCancel={() => {
|
||||
console.log('❌ User clicked cancel')
|
||||
setIsShow(false)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Basic warning dialog - Delete action
|
||||
export const WarningDialog: Story = {
|
||||
render: args => <ConfirmDemo {...args} />,
|
||||
args: {
|
||||
type: 'warning',
|
||||
title: 'Delete Confirmation',
|
||||
content: 'Are you sure you want to delete this project? This action cannot be undone.',
|
||||
isShow: false,
|
||||
},
|
||||
}
|
||||
|
||||
// Info dialog
|
||||
export const InfoDialog: Story = {
|
||||
render: args => <ConfirmDemo {...args} />,
|
||||
args: {
|
||||
type: 'info',
|
||||
title: 'Notice',
|
||||
content: 'Your changes have been saved. Do you want to proceed to the next step?',
|
||||
isShow: false,
|
||||
},
|
||||
}
|
||||
|
||||
// Custom button text
|
||||
export const CustomButtonText: Story = {
|
||||
render: args => <ConfirmDemo {...args} />,
|
||||
args: {
|
||||
type: 'warning',
|
||||
title: 'Exit Editor',
|
||||
content: 'You have unsaved changes. Are you sure you want to exit?',
|
||||
confirmText: 'Discard Changes',
|
||||
cancelText: 'Continue Editing',
|
||||
isShow: false,
|
||||
},
|
||||
}
|
||||
|
||||
// Loading state
|
||||
export const LoadingState: Story = {
|
||||
render: args => <ConfirmDemo {...args} />,
|
||||
args: {
|
||||
type: 'warning',
|
||||
title: 'Deleting...',
|
||||
content: 'Please wait while we delete the file...',
|
||||
isLoading: true,
|
||||
isShow: false,
|
||||
},
|
||||
}
|
||||
|
||||
// Disabled state
|
||||
export const DisabledState: Story = {
|
||||
render: args => <ConfirmDemo {...args} />,
|
||||
args: {
|
||||
type: 'info',
|
||||
title: 'Verification Required',
|
||||
content: 'Please complete email verification before proceeding.',
|
||||
isDisabled: true,
|
||||
isShow: false,
|
||||
},
|
||||
}
|
||||
|
||||
// Alert style - Confirm button only
|
||||
export const AlertStyle: Story = {
|
||||
render: args => <ConfirmDemo {...args} />,
|
||||
args: {
|
||||
type: 'info',
|
||||
title: 'Success',
|
||||
content: 'Your settings have been updated!',
|
||||
showCancel: false,
|
||||
confirmText: 'Got it',
|
||||
isShow: false,
|
||||
},
|
||||
}
|
||||
|
||||
// Dangerous action - Long content
|
||||
export const DangerousAction: Story = {
|
||||
render: args => <ConfirmDemo {...args} />,
|
||||
args: {
|
||||
type: 'warning',
|
||||
title: 'Permanently Delete Account',
|
||||
content: 'This action will permanently delete your account and all associated data, including: all projects and files, collaboration history, and personal settings. This action cannot be reversed!',
|
||||
confirmText: 'Delete My Account',
|
||||
cancelText: 'Keep My Account',
|
||||
isShow: false,
|
||||
},
|
||||
}
|
||||
|
||||
// Non-closable mask
|
||||
export const NotMaskClosable: Story = {
|
||||
render: args => <ConfirmDemo {...args} />,
|
||||
args: {
|
||||
type: 'warning',
|
||||
title: 'Important Action',
|
||||
content: 'This action requires your explicit choice. Clicking outside will not close this dialog.',
|
||||
maskClosable: false,
|
||||
isShow: false,
|
||||
},
|
||||
}
|
||||
|
||||
// Full feature demo - Playground
|
||||
export const Playground: Story = {
|
||||
render: args => <ConfirmDemo {...args} />,
|
||||
args: {
|
||||
type: 'warning',
|
||||
title: 'This is a title',
|
||||
content: 'This is the dialog content text...',
|
||||
confirmText: undefined,
|
||||
cancelText: undefined,
|
||||
isLoading: false,
|
||||
isDisabled: false,
|
||||
showConfirm: true,
|
||||
showCancel: true,
|
||||
maskClosable: true,
|
||||
isShow: false,
|
||||
},
|
||||
}
|
||||
@ -1,164 +0,0 @@
|
||||
/**
|
||||
* @deprecated Use `@/app/components/base/ui/alert-dialog` instead.
|
||||
* See issue #32767 for migration details.
|
||||
*/
|
||||
|
||||
import * as React from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '../button'
|
||||
import Tooltip from '../tooltip'
|
||||
|
||||
/** @deprecated Use `@/app/components/base/ui/alert-dialog` instead. */
|
||||
export type IConfirm = {
|
||||
className?: string
|
||||
isShow: boolean
|
||||
type?: 'info' | 'warning' | 'danger'
|
||||
title: string
|
||||
content?: React.ReactNode
|
||||
confirmText?: string | null
|
||||
onConfirm: () => void
|
||||
cancelText?: string
|
||||
onCancel: () => void
|
||||
isLoading?: boolean
|
||||
isDisabled?: boolean
|
||||
showConfirm?: boolean
|
||||
showCancel?: boolean
|
||||
maskClosable?: boolean
|
||||
confirmInputLabel?: string
|
||||
confirmInputPlaceholder?: string
|
||||
confirmInputValue?: string
|
||||
onConfirmInputChange?: (value: string) => void
|
||||
confirmInputMatchValue?: string
|
||||
}
|
||||
|
||||
function Confirm({
|
||||
isShow,
|
||||
type = 'warning',
|
||||
title,
|
||||
content,
|
||||
confirmText,
|
||||
cancelText,
|
||||
onConfirm,
|
||||
onCancel,
|
||||
showConfirm = true,
|
||||
showCancel = true,
|
||||
isLoading = false,
|
||||
isDisabled = false,
|
||||
maskClosable = true,
|
||||
confirmInputLabel,
|
||||
confirmInputPlaceholder,
|
||||
confirmInputValue = '',
|
||||
onConfirmInputChange,
|
||||
confirmInputMatchValue,
|
||||
}: IConfirm) {
|
||||
const { t } = useTranslation()
|
||||
const dialogRef = useRef<HTMLDivElement>(null)
|
||||
const titleRef = useRef<HTMLDivElement>(null)
|
||||
const [isVisible, setIsVisible] = useState(isShow)
|
||||
const [isTitleTruncated, setIsTitleTruncated] = useState(false)
|
||||
|
||||
const confirmTxt = confirmText || `${t('operation.confirm', { ns: 'common' })}`
|
||||
const cancelTxt = cancelText || `${t('operation.cancel', { ns: 'common' })}`
|
||||
const isConfirmDisabled = isDisabled || (confirmInputMatchValue ? confirmInputValue !== confirmInputMatchValue : false)
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape')
|
||||
onCancel()
|
||||
if (event.key === 'Enter' && isShow && !isConfirmDisabled) {
|
||||
event.preventDefault()
|
||||
onConfirm()
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', handleKeyDown)
|
||||
return () => {
|
||||
document.removeEventListener('keydown', handleKeyDown)
|
||||
}
|
||||
}, [onCancel, onConfirm, isShow, isConfirmDisabled])
|
||||
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (maskClosable && dialogRef.current && !dialogRef.current.contains(event.target as Node))
|
||||
onCancel()
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('mousedown', handleClickOutside)
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside)
|
||||
}
|
||||
}, [maskClosable])
|
||||
|
||||
useEffect(() => {
|
||||
if (isShow) {
|
||||
setIsVisible(true)
|
||||
}
|
||||
else {
|
||||
const timer = setTimeout(() => setIsVisible(false), 200)
|
||||
return () => clearTimeout(timer)
|
||||
}
|
||||
}, [isShow])
|
||||
|
||||
useEffect(() => {
|
||||
if (titleRef.current) {
|
||||
const isOverflowing = titleRef.current.scrollWidth > titleRef.current.clientWidth
|
||||
setIsTitleTruncated(isOverflowing)
|
||||
}
|
||||
}, [title, isVisible])
|
||||
|
||||
if (!isVisible)
|
||||
return null
|
||||
|
||||
return createPortal(
|
||||
<div
|
||||
className="fixed inset-0 z-10000000 flex items-center justify-center bg-background-overlay"
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}}
|
||||
data-testid="confirm-overlay"
|
||||
>
|
||||
<div ref={dialogRef} className="relative w-full max-w-[480px] overflow-hidden">
|
||||
<div className="shadows-shadow-lg flex max-w-full flex-col items-start rounded-2xl border-[0.5px] border-solid border-components-panel-border bg-components-panel-bg">
|
||||
<div className="flex flex-col items-start gap-2 self-stretch pb-4 pl-6 pr-6 pt-6">
|
||||
<Tooltip
|
||||
popupContent={title}
|
||||
disabled={!isTitleTruncated}
|
||||
portalContentClassName="z-10000001!"
|
||||
asChild={false}
|
||||
triggerClassName="w-full"
|
||||
>
|
||||
<div ref={titleRef} className="title-2xl-semi-bold w-full truncate text-text-primary">
|
||||
{title}
|
||||
</div>
|
||||
</Tooltip>
|
||||
<div className="w-full whitespace-pre-wrap wrap-break-word text-text-tertiary system-md-regular">{content}</div>
|
||||
{confirmInputLabel && (
|
||||
<div className="mt-2">
|
||||
<label className="mb-1 block text-text-secondary system-sm-regular">
|
||||
{confirmInputLabel}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className="border-components-input-border bg-components-input-bg focus:border-components-input-border-focus focus:ring-components-input-border-focus h-9 w-full rounded-lg border px-3 text-sm text-text-primary placeholder:text-text-quaternary focus:outline-hidden focus:ring-1"
|
||||
placeholder={confirmInputPlaceholder}
|
||||
value={confirmInputValue}
|
||||
onChange={e => onConfirmInputChange?.(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-start justify-end gap-2 self-stretch p-6">
|
||||
{showCancel && <Button onClick={onCancel}>{cancelTxt}</Button>}
|
||||
{showConfirm && <Button variant="primary" destructive={type !== 'info'} loading={isLoading} disabled={isConfirmDisabled} onClick={onConfirm}>{confirmTxt}</Button>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
document.body,
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(Confirm)
|
||||
@ -3,8 +3,8 @@ import type { Tag } from '@/app/components/base/tag-management/constant'
|
||||
import { useDebounceFn } from 'ahooks'
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import { toast } from '@/app/components/base/ui/toast'
|
||||
import { deleteTag, updateTag } from '@/service/tag'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
@ -16,8 +16,8 @@ type AlertDialogContentProps = {
|
||||
children: React.ReactNode
|
||||
className?: string
|
||||
overlayClassName?: string
|
||||
popupProps?: Omit<React.ComponentPropsWithoutRef<typeof BaseAlertDialog.Popup>, 'children' | 'className'>
|
||||
backdropProps?: Omit<React.ComponentPropsWithoutRef<typeof BaseAlertDialog.Backdrop>, 'className'>
|
||||
popupProps?: Omit<React.ComponentPropsWithoutRef<typeof BaseAlertDialog.Popup>, 'children' | 'className'> & React.ComponentPropsWithoutRef<'div'>
|
||||
backdropProps?: Omit<React.ComponentPropsWithoutRef<typeof BaseAlertDialog.Backdrop>, 'className'> & React.ComponentPropsWithoutRef<'div'>
|
||||
}
|
||||
|
||||
export function AlertDialogContent({
|
||||
|
||||
@ -0,0 +1,149 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import Confirm from '..'
|
||||
|
||||
const onCancel = vi.fn()
|
||||
const onConfirm = vi.fn()
|
||||
const getOverlay = () => {
|
||||
const overlay = document.querySelector('.confirm-dialog-overlay')
|
||||
if (!overlay)
|
||||
throw new Error('Expected confirm dialog overlay to be rendered')
|
||||
|
||||
return overlay
|
||||
}
|
||||
|
||||
describe('ConfirmDialog wrapper', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
// Rendering scenarios
|
||||
describe('Rendering', () => {
|
||||
it('should render confirm correctly when isShow is true', async () => {
|
||||
render(<Confirm isShow title="test title" onCancel={onCancel} onConfirm={onConfirm} />)
|
||||
|
||||
expect(await screen.findByRole('alertdialog')).toBeInTheDocument()
|
||||
expect(screen.getByText('test title')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should not render when isShow is false', () => {
|
||||
render(<Confirm isShow={false} title="test title" onCancel={onCancel} onConfirm={onConfirm} />)
|
||||
|
||||
expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render content when provided', async () => {
|
||||
render(<Confirm isShow title="title" content="some description" onCancel={onCancel} onConfirm={onConfirm} />)
|
||||
|
||||
expect(await screen.findByText('some description')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
// Prop-driven rendering and state
|
||||
describe('Props', () => {
|
||||
it('should hide cancel button when showCancel is false', async () => {
|
||||
render(<Confirm isShow title="test title" onCancel={onCancel} onConfirm={onConfirm} showCancel={false} />)
|
||||
|
||||
expect(await screen.findByRole('button', { name: 'common.operation.confirm' })).toBeInTheDocument()
|
||||
expect(screen.queryByRole('button', { name: 'common.operation.cancel' })).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should hide confirm button when showConfirm is false', async () => {
|
||||
render(<Confirm isShow title="test title" onCancel={onCancel} onConfirm={onConfirm} showConfirm={false} />)
|
||||
|
||||
expect(await screen.findByRole('button', { name: 'common.operation.cancel' })).toBeInTheDocument()
|
||||
expect(screen.queryByRole('button', { name: 'common.operation.confirm' })).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render custom confirm and cancel text', async () => {
|
||||
render(<Confirm isShow title="title" confirmText="Yes" cancelText="No" onCancel={onCancel} onConfirm={onConfirm} />)
|
||||
|
||||
expect(await screen.findByRole('button', { name: 'Yes' })).toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: 'No' })).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should disable confirm button when isDisabled is true', async () => {
|
||||
render(<Confirm isShow title="title" isDisabled={true} onCancel={onCancel} onConfirm={onConfirm} />)
|
||||
|
||||
expect(await screen.findByRole('button', { name: 'common.operation.confirm' })).toBeDisabled()
|
||||
})
|
||||
|
||||
it('should keep cancel button enabled when confirm button is loading', async () => {
|
||||
render(<Confirm isShow title="title" isLoading={true} onCancel={onCancel} onConfirm={onConfirm} />)
|
||||
|
||||
expect(await screen.findByRole('button', { name: 'common.operation.cancel' })).toBeEnabled()
|
||||
expect(screen.getByRole('button', { name: /common\.operation\.confirm/i })).toBeDisabled()
|
||||
})
|
||||
|
||||
it('should disable confirm button until confirm input matches expected value', async () => {
|
||||
render(
|
||||
<Confirm
|
||||
isShow
|
||||
title="title"
|
||||
confirmInputLabel="Type DELETE to continue"
|
||||
confirmInputPlaceholder="DELETE"
|
||||
confirmInputValue="DEL"
|
||||
confirmInputMatchValue="DELETE"
|
||||
onCancel={onCancel}
|
||||
onConfirm={onConfirm}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(await screen.findByRole('button', { name: 'common.operation.confirm' })).toBeDisabled()
|
||||
expect(screen.getByLabelText('Type DELETE to continue')).toHaveValue('DEL')
|
||||
})
|
||||
})
|
||||
|
||||
// User interactions
|
||||
describe('User Interactions', () => {
|
||||
it('should call onCancel when clicking the backdrop', async () => {
|
||||
render(<Confirm isShow title="test title" onCancel={onCancel} onConfirm={onConfirm} />)
|
||||
|
||||
const overlay = getOverlay()
|
||||
fireEvent.mouseDown(overlay)
|
||||
|
||||
expect(onCancel).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('should stop propagation on backdrop click', async () => {
|
||||
render(<Confirm isShow title="test title" onCancel={onCancel} onConfirm={onConfirm} />)
|
||||
|
||||
const overlay = getOverlay()
|
||||
const clickEvent = new MouseEvent('click', { bubbles: true, cancelable: true })
|
||||
const preventDefaultSpy = vi.spyOn(clickEvent, 'preventDefault')
|
||||
const stopPropagationSpy = vi.spyOn(clickEvent, 'stopPropagation')
|
||||
|
||||
overlay.dispatchEvent(clickEvent)
|
||||
|
||||
expect(preventDefaultSpy).toHaveBeenCalled()
|
||||
expect(stopPropagationSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not close on click away when maskClosable is false', async () => {
|
||||
render(<Confirm isShow title="test title" maskClosable={false} onCancel={onCancel} onConfirm={onConfirm} />)
|
||||
|
||||
const overlay = getOverlay()
|
||||
fireEvent.mouseDown(overlay)
|
||||
|
||||
expect(onCancel).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should call onCancel when Escape key is pressed', () => {
|
||||
render(<Confirm isShow title="test title" onCancel={onCancel} onConfirm={onConfirm} />)
|
||||
|
||||
fireEvent.keyDown(document, { key: 'Escape' })
|
||||
|
||||
expect(onCancel).toHaveBeenCalledTimes(1)
|
||||
expect(onConfirm).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should call onConfirm when Enter key is pressed', () => {
|
||||
render(<Confirm isShow title="test title" onCancel={onCancel} onConfirm={onConfirm} />)
|
||||
|
||||
fireEvent.keyDown(document, { key: 'Enter' })
|
||||
|
||||
expect(onConfirm).toHaveBeenCalledTimes(1)
|
||||
expect(onCancel).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
154
web/app/components/base/ui/confirm-dialog/index.tsx
Normal file
154
web/app/components/base/ui/confirm-dialog/index.tsx
Normal file
@ -0,0 +1,154 @@
|
||||
'use client'
|
||||
|
||||
import * as React from 'react'
|
||||
import { useEffect, useId } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Input from '@/app/components/base/input'
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogActions,
|
||||
AlertDialogCancelButton,
|
||||
AlertDialogConfirmButton,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogTitle,
|
||||
} from '@/app/components/base/ui/alert-dialog'
|
||||
|
||||
export type IConfirm = {
|
||||
className?: string
|
||||
isShow: boolean
|
||||
type?: 'info' | 'warning' | 'danger'
|
||||
title: string
|
||||
content?: React.ReactNode
|
||||
confirmText?: string | null
|
||||
onConfirm: () => void
|
||||
cancelText?: string
|
||||
onCancel: () => void
|
||||
isLoading?: boolean
|
||||
isDisabled?: boolean
|
||||
showConfirm?: boolean
|
||||
showCancel?: boolean
|
||||
maskClosable?: boolean
|
||||
confirmInputLabel?: string
|
||||
confirmInputPlaceholder?: string
|
||||
confirmInputValue?: string
|
||||
onConfirmInputChange?: (value: string) => void
|
||||
confirmInputMatchValue?: string
|
||||
}
|
||||
|
||||
function Confirm({
|
||||
className,
|
||||
isShow,
|
||||
type = 'warning',
|
||||
title,
|
||||
content,
|
||||
confirmText,
|
||||
cancelText,
|
||||
onConfirm,
|
||||
onCancel,
|
||||
showConfirm = true,
|
||||
showCancel = true,
|
||||
isLoading = false,
|
||||
isDisabled = false,
|
||||
maskClosable = true,
|
||||
confirmInputLabel,
|
||||
confirmInputPlaceholder,
|
||||
confirmInputValue = '',
|
||||
onConfirmInputChange,
|
||||
confirmInputMatchValue,
|
||||
}: IConfirm) {
|
||||
const { t } = useTranslation()
|
||||
const confirmInputId = useId()
|
||||
|
||||
const confirmTxt = confirmText || t('operation.confirm', { ns: 'common' })
|
||||
const cancelTxt = cancelText || t('operation.cancel', { ns: 'common' })
|
||||
const isConfirmDisabled = isDisabled || (confirmInputMatchValue ? confirmInputValue !== confirmInputMatchValue : false)
|
||||
|
||||
useEffect(() => {
|
||||
if (!isShow)
|
||||
return
|
||||
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Enter' && !isConfirmDisabled) {
|
||||
event.preventDefault()
|
||||
onConfirm()
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', handleKeyDown)
|
||||
return () => {
|
||||
document.removeEventListener('keydown', handleKeyDown)
|
||||
}
|
||||
}, [isConfirmDisabled, isShow, onConfirm])
|
||||
|
||||
return (
|
||||
<AlertDialog
|
||||
open={isShow}
|
||||
onOpenChange={(open) => {
|
||||
if (!open)
|
||||
onCancel()
|
||||
}}
|
||||
>
|
||||
<AlertDialogContent
|
||||
className={className}
|
||||
overlayClassName="confirm-dialog-overlay"
|
||||
backdropProps={{
|
||||
onClick: (event) => {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
},
|
||||
onMouseDown: () => {
|
||||
if (maskClosable)
|
||||
onCancel()
|
||||
},
|
||||
}}
|
||||
>
|
||||
<div className="flex flex-col gap-2 px-6 pt-6 pb-4">
|
||||
<AlertDialogTitle title={title} className="w-full truncate title-2xl-semi-bold text-text-primary">
|
||||
{title}
|
||||
</AlertDialogTitle>
|
||||
{content !== undefined && content !== null && (
|
||||
<AlertDialogDescription className="w-full system-md-regular wrap-break-word whitespace-pre-wrap text-text-tertiary">
|
||||
{content}
|
||||
</AlertDialogDescription>
|
||||
)}
|
||||
{confirmInputLabel && (
|
||||
<div className="mt-2">
|
||||
<label htmlFor={confirmInputId} className="mb-2 block system-sm-regular text-text-secondary">
|
||||
{confirmInputLabel}
|
||||
</label>
|
||||
<Input
|
||||
id={confirmInputId}
|
||||
value={confirmInputValue}
|
||||
placeholder={confirmInputPlaceholder}
|
||||
onChange={event => onConfirmInputChange?.(event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{(showCancel || showConfirm) && (
|
||||
<AlertDialogActions>
|
||||
{showCancel && (
|
||||
<AlertDialogCancelButton>
|
||||
{cancelTxt}
|
||||
</AlertDialogCancelButton>
|
||||
)}
|
||||
{showConfirm && (
|
||||
<AlertDialogConfirmButton
|
||||
variant="primary"
|
||||
destructive={type !== 'info'}
|
||||
loading={isLoading}
|
||||
disabled={isConfirmDisabled}
|
||||
onClick={onConfirm}
|
||||
>
|
||||
{confirmTxt}
|
||||
</AlertDialogConfirmButton>
|
||||
)}
|
||||
</AlertDialogActions>
|
||||
)}
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(Confirm)
|
||||
@ -1,6 +1,7 @@
|
||||
import type { PipelineTemplate } from '@/models/pipeline'
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import { fireEvent, render, screen, waitFor, within } from '@testing-library/react'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import * as toastModule from '@/app/components/base/ui/toast'
|
||||
import { ChunkingMode } from '@/models/datasets'
|
||||
import TemplateCard from '../index'
|
||||
|
||||
@ -14,21 +15,17 @@ vi.mock('@/app/components/base/amplitude', () => ({
|
||||
trackEvent: vi.fn(),
|
||||
}))
|
||||
|
||||
const { mockToastSuccess, mockToastError } = vi.hoisted(() => ({
|
||||
mockToastSuccess: vi.fn(),
|
||||
mockToastError: vi.fn(),
|
||||
}))
|
||||
const mockToastSuccess = vi.fn()
|
||||
const mockToastError = vi.fn()
|
||||
|
||||
vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('@/app/components/base/ui/toast')>()
|
||||
return {
|
||||
...actual,
|
||||
toast: {
|
||||
...actual.toast,
|
||||
success: mockToastSuccess,
|
||||
error: mockToastError,
|
||||
},
|
||||
}
|
||||
vi.spyOn(toastModule.toast, 'success').mockImplementation((...args) => {
|
||||
mockToastSuccess(...args)
|
||||
return 'toast-id'
|
||||
})
|
||||
|
||||
vi.spyOn(toastModule.toast, 'error').mockImplementation((...args) => {
|
||||
mockToastError(...args)
|
||||
return 'toast-id'
|
||||
})
|
||||
|
||||
// Mock download utilities
|
||||
@ -37,33 +34,6 @@ vi.mock('@/utils/download', () => ({
|
||||
downloadUrl: vi.fn(),
|
||||
}))
|
||||
|
||||
// Capture Confirm callbacks
|
||||
let _capturedOnConfirm: (() => void) | undefined
|
||||
let _capturedOnCancel: (() => void) | undefined
|
||||
|
||||
vi.mock('@/app/components/base/confirm', () => ({
|
||||
default: ({ isShow, onConfirm, onCancel, title, content }: {
|
||||
isShow: boolean
|
||||
onConfirm: () => void
|
||||
onCancel: () => void
|
||||
title: string
|
||||
content: string
|
||||
}) => {
|
||||
_capturedOnConfirm = onConfirm
|
||||
_capturedOnCancel = onCancel
|
||||
return isShow
|
||||
? (
|
||||
<div data-testid="confirm-dialog">
|
||||
<div data-testid="confirm-title">{title}</div>
|
||||
<div data-testid="confirm-content">{content}</div>
|
||||
<button data-testid="confirm-cancel" onClick={onCancel}>Cancel</button>
|
||||
<button data-testid="confirm-submit" onClick={onConfirm}>Confirm</button>
|
||||
</div>
|
||||
)
|
||||
: null
|
||||
},
|
||||
}))
|
||||
|
||||
// Capture Actions callbacks
|
||||
let _capturedHandleDelete: (() => void) | undefined
|
||||
let _capturedHandleExportDSL: (() => void) | undefined
|
||||
@ -187,8 +157,6 @@ describe('TemplateCard', () => {
|
||||
mockToastSuccess.mockReset()
|
||||
mockToastError.mockReset()
|
||||
mockIsExporting = false
|
||||
_capturedOnConfirm = undefined
|
||||
_capturedOnCancel = undefined
|
||||
_capturedHandleDelete = undefined
|
||||
_capturedHandleExportDSL = undefined
|
||||
_capturedOpenEditModal = undefined
|
||||
@ -507,7 +475,7 @@ describe('TemplateCard', () => {
|
||||
fireEvent.click(deleteButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('confirm-dialog')).toBeInTheDocument()
|
||||
expect(screen.getByRole('alertdialog')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
@ -517,14 +485,14 @@ describe('TemplateCard', () => {
|
||||
fireEvent.click(deleteButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('confirm-dialog')).toBeInTheDocument()
|
||||
expect(screen.getByRole('alertdialog')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
const cancelButton = screen.getByTestId('confirm-cancel')
|
||||
const cancelButton = within(screen.getByRole('alertdialog')).getByRole('button', { name: 'common.operation.cancel' })
|
||||
fireEvent.click(cancelButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId('confirm-dialog')).not.toBeInTheDocument()
|
||||
expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
@ -539,10 +507,10 @@ describe('TemplateCard', () => {
|
||||
fireEvent.click(deleteButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('confirm-dialog')).toBeInTheDocument()
|
||||
expect(screen.getByRole('alertdialog')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
const confirmButton = screen.getByTestId('confirm-submit')
|
||||
const confirmButton = within(screen.getByRole('alertdialog')).getAllByRole('button').at(-1)!
|
||||
fireEvent.click(confirmButton)
|
||||
|
||||
await waitFor(() => {
|
||||
@ -561,10 +529,10 @@ describe('TemplateCard', () => {
|
||||
fireEvent.click(deleteButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('confirm-dialog')).toBeInTheDocument()
|
||||
expect(screen.getByRole('alertdialog')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
const confirmButton = screen.getByTestId('confirm-submit')
|
||||
const confirmButton = within(screen.getByRole('alertdialog')).getAllByRole('button').at(-1)!
|
||||
fireEvent.click(confirmButton)
|
||||
|
||||
await waitFor(() => {
|
||||
@ -583,14 +551,14 @@ describe('TemplateCard', () => {
|
||||
fireEvent.click(deleteButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('confirm-dialog')).toBeInTheDocument()
|
||||
expect(screen.getByRole('alertdialog')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
const confirmButton = screen.getByTestId('confirm-submit')
|
||||
const confirmButton = within(screen.getByRole('alertdialog')).getAllByRole('button').at(-1)!
|
||||
fireEvent.click(confirmButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId('confirm-dialog')).not.toBeInTheDocument()
|
||||
expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -3,8 +3,8 @@ import * as React from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { trackEvent } from '@/app/components/base/amplitude'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import { toast } from '@/app/components/base/ui/toast'
|
||||
import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks'
|
||||
import { useRouter } from '@/next/navigation'
|
||||
|
||||
@ -7,12 +7,12 @@ import { noop } from 'es-toolkit/function'
|
||||
import * as React from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import { SearchLinesSparkle } from '@/app/components/base/icons/src/vender/knowledge'
|
||||
import CustomPopover from '@/app/components/base/popover'
|
||||
import Switch from '@/app/components/base/switch'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import { toast } from '@/app/components/base/ui/toast'
|
||||
import { IS_CE_EDITION } from '@/config'
|
||||
import { DataSourceType, DocumentActionType } from '@/models/datasets'
|
||||
|
||||
@ -4,9 +4,9 @@ import { useBoolean } from 'ahooks'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import { SearchLinesSparkle } from '@/app/components/base/icons/src/vender/knowledge'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import { IS_CE_EDITION } from '@/config'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
|
||||
@ -5,10 +5,10 @@ import * as React from 'react'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Badge from '@/app/components/base/badge'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import Switch from '@/app/components/base/switch'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import ImageList from '@/app/components/datasets/common/image-list'
|
||||
import { ChunkingMode } from '@/models/datasets'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
@ -5,9 +5,9 @@ import { memo, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import { PortalToFollowElem, PortalToFollowElemContent } from '@/app/components/base/portal-to-follow-elem'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import { toast } from '@/app/components/base/ui/toast'
|
||||
import { createExternalAPI } from '@/service/datasets'
|
||||
import Form from './Form'
|
||||
|
||||
@ -8,8 +8,8 @@ import * as React from 'react'
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import { ApiConnectionMod } from '@/app/components/base/icons/src/vender/solid/development'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import { useExternalKnowledgeApi } from '@/context/external-knowledge-api-context'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import { checkUsageExternalAPI, deleteExternalAPI, fetchExternalAPI, updateExternalAPI } from '@/service/datasets'
|
||||
|
||||
@ -19,28 +19,6 @@ vi.mock('../../../../rename-modal', () => ({
|
||||
),
|
||||
}))
|
||||
|
||||
// Mock Confirm component since it uses createPortal which can cause issues in tests
|
||||
vi.mock('@/app/components/base/confirm', () => ({
|
||||
default: ({ isShow, title, content, onConfirm, onCancel }: {
|
||||
isShow: boolean
|
||||
title: string
|
||||
content?: React.ReactNode
|
||||
onConfirm: () => void
|
||||
onCancel: () => void
|
||||
}) => (
|
||||
isShow
|
||||
? (
|
||||
<div data-testid="confirm-modal">
|
||||
<div data-testid="confirm-title">{title}</div>
|
||||
<div data-testid="confirm-content">{content}</div>
|
||||
<button onClick={onCancel} role="button" aria-label="cancel">Cancel</button>
|
||||
<button onClick={onConfirm} role="button" aria-label="confirm">Confirm</button>
|
||||
</div>
|
||||
)
|
||||
: null
|
||||
),
|
||||
}))
|
||||
|
||||
describe('DatasetCardModals', () => {
|
||||
const mockDataset: DataSet = {
|
||||
id: 'dataset-1',
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { DataSet } from '@/models/datasets'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import RenameDatasetModal from '../../../rename-modal'
|
||||
|
||||
type ModalState = {
|
||||
|
||||
@ -7,12 +7,12 @@ import * as React from 'react'
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import Drawer from '@/app/components/base/drawer'
|
||||
import Input from '@/app/components/base/input'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Switch from '@/app/components/base/switch'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import { toast } from '@/app/components/base/ui/toast'
|
||||
import CreateModal from '@/app/components/datasets/metadata/metadata-dataset/create-metadata-modal'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
@ -8,10 +8,10 @@ import {
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import CopyFeedback from '@/app/components/base/copy-feedback'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import useTimestamp from '@/hooks/use-timestamp'
|
||||
import {
|
||||
|
||||
@ -105,7 +105,7 @@ describe('Item Component', () => {
|
||||
|
||||
// Act
|
||||
fireEvent.click(screen.getByText('common.operation.delete'))
|
||||
const dialog = screen.getByTestId('confirm-overlay')
|
||||
const dialog = screen.getByRole('alertdialog')
|
||||
const confirmButton = within(dialog).getByText('common.operation.delete')
|
||||
fireEvent.click(confirmButton)
|
||||
|
||||
@ -123,7 +123,7 @@ describe('Item Component', () => {
|
||||
|
||||
// Act
|
||||
fireEvent.click(screen.getByText('common.operation.delete'))
|
||||
const dialog = screen.getByTestId('confirm-overlay')
|
||||
const dialog = screen.getByRole('alertdialog')
|
||||
const confirmButton = within(dialog).getByText('common.operation.delete')
|
||||
fireEvent.click(confirmButton)
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ import {
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import { deleteApiBasedExtension } from '@/service/common'
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ import {
|
||||
useRef,
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import {
|
||||
ApiKeyModal,
|
||||
usePluginAuthAction,
|
||||
|
||||
@ -20,12 +20,12 @@ import {
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import {
|
||||
PortalToFollowElem,
|
||||
PortalToFollowElemContent,
|
||||
PortalToFollowElemTrigger,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { useAuth } from '../hooks'
|
||||
import AuthorizedItem from './authorized-item'
|
||||
|
||||
@ -2,9 +2,9 @@ import type { Credential, CustomConfigurationModelFixedFields, ModelItem, ModelL
|
||||
import { memo, useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import { toast } from '@/app/components/base/ui/toast'
|
||||
import { SwitchCredentialInLoadBalancing } from '@/app/components/header/account-setting/model-provider-page/model-auth'
|
||||
import { useGetModelCredential, useUpdateModelLoadBalancingConfig } from '@/service/use-models'
|
||||
|
||||
@ -13,12 +13,12 @@ import {
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import {
|
||||
PortalToFollowElem,
|
||||
PortalToFollowElemContent,
|
||||
PortalToFollowElemTrigger,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import { toast } from '@/app/components/base/ui/toast'
|
||||
import Indicator from '@/app/components/header/indicator'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
@ -6,10 +6,10 @@ import * as React from 'react'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import { CopyCheck } from '@/app/components/base/icons/src/vender/line/files'
|
||||
import Switch from '@/app/components/base/switch'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import { toast } from '@/app/components/base/ui/toast'
|
||||
import Indicator from '@/app/components/header/indicator'
|
||||
import { addDefaultValue, toolCredentialToFormSchemas } from '@/app/components/tools/utils/to-form-schema'
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import type { MetaData, PluginCategoryEnum } from '../../types'
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import { fireEvent, render, screen, waitFor, within } from '@testing-library/react'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import * as toastModule from '@/app/components/base/ui/toast'
|
||||
|
||||
// ==================== Imports (after mocks) ====================
|
||||
|
||||
@ -16,30 +17,19 @@ const {
|
||||
mockCheckForUpdates,
|
||||
mockSetShowUpdatePluginModal,
|
||||
mockInvalidateInstalledPluginList,
|
||||
mockToastNotify,
|
||||
} = vi.hoisted(() => ({
|
||||
mockUninstallPlugin: vi.fn(),
|
||||
mockFetchReleases: vi.fn(),
|
||||
mockCheckForUpdates: vi.fn(),
|
||||
mockSetShowUpdatePluginModal: vi.fn(),
|
||||
mockInvalidateInstalledPluginList: vi.fn(),
|
||||
mockToastNotify: vi.fn(),
|
||||
}))
|
||||
const mockToastNotify = vi.fn()
|
||||
|
||||
vi.mock('@/app/components/base/ui/toast', () => ({
|
||||
toast: Object.assign(
|
||||
(message: string, options?: { type?: string }) => mockToastNotify({ type: options?.type, message }),
|
||||
{
|
||||
success: (message: string) => mockToastNotify({ type: 'success', message }),
|
||||
error: (message: string) => mockToastNotify({ type: 'error', message }),
|
||||
warning: (message: string) => mockToastNotify({ type: 'warning', message }),
|
||||
info: (message: string) => mockToastNotify({ type: 'info', message }),
|
||||
dismiss: vi.fn(),
|
||||
update: vi.fn(),
|
||||
promise: vi.fn(),
|
||||
},
|
||||
),
|
||||
}))
|
||||
vi.spyOn(toastModule, 'toast').mockImplementation((message, options) => {
|
||||
mockToastNotify({ type: options?.type, message })
|
||||
return 'toast-id'
|
||||
})
|
||||
|
||||
// Mock uninstall plugin service
|
||||
vi.mock('@/service/plugins', () => ({
|
||||
@ -92,30 +82,6 @@ vi.mock('../../../base/tooltip', () => ({
|
||||
),
|
||||
}))
|
||||
|
||||
// Mock Confirm - uses createPortal which has issues in test environment
|
||||
vi.mock('../../../base/confirm', () => ({
|
||||
default: ({ isShow, title, content, onCancel, onConfirm, isLoading, isDisabled }: {
|
||||
isShow: boolean
|
||||
title: string
|
||||
content: React.ReactNode
|
||||
onCancel: () => void
|
||||
onConfirm: () => void
|
||||
isLoading: boolean
|
||||
isDisabled: boolean
|
||||
}) => {
|
||||
if (!isShow)
|
||||
return null
|
||||
return (
|
||||
<div data-testid="confirm-modal" data-loading={isLoading} data-disabled={isDisabled}>
|
||||
<div data-testid="confirm-title">{title}</div>
|
||||
<div data-testid="confirm-content">{content}</div>
|
||||
<button data-testid="confirm-cancel" onClick={onCancel}>Cancel</button>
|
||||
<button data-testid="confirm-ok" onClick={onConfirm} disabled={isDisabled}>Confirm</button>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
}))
|
||||
|
||||
// ==================== Test Utilities ====================
|
||||
|
||||
type ActionProps = {
|
||||
@ -151,6 +117,11 @@ const createActionProps = (overrides: Partial<ActionProps> = {}): ActionProps =>
|
||||
...overrides,
|
||||
})
|
||||
|
||||
const getConfirmDialog = () => screen.getByRole('alertdialog')
|
||||
const getConfirmButtons = () => within(getConfirmDialog()).getAllByRole('button')
|
||||
const getConfirmCancelButton = () => getConfirmButtons()[0]
|
||||
const getConfirmConfirmButton = () => getConfirmButtons().at(-1)!
|
||||
|
||||
// ==================== Tests ====================
|
||||
|
||||
// Helper to find action buttons (real ActionButton component uses type="button")
|
||||
@ -277,8 +248,8 @@ describe('Action Component', () => {
|
||||
fireEvent.click(getActionButtons()[0])
|
||||
|
||||
// Assert
|
||||
expect(screen.getByTestId('confirm-modal')).toBeInTheDocument()
|
||||
expect(screen.getByTestId('confirm-title')).toHaveTextContent('plugin.action.delete')
|
||||
expect(getConfirmDialog()).toBeInTheDocument()
|
||||
expect(screen.getByText('plugin.action.delete')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should display plugin name in delete confirm content', () => {
|
||||
@ -298,7 +269,7 @@ describe('Action Component', () => {
|
||||
expect(screen.getByText('my-awesome-plugin')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should hide confirm modal when cancel is clicked', () => {
|
||||
it('should hide confirm modal when cancel is clicked', async () => {
|
||||
// Arrange
|
||||
const props = createActionProps({
|
||||
isShowDelete: true,
|
||||
@ -309,12 +280,14 @@ describe('Action Component', () => {
|
||||
// Act
|
||||
render(<Action {...props} />)
|
||||
fireEvent.click(getActionButtons()[0])
|
||||
expect(screen.getByTestId('confirm-modal')).toBeInTheDocument()
|
||||
expect(getConfirmDialog()).toBeInTheDocument()
|
||||
|
||||
fireEvent.click(screen.getByTestId('confirm-cancel'))
|
||||
fireEvent.click(getConfirmCancelButton())
|
||||
|
||||
// Assert
|
||||
expect(screen.queryByTestId('confirm-modal')).not.toBeInTheDocument()
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it('should call uninstallPlugin when confirm is clicked', async () => {
|
||||
@ -329,7 +302,7 @@ describe('Action Component', () => {
|
||||
// Act
|
||||
render(<Action {...props} />)
|
||||
fireEvent.click(getActionButtons()[0])
|
||||
fireEvent.click(screen.getByTestId('confirm-ok'))
|
||||
fireEvent.click(getConfirmConfirmButton())
|
||||
|
||||
// Assert
|
||||
await waitFor(() => {
|
||||
@ -351,7 +324,7 @@ describe('Action Component', () => {
|
||||
// Act
|
||||
render(<Action {...props} />)
|
||||
fireEvent.click(getActionButtons()[0])
|
||||
fireEvent.click(screen.getByTestId('confirm-ok'))
|
||||
fireEvent.click(getConfirmConfirmButton())
|
||||
|
||||
// Assert
|
||||
await waitFor(() => {
|
||||
@ -373,7 +346,7 @@ describe('Action Component', () => {
|
||||
// Act
|
||||
render(<Action {...props} />)
|
||||
fireEvent.click(getActionButtons()[0])
|
||||
fireEvent.click(screen.getByTestId('confirm-ok'))
|
||||
fireEvent.click(getConfirmConfirmButton())
|
||||
|
||||
// Assert
|
||||
await waitFor(() => {
|
||||
@ -395,7 +368,7 @@ describe('Action Component', () => {
|
||||
// Act
|
||||
render(<Action {...props} />)
|
||||
fireEvent.click(getActionButtons()[0])
|
||||
fireEvent.click(screen.getByTestId('confirm-ok'))
|
||||
fireEvent.click(getConfirmConfirmButton())
|
||||
|
||||
// Assert
|
||||
await waitFor(() => {
|
||||
@ -422,17 +395,17 @@ describe('Action Component', () => {
|
||||
// Act
|
||||
render(<Action {...props} />)
|
||||
fireEvent.click(getActionButtons()[0])
|
||||
fireEvent.click(screen.getByTestId('confirm-ok'))
|
||||
fireEvent.click(getConfirmConfirmButton())
|
||||
|
||||
// Assert - Loading state
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('confirm-modal')).toHaveAttribute('data-loading', 'true')
|
||||
expect(getConfirmConfirmButton()).toHaveAttribute('aria-busy', 'true')
|
||||
})
|
||||
|
||||
// Resolve and check modal closes
|
||||
resolveUninstall!({ success: true })
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId('confirm-modal')).not.toBeInTheDocument()
|
||||
expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -699,7 +672,7 @@ describe('Action Component', () => {
|
||||
// Act - First render and delete
|
||||
const { rerender } = render(<Action {...props} />)
|
||||
fireEvent.click(getActionButtons()[0])
|
||||
fireEvent.click(screen.getByTestId('confirm-ok'))
|
||||
fireEvent.click(getConfirmConfirmButton())
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUninstallPlugin).toHaveBeenCalledWith('stable-install-id')
|
||||
@ -709,7 +682,7 @@ describe('Action Component', () => {
|
||||
mockUninstallPlugin.mockClear()
|
||||
rerender(<Action {...props} />)
|
||||
fireEvent.click(getActionButtons()[0])
|
||||
fireEvent.click(screen.getByTestId('confirm-ok'))
|
||||
fireEvent.click(getConfirmConfirmButton())
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUninstallPlugin).toHaveBeenCalledWith('stable-install-id')
|
||||
@ -735,7 +708,7 @@ describe('Action Component', () => {
|
||||
// Act
|
||||
const { rerender } = render(<Action {...props1} />)
|
||||
fireEvent.click(getActionButtons()[0])
|
||||
fireEvent.click(screen.getByTestId('confirm-ok'))
|
||||
fireEvent.click(getConfirmConfirmButton())
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUninstallPlugin).toHaveBeenCalledWith('install-1')
|
||||
@ -744,7 +717,7 @@ describe('Action Component', () => {
|
||||
mockUninstallPlugin.mockClear()
|
||||
rerender(<Action {...props2} />)
|
||||
fireEvent.click(getActionButtons()[0])
|
||||
fireEvent.click(screen.getByTestId('confirm-ok'))
|
||||
fireEvent.click(getConfirmConfirmButton())
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUninstallPlugin).toHaveBeenCalledWith('install-2')
|
||||
@ -772,7 +745,7 @@ describe('Action Component', () => {
|
||||
// Act
|
||||
const { rerender } = render(<Action {...props1} />)
|
||||
fireEvent.click(getActionButtons()[0])
|
||||
fireEvent.click(screen.getByTestId('confirm-ok'))
|
||||
fireEvent.click(getConfirmConfirmButton())
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onDelete1).toHaveBeenCalled()
|
||||
@ -781,7 +754,7 @@ describe('Action Component', () => {
|
||||
|
||||
rerender(<Action {...props2} />)
|
||||
fireEvent.click(getActionButtons()[0])
|
||||
fireEvent.click(screen.getByTestId('confirm-ok'))
|
||||
fireEvent.click(getConfirmConfirmButton())
|
||||
|
||||
await waitFor(() => {
|
||||
expect(onDelete2).toHaveBeenCalled()
|
||||
@ -847,17 +820,17 @@ describe('Action Component', () => {
|
||||
// Act
|
||||
render(<Action {...props} />)
|
||||
fireEvent.click(getActionButtons()[0])
|
||||
fireEvent.click(screen.getByTestId('confirm-ok'))
|
||||
fireEvent.click(getConfirmConfirmButton())
|
||||
|
||||
// The confirm button should be disabled during deletion
|
||||
expect(screen.getByTestId('confirm-modal')).toHaveAttribute('data-loading', 'true')
|
||||
expect(screen.getByTestId('confirm-modal')).toHaveAttribute('data-disabled', 'true')
|
||||
expect(getConfirmConfirmButton()).toHaveAttribute('aria-busy', 'true')
|
||||
expect(getConfirmConfirmButton()).toBeDisabled()
|
||||
|
||||
// Resolve the deletion
|
||||
resolveFirst!({ success: true })
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId('confirm-modal')).not.toBeInTheDocument()
|
||||
expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -12,8 +12,8 @@ import { useModalContext } from '@/context/modal-context'
|
||||
import { uninstallPlugin } from '@/service/plugins'
|
||||
import { useInvalidateInstalledPluginList } from '@/service/use-plugins'
|
||||
import ActionButton from '../../base/action-button'
|
||||
import Confirm from '../../base/confirm'
|
||||
import Tooltip from '../../base/tooltip'
|
||||
import Confirm from '../../base/ui/confirm-dialog'
|
||||
import { checkForUpdates, fetchReleases } from '../install-plugin/hooks'
|
||||
import PluginInfo from '../plugin-page/plugin-info'
|
||||
import { PluginSource } from '../types'
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { fireEvent, render, screen, within } from '@testing-library/react'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import * as toastModule from '@/app/components/base/ui/toast'
|
||||
|
||||
import Conversion from '../conversion'
|
||||
|
||||
@ -23,23 +24,8 @@ vi.mock('@/service/knowledge/use-dataset', () => ({
|
||||
vi.mock('@/service/use-base', () => ({
|
||||
useInvalid: () => mockInvalidDatasetDetail,
|
||||
}))
|
||||
|
||||
const { mockToast } = vi.hoisted(() => {
|
||||
const mockToast = Object.assign(vi.fn(), {
|
||||
success: vi.fn(),
|
||||
error: vi.fn(),
|
||||
warning: vi.fn(),
|
||||
info: vi.fn(),
|
||||
dismiss: vi.fn(),
|
||||
update: vi.fn(),
|
||||
promise: vi.fn(),
|
||||
})
|
||||
return { mockToast }
|
||||
})
|
||||
|
||||
vi.mock('@/app/components/base/ui/toast', () => ({
|
||||
toast: mockToast,
|
||||
}))
|
||||
const mockToastSuccess = vi.fn()
|
||||
const mockToastError = vi.fn()
|
||||
|
||||
vi.mock('@/app/components/base/button', () => ({
|
||||
default: ({ children, onClick, ...props }: Record<string, unknown>) => (
|
||||
@ -47,29 +33,6 @@ vi.mock('@/app/components/base/button', () => ({
|
||||
),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/confirm', () => ({
|
||||
default: ({
|
||||
isShow,
|
||||
onConfirm,
|
||||
onCancel,
|
||||
title,
|
||||
}: {
|
||||
isShow: boolean
|
||||
onConfirm: () => void
|
||||
onCancel: () => void
|
||||
title: string
|
||||
}) =>
|
||||
isShow
|
||||
? (
|
||||
<div data-testid="confirm-modal">
|
||||
<span>{title}</span>
|
||||
<button data-testid="confirm-btn" onClick={onConfirm}>Confirm</button>
|
||||
<button data-testid="cancel-btn" onClick={onCancel}>Cancel</button>
|
||||
</div>
|
||||
)
|
||||
: null,
|
||||
}))
|
||||
|
||||
vi.mock('../screenshot', () => ({
|
||||
default: () => <div data-testid="screenshot" />,
|
||||
}))
|
||||
@ -77,10 +40,14 @@ vi.mock('../screenshot', () => ({
|
||||
describe('Conversion', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks()
|
||||
vi.spyOn(toastModule.toast, 'success').mockImplementation((...args) => {
|
||||
mockToastSuccess(...args)
|
||||
return 'toast-id'
|
||||
})
|
||||
vi.spyOn(toastModule.toast, 'error').mockImplementation((...args) => {
|
||||
mockToastError(...args)
|
||||
return 'toast-id'
|
||||
})
|
||||
})
|
||||
|
||||
it('should render conversion title and description', () => {
|
||||
@ -112,29 +79,31 @@ describe('Conversion', () => {
|
||||
it('should show confirm modal when convert button clicked', () => {
|
||||
render(<Conversion />)
|
||||
|
||||
expect(screen.queryByTestId('confirm-modal')).not.toBeInTheDocument()
|
||||
expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument()
|
||||
|
||||
fireEvent.click(screen.getByText('datasetPipeline.operations.convert'))
|
||||
|
||||
expect(screen.getByTestId('confirm-modal')).toBeInTheDocument()
|
||||
expect(screen.getByText('datasetPipeline.conversion.confirm.title')).toBeInTheDocument()
|
||||
const dialog = screen.getByRole('alertdialog')
|
||||
expect(dialog).toBeInTheDocument()
|
||||
expect(within(dialog).getByText('datasetPipeline.conversion.confirm.title')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should hide confirm modal when cancel is clicked', () => {
|
||||
render(<Conversion />)
|
||||
|
||||
fireEvent.click(screen.getByText('datasetPipeline.operations.convert'))
|
||||
expect(screen.getByTestId('confirm-modal')).toBeInTheDocument()
|
||||
const dialog = screen.getByRole('alertdialog')
|
||||
expect(dialog).toBeInTheDocument()
|
||||
|
||||
fireEvent.click(screen.getByTestId('cancel-btn'))
|
||||
expect(screen.queryByTestId('confirm-modal')).not.toBeInTheDocument()
|
||||
fireEvent.click(within(dialog).getByRole('button', { name: 'common.operation.cancel' }))
|
||||
expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should call convert when confirm is clicked', () => {
|
||||
render(<Conversion />)
|
||||
|
||||
fireEvent.click(screen.getByText('datasetPipeline.operations.convert'))
|
||||
fireEvent.click(screen.getByTestId('confirm-btn'))
|
||||
fireEvent.click(within(screen.getByRole('alertdialog')).getAllByRole('button').at(-1)!)
|
||||
|
||||
expect(mockConvert).toHaveBeenCalledWith('ds-123', expect.objectContaining({
|
||||
onSuccess: expect.any(Function),
|
||||
@ -150,9 +119,9 @@ describe('Conversion', () => {
|
||||
render(<Conversion />)
|
||||
|
||||
fireEvent.click(screen.getByText('datasetPipeline.operations.convert'))
|
||||
fireEvent.click(screen.getByTestId('confirm-btn'))
|
||||
fireEvent.click(within(screen.getByRole('alertdialog')).getAllByRole('button').at(-1)!)
|
||||
|
||||
expect(mockToast.success).toHaveBeenCalledWith('datasetPipeline.conversion.successMessage')
|
||||
expect(mockToastSuccess).toHaveBeenCalledWith('datasetPipeline.conversion.successMessage')
|
||||
expect(mockInvalidDatasetDetail).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
@ -164,9 +133,9 @@ describe('Conversion', () => {
|
||||
render(<Conversion />)
|
||||
|
||||
fireEvent.click(screen.getByText('datasetPipeline.operations.convert'))
|
||||
fireEvent.click(screen.getByTestId('confirm-btn'))
|
||||
fireEvent.click(within(screen.getByRole('alertdialog')).getAllByRole('button').at(-1)!)
|
||||
|
||||
expect(mockToast.error).toHaveBeenCalledWith('datasetPipeline.conversion.errorMessage')
|
||||
expect(mockToastError).toHaveBeenCalledWith('datasetPipeline.conversion.errorMessage')
|
||||
})
|
||||
|
||||
it('should handle conversion error', async () => {
|
||||
@ -177,8 +146,8 @@ describe('Conversion', () => {
|
||||
render(<Conversion />)
|
||||
|
||||
fireEvent.click(screen.getByText('datasetPipeline.operations.convert'))
|
||||
fireEvent.click(screen.getByTestId('confirm-btn'))
|
||||
fireEvent.click(within(screen.getByRole('alertdialog')).getAllByRole('button').at(-1)!)
|
||||
|
||||
expect(mockToast.error).toHaveBeenCalledWith('datasetPipeline.conversion.errorMessage')
|
||||
expect(mockToastError).toHaveBeenCalledWith('datasetPipeline.conversion.errorMessage')
|
||||
})
|
||||
})
|
||||
|
||||
@ -2,7 +2,7 @@ import * as React from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import { toast } from '@/app/components/base/ui/toast'
|
||||
import { useParams } from '@/next/navigation'
|
||||
import { datasetDetailQueryKeyPrefix } from '@/service/knowledge/use-dataset'
|
||||
|
||||
@ -5,24 +5,6 @@ import Popup from '../popup'
|
||||
|
||||
const mockPublishWorkflow = vi.fn().mockResolvedValue({ created_at: '2024-01-01T00:00:00Z' })
|
||||
const mockPublishAsCustomizedPipeline = vi.fn().mockResolvedValue({})
|
||||
const toastMocks = vi.hoisted(() => ({
|
||||
call: vi.fn(),
|
||||
dismiss: vi.fn(),
|
||||
update: vi.fn(),
|
||||
promise: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/ui/toast', () => ({
|
||||
toast: Object.assign(toastMocks.call, {
|
||||
success: vi.fn((message: string, options?: Record<string, unknown>) => toastMocks.call({ type: 'success', message, ...options })),
|
||||
error: vi.fn((message: string, options?: Record<string, unknown>) => toastMocks.call({ type: 'error', message, ...options })),
|
||||
warning: vi.fn((message: string, options?: Record<string, unknown>) => toastMocks.call({ type: 'warning', message, ...options })),
|
||||
info: vi.fn((message: string, options?: Record<string, unknown>) => toastMocks.call({ type: 'info', message, ...options })),
|
||||
dismiss: toastMocks.dismiss,
|
||||
update: toastMocks.update,
|
||||
promise: toastMocks.promise,
|
||||
}),
|
||||
}))
|
||||
const mockPush = vi.fn()
|
||||
const mockHandleCheckBeforePublish = vi.fn().mockResolvedValue(true)
|
||||
const mockSetPublishedAt = vi.fn()
|
||||
@ -87,24 +69,6 @@ vi.mock('@/app/components/base/button', () => ({
|
||||
),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/confirm', () => ({
|
||||
default: ({ isShow, onConfirm, onCancel, title }: {
|
||||
isShow: boolean
|
||||
onConfirm: () => void
|
||||
onCancel: () => void
|
||||
title: string
|
||||
}) =>
|
||||
isShow
|
||||
? (
|
||||
<div data-testid="confirm-modal">
|
||||
<span>{title}</span>
|
||||
<button data-testid="publish-confirm" onClick={onConfirm}>OK</button>
|
||||
<button data-testid="publish-cancel" onClick={onCancel}>Cancel</button>
|
||||
</div>
|
||||
)
|
||||
: null,
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/divider', () => ({
|
||||
default: () => <hr />,
|
||||
}))
|
||||
|
||||
@ -6,10 +6,10 @@ import { memo, useCallback, useState } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { trackEvent } from '@/app/components/base/amplitude'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import { SparklesSoft } from '@/app/components/base/icons/src/public/common'
|
||||
import PremiumBadge from '@/app/components/base/premium-badge'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import { toast } from '@/app/components/base/ui/toast'
|
||||
import { useChecklistBeforePublish } from '@/app/components/workflow/hooks'
|
||||
import ShortcutsName from '@/app/components/workflow/shortcuts-name'
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import type { ToolWithProvider } from '@/app/components/workflow/types'
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import { fireEvent, render, screen, waitFor, within } from '@testing-library/react'
|
||||
import * as React from 'react'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import MCPCard from '../provider-card'
|
||||
@ -49,31 +49,6 @@ vi.mock('../modal', () => ({
|
||||
},
|
||||
}))
|
||||
|
||||
// Mock the Confirm dialog
|
||||
type ConfirmDialogProps = {
|
||||
isShow: boolean
|
||||
onConfirm: () => void
|
||||
onCancel: () => void
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
vi.mock('@/app/components/base/confirm', () => ({
|
||||
default: ({ isShow, onConfirm, onCancel, isLoading }: ConfirmDialogProps) => {
|
||||
if (!isShow)
|
||||
return null
|
||||
return (
|
||||
<div data-testid="confirm-dialog">
|
||||
<button data-testid="confirm-delete-btn" onClick={onConfirm} disabled={isLoading}>
|
||||
{isLoading ? 'Deleting...' : 'Confirm Delete'}
|
||||
</button>
|
||||
<button data-testid="cancel-delete-btn" onClick={onCancel}>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
}))
|
||||
|
||||
// Mock the OperationDropdown
|
||||
type OperationDropdownProps = {
|
||||
onEdit: () => void
|
||||
@ -450,7 +425,7 @@ describe('MCPCard', () => {
|
||||
|
||||
// Confirm dialog should be shown
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('confirm-dialog')).toBeInTheDocument()
|
||||
expect(screen.getByRole('alertdialog')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
@ -462,15 +437,15 @@ describe('MCPCard', () => {
|
||||
fireEvent.click(removeBtn)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('confirm-dialog')).toBeInTheDocument()
|
||||
expect(screen.getByRole('alertdialog')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Cancel
|
||||
const cancelBtn = screen.getByTestId('cancel-delete-btn')
|
||||
const cancelBtn = within(screen.getByRole('alertdialog')).getByRole('button', { name: 'common.operation.cancel' })
|
||||
fireEvent.click(cancelBtn)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId('confirm-dialog')).not.toBeInTheDocument()
|
||||
expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
@ -483,11 +458,11 @@ describe('MCPCard', () => {
|
||||
fireEvent.click(removeBtn)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('confirm-dialog')).toBeInTheDocument()
|
||||
expect(screen.getByRole('alertdialog')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Confirm delete
|
||||
const confirmBtn = screen.getByTestId('confirm-delete-btn')
|
||||
const confirmBtn = within(screen.getByRole('alertdialog')).getAllByRole('button').at(-1)!
|
||||
fireEvent.click(confirmBtn)
|
||||
|
||||
await waitFor(() => {
|
||||
@ -506,11 +481,11 @@ describe('MCPCard', () => {
|
||||
fireEvent.click(removeBtn)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('confirm-dialog')).toBeInTheDocument()
|
||||
expect(screen.getByRole('alertdialog')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Confirm delete
|
||||
const confirmBtn = screen.getByTestId('confirm-delete-btn')
|
||||
const confirmBtn = within(screen.getByRole('alertdialog')).getAllByRole('button').at(-1)!
|
||||
fireEvent.click(confirmBtn)
|
||||
|
||||
await waitFor(() => {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import type { ToolWithProvider } from '@/app/components/workflow/types'
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import { fireEvent, render, screen, waitFor, within } from '@testing-library/react'
|
||||
import * as React from 'react'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import MCPDetailContent from '../content'
|
||||
@ -84,20 +84,6 @@ vi.mock('../../modal', () => ({
|
||||
},
|
||||
}))
|
||||
|
||||
// Mock Confirm dialog
|
||||
vi.mock('@/app/components/base/confirm', () => ({
|
||||
default: ({ isShow, onConfirm, onCancel, title }: { isShow: boolean, onConfirm: () => void, onCancel: () => void, title: string }) => {
|
||||
if (!isShow)
|
||||
return null
|
||||
return (
|
||||
<div data-testid="confirm-dialog" data-title={title}>
|
||||
<button data-testid="confirm-btn" onClick={onConfirm}>Confirm</button>
|
||||
<button data-testid="cancel-btn" onClick={onCancel}>Cancel</button>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
}))
|
||||
|
||||
// Mock OperationDropdown
|
||||
vi.mock('../operation-dropdown', () => ({
|
||||
default: ({ onEdit, onRemove }: { onEdit: () => void, onRemove: () => void }) => (
|
||||
@ -494,7 +480,7 @@ describe('MCPDetailContent', () => {
|
||||
fireEvent.click(updateBtn)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('confirm-dialog')).toBeInTheDocument()
|
||||
expect(screen.getByRole('alertdialog')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
@ -514,11 +500,11 @@ describe('MCPDetailContent', () => {
|
||||
fireEvent.click(updateBtn)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('confirm-dialog')).toBeInTheDocument()
|
||||
expect(screen.getByRole('alertdialog')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Confirm the update
|
||||
const confirmBtn = screen.getByTestId('confirm-btn')
|
||||
const confirmBtn = within(screen.getByRole('alertdialog')).getAllByRole('button').at(-1)!
|
||||
fireEvent.click(confirmBtn)
|
||||
|
||||
await waitFor(() => {
|
||||
@ -636,7 +622,7 @@ describe('MCPDetailContent', () => {
|
||||
fireEvent.click(removeBtn)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('confirm-dialog')).toBeInTheDocument()
|
||||
expect(screen.getByRole('alertdialog')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
@ -648,15 +634,15 @@ describe('MCPDetailContent', () => {
|
||||
fireEvent.click(removeBtn)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('confirm-dialog')).toBeInTheDocument()
|
||||
expect(screen.getByRole('alertdialog')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Cancel
|
||||
const cancelBtn = screen.getByTestId('cancel-btn')
|
||||
const cancelBtn = within(screen.getByRole('alertdialog')).getByRole('button', { name: 'common.operation.cancel' })
|
||||
fireEvent.click(cancelBtn)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId('confirm-dialog')).not.toBeInTheDocument()
|
||||
expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
@ -669,11 +655,11 @@ describe('MCPDetailContent', () => {
|
||||
fireEvent.click(removeBtn)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('confirm-dialog')).toBeInTheDocument()
|
||||
expect(screen.getByRole('alertdialog')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Confirm delete
|
||||
const confirmBtn = screen.getByTestId('confirm-btn')
|
||||
const confirmBtn = within(screen.getByRole('alertdialog')).getAllByRole('button').at(-1)!
|
||||
fireEvent.click(confirmBtn)
|
||||
|
||||
await waitFor(() => {
|
||||
@ -692,11 +678,11 @@ describe('MCPDetailContent', () => {
|
||||
fireEvent.click(removeBtn)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('confirm-dialog')).toBeInTheDocument()
|
||||
expect(screen.getByRole('alertdialog')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Confirm delete
|
||||
const confirmBtn = screen.getByTestId('confirm-btn')
|
||||
const confirmBtn = within(screen.getByRole('alertdialog')).getAllByRole('button').at(-1)!
|
||||
fireEvent.click(confirmBtn)
|
||||
|
||||
await waitFor(() => {
|
||||
@ -840,15 +826,15 @@ describe('MCPDetailContent', () => {
|
||||
fireEvent.click(updateBtn)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('confirm-dialog')).toBeInTheDocument()
|
||||
expect(screen.getByRole('alertdialog')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Cancel the update
|
||||
const cancelBtn = screen.getByTestId('cancel-btn')
|
||||
const cancelBtn = within(screen.getByRole('alertdialog')).getByRole('button', { name: 'common.operation.cancel' })
|
||||
fireEvent.click(cancelBtn)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId('confirm-dialog')).not.toBeInTheDocument()
|
||||
expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -13,8 +13,8 @@ import { useCallback, useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import Indicator from '@/app/components/header/indicator'
|
||||
import Icon from '@/app/components/plugins/card/base/card-icon'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
|
||||
@ -7,12 +7,12 @@ import { RiEditLine, RiLoopLeftLine } from '@remixicon/react'
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import CopyFeedback from '@/app/components/base/copy-feedback'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import { Mcp } from '@/app/components/base/icons/src/vender/other'
|
||||
import Switch from '@/app/components/base/switch'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import Indicator from '@/app/components/header/indicator'
|
||||
import MCPServerModal from '@/app/components/tools/mcp/mcp-server-modal'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
@ -4,7 +4,7 @@ import { RiHammerFill } from '@remixicon/react'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import Indicator from '@/app/components/header/indicator'
|
||||
import Icon from '@/app/components/plugins/card/base/card-icon'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { Collection } from '../../types'
|
||||
import { act, cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import { act, cleanup, fireEvent, render, screen, waitFor, within } from '@testing-library/react'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { AuthType, CollectionType } from '../../types'
|
||||
import ProviderDetail from '../detail'
|
||||
@ -79,28 +79,6 @@ vi.mock('@/app/components/base/drawer', () => ({
|
||||
isOpen ? <div data-testid="drawer">{children}</div> : null,
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/confirm', () => ({
|
||||
default: ({ isShow, onConfirm, onCancel, title }: { isShow: boolean, onConfirm: () => void, onCancel: () => void, title: string }) =>
|
||||
isShow
|
||||
? (
|
||||
<div data-testid="confirm-dialog">
|
||||
<span>{title}</span>
|
||||
<button data-testid="confirm-btn" onClick={onConfirm}>Confirm</button>
|
||||
<button data-testid="cancel-btn" onClick={onCancel}>Cancel</button>
|
||||
</div>
|
||||
)
|
||||
: null,
|
||||
}))
|
||||
|
||||
const mockToastSuccess = vi.hoisted(() => vi.fn())
|
||||
const mockToastError = vi.hoisted(() => vi.fn())
|
||||
vi.mock('@/app/components/base/ui/toast', () => ({
|
||||
toast: {
|
||||
success: mockToastSuccess,
|
||||
error: mockToastError,
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/header/indicator', () => ({
|
||||
default: () => <span data-testid="indicator" />,
|
||||
}))
|
||||
@ -552,9 +530,9 @@ describe('ProviderDetail', () => {
|
||||
})
|
||||
fireEvent.click(screen.getByText('tools.createTool.editAction'))
|
||||
fireEvent.click(screen.getByTestId('edit-remove'))
|
||||
expect(screen.getByTestId('confirm-dialog')).toBeInTheDocument()
|
||||
expect(screen.getByRole('alertdialog')).toBeInTheDocument()
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByTestId('confirm-btn'))
|
||||
fireEvent.click(within(screen.getByRole('alertdialog')).getAllByRole('button').at(-1)!)
|
||||
})
|
||||
await waitFor(() => {
|
||||
expect(mockRemoveCustomCollection).toHaveBeenCalledWith('test-collection')
|
||||
@ -626,9 +604,9 @@ describe('ProviderDetail', () => {
|
||||
})
|
||||
fireEvent.click(screen.getByText('tools.createTool.editAction'))
|
||||
fireEvent.click(screen.getByTestId('wf-remove'))
|
||||
expect(screen.getByTestId('confirm-dialog')).toBeInTheDocument()
|
||||
expect(screen.getByRole('alertdialog')).toBeInTheDocument()
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByTestId('confirm-btn'))
|
||||
fireEvent.click(within(screen.getByRole('alertdialog')).getAllByRole('button').at(-1)!)
|
||||
})
|
||||
await waitFor(() => {
|
||||
expect(mockDeleteWorkflowTool).toHaveBeenCalledWith('test-id')
|
||||
@ -710,9 +688,9 @@ describe('ProviderDetail', () => {
|
||||
})
|
||||
fireEvent.click(screen.getByText('tools.createTool.editAction'))
|
||||
fireEvent.click(screen.getByTestId('edit-remove'))
|
||||
expect(screen.getByTestId('confirm-dialog')).toBeInTheDocument()
|
||||
fireEvent.click(screen.getByTestId('cancel-btn'))
|
||||
expect(screen.queryByTestId('confirm-dialog')).not.toBeInTheDocument()
|
||||
expect(screen.getByRole('alertdialog')).toBeInTheDocument()
|
||||
fireEvent.click(within(screen.getByRole('alertdialog')).getByRole('button', { name: 'common.operation.cancel' }))
|
||||
expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -9,10 +9,10 @@ import { useCallback, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import Drawer from '@/app/components/base/drawer'
|
||||
import { LinkExternal02, Settings01 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import { toast } from '@/app/components/base/ui/toast'
|
||||
import { ConfigurationMethodEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import Indicator from '@/app/components/header/indicator'
|
||||
|
||||
@ -103,7 +103,7 @@ import { WorkflowHistoryProvider } from './workflow-history-store'
|
||||
import 'reactflow/dist/style.css'
|
||||
import './style.css'
|
||||
|
||||
const Confirm = dynamic(() => import('@/app/components/base/confirm'), {
|
||||
const Confirm = dynamic(() => import('@/app/components/base/ui/confirm-dialog'), {
|
||||
ssr: false,
|
||||
})
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
import type { FC } from 'react'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
|
||||
type Props = {
|
||||
isShow: boolean
|
||||
|
||||
@ -132,7 +132,7 @@
|
||||
},
|
||||
"app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-config-modal.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 2
|
||||
"count": 1
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 3
|
||||
@ -353,7 +353,7 @@
|
||||
},
|
||||
"app/components/app-sidebar/dataset-info/dropdown.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 2
|
||||
"count": 1
|
||||
},
|
||||
"ts/no-explicit-any": {
|
||||
"count": 1
|
||||
@ -415,9 +415,6 @@
|
||||
}
|
||||
},
|
||||
"app/components/app/annotation/batch-action.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 4
|
||||
}
|
||||
@ -452,11 +449,6 @@
|
||||
"count": 2
|
||||
}
|
||||
},
|
||||
"app/components/app/annotation/clear-all-annotations-confirm-modal/index.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/app/annotation/edit-annotation-modal/edit-item/index.tsx": {
|
||||
"erasable-syntax-only/enums": {
|
||||
"count": 1
|
||||
@ -472,9 +464,6 @@
|
||||
}
|
||||
},
|
||||
"app/components/app/annotation/edit-annotation-modal/index.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 2
|
||||
}
|
||||
@ -517,11 +506,6 @@
|
||||
"count": 8
|
||||
}
|
||||
},
|
||||
"app/components/app/annotation/remove-annotation-confirm-modal/index.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/app/annotation/type.ts": {
|
||||
"erasable-syntax-only/enums": {
|
||||
"count": 2
|
||||
@ -531,9 +515,6 @@
|
||||
"erasable-syntax-only/enums": {
|
||||
"count": 1
|
||||
},
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"react/set-state-in-effect": {
|
||||
"count": 5
|
||||
},
|
||||
@ -571,9 +552,6 @@
|
||||
}
|
||||
},
|
||||
"app/components/app/app-publisher/features-wrapper.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"ts/no-explicit-any": {
|
||||
"count": 4
|
||||
}
|
||||
@ -711,7 +689,7 @@
|
||||
},
|
||||
"app/components/app/configuration/config-var/index.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 2
|
||||
"count": 1
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 1
|
||||
@ -839,7 +817,7 @@
|
||||
},
|
||||
"app/components/app/configuration/config/automatic/get-automatic-res.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 2
|
||||
"count": 1
|
||||
},
|
||||
"react/set-state-in-effect": {
|
||||
"count": 4
|
||||
@ -910,7 +888,7 @@
|
||||
},
|
||||
"app/components/app/configuration/config/code-generator/get-code-generator-res.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 2
|
||||
"count": 1
|
||||
},
|
||||
"react/set-state-in-effect": {
|
||||
"count": 4
|
||||
@ -1297,7 +1275,7 @@
|
||||
},
|
||||
"app/components/app/switch-app-modal/index.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 2
|
||||
"count": 1
|
||||
},
|
||||
"react/set-state-in-effect": {
|
||||
"count": 1
|
||||
@ -1653,9 +1631,6 @@
|
||||
}
|
||||
},
|
||||
"app/components/base/chat/chat-with-history/header-in-mobile.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 4
|
||||
},
|
||||
@ -1665,7 +1640,7 @@
|
||||
},
|
||||
"app/components/base/chat/chat-with-history/header/index.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 2
|
||||
"count": 1
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 1
|
||||
@ -1719,9 +1694,6 @@
|
||||
}
|
||||
},
|
||||
"app/components/base/chat/chat-with-history/sidebar/index.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 1
|
||||
}
|
||||
@ -1980,22 +1952,6 @@
|
||||
"count": 3
|
||||
}
|
||||
},
|
||||
"app/components/base/confirm/index.stories.tsx": {
|
||||
"no-console": {
|
||||
"count": 4
|
||||
},
|
||||
"ts/no-explicit-any": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/base/confirm/index.tsx": {
|
||||
"react/set-state-in-effect": {
|
||||
"count": 2
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 6
|
||||
}
|
||||
},
|
||||
"app/components/base/content-dialog/index.stories.tsx": {
|
||||
"react/set-state-in-effect": {
|
||||
"count": 1
|
||||
@ -3730,7 +3686,7 @@
|
||||
},
|
||||
"app/components/base/tag-management/tag-item-editor.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 2
|
||||
"count": 1
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 2
|
||||
@ -4209,7 +4165,7 @@
|
||||
},
|
||||
"app/components/datasets/create-from-pipeline/list/template-card/index.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 2
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/datasets/create-from-pipeline/list/template-card/operations.tsx": {
|
||||
@ -4525,7 +4481,7 @@
|
||||
},
|
||||
"app/components/datasets/documents/components/operations.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 3
|
||||
"count": 2
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 1
|
||||
@ -4792,9 +4748,6 @@
|
||||
}
|
||||
},
|
||||
"app/components/datasets/documents/detail/completed/common/batch-action.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 2
|
||||
}
|
||||
@ -4893,7 +4846,7 @@
|
||||
},
|
||||
"app/components/datasets/documents/detail/completed/segment-card/index.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 2
|
||||
"count": 1
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 3
|
||||
@ -5034,7 +4987,7 @@
|
||||
},
|
||||
"app/components/datasets/external-api/external-api-modal/index.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 3
|
||||
"count": 2
|
||||
},
|
||||
"react/set-state-in-effect": {
|
||||
"count": 1
|
||||
@ -5049,9 +5002,6 @@
|
||||
}
|
||||
},
|
||||
"app/components/datasets/external-api/external-knowledge-api-card/index.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 1
|
||||
}
|
||||
@ -5227,11 +5177,6 @@
|
||||
"count": 9
|
||||
}
|
||||
},
|
||||
"app/components/datasets/list/dataset-card/components/dataset-card-modals.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/datasets/list/dataset-card/components/description.tsx": {
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 1
|
||||
@ -5344,7 +5289,7 @@
|
||||
},
|
||||
"app/components/datasets/metadata/metadata-dataset/dataset-metadata-drawer.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 3
|
||||
"count": 2
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 4
|
||||
@ -5535,7 +5480,7 @@
|
||||
},
|
||||
"app/components/develop/secret-key/secret-key-modal.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 2
|
||||
"count": 1
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 2
|
||||
@ -5800,11 +5745,6 @@
|
||||
"count": 2
|
||||
}
|
||||
},
|
||||
"app/components/header/account-setting/api-based-extension-page/item.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/header/account-setting/api-based-extension-page/modal.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
@ -5827,9 +5767,6 @@
|
||||
}
|
||||
},
|
||||
"app/components/header/account-setting/data-source-page-new/card.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 3
|
||||
},
|
||||
@ -6047,7 +5984,7 @@
|
||||
},
|
||||
"app/components/header/account-setting/model-provider-page/model-auth/authorized/index.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 3
|
||||
"count": 2
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 2
|
||||
@ -6282,7 +6219,7 @@
|
||||
},
|
||||
"app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 2
|
||||
"count": 1
|
||||
},
|
||||
"react/set-state-in-effect": {
|
||||
"count": 1
|
||||
@ -6660,7 +6597,7 @@
|
||||
},
|
||||
"app/components/plugins/plugin-auth/authorized/index.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 3
|
||||
"count": 2
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 2
|
||||
@ -6807,7 +6744,7 @@
|
||||
},
|
||||
"app/components/plugins/plugin-detail-panel/endpoint-card.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 2
|
||||
"count": 1
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 5
|
||||
@ -7077,7 +7014,7 @@
|
||||
},
|
||||
"app/components/plugins/plugin-item/action.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 2
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/plugins/plugin-item/index.tsx": {
|
||||
@ -7307,9 +7244,6 @@
|
||||
}
|
||||
},
|
||||
"app/components/rag-pipeline/components/conversion.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 1
|
||||
}
|
||||
@ -7472,9 +7406,6 @@
|
||||
}
|
||||
},
|
||||
"app/components/rag-pipeline/components/rag-pipeline-header/publisher/popup.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 7
|
||||
}
|
||||
@ -7735,7 +7666,7 @@
|
||||
},
|
||||
"app/components/tools/mcp/detail/content.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 2
|
||||
"count": 1
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 9
|
||||
@ -7793,7 +7724,7 @@
|
||||
},
|
||||
"app/components/tools/mcp/mcp-service-card.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 2
|
||||
"count": 1
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 7
|
||||
@ -7808,9 +7739,6 @@
|
||||
}
|
||||
},
|
||||
"app/components/tools/mcp/provider-card.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 7
|
||||
},
|
||||
@ -7845,9 +7773,6 @@
|
||||
}
|
||||
},
|
||||
"app/components/tools/provider/detail.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
},
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 3
|
||||
}
|
||||
@ -8776,11 +8701,6 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/workflow/nodes/_base/components/remove-effect-var-confirm.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"app/components/workflow/nodes/_base/components/retry/retry-on-node.tsx": {
|
||||
"tailwindcss/enforce-consistent-class-order": {
|
||||
"count": 1
|
||||
@ -11686,9 +11606,6 @@
|
||||
}
|
||||
},
|
||||
"hooks/use-pay.tsx": {
|
||||
"no-restricted-imports": {
|
||||
"count": 2
|
||||
},
|
||||
"react/set-state-in-effect": {
|
||||
"count": 4
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
'use client'
|
||||
|
||||
import type { IConfirm } from '@/app/components/base/confirm'
|
||||
import type { IConfirm } from '@/app/components/base/ui/confirm-dialog'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import Confirm from '@/app/components/base/ui/confirm-dialog'
|
||||
import { useRouter, useSearchParams } from '@/next/navigation'
|
||||
import { useNotionBinding } from '@/service/use-common'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user