]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-11017 Improve ToC (#529)
authorPascal Mugnier <pascal.mugnier@sonarsource.com>
Fri, 20 Jul 2018 15:24:48 +0000 (17:24 +0200)
committerSonarTech <sonartech@sonarsource.com>
Wed, 25 Jul 2018 18:21:21 +0000 (20:21 +0200)
server/sonar-docs/src/templates/page.css
server/sonar-docs/src/templates/page.js
server/sonar-web/package.json
server/sonar-web/src/main/js/apps/documentation/styles.css
server/sonar-web/src/main/js/components/docs/DocMarkdownBlock.tsx
server/sonar-web/src/main/js/components/docs/plugins/remark-toc.js [new file with mode: 0644]
server/sonar-web/yarn.lock

index 1e6c4dd8de57fa526fcab9620628c0fffeebfcec..73515b1460dd3deb5400f4a70f6149e8d7fb0ac1 100644 (file)
   color: #3c763d;
 }
 
+ul > ul {
+  margin-bottom: 0 !important;
+}
+
 .collapse {
   border: 1px solid #e6e6e6;
   border-radius: 2px;
 .collapse .alert {
   margin: 0 0.5em 1.5rem;
 }
+
+img[src='/images/exclamation.svg'],
+img[src='/images/check.svg'],
+img[src='/images/cross.svg'],
+img[src='/images/info.svg'] {
+  margin-bottom: 0;
+  top: 0 !important;
+}
index 2c04417fe558da891a2b47723957d78d30f87100..d7f9859a9459741b77cd259062de4551ab353233 100644 (file)
@@ -54,6 +54,8 @@ export default class Page extends React.PureComponent {
       htmlWithInclusions = generateTableOfContents(htmlWithInclusions, page.headings);
     }
 
+    htmlWithInclusions = replaceDynamicLinks(htmlWithInclusions);
+
     return (
       <div css={{ paddingTop: 24, paddingBottom: 24 }}>
         <Helmet title={page.frontmatter.title} />
@@ -98,10 +100,24 @@ export const query = graphql`
   }
 `;
 
+function replaceDynamicLinks(content) {
+  return content.replace(
+    /\<a href="\/#(?:sonarqube|sonarcloud|sonarqube-admin)#.*"\>(.*)\<\/a\>/gim,
+    '$1'
+  );
+}
+
 function generateTableOfContents(content, headings) {
   let html = '<h2>Table Of Contents</h2>';
   let depth = headings[0].depth - 1;
   for (let i = 1; i < headings.length; i++) {
+    // Do not include title from collapsible content
+    if (
+      content.match(new RegExp(`\<div class="collapse"\>\<h2\>${headings[i].value}\<\/h2\>`, 'gi'))
+    ) {
+      continue;
+    }
+
     while (headings[i].depth > depth) {
       html += '<ul>';
       depth++;
@@ -116,7 +132,8 @@ function generateTableOfContents(content, headings) {
       `<h${headings[i].depth} id="header-${i}">${headings[i].value}</h${headings[i].depth}>`
     );
   }
-  return content.replace(/<h[1-9]>Table Of Contents<\/h[1-9]>/, html);
+  html += '</ul>';
+  return content.replace(/<h[1-9]>Table Of Contents<\/h[1-9]>/i, html);
 }
 
 function cutSonarCloudContent(content) {
index 5fdb34c101754c2dc748f4f5d91efd2bdb292222..a65e68ff7f985744cf2c09adec8a23b928634352 100644 (file)
@@ -22,6 +22,7 @@
     "keymaster": "1.6.2",
     "lodash": "4.17.10",
     "lunr": "2.3.0",
+    "mdast-util-toc": "2.0.1",
     "prop-types": "15.6.1",
     "react": "16.2.0",
     "react-day-picker": "7.1.8",
@@ -41,6 +42,7 @@
     "remark-custom-blocks": "2.2.0",
     "remark-toc": "5.0.0",
     "unist-util-visit": "1.3.1",
+    "remark-slug": "5.0.0",
     "whatwg-fetch": "2.0.4"
   },
   "devDependencies": {
   ],
   "jest": {
     "coverageDirectory": "<rootDir>/target/coverage",
-    "coveragePathIgnorePatterns": ["<rootDir>/node_modules", "<rootDir>/tests"],
-    "moduleFileExtensions": ["ts", "tsx", "js", "json"],
+    "coveragePathIgnorePatterns": [
+      "<rootDir>/node_modules",
+      "<rootDir>/tests"
+    ],
+    "moduleFileExtensions": [
+      "ts",
+      "tsx",
+      "js",
+      "json"
+    ],
     "moduleNameMapper": {
-      "^.+\\.(md|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$":
-        "<rootDir>/config/jest/FileStub.js",
+      "^.+\\.(md|jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/config/jest/FileStub.js",
       "^.+\\.css$": "<rootDir>/config/jest/CSSStub.js"
     },
     "setupFiles": [
       "<rootDir>/config/jest/SetupTestEnvironment.js",
       "<rootDir>/config/jest/SetupEnzyme.js"
     ],
-    "snapshotSerializers": ["enzyme-to-json/serializer"],
+    "snapshotSerializers": [
+      "enzyme-to-json/serializer"
+    ],
     "testPathIgnorePatterns": [
       "<rootDir>/node_modules",
       "<rootDir>/src/main/webapp",
   "lint-staged": {
     "gitDir": "../../",
     "linters": {
-      "*.{css,json}": ["prettier --write", "git add"],
+      "*.{css,json}": [
+        "prettier --write",
+        "git add"
+      ],
       "*.{js,ts,tsx}": [
         "eslint",
         "jest --bail --passWithNoTests --findRelatedTests",
index 5eb00833df48866d919a36c8c6c3cdadb563f1dc..08c38b1b40bd16e716fce056b1f42bae571d34b6 100644 (file)
   margin: 0.8em 0 2em;
 }
 
+.documentation-content.markdown ul {
+  margin: 0 0 2em;
+}
+
+.documentation-content.markdown ul > ul {
+  margin: 0;
+}
+
 .documentation-content.markdown p + ul,
 .documentation-content.markdown p + ol,
 .documentation-content.markdown p + pre {
index bb033ac299943ce153544fdd7059bdab25a137d2..1480de4352c23f3fa7a891f5e2bc7a426fe12e91 100644 (file)
@@ -21,11 +21,11 @@ import * as React from 'react';
 import * as classNames from 'classnames';
 import remark from 'remark';
 import reactRenderer from 'remark-react';
-import remarkToc from 'remark-toc';
 import remarkCustomBlocks from 'remark-custom-blocks';
 import DocLink from './DocLink';
 import DocImg from './DocImg';
 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';
@@ -43,7 +43,7 @@ export default class DocMarkdownBlock extends React.PureComponent<Props> {
 
   handleAnchorClick = (href: string, event: React.MouseEvent<HTMLAnchorElement>) => {
     if (this.node) {
-      const element = this.node.querySelector(`#user-content-${href.substr(1)}`);
+      const element = this.node.querySelector(`#${href.substr(1)}`);
       if (element) {
         event.preventDefault();
         scrollToElement(element, { bottomOffset: window.innerHeight - 80 });
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
new file mode 100644 (file)
index 0000000..84923f4
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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 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)
+    );
+  }
+}
index 5046b41e5cbf5463414ddf56acb47c52f3e9e96f..5288fdc5438474324c7d44edea774d03323f979f 100644 (file)
@@ -5603,7 +5603,7 @@ mdast-util-to-string@^1.0.0, mdast-util-to-string@^1.0.2:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-1.0.4.tgz#5c455c878c9355f0c1e7f3e8b719cf583691acfb"
 
-mdast-util-toc@^2.0.0:
+mdast-util-toc@2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/mdast-util-toc/-/mdast-util-toc-2.0.1.tgz#b1d2cb23bfb01f812fa7b55bffe8b0a8bedf6f21"
   dependencies:
@@ -7396,7 +7396,7 @@ remark-react@4.0.3:
     hast-util-sanitize "^1.0.0"
     mdast-util-to-hast "^3.0.0"
 
-remark-slug@^5.0.0:
+remark-slug@5.0.0:
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/remark-slug/-/remark-slug-5.0.0.tgz#9de71fcdc2bfae33ebb4a41eb83035288a829980"
   dependencies:
@@ -7423,13 +7423,6 @@ remark-stringify@^5.0.0:
     unherit "^1.0.4"
     xtend "^4.0.1"
 
-remark-toc@5.0.0:
-  version "5.0.0"
-  resolved "https://registry.yarnpkg.com/remark-toc/-/remark-toc-5.0.0.tgz#f1e13ed11062ad4d102b02e70168bd85015bf129"
-  dependencies:
-    mdast-util-toc "^2.0.0"
-    remark-slug "^5.0.0"
-
 remark@9.0.0:
   version "9.0.0"
   resolved "https://registry.yarnpkg.com/remark/-/remark-9.0.0.tgz#c5cfa8ec535c73a67c4b0f12bfdbd3a67d8b2f60"