diff --git a/eslint-suppressions.json b/eslint-suppressions.json index 9cb80eb758..87230e947e 100644 --- a/eslint-suppressions.json +++ b/eslint-suppressions.json @@ -192,11 +192,6 @@ "count": 1 } }, - "web/app/(commonLayout)/snippets/[snippetId]/page.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "web/app/(shareLayout)/components/authenticated-layout.tsx": { "jsx-a11y/click-events-have-key-events": { "count": 1 diff --git a/web/app/(commonLayout)/snippets/[snippetId]/page.tsx b/web/app/(commonLayout)/snippets/[snippetId]/page.tsx index 3b35e29360..2aea2751f0 100644 --- a/web/app/(commonLayout)/snippets/[snippetId]/page.tsx +++ b/web/app/(commonLayout)/snippets/[snippetId]/page.tsx @@ -1,4 +1,4 @@ -import { redirect } from 'next/navigation' +import { redirect } from '@/next/navigation' const Page = async (props: { params: Promise<{ snippetId: string }> diff --git a/web/app/components/apps/__tests__/list.spec.tsx b/web/app/components/apps/__tests__/list.spec.tsx index 52af9b17f4..22c86a1fbe 100644 --- a/web/app/components/apps/__tests__/list.spec.tsx +++ b/web/app/components/apps/__tests__/list.spec.tsx @@ -417,15 +417,17 @@ describe('List', () => { expect(screen.getByRole('button', { name: 'common.operation.create' }))!.toBeInTheDocument() }) - it('should render link to snippets before the create button', () => { + it('should render sort filter before search and the snippets link', () => { renderList() const sortButton = screen.getByRole('button', { name: 'Sort by Last modified' }) + const searchInput = screen.getByRole('searchbox', { name: 'app.gotoAnything.actions.searchApplications' }) const snippetsLink = screen.getByRole('link', { name: 'app.studio.viewSnippets' }) const createButton = screen.getByRole('button', { name: 'common.operation.create' }) expect(snippetsLink).toHaveAttribute('href', '/snippets') - expect(sortButton.compareDocumentPosition(snippetsLink) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy() + expect(sortButton.compareDocumentPosition(searchInput) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy() + expect(searchInput.compareDocumentPosition(snippetsLink) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy() expect(snippetsLink.compareDocumentPosition(createButton) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy() }) diff --git a/web/app/components/apps/app-list-header-filters.tsx b/web/app/components/apps/app-list-header-filters.tsx index b0a2319dad..df32f57d7b 100644 --- a/web/app/components/apps/app-list-header-filters.tsx +++ b/web/app/components/apps/app-list-header-filters.tsx @@ -62,6 +62,7 @@ export function AppListHeaderFilters({ showLeadingIcon={false} /> +
- { expect(screen.getByRole('link', { name: /common.mainNav.home/ })).not.toHaveAttribute('aria-current') }) + it('hides the main menu on snippet detail routes while keeping account settings available', () => { + mockPathname = '/snippets/snippet-1/orchestrate' + + renderMainNav() + + expect(screen.getByRole('complementary')).toHaveClass('w-16') + expect(screen.queryByLabelText('Dify')).not.toBeInTheDocument() + expect(screen.queryByRole('button', { name: 'common.mainNav.workspace.openMenu' })).not.toBeInTheDocument() + expect(screen.queryByRole('link', { name: /common.mainNav.home/ })).not.toBeInTheDocument() + expect(screen.queryByRole('link', { name: /common.menus.apps/ })).not.toBeInTheDocument() + expect(screen.queryByRole('button', { name: 'explore.sidebar.webApps' })).not.toBeInTheDocument() + expect(screen.getByRole('button', { name: 'common.account.account' })).toBeInTheDocument() + expect(screen.getByRole('button', { name: 'common.mainNav.help.openMenu' })).toBeInTheDocument() + }) + it('replaces global navigation with app detail navigation on app routes', () => { mockPathname = '/app/app-1/overview' diff --git a/web/app/components/main-nav/index.tsx b/web/app/components/main-nav/index.tsx index c8f6950c9c..ccb149978d 100644 --- a/web/app/components/main-nav/index.tsx +++ b/web/app/components/main-nav/index.tsx @@ -60,6 +60,12 @@ const isDatasetDetailPathname = (pathname: string) => { return true } +const isSnippetDetailPathname = (pathname: string) => { + const [section, snippetId] = pathname.split('/').filter(Boolean) + + return section === 'snippets' && !!snippetId +} + const MainNav = ({ className, }: MainNavProps) => { @@ -70,6 +76,7 @@ const MainNav = ({ const showEnvTag = langGeniusVersionInfo.current_env === 'TESTING' || langGeniusVersionInfo.current_env === 'DEVELOPMENT' const showAppDetailNavigation = !isCurrentWorkspaceDatasetOperator && pathname.startsWith('/app/') const showDatasetDetailNavigation = isDatasetDetailPathname(pathname) + const showSnippetDetailBottomNavigation = isSnippetDetailPathname(pathname) const showDetailNavigation = showAppDetailNavigation || showDatasetDetailNavigation const { hasAppDetail, appSidebarExpand, setAppDetail, setAppSidebarExpand } = useAppStore(useShallow(state => ({ hasAppDetail: !!state.appDetail, @@ -87,7 +94,9 @@ const MainNav = ({ const detailNavigationTransitionTimerRef = useRef | null>(null) const isDetailNavigationHoverPreviewOpen = isCollapsedDetailNavigation && detailNavigationHoverPreviewOpen const detailNavigationVisibleExpanded = detailNavigationExpanded || isDetailNavigationHoverPreviewOpen - const bottomNavigationExpanded = !showDetailNavigation || detailNavigationVisibleExpanded + const bottomNavigationExpanded = showSnippetDetailBottomNavigation + ? false + : !showDetailNavigation || detailNavigationVisibleExpanded const handleToggleDetailNavigation = useCallback(() => { if (isDetailNavigationHoverPreviewOpen) { if (detailNavigationTransitionTimerRef.current) @@ -234,7 +243,9 @@ const MainNav = ({ ? detailNavigationExpanded ? 'w-[248px] bg-background-body p-1' : 'w-16 bg-background-body p-1' - : 'w-60 flex-col', + : showSnippetDetailBottomNavigation + ? 'w-16 bg-background-body p-1' + : 'w-60 flex-col', 'bg-background-body', className, )} @@ -267,32 +278,36 @@ const MainNav = ({ onToggle={handleToggleDetailNavigation} /> ) - : ( - <> -
- {renderLogo()} - -
-
- -
- - )} + : showSnippetDetailBottomNavigation + ? null + : ( + <> +
+ {renderLogo()} + +
+
+ +
+ + )} {showDetailNavigation ? showAppDetailNavigation ? : - : ( - <> - - {!isCurrentWorkspaceDatasetOperator && } - - )} - {showEnvTag && detailNavigationVisibleExpanded && ( + : showSnippetDetailBottomNavigation + ? null + : ( + <> + + {!isCurrentWorkspaceDatasetOperator && } + + )} + {showEnvTag && !showSnippetDetailBottomNavigation && detailNavigationVisibleExpanded && (