@mintlify/common 1.0.1252 → 1.0.1253
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/navigation/clientNavView.d.ts +3 -0
- package/dist/navigation/clientNavView.js +44 -0
- package/dist/navigation/getScopedNavForPath.test.js +3 -1
- package/dist/navigation/scopeNavStateFlip.test.js +7 -3
- package/dist/navigation/scopeNavToPath.js +3 -2
- package/dist/navigation/trimScopedNavPages.d.ts +4 -0
- package/dist/navigation/trimScopedNavPages.js +134 -0
- package/dist/navigation/trimScopedNavPages.test.d.ts +1 -0
- package/dist/navigation/trimScopedNavPages.test.js +194 -0
- package/dist/paths/isEqualIgnoringLeadingSlash.d.ts +1 -0
- package/dist/paths/isEqualIgnoringLeadingSlash.js +4 -3
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { DecoratedPageOrGroupConfig } from '@mintlify/validation';
|
|
2
|
+
export declare function clientNavView(entries: DecoratedPageOrGroupConfig[]): unknown[];
|
|
3
|
+
export declare function paginationDescriptions(entries: DecoratedPageOrGroupConfig[], currentPath: string): [unknown, unknown];
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { isEqualIgnoringLeadingSlash } from '../paths/isEqualIgnoringLeadingSlash.js';
|
|
13
|
+
import { replaceSlashIndex } from '../slug/replaceSlashIndex.js';
|
|
14
|
+
import { isPage } from './isPage.js';
|
|
15
|
+
import { CLIENT_NAV_PAGE_FIELDS } from './trimScopedNavPages.js';
|
|
16
|
+
function clientPage(page) {
|
|
17
|
+
return Object.fromEntries(CLIENT_NAV_PAGE_FIELDS.filter((field) => Object.hasOwn(page, field)).map((field) => [
|
|
18
|
+
field,
|
|
19
|
+
page[field],
|
|
20
|
+
]));
|
|
21
|
+
}
|
|
22
|
+
export function clientNavView(entries) {
|
|
23
|
+
return entries.map((entry) => {
|
|
24
|
+
if (isPage(entry))
|
|
25
|
+
return clientPage(entry);
|
|
26
|
+
const { root, pages } = entry, rest = __rest(entry, ["root", "pages"]);
|
|
27
|
+
return Object.assign(Object.assign(Object.assign({}, rest), (root ? { root: clientPage(root) } : {})), { pages: clientNavView(pages) });
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function flattenPages(entries) {
|
|
31
|
+
return entries.flatMap((entry) => {
|
|
32
|
+
if (isPage(entry))
|
|
33
|
+
return [entry];
|
|
34
|
+
return [...(entry.root ? [entry.root] : []), ...flattenPages(entry.pages)];
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
export function paginationDescriptions(entries, currentPath) {
|
|
38
|
+
var _a, _b;
|
|
39
|
+
const pages = flattenPages(entries);
|
|
40
|
+
const index = pages.findIndex((page) => isEqualIgnoringLeadingSlash(replaceSlashIndex(page.href), currentPath));
|
|
41
|
+
if (index === -1)
|
|
42
|
+
return [undefined, undefined];
|
|
43
|
+
return [(_a = pages[index - 1]) === null || _a === void 0 ? void 0 : _a.description, (_b = pages[index + 1]) === null || _b === void 0 ? void 0 : _b.description];
|
|
44
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { clientNavView, paginationDescriptions } from './clientNavView.js';
|
|
1
2
|
import { decoratedNavigationConfig } from './decoratedNavigationConfig.js';
|
|
2
3
|
import { filterDivisions } from './filterDivisions.js';
|
|
3
4
|
import { getAllPathsInDecoratedNav } from './getAllPathsInDecoratedNav.js';
|
|
@@ -99,7 +100,8 @@ function assertInvariantForEveryPath(nav, includeProductVersionMetadata = false)
|
|
|
99
100
|
});
|
|
100
101
|
const full = filterWith(nav, currentPath, scoped.currentVersion, scoped.currentLanguage);
|
|
101
102
|
const rescoped = filterWith(scoped.scopedNav, currentPath, scoped.currentVersion, scoped.currentLanguage);
|
|
102
|
-
expect(rescoped.groupsOrPages, currentPath).toEqual(full.groupsOrPages);
|
|
103
|
+
expect(clientNavView(rescoped.groupsOrPages), currentPath).toEqual(clientNavView(full.groupsOrPages));
|
|
104
|
+
expect(paginationDescriptions(rescoped.groupsOrPages, currentPath), currentPath).toEqual(paginationDescriptions(full.groupsOrPages, currentPath));
|
|
103
105
|
expect(divisionSummary(rescoped), currentPath).toEqual(divisionSummary(full));
|
|
104
106
|
expect(mapHrefs(rescoped.firstHrefInVersion), currentPath).toEqual(mapHrefs(full.firstHrefInVersion));
|
|
105
107
|
expect(mapHrefs(rescoped.firstHrefInLanguage), currentPath).toEqual(mapHrefs(full.firstHrefInLanguage));
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isEqualIgnoringLeadingSlash } from '../paths/isEqualIgnoringLeadingSlash.js';
|
|
2
|
+
import { clientNavView, paginationDescriptions } from './clientNavView.js';
|
|
2
3
|
import { decoratedNavigationConfig } from './decoratedNavigationConfig.js';
|
|
3
4
|
import { filterDivisions } from './filterDivisions.js';
|
|
4
5
|
import { getAllPathsInDecoratedNav } from './getAllPathsInDecoratedNav.js';
|
|
@@ -56,7 +57,8 @@ function assertStateFlipSafety(nav) {
|
|
|
56
57
|
const label = `${currentPath} -> version ${version.name}`;
|
|
57
58
|
const flippedFull = filterWith(nav, currentPath, version.name, scoped.currentLanguage);
|
|
58
59
|
const flippedScoped = filterWith(scoped.scopedNav, currentPath, version.name, scoped.currentLanguage);
|
|
59
|
-
expect(flippedScoped.groupsOrPages, label).toEqual(flippedFull.groupsOrPages);
|
|
60
|
+
expect(clientNavView(flippedScoped.groupsOrPages), label).toEqual(clientNavView(flippedFull.groupsOrPages));
|
|
61
|
+
expect(paginationDescriptions(flippedScoped.groupsOrPages, currentPath), label).toEqual(paginationDescriptions(flippedFull.groupsOrPages, currentPath));
|
|
60
62
|
expect(divisionSummary(flippedScoped), label).toEqual(divisionSummary(flippedFull));
|
|
61
63
|
}
|
|
62
64
|
// deliberately unguarded (no stays check): even a flip to a stubbed,
|
|
@@ -66,7 +68,8 @@ function assertStateFlipSafety(nav) {
|
|
|
66
68
|
const label = `${currentPath} -> product ${product.name}`;
|
|
67
69
|
const flippedFull = filterWith(nav, currentPath, scoped.currentVersion, scoped.currentLanguage, product.name);
|
|
68
70
|
const flippedScoped = filterWith(scoped.scopedNav, currentPath, scoped.currentVersion, scoped.currentLanguage, product.name);
|
|
69
|
-
expect(flippedScoped.groupsOrPages, label).toEqual(flippedFull.groupsOrPages);
|
|
71
|
+
expect(clientNavView(flippedScoped.groupsOrPages), label).toEqual(clientNavView(flippedFull.groupsOrPages));
|
|
72
|
+
expect(paginationDescriptions(flippedScoped.groupsOrPages, currentPath), label).toEqual(paginationDescriptions(flippedFull.groupsOrPages, currentPath));
|
|
70
73
|
expect(divisionSummary(flippedScoped), label).toEqual(divisionSummary(flippedFull));
|
|
71
74
|
}
|
|
72
75
|
for (const language of full.languages) {
|
|
@@ -77,7 +80,8 @@ function assertStateFlipSafety(nav) {
|
|
|
77
80
|
const label = `${currentPath} -> language ${language.language}`;
|
|
78
81
|
const flippedFull = filterWith(nav, currentPath, scoped.currentVersion, language.language);
|
|
79
82
|
const flippedScoped = filterWith(scoped.scopedNav, currentPath, scoped.currentVersion, language.language);
|
|
80
|
-
expect(flippedScoped.groupsOrPages, label).toEqual(flippedFull.groupsOrPages);
|
|
83
|
+
expect(clientNavView(flippedScoped.groupsOrPages), label).toEqual(clientNavView(flippedFull.groupsOrPages));
|
|
84
|
+
expect(paginationDescriptions(flippedScoped.groupsOrPages, currentPath), label).toEqual(paginationDescriptions(flippedFull.groupsOrPages, currentPath));
|
|
81
85
|
expect(divisionSummary(flippedScoped), label).toEqual(divisionSummary(flippedFull));
|
|
82
86
|
}
|
|
83
87
|
}
|
|
@@ -18,6 +18,7 @@ import { filterDivisions } from './filterDivisions.js';
|
|
|
18
18
|
import { getFirstPageFromNavigation } from './getFirstPageFromNavigation.js';
|
|
19
19
|
import { getVersionOrLanguageFromPath } from './getVersionOrLanguageFromPath.js';
|
|
20
20
|
import { isPageGroups } from './groupAccess.js';
|
|
21
|
+
import { trimScopedNavPages } from './trimScopedNavPages.js';
|
|
21
22
|
import { pathsDifferByOneSegment } from './updateFirstHrefInDivision.js';
|
|
22
23
|
const SCOPED_DIVISION_KEYS = ['versions', 'languages'];
|
|
23
24
|
const DIVISION_TYPE_BY_KEY = {
|
|
@@ -621,7 +622,7 @@ export function getScopedNavForPath({ decoratedNav, currentPath, userGroups = ne
|
|
|
621
622
|
cache: { dropdownCache: new Map() },
|
|
622
623
|
isPreview,
|
|
623
624
|
});
|
|
624
|
-
const scopedNav = scopeNode(
|
|
625
|
+
const scopedNav = trimScopedNavPages(scopeNode(
|
|
625
626
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- structural nav traversal over the zod union types
|
|
626
627
|
decoratedNav, {
|
|
627
628
|
currentPath,
|
|
@@ -634,7 +635,7 @@ export function getScopedNavForPath({ decoratedNav, currentPath, userGroups = ne
|
|
|
634
635
|
isPreview,
|
|
635
636
|
skipVersionLanguageStubs: productAmbiguous,
|
|
636
637
|
includeProductVersionMetadata,
|
|
637
|
-
}
|
|
638
|
+
}), currentPath
|
|
638
639
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- structural nav traversal over the zod union types
|
|
639
640
|
);
|
|
640
641
|
return {
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
type NavNode = Record<string, unknown>;
|
|
2
|
+
export declare const CLIENT_NAV_PAGE_FIELDS: readonly ["href", "title", "hidden", "groups", "sidebarTitle", "url", "icon", "iconType", "tag", "api", "openapi", "asyncapi", "graphql", "deprecated", "hideApiMarker", "mode", "playground", "version"];
|
|
3
|
+
export declare function trimScopedNavPages(nav: NavNode, currentPath: string): NavNode;
|
|
4
|
+
export {};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { normalizeComparablePath } from '../paths/isEqualIgnoringLeadingSlash.js';
|
|
2
|
+
import { replaceSlashIndex } from '../slug/replaceSlashIndex.js';
|
|
3
|
+
function pageFieldAllowlist(fields) {
|
|
4
|
+
return fields;
|
|
5
|
+
}
|
|
6
|
+
export const CLIENT_NAV_PAGE_FIELDS = pageFieldAllowlist([
|
|
7
|
+
'href',
|
|
8
|
+
'title',
|
|
9
|
+
'hidden',
|
|
10
|
+
'groups',
|
|
11
|
+
'sidebarTitle',
|
|
12
|
+
'url',
|
|
13
|
+
'icon',
|
|
14
|
+
'iconType',
|
|
15
|
+
'tag',
|
|
16
|
+
'api',
|
|
17
|
+
'openapi',
|
|
18
|
+
'asyncapi',
|
|
19
|
+
'graphql',
|
|
20
|
+
'deprecated',
|
|
21
|
+
'hideApiMarker',
|
|
22
|
+
'mode',
|
|
23
|
+
'playground',
|
|
24
|
+
'version',
|
|
25
|
+
]);
|
|
26
|
+
const isNavNode = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
27
|
+
const isPageNode = (node) => Object.hasOwn(node, 'href') && Object.hasOwn(node, 'title');
|
|
28
|
+
const hrefMatches = (href, target) => typeof href === 'string' && normalizeComparablePath(replaceSlashIndex(href)) === target;
|
|
29
|
+
function collectPagesInOrder(node, out, inHidden = false) {
|
|
30
|
+
if (Array.isArray(node)) {
|
|
31
|
+
for (const entry of node)
|
|
32
|
+
collectPagesInOrder(entry, out, inHidden);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (!isNavNode(node))
|
|
36
|
+
return;
|
|
37
|
+
if (isPageNode(node)) {
|
|
38
|
+
out.push({
|
|
39
|
+
page: node,
|
|
40
|
+
visibleToEveryone: !inHidden && node.hidden !== true && node.groups === undefined,
|
|
41
|
+
});
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const hidden = inHidden || node.hidden === true;
|
|
45
|
+
if (isNavNode(node.root) && isPageNode(node.root))
|
|
46
|
+
collectPagesInOrder(node.root, out, hidden);
|
|
47
|
+
for (const [key, value] of Object.entries(node)) {
|
|
48
|
+
if (key === 'root' || key === 'global')
|
|
49
|
+
continue;
|
|
50
|
+
if (Array.isArray(value))
|
|
51
|
+
collectPagesInOrder(value, out, hidden);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function addPaginationNeighbors(nav, target, keep) {
|
|
55
|
+
const pages = [];
|
|
56
|
+
collectPagesInOrder(nav, pages);
|
|
57
|
+
pages.forEach(({ page }, index) => {
|
|
58
|
+
if (!hrefMatches(page.href, target))
|
|
59
|
+
return;
|
|
60
|
+
keepUntilVisible(pages, index, -1, keep);
|
|
61
|
+
keepUntilVisible(pages, index, 1, keep);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
function keepUntilVisible(pages, from, step, keep) {
|
|
65
|
+
for (let i = from + step; i >= 0 && i < pages.length; i += step) {
|
|
66
|
+
const entry = pages[i];
|
|
67
|
+
if (!entry)
|
|
68
|
+
return;
|
|
69
|
+
keep.add(entry.page);
|
|
70
|
+
if (entry.visibleToEveryone)
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function addDirectPages(group, keep) {
|
|
75
|
+
if (!Array.isArray(group.pages))
|
|
76
|
+
return;
|
|
77
|
+
for (const entry of group.pages) {
|
|
78
|
+
if (isNavNode(entry) && isPageNode(entry))
|
|
79
|
+
keep.add(entry);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function addDirectoryChildren(node, target, keep) {
|
|
83
|
+
if (Array.isArray(node)) {
|
|
84
|
+
for (const entry of node)
|
|
85
|
+
addDirectoryChildren(entry, target, keep);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (!isNavNode(node) || isPageNode(node))
|
|
89
|
+
return;
|
|
90
|
+
if (isNavNode(node.root) && hrefMatches(node.root.href, target)) {
|
|
91
|
+
addDirectPages(node, keep);
|
|
92
|
+
if (Array.isArray(node.pages)) {
|
|
93
|
+
for (const entry of node.pages) {
|
|
94
|
+
if (isNavNode(entry) && !isPageNode(entry))
|
|
95
|
+
addDirectPages(entry, keep);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
for (const [key, value] of Object.entries(node)) {
|
|
100
|
+
if (key !== 'global' && Array.isArray(value))
|
|
101
|
+
addDirectoryChildren(value, target, keep);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const CLIENT_FIELDS = new Set(CLIENT_NAV_PAGE_FIELDS);
|
|
105
|
+
function trimPage(page, keepDescription) {
|
|
106
|
+
const trimmed = {};
|
|
107
|
+
for (const key of Object.keys(page)) {
|
|
108
|
+
if (CLIENT_FIELDS.has(key) || (keepDescription && key === 'description')) {
|
|
109
|
+
trimmed[key] = page[key];
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return trimmed;
|
|
113
|
+
}
|
|
114
|
+
function trimNode(node, keepDescription) {
|
|
115
|
+
if (Array.isArray(node))
|
|
116
|
+
return node.map((entry) => trimNode(entry, keepDescription));
|
|
117
|
+
if (!isNavNode(node))
|
|
118
|
+
return node;
|
|
119
|
+
if (isPageNode(node))
|
|
120
|
+
return trimPage(node, keepDescription.has(node));
|
|
121
|
+
const trimmed = {};
|
|
122
|
+
for (const [key, value] of Object.entries(node)) {
|
|
123
|
+
trimmed[key] = key === 'global' ? value : trimNode(value, keepDescription);
|
|
124
|
+
}
|
|
125
|
+
return trimmed;
|
|
126
|
+
}
|
|
127
|
+
export function trimScopedNavPages(nav, currentPath) {
|
|
128
|
+
const keepDescription = new Set();
|
|
129
|
+
const target = normalizeComparablePath(replaceSlashIndex(currentPath));
|
|
130
|
+
addPaginationNeighbors(nav, target, keepDescription);
|
|
131
|
+
addDirectoryChildren(nav, target, keepDescription);
|
|
132
|
+
const trimmed = trimNode(nav, keepDescription);
|
|
133
|
+
return isNavNode(trimmed) ? trimmed : nav;
|
|
134
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { trimScopedNavPages } from './trimScopedNavPages.js';
|
|
2
|
+
const page = (slug, extra = {}) => (Object.assign({ title: slug, href: `/${slug}`, description: `${slug} description` }, extra));
|
|
3
|
+
function descriptions(nav) {
|
|
4
|
+
const out = {};
|
|
5
|
+
const walk = (node) => {
|
|
6
|
+
if (Array.isArray(node))
|
|
7
|
+
return node.forEach(walk);
|
|
8
|
+
if (typeof node !== 'object' || node === null)
|
|
9
|
+
return;
|
|
10
|
+
if ('href' in node && 'title' in node && typeof node.href === 'string') {
|
|
11
|
+
out[node.href] = 'description' in node ? node.description : undefined;
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
Object.values(node).forEach(walk);
|
|
15
|
+
};
|
|
16
|
+
walk(nav);
|
|
17
|
+
return out;
|
|
18
|
+
}
|
|
19
|
+
describe('trimScopedNavPages', () => {
|
|
20
|
+
it('keeps only client fields on pages', () => {
|
|
21
|
+
const nav = {
|
|
22
|
+
pages: [
|
|
23
|
+
page('a', {
|
|
24
|
+
sidebarTitle: 'A',
|
|
25
|
+
openapi: 'get /a',
|
|
26
|
+
deprecated: true,
|
|
27
|
+
playground: 'simple',
|
|
28
|
+
version: 'v2',
|
|
29
|
+
mode: 'wide',
|
|
30
|
+
autogeneratedByOpenApi: true,
|
|
31
|
+
authors: [{ name: 'x' }],
|
|
32
|
+
keywords: ['k'],
|
|
33
|
+
customFrontmatter: 'x',
|
|
34
|
+
}),
|
|
35
|
+
],
|
|
36
|
+
};
|
|
37
|
+
expect(trimScopedNavPages(nav, '/a')).toEqual({
|
|
38
|
+
pages: [
|
|
39
|
+
{
|
|
40
|
+
title: 'a',
|
|
41
|
+
href: '/a',
|
|
42
|
+
sidebarTitle: 'A',
|
|
43
|
+
openapi: 'get /a',
|
|
44
|
+
deprecated: true,
|
|
45
|
+
playground: 'simple',
|
|
46
|
+
version: 'v2',
|
|
47
|
+
mode: 'wide',
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
it('keeps descriptions only on pagination neighbors', () => {
|
|
53
|
+
const nav = {
|
|
54
|
+
groups: [
|
|
55
|
+
{ group: 'One', pages: [page('a'), page('b')] },
|
|
56
|
+
{ group: 'Two', pages: [page('c'), page('d')] },
|
|
57
|
+
],
|
|
58
|
+
};
|
|
59
|
+
expect(descriptions(trimScopedNavPages(nav, '/b'))).toEqual({
|
|
60
|
+
'/a': 'a description',
|
|
61
|
+
'/b': undefined,
|
|
62
|
+
'/c': 'c description',
|
|
63
|
+
'/d': undefined,
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
it('orders a group root before its pages', () => {
|
|
67
|
+
const nav = {
|
|
68
|
+
groups: [{ group: 'G', root: page('root'), pages: [page('a'), page('b')] }],
|
|
69
|
+
};
|
|
70
|
+
expect(descriptions(trimScopedNavPages(nav, '/a'))).toEqual({
|
|
71
|
+
'/root': 'root description',
|
|
72
|
+
'/a': undefined,
|
|
73
|
+
'/b': 'b description',
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
it('steps past neighbors some viewers cannot see', () => {
|
|
77
|
+
const nav = {
|
|
78
|
+
pages: [
|
|
79
|
+
page('a'),
|
|
80
|
+
page('hidden', { hidden: true }),
|
|
81
|
+
page('current'),
|
|
82
|
+
page('gated', { groups: ['admin'] }),
|
|
83
|
+
page('b'),
|
|
84
|
+
page('c'),
|
|
85
|
+
],
|
|
86
|
+
};
|
|
87
|
+
expect(descriptions(trimScopedNavPages(nav, '/current'))).toEqual({
|
|
88
|
+
'/a': 'a description',
|
|
89
|
+
'/hidden': 'hidden description',
|
|
90
|
+
'/current': undefined,
|
|
91
|
+
'/gated': 'gated description',
|
|
92
|
+
'/b': 'b description',
|
|
93
|
+
'/c': undefined,
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
it('steps past pages under a hidden group or division', () => {
|
|
97
|
+
const nav = {
|
|
98
|
+
tabs: [
|
|
99
|
+
{
|
|
100
|
+
tab: 'Docs',
|
|
101
|
+
groups: [
|
|
102
|
+
{ group: 'One', pages: [page('current')] },
|
|
103
|
+
{ group: 'Drafts', hidden: true, root: page('draft-root'), pages: [page('draft')] },
|
|
104
|
+
{ group: 'Two', pages: [page('next'), page('after')] },
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
{ tab: 'Hidden', hidden: true, pages: [page('hidden-tab')] },
|
|
108
|
+
{ tab: 'Other', pages: [page('other')] },
|
|
109
|
+
],
|
|
110
|
+
};
|
|
111
|
+
expect(descriptions(trimScopedNavPages(nav, '/current'))).toEqual({
|
|
112
|
+
'/current': undefined,
|
|
113
|
+
'/draft-root': 'draft-root description',
|
|
114
|
+
'/draft': 'draft description',
|
|
115
|
+
'/next': 'next description',
|
|
116
|
+
'/after': undefined,
|
|
117
|
+
'/hidden-tab': undefined,
|
|
118
|
+
'/other': undefined,
|
|
119
|
+
});
|
|
120
|
+
expect(descriptions(trimScopedNavPages(nav, '/after'))).toMatchObject({
|
|
121
|
+
'/hidden-tab': 'hidden-tab description',
|
|
122
|
+
'/other': 'other description',
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
it('keeps descriptions a directory root lists', () => {
|
|
126
|
+
const nav = {
|
|
127
|
+
groups: [
|
|
128
|
+
{
|
|
129
|
+
group: 'Dir',
|
|
130
|
+
directory: 'card',
|
|
131
|
+
root: page('dir'),
|
|
132
|
+
pages: [
|
|
133
|
+
page('child'),
|
|
134
|
+
{
|
|
135
|
+
group: 'Sub',
|
|
136
|
+
pages: [page('grandchild'), { group: 'Deep', pages: [page('deep')] }],
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
},
|
|
140
|
+
{ group: 'Other', pages: [page('x'), page('y')] },
|
|
141
|
+
],
|
|
142
|
+
};
|
|
143
|
+
expect(descriptions(trimScopedNavPages(nav, '/dir'))).toEqual({
|
|
144
|
+
'/dir': undefined,
|
|
145
|
+
'/child': 'child description',
|
|
146
|
+
'/grandchild': 'grandchild description',
|
|
147
|
+
'/deep': undefined,
|
|
148
|
+
'/x': undefined,
|
|
149
|
+
'/y': undefined,
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
it('matches index hrefs like the client', () => {
|
|
153
|
+
const nav = { pages: [page('a'), page('guide/index'), page('b')] };
|
|
154
|
+
expect(descriptions(trimScopedNavPages(nav, '/guide'))).toEqual({
|
|
155
|
+
'/a': 'a description',
|
|
156
|
+
'/guide/index': undefined,
|
|
157
|
+
'/b': 'b description',
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
it('leaves division descriptions and global links alone', () => {
|
|
161
|
+
const nav = {
|
|
162
|
+
global: {
|
|
163
|
+
anchors: [{ anchor: 'Blog', href: 'https://example.com', description: 'kept' }],
|
|
164
|
+
},
|
|
165
|
+
dropdowns: [
|
|
166
|
+
{
|
|
167
|
+
dropdown: 'D',
|
|
168
|
+
description: 'dropdown description',
|
|
169
|
+
pages: [page('a')],
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
};
|
|
173
|
+
expect(trimScopedNavPages(nav, '/a')).toEqual({
|
|
174
|
+
global: {
|
|
175
|
+
anchors: [{ anchor: 'Blog', href: 'https://example.com', description: 'kept' }],
|
|
176
|
+
},
|
|
177
|
+
dropdowns: [
|
|
178
|
+
{
|
|
179
|
+
dropdown: 'D',
|
|
180
|
+
description: 'dropdown description',
|
|
181
|
+
pages: [{ title: 'a', href: '/a' }],
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
it('does not mutate the input', () => {
|
|
187
|
+
const nav = {
|
|
188
|
+
pages: [page('a', { keywords: ['k'] }), page('b'), page('c')],
|
|
189
|
+
};
|
|
190
|
+
const before = structuredClone(nav);
|
|
191
|
+
trimScopedNavPages(nav, '/a');
|
|
192
|
+
expect(nav).toEqual(before);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
@@ -15,7 +15,8 @@ export function isEqualIgnoringLeadingSlash(path1, path2) {
|
|
|
15
15
|
if (path1 == null || path2 == null || typeof path1 !== 'string' || typeof path2 !== 'string') {
|
|
16
16
|
return false;
|
|
17
17
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
return normalizeComparablePath(path1) === normalizeComparablePath(path2);
|
|
19
|
+
}
|
|
20
|
+
export function normalizeComparablePath(path) {
|
|
21
|
+
return normalizeIndexPath(optionallyRemoveTrailingSlash(optionallyRemoveLeadingSlash(path)));
|
|
21
22
|
}
|