diff --git a/e2e/tests/auto-nav-sidebar-dir-convension.test.ts b/e2e/tests/auto-nav-sidebar-dir-convension.test.ts index d9bf5c19c..05e3c25a8 100644 --- a/e2e/tests/auto-nav-sidebar-dir-convension.test.ts +++ b/e2e/tests/auto-nav-sidebar-dir-convension.test.ts @@ -112,4 +112,51 @@ test.describe('Auto nav and sidebar dir convention', async () => { ); expect(contexts3.join(',')).toEqual(['context-index-in-meta'].join(',')); }); + + test('/api/config/index.html /api/config/index /api/config should be the same page', async ({ + page, + }) => { + async function getSidebarLength(): Promise { + return ( + (await page.$$( + `.rspress-sidebar .rspress-scrollbar > nav > section, + .rspress-sidebar .rspress-scrollbar > nav > a`, + )) ?? [] + ).length; + } + + async function isMenuItemActive(): Promise { + const activeMenuItem = await page.$( + '.rspress-sidebar-collapse[class*="menuItemActive"]', + ); + const content = await activeMenuItem?.textContent(); + return content === 'index md convention'; + } + // /api/config/index.html + await page.goto( + `http://localhost:${appPort}/guide/index-md-convention/index.html`, + { + waitUntil: 'networkidle', + }, + ); + expect(await getSidebarLength()).toBe(7); + expect(await isMenuItemActive()).toBe(true); + + // /api/config/index + await page.goto( + `http://localhost:${appPort}/guide/index-md-convention/index`, + { + waitUntil: 'networkidle', + }, + ); + expect(await getSidebarLength()).toBe(7); + expect(await isMenuItemActive()).toBe(true); + + // /api/config + await page.goto(`http://localhost:${appPort}/guide/index-md-convention`, { + waitUntil: 'networkidle', + }); + expect(await getSidebarLength()).toBe(7); + expect(await isMenuItemActive()).toBe(true); + }); }); diff --git a/packages/theme-default/src/components/Sidebar/index.tsx b/packages/theme-default/src/components/Sidebar/index.tsx index 5ad723197..53dad7842 100644 --- a/packages/theme-default/src/components/Sidebar/index.tsx +++ b/packages/theme-default/src/components/Sidebar/index.tsx @@ -137,12 +137,13 @@ export function Sidebar(props: Props) { const removeLangPrefix = (path: string) => { return path.replace(langRoutePrefix, ''); }; - const activeMatcher = (path: string) => - isActive( + const activeMatcher = (path: string) => { + return isActive( removeBase(removeLangPrefix(pathname)), removeLangPrefix(path), true, ); + }; const preloadLink = (link: string) => { const match = matchRoutes(routes, link); if (match?.length) { diff --git a/packages/theme-default/src/index.ts b/packages/theme-default/src/index.ts index 0731e42b1..cca2fe33a 100644 --- a/packages/theme-default/src/index.ts +++ b/packages/theme-default/src/index.ts @@ -1,9 +1,9 @@ // eslint-disable-next-line @typescript-eslint/no-unused-vars import React from 'react'; -import { NotFoundLayout } from './layout/NotFountLayout'; -import { Layout } from './layout/Layout'; -import { HomeLayout } from './layout/HomeLayout'; import { DocLayout } from './layout/DocLayout'; +import { HomeLayout } from './layout/HomeLayout'; +import { Layout } from './layout/Layout'; +import { NotFoundLayout } from './layout/NotFountLayout'; import { setup } from './logic'; export default { diff --git a/packages/theme-default/src/logic/useSidebarData.ts b/packages/theme-default/src/logic/useSidebarData.ts index 70ad48a96..9d393254e 100644 --- a/packages/theme-default/src/logic/useSidebarData.ts +++ b/packages/theme-default/src/logic/useSidebarData.ts @@ -1,12 +1,12 @@ +import { isEqualPath, useLocation, withBase } from '@rspress/runtime'; import { - type NormalizedSidebarGroup, - type SidebarItem, type NormalizedSidebar, + type NormalizedSidebarGroup, type SidebarDivider, + type SidebarItem, addTrailingSlash, } from '@rspress/shared'; import { useEffect, useState } from 'react'; -import { useLocation, withBase, isEqualPath } from '@rspress/runtime'; import { useLocaleSiteData } from './useLocaleSiteData'; interface SidebarData { @@ -65,6 +65,7 @@ export const getSidebarGroupData = ( currentPathname.startsWith( // https://github.com/web-infra-dev/rspress/issues/360 // Ensure the other group name ends with `/` to avoid some unexpected results, for example, `/react-native` will match `/react`, that's not what we want + // FIXME: should parse url instead of add trailing slash addTrailingSlash(withBase(otherGroupName)), ) ) { @@ -74,20 +75,42 @@ export const getSidebarGroupData = ( } } } - const equalFunc = () => - 'link' in item && - item.link !== '' && - isEqualPath(withBase(item.link), currentPathname); - if ('items' in item) { - // If the current path is the same as the group link, return true - if (equalFunc()) { + const isLink = 'link' in item && item.link !== ''; + const isDir = 'items' in item; + + // 0. divider or section headers others return false + + // 1. file link + if (!isDir && isLink) { + // 1.1 /api/config /api/config.html + if (isEqualPath(withBase(item.link), currentPathname)) { + return true; + } + // 1.2 /api/config/index /api/config/index.html + if ( + currentPathname.includes('index') && + isEqualPath(`${item.link}/index`, currentPathname) + ) { + return true; + } + } + + // 2. dir + if (isDir) { + // 2.1 dir link (index convention) + if ( + isLink && + (isEqualPath(withBase(item.link), currentPathname) || + isEqualPath(withBase(`${item.link}/index`), currentPathname)) + ) { return true; } + // 2.2 dir recursive return item.items.some(i => match(i)); } - return equalFunc(); + return false; }; return match(group); diff --git a/packages/theme-default/src/logic/utils.ts b/packages/theme-default/src/logic/utils.ts index 0dd7b92d8..2362b8423 100644 --- a/packages/theme-default/src/logic/utils.ts +++ b/packages/theme-default/src/logic/utils.ts @@ -1,9 +1,9 @@ -// eslint-disable-next-line @typescript-eslint/no-unused-vars -import React from 'react'; -import htmr from 'htmr'; -import { parseDocument } from 'htmlparser2'; import { isEqualPath } from '@rspress/runtime'; +import { parseDocument } from 'htmlparser2'; +import htmr from 'htmr'; import { isNumber } from 'lodash-es'; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import React from 'react'; export function isActive( currentPath: string, @@ -14,7 +14,10 @@ export function isActive( return false; } if (strict) { - return isEqualPath(currentPath, targetLink); + return ( + isEqualPath(currentPath, targetLink) || + isEqualPath(currentPath, `${targetLink}/index`) + ); } return (