]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10611 Display inline documentation tooltips (#180)
authorStas Vilchik <stas.vilchik@sonarsource.com>
Wed, 25 Apr 2018 11:54:16 +0000 (13:54 +0200)
committerSonarTech <sonartech@sonarsource.com>
Thu, 3 May 2018 18:20:50 +0000 (20:20 +0200)
67 files changed:
server/sonar-docs/src/tooltips/quality-gates/built-in-quality-gate.md [new file with mode: 0644]
server/sonar-docs/src/tooltips/quality-gates/default-quality-gate.md [new file with mode: 0644]
server/sonar-docs/src/tooltips/quality-gates/project-homepage-quality-gate.md [new file with mode: 0644]
server/sonar-docs/src/tooltips/quality-gates/quality-gate-conditions.md [new file with mode: 0644]
server/sonar-docs/src/tooltips/quality-gates/quality-gate-projects.md [new file with mode: 0644]
server/sonar-docs/src/tooltips/quality-gates/quality-gate.md [new file with mode: 0644]
server/sonar-docs/src/tooltips/quality-profiles/built-in-quality-profile.md [new file with mode: 0644]
server/sonar-docs/src/tooltips/quality-profiles/default-quality-profile.md [new file with mode: 0644]
server/sonar-docs/src/tooltips/quality-profiles/quality-profile-projects.md [new file with mode: 0644]
server/sonar-docs/src/tooltips/rules/custom-rule-removal.md [new file with mode: 0644]
server/sonar-docs/src/tooltips/rules/custom-rules.md [new file with mode: 0644]
server/sonar-docs/src/tooltips/rules/rule-templates.md [new file with mode: 0644]
server/sonar-docs/src/tooltips/rules/rules-quality-profiles.md [new file with mode: 0644]
server/sonar-web/config/webpack.config.js
server/sonar-web/package.json
server/sonar-web/src/main/js/@types/remark-react.d.ts [new file with mode: 0644]
server/sonar-web/src/main/js/@types/remark.d.ts [new file with mode: 0644]
server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.css
server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBranch.tsx
server/sonar-web/src/main/js/app/components/nav/component/NoBranchSupportPopup.tsx
server/sonar-web/src/main/js/app/components/nav/component/SingleBranchHelperPopup.tsx
server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranch-test.tsx
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranch-test.tsx.snap
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/NoBranchSupportPopup-test.tsx.snap
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/SingleBranchHelperPopup-test.tsx.snap
server/sonar-web/src/main/js/app/styles/components/badges.css
server/sonar-web/src/main/js/app/styles/components/search-navigator.css
server/sonar-web/src/main/js/app/styles/components/tooltips.css [deleted file]
server/sonar-web/src/main/js/app/styles/init/links.css
server/sonar-web/src/main/js/app/styles/init/tables.css
server/sonar-web/src/main/js/app/styles/style.css
server/sonar-web/src/main/js/apps/coding-rules/components/Facet.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/ProfileFacet.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetails.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsMeta.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/TemplateFacet.tsx
server/sonar-web/src/main/js/apps/overview/qualityGate/QualityGate.js
server/sonar-web/src/main/js/apps/overview/qualityGate/__tests__/__snapshots__/QualityGate-test.js.snap
server/sonar-web/src/main/js/apps/overview/styles.css
server/sonar-web/src/main/js/apps/projectQualityGate/Header.tsx
server/sonar-web/src/main/js/apps/projectQualityGate/__tests__/__snapshots__/Header-test.tsx.snap
server/sonar-web/src/main/js/apps/projectQualityProfiles/Header.tsx
server/sonar-web/src/main/js/apps/projectQualityProfiles/__tests__/__snapshots__/Header-test.tsx.snap
server/sonar-web/src/main/js/apps/quality-gates/components/Conditions.js
server/sonar-web/src/main/js/apps/quality-gates/components/DetailsContent.js
server/sonar-web/src/main/js/apps/quality-gates/components/DetailsHeader.tsx
server/sonar-web/src/main/js/apps/quality-gates/components/ListHeader.tsx
server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileLink.tsx
server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesList.tsx
server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.tsx
server/sonar-web/src/main/js/components/common/BubblePopup.tsx
server/sonar-web/src/main/js/components/controls/Popup.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/controls/Tooltip.css
server/sonar-web/src/main/js/components/controls/Tooltip.tsx
server/sonar-web/src/main/js/components/docs/DocLink.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/docs/DocMarkdownBlock.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/docs/DocTooltip.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/docs/__tests__/DocLink-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/docs/__tests__/DocMarkdownBlock-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/docs/__tests__/DocTooltip-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/docs/__tests__/__snapshots__/DocLink-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/docs/__tests__/__snapshots__/DocMarkdownBlock-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/docs/__tests__/__snapshots__/DocTooltip-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/facet/FacetHeader.tsx
server/sonar-web/src/main/js/components/icons-components/PlusCircleIcon.tsx [new file with mode: 0644]
server/sonar-web/yarn.lock
sonar-core/src/main/resources/org/sonar/l10n/core.properties

diff --git a/server/sonar-docs/src/tooltips/quality-gates/built-in-quality-gate.md b/server/sonar-docs/src/tooltips/quality-gates/built-in-quality-gate.md
new file mode 100644 (file)
index 0000000..7d5470b
--- /dev/null
@@ -0,0 +1 @@
+Built-in, immutable Quality Gate reflecting best practices.
diff --git a/server/sonar-docs/src/tooltips/quality-gates/default-quality-gate.md b/server/sonar-docs/src/tooltips/quality-gates/default-quality-gate.md
new file mode 100644 (file)
index 0000000..2cf1296
--- /dev/null
@@ -0,0 +1 @@
+The Default gate is applied to all projects not explicitly assigned to a gate. Quality Profile and Gate administrators can assign projects to a gate from the Quality Profile page. Project administrators can also choose a non-default gate.
diff --git a/server/sonar-docs/src/tooltips/quality-gates/project-homepage-quality-gate.md b/server/sonar-docs/src/tooltips/quality-gates/project-homepage-quality-gate.md
new file mode 100644 (file)
index 0000000..115b34f
--- /dev/null
@@ -0,0 +1 @@
+A Quality Gate is a set of measure-based Boolean conditions. It helps you know immediately whether your project is production-ready. If your current status is not Passed, you'll see which values caused the problem and the value required to pass.
diff --git a/server/sonar-docs/src/tooltips/quality-gates/quality-gate-conditions.md b/server/sonar-docs/src/tooltips/quality-gates/quality-gate-conditions.md
new file mode 100644 (file)
index 0000000..e961eeb
--- /dev/null
@@ -0,0 +1,5 @@
+For each Quality Gate condition you must choose the metric to be tested, the threshold at which to raise a warning or error, and whether or not to apply the condition to all code or only to Leak Period code (irrelevant for conditions "on New Code").
+
+---
+
+See also: [Fixing the Water Leak](/fixing-the-water-leak)
diff --git a/server/sonar-docs/src/tooltips/quality-gates/quality-gate-projects.md b/server/sonar-docs/src/tooltips/quality-gates/quality-gate-projects.md
new file mode 100644 (file)
index 0000000..2cf1296
--- /dev/null
@@ -0,0 +1 @@
+The Default gate is applied to all projects not explicitly assigned to a gate. Quality Profile and Gate administrators can assign projects to a gate from the Quality Profile page. Project administrators can also choose a non-default gate.
diff --git a/server/sonar-docs/src/tooltips/quality-gates/quality-gate.md b/server/sonar-docs/src/tooltips/quality-gates/quality-gate.md
new file mode 100644 (file)
index 0000000..0762cbc
--- /dev/null
@@ -0,0 +1 @@
+A Quality Gate is a set of measure-based, Boolean conditions. It helps you know immediately whether your projects are production-ready. Ideally, all projects will use the same quality gate. Each project's Quality Gate status is displayed prominently on its homepage.
diff --git a/server/sonar-docs/src/tooltips/quality-profiles/built-in-quality-profile.md b/server/sonar-docs/src/tooltips/quality-profiles/built-in-quality-profile.md
new file mode 100644 (file)
index 0000000..5c0ddbf
--- /dev/null
@@ -0,0 +1 @@
+Built-in profiles are provided directly by the language analyzers and may be updated with each new analyzer version. They represent best-practice, minimum rule sets.
diff --git a/server/sonar-docs/src/tooltips/quality-profiles/default-quality-profile.md b/server/sonar-docs/src/tooltips/quality-profiles/default-quality-profile.md
new file mode 100644 (file)
index 0000000..310164c
--- /dev/null
@@ -0,0 +1 @@
+For each language there is a default profile. All projects not explicitly assigned to some other profile will be analyzed with the default.
diff --git a/server/sonar-docs/src/tooltips/quality-profiles/quality-profile-projects.md b/server/sonar-docs/src/tooltips/quality-profiles/quality-profile-projects.md
new file mode 100644 (file)
index 0000000..0c96fe0
--- /dev/null
@@ -0,0 +1 @@
+Projects assigned to a profile will always be analyzed with it for that language, regardless of which profile is the default. Quality Profile administrators may assign projects to a profile. Project administrators may also choose a non-default profile for each language.
diff --git a/server/sonar-docs/src/tooltips/rules/custom-rule-removal.md b/server/sonar-docs/src/tooltips/rules/custom-rule-removal.md
new file mode 100644 (file)
index 0000000..c8d9be8
--- /dev/null
@@ -0,0 +1 @@
+Only custom rules may be deleted. When a custom rule is deleted, it is not removed from the SonarQube instance. Instead its status is set to "REMOVED", allowing relevant issues to continue to be displayed properly.
diff --git a/server/sonar-docs/src/tooltips/rules/custom-rules.md b/server/sonar-docs/src/tooltips/rules/custom-rules.md
new file mode 100644 (file)
index 0000000..37981a7
--- /dev/null
@@ -0,0 +1 @@
+Custom rules are created by administrators from templates, and are the only fully-editable rules.
diff --git a/server/sonar-docs/src/tooltips/rules/rule-templates.md b/server/sonar-docs/src/tooltips/rules/rule-templates.md
new file mode 100644 (file)
index 0000000..582e4cf
--- /dev/null
@@ -0,0 +1 @@
+Rule Templates allow users to easily define their own rules. They are like cookie cutters from which you can stamp out new, "custom rules". The rules created from a template are listed on its rule detail page.
diff --git a/server/sonar-docs/src/tooltips/rules/rules-quality-profiles.md b/server/sonar-docs/src/tooltips/rules/rules-quality-profiles.md
new file mode 100644 (file)
index 0000000..809df1d
--- /dev/null
@@ -0,0 +1,5 @@
+Quality Profiles are collections of Rules to apply during an analysis.
+
+---
+
+See also: [Quality Profiles](/quality-profiles)
index cbad122a5b53d234013ea15a97dc971fd3068b2e..cc2bfe7e970c9e0e9cdea28b71cca5299501aeda 100644 (file)
@@ -33,7 +33,11 @@ module.exports = ({ production = true }) => ({
   devtool: production ? 'source-map' : 'cheap-module-source-map',
   resolve: {
     // Add '.ts' and '.tsx' as resolvable extensions.
-    extensions: ['.ts', '.tsx', '.js', '.json']
+    extensions: ['.ts', '.tsx', '.js', '.json'],
+    // import from 'Docs/foo.md' is rewritten to import from 'sonar-docs/src/foo.md'
+    alias: {
+      Docs: path.resolve(__dirname, '../../sonar-docs/src/tooltips')
+    }
   },
   entry: [
     !production && require.resolve('react-dev-utils/webpackHotDevClient'),
@@ -69,6 +73,10 @@ module.exports = ({ production = true }) => ({
           utils.postcssLoader()
         ].filter(Boolean)
       },
+      {
+        test: /\.md$/,
+        use: 'raw-loader'
+      },
       { test: require.resolve('lodash'), loader: 'expose-loader?_' },
       { test: require.resolve('react'), loader: 'expose-loader?React' },
       { test: require.resolve('react-dom'), loader: 'expose-loader?ReactDOM' }
index 11e65a186d74e5e5aafdacb401f5f0d60b85c032..a8b3f5225695db44986273f382fc2867f241c15c 100644 (file)
     "postcss-custom-properties": "6.2.0",
     "postcss-loader": "2.1.1",
     "prettier": "1.11.1",
+    "raw-loader": "0.5.1",
     "react-dev-utils": "5.0.0",
     "react-error-overlay": "1.0.7",
     "react-test-renderer": "16.2.0",
+    "remark": "9.0.0",
+    "remark-react": "4.0.1",
     "style-loader": "0.20.3",
     "ts-jest": "22.0.1",
     "ts-loader": "4.1.0",
diff --git a/server/sonar-web/src/main/js/@types/remark-react.d.ts b/server/sonar-web/src/main/js/@types/remark-react.d.ts
new file mode 100644 (file)
index 0000000..0e94411
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+declare module 'remark-react' {
+  interface Options {
+    /** `h()` */
+    createElement?: (...args: any[]) => JSX.Element;
+    /** Key prefix. */
+    prefix?: string;
+    /** Components. */
+    remarkReactComponents?: any;
+    /** Sanitation schema. */
+    sanitize?: any;
+  }
+
+  export default function remarkReact(options?: Options): JSX.Element;
+}
diff --git a/server/sonar-web/src/main/js/@types/remark.d.ts b/server/sonar-web/src/main/js/@types/remark.d.ts
new file mode 100644 (file)
index 0000000..60cc5c5
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+declare module 'remark' {
+  export default function remark(): any;
+}
index 2362ba94a4eac6a56d8d17712be4d0edf6a76908..8be68d6447c76b80be41f0c7eca0045b1bcca9d6 100644 (file)
@@ -21,6 +21,7 @@
 .navbar-context-branches {
   display: inline-flex;
   justify-content: center;
+  align-items: center;
   flex-shrink: 1 !important;
   min-width: 0;
   line-height: calc(2 * var(--gridSize));
index a369fd1412a3fef829619ba7e5931b895e53819b..5569d2a7844433065d1a19f027a3d1376ba28241 100644 (file)
@@ -34,8 +34,8 @@ import {
   isPullRequest
 } from '../../../../helpers/branches';
 import { translate } from '../../../../helpers/l10n';
-import HelpIcon from '../../../../components/icons-components/HelpIcon';
-import BubblePopupHelper from '../../../../components/common/BubblePopupHelper';
+import PlusCircleIcon from '../../../../components/icons-components/PlusCircleIcon';
+import Popup from '../../../../components/controls/Popup';
 import Tooltip from '../../../../components/controls/Tooltip';
 
 interface Props {
@@ -47,8 +47,6 @@ interface Props {
 
 interface State {
   dropdownOpen: boolean;
-  noBranchSupportPopupOpen: boolean;
-  singleBranchPopupOpen: boolean;
 }
 
 export default class ComponentNavBranch extends React.PureComponent<Props, State> {
@@ -59,14 +57,9 @@ export default class ComponentNavBranch extends React.PureComponent<Props, State
     onSonarCloud: PropTypes.bool
   };
 
-  constructor(props: Props) {
-    super(props);
-    this.state = {
-      dropdownOpen: false,
-      noBranchSupportPopupOpen: false,
-      singleBranchPopupOpen: false
-    };
-  }
+  state: State = {
+    dropdownOpen: false
+  };
 
   componentDidMount() {
     this.mounted = true;
@@ -78,7 +71,7 @@ export default class ComponentNavBranch extends React.PureComponent<Props, State
       !isSameBranchLike(nextProps.currentBranchLike, this.props.currentBranchLike) ||
       nextProps.location !== this.props.location
     ) {
-      this.setState({ dropdownOpen: false, singleBranchPopupOpen: false });
+      this.setState({ dropdownOpen: false });
     }
   }
 
@@ -99,34 +92,6 @@ export default class ComponentNavBranch extends React.PureComponent<Props, State
     }
   };
 
-  toggleSingleBranchPopup = (show?: boolean) => {
-    if (show !== undefined) {
-      this.setState({ singleBranchPopupOpen: show });
-    } else {
-      this.setState(state => ({ singleBranchPopupOpen: !state.singleBranchPopupOpen }));
-    }
-  };
-
-  toggleNoBranchSupportPopup = (show?: boolean) => {
-    if (show !== undefined) {
-      this.setState({ noBranchSupportPopupOpen: show });
-    } else {
-      this.setState(state => ({ noBranchSupportPopupOpen: !state.noBranchSupportPopupOpen }));
-    }
-  };
-
-  handleSingleBranchClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
-    event.preventDefault();
-    event.stopPropagation();
-    this.toggleSingleBranchPopup();
-  };
-
-  handleNoBranchSupportClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
-    event.preventDefault();
-    event.stopPropagation();
-    this.toggleNoBranchSupportPopup();
-  };
-
   renderDropdown = () => {
     const { configuration } = this.props.component;
     return this.state.dropdownOpen ? (
@@ -174,31 +139,23 @@ export default class ComponentNavBranch extends React.PureComponent<Props, State
   };
 
   renderSingleBranchPopup = () => (
-    <div className="display-inline-block spacer-left">
-      <a className="link-no-underline" href="#" onClick={this.handleSingleBranchClick}>
-        <HelpIcon fill={theme.blue} />
-      </a>
-      <BubblePopupHelper
-        isOpen={this.state.singleBranchPopupOpen}
-        popup={<SingleBranchHelperPopup />}
-        position="bottomleft"
-        togglePopup={this.toggleSingleBranchPopup}
-      />
-    </div>
+    <Popup overlay={<SingleBranchHelperPopup />}>
+      {({ onClick }) => (
+        <a className="display-flex-center spacer-left link-no-underline" href="#" onClick={onClick}>
+          <PlusCircleIcon fill={theme.blue} size={12} />
+        </a>
+      )}
+    </Popup>
   );
 
   renderNoBranchSupportPopup = () => (
-    <div className="display-inline-block spacer-left">
-      <a className="link-no-underline" href="#" onClick={this.handleNoBranchSupportClick}>
-        <HelpIcon fill={theme.gray80} />
-      </a>
-      <BubblePopupHelper
-        isOpen={this.state.noBranchSupportPopupOpen}
-        popup={<NoBranchSupportPopup />}
-        position="bottomleft"
-        togglePopup={this.toggleNoBranchSupportPopup}
-      />
-    </div>
+    <Popup overlay={<NoBranchSupportPopup />}>
+      {({ onClick }) => (
+        <a className="display-flex-center spacer-left link-no-underline" href="#" onClick={onClick}>
+          <PlusCircleIcon fill={theme.gray80} size={12} />
+        </a>
+      )}
+    </Popup>
   );
 
   render() {
index d6a15f81671790c61b67819569b7f02693d4c06d..5493c37d39ceade3d4491bea4b29aefcbe8e74cc 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import BubblePopup from '../../../../components/common/BubblePopup';
 import { translate } from '../../../../helpers/l10n';
 
-interface Props {
-  popupPosition?: any;
-}
-
-export default function NoBranchSupportPopup(props: Props) {
+export default function NoBranchSupportPopup() {
   return (
-    <BubblePopup position={props.popupPosition} customClass="bubble-popup-bottom">
-      <div className="abs-width-400">
-        <h6 className="spacer-bottom">{translate('branches.no_support.header')}</h6>
-        <p className="big-spacer-bottom markdown">{translate('branches.no_support.header.text')}</p>
-        <p>
-          <a href="https://redirect.sonarsource.com/editions/developer.html" target="_blank">
-            {translate('learn_more')}
-          </a>
-        </p>
-      </div>
-    </BubblePopup>
+    <>
+      <h6 className="spacer-bottom">{translate('branches.no_support.header')}</h6>
+      <p className="big-spacer-bottom markdown">{translate('branches.no_support.header.text')}</p>
+      <p>
+        <a
+          href="https://redirect.sonarsource.com/editions/developer.html"
+          rel="noopener noreferrer"
+          target="_blank">
+          {translate('learn_more')}
+        </a>
+      </p>
+    </>
   );
 }
index 399aff4565032caa8352c3cf60b7563334823c1e..b569b999505e035bf68ef5fb1ac2a2017a7a2530 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
-import BubblePopup from '../../../../components/common/BubblePopup';
 import { translate } from '../../../../helpers/l10n';
 
-interface Props {
-  popupPosition?: any;
-}
-
-export default function SingleBranchHelperPopup(props: Props) {
+export default function SingleBranchHelperPopup() {
   return (
-    <BubblePopup position={props.popupPosition} customClass="bubble-popup-bottom">
-      <div className="abs-width-400">
-        <h6 className="spacer-bottom">{translate('branches.learn_how_to_analyze')}</h6>
-        <p className="big-spacer-bottom markdown">
-          {translate('branches.learn_how_to_analyze.text')}
-        </p>
-        <a
-          className="button"
-          href="https://redirect.sonarsource.com/doc/branches.html"
-          target="_blank">
-          {translate('about_page.read_documentation')}
-        </a>
-      </div>
-    </BubblePopup>
+    <>
+      <h6 className="spacer-bottom">{translate('branches.learn_how_to_analyze')}</h6>
+      <p className="big-spacer-bottom markdown">
+        {translate('branches.learn_how_to_analyze.text')}
+      </p>
+      <a
+        className="button"
+        href="https://redirect.sonarsource.com/doc/branches.html"
+        rel="noopener noreferrer"
+        target="_blank">
+        {translate('about_page.read_documentation')}
+      </a>
+    </>
   );
 }
index 5258daf383f663a15e0978b6357c52ada66354ae..2c2651988af0ca1b64e085e704e759663cb99c63 100644 (file)
@@ -114,10 +114,7 @@ it('renders single branch popup', () => {
     />,
     { context: { branchesEnabled: true } }
   );
-  expect(wrapper).toMatchSnapshot();
-  expect(wrapper.find('BubblePopupHelper').prop('isOpen')).toBe(false);
-  click(wrapper.find('a'));
-  expect(wrapper.find('BubblePopupHelper').prop('isOpen')).toBe(true);
+  expect(wrapper.find('Popup')).toMatchSnapshot();
 });
 
 it('renders no branch support popup', () => {
@@ -130,10 +127,7 @@ it('renders no branch support popup', () => {
     />,
     { context: { branchesEnabled: false } }
   );
-  expect(wrapper).toMatchSnapshot();
-  expect(wrapper.find('BubblePopupHelper').prop('isOpen')).toBe(false);
-  click(wrapper.find('a'));
-  expect(wrapper.find('BubblePopupHelper').prop('isOpen')).toBe(true);
+  expect(wrapper.find('Popup')).toMatchSnapshot();
 });
 
 it('renders nothing on SonarCloud without branch support', () => {
index 42bc367a2742f5d4db145ad4024b50a9cd5427d7..c3042574729c795b7d2c6636f9b2f004ae8a6b6d 100644 (file)
@@ -36,44 +36,9 @@ exports[`renders main branch 1`] = `
 `;
 
 exports[`renders no branch support popup 1`] = `
-<div
-  className="navbar-context-branches"
->
-  <BranchIcon
-    branchLike={
-      Object {
-        "isMain": true,
-        "name": "master",
-      }
-    }
-    className="little-spacer-right"
-    fill="#cdcdcd"
-  />
-  <span
-    className="note"
-  >
-    master
-  </span>
-  <div
-    className="display-inline-block spacer-left"
-  >
-    <a
-      className="link-no-underline"
-      href="#"
-      onClick={[Function]}
-    >
-      <HelpIcon
-        fill="#cdcdcd"
-      />
-    </a>
-    <BubblePopupHelper
-      isOpen={false}
-      popup={<NoBranchSupportPopup />}
-      position="bottomleft"
-      togglePopup={[Function]}
-    />
-  </div>
-</div>
+<Popup
+  overlay={<NoBranchSupportPopup />}
+/>
 `;
 
 exports[`renders pull request 1`] = `
@@ -185,41 +150,7 @@ exports[`renders short-living branch 1`] = `
 `;
 
 exports[`renders single branch popup 1`] = `
-<div
-  className="navbar-context-branches"
->
-  <BranchIcon
-    branchLike={
-      Object {
-        "isMain": true,
-        "name": "master",
-      }
-    }
-    className="little-spacer-right"
-  />
-  <span
-    className="note"
-  >
-    master
-  </span>
-  <div
-    className="display-inline-block spacer-left"
-  >
-    <a
-      className="link-no-underline"
-      href="#"
-      onClick={[Function]}
-    >
-      <HelpIcon
-        fill="#4b9fd5"
-      />
-    </a>
-    <BubblePopupHelper
-      isOpen={false}
-      popup={<SingleBranchHelperPopup />}
-      position="bottomleft"
-      togglePopup={[Function]}
-    />
-  </div>
-</div>
+<Popup
+  overlay={<SingleBranchHelperPopup />}
+/>
 `;
index cdc9d4cb9658e1111d923c847017bedafeb05796..964fc40a82307de2f206944ca4ab209abb933368 100644 (file)
@@ -1,30 +1,25 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`renders 1`] = `
-<BubblePopup
-  customClass="bubble-popup-bottom"
->
-  <div
-    className="abs-width-400"
+<React.Fragment>
+  <h6
+    className="spacer-bottom"
   >
-    <h6
-      className="spacer-bottom"
-    >
-      branches.no_support.header
-    </h6>
-    <p
-      className="big-spacer-bottom markdown"
+    branches.no_support.header
+  </h6>
+  <p
+    className="big-spacer-bottom markdown"
+  >
+    branches.no_support.header.text
+  </p>
+  <p>
+    <a
+      href="https://redirect.sonarsource.com/editions/developer.html"
+      rel="noopener noreferrer"
+      target="_blank"
     >
-      branches.no_support.header.text
-    </p>
-    <p>
-      <a
-        href="https://redirect.sonarsource.com/editions/developer.html"
-        target="_blank"
-      >
-        learn_more
-      </a>
-    </p>
-  </div>
-</BubblePopup>
+      learn_more
+    </a>
+  </p>
+</React.Fragment>
 `;
index 459630aab4737ba2fcb2508a1d7cd9716ff59e09..a27c56302ebe76a7467b71ca3003c29d505b3b9e 100644 (file)
@@ -1,29 +1,24 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`renders 1`] = `
-<BubblePopup
-  customClass="bubble-popup-bottom"
->
-  <div
-    className="abs-width-400"
+<React.Fragment>
+  <h6
+    className="spacer-bottom"
   >
-    <h6
-      className="spacer-bottom"
-    >
-      branches.learn_how_to_analyze
-    </h6>
-    <p
-      className="big-spacer-bottom markdown"
-    >
-      branches.learn_how_to_analyze.text
-    </p>
-    <a
-      className="button"
-      href="https://redirect.sonarsource.com/doc/branches.html"
-      target="_blank"
-    >
-      about_page.read_documentation
-    </a>
-  </div>
-</BubblePopup>
+    branches.learn_how_to_analyze
+  </h6>
+  <p
+    className="big-spacer-bottom markdown"
+  >
+    branches.learn_how_to_analyze.text
+  </p>
+  <a
+    className="button"
+    href="https://redirect.sonarsource.com/doc/branches.html"
+    rel="noopener noreferrer"
+    target="_blank"
+  >
+    about_page.read_documentation
+  </a>
+</React.Fragment>
 `;
index c7c42cc6d598811079a2e35bedda0757d1522eae..9d0e5a5a2d9f6e3f961729088e475384ddb00385 100644 (file)
@@ -143,6 +143,7 @@ a.badge-focus:active {
   color: var(--secondFontColor);
   font-size: var(--smallFontSize);
   font-weight: 400;
+  white-space: nowrap;
 }
 
 .outline-badge.active {
index 6c9d545ce78c1fe17be184cdd05ad26e1100c1bf..b661a707b43f8542f855747784789c2a12cef8d5 100644 (file)
@@ -464,7 +464,6 @@ a.search-navigator-facet:focus .facet-stat {
 
 .search-navigator-facet-header-value {
   display: block;
-  padding: 8px 0;
   overflow: hidden;
 }
 
diff --git a/server/sonar-web/src/main/js/app/styles/components/tooltips.css b/server/sonar-web/src/main/js/app/styles/components/tooltips.css
deleted file mode 100644 (file)
index ca2109a..0000000
+++ /dev/null
@@ -1,168 +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.
- */
-.tooltip,
-.rc-tooltip {
-  position: absolute;
-  z-index: var(--tooltipZIndex);
-  display: block;
-  height: auto;
-  font-size: var(--smallFontSize);
-  font-weight: 300;
-  line-height: 1.5;
-  animation: fadeIn 0.3s forwards;
-}
-
-.rc-tooltip-hidden {
-  display: none;
-}
-
-.tooltip.top,
-.rc-tooltip-placement-top {
-  padding: 5px 0;
-  margin-top: -3px;
-}
-
-.tooltip.right,
-.rc-tooltip-placement-right {
-  padding: 0 5px;
-  margin-left: 3px;
-}
-
-.tooltip.bottom,
-.rc-tooltip-placement-bottom {
-  padding: 5px 0;
-  margin-top: 3px;
-}
-
-.tooltip.left,
-.rc-tooltip-placement-left {
-  padding: 0 5px;
-  margin-left: -3px;
-}
-
-.tooltip-inner,
-.rc-tooltip-inner {
-  max-width: 300px;
-  padding: 3px 8px;
-  color: #fff;
-  text-align: left;
-  text-decoration: none;
-  background-color: #475760;
-  border-radius: 4px;
-  letter-spacing: 0.04em;
-  overflow: hidden;
-  word-break: break-word;
-}
-
-.tooltip-inner .alert,
-.rc-tooltip-inner .alert {
-  margin-bottom: 5px;
-  border-radius: 4px;
-}
-
-.tooltip-arrow,
-.rc-tooltip-arrow {
-  position: absolute;
-  width: 0;
-  height: 0;
-  border: solid transparent;
-}
-
-.tooltip.top .tooltip-arrow,
-.rc-tooltip-placement-top .rc-tooltip-arrow {
-  bottom: 0;
-  left: 50%;
-  margin-left: -5px;
-  border-width: 5px 5px 0;
-  border-top-color: #475760;
-}
-
-.tooltip.top-left .tooltip-arrow,
-.rc-tooltip-placement-topLeft .rc-tooltip-arrow {
-  right: 5px;
-  bottom: 0;
-  margin-bottom: -5px;
-  border-width: 5px 5px 0;
-  border-top-color: #475760;
-}
-
-.tooltip.top-right .tooltip-arrow,
-.rc-tooltip-placement-topRight .rc-tooltip-arrow {
-  bottom: 0;
-  left: 5px;
-  margin-bottom: -5px;
-  border-width: 5px 5px 0;
-  border-top-color: #475760;
-}
-
-.tooltip.right .tooltip-arrow,
-.rc-tooltip-placement-right .rc-tooltip-arrow {
-  top: 50%;
-  left: 0;
-  margin-top: -5px;
-  border-width: 5px 5px 5px 0;
-  border-right-color: #475760;
-}
-
-.tooltip.left .tooltip-arrow,
-.rc-tooltip-placement-left .rc-tooltip-arrow {
-  top: 50%;
-  right: 0;
-  margin-top: -5px;
-  border-width: 5px 0 5px 5px;
-  border-left-color: #475760;
-}
-
-.tooltip.bottom .tooltip-arrow,
-.rc-tooltip-placement-bottom .rc-tooltip-arrow {
-  top: 0;
-  left: 50%;
-  margin-left: -5px;
-  border-width: 0 5px 5px;
-  border-bottom-color: #475760;
-}
-
-.tooltip.bottom-left .tooltip-arrow,
-.rc-tooltip-placement-bottomLeft .rc-tooltip-arrow {
-  top: 0;
-  right: 5px;
-  margin-top: -5px;
-  border-width: 0 5px 5px;
-  border-bottom-color: #475760;
-}
-
-.tooltip.bottom-right .tooltip-arrow,
-.rc-tooltip-placement-bottomRight .rc-tooltip-arrow {
-  top: 0;
-  left: 5px;
-  margin-top: -5px;
-  border-width: 0 5px 5px;
-  border-bottom-color: #475760;
-}
-
-@keyframes fadeIn {
-  from {
-    opacity: 0;
-  }
-
-  to {
-    opacity: 1;
-  }
-}
index c370f7bb0e83eb8f3be3b8b2b22625521c4c7f55..bcf268567d2dbd548991e73e2fe8aed7b5b649ec 100644 (file)
@@ -52,10 +52,6 @@ a:focus {
   border-bottom-color: var(--lightBlue);
 }
 
-.tooltip a {
-  color: var(--lightBlue);
-}
-
 .link-no-underline {
   border-bottom: none;
 }
index bdc3568bf8d5df47d3a4a2a308cd55ae49f2cc7a..2a2a29366bb255838266a170fde1079948a6913b 100644 (file)
@@ -76,6 +76,7 @@ table.data > thead:after {
 }
 
 table.data > thead > tr > th {
+  position: relative;
   vertical-align: top;
   line-height: 18px;
   padding: 8px 10px;
@@ -96,6 +97,7 @@ table.data > tfoot > tr > td {
 }
 
 table.data > tbody > tr > td {
+  position: relative;
   padding: 8px 10px;
   vertical-align: text-top;
   line-height: 16px;
@@ -263,3 +265,11 @@ table.form td img {
 table#project-history tr > td {
   vertical-align: top;
 }
+
+.table-cell-doc {
+  position: absolute;
+  z-index: var(--aboveNormalZIndex);
+  right: -8px;
+  top: 50%;
+  margin-top: -6px;
+}
index f5bcff13ca54dabbf2a1ba04c7bc7a8b2e84139e..4fc6c561948ff61fec14d226b3e1411f92049268 100644 (file)
   line-height: 1.5;
 }
 
+.markdown.cut-margins > *:first-child {
+  margin-top: 0 !important;
+}
+
+.markdown.cut-margins > *:last-child {
+  margin-bottom: 0 !important;
+}
+
 .rule-desc p,
 .markdown p,
 .rule-desc ul,
index dd0a053202e013d517a1001a7e722bf8b0260778..7e8442789700defc981812ed7672eb3f3761b098 100644 (file)
@@ -21,6 +21,7 @@ import * as React from 'react';
 import { orderBy, without, sortBy } from 'lodash';
 import * as classNames from 'classnames';
 import { FacetKey } from '../query';
+import Tooltip from '../../../components/controls/Tooltip';
 import FacetBox from '../../../components/facet/FacetBox';
 import FacetHeader from '../../../components/facet/FacetHeader';
 import FacetItem from '../../../components/facet/FacetItem';
@@ -37,6 +38,7 @@ export interface BasicProps {
 }
 
 interface Props extends BasicProps {
+  children?: React.ReactNode;
   disabled?: boolean;
   disabledHelper?: string;
   halfWidth?: boolean;
@@ -101,13 +103,17 @@ export default class Facet extends React.PureComponent<Props> {
         className={classNames({ 'search-navigator-facet-box-forbidden': this.props.disabled })}
         property={this.props.property}>
         <FacetHeader
-          helper={this.props.disabled ? this.props.disabledHelper : undefined}
-          name={translate('coding_rules.facet', this.props.property)}
+          name={
+            <Tooltip overlay={this.props.disabled ? this.props.disabledHelper : undefined}>
+              <span>{translate('coding_rules.facet', this.props.property)}</span>
+            </Tooltip>
+          }
           onClear={this.handleClear}
           onClick={this.props.disabled ? undefined : this.handleHeaderClick}
           open={this.props.open && !this.props.disabled}
-          values={values}
-        />
+          values={values}>
+          {this.props.children}
+        </FacetHeader>
 
         {this.props.open &&
           items !== undefined && <FacetItemsList>{items.map(this.renderItem)}</FacetItemsList>}
index 402b971dcaae8dbfb1cc868a75290514f0c7a224..68b6f9ae1ca51eb8dbb76eae3752948b9fc286b9 100644 (file)
@@ -22,6 +22,7 @@ import { sortBy } from 'lodash';
 import * as classNames from 'classnames';
 import { Query, FacetKey } from '../query';
 import { Profile } from '../../../api/quality-profiles';
+import DocTooltip from '../../../components/docs/DocTooltip';
 import FacetBox from '../../../components/facet/FacetBox';
 import FacetHeader from '../../../components/facet/FacetHeader';
 import FacetItem from '../../../components/facet/FacetItem';
@@ -161,8 +162,9 @@ export default class ProfileFacet extends React.PureComponent<Props> {
           onClear={this.handleClear}
           onClick={this.handleHeaderClick}
           open={this.props.open}
-          values={this.getTextValue()}
-        />
+          values={this.getTextValue()}>
+          <DocTooltip className="spacer-left" doc="rules/rules-quality-profiles" />
+        </FacetHeader>
 
         {this.props.open && <FacetItemsList>{profiles.map(this.renderItem)}</FacetItemsList>}
       </FacetBox>
index e951e3b9d44e2180107c4fbea23bf0e26b36cbc7..21481a1f240a1e646cea6f872a69552a515b5227 100644 (file)
@@ -31,6 +31,7 @@ import { getRuleDetails, deleteRule, updateRule } from '../../../api/rules';
 import { RuleActivation, RuleDetails as IRuleDetails } from '../../../app/types';
 import DeferredSpinner from '../../../components/common/DeferredSpinner';
 import ConfirmButton from '../../../components/controls/ConfirmButton';
+import DocTooltip from '../../../components/docs/DocTooltip';
 import { Button } from '../../../components/ui/buttons';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 
@@ -175,7 +176,7 @@ export default class RuleDetails extends React.PureComponent<Props, State> {
           {params.length > 0 && <RuleDetailsParameters params={params} />}
 
           {isEditable && (
-            <div className="coding-rules-detail-description">
+            <div className="coding-rules-detail-description display-flex-center">
               {/* `templateRule` is used to get rule meta data, `customRule` is used to get parameter values */}
               {/* it's expected to pass the same rule to both parameters */}
               <CustomRuleButton
@@ -202,12 +203,15 @@ export default class RuleDetails extends React.PureComponent<Props, State> {
                 modalHeader={translate('coding_rules.delete_rule')}
                 onConfirm={this.handleDelete}>
                 {({ onClick }) => (
-                  <Button
-                    className="button-red spacer-left js-delete"
-                    id="coding-rules-detail-rule-delete"
-                    onClick={onClick}>
-                    {translate('delete')}
-                  </Button>
+                  <>
+                    <Button
+                      className="button-red spacer-left js-delete"
+                      id="coding-rules-detail-rule-delete"
+                      onClick={onClick}>
+                      {translate('delete')}
+                    </Button>
+                    <DocTooltip className="spacer-left" doc="rules/custom-rule-removal" />
+                  </>
                 )}
               </ConfirmButton>
             </div>
index 5d5bc25cdae751638d6d87ac8600908d2cf7f9d7..16e9f0d6f6543320e2f5ac455e55815917b620a0 100644 (file)
@@ -27,6 +27,7 @@ import { getRuleUrl } from '../../../helpers/urls';
 import LinkIcon from '../../../components/icons-components/LinkIcon';
 import RuleScopeIcon from '../../../components/icons-components/RuleScopeIcon';
 import Tooltip from '../../../components/controls/Tooltip';
+import DocTooltip from '../../../components/docs/DocTooltip';
 import { translate } from '../../../helpers/l10n';
 import IssueTypeIcon from '../../../components/ui/IssueTypeIcon';
 import SeverityHelper from '../../../components/shared/SeverityHelper';
@@ -174,16 +175,15 @@ export default class RuleDetailsMeta extends React.PureComponent<Props, State> {
       return null;
     }
     return (
-      <Tooltip overlay={translate('coding_rules.custom_rule.title')}>
-        <li className="coding-rules-detail-property">
-          {translate('coding_rules.custom_rule')}
-          {' ('}
-          <Link to={getRuleUrl(ruleDetails.templateKey, this.props.organization)}>
-            {translate('coding_rules.show_template')}
-          </Link>
-          {')'}
-        </li>
-      </Tooltip>
+      <li className="coding-rules-detail-property">
+        {translate('coding_rules.custom_rule')}
+        {' ('}
+        <Link to={getRuleUrl(ruleDetails.templateKey, this.props.organization)}>
+          {translate('coding_rules.show_template')}
+        </Link>
+        {')'}
+        <DocTooltip className="little-spacer-left" doc="rules/custom-rules" />
+      </li>
     );
   };
 
index 4a5c87d47741f3038add29f6e6ccf00932a1eaae..1f9c3f803ab081fa0595e00edc7b8c9c3e30fe13 100644 (file)
@@ -20,6 +20,7 @@
 import * as React from 'react';
 import Facet, { BasicProps } from './Facet';
 import { Omit } from '../../../app/types';
+import DocTooltip from '../../../components/docs/DocTooltip';
 import { translate } from '../../../helpers/l10n';
 
 interface Props extends Omit<BasicProps, 'onChange' | 'values'> {
@@ -55,8 +56,9 @@ export default class TemplateFacet extends React.PureComponent<Props> {
         renderName={this.renderName}
         renderTextName={this.renderName}
         singleSelection={true}
-        values={value !== undefined ? [String(value)] : []}
-      />
+        values={value !== undefined ? [String(value)] : []}>
+        <DocTooltip className="spacer-left" doc="rules/rule-templates" />
+      </Facet>
     );
   }
 }
index 20ab78851469ac7efa294a23ceeea7bbf979ee7c..8c647e71b56a7970b76727954c9fa04a7faf1f2a 100644 (file)
@@ -25,6 +25,7 @@ import * as theme from '../../../app/theme';
 import { translate } from '../../../helpers/l10n';
 import Level from '../../../components/ui/Level';
 import Tooltip from '../../../components/controls/Tooltip';
+import DocTooltip from '../../../components/docs/DocTooltip';
 import HelpIcon from '../../../components/icons-components/HelpIcon';
 /*:: import type { Component, MeasuresList } from '../types'; */
 
@@ -64,10 +65,11 @@ export default function QualityGate({ branchLike, component, measures } /*: Prop
 
   return (
     <div className="overview-quality-gate" id="overview-quality-gate">
-      <h2 className="overview-title">
-        {translate('overview.quality_gate')}
-        <Level level={level} />
-      </h2>
+      <div className="display-flex-center">
+        <h2 className="overview-title">{translate('overview.quality_gate')}</h2>
+        <DocTooltip className="spacer-left" doc="quality-gates/project-homepage-quality-gate" />
+        <Level className="big-spacer-left" level={level} />
+      </div>
 
       {ignoredConditions && (
         <div className="alert alert-info display-inline-block big-spacer-top">
index 2b9f86b60625687f221534fd0a01955b4a153114..2387806ada664a54f2165890a2986ef1baced97a 100644 (file)
@@ -5,14 +5,23 @@ exports[`renders message about ignored conditions 1`] = `
   className="overview-quality-gate"
   id="overview-quality-gate"
 >
-  <h2
-    className="overview-title"
+  <div
+    className="display-flex-center"
   >
-    overview.quality_gate
+    <h2
+      className="overview-title"
+    >
+      overview.quality_gate
+    </h2>
+    <DocTooltip
+      className="spacer-left"
+      doc="quality-gates/project-homepage-quality-gate"
+    />
     <Level
+      className="big-spacer-left"
       level="OK"
     />
-  </h2>
+  </div>
   <div
     className="alert alert-info display-inline-block big-spacer-top"
   >
index ac492720ff53645528ed33bdb80507434f28944e..0d0dc6dbee92f4676b704677973a8ec54b5a260c 100644 (file)
   font-weight: 400;
 }
 
-.overview-title > .level {
-  vertical-align: top;
-  margin-left: 15px;
-}
-
 /*
  * Quality Gate
  */
index 5adc7becf55a612e349fc09556d6757657ecf6b3..de42184f96dcb9dd1e31e686e0f301efaf64a001 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
+import DocTooltip from '../../components/docs/DocTooltip';
 import { translate } from '../../helpers/l10n';
 
 export default function Header() {
   return (
     <header className="page-header">
-      <h1 className="page-title">{translate('project_quality_gate.page')}</h1>
+      <div className="page-title display-flex-center">
+        <h1>{translate('project_quality_gate.page')}</h1>
+        <DocTooltip className="spacer-left" doc="quality-gates/quality-gate-projects" />
+      </div>
       <div className="page-description">{translate('project_quality_gate.page.description')}</div>
     </header>
   );
index eaade8b5468ac28dc4e912ae3ef3eb4696c6d9a1..ccf8e090d5791ba8826d198bd4bd38019d68bc61 100644 (file)
@@ -4,11 +4,17 @@ exports[`renders 1`] = `
 <header
   className="page-header"
 >
-  <h1
-    className="page-title"
+  <div
+    className="page-title display-flex-center"
   >
-    project_quality_gate.page
-  </h1>
+    <h1>
+      project_quality_gate.page
+    </h1>
+    <DocTooltip
+      className="spacer-left"
+      doc="quality-gates/quality-gate-projects"
+    />
+  </div>
   <div
     className="page-description"
   >
index b1347b6ff2efe22b058efa95367d78ffc456c859..cf31adcb6211d93b79b63cfaab2497ab23e68928 100644 (file)
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
+import DocTooltip from '../../components/docs/DocTooltip';
 import { translate } from '../../helpers/l10n';
 
 export default function Header() {
   return (
     <header className="page-header">
-      <h1 className="page-title">{translate('project_quality_profiles.page')}</h1>
+      <div className="page-title display-flex-center">
+        <h1>{translate('project_quality_profiles.page')}</h1>
+        <DocTooltip className="spacer-left" doc="quality-profiles/quality-profile-projects" />
+      </div>
       <div className="page-description">
         {translate('project_quality_profiles.page.description')}
       </div>
index 4f0e35f4a2f3c753c1f3b6fe4450bca42d532c72..03ed0f32b011ccb216829a0cd151b15dbd59c51c 100644 (file)
@@ -4,11 +4,17 @@ exports[`renders 1`] = `
 <header
   className="page-header"
 >
-  <h1
-    className="page-title"
+  <div
+    className="page-title display-flex-center"
   >
-    project_quality_profiles.page
-  </h1>
+    <h1>
+      project_quality_profiles.page
+    </h1>
+    <DocTooltip
+      className="spacer-left"
+      doc="quality-profiles/quality-profile-projects"
+    />
+  </div>
   <div
     className="page-description"
   >
index f3fb35559e72e180ea181aa2adc046a7794e1b55..982d6d9f2ab873dde05e7f1654fac8614d587b8b 100644 (file)
@@ -21,6 +21,7 @@ import React from 'react';
 import { sortBy, uniqBy } from 'lodash';
 import AddConditionForm from './AddConditionForm';
 import Condition from './Condition';
+import DocTooltip from '../../../components/docs/DocTooltip';
 import { translate, getLocalizedMetricName } from '../../../helpers/l10n';
 
 function getKey(condition, index) {
@@ -89,7 +90,10 @@ export default class Conditions extends React.PureComponent {
     }));
     return (
       <div className="quality-gate-section" id="quality-gate-conditions">
-        <h3 className="spacer-bottom">{translate('quality_gates.conditions')}</h3>
+        <header className="display-flex-center spacer-bottom">
+          <h3>{translate('quality_gates.conditions')}</h3>
+          <DocTooltip className="spacer-left" doc="quality-gates/quality-gate-conditions" />
+        </header>
 
         <div className="big-spacer-bottom">{translate('quality_gates.introduction')}</div>
 
index ebe73fe1e8afee143701d4a36eac723945182f2d..35b37f12bae95c873ef916d8f193d248b5742995 100644 (file)
@@ -20,6 +20,7 @@
 import React from 'react';
 import Conditions from './Conditions';
 import Projects from './Projects';
+import DocTooltip from '../../../components/docs/DocTooltip';
 import { translate } from '../../../helpers/l10n';
 
 export default class DetailsContent extends React.PureComponent {
@@ -47,7 +48,10 @@ export default class DetailsContent extends React.PureComponent {
         />
 
         <div id="quality-gate-projects" className="quality-gate-section">
-          <h3 className="spacer-bottom">{translate('quality_gates.projects')}</h3>
+          <header className="display-flex-center spacer-bottom">
+            <h3>{translate('quality_gates.projects')}</h3>
+            <DocTooltip className="spacer-left" doc="quality-gates/quality-gate-projects" />
+          </header>
           {gate.isDefault ? (
             defaultMessage
           ) : (
index a661becfdefa5a980910d0c1384aa129e1c6c54d..8e2b1aa14ba1af9dd4c81298769d6c1ec08dd4d5 100644 (file)
@@ -23,6 +23,7 @@ import RenameQualityGateForm from './RenameQualityGateForm';
 import CopyQualityGateForm from './CopyQualityGateForm';
 import DeleteQualityGateForm from './DeleteQualityGateForm';
 import { fetchQualityGate, QualityGate, setQualityGateAsDefault } from '../../../api/quality-gates';
+import DocTooltip from '../../../components/docs/DocTooltip';
 import { Button } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 
@@ -73,10 +74,15 @@ export default class DetailsHeader extends React.PureComponent<Props, State> {
       <div className="layout-page-header-panel layout-page-main-header issues-main-header">
         <div className="layout-page-header-panel-inner layout-page-main-header-inner">
           <div className="layout-page-main-inner">
-            <h2 className="pull-left">
-              {qualityGate.name}
-              {qualityGate.isBuiltIn && <BuiltInQualityGateBadge className="spacer-left" />}
-            </h2>
+            <div className="pull-left display-flex-center">
+              <h2>{qualityGate.name}</h2>
+              {qualityGate.isBuiltIn && (
+                <>
+                  <BuiltInQualityGateBadge className="spacer-left" />
+                  <DocTooltip className="spacer-left" doc="quality-gates/built-in-quality-gate" />
+                </>
+              )}
+            </div>
 
             <div className="pull-right">
               {actions.rename && (
index 4a267e581fb4f61772fc5d7f664ee615e9984c3a..da61b1c5623ceece9079063f61f74ab7f3eb3cd4 100644 (file)
@@ -21,6 +21,7 @@ import * as React from 'react';
 import CreateQualityGateForm from '../components/CreateQualityGateForm';
 import { QualityGate } from '../../../api/quality-gates';
 import { Organization } from '../../../app/types';
+import DocTooltip from '../../../components/docs/DocTooltip';
 import { Button } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
 
@@ -50,7 +51,6 @@ export default class ListHeader extends React.PureComponent<Props, State> {
 
     return (
       <header className="page-header">
-        <h1 className="page-title">{translate('quality_gates.page')}</h1>
         {this.props.canCreate && (
           <div className="page-actions">
             <Button id="quality-gate-add" onClick={this.openCreateQualityGateForm}>
@@ -58,6 +58,10 @@ export default class ListHeader extends React.PureComponent<Props, State> {
             </Button>
           </div>
         )}
+        <div className="display-flex-center">
+          <h1 className="page-title">{translate('quality_gates.page')}</h1>
+          <DocTooltip className="spacer-left" doc="quality-gates/quality-gate" />
+        </div>
         {this.state.createQualityGateOpen && (
           <CreateQualityGateForm
             onClose={this.closeCreateQualityGateForm}
index c30eff07980b79f752282986d0e2fdb1850a2dc8..af274592121744427eb369d1e0b2740700a89eae 100644 (file)
@@ -32,8 +32,8 @@ interface Props {
 export default function ProfileLink({ name, language, organization, children, ...other }: Props) {
   return (
     <Link
-      to={getProfilePath(name, language, organization)}
       activeClassName="link-no-underline"
+      to={getProfilePath(name, language, organization)}
       {...other}>
       {children}
     </Link>
index 812d6110047b71bb027f6d593c38ee08d9e9cd53..ae1e57854f94230d2650d89bd4b0cb5684c1d491 100644 (file)
@@ -21,6 +21,7 @@ import * as React from 'react';
 import { groupBy, pick, sortBy } from 'lodash';
 import ProfilesListRow from './ProfilesListRow';
 import ProfilesListHeader from './ProfilesListHeader';
+import DocTooltip from '../../../components/docs/DocTooltip';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { Profile } from '../types';
 
@@ -61,7 +62,13 @@ export default class ProfilesList extends React.PureComponent<Props> {
             {', '}
             {translateWithParameters('quality_profiles.x_profiles', profilesCount)}
           </th>
-          <th className="text-right nowrap">{translate('quality_profiles.list.projects')}</th>
+          <th className="text-right nowrap">
+            {translate('quality_profiles.list.projects')}
+            <DocTooltip
+              className="table-cell-doc"
+              doc="quality-profiles/quality-profile-projects"
+            />
+          </th>
           <th className="text-right nowrap">{translate('quality_profiles.list.rules')}</th>
           <th className="text-right nowrap">{translate('quality_profiles.list.updated')}</th>
           <th className="text-right nowrap">{translate('quality_profiles.list.used')}</th>
index 677ce226b99b1b312b926d9ceea57cb98e516386..a0b3302ff480dd3d37de156306158bdc7fde6155 100644 (file)
@@ -28,6 +28,7 @@ import { getRulesUrl } from '../../../helpers/urls';
 import { isStagnant } from '../utils';
 import { Profile } from '../types';
 import Tooltip from '../../../components/controls/Tooltip';
+import DocTooltip from '../../../components/docs/DocTooltip';
 
 interface Props {
   onRequestFail: (reason: any) => void;
@@ -41,14 +42,21 @@ export default class ProfilesListRow extends React.PureComponent<Props> {
     const { profile } = this.props;
     const offset = 25 * (profile.depth - 1);
     return (
-      <div style={{ paddingLeft: offset }}>
-        <ProfileLink
-          language={profile.language}
-          name={profile.name}
-          organization={this.props.organization}>
-          {profile.name}
-        </ProfileLink>
-        {profile.isBuiltIn && <BuiltInQualityProfileBadge className="spacer-left" />}
+      <div className="display-flex-center" style={{ paddingLeft: offset }}>
+        <div>
+          <ProfileLink
+            language={profile.language}
+            name={profile.name}
+            organization={this.props.organization}>
+            {profile.name}
+          </ProfileLink>
+        </div>
+        {profile.isBuiltIn && (
+          <>
+            <BuiltInQualityProfileBadge className="spacer-left" />
+            <DocTooltip className="spacer-left" doc="quality-profiles/built-in-quality-profile" />
+          </>
+        )}
       </div>
     );
   }
@@ -57,7 +65,12 @@ export default class ProfilesListRow extends React.PureComponent<Props> {
     const { profile } = this.props;
 
     if (profile.isDefault) {
-      return <span className="badge">{translate('default')}</span>;
+      return (
+        <>
+          <span className="badge">{translate('default')}</span>
+          <DocTooltip className="table-cell-doc" doc="quality-profiles/default-quality-profile" />
+        </>
+      );
     }
 
     return <span>{profile.projectCount}</span>;
@@ -122,23 +135,23 @@ export default class ProfilesListRow extends React.PureComponent<Props> {
   render() {
     return (
       <tr
-        className="quality-profiles-table-row"
+        className="quality-profiles-table-row text-middle"
         data-key={this.props.profile.key}
         data-name={this.props.profile.name}>
-        <td className="quality-profiles-table-name">{this.renderName()}</td>
-        <td className="quality-profiles-table-projects thin nowrap text-right">
+        <td className="quality-profiles-table-name text-middle">{this.renderName()}</td>
+        <td className="quality-profiles-table-projects thin nowrap text-middle text-right">
           {this.renderProjects()}
         </td>
-        <td className="quality-profiles-table-rules thin nowrap text-right">
+        <td className="quality-profiles-table-rules thin nowrap text-middle text-right">
           {this.renderRules()}
         </td>
-        <td className="quality-profiles-table-date thin nowrap text-right">
+        <td className="quality-profiles-table-date thin nowrap text-middle text-right">
           {this.renderUpdateDate()}
         </td>
-        <td className="quality-profiles-table-date thin nowrap text-right">
+        <td className="quality-profiles-table-date thin nowrap text-middle text-right">
           {this.renderUsageDate()}
         </td>
-        <td className="quality-profiles-table-actions thin nowrap text-right">
+        <td className="quality-profiles-table-actions thin nowrap text-middle text-right">
           <ProfileActions
             fromList={true}
             onRequestFail={this.props.onRequestFail}
index 38ebd8349890248e7789f753b93097400a721409..7020ee5749056225d83572cd2596f2d877d6c483 100644 (file)
@@ -32,6 +32,10 @@ interface Props {
   position: BubblePopupPosition;
 }
 
+/**
+ * Deprecated.
+ * Use <Popup /> instead.
+ */
 export default function BubblePopup(props: Props) {
   const popupClass = classNames('bubble-popup', props.customClass);
   const popupStyle = { ...props.position };
diff --git a/server/sonar-web/src/main/js/components/controls/Popup.tsx b/server/sonar-web/src/main/js/components/controls/Popup.tsx
new file mode 100644 (file)
index 0000000..68f8415
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * 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 OutsideClickHandler from './OutsideClickHandler';
+import Tooltip from './Tooltip';
+
+interface Props {
+  children: (props: { onClick: () => void }) => React.ReactElement<any>;
+  overlay: React.ReactNode;
+}
+
+interface State {
+  visible: boolean;
+}
+
+export default class Popup extends React.Component<Props, State> {
+  state: State = { visible: false };
+
+  componentWillReceiveProps(nextProps: Props) {
+    if (nextProps.overlay !== this.props.overlay) {
+      this.setState({ visible: false });
+    }
+  }
+
+  handleClick = (event?: React.MouseEvent<HTMLElement>) => {
+    if (event) {
+      event.preventDefault();
+      event.currentTarget.blur();
+    }
+
+    // defer opening to not trigger OutsideClickHandler.onClickOutside callback
+    setTimeout(() => {
+      this.setState({ visible: true });
+    }, 0);
+  };
+
+  handleClickOutside = () => {
+    this.setState({ visible: false });
+  };
+
+  renderOverlay() {
+    return (
+      <OutsideClickHandler onClickOutside={this.handleClickOutside}>
+        {({ ref }) => <div ref={ref}>{this.props.overlay}</div>}
+      </OutsideClickHandler>
+    );
+  }
+
+  render() {
+    return (
+      <Tooltip classNameSpace="popup" overlay={this.renderOverlay()} visible={this.state.visible}>
+        {this.props.children({ onClick: this.handleClick })}
+      </Tooltip>
+    );
+  }
+}
index c2529a06b750f93a6ba78925fc768065fcbd467d..bbb8ae5c9427f4053fdf642401bf42861bc340a0 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-.tooltip {
+.tooltip,
+.popup {
   position: absolute;
   z-index: var(--tooltipZIndex);
   display: block;
   height: auto;
   box-sizing: border-box;
+}
+
+.tooltip {
   font-size: var(--smallFontSize);
   font-weight: 300;
   line-height: 1.5;
   animation: fadeIn 0.3s forwards;
 }
 
-.tooltip.top {
+.popup {
+  font-size: var(--baseFontSize);
+  font-weight: normal;
+}
+
+.tooltip.top,
+.popup.top {
   padding: 5px 0;
   margin-top: -3px;
 }
 
-.tooltip.right {
+.tooltip.right,
+.popup.right {
   padding: 0 5px;
   margin-left: 3px;
 }
 
-.tooltip.bottom {
+.tooltip.bottom,
+.popup.bottom {
   padding: 5px 0;
   margin-top: 3px;
 }
 
-.tooltip.left {
+.tooltip.left,
+.popup.left {
   padding: 0 5px;
   margin-left: -3px;
 }
 
-.tooltip-inner {
+.tooltip-inner,
+.popup-inner {
   max-width: 300px;
-  padding: 3px 8px;
-  color: #fff;
   text-align: left;
   text-decoration: none;
-  background-color: #475760;
   border-radius: 4px;
-  letter-spacing: 0.04em;
   overflow: hidden;
   word-break: break-word;
 }
 
+.tooltip-inner {
+  padding: 3px 8px;
+  color: #fff;
+  background-color: #475760;
+  letter-spacing: 0.04em;
+}
+
+.popup-inner {
+  padding: calc(2 * var(--gridSize));
+  border: 1px solid var(--barBorderColor);
+  color: var(--baseFontColor);
+  background-color: #fff;
+  box-shadow: var(--defaultShadow);
+}
+
 .tooltip-inner .alert {
   margin-bottom: 5px;
   border-radius: 4px;
 }
 
-.tooltip-arrow {
+.tooltip-inner a {
+  color: var(--lightBlue);
+}
+
+.tooltip-arrow,
+.popup-arrow,
+.popup-arrow::after {
   position: absolute;
   width: 0;
   height: 0;
   border: solid transparent;
 }
 
-.tooltip.top .tooltip-arrow {
+.tooltip.top .tooltip-arrow,
+.popup.top .popup-arrow,
+.popup.top .popup-arrow::after {
   bottom: 0;
   left: 50%;
   border-width: 5px 5px 0;
-  border-top-color: #475760;
   transform: translateX(-5px);
 }
 
-.tooltip.right .tooltip-arrow {
+.tooltip.top .tooltip-arrow {
+  border-top-color: #475760;
+}
+
+.popup.top .popup-arrow {
+  border-top-color: var(--barBorderColor);
+}
+
+.popup.top .popup-arrow::after {
+  content: '';
+  border-top-color: #fff;
+  transform: translateX(-5px) translateY(-1px);
+}
+
+.tooltip.right .tooltip-arrow,
+.popup.right .popup-arrow,
+.popup.right .popup-arrow::after {
   top: 50%;
   left: 0;
   transform: translateY(-5px);
   border-right-color: #475760;
 }
 
-.tooltip.left .tooltip-arrow {
+.tooltip.right .tooltip-arrow {
+  border-right-color: #475760;
+}
+
+.popup.right .popup-arrow {
+  border-right-color: var(--barBorderColor);
+}
+
+.popup.right .popup-arrow::after {
+  content: '';
+  border-right-color: #fff;
+  transform: translateY(-5px) translateX(1px);
+}
+
+.tooltip.left .tooltip-arrow,
+.popup.left .popup-arrow,
+.popup.left .popup-arrow::after {
   top: 50%;
   right: 0;
   transform: translateY(-5px);
   border-left-color: #475760;
 }
 
-.tooltip.bottom .tooltip-arrow {
+.tooltip.left .tooltip-arrow {
+  border-left-color: #475760;
+}
+
+.popup.left .popup-arrow {
+  border-left-color: var(--barBorderColor);
+}
+
+.popup.left .popup-arrow::after {
+  content: '';
+  border-left-color: #fff;
+  transform: translateY(-5px) translateX(-1px);
+}
+
+.tooltip.bottom .tooltip-arrow,
+.popup.bottom .popup-arrow,
+.popup.bottom .popup-arrow::after {
   top: 0;
   left: 50%;
   transform: translateX(-5px);
   border-bottom-color: #475760;
 }
 
+.tooltip.bottom .tooltip-arrow {
+  border-bottom-color: #475760;
+}
+
+.popup.bottom .popup-arrow {
+  border-bottom-color: var(--barBorderColor);
+}
+
+.popup.bottom .popup-arrow::after {
+  content: '';
+  border-bottom-color: #fff;
+  transform: translateX(-5px) translateY(1px);
+}
+
 @keyframes fadeIn {
   from {
     opacity: 0;
index a16c9c691dbe59297c02d4b12dc715e06cd90272..adc29a6270e3f84469a03764664f518da8b2c720 100644 (file)
@@ -25,6 +25,7 @@ import './Tooltip.css';
 export type Placement = 'bottom' | 'right' | 'left' | 'top';
 
 interface Props {
+  classNameSpace?: string;
   children: React.ReactElement<{}>;
   mouseEnterDelay?: number;
   onShow?: () => void;
@@ -244,6 +245,8 @@ export class TooltipInner extends React.Component<Props, State> {
   };
 
   render() {
+    const { classNameSpace = 'tooltip' } = this.props;
+
     return (
       <>
         {React.cloneElement(this.props.children, {
@@ -253,7 +256,7 @@ export class TooltipInner extends React.Component<Props, State> {
         {this.isVisible() && (
           <TooltipPortal>
             <div
-              className={`tooltip ${this.getPlacement()}`}
+              className={`${classNameSpace} ${this.getPlacement()}`}
               ref={this.tooltipNodeRef}
               style={
                 isMeasured(this.state)
@@ -265,9 +268,9 @@ export class TooltipInner extends React.Component<Props, State> {
                     }
                   : undefined
               }>
-              <div className="tooltip-inner">{this.props.overlay}</div>
+              <div className={`${classNameSpace}-inner`}>{this.props.overlay}</div>
               <div
-                className="tooltip-arrow"
+                className={`${classNameSpace}-arrow`}
                 style={
                   isMeasured(this.state)
                     ? { marginLeft: -this.state.leftFix, marginTop: -this.state.topFix }
diff --git a/server/sonar-web/src/main/js/components/docs/DocLink.tsx b/server/sonar-web/src/main/js/components/docs/DocLink.tsx
new file mode 100644 (file)
index 0000000..fd19f91
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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 { Link } from 'react-router';
+
+export default function DocLink(props: React.AnchorHTMLAttributes<HTMLAnchorElement>) {
+  const { children, href, ...other } = props;
+
+  if (process.env.NODE_ENV === 'development') {
+    if (href && href.startsWith('#')) {
+      return (
+        <>
+          {/* TODO implement after SONAR-10612 Create documentation space in the web app */}
+          <Link to="" {...other}>
+            {children}
+          </Link>
+          <strong className="little-spacer-left text-danger">[TODO]</strong>
+        </>
+      );
+    }
+  }
+
+  return (
+    <a href={href} {...other}>
+      {children}
+    </a>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/docs/DocMarkdownBlock.tsx b/server/sonar-web/src/main/js/components/docs/DocMarkdownBlock.tsx
new file mode 100644 (file)
index 0000000..7a74a9f
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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 * as classNames from 'classnames';
+import remark from 'remark';
+import reactRenderer from 'remark-react';
+import DocLink from './DocLink';
+
+interface Props {
+  className?: string;
+  content: string | undefined;
+}
+
+export default function DocMarkdownBlock({ className, content }: Props) {
+  return (
+    <div className={classNames('markdown', className)}>
+      {
+        remark()
+          .use(reactRenderer, {
+            remarkReactComponents: {
+              // do not render outer <div />
+              div: React.Fragment,
+              // use custom link to render documentation anchors
+              a: DocLink
+            }
+          })
+          .processSync(content).contents
+      }
+    </div>
+  );
+}
diff --git a/server/sonar-web/src/main/js/components/docs/DocTooltip.tsx b/server/sonar-web/src/main/js/components/docs/DocTooltip.tsx
new file mode 100644 (file)
index 0000000..0dc27dd
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * 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 * as classNames from 'classnames';
+import DocMarkdownBlock from './DocMarkdownBlock';
+import HelpIcon from '../icons-components/HelpIcon';
+import Tooltip from '../controls/Tooltip';
+import OutsideClickHandler from '../controls/OutsideClickHandler';
+import * as theme from '../../app/theme';
+
+interface Props {
+  className?: string;
+  /** Key of the documentation chunk */
+  doc: 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 };
+
+  componentDidMount() {
+    this.mounted = true;
+    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/${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 });
+  };
+
+  handleHelpClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
+    event.preventDefault();
+    event.currentTarget.blur();
+    if (!this.state.open && !this.state.loading && this.state.content === undefined) {
+      this.fetchContent();
+    }
+
+    if (this.state.open) {
+      this.setState({ open: false });
+    } else {
+      // defer opening to not trigger OutsideClickHandler.onClickOutside callback
+      setTimeout(() => {
+        this.setState({ open: true });
+      }, 0);
+    }
+  };
+
+  renderOverlay() {
+    if (this.state.loading) {
+      return (
+        <div className="abs-width-300">
+          <i className="spinner" />
+        </div>
+      );
+    }
+
+    return (
+      <OutsideClickHandler onClickOutside={this.close}>
+        {({ ref }) => (
+          <div ref={ref}>
+            <DocMarkdownBlock className="cut-margins abs-width-300" content={this.state.content} />
+          </div>
+        )}
+      </OutsideClickHandler>
+    );
+  }
+
+  render() {
+    return (
+      <div className={classNames('display-flex-center', this.props.className)}>
+        <Tooltip
+          classNameSpace="popup"
+          overlay={this.renderOverlay()}
+          visible={this.state.content !== undefined && this.state.open}>
+          <a
+            className="display-flex-center link-no-underline"
+            href="#"
+            onClick={this.handleHelpClick}>
+            <HelpIcon fill={theme.gray80} size={12} />
+          </a>
+        </Tooltip>
+      </div>
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/components/docs/__tests__/DocLink-test.tsx b/server/sonar-web/src/main/js/components/docs/__tests__/DocLink-test.tsx
new file mode 100644 (file)
index 0000000..ad740c2
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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 { shallow } from 'enzyme';
+import DocLink from '../DocLink';
+
+it('should render simple link', () => {
+  expect(shallow(<DocLink href="http://sample.com" />)).toMatchSnapshot();
+});
+
+it.skip('should render documentation anchor', () => {
+  expect(shallow(<DocLink href="#quality-profiles" />)).toMatchSnapshot();
+});
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
new file mode 100644 (file)
index 0000000..587343d
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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 { shallow } from 'enzyme';
+import DocMarkdownBlock from '../DocMarkdownBlock';
+
+// mock `remark` and `remark-react` to work around the issue with cjs imports
+jest.mock('remark', () => {
+  const remark = require.requireActual('remark');
+  return { default: remark };
+});
+
+jest.mock('remark-react', () => {
+  const remarkReact = require.requireActual('remark-react');
+  return { default: remarkReact };
+});
+
+it('should render simple markdown', () => {
+  expect(shallow(<DocMarkdownBlock content="this is *bold* text" />)).toMatchSnapshot();
+});
+
+it('should render use custom component for links', () => {
+  expect(
+    shallow(<DocMarkdownBlock content="some [link](#quality-profiles)" />).find('DocLink')
+  ).toMatchSnapshot();
+});
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
new file mode 100644 (file)
index 0000000..bc5b499
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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 { shallow } from 'enzyme';
+import DocTooltip from '../DocTooltip';
+import { click } 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 });
+  expect(wrapper).toMatchSnapshot();
+  wrapper.setState({ loading: false });
+  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 });
+});
+
+it('should toggle', () => {
+  const wrapper = shallow(<DocTooltip doc="foo/bar" />);
+  expect(wrapper.state('open')).toBe(false);
+  click(wrapper.find('a'));
+  jest.runAllTimers();
+  expect(wrapper.state('open')).toBe(true);
+});
diff --git a/server/sonar-web/src/main/js/components/docs/__tests__/__snapshots__/DocLink-test.tsx.snap b/server/sonar-web/src/main/js/components/docs/__tests__/__snapshots__/DocLink-test.tsx.snap
new file mode 100644 (file)
index 0000000..97a6d1d
--- /dev/null
@@ -0,0 +1,7 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render simple link 1`] = `
+<a
+  href="http://sample.com"
+/>
+`;
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
new file mode 100644 (file)
index 0000000..28a2441
--- /dev/null
@@ -0,0 +1,32 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render simple markdown 1`] = `
+<div
+  className="markdown"
+>
+  <React.Fragment
+    key="h-1"
+  >
+    <p
+      key="h-2"
+    >
+      this is 
+      <em
+        key="h-3"
+      >
+        bold
+      </em>
+       text
+    </p>
+  </React.Fragment>
+</div>
+`;
+
+exports[`should render use custom component for links 1`] = `
+<DocLink
+  href="#quality-profiles"
+  key="h-3"
+>
+  link
+</DocLink>
+`;
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
new file mode 100644 (file)
index 0000000..38e7789
--- /dev/null
@@ -0,0 +1,61 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render 1`] = `
+<div
+  className="display-flex-center"
+>
+  <Tooltip
+    classNameSpace="popup"
+    overlay={
+      <div
+        className="abs-width-300"
+      >
+        <i
+          className="spinner"
+        />
+      </div>
+    }
+    visible={true}
+  >
+    <a
+      className="display-flex-center link-no-underline"
+      href="#"
+      onClick={[Function]}
+    >
+      <HelpIcon
+        fill="#cdcdcd"
+        size={12}
+      />
+    </a>
+  </Tooltip>
+</div>
+`;
+
+exports[`should render 2`] = `
+<div
+  className="display-flex-center"
+>
+  <Tooltip
+    classNameSpace="popup"
+    overlay={
+      <OutsideClickHandler
+        onClickOutside={[Function]}
+      >
+        [Function]
+      </OutsideClickHandler>
+    }
+    visible={true}
+  >
+    <a
+      className="display-flex-center link-no-underline"
+      href="#"
+      onClick={[Function]}
+    >
+      <HelpIcon
+        fill="#cdcdcd"
+        size={12}
+      />
+    </a>
+  </Tooltip>
+</div>
+`;
index 00b6803c1247324890c57f09483df8c6eaec3d98..cc1ae72047aea669b39cc21d37407fb7fb91c485 100644 (file)
@@ -25,8 +25,9 @@ import { Button } from '../ui/buttons';
 import { translate, translateWithParameters } from '../../helpers/l10n';
 
 interface Props {
+  children?: React.ReactNode;
   helper?: string;
-  name: string;
+  name: React.ReactNode;
   onClear?: () => void;
   onClick?: () => void;
   open: boolean;
@@ -90,6 +91,8 @@ export default class FacetHeader extends React.PureComponent<Props> {
           </span>
         )}
 
+        {this.props.children}
+
         <span className="search-navigator-facet-header-value spacer-left spacer-right ">
           {this.renderValueIndicator()}
         </span>
diff --git a/server/sonar-web/src/main/js/components/icons-components/PlusCircleIcon.tsx b/server/sonar-web/src/main/js/components/icons-components/PlusCircleIcon.tsx
new file mode 100644 (file)
index 0000000..eb529b7
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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 { IconProps } from './types';
+
+export default function PlusCircleIcon({ className, fill = 'currentColor', size = 16 }: IconProps) {
+  return (
+    <svg
+      className={className}
+      height={size}
+      version="1.1"
+      viewBox="0 0 16 16"
+      width={size}
+      xmlSpace="preserve"
+      xmlnsXlink="http://www.w3.org/1999/xlink">
+      <path
+        d="M8 1c3.863 0 7 3.137 7 7s-3.137 7-7 7-7-3.137-7-7 3.137-7 7-7zm3.726 7.985A.274.274 0 0 0 12 8.711V7.289a.274.274 0 0 0-.274-.274H8.985V4.274A.274.274 0 0 0 8.711 4H7.289a.274.274 0 0 0-.274.274v2.741H4.274A.274.274 0 0 0 4 7.289v1.422c0 .152.123.274.274.274h2.741v2.741c0 .151.122.274.274.274h1.422a.274.274 0 0 0 .274-.274V8.985h2.741z"
+        style={{ fill }}
+      />;
+    </svg>
+  );
+}
index 873367f260ee0157f267903d79b58e987ec770be..d9881ef214d274107e40477a67f6e3c9430dc37e 100644 (file)
   version "16.0.29"
   resolved "https://registry.yarnpkg.com/@types/react/-/react-16.0.29.tgz#4eea6a8de9f40ca71d580ae7a9f3b4b77b368de8"
 
+JSONStream@^1.0.4:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.2.tgz#c102371b6ec3a7cf3b847ca00c20bb0fce4c6dea"
+  dependencies:
+    jsonparse "^1.2.0"
+    through ">=2.2.7 <3"
+
 abab@^1.0.3:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.4.tgz#5faad9c2c07f60dd76770f71cf025b62a63cfd4e"
@@ -162,6 +169,10 @@ acorn@^5.0.0, acorn@^5.1.2, acorn@^5.3.0, acorn@^5.5.0:
   version "5.5.3"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.5.3.tgz#f473dd47e0277a08e28e9bec5aeeb04751f0b8c9"
 
+add-stream@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa"
+
 address@1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/address/-/address-1.0.2.tgz#480081e82b587ba319459fef512f516fe03d58af"
@@ -345,6 +356,10 @@ array-flatten@^2.1.0:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.1.tgz#426bb9da84090c1838d812c8150af20a8331e296"
 
+array-ify@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece"
+
 array-includes@^3.0.3:
   version "3.0.3"
   resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d"
@@ -352,6 +367,10 @@ array-includes@^3.0.3:
     define-properties "^1.1.2"
     es-abstract "^1.7.0"
 
+array-iterate@^1.0.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/array-iterate/-/array-iterate-1.1.2.tgz#f66a57e84426f8097f4197fbb6c051b8e5cdf7d8"
+
 array-map@~0.0.0:
   version "0.0.0"
   resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662"
@@ -1115,6 +1134,10 @@ babylon@^6.18.0:
   version "6.18.0"
   resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
 
+bail@^1.0.0:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.3.tgz#63cfb9ddbac829b02a3128cd53224be78e6c21a3"
+
 balanced-match@^0.4.2:
   version "0.4.2"
   resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
@@ -1437,6 +1460,14 @@ camelcase-keys@^2.0.0:
     camelcase "^2.0.0"
     map-obj "^1.0.0"
 
+camelcase-keys@^4.0.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-4.2.0.tgz#a2aa5fb1af688758259c32c141426d78923b9b77"
+  dependencies:
+    camelcase "^4.1.0"
+    map-obj "^2.0.0"
+    quick-lru "^1.0.0"
+
 camelcase@^1.0.2:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39"
@@ -1470,6 +1501,10 @@ caseless@~0.12.0:
   version "0.12.0"
   resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
 
+ccount@^1.0.0:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.0.3.tgz#f1cec43f332e2ea5a569fd46f9f5bde4e6102aff"
+
 center-align@^0.1.1:
   version "0.1.3"
   resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad"
@@ -1503,6 +1538,22 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0:
     escape-string-regexp "^1.0.5"
     supports-color "^5.3.0"
 
+character-entities-html4@^1.0.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-1.1.2.tgz#c44fdde3ce66b52e8d321d6c1bf46101f0150610"
+
+character-entities-legacy@^1.0.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.2.tgz#7c6defb81648498222c9855309953d05f4d63a9c"
+
+character-entities@^1.0.0:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.2.tgz#58c8f371c0774ef0ba9b2aca5f00d8f100e6e363"
+
+character-reference-invalid@^1.0.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.2.tgz#21e421ad3d84055952dab4a43a04e73cd425d3ed"
+
 check-types@^7.3.0:
   version "7.3.0"
   resolved "https://registry.yarnpkg.com/check-types/-/check-types-7.3.0.tgz#468f571a4435c24248f5fd0cb0e8d87c3c341e7d"
@@ -1675,6 +1726,10 @@ code-point-at@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
 
+collapse-white-space@^1.0.0, collapse-white-space@^1.0.2:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.4.tgz#ce05cf49e54c3277ae573036a26851ba430a0091"
+
 collection-visit@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
@@ -1728,6 +1783,12 @@ combined-stream@^1.0.5, combined-stream@~1.0.5:
   dependencies:
     delayed-stream "~1.0.0"
 
+comma-separated-tokens@^1.0.0:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.5.tgz#b13793131d9ea2d2431cf5b507ddec258f0ce0db"
+  dependencies:
+    trim "0.0.1"
+
 commander@2.11.x, commander@~2.11.0:
   version "2.11.0"
   resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563"
@@ -1744,6 +1805,13 @@ commondir@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
 
+compare-func@^1.3.1:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-1.3.2.tgz#99dd0ba457e1f9bc722b12c08ec33eeab31fa648"
+  dependencies:
+    array-ify "^1.0.0"
+    dot-prop "^3.0.0"
+
 component-emitter@^1.2.1:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
@@ -1813,6 +1881,62 @@ content-type@~1.0.4:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
 
+conventional-changelog-angular@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-0.1.0.tgz#ac3d4b869878de5ad57726696b21eedd798ae3c7"
+  dependencies:
+    compare-func "^1.3.1"
+    q "^1.4.1"
+
+conventional-changelog-core@^0.0.2:
+  version "0.0.2"
+  resolved "https://registry.yarnpkg.com/conventional-changelog-core/-/conventional-changelog-core-0.0.2.tgz#795a298de84f8801398cd0fee20fb799a1f02089"
+  dependencies:
+    conventional-changelog-writer "^0.4.1"
+    conventional-commits-parser "^0.1.2"
+    dateformat "^1.0.12"
+    get-pkg-repo "^0.1.0"
+    git-raw-commits "^0.1.2"
+    git-semver-tags "^1.1.0"
+    lodash "^4.0.0"
+    q "^1.4.1"
+    read-pkg "^1.1.0"
+    read-pkg-up "^1.0.1"
+    through2 "^2.0.0"
+
+conventional-changelog-writer@^0.4.1:
+  version "0.4.2"
+  resolved "https://registry.yarnpkg.com/conventional-changelog-writer/-/conventional-changelog-writer-0.4.2.tgz#ccb03c5ebd17ceb94a236cb80b27f4ef6bee7731"
+  dependencies:
+    compare-func "^1.3.1"
+    conventional-commits-filter "^0.1.0"
+    dateformat "^1.0.11"
+    handlebars "^4.0.2"
+    lodash "^4.0.0"
+    meow "^3.3.0"
+    semver "^5.0.1"
+    split "^1.0.0"
+    through2 "^2.0.0"
+
+conventional-commits-filter@^0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-0.1.1.tgz#d9d26c7599f89de3d249cba3def7911fc51c0dab"
+  dependencies:
+    is-subset "^0.1.1"
+    modify-values "^1.0.0"
+
+conventional-commits-parser@^0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-0.1.2.tgz#4a624010634f02122520ecbaf19ca0ba23120437"
+  dependencies:
+    JSONStream "^1.0.4"
+    is-text-path "^1.0.0"
+    lodash "^3.3.1"
+    meow "^3.3.0"
+    split "^1.0.0"
+    through2 "^2.0.0"
+    trim-off-newlines "^1.0.0"
+
 convert-source-map@^1.4.0, convert-source-map@^1.5.0:
   version "1.5.0"
   resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5"
@@ -2158,6 +2282,12 @@ damerau-levenshtein@^1.0.0:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz#03191c432cb6eea168bb77f3a55ffdccb8978514"
 
+dargs@^4.0.1:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/dargs/-/dargs-4.1.0.tgz#03a9dbb4b5c2f139bf14ae53f0b8a2a6a86f4e17"
+  dependencies:
+    number-is-nan "^1.0.0"
+
 dashdash@^1.12.0:
   version "1.14.1"
   resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
@@ -2172,6 +2302,13 @@ date-now@^0.1.4:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
 
+dateformat@^1.0.11, dateformat@^1.0.12:
+  version "1.0.12"
+  resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9"
+  dependencies:
+    get-stdin "^4.0.1"
+    meow "^3.3.0"
+
 debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.6, debug@^2.6.8:
   version "2.6.9"
   resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
@@ -2184,7 +2321,14 @@ debug@^3.1.0:
   dependencies:
     ms "2.0.0"
 
-decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2:
+decamelize-keys@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9"
+  dependencies:
+    decamelize "^1.1.0"
+    map-obj "^1.0.0"
+
+decamelize@^1.0.0, decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.1.2:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
 
@@ -2214,7 +2358,7 @@ default-require-extensions@^1.0.0:
   dependencies:
     strip-bom "^2.0.0"
 
-define-properties@^1.1.2:
+define-properties@^1.1.1, define-properties@^1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94"
   dependencies:
@@ -2298,6 +2442,12 @@ destroy@~1.0.4:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
 
+detab@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/detab/-/detab-2.0.1.tgz#531f5e326620e2fd4f03264a905fb3bcc8af4df4"
+  dependencies:
+    repeat-string "^1.5.4"
+
 detect-indent@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208"
@@ -2444,6 +2594,12 @@ domutils@^1.5.1:
     dom-serializer "0"
     domelementtype "1"
 
+dot-prop@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-3.0.0.tgz#1b708af094a49c9a0e7dbcad790aba539dac1177"
+  dependencies:
+    is-obj "^1.0.0"
+
 duplexer@^0.1.1:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"
@@ -2580,7 +2736,7 @@ errno@^0.1.3, errno@~0.1.7:
   dependencies:
     prr "~1.0.1"
 
-error-ex@^1.2.0:
+error-ex@^1.2.0, error-ex@^1.3.1:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc"
   dependencies:
@@ -2935,7 +3091,7 @@ extend-shallow@^3.0.2:
     assign-symbols "^1.0.0"
     is-extendable "^1.0.1"
 
-extend@~3.0.0, extend@~3.0.1:
+extend@^3.0.0, extend@~3.0.0, extend@~3.0.1:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
 
@@ -3286,6 +3442,15 @@ get-own-enumerable-property-symbols@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-2.0.1.tgz#5c4ad87f2834c4b9b4e84549dc1e0650fb38c24b"
 
+get-pkg-repo@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/get-pkg-repo/-/get-pkg-repo-0.1.0.tgz#7f04d968564bf9cd2e901810577f84c37f2b03bd"
+  dependencies:
+    hosted-git-info "^2.1.4"
+    meow "^3.3.0"
+    normalize-package-data "^2.3.0"
+    through2 "^2.0.0"
+
 get-stdin@^4.0.1:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
@@ -3304,6 +3469,23 @@ getpass@^0.1.1:
   dependencies:
     assert-plus "^1.0.0"
 
+git-raw-commits@^0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-0.1.2.tgz#2becbdcd3f96ef0b19f16863f7a2f6d9d8ab8369"
+  dependencies:
+    dargs "^4.0.1"
+    lodash.template "^3.6.1"
+    meow "^3.1.0"
+    split2 "^1.0.0"
+    through2 "^2.0.0"
+
+git-semver-tags@^1.1.0:
+  version "1.3.6"
+  resolved "https://registry.yarnpkg.com/git-semver-tags/-/git-semver-tags-1.3.6.tgz#357ea01f7280794fe0927f2806bee6414d2caba5"
+  dependencies:
+    meow "^4.0.0"
+    semver "^5.5.0"
+
 glob-base@^0.3.0:
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4"
@@ -3430,7 +3612,7 @@ handle-thing@^1.2.5:
   version "1.2.5"
   resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4"
 
-handlebars@^4.0.3:
+handlebars@^4.0.2, handlebars@^4.0.3:
   version "4.0.11"
   resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc"
   dependencies:
@@ -3541,6 +3723,24 @@ hash.js@^1.0.0, hash.js@^1.0.3:
     inherits "^2.0.3"
     minimalistic-assert "^1.0.0"
 
+hast-to-hyperscript@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/hast-to-hyperscript/-/hast-to-hyperscript-3.1.0.tgz#58ef4af5344f4da22f0622e072a8d5fa062693d3"
+  dependencies:
+    comma-separated-tokens "^1.0.0"
+    is-nan "^1.2.1"
+    kebab-case "^1.0.0"
+    property-information "^3.0.0"
+    space-separated-tokens "^1.0.0"
+    trim "0.0.1"
+    unist-util-is "^2.0.0"
+
+hast-util-sanitize@^1.0.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/hast-util-sanitize/-/hast-util-sanitize-1.1.2.tgz#d10bd6757a21e59c13abc8ae3530dd3b6d7d679e"
+  dependencies:
+    xtend "^4.0.1"
+
 hawk@3.1.3, hawk@~3.1.3:
   version "3.1.3"
   resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4"
@@ -3906,6 +4106,21 @@ is-accessor-descriptor@^1.0.0:
   dependencies:
     kind-of "^6.0.0"
 
+is-alphabetical@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.2.tgz#1fa6e49213cb7885b75d15862fb3f3d96c884f41"
+
+is-alphanumeric@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-alphanumeric/-/is-alphanumeric-1.0.0.tgz#4a9cef71daf4c001c1d81d63d140cf53fd6889f4"
+
+is-alphanumerical@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.2.tgz#1138e9ae5040158dc6ff76b820acd6b7a181fd40"
+  dependencies:
+    is-alphabetical "^1.0.0"
+    is-decimal "^1.0.0"
+
 is-arrayish@^0.2.1:
   version "0.2.1"
   resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
@@ -3920,7 +4135,7 @@ is-boolean-object@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.0.tgz#98f8b28030684219a95f375cfbd88ce3405dff93"
 
-is-buffer@^1.1.5:
+is-buffer@^1.1.4, is-buffer@^1.1.5:
   version "1.1.6"
   resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
 
@@ -3956,6 +4171,10 @@ is-date-object@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16"
 
+is-decimal@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.2.tgz#894662d6a8709d307f3a276ca4339c8fa5dff0ff"
+
 is-descriptor@^0.1.0:
   version "0.1.6"
   resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca"
@@ -4042,6 +4261,16 @@ is-glob@^4.0.0:
   dependencies:
     is-extglob "^2.1.1"
 
+is-hexadecimal@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.2.tgz#b6e710d7d07bb66b98cb8cece5c9b4921deeb835"
+
+is-nan@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.2.1.tgz#9faf65b6fb6db24b7f5c0628475ea71f988401e2"
+  dependencies:
+    define-properties "^1.1.1"
+
 is-number-object@^1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.3.tgz#f265ab89a9f445034ef6aff15a8f00b00f551799"
@@ -4062,7 +4291,7 @@ is-number@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff"
 
-is-obj@^1.0.1:
+is-obj@^1.0.0, is-obj@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
 
@@ -4088,7 +4317,7 @@ is-path-inside@^1.0.0:
   dependencies:
     path-is-inside "^1.0.1"
 
-is-plain-obj@^1.0.0:
+is-plain-obj@^1.0.0, is-plain-obj@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
 
@@ -4152,6 +4381,12 @@ is-symbol@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572"
 
+is-text-path@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-text-path/-/is-text-path-1.0.1.tgz#4e1aa0fb51bfbcb3e92688001397202c1775b66e"
+  dependencies:
+    text-extensions "^1.0.0"
+
 is-typedarray@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
@@ -4160,10 +4395,18 @@ is-utf8@^0.2.0:
   version "0.2.1"
   resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
 
+is-whitespace-character@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.2.tgz#ede53b4c6f6fb3874533751ec9280d01928d03ed"
+
 is-windows@^1.0.1, is-windows@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
 
+is-word-character@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.1.tgz#5a03fa1ea91ace8a6eb0c7cd770eb86d65c8befb"
+
 is-wsl@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
@@ -4590,6 +4833,10 @@ jsesc@~0.5.0:
   version "0.5.0"
   resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
 
+json-parse-better-errors@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
+
 json-schema-traverse@^0.3.0:
   version "0.3.1"
   resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340"
@@ -4630,6 +4877,10 @@ jsonify@~0.0.0:
   version "0.0.0"
   resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
 
+jsonparse@^1.2.0:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
+
 jsprim@^1.2.2:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
@@ -4645,6 +4896,10 @@ jsx-ast-utils@^2.0.0, jsx-ast-utils@^2.0.1:
   dependencies:
     array-includes "^3.0.3"
 
+kebab-case@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/kebab-case/-/kebab-case-1.0.0.tgz#3f9e4990adcad0c686c0e701f7645868f75f91eb"
+
 keymaster@1.6.2:
   version "1.6.2"
   resolved "https://registry.yarnpkg.com/keymaster/-/keymaster-1.6.2.tgz#e1ae54d0ea9488f9f60b66b668f02e9a1946c6eb"
@@ -4790,6 +5045,15 @@ load-json-file@^2.0.0:
     pify "^2.0.0"
     strip-bom "^3.0.0"
 
+load-json-file@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b"
+  dependencies:
+    graceful-fs "^4.1.2"
+    parse-json "^4.0.0"
+    pify "^3.0.0"
+    strip-bom "^3.0.0"
+
 loader-runner@^2.3.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2"
@@ -4822,6 +5086,34 @@ lodash-es@^4.2.0, lodash-es@^4.2.1:
   version "4.17.4"
   resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.4.tgz#dcc1d7552e150a0640073ba9cb31d70f032950e7"
 
+lodash._basecopy@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36"
+
+lodash._basetostring@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz#d1861d877f824a52f669832dcaf3ee15566a07d5"
+
+lodash._basevalues@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz#5b775762802bde3d3297503e26300820fdf661b7"
+
+lodash._getnative@^3.0.0:
+  version "3.9.1"
+  resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
+
+lodash._isiterateecall@^3.0.0:
+  version "3.0.9"
+  resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c"
+
+lodash._reinterpolate@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
+
+lodash._root@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692"
+
 lodash.camelcase@^4.3.0:
   version "4.3.0"
   resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
@@ -4830,22 +5122,73 @@ lodash.clonedeep@^4.5.0:
   version "4.5.0"
   resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
 
+lodash.cond@^4.3.0:
+  version "4.5.2"
+  resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5"
+
+lodash.escape@^3.0.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-3.2.0.tgz#995ee0dc18c1b48cc92effae71a10aab5b487698"
+  dependencies:
+    lodash._root "^3.0.0"
+
 lodash.flattendeep@^4.4.0:
   version "4.4.0"
   resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2"
 
+lodash.isarguments@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
+
+lodash.isarray@^3.0.0:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55"
+
 lodash.isequal@4.5.0:
   version "4.5.0"
   resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
 
+lodash.keys@^3.0.0:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a"
+  dependencies:
+    lodash._getnative "^3.0.0"
+    lodash.isarguments "^3.0.0"
+    lodash.isarray "^3.0.0"
+
 lodash.memoize@^4.1.2:
   version "4.1.2"
   resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
 
+lodash.restparam@^3.0.0:
+  version "3.6.1"
+  resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805"
+
 lodash.sortby@^4.7.0:
   version "4.7.0"
   resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
 
+lodash.template@^3.6.1:
+  version "3.6.2"
+  resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-3.6.2.tgz#f8cdecc6169a255be9098ae8b0c53d378931d14f"
+  dependencies:
+    lodash._basecopy "^3.0.0"
+    lodash._basetostring "^3.0.0"
+    lodash._basevalues "^3.0.0"
+    lodash._isiterateecall "^3.0.0"
+    lodash._reinterpolate "^3.0.0"
+    lodash.escape "^3.0.0"
+    lodash.keys "^3.0.0"
+    lodash.restparam "^3.0.0"
+    lodash.templatesettings "^3.0.0"
+
+lodash.templatesettings@^3.0.0:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz#fb307844753b66b9f1afa54e262c745307dba8e5"
+  dependencies:
+    lodash._reinterpolate "^3.0.0"
+    lodash.escape "^3.0.0"
+
 lodash.topath@4.5.2:
   version "4.5.2"
   resolved "https://registry.yarnpkg.com/lodash.topath/-/lodash.topath-4.5.2.tgz#3616351f3bba61994a0931989660bd03254fd009"
@@ -4862,6 +5205,14 @@ lodash@4.17.4, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.2, l
   version "4.17.4"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
 
+lodash@^3.3.1:
+  version "3.10.1"
+  resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
+
+lodash@^4.0.0, lodash@^4.1.0:
+  version "4.17.5"
+  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511"
+
 log-symbols@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18"
@@ -4889,6 +5240,10 @@ loglevelnext@^1.0.1:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/loglevelnext/-/loglevelnext-1.0.3.tgz#0f69277e73bbbf2cd61b94d82313216bf87ac66e"
 
+longest-streak@^2.0.1:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.2.tgz#2421b6ba939a443bb9ffebf596585a50b4c38e2e"
+
 longest@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
@@ -4941,12 +5296,24 @@ map-obj@^1.0.0, map-obj@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d"
 
+map-obj@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-2.0.0.tgz#a65cd29087a92598b8791257a523e021222ac1f9"
+
 map-visit@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f"
   dependencies:
     object-visit "^1.0.0"
 
+markdown-escapes@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.2.tgz#e639cbde7b99c841c0bacc8a07982873b46d2122"
+
+markdown-table@^1.1.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-1.1.2.tgz#c78db948fa879903a41bce522e3b96f801c63786"
+
 math-expression-evaluator@^1.2.14:
   version "1.2.17"
   resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac"
@@ -4958,6 +5325,39 @@ md5.js@^1.3.4:
     hash-base "^3.0.0"
     inherits "^2.0.1"
 
+mdast-util-compact@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/mdast-util-compact/-/mdast-util-compact-1.0.1.tgz#cdb5f84e2b6a2d3114df33bd05d9cb32e3c4083a"
+  dependencies:
+    unist-util-modify-children "^1.0.0"
+    unist-util-visit "^1.1.0"
+
+mdast-util-definitions@^1.2.0:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-1.2.2.tgz#673f4377c3e23d3de7af7a4fe2214c0e221c5ac7"
+  dependencies:
+    unist-util-visit "^1.0.0"
+
+mdast-util-to-hast@^2.0.0:
+  version "2.5.0"
+  resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-2.5.0.tgz#f087844d255c7540f36906da30ba106c0ee5ee2f"
+  dependencies:
+    collapse-white-space "^1.0.0"
+    detab "^2.0.0"
+    mdast-util-definitions "^1.2.0"
+    mdurl "^1.0.1"
+    trim "0.0.1"
+    trim-lines "^1.0.0"
+    unist-builder "^1.0.1"
+    unist-util-generated "^1.1.0"
+    unist-util-position "^3.0.0"
+    unist-util-visit "^1.1.0"
+    xtend "^4.0.1"
+
+mdurl@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
+
 media-typer@0.3.0:
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
@@ -4975,7 +5375,7 @@ memory-fs@^0.4.0, memory-fs@~0.4.1:
     errno "^0.1.3"
     readable-stream "^2.0.1"
 
-meow@^3.3.0:
+meow@^3.1.0, meow@^3.3.0, meow@^3.7.0:
   version "3.7.0"
   resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb"
   dependencies:
@@ -4990,6 +5390,20 @@ meow@^3.3.0:
     redent "^1.0.0"
     trim-newlines "^1.0.0"
 
+meow@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/meow/-/meow-4.0.0.tgz#fd5855dd008db5b92c552082db1c307cba20b29d"
+  dependencies:
+    camelcase-keys "^4.0.0"
+    decamelize-keys "^1.0.0"
+    loud-rejection "^1.0.0"
+    minimist "^1.1.3"
+    minimist-options "^3.0.1"
+    normalize-package-data "^2.3.4"
+    read-pkg-up "^3.0.0"
+    redent "^2.0.0"
+    trim-newlines "^2.0.0"
+
 merge-descriptors@1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
@@ -5100,6 +5514,13 @@ minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4:
   dependencies:
     brace-expansion "^1.1.7"
 
+minimist-options@^3.0.1:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-3.0.2.tgz#fba4c8191339e13ecf4d61beb03f070103f3d954"
+  dependencies:
+    arrify "^1.0.1"
+    is-plain-obj "^1.1.0"
+
 minimist@0.0.8:
   version "0.0.8"
   resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
@@ -5140,6 +5561,10 @@ mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1:
   dependencies:
     minimist "0.0.8"
 
+modify-values@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022"
+
 move-concurrently@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
@@ -5304,7 +5729,7 @@ nopt@^4.0.1:
     abbrev "1"
     osenv "^0.1.4"
 
-normalize-package-data@^2.3.2, normalize-package-data@^2.3.4:
+normalize-package-data@^2.3.0, normalize-package-data@^2.3.2, normalize-package-data@^2.3.4:
   version "2.4.0"
   resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f"
   dependencies:
@@ -5618,6 +6043,17 @@ parse-asn1@^5.0.0:
     evp_bytestokey "^1.0.0"
     pbkdf2 "^3.0.3"
 
+parse-entities@^1.0.2, parse-entities@^1.1.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-1.1.1.tgz#8112d88471319f27abae4d64964b122fe4e1b890"
+  dependencies:
+    character-entities "^1.0.0"
+    character-entities-legacy "^1.0.0"
+    character-reference-invalid "^1.0.0"
+    is-alphanumerical "^1.0.0"
+    is-decimal "^1.0.0"
+    is-hexadecimal "^1.0.0"
+
 parse-glob@^3.0.4:
   version "3.0.4"
   resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c"
@@ -5633,6 +6069,13 @@ parse-json@^2.2.0:
   dependencies:
     error-ex "^1.2.0"
 
+parse-json@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
+  dependencies:
+    error-ex "^1.3.1"
+    json-parse-better-errors "^1.0.1"
+
 parse-passwd@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6"
@@ -6150,6 +6593,10 @@ prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.8,
     loose-envify "^1.3.1"
     object-assign "^4.1.1"
 
+property-information@^3.0.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/property-information/-/property-information-3.2.0.tgz#fd1483c8fbac61808f5fe359e7693a1f48a58331"
+
 proxy-addr@~2.0.3:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341"
@@ -6202,7 +6649,7 @@ punycode@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d"
 
-q@^1.1.2:
+q@^1.1.2, q@^1.4.1:
   version "1.5.1"
   resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
 
@@ -6237,6 +6684,10 @@ querystringify@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-1.0.0.tgz#6286242112c5b712fa654e526652bf6a13ff05cb"
 
+quick-lru@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8"
+
 raf@^3.4.0:
   version "3.4.0"
   resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.0.tgz#a28876881b4bc2ca9117d4138163ddb80f781575"
@@ -6280,6 +6731,10 @@ raw-body@2.3.2:
     iconv-lite "0.4.19"
     unpipe "1.0.0"
 
+raw-loader@0.5.1:
+  version "0.5.1"
+  resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa"
+
 rc@^1.1.7:
   version "1.2.2"
   resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.2.tgz#d8ce9cb57e8d64d9c7badd9876c7c34cbe3c7077"
@@ -6493,7 +6948,14 @@ read-pkg-up@^2.0.0:
     find-up "^2.0.0"
     read-pkg "^2.0.0"
 
-read-pkg@^1.0.0:
+read-pkg-up@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07"
+  dependencies:
+    find-up "^2.0.0"
+    read-pkg "^3.0.0"
+
+read-pkg@^1.0.0, read-pkg@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28"
   dependencies:
@@ -6509,6 +6971,14 @@ read-pkg@^2.0.0:
     normalize-package-data "^2.3.2"
     path-type "^2.0.0"
 
+read-pkg@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389"
+  dependencies:
+    load-json-file "^4.0.0"
+    normalize-package-data "^2.3.2"
+    path-type "^3.0.0"
+
 "readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.6, readable-stream@^2.2.9:
   version "2.3.5"
   resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.5.tgz#b4f85003a938cbb6ecbce2a124fb1012bd1a838d"
@@ -6558,6 +7028,13 @@ redent@^1.0.0:
     indent-string "^2.1.0"
     strip-indent "^1.0.1"
 
+redent@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/redent/-/redent-2.0.0.tgz#c1b2007b42d57eb1389079b3c8333639d5e1ccaa"
+  dependencies:
+    indent-string "^3.0.0"
+    strip-indent "^2.0.0"
+
 reduce-css-calc@^1.2.6:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716"
@@ -6664,6 +7141,63 @@ relateurl@0.2.x:
   version "0.2.7"
   resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
 
+remark-parse@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-5.0.0.tgz#4c077f9e499044d1d5c13f80d7a98cf7b9285d95"
+  dependencies:
+    collapse-white-space "^1.0.2"
+    is-alphabetical "^1.0.0"
+    is-decimal "^1.0.0"
+    is-whitespace-character "^1.0.0"
+    is-word-character "^1.0.0"
+    markdown-escapes "^1.0.0"
+    parse-entities "^1.1.0"
+    repeat-string "^1.5.4"
+    state-toggle "^1.0.0"
+    trim "0.0.1"
+    trim-trailing-lines "^1.0.0"
+    unherit "^1.0.4"
+    unist-util-remove-position "^1.0.0"
+    vfile-location "^2.0.0"
+    xtend "^4.0.1"
+
+remark-react@4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/remark-react/-/remark-react-4.0.1.tgz#82926c3b445c2e3d1034b354edc292f37369f823"
+  dependencies:
+    hast-to-hyperscript "^3.0.0"
+    hast-util-sanitize "^1.0.0"
+    mdast-util-to-hast "^2.0.0"
+    standard-changelog "^0.0.1"
+    xtend "^4.0.1"
+
+remark-stringify@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-5.0.0.tgz#336d3a4d4a6a3390d933eeba62e8de4bd280afba"
+  dependencies:
+    ccount "^1.0.0"
+    is-alphanumeric "^1.0.0"
+    is-decimal "^1.0.0"
+    is-whitespace-character "^1.0.0"
+    longest-streak "^2.0.1"
+    markdown-escapes "^1.0.0"
+    markdown-table "^1.1.0"
+    mdast-util-compact "^1.0.0"
+    parse-entities "^1.0.2"
+    repeat-string "^1.5.4"
+    state-toggle "^1.0.0"
+    stringify-entities "^1.0.1"
+    unherit "^1.0.4"
+    xtend "^4.0.1"
+
+remark@9.0.0:
+  version "9.0.0"
+  resolved "https://registry.yarnpkg.com/remark/-/remark-9.0.0.tgz#c5cfa8ec535c73a67c4b0f12bfdbd3a67d8b2f60"
+  dependencies:
+    remark-parse "^5.0.0"
+    remark-stringify "^5.0.0"
+    unified "^6.0.0"
+
 remove-trailing-separator@^1.0.1:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
@@ -6682,7 +7216,7 @@ repeat-element@^1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a"
 
-repeat-string@^1.5.2, repeat-string@^1.6.1:
+repeat-string@^1.5.2, repeat-string@^1.5.4, repeat-string@^1.6.1:
   version "1.6.1"
   resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
 
@@ -6692,6 +7226,10 @@ repeating@^2.0.0:
   dependencies:
     is-finite "^1.0.0"
 
+replace-ext@1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb"
+
 request-promise-core@1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.1.tgz#3eee00b2c5aa83239cfb04c5700da36f81cd08b6"
@@ -6939,7 +7477,7 @@ selfsigned@^1.9.1:
   dependencies:
     node-forge "0.6.33"
 
-"semver@2 || 3 || 4 || 5", semver@5.5.0, semver@^5.0.1, semver@^5.3.0:
+"semver@2 || 3 || 4 || 5", semver@5.5.0, semver@^5.0.1, semver@^5.3.0, semver@^5.5.0:
   version "5.5.0"
   resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
 
@@ -7197,6 +7735,12 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
   version "0.6.1"
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
 
+space-separated-tokens@^1.0.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.2.tgz#e95ab9d19ae841e200808cd96bc7bd0adbbb3412"
+  dependencies:
+    trim "0.0.1"
+
 spdx-correct@~1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40"
@@ -7240,6 +7784,18 @@ split-string@^3.0.1, split-string@^3.0.2:
   dependencies:
     extend-shallow "^2.0.1"
 
+split2@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/split2/-/split2-1.1.1.tgz#162d9b18865f02ab2f2ad9585522db9b54c481f9"
+  dependencies:
+    through2 "~2.0.0"
+
+split@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9"
+  dependencies:
+    through "2"
+
 sprintf-js@~1.0.2:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
@@ -7272,6 +7828,21 @@ staged-git-files@0.0.4:
   version "0.0.4"
   resolved "https://registry.yarnpkg.com/staged-git-files/-/staged-git-files-0.0.4.tgz#d797e1b551ca7a639dec0237dc6eb4bb9be17d35"
 
+standard-changelog@^0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/standard-changelog/-/standard-changelog-0.0.1.tgz#b367266fce05ca39ef2bbc09c0d24ddbd4191891"
+  dependencies:
+    add-stream "^1.0.0"
+    conventional-changelog-angular "^0.1.0"
+    conventional-changelog-core "^0.0.2"
+    lodash "^4.1.0"
+    meow "^3.7.0"
+    tempfile "^1.1.1"
+
+state-toggle@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.1.tgz#c3cb0974f40a6a0f8e905b96789eb41afa1cde3a"
+
 static-extend@^0.1.1:
   version "0.1.2"
   resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"
@@ -7355,6 +7926,15 @@ string_decoder@~1.0.3:
   dependencies:
     safe-buffer "~5.1.0"
 
+stringify-entities@^1.0.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-1.3.1.tgz#b150ec2d72ac4c1b5f324b51fb6b28c9cdff058c"
+  dependencies:
+    character-entities-html4 "^1.0.0"
+    character-entities-legacy "^1.0.0"
+    is-alphanumerical "^1.0.0"
+    is-hexadecimal "^1.0.0"
+
 stringify-object@^3.2.0:
   version "3.2.1"
   resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.2.1.tgz#2720c2eff940854c819f6ee252aaeb581f30624d"
@@ -7399,6 +7979,10 @@ strip-indent@^1.0.1:
   dependencies:
     get-stdin "^4.0.1"
 
+strip-indent@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68"
+
 strip-json-comments@~2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
@@ -7494,6 +8078,13 @@ tar@^2.2.1:
     fstream "^1.0.2"
     inherits "2"
 
+tempfile@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/tempfile/-/tempfile-1.1.1.tgz#5bcc4eaecc4ab2c707d8bc11d99ccc9a2cb287f2"
+  dependencies:
+    os-tmpdir "^1.0.0"
+    uuid "^2.0.1"
+
 test-exclude@^4.1.1:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.1.1.tgz#4d84964b0966b0087ecc334a2ce002d3d9341e26"
@@ -7504,6 +8095,10 @@ test-exclude@^4.1.1:
     read-pkg-up "^1.0.1"
     require-main-filename "^1.0.1"
 
+text-extensions@^1.0.0:
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.7.0.tgz#faaaba2625ed746d568a23e4d0aacd9bf08a8b39"
+
 text-table@0.2.0, text-table@~0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
@@ -7512,14 +8107,14 @@ throat@^4.0.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a"
 
-through2@^2.0.0:
+through2@^2.0.0, through2@~2.0.0:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be"
   dependencies:
     readable-stream "^2.1.5"
     xtend "~4.0.1"
 
-through@^2.3.6:
+through@2, "through@>=2.2.7 <3", through@^2.3.6:
   version "2.3.8"
   resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
 
@@ -7592,14 +8187,38 @@ tr46@^1.0.0:
   dependencies:
     punycode "^2.1.0"
 
+trim-lines@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-1.1.1.tgz#da738ff58fa74817588455e30b11b85289f2a396"
+
 trim-newlines@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
 
+trim-newlines@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-2.0.0.tgz#b403d0b91be50c331dfc4b82eeceb22c3de16d20"
+
+trim-off-newlines@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3"
+
 trim-right@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
 
+trim-trailing-lines@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.0.tgz#7aefbb7808df9d669f6da2e438cac8c46ada7684"
+
+trim@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd"
+
+trough@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.2.tgz#7f1663ec55c480139e2de5e486c6aef6cc24a535"
+
 tryer@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.0.tgz#027b69fa823225e551cace3ef03b11f6ab37c1d7"
@@ -7727,6 +8346,25 @@ underscore@~1.4.4:
   version "1.4.4"
   resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.4.4.tgz#61a6a32010622afa07963bf325203cf12239d604"
 
+unherit@^1.0.4:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.0.tgz#6b9aaedfbf73df1756ad9e316dd981885840cd7d"
+  dependencies:
+    inherits "^2.0.1"
+    xtend "^4.0.1"
+
+unified@^6.0.0:
+  version "6.1.6"
+  resolved "https://registry.yarnpkg.com/unified/-/unified-6.1.6.tgz#5ea7f807a0898f1f8acdeefe5f25faa010cc42b1"
+  dependencies:
+    bail "^1.0.0"
+    extend "^3.0.0"
+    is-plain-obj "^1.1.0"
+    trough "^1.0.0"
+    vfile "^2.0.0"
+    x-is-function "^1.0.4"
+    x-is-string "^0.1.0"
+
 union-value@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4"
@@ -7762,6 +8400,46 @@ unique-slug@^2.0.0:
   dependencies:
     imurmurhash "^0.1.4"
 
+unist-builder@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-1.0.2.tgz#8c3b9903ef64bcfb117dd7cf6a5d98fc1b3b27b6"
+  dependencies:
+    object-assign "^4.1.0"
+
+unist-util-generated@^1.1.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-1.1.1.tgz#99f16c78959ac854dee7c615c291924c8bf4de7f"
+
+unist-util-is@^2.0.0, unist-util-is@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-2.1.1.tgz#0c312629e3f960c66e931e812d3d80e77010947b"
+
+unist-util-modify-children@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/unist-util-modify-children/-/unist-util-modify-children-1.1.1.tgz#66d7e6a449e6f67220b976ab3cb8b5ebac39e51d"
+  dependencies:
+    array-iterate "^1.0.0"
+
+unist-util-position@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-3.0.0.tgz#e6e1e03eeeb81c5e1afe553e8d4adfbd7c0d8f82"
+
+unist-util-remove-position@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-1.1.1.tgz#5a85c1555fc1ba0c101b86707d15e50fa4c871bb"
+  dependencies:
+    unist-util-visit "^1.1.0"
+
+unist-util-stringify-position@^1.0.0, unist-util-stringify-position@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-1.1.1.tgz#3ccbdc53679eed6ecf3777dd7f5e3229c1b6aa3c"
+
+unist-util-visit@^1.0.0, unist-util-visit@^1.1.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-1.3.0.tgz#41ca7c82981fd1ce6c762aac397fc24e35711444"
+  dependencies:
+    unist-util-is "^2.1.1"
+
 universalify@^0.1.0:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.1.tgz#fa71badd4437af4c148841e3b3b165f9e9e590b7"
@@ -7851,6 +8529,10 @@ utils-merge@1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
 
+uuid@^2.0.1:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a"
+
 uuid@^3.0.0, uuid@^3.0.1, uuid@^3.1.0:
   version "3.2.1"
   resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14"
@@ -7878,6 +8560,25 @@ verror@1.10.0:
     core-util-is "1.0.2"
     extsprintf "^1.2.0"
 
+vfile-location@^2.0.0:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-2.0.2.tgz#d3675c59c877498e492b4756ff65e4af1a752255"
+
+vfile-message@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-1.0.0.tgz#a6adb0474ea400fa25d929f1d673abea6a17e359"
+  dependencies:
+    unist-util-stringify-position "^1.1.1"
+
+vfile@^2.0.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/vfile/-/vfile-2.3.0.tgz#e62d8e72b20e83c324bc6c67278ee272488bf84a"
+  dependencies:
+    is-buffer "^1.1.4"
+    replace-ext "1.0.0"
+    unist-util-stringify-position "^1.0.0"
+    vfile-message "^1.0.0"
+
 vm-browserify@0.0.4:
   version "0.0.4"
   resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73"
@@ -8126,6 +8827,14 @@ ws@^4.0.0:
     async-limiter "~1.0.0"
     safe-buffer "~5.1.0"
 
+x-is-function@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/x-is-function/-/x-is-function-1.0.4.tgz#5d294dc3d268cbdd062580e0c5df77a391d1fa1e"
+
+x-is-string@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/x-is-string/-/x-is-string-0.1.0.tgz#474b50865af3a49a9c4657f05acd145458f77d82"
+
 xml-char-classes@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/xml-char-classes/-/xml-char-classes-1.0.0.tgz#64657848a20ffc5df583a42ad8a277b4512bbc4d"
@@ -8134,7 +8843,7 @@ xml-name-validator@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635"
 
-xtend@^4.0.0, xtend@~4.0.1:
+xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
 
index c8f3db1dc4bb40988587cb928f9430ee2b07b1e5..331c0b74f453684d9aa824b2778d71564a4b1f5b 100644 (file)
@@ -1102,7 +1102,7 @@ quality_profile.empty_comparison=The quality profiles are equal.
 quality_profiles.activate_more=Activate More
 quality_profiles.activate_more_rules=Activate More Rules
 quality_profiles.intro1=Quality Profiles are collections of rules to apply during an analysis.
-quality_profiles.intro2=For each language there is a default profile. All projects not explicitly assigned to some other profile will be analyzed with the default.
+quality_profiles.intro2=For each language there is a default profile. All projects not explicitly assigned to some other profile will be analyzed with the default. Ideally, all projects will use the same profile for a language.
 quality_profiles.list.projects=Projects
 quality_profiles.list.rules=Rules
 quality_profiles.list.updated=Updated
@@ -1227,7 +1227,6 @@ coding_rules.change_details=Change Details of Quality Profile
 coding_rules.create=Create
 coding_rules.create_custom_rule=Create Custom Rule
 coding_rules.custom_rule=Custom Rule
-coding_rules.custom_rule.title=This rule has been created through customization of a rule template
 coding_rules.custom_rule.activation_notice=Note: parameters of a custom rule are not customizable on rule activation, only during creation/edit.
 coding_rules.custom_rules=Custom Rules
 coding_rules.deactivate_in_quality_profile=Deactivate In Quality Profile