feat(mkdocs-reader): initial navigation inside docs

This commit is contained in:
Sebastian Qvarfordt
2020-06-26 12:18:52 +02:00
parent f418681341
commit 984b5acccc
7 changed files with 163 additions and 36 deletions
+1
View File
@@ -29,6 +29,7 @@
"@material-ui/lab": "4.0.0-alpha.45",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-router-dom": "^5.2.0",
"react-use": "^14.2.0"
},
"devDependencies": {
+16
View File
@@ -0,0 +1,16 @@
/*
* 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.
*/
export const baseUrl = 'https://techdocs-mock-sites.storage.googleapis.com';
+1 -1
View File
@@ -33,7 +33,7 @@ import { createPlugin, createRouteRef } from '@backstage/core';
import { Reader } from './reader/components/Reader';
export const rootRouteRef = createRouteRef({
path: '/docs',
path: '/docs/:componentId/*',
title: 'Docs',
});
@@ -17,6 +17,10 @@
import React from 'react';
import { useShadowDom } from '..';
import { useAsync } from 'react-use';
import transformer, { addBaseUrl, rewriteDocLinks } from '../../transformers';
import { baseUrl } from '../../config';
import { Link } from '@backstage/core';
import { useLocation, useParams } from 'react-router-dom';
const useFetch = (url: string) => {
const state = useAsync(async () => {
@@ -28,54 +32,42 @@ const useFetch = (url: string) => {
return state;
};
const addBaseUrl = (htmlString: string, baseUrl: string): string => {
const domParser = new DOMParser().parseFromString(htmlString, 'text/html');
const updateDom = <T extends Element>(
list: Array<T>,
attributeName: string,
): void => {
Array.from(list).forEach((elem: T) => {
const newUrl = new URL(
elem.getAttribute(attributeName)!,
baseUrl,
).toString();
elem.setAttribute(attributeName, newUrl);
});
};
updateDom<HTMLImageElement>(Array.from(domParser.images), 'src');
updateDom<HTMLAnchorElement | HTMLAreaElement>(
Array.from(domParser.links),
'href',
);
updateDom<HTMLLinkElement>(
Array.from(domParser.querySelectorAll('link')),
'href',
);
return domParser.body.parentElement?.outerHTML || htmlString;
const normalizeUrl = (path: string) => {
return path.replace(/\/\/index.html$/, '/index.html');
};
export const Reader = () => {
const location = useLocation();
const { componentId, '*': path } = useParams();
const shadowDomRef = useShadowDom();
const state = useFetch(
'https://techdocs-mock-sites.storage.googleapis.com/mkdocs/index.html',
normalizeUrl(
`${baseUrl}${location.pathname.replace('/docs', '')}/index.html`,
),
);
// https://techdocs-mock-sites.storage.googleapis.com/mkdocs/user-guide/configuration/custom-themes/index.html
React.useEffect(() => {
const divElement = shadowDomRef.current;
if (divElement?.shadowRoot && state.value) {
divElement.shadowRoot.innerHTML = addBaseUrl(
state.value,
'https://techdocs-mock-sites.storage.googleapis.com/mkdocs/',
);
divElement.shadowRoot.innerHTML = transformer(state.value, [
addBaseUrl({
baseUrl,
componentId,
path,
}),
rewriteDocLinks({
componentId,
}),
]);
}
}, [shadowDomRef, state]);
}, [shadowDomRef, state, componentId, path]);
return (
<>
<h3>Shadow DOM should be underneath</h3>
<nav>
<Link to="/docs/mkdocs">mkdocs</Link>
<Link to="/docs/backstage-microsite">Backstage docs</Link>
</nav>
<div ref={shadowDomRef} />
</>
);
@@ -0,0 +1,52 @@
/*
* 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.
*/
type AddBaseUrlOptions = {
baseUrl: string;
componentId: string;
path: string;
};
export const addBaseUrl = ({
baseUrl,
componentId,
path,
}: AddBaseUrlOptions) => {
return (dom: Document): Document => {
const updateDom = <T extends Element>(
list: Array<T>,
attributeName: string,
): void => {
Array.from(list)
.filter(elem => !!elem.getAttribute(attributeName))
.forEach((elem: T) => {
const newUrl = new URL(
elem.getAttribute(attributeName)!,
`${baseUrl}/${componentId}/${path}`,
).toString();
elem.setAttribute(attributeName, newUrl);
});
};
updateDom<HTMLImageElement>(Array.from(dom.images), 'src');
updateDom<HTMLScriptElement>(Array.from(dom.scripts), 'src');
updateDom<HTMLLinkElement>(
Array.from(dom.querySelectorAll('link')),
'href',
);
return dom;
};
};
@@ -0,0 +1,28 @@
/*
* 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.
*/
export * from './addBaseUrl';
export * from './rewriteDocLinks';
type Transformer = (dom: Document) => Document;
export default (html: string, transformers: Transformer[]): string => {
const dom = new DOMParser().parseFromString(html, 'text/html');
transformers.forEach(transformer => transformer(dom));
return dom.body.parentElement?.outerHTML ?? '';
};
@@ -0,0 +1,38 @@
/*
* 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.
*/
type AddBaseUrlOptions = {
componentId: string;
};
export const rewriteDocLinks = ({ componentId }: AddBaseUrlOptions) => {
return (dom: Document): Document => {
const updateDom = <T extends Element>(
list: Array<T>,
attributeName: string,
): void => {
Array.from(list)
.filter(elem => !!elem.getAttribute(attributeName))
.forEach((elem: T) => {
const newUrl = `${componentId}/${elem.getAttribute(attributeName)}`;
elem.setAttribute(attributeName, newUrl);
});
};
updateDom(Array.from(dom.getElementsByTagName('a')), 'href');
return dom;
};
};