diff options
author | Stas Vilchik <stas.vilchik@sonarsource.com> | 2018-07-13 12:27:02 +0200 |
---|---|---|
committer | SonarTech <sonartech@sonarsource.com> | 2018-07-25 20:21:20 +0200 |
commit | b998b44aafeff3726122bbc491e2c84aed284b61 (patch) | |
tree | fbc8aea8d272107b7aeeec6b89e6cee1eac93ef6 /server/sonar-web/src/main/js/components/docs | |
parent | 4423587a87475044fb3eea1229eca5f52177db95 (diff) | |
download | sonarqube-b998b44aafeff3726122bbc491e2c84aed284b61.tar.gz sonarqube-b998b44aafeff3726122bbc491e2c84aed284b61.zip |
SONAR-11013 Add search capabilities to the embedded documentation (#513)
Diffstat (limited to 'server/sonar-web/src/main/js/components/docs')
7 files changed, 46 insertions, 227 deletions
diff --git a/server/sonar-web/src/main/js/components/docs/DocInclude.tsx b/server/sonar-web/src/main/js/components/docs/DocInclude.tsx deleted file mode 100644 index d499b27c22c..00000000000 --- a/server/sonar-web/src/main/js/components/docs/DocInclude.tsx +++ /dev/null @@ -1,75 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import * as React from 'react'; -import { lazyLoad } from '../lazyLoad'; - -const DocMarkdownBlock = lazyLoad(() => import('./DocMarkdownBlock')); - -interface Props { - className?: string; - path: string; -} - -interface State { - content?: string; -} - -export default class DocInclude extends React.PureComponent<Props, State> { - mounted = false; - state: State = {}; - - componentDidMount() { - this.mounted = true; - this.fetchContent(); - } - - componentWillReceiveProps(nextProps: Props) { - if (nextProps.path !== this.props.path) { - this.setState({ content: undefined }); - } - } - - componentDidUpdate(prevProps: Props) { - if (prevProps.path !== this.props.path) { - this.fetchContent(); - } - } - - componentWillUnmount() { - this.mounted = false; - } - - fetchContent = () => { - // even if `this.props.path` starts with `/`, - // it is important to keep `Docs/` in the string to let webpack correctly resolve imports - import(`Docs/${this.props.path.substr(1)}.md`).then( - ({ default: content }) => { - if (this.mounted) { - this.setState({ content }); - } - }, - () => {} - ); - }; - - render() { - return <DocMarkdownBlock className={this.props.className} content={this.state.content} />; - } -} diff --git a/server/sonar-web/src/main/js/components/docs/DocMarkdownBlock.tsx b/server/sonar-web/src/main/js/components/docs/DocMarkdownBlock.tsx index 0bc5350b297..c61a7212c52 100644 --- a/server/sonar-web/src/main/js/components/docs/DocMarkdownBlock.tsx +++ b/server/sonar-web/src/main/js/components/docs/DocMarkdownBlock.tsx @@ -23,11 +23,9 @@ import remark from 'remark'; import reactRenderer from 'remark-react'; import remarkToc from 'remark-toc'; import DocLink from './DocLink'; -import DocParagraph from './DocParagraph'; import DocImg from './DocImg'; import DocTooltipLink from './DocTooltipLink'; -import { separateFrontMatter } from '../../helpers/markdown'; -import { isSonarCloud } from '../../helpers/system'; +import { separateFrontMatter, filterContent } from '../../helpers/markdown'; import { scrollToElement } from '../../helpers/scrolling'; interface Props { @@ -59,7 +57,6 @@ export default class DocMarkdownBlock extends React.PureComponent<Props> { {displayH1 && <h1>{parsed.frontmatter.title}</h1>} { remark() - // .use(remarkInclude) .use(remarkToc, { maxDepth: 3 }) .use(reactRenderer, { remarkReactComponents: { @@ -69,8 +66,6 @@ export default class DocMarkdownBlock extends React.PureComponent<Props> { a: isTooltip ? withChildProps(DocTooltipLink, childProps) : withChildProps(DocLink, { onAnchorClick: this.handleAnchorClick }), - // used to handle `@include` - p: DocParagraph, // use custom img tag to render documentation images img: DocImg }, @@ -91,19 +86,3 @@ function withChildProps<P>( return <WrappedComponent customProps={childProps} {...props} />; }; } - -function filterContent(content: string) { - const beginning = isSonarCloud() ? '<!-- sonarqube -->' : '<!-- sonarcloud -->'; - const ending = isSonarCloud() ? '<!-- /sonarqube -->' : '<!-- /sonarcloud -->'; - - let newContent = content; - let start = newContent.indexOf(beginning); - let end = newContent.indexOf(ending); - while (start !== -1 && end !== -1) { - newContent = newContent.substring(0, start) + newContent.substring(end + ending.length); - start = newContent.indexOf(beginning); - end = newContent.indexOf(ending); - } - - return newContent; -} diff --git a/server/sonar-web/src/main/js/components/docs/DocParagraph.tsx b/server/sonar-web/src/main/js/components/docs/DocParagraph.tsx deleted file mode 100644 index 54b34660482..00000000000 --- a/server/sonar-web/src/main/js/components/docs/DocParagraph.tsx +++ /dev/null @@ -1,35 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import * as React from 'react'; -import DocInclude from './DocInclude'; - -const INCLUDE = '@include'; - -export default function DocParagraph(props: React.HTMLAttributes<HTMLParagraphElement>) { - if (Array.isArray(props.children) && props.children.length === 1) { - const child = props.children[0]; - if (typeof child === 'string' && child.startsWith(INCLUDE)) { - const includePath = child.substr(INCLUDE.length + 1); - return <DocInclude path={includePath} />; - } - } - - return <p {...props} />; -} diff --git a/server/sonar-web/src/main/js/components/docs/DocTooltip.tsx b/server/sonar-web/src/main/js/components/docs/DocTooltip.tsx index 3f7d4ec0195..a912b43c7b3 100644 --- a/server/sonar-web/src/main/js/components/docs/DocTooltip.tsx +++ b/server/sonar-web/src/main/js/components/docs/DocTooltip.tsx @@ -26,82 +26,55 @@ const DocMarkdownBlock = lazyLoad(() => import('./DocMarkdownBlock')); interface Props { className?: string; children?: React.ReactNode; - /** Key of the documentation chunk */ - doc: string; + // Use as `import(/* webpackMode: "eager" */ 'Docs/tooltips/foo/bar.md')` + doc: Promise<{ default: string }>; overlayProps?: { [k: string]: string }; } interface State { content?: string; - loading: boolean; open: boolean; } export default class DocTooltip extends React.PureComponent<Props, State> { - mounted = false; - state: State = { loading: false, open: false }; + state: State = { open: false }; componentDidMount() { - this.mounted = true; + this.props.doc.then( + ({ default: content }) => { + this.setState({ content }); + }, + () => {} + ); document.addEventListener('scroll', this.close, true); } - componentWillReceiveProps(nextProps: Props) { - if (nextProps.doc !== this.props.doc) { - this.setState({ content: undefined, loading: false, open: false }); - } - } - componentWillUnmount() { - this.mounted = false; document.removeEventListener('scroll', this.close, true); } - fetchContent = () => { - this.setState({ loading: true }); - import(`Docs/tooltips/${this.props.doc}.md`).then( - ({ default: content }) => { - if (this.mounted) { - this.setState({ content, loading: false }); - } - }, - () => { - if (this.mounted) { - this.setState({ loading: false }); - } - } - ); - }; - close = () => { this.setState({ open: false }); }; - renderOverlay() { - return ( - <div className="abs-width-300"> - {this.state.loading ? ( - <i className="spinner" /> - ) : ( - <DocMarkdownBlock - childProps={this.props.overlayProps} - className="cut-margins" - content={this.state.content} - isTooltip={true} - /> - )} - </div> - ); - } - render() { - return ( + return this.state.content ? ( <HelpTooltip className={this.props.className} - onShow={this.fetchContent} - overlay={this.renderOverlay()}> + overlay={ + <div className="abs-width-300"> + <DocMarkdownBlock + childProps={this.props.overlayProps} + className="cut-margins" + content={this.state.content} + isTooltip={true} + /> + </div> + }> {this.props.children} </HelpTooltip> + ) : ( + this.props.children || null ); } } diff --git a/server/sonar-web/src/main/js/components/docs/__tests__/DocTooltip-test.tsx b/server/sonar-web/src/main/js/components/docs/__tests__/DocTooltip-test.tsx index 570b68e37c0..8e933bddbcd 100644 --- a/server/sonar-web/src/main/js/components/docs/__tests__/DocTooltip-test.tsx +++ b/server/sonar-web/src/main/js/components/docs/__tests__/DocTooltip-test.tsx @@ -20,20 +20,11 @@ import * as React from 'react'; import { shallow } from 'enzyme'; import DocTooltip from '../DocTooltip'; +import { waitAndUpdate } from '../../../helpers/testUtils'; -jest.useFakeTimers(); - -it('should render', () => { - const wrapper = shallow(<DocTooltip doc="foo/bar" />); - wrapper.setState({ content: 'this is *bold* text', open: true, loading: true }); +it('should render', async () => { + const wrapper = shallow(<DocTooltip doc={Promise.resolve({ default: 'this is *bold* text' })} />); expect(wrapper).toMatchSnapshot(); - wrapper.setState({ loading: false }); + await waitAndUpdate(wrapper); expect(wrapper).toMatchSnapshot(); }); - -it('should reset state when receiving new doc', () => { - const wrapper = shallow(<DocTooltip doc="foo/bar" />); - wrapper.setState({ content: 'this is *bold* text', open: true }); - wrapper.setProps({ doc: 'baz' }); - expect(wrapper.state()).toEqual({ content: undefined, loading: false, open: false }); -}); diff --git a/server/sonar-web/src/main/js/components/docs/__tests__/__snapshots__/DocMarkdownBlock-test.tsx.snap b/server/sonar-web/src/main/js/components/docs/__tests__/__snapshots__/DocMarkdownBlock-test.tsx.snap index c47b4d7fe4b..c84fe63dedf 100644 --- a/server/sonar-web/src/main/js/components/docs/__tests__/__snapshots__/DocMarkdownBlock-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/docs/__tests__/__snapshots__/DocMarkdownBlock-test.tsx.snap @@ -7,39 +7,39 @@ exports[`should cut sonarqube/sonarcloud content 1`] = ` <React.Fragment key="h-1" > - <DocParagraph + <p key="h-2" > some - </DocParagraph> + </p> - <DocParagraph + <p key="h-3" > sonarqube - </DocParagraph> + </p> - <DocParagraph + <p key="h-4" > long - </DocParagraph> + </p> - <DocParagraph + <p key="h-5" > multiline - </DocParagraph> + </p> - <DocParagraph + <p key="h-6" > text - </DocParagraph> + </p> </React.Fragment> </div> `; @@ -51,25 +51,25 @@ exports[`should cut sonarqube/sonarcloud content 2`] = ` <React.Fragment key="h-1" > - <DocParagraph + <p key="h-2" > some - </DocParagraph> + </p> - <DocParagraph + <p key="h-3" > sonarcloud - </DocParagraph> + </p> - <DocParagraph + <p key="h-4" > text - </DocParagraph> + </p> </React.Fragment> </div> `; @@ -81,7 +81,7 @@ exports[`should render simple markdown 1`] = ` <React.Fragment key="h-1" > - <DocParagraph + <p key="h-2" > this is @@ -91,7 +91,7 @@ exports[`should render simple markdown 1`] = ` bold </em> text - </DocParagraph> + </p> </React.Fragment> </div> `; diff --git a/server/sonar-web/src/main/js/components/docs/__tests__/__snapshots__/DocTooltip-test.tsx.snap b/server/sonar-web/src/main/js/components/docs/__tests__/__snapshots__/DocTooltip-test.tsx.snap index f19fea4d065..2c996886133 100644 --- a/server/sonar-web/src/main/js/components/docs/__tests__/__snapshots__/DocTooltip-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/docs/__tests__/__snapshots__/DocTooltip-test.tsx.snap @@ -1,23 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`should render 1`] = ` -<HelpTooltip - onShow={[Function]} - overlay={ - <div - className="abs-width-300" - > - <i - className="spinner" - /> - </div> - } -/> -`; +exports[`should render 1`] = `""`; exports[`should render 2`] = ` <HelpTooltip - onShow={[Function]} overlay={ <div className="abs-width-300" |