diff --git a/README.md b/README.md index 7bdc030..267a659 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/ToDo.md b/ToDo.md index 7ca8d8c..f3849ff 100644 --- a/ToDo.md +++ b/ToDo.md @@ -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 diff --git a/config/package-solution.json b/config/package-solution.json index 421e9e6..2e27f4a 100644 --- a/config/package-solution.json +++ b/config/package-solution.json @@ -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 }, diff --git a/package-lock.json b/package-lock.json index 4434351..7436942 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "current-navigation", - "version": "1.1.5", + "version": "1.1.6", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 12c4f2a..7c381b0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "current-navigation", - "version": "1.1.5", + "version": "1.1.6", "private": true, "main": "lib/index.js", "engines": { diff --git a/src/extensions/currentNavigation/CurrentNavigationApplicationCustomizer.ts b/src/extensions/currentNavigation/CurrentNavigationApplicationCustomizer.ts index fae82fb..0cc4eb8 100644 --- a/src/extensions/currentNavigation/CurrentNavigationApplicationCustomizer.ts +++ b/src/extensions/currentNavigation/CurrentNavigationApplicationCustomizer.ts @@ -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); } } diff --git a/src/extensions/currentNavigation/CurrentNavigationRenderer.ts b/src/extensions/currentNavigation/CurrentNavigationRenderer.ts index 22778f3..67a9937 100644 --- a/src/extensions/currentNavigation/CurrentNavigationRenderer.ts +++ b/src/extensions/currentNavigation/CurrentNavigationRenderer.ts @@ -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); } } diff --git a/src/services/CurrentNavigationCore.ts b/src/services/CurrentNavigationCore.ts index 929460e..a993f7a 100644 --- a/src/services/CurrentNavigationCore.ts +++ b/src/services/CurrentNavigationCore.ts @@ -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, diff --git a/tests/CurrentNavigationCore.test.ts b/tests/CurrentNavigationCore.test.ts index 38b8e62..c9581a0 100644 --- a/tests/CurrentNavigationCore.test.ts +++ b/tests/CurrentNavigationCore.test.ts @@ -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'); diff --git a/tests/validate-static-assets.js b/tests/validate-static-assets.js index 103c85e..c8c3302 100644 --- a/tests/validate-static-assets.js +++ b/tests/validate-static-assets.js @@ -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);