diff options
author | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2019-04-11 15:06:52 +0200 |
---|---|---|
committer | SonarTech <sonartech@sonarsource.com> | 2019-04-12 20:21:04 +0200 |
commit | 0ff98114be62661af4c745d6110e6047a67adbe2 (patch) | |
tree | 13335d3f933c0280c19d75b459dcef84860ceb53 /server | |
parent | 4380a1d1594cb112f3d3e17b99f4a801c3d2fda8 (diff) | |
download | sonarqube-0ff98114be62661af4c745d6110e6047a67adbe2.tar.gz sonarqube-0ff98114be62661af4c745d6110e6047a67adbe2.zip |
SONAR-11956 Correct TOC parsing logic, always show sticky TOC for pages
Diffstat (limited to 'server')
10 files changed, 11 insertions, 329 deletions
diff --git a/server/sonar-web/package.json b/server/sonar-web/package.json index 632f98c71a2..ad0b5780b38 100644 --- a/server/sonar-web/package.json +++ b/server/sonar-web/package.json @@ -43,7 +43,6 @@ "regenerator-runtime": "0.13.2", "remark-custom-blocks": "2.3.0", "remark-slug": "5.1.0", - "remark-toc": "5.0.0", "unist-util-visit": "1.4.0", "valid-url": "1.0.9", "whatwg-fetch": "2.0.4" diff --git a/server/sonar-web/src/main/js/@types/remark-toc.d.ts b/server/sonar-web/src/main/js/@types/remark-toc.d.ts deleted file mode 100644 index a2a0996d2d3..00000000000 --- a/server/sonar-web/src/main/js/@types/remark-toc.d.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 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. - */ -declare module 'remark-toc' { - interface Options { - /** Heading to look for */ - heading?: string; - /** Maximum heading depth, inclusive */ - maxDepth?: number; - /** Display list-item tightly */ - tight?: boolean; - } - - export default function remarkToc(options?: Options): JSX.Element; -} 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 92c34ad59b6..01b5f4c5b78 100644 --- a/server/sonar-web/src/main/js/components/docs/DocMarkdownBlock.tsx +++ b/server/sonar-web/src/main/js/components/docs/DocMarkdownBlock.tsx @@ -27,7 +27,6 @@ import DocLink from './DocLink'; import DocImg from './DocImg'; import DocToc from './DocToc'; import DocTooltipLink from './DocTooltipLink'; -import remarkToc from './plugins/remark-toc'; import DocCollapsibleBlock from './DocCollapsibleBlock'; import { separateFrontMatter, filterContent } from '../../helpers/markdown'; import { scrollToElement } from '../../helpers/scrolling'; @@ -60,8 +59,8 @@ export default class DocMarkdownBlock extends React.PureComponent<Props> { render() { const { childProps, content, className, displayH1, stickyToc, isTooltip } = this.props; const parsed = separateFrontMatter(content || ''); - let filteredContent = filterContent(parsed.content); - const tocContent = filteredContent; + const filteredContent = filterContent(parsed.content); + const md = remark(); // TODO find a way to replace these custom blocks with real Alert components @@ -87,12 +86,6 @@ export default class DocMarkdownBlock extends React.PureComponent<Props> { }) .use(slug); - if (stickyToc) { - filteredContent = filteredContent.replace(/#*\s*(toc|table[ -]of[ -]contents?).*/i, ''); - } else { - md.use(remarkToc, { maxDepth: 3 }); - } - return ( <div className={classNames('markdown', className, { 'has-toc': stickyToc })} @@ -101,7 +94,7 @@ export default class DocMarkdownBlock extends React.PureComponent<Props> { {displayH1 && <h1 className="documentation-title">{parsed.frontmatter.title}</h1>} {md.processSync(filteredContent).contents} </div> - {stickyToc && <DocToc content={tocContent} onAnchorClick={this.handleAnchorClick} />} + {stickyToc && <DocToc content={filteredContent} onAnchorClick={this.handleAnchorClick} />} </div> ); } diff --git a/server/sonar-web/src/main/js/components/docs/DocToc.tsx b/server/sonar-web/src/main/js/components/docs/DocToc.tsx index 6f2f2cac2f3..7ff1d48a12d 100644 --- a/server/sonar-web/src/main/js/components/docs/DocToc.tsx +++ b/server/sonar-web/src/main/js/components/docs/DocToc.tsx @@ -48,11 +48,11 @@ export default class DocToc extends React.PureComponent<Props, State> { state: State = { anchors: [] }; - static getAnchors = memoize(content => { + static getAnchors = memoize((content: string) => { const file: { contents: JSX.Element } = remark() .use(reactRenderer) .use(onlyToc) - .processSync(content); + .processSync('\n## doctoc\n' + content); if (file && file.contents.props.children) { let list = file.contents; diff --git a/server/sonar-web/src/main/js/components/docs/__tests__/DocMarkdownBlock-test.tsx b/server/sonar-web/src/main/js/components/docs/__tests__/DocMarkdownBlock-test.tsx index afacbd5ec0a..3f276e19f52 100644 --- a/server/sonar-web/src/main/js/components/docs/__tests__/DocMarkdownBlock-test.tsx +++ b/server/sonar-web/src/main/js/components/docs/__tests__/DocMarkdownBlock-test.tsx @@ -21,9 +21,7 @@ import * as React from 'react'; import { shallow } from 'enzyme'; import DocMarkdownBlock from '../DocMarkdownBlock'; -const CONTENT_WITH_TOC = ` -## Table of Contents - +const CONTENT = ` ## Lorem ipsum Quisque vitae tincidunt felis. Nam blandit risus placerat, efficitur enim ut, pellentesque sem. Mauris non lorem auctor, consequat neque eget, dignissim augue. @@ -84,14 +82,8 @@ it('should render with custom props for links', () => { ).toMatchSnapshot(); }); -it('should render a TOC if available', () => { - const wrapper = shallowRender({ content: CONTENT_WITH_TOC }); - expect(wrapper).toMatchSnapshot(); - expect(wrapper.find('ul').exists()).toBe(true); -}); - it('should render a sticky TOC if available', () => { - const wrapper = shallowRender({ content: CONTENT_WITH_TOC, stickyToc: true }); + const wrapper = shallowRender({ content: CONTENT, stickyToc: true }); expect(wrapper).toMatchSnapshot(); expect(wrapper.find('DocToc').exists()).toBe(true); }); diff --git a/server/sonar-web/src/main/js/components/docs/__tests__/DocToc-test.tsx b/server/sonar-web/src/main/js/components/docs/__tests__/DocToc-test.tsx index ce8f39319f2..39958cdfcca 100644 --- a/server/sonar-web/src/main/js/components/docs/__tests__/DocToc-test.tsx +++ b/server/sonar-web/src/main/js/components/docs/__tests__/DocToc-test.tsx @@ -24,7 +24,7 @@ import { click, scrollTo } from '../../../helpers/testUtils'; const OFFSET = 300; -const CONTENT_NO_TOC = ` +const CONTENT = ` ## Lorem ipsum Quisque vitae tincidunt felis. Nam blandit risus placerat, efficitur enim ut, pellentesque sem. Mauris non lorem auctor, consequat neque eget, dignissim augue. @@ -44,12 +44,6 @@ At cursus turpis. Aenean at elit fringilla, porttitor mi eget, dapibus nisi. Don Risus placerat, efficitur enim ut, pellentesque sem. Mauris non lorem auctor, consequat neque eget, dignissim augue. `; -const CONTENT_WITH_TOC = ` -## toc - -${CONTENT_NO_TOC} -`; - jest.mock('remark', () => { const remark = require.requireActual('remark'); return { default: remark }; @@ -75,11 +69,6 @@ it('should render correctly', () => { expect(wrapper).toMatchSnapshot(); }); -it('should render correctly if no TOC is available', () => { - const wrapper = renderComponent({ content: CONTENT_NO_TOC }); - expect(wrapper).toMatchSnapshot(); -}); - it('should trigger the handler when an anchor is clicked', () => { const onAnchorClick = jest.fn(); const wrapper = renderComponent({ onAnchorClick }); @@ -99,7 +88,7 @@ it('should highlight anchors when scrolling', () => { }); function renderComponent(props: Partial<DocToc['props']> = {}) { - return mount(<DocToc content={CONTENT_WITH_TOC} onAnchorClick={jest.fn()} {...props} />); + return mount(<DocToc content={CONTENT} onAnchorClick={jest.fn()} {...props} />); } function mockDomEnv() { @@ -109,11 +98,7 @@ function mockDomEnv() { parent.appendChild(element); let offset = OFFSET; - (CONTENT_WITH_TOC.match(/^## .+$/gm) as Array<string>).forEach(match => { - if (/toc/.test(match)) { - return; - } - + (CONTENT.match(/^## .+$/gm) as Array<string>).forEach(match => { const slug = match .replace(/^#+ */, '') .replace(' ', '-') 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 ae537c7f712..6e5b67907c5 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 @@ -1,178 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`should render a TOC if available 1`] = ` -<div - className="markdown" -> - <div - className="markdown-content" - > - <Block - key="h-1" - > - <h2 - id="table-of-contents" - key="h-2" - > - Table of Contents - </h2> - - - <ul - key="h-3" - > - - - <li - key="h-4" - > - <withChildProps - href="#lorem-ipsum" - key="h-5" - > - Lorem ipsum - </withChildProps> - </li> - - - <li - key="h-6" - > - - - <p - key="h-7" - > - <withChildProps - href="#sit-amet" - key="h-8" - > - Sit amet - </withChildProps> - </p> - - - <ul - key="h-9" - > - - - <li - key="h-10" - > - <withChildProps - href="#maecenas-diam" - key="h-11" - > - Maecenas diam - </withChildProps> - </li> - - - <li - key="h-12" - > - <withChildProps - href="#integer" - key="h-13" - > - Integer - </withChildProps> - </li> - - - </ul> - - - </li> - - - <li - key="h-14" - > - <withChildProps - href="#nam-blandit" - key="h-15" - > - Nam blandit - </withChildProps> - </li> - - - </ul> - - - <h2 - id="lorem-ipsum" - key="h-16" - > - Lorem ipsum - </h2> - - - <p - key="h-17" - > - Quisque vitae tincidunt felis. Nam blandit risus placerat, efficitur enim ut, pellentesque sem. Mauris non lorem auctor, consequat neque eget, dignissim augue. - </p> - - - <h2 - id="sit-amet" - key="h-18" - > - Sit amet - </h2> - - - <h3 - id="maecenas-diam" - key="h-19" - > - Maecenas diam - </h3> - - - <p - key="h-20" - > - Velit, vestibulum nec ultrices id, mollis eget arcu. Sed dapibus, sapien ut auctor consectetur, mi tortor vestibulum ante, eget dapibus lacus risus. - </p> - - - <h3 - id="integer" - key="h-21" - > - Integer - </h3> - - - <p - key="h-22" - > - At cursus turpis. Aenean at elit fringilla, porttitor mi eget, dapibus nisi. Donec quis congue odio. - </p> - - - <h2 - id="nam-blandit" - key="h-23" - > - Nam blandit - </h2> - - - <p - key="h-24" - > - Risus placerat, efficitur enim ut, pellentesque sem. Mauris non lorem auctor, consequat neque eget, dignissim augue. - </p> - </Block> - </div> -</div> -`; - exports[`should render a sticky TOC if available 1`] = ` <div className="markdown has-toc" @@ -253,8 +80,6 @@ exports[`should render a sticky TOC if available 1`] = ` </div> <DocToc content=" -## Table of Contents - ## Lorem ipsum Quisque vitae tincidunt felis. Nam blandit risus placerat, efficitur enim ut, pellentesque sem. Mauris non lorem auctor, consequat neque eget, dignissim augue. diff --git a/server/sonar-web/src/main/js/components/docs/__tests__/__snapshots__/DocToc-test.tsx.snap b/server/sonar-web/src/main/js/components/docs/__tests__/__snapshots__/DocToc-test.tsx.snap index c89f2c0a0d3..ea33e896b40 100644 --- a/server/sonar-web/src/main/js/components/docs/__tests__/__snapshots__/DocToc-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/docs/__tests__/__snapshots__/DocToc-test.tsx.snap @@ -3,9 +3,6 @@ exports[`should render correctly 1`] = ` <DocToc content=" -## toc - - ## Lorem ipsum Quisque vitae tincidunt felis. Nam blandit risus placerat, efficitur enim ut, pellentesque sem. Mauris non lorem auctor, consequat neque eget, dignissim augue. @@ -23,7 +20,6 @@ At cursus turpis. Aenean at elit fringilla, porttitor mi eget, dapibus nisi. Don ## Nam blandit Risus placerat, efficitur enim ut, pellentesque sem. Mauris non lorem auctor, consequat neque eget, dignissim augue. - " onAnchorClick={[MockFunction]} > @@ -64,28 +60,3 @@ Risus placerat, efficitur enim ut, pellentesque sem. Mauris non lorem auctor, co </div> </DocToc> `; - -exports[`should render correctly if no TOC is available 1`] = ` -<DocToc - content=" -## Lorem ipsum - -Quisque vitae tincidunt felis. Nam blandit risus placerat, efficitur enim ut, pellentesque sem. Mauris non lorem auctor, consequat neque eget, dignissim augue. - -## Sit amet - -### Maecenas diam - -Velit, vestibulum nec ultrices id, mollis eget arcu. Sed dapibus, sapien ut auctor consectetur, mi tortor vestibulum ante, eget dapibus lacus risus. - -### Integer - -At cursus turpis. Aenean at elit fringilla, porttitor mi eget, dapibus nisi. Donec quis congue odio. - -## Nam blandit - -Risus placerat, efficitur enim ut, pellentesque sem. Mauris non lorem auctor, consequat neque eget, dignissim augue. -" - onAnchorClick={[MockFunction]} -/> -`; diff --git a/server/sonar-web/src/main/js/components/docs/plugins/remark-only-toc.js b/server/sonar-web/src/main/js/components/docs/plugins/remark-only-toc.js index 3489d2d6273..521804fcec3 100644 --- a/server/sonar-web/src/main/js/components/docs/plugins/remark-only-toc.js +++ b/server/sonar-web/src/main/js/components/docs/plugins/remark-only-toc.js @@ -30,7 +30,7 @@ export default function onlyToc() { return transformer; function transformer(node) { - const result = util(node, { heading: 'toc|table[ -]of[ -]contents?', maxDepth: 2 }); + const result = util(node, { heading: 'doctoc', maxDepth: 2 }); if (result.index === null || result.index === -1 || !result.map) { node.children = []; diff --git a/server/sonar-web/src/main/js/components/docs/plugins/remark-toc.js b/server/sonar-web/src/main/js/components/docs/plugins/remark-toc.js deleted file mode 100644 index 0182f769231..00000000000 --- a/server/sonar-web/src/main/js/components/docs/plugins/remark-toc.js +++ /dev/null @@ -1,52 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 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 slug from 'remark-slug'; -import util from 'mdast-util-toc'; - -const DEFAULT_HEADING = 'toc|table[ -]of[ -]contents?'; - -/** - * This comes from the remark-toc plugin: https://github.com/remarkjs/remark-toc - * We cannot use it directly before the following issue gets fixed: https://github.com/remarkjs/remark-toc/issues/18 - */ -export default function toc(options) { - const settings = options || {}; - const heading = settings.heading || DEFAULT_HEADING; - const depth = settings.maxDepth || 6; - const { tight } = settings; - - this.use(slug); - - return transformer; - - function transformer(node) { - const result = util(node, { heading, maxDepth: depth, tight }); - - if (result.index === null || result.index === -1 || !result.map) { - return; - } - - node.children = [].concat( - node.children.slice(0, result.index), - result.map, - node.children.slice(result.index) - ); - } -} |