@f5-sales-demo/docs-theme 4.7.1 → 4.9.0
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.
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
---
|
|
2
|
+
import MobileMenuFooter from 'virtual:starlight/components/MobileMenuFooter';
|
|
3
|
+
import SidebarPersister from '@astrojs/starlight/components/SidebarPersister.astro';
|
|
4
|
+
import SidebarSublist from '@astrojs/starlight/components/SidebarSublist.astro';
|
|
5
|
+
import SidebarRestorePoint from '@astrojs/starlight/components/SidebarRestorePoint.astro';
|
|
6
|
+
|
|
7
|
+
const { sidebar } = Astro.locals.starlightRoute;
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
<SidebarPersister>
|
|
11
|
+
<ul class="canada-sidebar">
|
|
12
|
+
{sidebar.map((entry) => {
|
|
13
|
+
if (entry.type === 'link') {
|
|
14
|
+
return <li><a class="home-link" href={entry.href} aria-current={entry.isCurrent ? 'page' : undefined}>{entry.label}</a></li>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const [overview, ...children] = entry.entries;
|
|
18
|
+
if (!overview || overview.type !== 'link') {
|
|
19
|
+
return <li><SidebarSublist sublist={[entry]} /></li>;
|
|
20
|
+
}
|
|
21
|
+
const active = overview.isCurrent || children.some((child) => child.type === 'link' && child.isCurrent);
|
|
22
|
+
|
|
23
|
+
return <li class="stage">
|
|
24
|
+
<a class="stage-link" href={overview.href} aria-current={overview.isCurrent ? 'page' : undefined}>{entry.label}</a>
|
|
25
|
+
<details open={active || !entry.collapsed}>
|
|
26
|
+
<summary aria-label={`Toggle ${entry.label} pages`} title={`Toggle ${entry.label} pages`}><span class="caret" aria-hidden="true"></span></summary>
|
|
27
|
+
<SidebarRestorePoint />
|
|
28
|
+
<SidebarSublist sublist={children} nested />
|
|
29
|
+
</details>
|
|
30
|
+
</li>;
|
|
31
|
+
})}
|
|
32
|
+
</ul>
|
|
33
|
+
</SidebarPersister>
|
|
34
|
+
|
|
35
|
+
<div class="md:sl-hidden"><MobileMenuFooter /></div>
|
|
36
|
+
|
|
37
|
+
<style>
|
|
38
|
+
@layer starlight.core {
|
|
39
|
+
.canada-sidebar { list-style: none; margin: 0; padding: 0; }
|
|
40
|
+
.canada-sidebar > li + li { margin-top: 0.5rem; }
|
|
41
|
+
.stage { position: relative; }
|
|
42
|
+
.home-link, .stage-link {
|
|
43
|
+
display: block;
|
|
44
|
+
border-radius: 0.25rem;
|
|
45
|
+
color: var(--sl-color-gray-2);
|
|
46
|
+
line-height: 1.4;
|
|
47
|
+
padding: 0.3em 2.5rem 0.3em 0.5rem;
|
|
48
|
+
text-decoration: none;
|
|
49
|
+
font-size: var(--sl-text-sm);
|
|
50
|
+
}
|
|
51
|
+
.stage-link { color: var(--sl-color-white); font-weight: 600; }
|
|
52
|
+
.home-link:hover, .home-link:focus, .stage-link:hover, .stage-link:focus { color: var(--sl-color-white); }
|
|
53
|
+
a[aria-current='page'], a[aria-current='page']:hover, a[aria-current='page']:focus {
|
|
54
|
+
color: var(--sl-color-text-invert);
|
|
55
|
+
background-color: var(--sl-color-text-accent);
|
|
56
|
+
font-weight: 600;
|
|
57
|
+
}
|
|
58
|
+
summary {
|
|
59
|
+
position: absolute;
|
|
60
|
+
inset-block-start: 0;
|
|
61
|
+
inset-inline-end: 0;
|
|
62
|
+
display: grid;
|
|
63
|
+
place-items: center;
|
|
64
|
+
width: 2rem;
|
|
65
|
+
height: 2rem;
|
|
66
|
+
cursor: pointer;
|
|
67
|
+
border-radius: 0.25rem;
|
|
68
|
+
list-style: none;
|
|
69
|
+
}
|
|
70
|
+
summary::-webkit-details-marker { display: none; }
|
|
71
|
+
summary:hover, summary:focus-visible { color: var(--sl-color-white); background: var(--sl-color-gray-5); }
|
|
72
|
+
.caret { width: 0.55rem; height: 0.55rem; border-block-start: 2px solid currentColor; border-inline-end: 2px solid currentColor; transform: rotate(45deg); transition: transform 0.2s ease; }
|
|
73
|
+
details[open] .caret { transform: rotate(135deg); }
|
|
74
|
+
:global([dir='rtl']) .caret { transform: rotate(225deg); }
|
|
75
|
+
:global([dir='rtl']) details[open] .caret { transform: rotate(135deg); }
|
|
76
|
+
}
|
|
77
|
+
</style>
|
package/config.ts
CHANGED
|
@@ -35,7 +35,7 @@ import canonicalLlms from './src/utils/canonical-llms.mjs';
|
|
|
35
35
|
import { providerMegaMenu } from './src/utils/canonical-provider.ts';
|
|
36
36
|
import { menuLocalizationPolicy } from './src/utils/menu-localization.ts';
|
|
37
37
|
import { stagedProviderSections } from './src/utils/provider-sections.ts';
|
|
38
|
-
import { repositoryPublicationProfile } from './src/utils/publication-profiles.ts';
|
|
38
|
+
import { publicationSidebar, repositoryPublicationProfile } from './src/utils/publication-profiles.ts';
|
|
39
39
|
import { resolveMegaMenuIcon } from './src/utils/resolve-icon.ts';
|
|
40
40
|
import providerSharedAssets from './src/utils/share-provider-assets.mjs';
|
|
41
41
|
import { buildSubcategorySidebar } from './src/utils/subcategory-sidebar.ts';
|
|
@@ -782,7 +782,7 @@ export function createF5xcDocsConfig(options: F5xcDocsConfigOptions = {}) {
|
|
|
782
782
|
starlightMegaMenu({ items: megaMenuItems, mobileLabels }),
|
|
783
783
|
starlightVideosPlugin(),
|
|
784
784
|
starlightImageZoom(),
|
|
785
|
-
f5xcDocsTheme(),
|
|
785
|
+
f5xcDocsTheme(githubRepository),
|
|
786
786
|
starlightScrollToTop({
|
|
787
787
|
showTooltip: true,
|
|
788
788
|
tooltipText: {
|
|
@@ -843,7 +843,9 @@ export function createF5xcDocsConfig(options: F5xcDocsConfigOptions = {}) {
|
|
|
843
843
|
];
|
|
844
844
|
|
|
845
845
|
const contentDir = process.env.CONTENT_DIR || 'src/content/docs';
|
|
846
|
-
const subcategorySidebar = canonicalProvider
|
|
846
|
+
const subcategorySidebar = canonicalProvider
|
|
847
|
+
? []
|
|
848
|
+
: publicationSidebar(githubRepository, buildSubcategorySidebar(contentDir));
|
|
847
849
|
|
|
848
850
|
// Auto-detect i18n: enable locales only when content has an en/ subdirectory.
|
|
849
851
|
// Repos that haven't migrated to docs/en/ won't get a broken language selector.
|
package/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { StarlightPlugin } from '@astrojs/starlight/types';
|
|
2
2
|
import { translations } from './src/i18n/translations.ts';
|
|
3
3
|
|
|
4
|
-
export default function f5xcDocsTheme(): StarlightPlugin {
|
|
4
|
+
export default function f5xcDocsTheme(repository = process.env.GITHUB_REPOSITORY || ''): StarlightPlugin {
|
|
5
5
|
return {
|
|
6
6
|
name: '@f5-sales-demo/docs-theme',
|
|
7
7
|
hooks: {
|
|
@@ -29,7 +29,11 @@ export default function f5xcDocsTheme(): StarlightPlugin {
|
|
|
29
29
|
? {
|
|
30
30
|
Sidebar: '@f5-sales-demo/docs-theme/components/ProviderSidebar.astro',
|
|
31
31
|
}
|
|
32
|
-
:
|
|
32
|
+
: repository === 'f5-sales-demo/canada'
|
|
33
|
+
? {
|
|
34
|
+
Sidebar: '@f5-sales-demo/docs-theme/components/CanadaSidebar.astro',
|
|
35
|
+
}
|
|
36
|
+
: {}),
|
|
33
37
|
Banner: '@f5-sales-demo/docs-theme/components/Banner.astro',
|
|
34
38
|
EditLink: '@f5-sales-demo/docs-theme/components/EditLink.astro',
|
|
35
39
|
Footer: '@f5-sales-demo/docs-theme/components/Footer.astro',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@f5-sales-demo/docs-theme",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.9.0",
|
|
4
4
|
"description": "F5 Distributed Cloud branded Starlight documentation theme",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"./src/utils/localize-ecosystem-href.ts": "./src/utils/localize-ecosystem-href.ts",
|
|
44
44
|
"./canonical-route-middleware": "./canonical-route-middleware.ts",
|
|
45
45
|
"./components/ProviderSidebar.astro": "./components/ProviderSidebar.astro",
|
|
46
|
+
"./components/CanadaSidebar.astro": "./components/CanadaSidebar.astro",
|
|
46
47
|
"./components/ProviderHeader.astro": "./components/ProviderHeader.astro",
|
|
47
48
|
"./components/SharedHeader.astro": "./components/SharedHeader.astro",
|
|
48
49
|
"./assets/canada-flag.svg": "./assets/canada-flag.svg",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import f5xcDocsTheme from '../../index';
|
|
3
|
+
|
|
4
|
+
function configuredComponents(repository: string) {
|
|
5
|
+
const updateConfig = vi.fn();
|
|
6
|
+
const setup = f5xcDocsTheme(repository).hooks?.['config:setup'];
|
|
7
|
+
if (!setup) throw new Error('theme setup hook is missing');
|
|
8
|
+
setup({
|
|
9
|
+
config: { components: {} },
|
|
10
|
+
updateConfig,
|
|
11
|
+
addRouteMiddleware: vi.fn(),
|
|
12
|
+
logger: { info: vi.fn() },
|
|
13
|
+
} as never);
|
|
14
|
+
return updateConfig.mock.calls[0][0].components;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe('Canada-only sidebar override', () => {
|
|
18
|
+
it('selects the linked stage sidebar for Canada', () => {
|
|
19
|
+
expect(configuredComponents('f5-sales-demo/canada').Sidebar).toBe(
|
|
20
|
+
'@f5-sales-demo/docs-theme/components/CanadaSidebar.astro',
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('leaves other repositories on their existing sidebar', () => {
|
|
25
|
+
expect(configuredComponents('f5-sales-demo/multi-cloud-networking')).not.toHaveProperty('Sidebar');
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
2
|
import { localizeEcosystemHref } from './localize-ecosystem-href';
|
|
3
|
-
import { repositoryPublicationProfile } from './publication-profiles';
|
|
3
|
+
import { publicationSidebar, repositoryPublicationProfile } from './publication-profiles';
|
|
4
4
|
|
|
5
5
|
describe('Canadian publication', () => {
|
|
6
6
|
it('publishes only English and selects the flag favicon', () => {
|
|
7
|
-
expect(repositoryPublicationProfile('f5-sales-demo/canada')).
|
|
7
|
+
expect(repositoryPublicationProfile('f5-sales-demo/canada')).toMatchObject({
|
|
8
8
|
locales: { en: { label: 'English', lang: 'en' } },
|
|
9
9
|
defaultLocale: 'en',
|
|
10
10
|
favicon: '/assets/canada-favicon.svg',
|
|
@@ -17,3 +17,43 @@ describe('Canadian publication', () => {
|
|
|
17
17
|
);
|
|
18
18
|
});
|
|
19
19
|
});
|
|
20
|
+
|
|
21
|
+
describe('publication profile sidebar', () => {
|
|
22
|
+
it('orders four linked stages and covers all 14 pages once', () => {
|
|
23
|
+
const sidebar = publicationSidebar('f5-sales-demo/canada', []);
|
|
24
|
+
expect(sidebar?.[0]).toEqual({ slug: 'index' });
|
|
25
|
+
const stages = sidebar?.slice(1) ?? [];
|
|
26
|
+
expect(stages.map((stage) => ('label' in stage ? stage.label : ''))).toEqual([
|
|
27
|
+
'Design',
|
|
28
|
+
'Deploy',
|
|
29
|
+
'Verify',
|
|
30
|
+
'Operate',
|
|
31
|
+
]);
|
|
32
|
+
const expected = [
|
|
33
|
+
['design', 'use-case', 'architecture'],
|
|
34
|
+
['deploy', 'deployment', 'terraform'],
|
|
35
|
+
['verify', 'presentation', 'verification'],
|
|
36
|
+
['operate', 'troubleshooting', 'failover', 'teardown'],
|
|
37
|
+
];
|
|
38
|
+
const routes = ['index'];
|
|
39
|
+
stages.forEach((stage, index) => {
|
|
40
|
+
expect(stage).toMatchObject({ collapsed: true });
|
|
41
|
+
if (!('items' in stage)) throw new Error('Canada stage must have items');
|
|
42
|
+
const stageRoutes = stage.items.map((item) => {
|
|
43
|
+
if (!('slug' in item)) throw new Error('Canada stage item must be a page');
|
|
44
|
+
return item.slug;
|
|
45
|
+
});
|
|
46
|
+
expect(stageRoutes).toEqual(expected[index]);
|
|
47
|
+
routes.push(...stageRoutes);
|
|
48
|
+
});
|
|
49
|
+
expect(new Set(routes).size).toBe(14);
|
|
50
|
+
expect(routes).toHaveLength(14);
|
|
51
|
+
expect(stages.every((stage) => 'label' in stage && stage.label.split(/\s+/).length <= 3)).toBe(true);
|
|
52
|
+
});
|
|
53
|
+
it('preserves fallback navigation for every repository without a sidebar profile', () => {
|
|
54
|
+
const fallback = [{ label: 'Existing', slug: 'en/existing' }];
|
|
55
|
+
expect(publicationSidebar('f5-sales-demo/multi-cloud-networking', fallback)).toBe(fallback);
|
|
56
|
+
expect(publicationSidebar('f5-sales-demo/docs-theme', undefined)).toBeUndefined();
|
|
57
|
+
expect(publicationSidebar('f5-sales-demo/canada', fallback)).not.toBe(fallback);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -1,15 +1,51 @@
|
|
|
1
|
+
import type starlight from '@astrojs/starlight';
|
|
2
|
+
|
|
3
|
+
type Sidebar = NonNullable<Parameters<typeof starlight>[0]['sidebar']>;
|
|
4
|
+
|
|
5
|
+
interface PublicationProfile {
|
|
6
|
+
locales: Record<string, { label: string; lang: string }>;
|
|
7
|
+
defaultLocale: string;
|
|
8
|
+
favicon: string;
|
|
9
|
+
sidebar?: Sidebar;
|
|
10
|
+
}
|
|
11
|
+
|
|
1
12
|
/** Repository publication decisions shared by navigation and site configuration. */
|
|
2
|
-
export const publicationProfiles: Record<
|
|
3
|
-
string,
|
|
4
|
-
{ locales: Record<string, { label: string; lang: string }>; defaultLocale: string; favicon: string }
|
|
5
|
-
> = {
|
|
13
|
+
export const publicationProfiles: Record<string, PublicationProfile> = {
|
|
6
14
|
canada: {
|
|
7
15
|
locales: { en: { label: 'English', lang: 'en' } },
|
|
8
16
|
defaultLocale: 'en',
|
|
9
17
|
favicon: '/assets/canada-favicon.svg',
|
|
18
|
+
sidebar: [
|
|
19
|
+
{ slug: 'index' },
|
|
20
|
+
{
|
|
21
|
+
label: 'Design',
|
|
22
|
+
collapsed: true,
|
|
23
|
+
items: [{ slug: 'design' }, { slug: 'use-case' }, { slug: 'architecture' }],
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
label: 'Deploy',
|
|
27
|
+
collapsed: true,
|
|
28
|
+
items: [{ slug: 'deploy' }, { slug: 'deployment' }, { slug: 'terraform' }],
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
label: 'Verify',
|
|
32
|
+
collapsed: true,
|
|
33
|
+
items: [{ slug: 'verify' }, { slug: 'presentation' }, { slug: 'verification' }],
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
label: 'Operate',
|
|
37
|
+
collapsed: true,
|
|
38
|
+
items: [{ slug: 'operate' }, { slug: 'troubleshooting' }, { slug: 'failover' }, { slug: 'teardown' }],
|
|
39
|
+
},
|
|
40
|
+
],
|
|
10
41
|
},
|
|
11
42
|
};
|
|
12
43
|
|
|
13
44
|
export function repositoryPublicationProfile(repository: string) {
|
|
14
45
|
return publicationProfiles[repository.split('/').pop() || ''];
|
|
15
46
|
}
|
|
47
|
+
|
|
48
|
+
/** Preserve automatic navigation unless this repository explicitly supplies a sidebar. */
|
|
49
|
+
export function publicationSidebar(repository: string, fallback: Sidebar | undefined) {
|
|
50
|
+
return repositoryPublicationProfile(repository)?.sidebar ?? fallback;
|
|
51
|
+
}
|