Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions docs/angular/astro.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { createDocsSite, type DocsMode } from 'docs-template/integration';
import { IGDOCS_PLATFORMS, type NavLang } from 'docs-template/platform';
import { IGDOCS_PLATFORMS, IGDOCS_HOSTS, type NavLang } from 'docs-template/platform';
import { generateGridTopics } from './src/scripts/generate-grids.mjs';
import mdx from '@astrojs/mdx';

Expand All @@ -29,9 +29,10 @@ if (docsEnv !== 'development' && docsEnv !== 'staging' && docsEnv !== 'productio

const mode: DocsMode = docsEnv;

// ── Site URL — varies by build mode ─────────────────────────────────────────
const PROD_HOST = 'https://www.infragistics.com';
const STAGING_HOST = 'https://staging.infragistics.com';
// ── Site URL — varies by build mode and language ────────────────────────────
// Hosts source: legacy igniteui-docfx environment.json (per-language).
const PROD_HOST = IGDOCS_HOSTS[docsLang].production;
const STAGING_HOST = IGDOCS_HOSTS[docsLang].staging;

const platformKey = docsLang === 'jp' ? 'AngularJP' : 'Angular';
const { base } = IGDOCS_PLATFORMS[platformKey];
Expand All @@ -56,6 +57,10 @@ export default createDocsSite({
platform: 'angular',
navLang: docsLang,
mode,
build: {
format: 'file'
},
trailingSlash: 'never',
productLinks: Object.values(IGDOCS_PLATFORMS)
.filter(p => p.lang === docsLang)
.map(({ label, key, base: b }) => ({
Expand All @@ -66,6 +71,7 @@ export default createDocsSite({
source: {
tocPath: `${componentsDocsDir}/toc.json`,
docsDir: componentsDocsDir,
slugPrefix: 'components',
},
sidebar: { exclude: [/^internal\//] },
starlight: {
Expand Down
20 changes: 19 additions & 1 deletion docs/angular/public/web.config
Original file line number Diff line number Diff line change
Expand Up @@ -760,7 +760,7 @@
<conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
<action type="Redirect" url="{R:1}/interactivity/accessibility-compliance" redirectType="Permanent" />
</rule>
<!-- Redirect legacy .html URLs — Astro uses directory-based routing (page/index.html) -->
<!-- Redirect legacy .html URLs — Astro uses extensionless URLs -->
<rule name="Redirect - remove .html" enabled="true" stopProcessing="true">
<match url="^(.*?)\.html$" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
Expand All @@ -769,6 +769,24 @@
</conditions>
<action type="Redirect" url="{R:1}" redirectType="Permanent" />
</rule>
<!-- Remove trailing slashes (except bare root) -->
<rule name="Remove trailing slash" enabled="true" stopProcessing="true">
<match url="^(.+)/$" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Redirect" url="{R:1}" redirectType="Permanent" />
</rule>
<!-- Serve extensionless URLs from pre-rendered .html files -->
<rule name="Rewrite extensionless to .html" enabled="true" stopProcessing="false">
<match url="^([^.]+)$" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="{R:1}.html" />
</rule>
</rules>
</rewrite>
</system.webServer>
Expand Down
4 changes: 2 additions & 2 deletions docs/angular/src/content.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ try {
lang = cfg.lang ?? lang;
} catch { /* use defaults */ }

const docsDir = path.join(root, 'src', 'content', lang, 'components');
const docsDir = path.join(root, 'src', 'content', lang);

const tableOfContentsSchema = z.object({
tableOfContents: z
Expand All @@ -28,5 +28,5 @@ const tableOfContentsSchema = z.object({
});

export const collections = {
docs: createDocsCollection(docsDir, { exclude: ['**/*.md'], extendSchema: tableOfContentsSchema }),
docs: createDocsCollection(docsDir, { exclude: ['**/*.md', 'grids_templates/**', 'images/**', 'index.mdx'], extendSchema: tableOfContentsSchema }),
};
24 changes: 19 additions & 5 deletions docs/angular/src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,19 @@ const toc: TocItem[] = existsSync(tocPath)
: [];

function hrefToSlug(href: string): string {
return href.replace(/\\/g, '/').replace(/\.(md|mdx)$/i, '').toLowerCase().replace(/\/index$/, '');
if (!href) return '';
let slug = href
.replace(/\\/g, '/')
.replace(/\.(md|mdx)$/i, '')
.toLowerCase();
slug = slug.replace(/\/index$/, '');
return slug === 'index' ? '' : slug;
}


function joinSlugPrefix(slugPrefix: string, slug: string): string {
if (!slugPrefix) return slug;
return slug ? `${slugPrefix}/${slug}` : slugPrefix;
}

function findDefaultSlug(items: TocItem[]): string | null {
Expand Down Expand Up @@ -52,10 +64,12 @@ function slugExistsInSidebar(items: SidebarEntry[], target: string): boolean {

const typedSidebar = sidebar as SidebarEntry[];
const defaultSlug = findDefaultSlug(toc);
const slug = (defaultSlug && slugExistsInSidebar(typedSidebar, defaultSlug))
? defaultSlug
const slugPrefix = process.env.DOCS_SLUG_PREFIX ?? '';
const prefixedDefault = defaultSlug !== null ? joinSlugPrefix(slugPrefix, defaultSlug) : null;
const slug = (prefixedDefault && slugExistsInSidebar(typedSidebar, prefixedDefault))
? prefixedDefault
: firstSlug(typedSidebar);
const base = import.meta.env.BASE_URL.replace(/\/$/, '');
const redirectTo = slug ? `${base}/${slug}/` : `${base}/404/`;
const redirectTo = slug ? `${base}/${slug}` : `${base}/404`;
return Astro.redirect(redirectTo, 301);
---
---
11 changes: 8 additions & 3 deletions docs/xplat/astro.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import path from 'node:path';
import { readFileSync, existsSync, mkdirSync, writeFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { createDocsSite, type DocsMode } from 'docs-template/integration';
import { IGDOCS_PLATFORMS, type NavLang } from 'docs-template/platform';
import { IGDOCS_PLATFORMS, IGDOCS_HOSTS, type NavLang } from 'docs-template/platform';
import mdx from '@astrojs/mdx';

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -304,8 +304,9 @@ function buildFilteredToc(): string {
const filteredTocPath = buildFilteredToc();

console.log(`[astro.config] Platform: ${platform} lang: ${lang} mode: ${mode}`);
const PROD_HOST = 'https://www.infragistics.com';
const STAGING_HOST = 'https://staging.infragistics.com';
// Hosts source: legacy igniteui-docfx environment.json (per-language).
const PROD_HOST = IGDOCS_HOSTS[lang].production;
const STAGING_HOST = IGDOCS_HOSTS[lang].staging;

const platformLangKey = lang === 'jp' ? `${platform}JP` : platform;
const p = PLATFORMS[platformLangKey] ?? PLATFORMS[platform];
Expand All @@ -325,6 +326,10 @@ export default createDocsSite({
platform: p.key,
navLang: lang,
mode,
build: {
format: 'file'
},
trailingSlash: 'never',
source: {
tocPath: filteredTocPath,
docsDir: path.join(XPLAT_ROOT, 'components'),
Expand Down
22 changes: 21 additions & 1 deletion src/integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,16 @@ export interface DocsSiteSource {
tocPath: string;
/** Absolute path to the Markdown docs directory. */
docsDir: string;
/**
* Path prefix prepended to every sidebar slug.
* When set, the content collection base is assumed to be a parent of
* `docsDir` so that entry IDs (and therefore routes) include this prefix.
*
* Example: `slugPrefix: 'components'` makes sidebar slugs like
* `components/accordion` instead of `accordion`, matching a content
* collection whose glob base is one directory above `docsDir`.
*/
slugPrefix?: string;
}

export interface CreateDocsSiteOptions {
Expand Down Expand Up @@ -720,16 +730,26 @@ export function createDocsSite(options: CreateDocsSiteOptions = {} as CreateDocs
...astroExtra
} = options;

const prefixSegments = source.slugPrefix ? source.slugPrefix.split('/').filter(Boolean) : [];

const sidebar = buildSidebarFromToc({
tocPath: source.tocPath!,
docsDir: source.docsDir!,
exclude: sidebarOptions.exclude ?? [],
slugPrefix: source.slugPrefix,
});

// When slugPrefix is set the content collection base is a parent of docsDir.
// Compute the content root so llms.txt and dev-middleware resolve slugs correctly.
const contentRoot = prefixSegments.length > 0
? path.resolve(source.docsDir!, ...prefixSegments.map(() => '..'))
: source.docsDir;

// Expose env vars so consuming content.config.ts and components can read them.
if (source.docsDir) {
process.env.DOCS_SOURCE_PATH = source.docsDir;
}
process.env.DOCS_SLUG_PREFIX = source.slugPrefix ?? '';
process.env.DOCS_BUILD_MODE = mode;
process.env.DOCS_BASE = base ? base.replace(/\/$/, '') : '';
if (!process.env.DOCS_ENV) {
Expand Down Expand Up @@ -808,7 +828,7 @@ export function createDocsSite(options: CreateDocsSiteOptions = {} as CreateDocs
siteMetaIntegration({
title,
description,
docsDir: source.docsDir,
docsDir: contentRoot,
sidebar,
platform,
navLang,
Expand Down
38 changes: 30 additions & 8 deletions src/platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,59 +184,81 @@ export const PLATFORM_DEFS: Record<PlatformKey, PlatformDef> = {
* Shared per-platform metadata for all Ignite UI docs sites.
* Import this in `astro.config.ts` files instead of duplicating the data.
*/
// Base paths that match the legacy DocFX URL shape served by IIS.
// Same path is used regardless of language — language is encoded in the host
// (see IGDOCS_HOSTS below). Source: legacy igniteui-docfx and igniteui-xplat-docs
// global.json (_currentBaseUrl: /products/{ProductSpinal}/{PlatformLower}/).
const ANGULAR_BASE = '/products/ignite-ui-angular/angular';
const REACT_BASE = '/products/ignite-ui-react/react';
const WEBCOMPONENTS_BASE = '/products/ignite-ui-web-components/web-components';
const BLAZOR_BASE = '/products/ignite-ui-blazor/blazor';

export const IGDOCS_PLATFORMS: Record<string, PlatformMeta> = {
// English
Angular: {
lang: 'en', label: 'Angular', key: 'angular', devPort: 4331,
base: '/angularsite',
base: ANGULAR_BASE,
title: 'Ignite UI for Angular',
description: 'Component documentation for Ignite UI for Angular.',
},
React: {
lang: 'en', label: 'React', key: 'react', devPort: 4332,
base: '/reactsite',
base: REACT_BASE,
title: 'Ignite UI for React',
description: 'Component documentation for Ignite UI for React.',
},
WebComponents: {
lang: 'en', label: 'Web Components', key: 'web-components', devPort: 4333,
base: '/webcomponentssite',
base: WEBCOMPONENTS_BASE,
title: 'Ignite UI for Web Components',
description: 'Component documentation for Ignite UI for Web Components.',
},
Blazor: {
lang: 'en', label: 'Blazor', key: 'blazor', devPort: 4334,
base: '/blazorsite',
base: BLAZOR_BASE,
title: 'Ignite UI for Blazor',
description: 'Component documentation for Ignite UI for Blazor.',
},
// Japanese
AngularJP: {
lang: 'jp', label: 'Angular', key: 'angular', devPort: 4341,
base: '/angularsite',
base: ANGULAR_BASE,
title: 'Ignite UI for Angular',
description: 'Component documentation for Ignite UI for Angular.',
},
ReactJP: {
lang: 'jp', label: 'React', key: 'react', devPort: 4342,
base: '/reactsite',
base: REACT_BASE,
title: 'Ignite UI for React',
description: 'Component documentation for Ignite UI for React.',
},
WebComponentsJP: {
lang: 'jp', label: 'Web Components', key: 'web-components', devPort: 4343,
base: '/webcomponentssite',
base: WEBCOMPONENTS_BASE,
title: 'Ignite UI for Web Components',
description: 'Component documentation for Ignite UI for Web Components.',
},
BlazorJP: {
lang: 'jp', label: 'Blazor', key: 'blazor', devPort: 4344,
base: '/blazorsite',
base: BLAZOR_BASE,
title: 'Ignite UI for Blazor',
description: 'Component documentation for Ignite UI for Blazor.',
},
};

/**
* Per-language hostname for staging and production.
* Source: legacy igniteui-docfx environment.json files (one per locale).
* en → www.infragistics.com (staging: staging.infragistics.com)
* jp → jp.infragistics.com (staging: jp.staging.infragistics.com)
* kr → www.infragistics.co.kr (staging: staging.infragistics.co.kr)
*/
export const IGDOCS_HOSTS: Record<NavLang, { production: string; staging: string }> = {
en: { production: 'https://www.infragistics.com', staging: 'https://staging.infragistics.com' },
jp: { production: 'https://jp.infragistics.com', staging: 'https://jp.staging.infragistics.com' },
kr: { production: 'https://www.infragistics.co.kr', staging: 'https://staging.infragistics.co.kr' },
};

/**
* Returns an array of Starlight `head` entries for the given platform.
* Pass the result directly to `starlight({ head: getPlatformHead(...) })`.
Expand Down
27 changes: 21 additions & 6 deletions src/sidebar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ function hrefToSlug(href: string): string {
return slug === 'index' ? '' : slug;
}


function joinSlugPrefix(slugPrefix: string, slug: string): string {
if (!slugPrefix) return slug;
return slug ? `${slugPrefix}/${slug}` : slugPrefix;
}

/**
* Initial collapsed state by depth:
* • depth 0 (root groups, incl. `header:true` sections) → `collapsed: false`
Expand All @@ -80,6 +86,7 @@ function convertTocItem(
item: TocItem,
exclude: RegExp[],
depth: number,
slugPrefix: string,
): SidebarEntry | null {
if (!item.name) return null;

Expand All @@ -90,18 +97,18 @@ function convertTocItem(
collapsed: collapsedForDepth(depth),
};
if (item.href && docExists(docsDir, item.href, exclude)) {
group.items.push({ label: 'Overview', slug: hrefToSlug(item.href) });
group.items.push({ label: 'Overview', slug: joinSlugPrefix(slugPrefix, hrefToSlug(item.href)) });
}
for (const child of item.items) {
const entry = convertTocItem(docsDir, child, exclude, depth + 1);
const entry = convertTocItem(docsDir, child, exclude, depth + 1, slugPrefix);
if (entry) group.items.push(entry);
}
return group.items.length > 0 ? group : null;
}

if (item.href) {
if (!docExists(docsDir, item.href, exclude)) return null;
const entry: SidebarLink = { label: item.name, slug: hrefToSlug(item.href) };
const entry: SidebarLink = { label: item.name, slug: joinSlugPrefix(slugPrefix, hrefToSlug(item.href)) };
// Status badge — only one slot available in Starlight, priority order:
if (item.new) entry.badge = { text: 'New', variant: 'success' };
else if (item.preview) entry.badge = { text: 'Preview', variant: 'caution' };
Expand Down Expand Up @@ -130,16 +137,24 @@ export interface BuildSidebarFromTocOptions {
docsDir: string;
/** Extra patterns to exclude (matched against the `href`). */
exclude?: RegExp[];
/**
* Path prefix prepended to every generated slug.
* Use when the content collection base is a parent of `docsDir` so that
* slugs include the intermediate directory (e.g. `'components/'`).
*/
slugPrefix?: string;
}

/**
* Reads a YAML or JSON TOC file and converts it to a Starlight sidebar array.
*/
export function buildSidebarFromToc({ tocPath, docsDir, exclude = [] }: BuildSidebarFromTocOptions): SidebarEntry[] {
export function buildSidebarFromToc({ tocPath, docsDir, exclude = [], slugPrefix }: BuildSidebarFromTocOptions): SidebarEntry[] {
if (!tocPath || !fs.existsSync(tocPath)) return [];
const tocRaw = fs.readFileSync(tocPath, 'utf-8');
const tocItems = tocPath.endsWith('.json') ? JSON.parse(tocRaw) : yaml.load(tocRaw) as TocItem[];

const prefix = slugPrefix ?? '';

const sidebar: SidebarEntry[] = [];
let currentGroup: SidebarGroup | null = null;

Expand All @@ -149,14 +164,14 @@ export function buildSidebarFromToc({ tocPath, docsDir, exclude = [] }: BuildSid
// Root-level header section — open by default.
currentGroup = { label: item.name!, items: [], collapsed: collapsedForDepth(0) };
if (item.href && docExists(docsDir, item.href, exclude)) {
currentGroup.items.push({ label: 'Overview', slug: hrefToSlug(item.href) });
currentGroup.items.push({ label: 'Overview', slug: joinSlugPrefix(prefix, hrefToSlug(item.href)) });
}
continue;
}
// Items inside a header section are at depth 1 (nested);
// items outside any header section are at depth 0 (root).
const depth = currentGroup ? 1 : 0;
const entry = convertTocItem(docsDir, item, exclude, depth);
const entry = convertTocItem(docsDir, item, exclude, depth, prefix);
if (!entry) continue;
if (currentGroup) currentGroup.items.push(entry);
else sidebar.push(entry);
Expand Down
Loading