diff --git a/plugins/techdocs/src/reader/components/Reader.tsx b/plugins/techdocs/src/reader/components/Reader.tsx index d4e84aa8d5..0175462cc3 100644 --- a/plugins/techdocs/src/reader/components/Reader.tsx +++ b/plugins/techdocs/src/reader/components/Reader.tsx @@ -17,10 +17,14 @@ import React from 'react'; import { useShadowDom } from '..'; import { useAsync } from 'react-use'; -import transformer, { addBaseUrl, rewriteDocLinks } from '../transformers'; +import transformer, { + addBaseUrl, + rewriteDocLinks, + addEventListener, +} from '../transformers'; import { docStorageURL } from '../../config'; import { Link } from '@backstage/core'; -import { useLocation, useParams } from 'react-router-dom'; +import { useLocation, useParams, useNavigate } from 'react-router-dom'; import URLParser from '../urlParser'; const useFetch = (url: string) => { @@ -33,10 +37,6 @@ const useFetch = (url: string) => { return state; }; -const normalizeUrl = (path: string) => { - return path.replace(/\/\/index.html$/, '/index.html'); -}; - const useEnforcedTrailingSlash = (): void => { React.useEffect(() => { const actualUrl = window.location.href; @@ -52,11 +52,12 @@ export const Reader = () => { const location = useLocation(); const { componentId, '*': path } = useParams(); const shadowDomRef = useShadowDom(); - const state = useFetch( - normalizeUrl( - `${docStorageURL}${location.pathname.replace('/docs', '')}/index.html`, - ), - ); + const navigate = useNavigate(); + const normalizedUrl = new URLParser( + `${docStorageURL}${location.pathname.replace('/docs', '')}`, + '.', + ).parse(); + const state = useFetch(`${normalizedUrl}index.html`); useEnforcedTrailingSlash(); @@ -75,10 +76,16 @@ export const Reader = () => { ]); divElement.shadowRoot.innerHTML = ''; - if (transformedElement) + if (transformedElement) { divElement.shadowRoot.appendChild(transformedElement); + transformer(divElement.shadowRoot.children[0], [ + addEventListener({ + onClick: navigate, + }), + ]); + } } - }, [shadowDomRef, state, componentId, path]); + }, [shadowDomRef, state, componentId, path, navigate]); return ( <> diff --git a/plugins/techdocs/src/reader/transformers/addBaseUrl.ts b/plugins/techdocs/src/reader/transformers/addBaseUrl.ts index e07a41fc03..653f0b2f71 100644 --- a/plugins/techdocs/src/reader/transformers/addBaseUrl.ts +++ b/plugins/techdocs/src/reader/transformers/addBaseUrl.ts @@ -15,6 +15,7 @@ */ import URLParser from '../urlParser'; +import type { Transformer } from './index'; type AddBaseUrlOptions = { docStorageURL: string; @@ -26,10 +27,10 @@ export const addBaseUrl = ({ docStorageURL, componentId, path, -}: AddBaseUrlOptions) => { - return (dom: Document): Document => { +}: AddBaseUrlOptions): Transformer => { + return dom => { const updateDom = ( - list: Array, + list: HTMLCollectionOf | NodeListOf, attributeName: string, ): void => { Array.from(list) @@ -43,12 +44,9 @@ export const addBaseUrl = ({ }); }; - updateDom(Array.from(dom.images), 'src'); - updateDom(Array.from(dom.scripts), 'src'); - updateDom( - Array.from(dom.querySelectorAll('link')), - 'href', - ); + updateDom(dom.querySelectorAll('img'), 'src'); + updateDom(dom.querySelectorAll('script'), 'src'); + updateDom(dom.querySelectorAll('link'), 'href'); return dom; }; diff --git a/plugins/techdocs/src/reader/transformers/addEventListener.ts b/plugins/techdocs/src/reader/transformers/addEventListener.ts new file mode 100644 index 0000000000..32b9d6b0b9 --- /dev/null +++ b/plugins/techdocs/src/reader/transformers/addEventListener.ts @@ -0,0 +1,41 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { Transformer } from './index'; + +type AddEventListenerOptions = { + onClick: (newUrl: string) => void; +}; + +export const addEventListener = ({ + onClick, +}: AddEventListenerOptions): Transformer => { + return dom => { + Array.from(dom.getElementsByTagName('a')).forEach(elem => { + elem.addEventListener('click', (e: MouseEvent) => { + e.preventDefault(); + const target = e.target as HTMLAnchorElement; + if (target?.getAttribute('href')) { + onClick( + target.getAttribute('href')!.replace(window.location.origin, ''), + ); + } + }); + }); + + return dom; + }; +}; diff --git a/plugins/techdocs/src/reader/transformers/index.ts b/plugins/techdocs/src/reader/transformers/index.ts index 008c86e261..74c9eb6549 100644 --- a/plugins/techdocs/src/reader/transformers/index.ts +++ b/plugins/techdocs/src/reader/transformers/index.ts @@ -16,16 +16,48 @@ export * from './addBaseUrl'; export * from './rewriteDocLinks'; +export * from './addEventListener'; -type Transformer = (dom: Document) => Document; +export type Transformer = (dom: Element) => Element; -export default ( - html: string, +function transform( + html: string | Element, transformers: Transformer[], -): HTMLElement | undefined => { - const dom = new DOMParser().parseFromString(html, 'text/html'); +): Element { + let dom: Element; + + if (typeof html === 'string') { + dom = new DOMParser().parseFromString(html, 'text/html').documentElement; + } else if (html instanceof Element) { + dom = html; + } else { + throw new Error('dom is not a recognized type'); + } transformers.forEach(transformer => transformer(dom)); - return dom.body.parentElement ?? undefined; -}; + return dom; +} + +// function transform( +// html: string, +// transformers: Transformer[], +// ): HTMLElement { +// const dom = new DOMParser().parseFromString(html, 'text/html'); + +// transformers.forEach(transformer => transformer(dom)); + +// return dom.documentElement; +// }; + +// function transform( +// html: HTMLElement, +// transformers: Transformer[], +// ): HTMLElement { + +// transformers.forEach(transformer => transformer(element)); + +// return html; +// }; + +export default transform; diff --git a/plugins/techdocs/src/reader/transformers/rewriteDocLinks.ts b/plugins/techdocs/src/reader/transformers/rewriteDocLinks.ts index 373db51abd..3f80dbe4e9 100644 --- a/plugins/techdocs/src/reader/transformers/rewriteDocLinks.ts +++ b/plugins/techdocs/src/reader/transformers/rewriteDocLinks.ts @@ -15,11 +15,12 @@ */ import URLParser from '../urlParser'; +import type { Transformer } from './index'; type AddBaseUrlOptions = {}; -export const rewriteDocLinks = ({}: AddBaseUrlOptions) => { - return (dom: Document): Document => { +export const rewriteDocLinks = ({}: AddBaseUrlOptions): Transformer => { + return dom => { const updateDom = ( list: Array, attributeName: string,