diff --git a/docs/about/faq.mdx b/docs/about/faq.mdx index dffcad3b..19e8ec6b 100644 --- a/docs/about/faq.mdx +++ b/docs/about/faq.mdx @@ -190,38 +190,38 @@ If you use your ham radio license with Meshtastic, consider both the privileges ## Overview - + ## Android Client - + ## Apple Clients - + ## Channels - + ## Python CLI - + ## Devices - + ## Amateur Radio (ham) Meshtastic can be used by both unlicensed people and licensed HAM operators. - + ## Mesh - + ## Modules - + diff --git a/src/components/FaqAccordion.tsx b/src/components/FaqAccordion.tsx index 08455534..4a9a3993 100644 --- a/src/components/FaqAccordion.tsx +++ b/src/components/FaqAccordion.tsx @@ -16,83 +16,69 @@ export interface Faq { } /** - * Gets the query parameter `openFaqItems` which is an array of - * faq items that should be pre-opened - * @type {Function} + * Finds the nearest heading to an element + * @param {Element} null The element to find the nearest heading to + * @return {Element|null} The heading or null */ -const getOpenFaqItemsFromUrl = (slug: string): string[] => { - if (typeof window !== "undefined") { - // Use URLSearchParams to parse the query parameters from the current URL - const searchParams = new URLSearchParams(window.location.search); +const findNearestHeading = (element: Element): Element | null => { + const isHeading = (element: Element): boolean => /^H[1-6]$/.test(element.tagName); + let currentElement: Element | null = element; - // Get the 'openFaqItems' parameter as a comma-separated string - const openFaqItemsString = searchParams.get(`openFaqItems-${slug}`); + while (currentElement !== null) { + // Check previous siblings + let prevSibling: Element | null = currentElement.previousElementSibling; + while (prevSibling) { + if (isHeading(prevSibling)) { + return prevSibling; + } + prevSibling = prevSibling.previousElementSibling; + } - // If the parameter exists, split it by commas into an array; otherwise, return an empty array - return openFaqItemsString ? openFaqItemsString.split(",") : []; + // If no heading is found among siblings, move to the parent node + currentElement = currentElement.parentElement; } + + return null; }; /** - * Updates query parameters in the url when items are opened - * so that a link can be shared with the faq item already opened + * Takes in uuids from react-accessible-accordion onchange event + * and updates the browser url with the nearest heading's id + * @param {[type]} void [description] + * @return {[type]} [description] */ -const handleChange = ( - openFaqItems: (string | number)[], - slug: string, -): void => { - const searchParams = new URLSearchParams(window.location.search); +const updateURLWithNearestHeadingId = (targetElementUuid: string): void => { + const targetElement: HTMLElement | null = document.getElementById(`accordion__heading-${targetElementUuid}`); - if (openFaqItems.length > 0) { - // Create comma-separated string and update/add the parameter - searchParams.set( - `openFaqItems-${slug}`, - openFaqItems.map(String).join(","), - ); - } else { - // If openFaqItems is empty, remove the parameter from the URL - searchParams.delete(`openFaqItems-${slug}`); + const nearestHeading: Element | null = targetElement ? findNearestHeading(targetElement) : null; + + if (nearestHeading && nearestHeading.id) { + window.history.pushState({}, '', `#${nearestHeading.id}`); } - - // Construct the new URL, preserve existing parameters - const newUrl = `${window.location.protocol}//${window.location.host}${ - window.location.pathname - }?${searchParams.toString()}`; - - // Change the URL without reloading the page - window.history.pushState({ path: newUrl }, "", newUrl); -}; +} export const FaqAccordion = ({ rows, - slug, -}: { rows: Faq[]; slug: string }): JSX.Element => { +}: { rows: Faq[] }): JSX.Element => { return ( - Loading FAQ's...}> - {() => { - return ( - { - handleChange(itemUuids, slug); - }} - preExpanded={getOpenFaqItemsFromUrl(slug)} - > - {rows.map((row, index) => ( - // biome-ignore lint/suspicious/noArrayIndexKey: React complains if there is no key - - - {row.title} - - - {row.content} - - - ))} - - ); + { + updateURLWithNearestHeadingId(itemUuids); }} - + > + {rows.map((row, index) => ( + // biome-ignore lint/suspicious/noArrayIndexKey: React complains if there is no key + + + {row.title} + + + {row.content} + + + ))} + ); };