[Docs Revamp Feedback] Support three levels of TOC nesting (End-user Guide > Collaborate) (#37663)

* docs(sidebar): support 3 levels of TOC nesting for End User Guide > Collaborate

Addresses item 6 of Eric Sethna's docs revamp feedback ("Can we support
three levels of nesting in the TOC? ... ie End-user guide > Collaborate").

PR #37630 (open, not merged) applies the manual-grouping-override pattern
to Administration Guide > Onboard/Scale but doesn't touch End User Guide,
and the pattern isn't a shared generic engine — each section gets its own
small builder/regroup pair. This adds that pair for Collaborate
(COLLABORATE_GROUPS/COLLABORATE_ORDER/COLLABORATE_HIDDEN +
buildCollaborateItem/regroupCollaborate/buildEndUserGuideSidebar),
regrouping its 49 flat files into Channels, Messaging & Threads, Calls &
Screen Sharing, Teams/Groups & Roles, Integrations & Connected Apps, and
Keyboard Shortcuts & Accessibility — giving End User Guide > Collaborate >
[group] > page, a genuine 3rd nesting level. Files stay flat on disk; no
URLs move.

* docs(sidebar): support inline subgroup specs in buildCollaborateItem

Addresses CodeRabbit review comment on #37663: buildCollaborateItem only
handled spec.group lookups, unlike buildAdminManageItem which also
supports inline {label, items} subgroup specs (used e.g. for the
Attribute-Based Access Control 4-level nesting under Admin Manage).
Detect spec.items before falling back to the COLLABORATE_GROUPS lookup
so Collaborate can gain the same nested-subgroup capability if needed,
without changing today's generated sidebar output.

* docs(sidebar): merge Collaborate regroup with master's agents-docs promotion

Rebasing onto master surfaced a duplicate 'buildEndUserGuideSidebar'
declaration: master gained its own End User Guide override (promoting
the Agents plugin's usage-tips page, from #37627) after this branch was
cut, so git's non-conflicting merge silently produced two function
bodies with the same name — a SyntaxError caught by CI's docs build
check, not by the rebase itself.

Merged both overrides into a single buildEndUserGuideSidebar: keeps the
agents usage-tips promotion from master, plus this branch's Collaborate
regrouping into COLLABORATE_GROUPS. Verified with a clean
'npm run build:sidebars' run (zero warnings) after initializing the
docs/vendor/mattermost-plugin-agents submodule locally to stage agents
docs first.

* docs(sidebar): clarify TOC nesting depth wording, reorder Collaborate items

- README: describe the inline sub-group nesting as a third category
  level / four-level path instead of ambiguous "3rd level" wording.
- Move invite-people out of the mismatched "Teams, Groups & Roles"
  group into its own top-level Collaborate entry, placed first.
- Reorder "Teams, Groups & Roles" so learn-about-roles leads the group.

---------

Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
Eva Sarafianou
2026-07-28 10:59:19 +03:00
committed by GitHub
co-authored by Hanzo Dev
parent aab91850d8
commit 87f524d0a8
2 changed files with 255 additions and 13 deletions
+15 -2
View File
@@ -57,7 +57,8 @@ top of the auto-generated tree for those sections only: Overview
(`DEPLOYMENT_GROUPS`/`DEPLOYMENT_ROOT_ORDER`), Administration Guide →
Configure (`ADMIN_CONFIGURE_GROUPS`/`ADMIN_CONFIGURE_ORDER`),
Administration Guide → Manage (`ADMIN_MANAGE_GROUPS`/`ADMIN_MANAGE_ORDER`),
and Integrations Guide (`INTEGRATIONS_GROUPS`/`INTEGRATIONS_ROOT_ORDER`).
End User Guide → Collaborate (`COLLABORATE_GROUPS`/`COLLABORATE_ORDER`), and
Integrations Guide (`INTEGRATIONS_GROUPS`/`INTEGRATIONS_ROOT_ORDER`).
The override only changes how the sidebar renders — files stay flat on
disk at their existing paths, so URLs don't move. Each override is a pair
@@ -65,11 +66,23 @@ of constants near the top of the script:
- A `*_GROUPS` map of group key → `{label, landing?, items}`, where `items`
are doc basenames (relative to that section's directory) or nested
inline group objects.
inline group objects. A group's `items` can itself contain a nested
`{label, items}` sub-group, which adds a third category level — a
four-level path of Guide → Group → Sub-group → page — for a section
large enough to need it — see `OVERVIEW_GROUPS.subscription`'s "Cloud"
sub-group, or `ADMIN_MANAGE_GROUPS.userAccess`'s "Attribute-Based Access
Control" sub-group, for existing examples.
- A `*_ROOT_ORDER`/`*_ORDER` array listing the top-level order: plain
strings for standalone docs, `{group: 'key'}` for a group from the map
above.
There's no single shared "grouping engine" — each overridden section gets
its own small `buildXItem`/`regroupX` (or `buildXSidebar`) pair that mirrors
the shape of the others (see `buildCollaborateItem`/`regroupCollaborate` for
the End User Guide → Collaborate one). Adding an override for a new section
means copying that shape for the new section, not extending a shared
function.
**Adding a new file to one of these sections:** the script fails loudly if
you forget it — it logs a `WARN: N file(s) missing from *_ORDER` and falls
back to appending the orphaned file(s) at the root of that section, so a
+240 -11
View File
@@ -29,17 +29,29 @@ const OUT = join(SITE_ROOT, 'sidebars', 'documentation.generated.json');
// Most sections build their sidebar straight from the filesystem: each
// subdirectory becomes a category, each file a doc, sorted by
// `sidebar_position` frontmatter then filename (see buildCategory below).
// Overview, Deployment Guide, Administration Guide > Configure, and
// Integrations Guide are flat piles of 15-40 files that read badly as one
// long alphabetical list, so each gets a manual grouping override applied
// at sidebar-render time only — the files themselves stay flat on disk, so
// URLs don't move.
// Overview, Deployment Guide, Administration Guide > Configure/Manage/
// Onboard/Scale, End User Guide > Collaborate, and Integrations Guide are
// flat piles of 15-49 files that read badly as one long alphabetical list,
// so each gets a manual grouping override applied at sidebar-render time
// only — the files themselves stay flat on disk, so URLs don't move.
//
// Each override is a `*_GROUPS` map (group key -> {label, landing?, items})
// plus a `*_ROOT_ORDER`/`*_ORDER` array giving the top-level order (plain
// strings for standalone docs, `{group: 'key'}` for a group from the map).
// A `*_HIDDEN` set lists files that got re-parented into a group so the
// orphan check below doesn't re-append them at the section root.
// orphan check below doesn't re-append them at the section root. A group's
// `items` can itself contain nested `{label, items}` sub-groups (see e.g.
// OVERVIEW_GROUPS.subscription's "Cloud" sub-group below) — that's what
// gets you a 3rd level of TOC nesting (Guide > Group > Sub-group > page)
// when a section's flat list is large enough to need it.
//
// This pattern isn't a single generic engine — each section with an
// override gets its own small `buildXItem`/`regroupX` pair (see
// buildCollaborateItem/regroupCollaborate for the newest one) that mirrors
// the others in shape. Adding an override for a new section means copying
// that shape, not extending a shared function; sections without one of
// these overrides just render every level of their filesystem tree as-is
// (buildCategory already recurses to unlimited depth on its own).
//
// Adding a new file to one of these sections: add its basename to the
// relevant group's `items` (or to the root order array, if standalone). If
@@ -559,6 +571,136 @@ const ADMIN_MANAGE_HIDDEN = new Set([
'bulk-export-tool', 'admin/migration',
]);
// ---------------------------------------------------------------------------
// End User Guide — Collaborate — manual grouping override.
// ---------------------------------------------------------------------------
//
// Collaborate is a flat 49-file dump (Channels, Messaging, Calls, Teams, and
// Accessibility topics all interleaved alphabetically) — the section
// End-user Guide > Collaborate feedback (Eric Sethna review, item 6) called
// out as "overwhelming". This override groups it by topic, same pattern as
// Administration Guide's Configure/Manage/Onboard/Scale (see #37591/#37630).
//
// `collaborate-within-channels` doubles as both the Channels group's landing
// page and a regular grouped item — it already reads as a "Channels" hub
// page in its own "Learn more" section, which the `channels` group's item
// list below mirrors.
const COLLABORATE_GROUPS = {
channels: {
label: 'Channels',
landing: 'collaborate-within-channels',
items: [
'channel-types',
'browse-channels',
'create-channels',
'join-leave-channels',
'navigate-between-channels',
'channel-naming-conventions',
'channel-header-purpose',
'rename-channels',
'archive-unarchive-channels',
'favorite-channels',
'mark-channels-unread',
'manage-channel-members',
'manage-channel-bookmarks',
'display-channel-banners',
'autotranslate-messages',
'convert-public-channels',
'convert-group-messages',
],
},
messaging: {
label: 'Messaging & Threads',
items: [
'send-messages',
'communicate-with-messages',
'reply-to-messages',
'organize-conversations',
'format-messages',
'mark-messages-unread',
'mention-people',
'message-priority',
'message-reminders',
'schedule-messages',
'save-pin-messages',
'flag-messages',
'forward-messages',
'search-for-messages',
'share-links',
'share-files-in-messages',
'react-with-emojis-gifs',
],
},
calls: {
label: 'Calls & Screen Sharing',
items: [
'make-calls',
'audio-and-screensharing',
],
},
teamsAndRoles: {
label: 'Teams, Groups & Roles',
items: [
'learn-about-roles',
'organize-using-teams',
'team-settings',
'organize-using-custom-user-groups',
],
},
integrations: {
label: 'Integrations & Connected Apps',
items: [
'extend-mattermost-with-integrations',
'agents-context-management',
'collaborate-within-connected-microsoft-teams',
],
},
accessibility: {
label: 'Keyboard Shortcuts & Accessibility',
items: [
'keyboard-shortcuts',
'team-keyboard-shortcuts',
'keyboard-accessibility',
'view-system-information',
],
},
};
// Top-level Collaborate order. Strings are doc basenames relative to
// end-user-guide/collaborate/; objects reference COLLABORATE_GROUPS keys.
const COLLABORATE_ORDER = [
'invite-people',
{group: 'channels'},
{group: 'messaging'},
{group: 'calls'},
{group: 'teamsAndRoles'},
{group: 'integrations'},
{group: 'accessibility'},
];
// Files re-parented into groups — exclude from the orphan check.
const COLLABORATE_HIDDEN = new Set([
'channel-types', 'browse-channels', 'create-channels', 'join-leave-channels',
'navigate-between-channels', 'channel-naming-conventions', 'channel-header-purpose',
'rename-channels', 'archive-unarchive-channels', 'favorite-channels',
'mark-channels-unread', 'manage-channel-members', 'manage-channel-bookmarks',
'display-channel-banners', 'autotranslate-messages', 'convert-public-channels',
'convert-group-messages',
'send-messages', 'communicate-with-messages', 'reply-to-messages', 'organize-conversations',
'format-messages', 'mark-messages-unread', 'mention-people', 'message-priority',
'message-reminders', 'schedule-messages', 'save-pin-messages', 'flag-messages',
'forward-messages', 'search-for-messages', 'share-links', 'share-files-in-messages',
'react-with-emojis-gifs',
'make-calls', 'audio-and-screensharing',
'organize-using-teams', 'team-settings', 'organize-using-custom-user-groups',
'learn-about-roles', 'invite-people',
'extend-mattermost-with-integrations', 'agents-context-management',
'collaborate-within-connected-microsoft-teams',
'keyboard-shortcuts', 'team-keyboard-shortcuts', 'keyboard-accessibility',
'view-system-information',
]);
// ---------------------------------------------------------------------------
// Integrations Guide — manual grouping override.
// ---------------------------------------------------------------------------
@@ -1065,6 +1207,65 @@ function buildAdminGuideSidebar(autoCat) {
return autoCat;
}
// ---------------------------------------------------------------------------
// End User Guide — builder (regroups the "Collaborate" sub-category).
// ---------------------------------------------------------------------------
function buildCollaborateItem(spec, leafLabels) {
if (typeof spec === 'string') {
const id = `end-user-guide/collaborate/${spec}`;
return {type: 'doc', id, label: leafLabels[id] || humanize(spec)};
}
if (spec.items) {
// Inline subgroup (no COLLABORATE_GROUPS lookup) — mirrors
// buildAdminManageItem/buildAdminManageGroup, so a group's items can
// nest a further {label, items} sub-group for a 4th nesting level.
return buildCollaborateGroup(spec, leafLabels);
}
const g = COLLABORATE_GROUPS[spec.group];
if (!g) throw new Error(`unknown collaborate group: ${spec.group}`);
return buildCollaborateGroup(g, leafLabels);
}
function buildCollaborateGroup(g, leafLabels) {
const items = g.items.map((it) => buildCollaborateItem(it, leafLabels));
const cat = {type: 'category', label: g.label, collapsed: true, items};
if (g.landing) cat.link = {type: 'doc', id: `end-user-guide/collaborate/${g.landing}`};
return cat;
}
// Replace the auto-generated "Collaborate" sub-category's items (in place,
// preserving its position among End User Guide's other sub-categories) with
// the manual grouping above — same pattern as regroupAdminConfigure/Manage.
function regroupCollaborate(collaborateCat) {
const leafLabels = collectLeafLabels(collaborateCat);
const items = COLLABORATE_ORDER.map((spec) => buildCollaborateItem(spec, leafLabels));
const known = new Set();
(function walk(n) {
if (Array.isArray(n)) n.forEach(walk);
else if (n && typeof n === 'object') {
if (n.type === 'doc' && n.id) known.add(n.id);
if (n.link && n.link.id) known.add(n.link.id);
if (n.items) walk(n.items);
}
})(items);
const hiddenIds = new Set();
for (const h of COLLABORATE_HIDDEN) hiddenIds.add(`end-user-guide/collaborate/${h}`);
const orphans = [];
for (const id of Object.keys(leafLabels)) {
if (!known.has(id) && !hiddenIds.has(id)) orphans.push(id);
}
if (orphans.length > 0) {
console.warn(`[sidebar] WARN: ${orphans.length} Collaborate file(s) missing from COLLABORATE_ORDER — falling through to root:`);
for (const id of orphans) console.warn(` - ${id}`);
for (const id of orphans) items.push({type: 'doc', id, label: leafLabels[id]});
}
collaborateCat.items = items;
return collaborateCat;
}
// ---------------------------------------------------------------------------
// Integrations Guide — builder.
// ---------------------------------------------------------------------------
@@ -1126,11 +1327,16 @@ function buildIntegrationsSidebar(autoCat) {
}
// ---------------------------------------------------------------------------
// End User Guide — nests the Agents plugin's usage-tips page under the
// existing "AI Agents" doc, the same way Configure nests Agents' admin-side
// pages (see ADMIN_CONFIGURE_GROUPS.agents above). End User Guide is
// otherwise fully filesystem-driven, so this is a narrow, targeted
// promotion rather than a full manual-grouping override.
// End User Guide — builder. Two independent overrides on top of the
// otherwise filesystem-driven auto-generated sidebar:
// 1. Nests the Agents plugin's usage-tips page under the existing "AI
// Agents" doc, the same way Configure nests Agents' admin-side pages
// (see ADMIN_CONFIGURE_GROUPS.agents above) — a narrow, targeted
// promotion rather than a full manual-grouping override.
// 2. Regroups the "Collaborate" sub-category (49 files) into the topic
// groups defined in COLLABORATE_GROUPS above, the same
// manual-grouping-override pattern used for Administration Guide's
// Configure/Manage/Onboard/Scale sections.
// ---------------------------------------------------------------------------
// Finds the {type: 'doc', id: docId} leaf anywhere in `items` and replaces
@@ -1162,6 +1368,27 @@ function buildEndUserGuideSidebar(autoCat) {
if (!promoted) {
console.warn('[sidebar] WARN: End User Guide "agents" doc not found — Agents usage-tips nesting was not applied.');
}
let foundCollaborate = false;
for (const it of autoCat.items) {
if (it.type !== 'category') continue;
let dirName = null;
if (it.link && it.link.id) {
dirName = it.link.id.split('/')[1];
}
if (!dirName && it.items) {
const firstDoc = it.items.find((c) => c.type === 'doc' && c.id);
if (firstDoc) dirName = firstDoc.id.split('/')[1];
}
if (dirName === 'collaborate') {
regroupCollaborate(it);
foundCollaborate = true;
}
}
if (!foundCollaborate) {
console.warn('[sidebar] WARN: End User Guide "Collaborate" sub-category not found — COLLABORATE_GROUPS override was not applied.');
}
return autoCat;
}
@@ -1186,6 +1413,8 @@ function main() {
cat = buildDeploymentSidebar(cat);
} else if (dir === 'administration-guide') {
cat = buildAdminGuideSidebar(cat);
} else if (dir === 'end-user-guide') {
cat = buildEndUserGuideSidebar(cat);
} else if (dir === 'integrations-guide') {
cat = buildIntegrationsSidebar(cat);
} else if (dir === 'end-user-guide') {