diff --git a/web/app/components/datasets/documents/detail/settings/__tests__/document-settings.spec.tsx b/web/app/components/datasets/documents/detail/settings/__tests__/document-settings.spec.tsx index e6109132a4..84534298c9 100644 --- a/web/app/components/datasets/documents/detail/settings/__tests__/document-settings.spec.tsx +++ b/web/app/components/datasets/documents/detail/settings/__tests__/document-settings.spec.tsx @@ -100,10 +100,10 @@ vi.mock('@/app/components/datasets/create/step-two', () => ({ })) vi.mock('@/app/components/header/account-setting', () => ({ - default: ({ activeTab, onCancel }: { activeTab?: string, onCancel?: () => void }) => ( + default: ({ activeTab, onCancelAction }: { activeTab?: string, onCancelAction?: () => void }) => (
{activeTab} - +
), })) diff --git a/web/app/components/datasets/documents/detail/settings/document-settings.tsx b/web/app/components/datasets/documents/detail/settings/document-settings.tsx index 0d10f8a51e..67773cb7d6 100644 --- a/web/app/components/datasets/documents/detail/settings/document-settings.tsx +++ b/web/app/components/datasets/documents/detail/settings/document-settings.tsx @@ -1,3 +1,4 @@ +import type { AccountSettingTab } from '@/app/components/header/account-setting/constants' import type { DataSourceProvider, NotionPage } from '@/models/common' import type { CrawlOptions, @@ -19,6 +20,7 @@ import AppUnavailable from '@/app/components/base/app-unavailable' import Loading from '@/app/components/base/loading' import StepTwo from '@/app/components/datasets/create/step-two' import AccountSetting from '@/app/components/header/account-setting' +import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants' import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { useDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks' import DatasetDetailContext from '@/context/dataset-detail' @@ -33,8 +35,13 @@ const DocumentSettings = ({ datasetId, documentId }: DocumentSettingsProps) => { const { t } = useTranslation() const router = useRouter() const [isShowSetAPIKey, { setTrue: showSetAPIKey, setFalse: hideSetAPIkey }] = useBoolean() + const [accountSettingTab, setAccountSettingTab] = React.useState(ACCOUNT_SETTING_TAB.PROVIDER) const { indexingTechnique, dataset } = useContext(DatasetDetailContext) const { data: embeddingsDefaultModel } = useDefaultModel(ModelTypeEnum.textEmbedding) + const handleOpenAccountSetting = React.useCallback(() => { + setAccountSettingTab(ACCOUNT_SETTING_TAB.PROVIDER) + showSetAPIKey() + }, [showSetAPIKey]) const invalidDocumentList = useInvalidDocumentList(datasetId) const invalidDocumentDetail = useInvalidDocumentDetail() @@ -135,7 +142,7 @@ const DocumentSettings = ({ datasetId, documentId }: DocumentSettingsProps) => { {dataset && documentDetail && ( { {isShowSetAPIKey && ( { + activeTab={accountSettingTab} + onTabChangeAction={setAccountSettingTab} + onCancelAction={async () => { hideSetAPIkey() }} /> diff --git a/web/app/components/header/account-setting/index.spec.tsx b/web/app/components/header/account-setting/index.spec.tsx index 74480f0a1b..ee993e2f73 100644 --- a/web/app/components/header/account-setting/index.spec.tsx +++ b/web/app/components/header/account-setting/index.spec.tsx @@ -1,6 +1,8 @@ +import type { AccountSettingTab } from './constants' import type { AppContextValue } from '@/context/app-context' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { fireEvent, render, screen } from '@testing-library/react' +import { useState } from 'react' import { useAppContext } from '@/context/app-context' import { baseProviderContextValue, useProviderContext } from '@/context/provider-context' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' @@ -112,6 +114,38 @@ const baseAppContextValue: AppContextValue = { describe('AccountSetting', () => { const mockOnCancel = vi.fn() const mockOnTabChange = vi.fn() + const renderAccountSetting = (props?: { + initialTab?: AccountSettingTab + onCancel?: () => void + onTabChange?: (tab: AccountSettingTab) => void + }) => { + const { + initialTab = ACCOUNT_SETTING_TAB.MEMBERS, + onCancel = mockOnCancel, + onTabChange = mockOnTabChange, + } = props ?? {} + + const StatefulAccountSetting = () => { + const [activeTab, setActiveTab] = useState(initialTab) + + return ( + { + setActiveTab(tab) + onTabChange(tab) + }} + /> + ) + } + + return render( + + + , + ) + } beforeEach(() => { vi.clearAllMocks() @@ -127,11 +161,7 @@ describe('AccountSetting', () => { describe('Rendering', () => { it('should render the sidebar with correct menu items', () => { // Act - render( - - - , - ) + renderAccountSetting() // Assert expect(screen.getByText('common.userProfile.settings')).toBeInTheDocument() @@ -144,13 +174,9 @@ describe('AccountSetting', () => { expect(screen.getAllByText('common.settings.language').length).toBeGreaterThan(0) }) - it('should respect the activeTab prop', () => { + it('should respect the initial tab', () => { // Act - render( - - - , - ) + renderAccountSetting({ initialTab: ACCOUNT_SETTING_TAB.DATA_SOURCE }) // Assert // Check that the active item title is Data Source @@ -164,11 +190,7 @@ describe('AccountSetting', () => { vi.mocked(useBreakpoints).mockReturnValue(MediaType.mobile) // Act - render( - - - , - ) + renderAccountSetting() // Assert // On mobile, the labels should not be rendered as per the implementation @@ -183,11 +205,7 @@ describe('AccountSetting', () => { }) // Act - render( - - - , - ) + renderAccountSetting() // Assert expect(screen.queryByText('common.settings.provider')).not.toBeInTheDocument() @@ -204,11 +222,7 @@ describe('AccountSetting', () => { }) // Act - render( - - - , - ) + renderAccountSetting() // Assert expect(screen.queryByText('common.settings.billing')).not.toBeInTheDocument() @@ -219,11 +233,7 @@ describe('AccountSetting', () => { describe('Tab Navigation', () => { it('should change active tab when clicking on menu item', () => { // Arrange - render( - - - , - ) + renderAccountSetting({ onTabChange: mockOnTabChange }) // Act fireEvent.click(screen.getByText('common.settings.provider')) @@ -236,11 +246,7 @@ describe('AccountSetting', () => { it('should navigate through various tabs and show correct details', () => { // Act & Assert - render( - - - , - ) + renderAccountSetting() // Billing fireEvent.click(screen.getByText('common.settings.billing')) @@ -274,11 +280,7 @@ describe('AccountSetting', () => { describe('Interactions', () => { it('should call onCancel when clicking close button', () => { // Act - render( - - - , - ) + renderAccountSetting() const closeIcon = document.querySelector('.i-ri-close-line') const closeButton = closeIcon?.closest('button') expect(closeButton).not.toBeNull() @@ -290,11 +292,7 @@ describe('AccountSetting', () => { it('should call onCancel when pressing Escape key', () => { // Act - render( - - - , - ) + renderAccountSetting() fireEvent.keyDown(document, { key: 'Escape' }) // Assert @@ -303,12 +301,7 @@ describe('AccountSetting', () => { it('should update search value in provider tab', () => { // Arrange - render( - - - , - ) - fireEvent.click(screen.getByText('common.settings.provider')) + renderAccountSetting({ initialTab: ACCOUNT_SETTING_TAB.PROVIDER }) // Act const input = screen.getByRole('textbox') @@ -321,11 +314,7 @@ describe('AccountSetting', () => { it('should handle scroll event in panel', () => { // Act - render( - - - , - ) + renderAccountSetting() const scrollContainer = screen.getByRole('dialog').querySelector('.overflow-y-auto') // Assert diff --git a/web/app/components/header/account-setting/index.tsx b/web/app/components/header/account-setting/index.tsx index f9c91b7d95..7e77af2e5f 100644 --- a/web/app/components/header/account-setting/index.tsx +++ b/web/app/components/header/account-setting/index.tsx @@ -27,9 +27,9 @@ const iconClassName = ` ` type IAccountSettingProps = { - onCancel: () => void - activeTab?: AccountSettingTab - onTabChange?: (tab: AccountSettingTab) => void + onCancelAction: () => void + activeTab: AccountSettingTab + onTabChangeAction: (tab: AccountSettingTab) => void } type GroupItem = { @@ -41,16 +41,12 @@ type GroupItem = { } export default function AccountSetting({ - onCancel, + onCancelAction, activeTab, - onTabChange, + onTabChangeAction, }: IAccountSettingProps) { const resetModelProviderListExpanded = useResetModelProviderListExpanded() - const isControlledTab = activeTab !== undefined && !!onTabChange - const [uncontrolledActiveMenu, setUncontrolledActiveMenu] = useState(activeTab ?? ACCOUNT_SETTING_TAB.MEMBERS) - const activeMenu = isControlledTab - ? (activeTab ?? ACCOUNT_SETTING_TAB.MEMBERS) - : uncontrolledActiveMenu + const activeMenu = activeTab const { t } = useTranslation() const { enableBilling, enableReplaceWebAppLogo } = useProviderContext() const { isCurrentWorkspaceDatasetOperator } = useAppContext() @@ -155,16 +151,13 @@ export default function AccountSetting({ if (tab === ACCOUNT_SETTING_TAB.PROVIDER) resetModelProviderListExpanded() - if (!isControlledTab) - setUncontrolledActiveMenu(tab) - - onTabChange?.(tab) - }, [isControlledTab, onTabChange, resetModelProviderListExpanded]) + onTabChangeAction(tab) + }, [onTabChangeAction, resetModelProviderListExpanded]) const handleClose = useCallback(() => { resetModelProviderListExpanded() - onCancel() - }, [onCancel, resetModelProviderListExpanded]) + onCancelAction() + }, [onCancelAction, resetModelProviderListExpanded]) return ( { menuItem.items.map(item => ( -
{ handleTabChange(item.key) @@ -197,7 +192,7 @@ export default function AccountSetting({ > {activeMenu === item.key ? item.activeIcon : item.icon} {!isMobile &&
{item.name}
} -
+ )) } @@ -212,6 +207,7 @@ export default function AccountSetting({ variant="tertiary" size="large" className="px-2" + aria-label={t('operation.close', { ns: 'common' })} onClick={handleClose} > diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx index d374447f6b..2206eac994 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/index.tsx @@ -125,9 +125,11 @@ const ProviderAddedCard: FC = ({ showCollapsedSection && (
{(showModelProvider || !notConfigured) && ( -
{ @@ -141,7 +143,7 @@ const ProviderAddedCard: FC = ({
) } -
+ )} {!showModelProvider && notConfigured && (
diff --git a/web/context/modal-context.tsx b/web/context/modal-context.tsx index ae1184e5bf..fdb3f837ea 100644 --- a/web/context/modal-context.tsx +++ b/web/context/modal-context.tsx @@ -343,8 +343,8 @@ export const ModalContextProvider = ({ accountSettingTab && ( ) }