From f4cdbc005db6950ff72223c2f481d80a00c1d340 Mon Sep 17 00:00:00 2001 From: Bilawal Hameed Date: Thu, 25 Jun 2020 11:18:47 +0200 Subject: [PATCH] techdocs: skeleton of reader (#1438) * chore: replaced default components * feat(techdocs): added skeleton of reader * fix: added types * fix: split out into separate file * fix: add shadowDom hook test * fix: added @backstage/test-utils as dep * fix: formatting python files --- .../container/techdocs-core/src/core.py | 64 +- plugins/techdocs/package.json | 1 + .../ExampleComponent.test.tsx | 34 - .../ExampleComponent/ExampleComponent.tsx | 57 -- .../ExampleFetchComponent.tsx | 108 --- plugins/techdocs/src/plugin.ts | 8 +- .../techdocs/src/reader/components/Reader.tsx | 816 ++++++++++++++++++ .../hooks}/index.ts | 2 +- .../src/reader/hooks/shadowDom.test.tsx | 44 + .../hooks/shadowDom.ts} | 24 +- .../index.ts => reader/index.tsx} | 2 +- 11 files changed, 912 insertions(+), 248 deletions(-) delete mode 100644 plugins/techdocs/src/components/ExampleComponent/ExampleComponent.test.tsx delete mode 100644 plugins/techdocs/src/components/ExampleComponent/ExampleComponent.tsx delete mode 100644 plugins/techdocs/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx create mode 100644 plugins/techdocs/src/reader/components/Reader.tsx rename plugins/techdocs/src/{components/ExampleComponent => reader/hooks}/index.ts (92%) create mode 100644 plugins/techdocs/src/reader/hooks/shadowDom.test.tsx rename plugins/techdocs/src/{components/ExampleFetchComponent/ExampleFetchComponent.test.tsx => reader/hooks/shadowDom.ts} (56%) rename plugins/techdocs/src/{components/ExampleFetchComponent/index.ts => reader/index.tsx} (92%) diff --git a/plugins/techdocs/mkdocs/container/techdocs-core/src/core.py b/plugins/techdocs/mkdocs/container/techdocs-core/src/core.py index 18c22c40e3..e6c4a0403d 100644 --- a/plugins/techdocs/mkdocs/container/techdocs-core/src/core.py +++ b/plugins/techdocs/mkdocs/container/techdocs-core/src/core.py @@ -44,42 +44,42 @@ class TechDocsCore(BasePlugin): config["plugins"]["search"] = search_plugin # Markdown Extensions - config['markdown_extensions'].append('admonition') - config['markdown_extensions'].append('abbr') - config['markdown_extensions'].append('attr_list') - config['markdown_extensions'].append('def_list') - config['markdown_extensions'].append('codehilite') - config['mdx_configs']['codehilite'] = { - 'linenums': True, - 'guess_lang': False, - 'pygments_style': 'friendly', + config["markdown_extensions"].append("admonition") + config["markdown_extensions"].append("abbr") + config["markdown_extensions"].append("attr_list") + config["markdown_extensions"].append("def_list") + config["markdown_extensions"].append("codehilite") + config["mdx_configs"]["codehilite"] = { + "linenums": True, + "guess_lang": False, + "pygments_style": "friendly", } - config['markdown_extensions'].append('toc') - config['mdx_configs']['toc'] = { - 'permalink': True, + config["markdown_extensions"].append("toc") + config["mdx_configs"]["toc"] = { + "permalink": True, } - config['markdown_extensions'].append('footnotes') - config['markdown_extensions'].append('markdown.extensions.tables') - config['markdown_extensions'].append('pymdownx.betterem') - config['mdx_configs']['pymdownx.betterem'] = { - 'smart_enable': 'all', + config["markdown_extensions"].append("footnotes") + config["markdown_extensions"].append("markdown.extensions.tables") + config["markdown_extensions"].append("pymdownx.betterem") + config["mdx_configs"]["pymdownx.betterem"] = { + "smart_enable": "all", } - config['markdown_extensions'].append('pymdownx.caret') - config['markdown_extensions'].append('pymdownx.critic') - config['markdown_extensions'].append('pymdownx.details') - config['markdown_extensions'].append('pymdownx.emoji') - config['mdx_configs']['pymdownx.emoji'] = { - 'emoji_generator': '!!python/name:pymdownx.emoji.to_svg', + config["markdown_extensions"].append("pymdownx.caret") + config["markdown_extensions"].append("pymdownx.critic") + config["markdown_extensions"].append("pymdownx.details") + config["markdown_extensions"].append("pymdownx.emoji") + config["mdx_configs"]["pymdownx.emoji"] = { + "emoji_generator": "!!python/name:pymdownx.emoji.to_svg", } - config['markdown_extensions'].append('pymdownx.inlinehilite') - config['markdown_extensions'].append('pymdownx.magiclink') - config['markdown_extensions'].append('pymdownx.mark') - config['markdown_extensions'].append('pymdownx.smartsymbols') - config['markdown_extensions'].append('pymdownx.superfences') - config['markdown_extensions'].append('pymdownx.tasklist') - config['mdx_configs']['pymdownx.tasklist'] = { - 'custom_checkbox': True, + config["markdown_extensions"].append("pymdownx.inlinehilite") + config["markdown_extensions"].append("pymdownx.magiclink") + config["markdown_extensions"].append("pymdownx.mark") + config["markdown_extensions"].append("pymdownx.smartsymbols") + config["markdown_extensions"].append("pymdownx.superfences") + config["markdown_extensions"].append("pymdownx.tasklist") + config["mdx_configs"]["pymdownx.tasklist"] = { + "custom_checkbox": True, } - config['markdown_extensions'].append('pymdownx.tilde') + config["markdown_extensions"].append("pymdownx.tilde") return config diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 1f83d6290f..9ef290fa40 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -22,6 +22,7 @@ }, "dependencies": { "@backstage/core": "^0.1.1-alpha.12", + "@backstage/test-utils": "^0.1.1-alpha.12", "@backstage/theme": "^0.1.1-alpha.12", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", diff --git a/plugins/techdocs/src/components/ExampleComponent/ExampleComponent.test.tsx b/plugins/techdocs/src/components/ExampleComponent/ExampleComponent.test.tsx deleted file mode 100644 index e4d760526e..0000000000 --- a/plugins/techdocs/src/components/ExampleComponent/ExampleComponent.test.tsx +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 React from 'react'; -import { render } from '@testing-library/react'; -import mockFetch from 'jest-fetch-mock'; -import ExampleComponent from './ExampleComponent'; -import { ThemeProvider } from '@material-ui/core'; -import { lightTheme } from '@backstage/theme'; - -describe('ExampleComponent', () => { - it('should render', () => { - mockFetch.mockResponse(() => new Promise(() => {})); - const rendered = render( - - - , - ); - expect(rendered.getByText('Welcome to techdocs!')).toBeInTheDocument(); - }); -}); diff --git a/plugins/techdocs/src/components/ExampleComponent/ExampleComponent.tsx b/plugins/techdocs/src/components/ExampleComponent/ExampleComponent.tsx deleted file mode 100644 index 2ab29604ee..0000000000 --- a/plugins/techdocs/src/components/ExampleComponent/ExampleComponent.tsx +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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 React, { FC } from 'react'; -import { Typography, Grid } from '@material-ui/core'; -import { - InfoCard, - Header, - Page, - pageTheme, - Content, - ContentHeader, - HeaderLabel, - SupportButton, -} from '@backstage/core'; -import ExampleFetchComponent from '../ExampleFetchComponent'; - -const ExampleComponent: FC<{}> = () => ( - -
- - -
- - - A description of your plugin goes here. - - - - - - All content should be wrapped in a card like this. - - - - - - - - -
-); - -export default ExampleComponent; diff --git a/plugins/techdocs/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx b/plugins/techdocs/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx deleted file mode 100644 index c2139befec..0000000000 --- a/plugins/techdocs/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx +++ /dev/null @@ -1,108 +0,0 @@ -/* - * 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 React, { FC } from 'react'; -import { makeStyles } from '@material-ui/core/styles'; -import { Table, TableColumn, Progress } from '@backstage/core'; -import Alert from '@material-ui/lab/Alert'; -import { useAsync } from 'react-use'; - -const useStyles = makeStyles({ - avatar: { - height: 32, - width: 32, - borderRadius: '50%', - }, -}); - -type User = { - gender: string; // "male" - name: { - title: string; // "Mr", - first: string; // "Duane", - last: string; // "Reed" - }; - location: object; // {street: {number: 5060, name: "Hickory Creek Dr"}, city: "Albany", state: "New South Wales",…} - email: string; // "duane.reed@example.com" - login: object; // {uuid: "4b785022-9a23-4ab9-8a23-cb3fb43969a9", username: "blackdog796", password: "patch",…} - dob: object; // {date: "1983-06-22T12:30:23.016Z", age: 37} - registered: object; // {date: "2006-06-13T18:48:28.037Z", age: 14} - phone: string; // "07-2154-5651" - cell: string; // "0405-592-879" - id: { - name: string; // "TFN", - value: string; // "796260432" - }; - picture: { medium: string }; // {medium: "https://randomuser.me/api/portraits/men/95.jpg",…} - nat: string; // "AU" -}; - -type DenseTableProps = { - users: User[]; -}; - -export const DenseTable: FC = ({ users }) => { - const classes = useStyles(); - - const columns: TableColumn[] = [ - { title: 'Avatar', field: 'avatar' }, - { title: 'Name', field: 'name' }, - { title: 'Email', field: 'email' }, - { title: 'Nationality', field: 'nationality' }, - ]; - - const data = users.map(user => { - return { - avatar: ( - {user.name.first} - ), - name: `${user.name.first} ${user.name.last}`, - email: user.email, - nationality: user.nat, - }; - }); - - return ( - - ); -}; - -const ExampleFetchComponent: FC<{}> = () => { - const { value, loading, error } = useAsync(async (): Promise => { - const response = await fetch('https://randomuser.me/api/?results=20'); - const data = await response.json(); - return data.results; - }, []); - - if (loading) { - return ; - } else if (error) { - return {error.message}; - } - - return ; -}; - -export default ExampleFetchComponent; diff --git a/plugins/techdocs/src/plugin.ts b/plugins/techdocs/src/plugin.ts index 29f9f8b085..23bfa3b714 100644 --- a/plugins/techdocs/src/plugin.ts +++ b/plugins/techdocs/src/plugin.ts @@ -30,16 +30,16 @@ */ import { createPlugin, createRouteRef } from '@backstage/core'; -import ExampleComponent from './components/ExampleComponent'; +import { Reader } from './reader/components/Reader'; export const rootRouteRef = createRouteRef({ - path: '/techdocs', - title: 'techdocs', + path: '/docs', + title: 'Docs', }); export const plugin = createPlugin({ id: 'techdocs', register({ router }) { - router.addRoute(rootRouteRef, ExampleComponent); + router.addRoute(rootRouteRef, Reader); }, }); diff --git a/plugins/techdocs/src/reader/components/Reader.tsx b/plugins/techdocs/src/reader/components/Reader.tsx new file mode 100644 index 0000000000..e306a72424 --- /dev/null +++ b/plugins/techdocs/src/reader/components/Reader.tsx @@ -0,0 +1,816 @@ +/* + * 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 React from 'react'; +import { useShadowDom } from '..'; + +const mockHtml: string = ` + + + + + + + + + + + + + + + + + + + + + + + + + + Download boilerplate - MkDocs Material Boilerplate + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+ +
+ + + + + + + + + + + + +
+
+ + +
+
+
+ +
+
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + + + + + + + + + +

Download boilerplate

+

Git clone

+
git clone https://github.com/peaceiris/mkdocs-material-boilerplate.git
+cd mkdocs-material-boilerplate
+
+ + +

Download zip

+
wget 'https://github.com/peaceiris/mkdocs-material-boilerplate/archive/master.zip'
+unzip master.zip
+cd mkdocs-material-boilerplate-master
+
+ + +

👉 Click me to download zip

+ + + + + + + +
+
+
+
+ + + + +
+ + + + + + + + + + + +`; + +export const Reader = () => { + const shadowDomRef = useShadowDom(); + + React.useEffect(() => { + const divElement = shadowDomRef.current; + if (!divElement?.shadowRoot) { + return; + } + divElement.shadowRoot.innerHTML = mockHtml; + }, [shadowDomRef]); + + return ( + <> +

Shadow DOM should be underneath

+
+ + ); +}; diff --git a/plugins/techdocs/src/components/ExampleComponent/index.ts b/plugins/techdocs/src/reader/hooks/index.ts similarity index 92% rename from plugins/techdocs/src/components/ExampleComponent/index.ts rename to plugins/techdocs/src/reader/hooks/index.ts index e785d45082..f66c5303ed 100644 --- a/plugins/techdocs/src/components/ExampleComponent/index.ts +++ b/plugins/techdocs/src/reader/hooks/index.ts @@ -14,4 +14,4 @@ * limitations under the License. */ -export { default } from './ExampleComponent'; +export { useShadowDom } from './shadowDom'; diff --git a/plugins/techdocs/src/reader/hooks/shadowDom.test.tsx b/plugins/techdocs/src/reader/hooks/shadowDom.test.tsx new file mode 100644 index 0000000000..1d58bf26e7 --- /dev/null +++ b/plugins/techdocs/src/reader/hooks/shadowDom.test.tsx @@ -0,0 +1,44 @@ +/* + * 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 React from 'react'; +import { renderWithEffects } from '@backstage/test-utils'; +import { useShadowDom } from './shadowDom'; + +const ComponentWithoutHook = () => { + return
; +}; + +const ComponentWithHook = () => { + const ref = useShadowDom(); + return
; +}; + +describe('useShadowDom', () => { + it('does not create a Shadow DOM instance', async () => { + const rendered = await renderWithEffects(); + + const divElement = rendered.getByTestId('shadow-dom'); + expect(divElement.shadowRoot).not.toBeInstanceOf(ShadowRoot); + }); + + it('create a Shadow DOM instance', async () => { + const rendered = await renderWithEffects(); + + const divElement = rendered.getByTestId('shadow-dom'); + expect(divElement.shadowRoot).toBeInstanceOf(ShadowRoot); + }); +}); diff --git a/plugins/techdocs/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx b/plugins/techdocs/src/reader/hooks/shadowDom.ts similarity index 56% rename from plugins/techdocs/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx rename to plugins/techdocs/src/reader/hooks/shadowDom.ts index 7fecdc6f11..5edb8f4e4d 100644 --- a/plugins/techdocs/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx +++ b/plugins/techdocs/src/reader/hooks/shadowDom.ts @@ -14,15 +14,17 @@ * limitations under the License. */ -import React from 'react'; -import { render } from '@testing-library/react'; -import mockFetch from 'jest-fetch-mock'; -import ExampleFetchComponent from './ExampleFetchComponent'; +import { useEffect, useRef } from 'react'; +import type { RefObject } from 'react'; -describe('ExampleFetchComponent', () => { - it('should render', async () => { - mockFetch.mockResponse(() => new Promise(() => {})); - const rendered = render(); - expect(await rendered.findByTestId('progress')).toBeInTheDocument(); - }); -}); +type IShadowDOMRefObject = RefObject; +export const useShadowDom: () => IShadowDOMRefObject = () => { + const ref: IShadowDOMRefObject = useRef(null); + + useEffect(() => { + const divElement = ref.current; + divElement?.attachShadow({ mode: 'open' }); + }, [ref]); + + return ref; +}; diff --git a/plugins/techdocs/src/components/ExampleFetchComponent/index.ts b/plugins/techdocs/src/reader/index.tsx similarity index 92% rename from plugins/techdocs/src/components/ExampleFetchComponent/index.ts rename to plugins/techdocs/src/reader/index.tsx index 28482f9fe1..72b51bd0dc 100644 --- a/plugins/techdocs/src/components/ExampleFetchComponent/index.ts +++ b/plugins/techdocs/src/reader/index.tsx @@ -14,4 +14,4 @@ * limitations under the License. */ -export { default } from './ExampleFetchComponent'; +export * from './hooks';