@f5-sales-demo/docs-theme 4.8.0 → 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
@@ -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: {
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.8.0",
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
+ });
@@ -19,21 +19,36 @@ describe('Canadian publication', () => {
19
19
  });
20
20
 
21
21
  describe('publication profile sidebar', () => {
22
- it('makes the Canada article primary and keeps every existing reference URL', () => {
22
+ it('orders four linked stages and covers all 14 pages once', () => {
23
23
  const sidebar = publicationSidebar('f5-sales-demo/canada', []);
24
- expect(sidebar?.slice(0, 2)).toEqual([{ slug: 'index' }, { slug: 'use-case' }]);
25
- const reference = sidebar?.[2];
26
- expect(reference).toMatchObject({ label: 'Reference', collapsed: true });
27
- expect(JSON.stringify(reference)).toContain('architecture');
28
- expect(JSON.stringify(reference)).toContain('verification');
29
- expect(JSON.stringify(reference)).toContain('terraform');
30
- expect(JSON.stringify(reference)).toContain('presentation');
31
- expect(reference).toMatchObject({
32
- items: expect.arrayContaining([expect.objectContaining({ label: 'Maintenance', collapsed: true })]),
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);
33
48
  });
34
- for (const slug of ['deployment', 'failover', 'troubleshooting', 'teardown']) {
35
- expect(JSON.stringify(reference)).toContain(`"${slug}"`);
36
- }
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);
37
52
  });
38
53
  it('preserves fallback navigation for every repository without a sidebar profile', () => {
39
54
  const fallback = [{ label: 'Existing', slug: 'en/existing' }];
@@ -17,21 +17,25 @@ export const publicationProfiles: Record<string, PublicationProfile> = {
17
17
  favicon: '/assets/canada-favicon.svg',
18
18
  sidebar: [
19
19
  { slug: 'index' },
20
- { slug: 'use-case' },
21
20
  {
22
- label: 'Reference',
21
+ label: 'Design',
23
22
  collapsed: true,
24
- items: [
25
- { slug: 'architecture' },
26
- { slug: 'verification' },
27
- { slug: 'presentation' },
28
- { slug: 'terraform' },
29
- {
30
- label: 'Maintenance',
31
- collapsed: true,
32
- items: [{ slug: 'deployment' }, { slug: 'failover' }, { slug: 'troubleshooting' }, { slug: 'teardown' }],
33
- },
34
- ],
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' }],
35
39
  },
36
40
  ],
37
41
  },