diff --git a/samples/react-page-navigator/README.md b/samples/react-page-navigator/README.md index eac915acb..9493a0809 100644 --- a/samples/react-page-navigator/README.md +++ b/samples/react-page-navigator/README.md @@ -28,7 +28,7 @@ Version|Date|Comments 1.2|May, 2022|SPFx Upgraded to 1.14.0 1.3|June 9, 2022|Updated React package from `^16.14.0` to `16.13.1` 1.4|June 29, 2022|Adds the capability to find collapsible section headers and insert them into the navigation -1.5|July 2, 2022|Fixes heading links containing a lot of spaces between words and caps heading length at 128 charsgu +1.5|July 19, 2022|Bug fixes ## Minimal Path to Awesome diff --git a/samples/react-page-navigator/assets/sample.json b/samples/react-page-navigator/assets/sample.json index 83712a382..9cf42a4a5 100644 --- a/samples/react-page-navigator/assets/sample.json +++ b/samples/react-page-navigator/assets/sample.json @@ -9,7 +9,7 @@ "This web part fetches all the automatically added Header anchor tags in a SharePoint page and displays them in a Navigation component." ], "creationDateTime": "2019-09-05", - "updateDateTime": "2022-07-02", + "updateDateTime": "2022-07-19", "products": [ "SharePoint" ], diff --git a/samples/react-page-navigator/src/Service/SPService.ts b/samples/react-page-navigator/src/Service/SPService.ts index 8cc3f981e..23de248c9 100644 --- a/samples/react-page-navigator/src/Service/SPService.ts +++ b/samples/react-page-navigator/src/Service/SPService.ts @@ -37,13 +37,38 @@ export class SPService { return anchorUrl; } + /** + * Nests a new nav link within the nav links tree + * @param currentLinks current nav links + * @param newLink the new nav link to be added to the structure + * @param order place order of the new link + * @param depth sequence depth + * @returns navLinks + */ + public static navLinkBuilder(currentLinks: INavLink[], newLink: INavLink, order: number, depth: number = 0): INavLink[] { + const lastIndex = currentLinks.length - 1; + + if (currentLinks[lastIndex].links.length === 0 || order === depth) { + if (depth === 0) { + currentLinks.push(newLink); + } else { + currentLinks[lastIndex].links.push(newLink); + } + } else { + depth++; + currentLinks[lastIndex].links.concat(this.navLinkBuilder(currentLinks[lastIndex].links, newLink, order, depth)); + } + + return currentLinks; + } + /** * Returns the Anchor Links for Nav element * @param context Web part context * @returns anchorLinks */ public static async GetAnchorLinks(context: WebPartContext) { - const anchorLinks: INavLink[] = []; + let anchorLinks: INavLink[] = []; try { /* Page ID on which the web part is added */ @@ -55,82 +80,51 @@ export class SPService { const canvasContent1 = jsonData.CanvasContent1; const canvasContent1JSON: any[] = JSON.parse(canvasContent1); - /* Initialize variables to be used for sorting and adding the Navigation links */ - let headingIndex = 0; - let subHeadingIndex = -1; - let prevHeadingOrder = 0; - this.allUrls = []; /* Traverse through all the Text web parts in the page */ canvasContent1JSON.map((webPart) => { - if (webPart.zoneGroupMetadata) { - const headingValue = webPart.zoneGroupMetadata.displayName; - const anchorUrl = this.GetAnchorUrl(headingValue); + if (webPart.zoneGroupMetadata && webPart.zoneGroupMetadata.type === 1) { + const headingIsEmpty: boolean = webPart.zoneGroupMetadata.displayName === ''; + const headingValue: string = headingIsEmpty ? 'Empty Heading' : webPart.zoneGroupMetadata.displayName ; + const anchorUrl: string = this.GetAnchorUrl(headingValue); this.allUrls.push(anchorUrl); - /* Add link to Nav element */ - anchorLinks.push({ name: headingValue, key: anchorUrl, url: anchorUrl, links: [], isExpanded: webPart.zoneGroupMetadata.isExpanded }); + // Limitation! This will break with headings containing the same name + if (anchorLinks.filter(x => x.name === headingValue).length === 0) { + // Add link to nav element + anchorLinks.push({ name: headingValue, key: anchorUrl, url: !headingIsEmpty && anchorUrl, links: [], isExpanded: webPart.zoneGroupMetadata.isExpanded }); + } } if (webPart.innerHTML) { const HTMLString: string = webPart.innerHTML; + const hasCollapsableHeader: boolean = webPart.zoneGroupMetadata && + webPart.zoneGroupMetadata.type === 1 && + ( anchorLinks.filter(x => x.name === webPart.zoneGroupMetadata.displayName).length === 1 || + webPart.zoneGroupMetadata.displayName === '' ); - const htmlObject = document.createElement('div'); + const htmlObject: HTMLDivElement = document.createElement('div'); htmlObject.innerHTML = HTMLString; - const headers = htmlObject.querySelectorAll('h1, h2, h3, h4'); + const headers: NodeListOf = htmlObject.querySelectorAll('h1, h2, h3, h4'); headers.forEach(header => { - const headingValue = header.textContent; - let headingOrder = parseInt(header.tagName.substring(1)); + const headingValue: string = header.textContent; + let headingOrder: number = parseInt(header.tagName.substring(1)); + // -2 because the text webpart heading 1 uses a h2 element + headingOrder -= 2; - if (webPart.zoneGroupMetadata) { + if (hasCollapsableHeader) { headingOrder++; } - const anchorUrl = this.GetAnchorUrl(headingValue); + const anchorUrl: string = this.GetAnchorUrl(headingValue); this.allUrls.push(anchorUrl); - /* Add links to Nav element */ - if (anchorLinks.length === 0) { - anchorLinks.push({ name: headingValue, key: anchorUrl, url: anchorUrl, links: [], isExpanded: true }); - } else { - if (headingOrder <= prevHeadingOrder) { - /* Adding or Promoting links */ - switch (headingOrder) { - case 2: - anchorLinks.push({ name: headingValue, key: anchorUrl, url: anchorUrl, links: [], isExpanded: true }); - headingIndex++; - subHeadingIndex = -1; - break; - case 4: - if (subHeadingIndex > -1) { - anchorLinks[headingIndex].links[subHeadingIndex].links.push({ name: headingValue, key: anchorUrl, url: anchorUrl, links: [], isExpanded: true }); - } else { - anchorLinks[headingIndex].links.push({ name: headingValue, key: anchorUrl, url: anchorUrl, links: [], isExpanded: true }); - } - break; - default: - anchorLinks[headingIndex].links.push({ name: headingValue, key: anchorUrl, url: anchorUrl, links: [], isExpanded: true }); - subHeadingIndex = anchorLinks[headingIndex].links.length - 1; - break; - } - } else { - /* Making sub links */ - if (headingOrder === 3) { - anchorLinks[headingIndex].links.push({ name: headingValue, key: anchorUrl, url: anchorUrl, links: [], isExpanded: true }); - subHeadingIndex = anchorLinks[headingIndex].links.length - 1; - } else { - if (subHeadingIndex > -1) { - anchorLinks[headingIndex].links[subHeadingIndex].links.push({ name: headingValue, key: anchorUrl, url: anchorUrl, links: [], isExpanded: true }); - } else { - anchorLinks[headingIndex].links.push({ name: headingValue, key: anchorUrl, url: anchorUrl, links: [], isExpanded: true }); - } - } - } - } - prevHeadingOrder = headingOrder; + // Add link to nav element + const newNavLink: INavLink = { name: headingValue, key: anchorUrl, url: anchorUrl, links: [], isExpanded: true }; + anchorLinks = this.navLinkBuilder(anchorLinks, newNavLink, headingOrder, hasCollapsableHeader ? 1 : 0); }); } });