Update Current Navigation to version 1.1.6 with new features and improvements, including reliable detection of active path using current SharePoint web URL.

This commit is contained in:
Torsten Brendgen
2026-08-23 22:14:52 +02:00
parent 26edaf6652
commit 6c67625eba
10 changed files with 48 additions and 12 deletions
+3 -1
View File
@@ -1,6 +1,6 @@
# CurrentNavigation
CurrentNavigation 1.1.5 ist ein SPFx-1.4.1-Application-Customizer für moderne Seiten in SharePoint Server
CurrentNavigation 1.1.6 ist ein SPFx-1.4.1-Application-Customizer für moderne Seiten in SharePoint Server
Subscription Edition. Die Solution ergänzt unterhalb der vorhandenen Einträge im modernen SiteNavigation-Bereich
einen Treeview für das aktuelle Web. Native Einträge wie Documents bleiben unverändert sichtbar.
@@ -23,6 +23,8 @@ Pos1 und Ende.
Der initiale Aufklappzustand ist über `initialExpansion` steuerbar: `activePath` öffnet wie bisher nur den aktiven
Pfad, `expanded` öffnet alle Zweige und `collapsed` startet mit vollständig geschlossenen Zweigen.
Für `activePath` wird die Term-URL sowohl mit der aktuellen Seite als auch mit der aktuellen SharePoint-Web-URL
verglichen. Dadurch wird der Pfad auch auf modernen `SitePages` und in Listen oder Bibliotheken korrekt geöffnet.
Wenn kein passender Root-Term gefunden wird oder das Termset nicht geladen werden kann, bleibt ausschließlich die
native SharePoint-Sitenavigation sichtbar.
+2 -1
View File
@@ -1,11 +1,12 @@
# CurrentNavigation – Umsetzungsstand
Zielversion: 1.1.4
Zielversion: 1.1.6
Status: Implementierung und lokale Tests abgeschlossen; Ship-Build auf Node 8 ausstehend
- [x] SPFx Application Customizer für moderne Seiten
- [x] Ergänzendes Rendering unterhalb der nativen Einträge im modernen SiteNavigation-Container
- [x] Barrierearmer Treeview mit Tastatursteuerung
- [x] `activePath` über aktuelle Seiten- und Web-URL zuverlässig erkennen
- [x] Site-Collection-weite Registrierung
- [x] Termset-Picker über PortalSettings
- [x] Cache-Dauer, Cache-Version und Debug-Modus
+1 -1
View File
@@ -3,7 +3,7 @@
"solution": {
"name": "current-navigation-client-side-solution",
"id": "bb8f9af5-b318-4111-8401-ce189cfe1b1a",
"version": "1.1.5.0",
"version": "1.1.6.0",
"includeClientSideAssets": true,
"skipFeatureDeployment": true
},
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "current-navigation",
"version": "1.1.5",
"version": "1.1.6",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "current-navigation",
"version": "1.1.5",
"version": "1.1.6",
"private": true,
"main": "lib/index.js",
"engines": {
@@ -124,7 +124,7 @@ export default class CurrentNavigationApplicationCustomizer
navigationLabel: strings.NavigationLabel,
expand: strings.Expand,
collapse: strings.Collapse
}, this.initialExpansion());
}, this.initialExpansion(), this.context.pageContext.web.absoluteUrl);
}
} catch (error) { this.debug(strings.LoadError, error); }
}
@@ -1,4 +1,4 @@
import { normalizeNavigationPath } from '../../services/CurrentNavigationCore';
import { isCurrentNavigationUrl } from '../../services/CurrentNavigationCore';
import { ICurrentNavigationItem } from '../../services/CurrentNavigationTypes';
export interface ICurrentNavigationRendererStrings {
@@ -22,17 +22,20 @@ export class CurrentNavigationRenderer {
private keydownHandler: ((event: KeyboardEvent) => void) | undefined;
private strings: ICurrentNavigationRendererStrings;
private initialExpansion: CurrentNavigationInitialExpansion = 'activePath';
private currentWebUrl: string = '';
public render(
container: HTMLElement,
items: ICurrentNavigationItem[],
strings: ICurrentNavigationRendererStrings,
initialExpansion: CurrentNavigationInitialExpansion
initialExpansion: CurrentNavigationInitialExpansion,
currentWebUrl?: string
): void {
this.clear(container);
this.container = container;
this.strings = strings;
this.initialExpansion = initialExpansion;
this.currentWebUrl = currentWebUrl || window.location.href;
const nav: HTMLElement = document.createElement('nav');
nav.className = 'current-navigation';
nav.setAttribute('aria-label', strings.navigationLabel);
@@ -208,6 +211,6 @@ export class CurrentNavigationRenderer {
return false;
}
private isCurrentUrl(url: string): boolean {
return normalizeNavigationPath(window.location.href) === normalizeNavigationPath(url);
return isCurrentNavigationUrl(url, window.location.href, this.currentWebUrl);
}
}
+12
View File
@@ -47,6 +47,18 @@ export function normalizeRootWebPath(value: string): string {
return path || '/';
}
export function isCurrentNavigationUrl(
navigationUrl: string,
currentPageUrl: string,
currentWebUrl: string
): boolean {
const navigationPath: string = normalizeNavigationPath(navigationUrl);
const pagePath: string = normalizeNavigationPath(currentPageUrl);
const webPath: string = normalizeNavigationPath(currentWebUrl);
return navigationPath === pagePath || navigationPath === webPath ||
normalizeRootWebPath(navigationUrl) === webPath;
}
export function selectCurrentNavigationRoot(
terms: ICurrentNavigationTerm[],
currentWebUrl: string,
+16 -1
View File
@@ -1,5 +1,5 @@
import {
buildCurrentNavigationItems, getLocalProperty, normalizeRootWebPath, parseBoolean, resolveTermUrl,
buildCurrentNavigationItems, getLocalProperty, isCurrentNavigationUrl, normalizeRootWebPath, parseBoolean, resolveTermUrl,
selectCurrentNavigationRoot
} from '../src/services/CurrentNavigationCore';
import { ICurrentNavigationTerm } from '../src/services/CurrentNavigationTypes';
@@ -41,6 +41,21 @@ const root: ICurrentNavigationTerm = selectCurrentNavigationRoot(
assert('root is selected through its site home URL', !!root && root.Id === 'root');
assert('site home URL is normalized to its web',
normalizeRootWebPath('/sites/portal/hr/SitePages/Home.aspx') === '/sites/portal/hr');
assert('site URL is current while a modern home page is open', isCurrentNavigationUrl(
'http://dhs.contoso.local/organisation/cir/CIR_13/',
'http://dhs.contoso.local/organisation/cir/CIR_13/SitePages/Home.aspx',
'http://dhs.contoso.local/organisation/cir/CIR_13'
));
assert('configured site home URL matches another page in the same web', isCurrentNavigationUrl(
'http://dhs.contoso.local/organisation/cir/CIR_13/SitePages/Home.aspx',
'http://dhs.contoso.local/organisation/cir/CIR_13/SitePages/News.aspx',
'http://dhs.contoso.local/organisation/cir/CIR_13/'
));
assert('different web is not current', !isCurrentNavigationUrl(
'http://dhs.contoso.local/organisation/cir/CIR_3/',
'http://dhs.contoso.local/organisation/cir/CIR_13/SitePages/Home.aspx',
'http://dhs.contoso.local/organisation/cir/CIR_13/'
));
const items = buildCurrentNavigationItems(terms, root, 'https://portal/sites/portal', 1031, 3, false);
assert('root is not rendered by default', items.length === 1 && items[0].id === 'docs');
assert('descendants are nested', items[0].items.length === 1 && items[0].items[0].id === 'forms');
+5 -2
View File
@@ -9,8 +9,8 @@ var manifest = json('src/extensions/currentNavigation/CurrentNavigationApplicati
var app = read('src/extensions/currentNavigation/CurrentNavigationApplicationCustomizer.ts');
var renderer = read('src/extensions/currentNavigation/CurrentNavigationRenderer.ts');
var service = read('src/services/CurrentNavigationTaxonomyService.ts');
assert('package version', packageJson.version === '1.1.5');
assert('solution version', solution.version === '1.1.5.0');
assert('package version', packageJson.version === '1.1.6');
assert('solution version', solution.version === '1.1.6.0');
assert('tenant deployment', solution.skipFeatureDeployment === true);
assert('component type', manifest.extensionType === 'ApplicationCustomizer');
assert('safe renderer', renderer.indexOf('innerHTML') < 0 && app.indexOf('innerHTML') < 0);
@@ -28,6 +28,9 @@ assert('treeview semantics', renderer.indexOf("setAttribute('role', root ? 'tree
renderer.indexOf("setAttribute('role', 'treeitem')") >= 0);
assert('configurable initial expansion', renderer.indexOf('isInitiallyExpanded(this.initialExpansion') >= 0 &&
renderer.indexOf("initialExpansion === 'expanded'") >= 0 && app.indexOf('this.initialExpansion()') >= 0);
assert('active path receives the current SharePoint web URL',
app.indexOf('this.context.pageContext.web.absoluteUrl') >= 0 &&
renderer.indexOf('isCurrentNavigationUrl(url, window.location.href, this.currentWebUrl)') >= 0);
assert('legacy selector compatibility', renderer.indexOf(':scope') < 0);
assert('local properties loaded', service.indexOf('LocalCustomProperties') >= 0);
assert('root property present', read('src/services/CurrentNavigationCore.ts').indexOf('CurrentNavigation.Root') >= 0);