feat: Enhance MegaMenu with localization, caching, and new properties

- Added localization support for loading states, empty navigation, and submenu actions in both German and English.
- Introduced new properties in IMenuItem for description, openInNewWindow, and external links.
- Updated SPTermStorePickerService to handle cache versioning and language-specific caching.
- Implemented a new MegaMenuCore service for handling term properties, cache management, and navigation URL resolution.
- Refactored TaxonomyNavigationService to build menu hierarchy and filter hidden terms.
- Added automated tests for MegaMenu core functionalities and static asset validation.
- Removed unused ItemDictionary service.
- Created a comprehensive ToDo document outlining the implementation status and future tasks.
This commit is contained in:
Torsten Brendgen
2026-07-20 21:34:24 +02:00
parent bc61166267
commit 50b85468f1
30 changed files with 1394 additions and 294 deletions
@@ -28,6 +28,7 @@ export interface IMegaMenuApplicationCustomizerProperties {
cacheMinutes?: number;
debug?: boolean;
menuMode?: MegaMenuMode;
cacheVersion?: string;
}
export default class MegaMenuApplicationCustomizer
@@ -42,7 +43,7 @@ export default class MegaMenuApplicationCustomizer
const debug: boolean = this._isDebugEnabled();
debugLog(debug, LOG_SOURCE, 'onInit started.', {
componentId: UserCustomActionMegaMenuId,
version: '2.1.0',
version: '2.2.0',
properties: this.properties || {},
pageUrl: window.location.href,
webUrl: this.context.pageContext.web.absoluteUrl,
@@ -108,13 +109,16 @@ export default class MegaMenuApplicationCustomizer
return;
}
this._renderStatus(strings.Loading);
try {
debugLog(debug, LOG_SOURCE, 'Creating TaxonomyNavigationService.');
const taxonomyService: TaxonomyNavigationService = new TaxonomyNavigationService(
this.context,
termSetIdentifier,
debug,
this._getCacheMinutes()
this._getCacheMinutes(),
this._getCacheVersion(),
this._getLanguageLcid()
);
debugLog(debug, LOG_SOURCE, 'Requesting navigation items from taxonomy.');
@@ -123,6 +127,11 @@ export default class MegaMenuApplicationCustomizer
topLevelItemCount: menuItems.length
});
if (!menuItems || menuItems.length === 0) {
this._renderStatus(strings.EmptyNavigation);
return;
}
if (this._renderer) {
debugLog(debug, LOG_SOURCE, 'Disposing the previous renderer instance.');
this._renderer.dispose();
@@ -131,7 +140,18 @@ export default class MegaMenuApplicationCustomizer
this._renderer = new MegaMenuRenderer(
menuItems,
debug,
this._getMenuMode()
this._getMenuMode(),
{
navigationLabel: strings.NavigationLabel,
openNavigation: strings.OpenNavigation,
closeNavigation: strings.CloseNavigation,
openSubmenu: strings.OpenSubmenu,
closeSubmenu: strings.CloseSubmenu,
submenuLabel: strings.SubmenuLabel,
externalLink: strings.ExternalLink,
opened: strings.Opened,
closed: strings.Closed
}
);
this._menuContainer = this._getOrCreateContainer('CustomHeader', this._topPlaceholder);
@@ -213,6 +233,19 @@ export default class MegaMenuApplicationCustomizer
return normalizeMegaMenuMode(this.properties && this.properties.menuMode);
}
private _getCacheVersion(): string {
const value: string = this.properties && this.properties.cacheVersion
? String(this.properties.cacheVersion).trim()
: '1';
return value || '1';
}
private _getLanguageLcid(): number {
const legacyContext: any = (this.context.pageContext as any).legacyPageContext; // tslint:disable-line:no-any
const value: number = legacyContext ? Number(legacyContext.currentLanguage) : 0;
return isFinite(value) && value > 0 ? value : 1033;
}
private _renderStatus(message: string): void {
if (!this._topPlaceholder) {
return;