Bump version to 1.1.3, add detailed lifecycle logging in debug mode, and enhance error handling in services

This commit is contained in:
Torsten Brendgen
2026-07-19 22:43:59 +02:00
parent 5f1d26c7f6
commit 2d723da5a2
8 changed files with 165 additions and 14 deletions

View File

@@ -37,40 +37,66 @@ export default class MegaMenuApplicationCustomizer
@override
public onInit(): Promise<void> {
debugLog(this._isDebugEnabled(), LOG_SOURCE, 'Initialized ' + strings.Title);
const debug: boolean = this._isDebugEnabled();
debugLog(debug, LOG_SOURCE, 'onInit started.', {
componentId: UserCustomActionMegaMenuId,
version: '1.1.3',
properties: this.properties || {},
pageUrl: window.location.href,
webUrl: this.context.pageContext.web.absoluteUrl,
siteUrl: this.context.pageContext.site.absoluteUrl
});
this.context.placeholderProvider.changedEvent.add(this, this._renderPlaceHolders);
debugLog(debug, LOG_SOURCE, 'Placeholder changedEvent handler registered.');
this._renderPlaceHolders();
debugLog(debug, LOG_SOURCE, 'onInit completed. Asynchronous menu loading may still be running.');
return Promise.resolve();
}
private _renderPlaceHolders(): void {
const debug: boolean = this._isDebugEnabled();
const availablePlaceholders: string = this.context.placeholderProvider.placeholderNames
.map(name => PlaceholderName[name])
.join(', ');
debugLog(this._isDebugEnabled(), LOG_SOURCE, 'Available placeholders:', availablePlaceholders);
debugLog(debug, LOG_SOURCE, 'Rendering placeholders.', {
availablePlaceholders: availablePlaceholders,
topPlaceholderAlreadyCreated: !!this._topPlaceholder
});
if (!this._topPlaceholder) {
debugLog(debug, LOG_SOURCE, 'Requesting the Top placeholder.');
this._topPlaceholder = this.context.placeholderProvider.tryCreateContent(
PlaceholderName.Top,
{ onDispose: this._onDispose }
);
if (!this._topPlaceholder) {
debugError(this._isDebugEnabled(), LOG_SOURCE, 'The expected placeholder (Top) was not found.');
debugError(debug, LOG_SOURCE, 'The expected placeholder (Top) was not found.');
return;
}
debugLog(debug, LOG_SOURCE, 'Top placeholder acquired.', {
domElementId: this._topPlaceholder.domElement.id || '',
domElementClassName: this._topPlaceholder.domElement.className || ''
});
const termSetIdentifier: string = this._getTermSetIdentifier();
if (!termSetIdentifier) {
debugWarn(this._isDebugEnabled(), LOG_SOURCE, 'No termSetId or termSetName configured. Mega Menu rendering is skipped.');
debugWarn(debug, LOG_SOURCE, 'No termSetId or termSetName configured. Mega Menu rendering is skipped.', this.properties || {});
this._renderStatus(strings.ConfigurationMissing);
return;
}
this._renderMegaMenu(termSetIdentifier);
debugLog(debug, LOG_SOURCE, 'Starting MegaMenu rendering.', {
termSetIdentifier: termSetIdentifier,
cacheMinutes: this._getCacheMinutes()
});
this._renderMegaMenu(termSetIdentifier, debug);
} else {
debugLog(debug, LOG_SOURCE, 'Top placeholder is already initialized; no duplicate render is started.');
}
}
@@ -80,6 +106,7 @@ export default class MegaMenuApplicationCustomizer
}
try {
debugLog(debug, LOG_SOURCE, 'Creating TaxonomyNavigationService.');
const taxonomyService: TaxonomyNavigationService = new TaxonomyNavigationService(
this.context,
termSetIdentifier,
@@ -87,9 +114,14 @@ export default class MegaMenuApplicationCustomizer
this._getCacheMinutes()
);
debugLog(debug, LOG_SOURCE, 'Requesting navigation items from taxonomy.');
const menuItems: IMenuItem[] = await taxonomyService.getMenuItems();
debugLog(debug, LOG_SOURCE, 'Navigation items received.', {
topLevelItemCount: menuItems.length
});
if (this._renderer) {
debugLog(debug, LOG_SOURCE, 'Disposing the previous renderer instance.');
this._renderer.dispose();
}
@@ -101,14 +133,19 @@ export default class MegaMenuApplicationCustomizer
this._menuContainer = this._getOrCreateContainer('CustomHeader', this._topPlaceholder);
if (this._menuContainer) {
debugLog(debug, LOG_SOURCE, 'Rendering into the selected container.', {
id: this._menuContainer.id || '',
className: this._menuContainer.className || ''
});
this._renderer.render(this._menuContainer);
} else {
debugWarn(debug, LOG_SOURCE, 'No dedicated container was returned; rendering into the Top placeholder.');
this._renderer.render(this._topPlaceholder.domElement);
}
debugLog(debug, LOG_SOURCE, 'MegaMenu rendered successfully with ' + menuItems.length + ' top-level items.');
} catch (error) {
debugError(debug, LOG_SOURCE, 'Error rendering MegaMenu.', error);
debugError(debug, LOG_SOURCE, 'Error rendering MegaMenu.', this._describeError(error), error);
this._renderStatus(strings.LoadError);
}
}
@@ -117,15 +154,26 @@ export default class MegaMenuApplicationCustomizer
const container = document.getElementById(id);
if (container) {
debugLog(this._isDebugEnabled(), LOG_SOURCE, 'Existing host container found.', {
requestedId: id,
containerId: container.id,
containerClassName: container.className || ''
});
let host: HTMLElement = document.getElementById('MegaMenuHost');
if (!host) {
host = document.createElement('div');
host.id = 'MegaMenuHost';
container.appendChild(host);
debugLog(this._isDebugEnabled(), LOG_SOURCE, 'MegaMenuHost was created inside the existing host container.');
} else {
debugLog(this._isDebugEnabled(), LOG_SOURCE, 'Existing MegaMenuHost will be reused.');
}
return host;
}
debugLog(this._isDebugEnabled(), LOG_SOURCE, 'No external host container found; the Top placeholder will be used.', {
requestedId: id
});
return placeholder.domElement;
}
@@ -175,6 +223,14 @@ export default class MegaMenuApplicationCustomizer
return /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(value);
}
private _describeError(error: any): { name: string; message: string; stack: string } { // tslint:disable-line:no-any
return {
name: error && error.name ? String(error.name) : '',
message: error && error.message ? String(error.message) : String(error || 'Unknown error'),
stack: error && error.stack ? String(error.stack) : ''
};
}
private _isDebugEnabled(debugOverride?: boolean): boolean {
if (typeof debugOverride === 'boolean') {
return debugOverride;