Browse Source

MMF-1319 Document SC specific features (#318)

* Adding pages to SonarCloud doc
* Add page for paid plan
* Adding documentation about integrations in SonarCloud
* Add page suggestions
* Add inline documentation
* Allow to reference SonarCloud links in the documentation
* Update the way to install the Bitbucket Cloud App since it is now available in the marketplace
tags/7.5
laurawacrenier 6 years ago
parent
commit
a1ae97a0d1
23 changed files with 586 additions and 33 deletions
  1. 35
    4
      server/sonar-docs/src/EmbedDocsSuggestions.json
  2. 25
    0
      server/sonar-docs/src/pages/analyze-a-project.md
  3. 79
    0
      server/sonar-docs/src/pages/integrations/bitbucketcloud.md
  4. 31
    0
      server/sonar-docs/src/pages/integrations/github.md
  5. 10
    0
      server/sonar-docs/src/pages/integrations/index.md
  6. 37
    0
      server/sonar-docs/src/pages/integrations/vsts.md
  7. 72
    0
      server/sonar-docs/src/pages/sonarcloud-pricing.md
  8. 5
    0
      server/sonar-docs/src/tooltips/organizations/organization.md
  9. 5
    1
      server/sonar-web/src/main/js/apps/account/organizations/CreateOrganizationForm.tsx
  10. 10
    8
      server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.js
  11. 3
    0
      server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationPage-test.js.snap
  12. 7
    1
      server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationStep.js
  13. 1
    1
      server/sonar-web/src/main/js/apps/tutorials/onboarding/Step.js
  14. 111
    2
      server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/OrganizationStep-test.js.snap
  15. 11
    9
      server/sonar-web/src/main/js/components/docs/DocLink.tsx
  16. 4
    2
      server/sonar-web/src/main/js/components/docs/DocMarkdownBlock.tsx
  17. 1
    1
      server/sonar-web/src/main/js/components/docs/DocTooltip.tsx
  18. 44
    0
      server/sonar-web/src/main/js/components/docs/DocTooltipLink.tsx
  19. 8
    0
      server/sonar-web/src/main/js/components/docs/__tests__/DocLink-test.tsx
  20. 30
    0
      server/sonar-web/src/main/js/components/docs/__tests__/DocTooltipLink-test.tsx
  21. 21
    4
      server/sonar-web/src/main/js/components/docs/__tests__/__snapshots__/DocLink-test.tsx.snap
  22. 1
    0
      server/sonar-web/src/main/js/components/docs/__tests__/__snapshots__/DocTooltip-test.tsx.snap
  23. 35
    0
      server/sonar-web/src/main/js/components/docs/__tests__/__snapshots__/DocTooltipLink-test.tsx.snap

+ 35
- 4
server/sonar-docs/src/EmbedDocsSuggestions.json View File

@@ -25,7 +25,13 @@
],
"custom_measures": [],
"custom_metrics": [],
"global_permissions": [],
"global_permissions": [
{
"link": "/documentation/organizations/manage-team",
"text": "Manage a Team",
"scope": "sonarcloud"
}
],
"issues": [
{
"link": "/documentation/keyboard-shortcuts",
@@ -41,6 +47,13 @@
}
],
"organization_projects": [
{
"link": "/documentation/organizations/manage-team",
"text": "Manage a Team",
"scope": "sonarcloud"
}
],
"organization_space": [
{
"link": "/documentation/organizations/index",
"text": "Organizations",
@@ -58,8 +71,20 @@
"project_activity": [],
"project_quality_gate": [],
"project_quality_profiles": [],
"projects_management": [],
"projects": [],
"projects_management": [
{
"link": "/documentation/analyze-a-project",
"text": "Analyze a Project",
"scope": "sonarcloud"
}
],
"projects": [
{
"link": "/documentation/analyze-a-project",
"text": "Analyze a Project",
"scope": "sonarcloud"
}
],
"quality_gates": [
{
"link": "/documentation/fixing-the-water-leak",
@@ -74,7 +99,13 @@
],
"settings": [],
"system_info": [],
"user_groups": [],
"user_groups": [
{
"link": "/documentation/organizations/manage-team",
"text": "Manage a Team",
"scope": "sonarcloud"
}
],
"users": [],
"webhooks": []
}

+ 25
- 0
server/sonar-docs/src/pages/analyze-a-project.md View File

@@ -0,0 +1,25 @@
---
title: Analyze a Project
scope: sonarcloud
---

## Prepare your organization

A project must belong to an [organization](/organizations/index). Create one if you intend to collaborate with your team mates, or use your personal organization for test purposes.

** /!\ Important note for private code:** Newly created organizations and personal organizations are under a free plan by default. This means projects analyzed on these organizations are public by default: the code will be browsable by anyone. If you want private projects, you should [upgrade your organization to a paid plan](/sonarcloud-pricing) in the "Administration > Billing" page of your organization.

Find the key of your organization, you will need it at later stages. It can be found on the top right corner of your organization's header.

## Run analysis

SonarCloud currently does not trigger analyses automatically - this feature will come in a near future. Currently, it's up to you to launch them inside your
existing CI scripts.

Depending on which cloud solution you are using for your developments, you can rely on dedicated integrations to help you:

* VSTS: [read our dedicated documentation](/integrations/vsts)
* Bitbucket Cloud: [read our dedicated documentation](/integrations/bitbucketcloud)
* GitHub: [read our dedicated documentation](/integrations/github)

If you are not using those solutions, you will have to find out what command to execute to run the analysis. Our [tutorial](/#sonarcloud#/onboarding) will help you on this.

+ 79
- 0
server/sonar-docs/src/pages/integrations/bitbucketcloud.md View File

@@ -0,0 +1,79 @@
---
title: Integration with Bitbucket Cloud
scope: sonarcloud
---

## Authentication

You can connect to SonarCloud using your Bitbucket Cloud account. On the [login page](/#sonarcloud#/sessions/new), just click on the "Log in with Bitbucket" button.

## Install SonarCloud add-on for Bitbucket Cloud

Our Bitbucket Cloud application allows users to automate the SonarCloud analysis with Pipelines. It also allows users to view their SonarCloud metrics directly on Bitbucket Cloud via our Code Quality widget and the decoration of pull-requests.

In Bitbucket Cloud, go to your team's "Settings > Find integrations" page, search for "SonarCloud" in the "Code Quality" category and click on "Add" to install the application.

## Analyzing with Pipelines

SonarCloud integrates with Bitbucket Pipelines to make it easier to trigger analyses. Follow the steps:

1. On SonarCloud, open and follow the "New Project" tutorial available from the `+` icon available at the top right part of the screen. You can copy-paste the command line displayed at the end.

2. On Bitbucket Cloud, go to the "Settings > Pipelines > Environment variables" page of your team, and add a new SONAR_TOKEN variable that contains the value of the SonarCloud token (something like `9ad01c85336b265406fa6554a9a681a4b281135f`) which you created during the [tutorial](/#sonarcloud#/onboarding) (and which is available inside the command line that you copy-pasted). **Make sure that you click on the "Lock" icon to encrypt and hide this token.**

3. Inside the `bitbucket-pipelines.yml` file of your repository, copy the command line provided by the tutorial and replace the actual token by its variable name. For example, for a Java Maven-based project, you should have something like:

```
script:
-mvn sonar:sonar -Dsonar.host.url=https://sonarcloud.io -Dsonar.organization=my-team-org -Dsonar.login=$SONAR_TOKEN
```

When this change on `bitbucket-pipelines.yml` is committed and pushed, Pipelines should automatically run a new build and therefore trigger the analysis of the repository. Shortly after, your project will appear on SonarCloud in your organization.

4. Once you see your project in SonarCloud, go to the Bitbucket Cloud "Settings > SonarCloud" page of your repository and find it in the select box to link it.

From now on, everytime Pipelines triggers a build, SonarCloud will:

* Analyze every new branch that contains the change on the `bitbucket-pipelines.yml` file.
* Analyze and decorate every pull request based on such a branch.

## Quality widget

SonarCloud provides a widget that shows the current quality metrics of your project directly on the repository's Overview page on Bitbucket Cloud.

If you have configured the analysis with Pipelines as described above, you will see this widget on the "Overview" page of your repository.

If you haven't configured the analysis with Pipelines (maybe because you are using another CI tool), follow these few steps:

1. Go to the repository where you want to display the widget. On the "Overview" page, you should see an empty SonarCloud widget. Click on the link inside the empty widget or just go directly to your repository's "Settings > SonarCloud Settings".

2. If you're not already logged in on SonarCloud, do it by using the options provided there. You can log in with Bitbucket Cloud by clicking on the blue button, or click on "More options" to log in with GitHub or VSTS.

3. Once you're logged in on SonarCloud, you should see a dropdown allowing you to choose one of the projects you administer. Just choose the one you want to link to this Bitbucket repository and click "Save".

4. You can now go back to your repository's Overview page on Bitbucket and see the widget with all current SonarCloud metrics displayed.

If you want to hide this widget (e.g. because your repository is not analyzed on SonarCloud), you can go to the "Settings > SonarCloud" page of your repository and check "Hide repository overview widget".

## FAQ

**Do you have a sample project on Bitbucket Cloud?**
For the time being, you can take a look at this very simple JS project: [Sample project analysed on SonarCloud](https://bitbucket.org/bellingard/fab)

**Pipelines can't find sonar-scanner**
If you want to analyze a non-Java project (JS, TS, PHP, Python, Go, ...), you will need to download and install the [Scanner CLI](https://redirect.sonarsource.com/doc/install-configure-scanner.html) during the execution of your build prior to the actual code scan. You have two options:

* You can download it (with curl for instance) from the links available on the documentation page and unpack it (preferably in a cached folder for later reuse).
* On Node environments, you can rely on a [community NPM module](https://www.npmjs.com/package/sonarqube-scanner) to install it globally and therefore make it available in the PATH.

**I don't see the any quality information whereas I configured everything**
Make sure that your browser is not using some extensions like AdBlocks. They tend to break the integration of third-party applications in BitBucket Cloud.

## Upcoming features and improvements

There are various areas in which you can expect new features and improvements:

* Tighter integration with Pipelines (less parameters to pass on the CLI, availability of the scanner, ...)
* Pull request decoration with inline comments to show the issues within the PR
* Better and easier team onboarding
* Automatic analysis (i.e. no need to configure anything from Pipelines)

+ 31
- 0
server/sonar-docs/src/pages/integrations/github.md View File

@@ -0,0 +1,31 @@
---
title: Integration with GitHub
scope: sonarcloud
---

## Authentication

You can connect to SonarCloud using your GitHub account. On the [login page](/#sonarcloud#/sessions/new), just click on the "Log in with GitHub" button.

## Trigger analyses

SonarCloud currently does not trigger analyses automatically. It's up to you to launch them inside your
existing CI scripts. Please follow the [tutorial](/#sonarcloud#/onboarding) to get started.

If you are using Travis CI, the SonarCloud Travis Add-on will make it easier to activate analyses. Simply follow
the [guide to integrating with Travis CI](https://docs.travis-ci.com/user/sonarcloud/).

## Activating pull request decoration

To have your pull requests decorated by SonarCloud in GitHub, you need to [install the SonarCloud application](https://github.com/apps/sonarcloud) on your GitHub organization(s).

Once installed, there is nothing more to do if you are using the Travis Add-on. In any other case, you will need
to pass the following properties in your script during the analysis:

```
sonar.pullrequest.base=master
sonar.pullrequest.branch=feature/my-new-feature
sonar.pullrequest.key=5
sonar.pullrequest.provider=GitHub
sonar.pullrequest.github.repository=my-company/my-repo
```

+ 10
- 0
server/sonar-docs/src/pages/integrations/index.md View File

@@ -0,0 +1,10 @@
---
title: Integrations
scope: sonarcloud
---

SonarCloud integrates with the following cloud services to help developers get the most out of their code:

* [Integration with GitHub](/integrations/github)
* [Integration with Bitbucket Cloud](/integrations/bitbucketcloud)
* [Integration with VSTS](/integrations/vsts)

+ 37
- 0
server/sonar-docs/src/pages/integrations/vsts.md View File

@@ -0,0 +1,37 @@
---
title: Integration with VSTS
scope: sonarcloud
---


## Authentication

You can connect to SonarCloud using your VSTS account. On the [login page](/#sonarcloud#/sessions/new), just click on the "Log in with VSTS" button.

** /!\ Warning:** Only work and school VSTS accounts are authorized to login on SonarCloud.

## Install the SonarCloud VSTS extension

The SonarCloud VSTS extension brings everything you need to have your projects analyzed on SonarCloud
very quickly:
* Integration with the Build definitions to easily trigger the analysis
* Pull request decoration to get quick feedback on the code changes
* Widget to have the overview quality of your projects inside VSTS dashboards

Install [SonarCloud extension for VSTS](https://marketplace.visualstudio.com/items?itemName=SonarSource.sonarcloud)by clicking on the "Get it free" button.

Then follow the comprehensive [Microsoft lab on how to integrate VSTS with SonarCloud](https://aka.ms/sonarcloudlab).

## Quality Gate Status widget

You can monitor the Quality Gate status of your projects directly in your VSTS dashboard. Follow these simple steps to configure your widget:

1. Once the VSTS extension is installed and your project has been successfully analyzed, go to one of your VSTS dashboards (or create one). Click on the pen icon in the bottom right corner of the screen, and then on the "+" icon to add a widget.

2. In the list of widgets, select the "Code Quality" one and then click on the "Add" button. An empty widget is added to your dashboard.

3. You can then click on the widget's cogwheel icon to configure it.

* **For public projects:** you can simply select your project from the dropdown. A searchbar inside the dropdown will help you find it easily. Just select it and click on the "Save" button.

* **For private projects:** you'll have to log in using the links provided under the dropdown. Once logged in, your private projects will appear in the dropdown. Select the one you are interested in, and click on "Save".

+ 72
- 0
server/sonar-docs/src/pages/sonarcloud-pricing.md View File

@@ -0,0 +1,72 @@
---
title: Pricing
scope: sonarcloud
---

Subscribing to a paid plan on SonarCloud allows you to analyze unlimited private projects. You can make your code visible by members of your organization only.

You can activate the paid plan on the "Administration > Billing" page of your organization.

## How is SonarCloud priced?

SonarCloud is priced on a monthly basis per lines of code. You pay up front for a maximum number of lines of code to be analyzed in your organization.

Find your max LOC below to see what it will cost you per month:

| Up to lines of code | Price per month in € |
| ------------- |--------------:|
| 100k | 10 |
| 250k | 75 |
| 500k | 150 |
| 1M | 250 |
| 2M | 500 |
| 5M | 1'500 |
| 10M | 3'000 |
| 20M | 4'000 |


## What's the difference between the free and paid plans?

2 options are available to start using SonarCloud: free and paid plans. Both plans let you benefit from all the features available on SonarCloud.

*Free plan:*

* For open source projects
* Anyone can see your projects
* You choose who can edit your projects
* Unlimited lines of code (LOCs)

*Paid plan:*

* If you need (some or all) private projects
* You choose who can see your private projects
* You choose who can edit your projects
* Priced by lines of private code


## Can I try a private project on SonarCloud for free?

Your first 14 days are on us. You just have to upgrade your organization to a paid plan, and fill your credit card information to get started. After your trial, if you love it you can continue using SonarCloud and you will be charged for the plan you selected when you first started your free trial. You can cancel anytime.

## What is a Line of Code (LOC) on SonarCloud?

LOCs are computed by summing up the LOCs of each project analyzed in SonarCloud. The LOCs used for a project are the LOCs found during the most recent analysis of this project.


## How are Lines of Code (LOCs) counted towards billing?

Only LOC from your private projects are counted toward your maximum number of LOCs. If you are getting close to the threshold you will be notified to either upgrade your plan or reduce the number of LOCs in your projects.

## When will I be invoiced?

You will be invoiced once a month, the day of the month after your trial ends. For example if you start your free trial on January 1st, it will last till January 14th and you will be first billed on January 15th for your upcoming month, aka January 15th to February 15th.

## Can I stop using the service?

Yes, you can stop using SonarCloud anytime you want.

## Still have more questions?

Contact us [here](https://about.sonarcloud.io/contact).



+ 5
- 0
server/sonar-docs/src/tooltips/organizations/organization.md View File

@@ -0,0 +1,5 @@
An organization is a space where a team or a whole company can collaborate across many projects. A new organization is on a free plan by default, which means its projects will be public. Subscribe to paid plan to analyze projects privately

---

See also: [Organizations](/organizations/index) and [Pricing](/paid-plan)

+ 5
- 1
server/sonar-web/src/main/js/apps/account/organizations/CreateOrganizationForm.tsx View File

@@ -24,6 +24,7 @@ import * as PropTypes from 'prop-types';
import { createOrganization } from '../../organizations/actions';
import { Organization } from '../../../app/types';
import Modal from '../../../components/controls/Modal';
import DocTooltip from '../../../components/docs/DocTooltip';
import { translate } from '../../../helpers/l10n';
import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons';

@@ -126,7 +127,10 @@ class CreateOrganizationForm extends React.PureComponent<Props, State> {
return (
<Modal contentLabel="modal form" onRequestClose={this.props.onClose}>
<header className="modal-head">
<h2>{translate('my_account.create_organization')}</h2>
<h2>
{translate('my_account.create_organization')}
<DocTooltip className="spacer-left" doc="organizations/organization" />
</h2>
</header>

<form onSubmit={this.handleSubmit}>

+ 10
- 8
server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.js View File

@@ -23,6 +23,7 @@ import Helmet from 'react-helmet';
import { connect } from 'react-redux';
import OrganizationNavigation from '../navigation/OrganizationNavigation';
import NotFound from '../../../app/components/NotFound';
import Suggestions from '../../../app/components/embed-docs-modal/Suggestions';
import { fetchOrganization } from '../actions';
import { getOrganizationByKey } from '../../../store/rootReducer';
/*:: import type { Organization } from '../../../store/organizations/duck'; */
@@ -69,15 +70,15 @@ export class OrganizationPage extends React.PureComponent {
this.mounted = false;
}

updateOrganization = (organizationKey /*: string */) => {
stopLoading = () => {
if (this.mounted) {
this.setState({ loading: true });
this.setState({ loading: false });
}
this.props.fetchOrganization(organizationKey).then(() => {
if (this.mounted) {
this.setState({ loading: false });
}
});
};
updateOrganization = (organizationKey /*: string */) => {
this.setState({ loading: true });
this.props.fetchOrganization(organizationKey).then(this.stopLoading, this.stopLoading);
};

render() {
@@ -94,7 +95,8 @@ export class OrganizationPage extends React.PureComponent {
return (
<div>
<Helmet defaultTitle={organization.name} titleTemplate={'%s - ' + organization.name} />
<OrganizationNavigation organization={organization} location={this.props.location} />
<Suggestions suggestions="organization_space" />
<OrganizationNavigation location={this.props.location} organization={organization} />
{this.props.children}
</div>
);

+ 3
- 0
server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationPage-test.js.snap View File

@@ -21,6 +21,9 @@ exports[`smoke test 1`] = `
encodeSpecialCharacters={true}
titleTemplate="%s - Foo"
/>
<Suggestions
suggestions="organization_space"
/>
<OrganizationNavigation
organization={
Object {

+ 7
- 1
server/sonar-web/src/main/js/apps/tutorials/onboarding/OrganizationStep.js View File

@@ -23,6 +23,7 @@ import classNames from 'classnames';
import { sortBy } from 'lodash';
import Step from './Step';
import NewOrganizationForm from './NewOrganizationForm';
import DocTooltip from '../../../components/docs/DocTooltip';
import AlertSuccessIcon from '../../../components/icons-components/AlertSuccessIcon';
import { getOrganizations } from '../../../api/organizations';
import Select from '../../../components/controls/Select';
@@ -263,7 +264,12 @@ export default class OrganizationStep extends React.PureComponent {
renderForm={this.renderForm}
renderResult={this.renderResult}
stepNumber={this.props.stepNumber}
stepTitle={translate('onboarding.organization.header')}
stepTitle={
<span>
{translate('onboarding.organization.header')}
<DocTooltip className="little-spacer-left" doc="organizations/organization" />
</span>
}
/>
);
}

+ 1
- 1
server/sonar-web/src/main/js/apps/tutorials/onboarding/Step.js View File

@@ -29,7 +29,7 @@ type Props = {|
renderForm: () => React.Element<*>,
renderResult: () => ?React.Element<*>,
stepNumber: number,
stepTitle: string
stepTitle: React.Element<*> | string
|};
*/


+ 111
- 2
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/OrganizationStep-test.js.snap View File

@@ -21,7 +21,15 @@ exports[`works with existing organization 1`] = `
renderForm={[Function]}
renderResult={[Function]}
stepNumber={1}
stepTitle="onboarding.organization.header"
stepTitle={
<span>
onboarding.organization.header
<DocTooltip
className="little-spacer-left"
doc="organizations/organization"
/>
</span>
}
>
<div
className="boxed-group onboarding-step is-open"
@@ -35,7 +43,108 @@ exports[`works with existing organization 1`] = `
className="boxed-group-header"
>
<h2>
onboarding.organization.header
<span>
onboarding.organization.header
<DocTooltip
className="little-spacer-left"
doc="organizations/organization"
>
<HelpTooltip
className="little-spacer-left"
onShow={[Function]}
overlay={
<div
className="abs-width-300"
>
<LazyLoader
className="cut-margins"
isTooltip={true}
/>
</div>
}
>
<div
className="help-tooltip little-spacer-left"
>
<Tooltip
mouseLeaveDelay={0.25}
onShow={[Function]}
overlay={
<div
className="abs-width-300"
>
<LazyLoader
className="cut-margins"
isTooltip={true}
/>
</div>
}
>
<TooltipInner
mouseEnterDelay={0.1}
mouseLeaveDelay={0.25}
onShow={[Function]}
overlay={
<div
className="abs-width-300"
>
<LazyLoader
className="cut-margins"
isTooltip={true}
/>
</div>
}
>
<span
className="display-inline-flex-center"
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
<HelpIcon
fill="#b4b4b4"
size={12}
>
<Icon
size={12}
>
<svg
height={12}
style={
Object {
"clipRule": "evenodd",
"fillRule": "evenodd",
"strokeLinejoin": "round",
"strokeMiterlimit": "1.41421",
}
}
version="1.1"
viewBox="0 0 16 16"
width={12}
xmlSpace="preserve"
xmlnsXlink="http://www.w3.org/1999/xlink"
>
<g
transform="matrix(0.0364583,0,0,0.0364583,1,-0.166667)"
>
<path
d="M224,344L224,296C224,293.667 223.25,291.75 221.75,290.25C220.25,288.75 218.333,288 216,288L168,288C165.667,288 163.75,288.75 162.25,290.25C160.75,291.75 160,293.667 160,296L160,344C160,346.333 160.75,348.25 162.25,349.75C163.75,351.25 165.667,352 168,352L216,352C218.333,352 220.25,351.25 221.75,349.75C223.25,348.25 224,346.333 224,344ZM288,176C288,161.333 283.375,147.75 274.125,135.25C264.875,122.75 253.333,113.083 239.5,106.25C225.667,99.417 211.5,96 197,96C156.5,96 125.583,113.75 104.25,149.25C101.75,153.25 102.417,156.75 106.25,159.75L139.25,184.75C140.417,185.75 142,186.25 144,186.25C146.667,186.25 148.75,185.25 150.25,183.25C159.083,171.917 166.25,164.25 171.75,160.25C177.417,156.25 184.583,154.25 193.25,154.25C201.25,154.25 208.375,156.417 214.625,160.75C220.875,165.083 224,170 224,175.5C224,181.833 222.333,186.917 219,190.75C215.667,194.583 210,198.333 202,202C191.5,206.667 181.875,213.875 173.125,223.625C164.375,233.375 160,243.833 160,255L160,264C160,266.333 160.75,268.25 162.25,269.75C163.75,271.25 165.667,272 168,272L216,272C218.333,272 220.25,271.25 221.75,269.75C223.25,268.25 224,266.333 224,264C224,260.833 225.792,256.708 229.375,251.625C232.958,246.542 237.5,242.417 243,239.25C248.333,236.25 252.417,233.875 255.25,232.125C258.083,230.375 261.917,227.458 266.75,223.375C271.583,219.292 275.292,215.292 277.875,211.375C280.458,207.458 282.792,202.417 284.875,196.25C286.958,190.083 288,183.333 288,176ZM384,224C384,258.833 375.417,290.958 358.25,320.375C341.083,349.792 317.792,373.083 288.375,390.25C258.958,407.417 226.833,416 192,416C157.167,416 125.042,407.417 95.625,390.25C66.208,373.083 42.917,349.792 25.75,320.375C8.583,290.958 0,258.833 0,224C0,189.167 8.583,157.042 25.75,127.625C42.917,98.208 66.208,74.917 95.625,57.75C125.042,40.583 157.167,32 192,32C226.833,32 258.958,40.583 288.375,57.75C317.792,74.917 341.083,98.208 358.25,127.625C375.417,157.042 384,189.167 384,224Z"
style={
Object {
"fill": "#b4b4b4",
}
}
/>
</g>
</svg>
</Icon>
</HelpIcon>
</span>
</TooltipInner>
</Tooltip>
</div>
</HelpTooltip>
</DocTooltip>
</span>
</h2>
</div>
<div

+ 11
- 9
server/sonar-web/src/main/js/components/docs/DocLink.tsx View File

@@ -21,26 +21,28 @@ import * as React from 'react';
import { Link } from 'react-router';
import DetachIcon from '../../components/icons-components/DetachIcon';

const SONARCLOUD_LINK = '/#sonarcloud#/';

export default function DocLink(props: React.AnchorHTMLAttributes<HTMLAnchorElement>) {
const { children, href, ...other } = props;

if (href && href.startsWith('/')) {
let url = `/documentation/${href.substr(1)}`;
if (href.startsWith(SONARCLOUD_LINK)) {
url = `/${href.substr(SONARCLOUD_LINK.length)}`;
}
return (
<Link to={`/documentation/${href.substr(1)}`} {...other}>
<Link to={url} {...other}>
{children}
</Link>
);
}

return (
<>
<a className="text-middle" href={href} rel="noopener noreferrer" target="_blank" {...other}>
<span className="display-inline-flex-center">
<a href={href} rel="noopener noreferrer" target="_blank" {...other}>
{children}
</a>
<DetachIcon
className="text-middle text-muted little-spacer-left little-spacer-right"
size={12}
/>
</>
<DetachIcon className="text-muted little-spacer-left little-spacer-right" size={12} />
</span>
);
}

+ 4
- 2
server/sonar-web/src/main/js/components/docs/DocMarkdownBlock.tsx View File

@@ -24,6 +24,7 @@ import reactRenderer from 'remark-react';
import DocLink from './DocLink';
import DocParagraph from './DocParagraph';
import DocImg from './DocImg';
import DocTooltipLink from './DocTooltipLink';
import { separateFrontMatter } from '../../helpers/markdown';
import { isSonarCloud } from '../../helpers/system';

@@ -31,9 +32,10 @@ interface Props {
className?: string;
content: string | undefined;
displayH1?: boolean;
isTooltip?: boolean;
}

export default function DocMarkdownBlock({ className, content, displayH1 }: Props) {
export default function DocMarkdownBlock({ className, content, displayH1, isTooltip }: Props) {
const parsed = separateFrontMatter(content || '');
return (
<div className={classNames('markdown', className)}>
@@ -46,7 +48,7 @@ export default function DocMarkdownBlock({ className, content, displayH1 }: Prop
// do not render outer <div />
div: React.Fragment,
// use custom link to render documentation anchors
a: DocLink,
a: isTooltip ? DocTooltipLink : DocLink,
// used to handle `@include`
p: DocParagraph,
// use custom img tag to render documentation images

+ 1
- 1
server/sonar-web/src/main/js/components/docs/DocTooltip.tsx View File

@@ -82,7 +82,7 @@ export default class DocTooltip extends React.PureComponent<Props, State> {
{this.state.loading ? (
<i className="spinner" />
) : (
<DocMarkdownBlock className="cut-margins" content={this.state.content} />
<DocMarkdownBlock className="cut-margins" content={this.state.content} isTooltip={true} />
)}
</div>
);

+ 44
- 0
server/sonar-web/src/main/js/components/docs/DocTooltipLink.tsx View File

@@ -0,0 +1,44 @@
/*
* 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';
import DetachIcon from '../../components/icons-components/DetachIcon';

export default function DocTooltipLink(props: React.AnchorHTMLAttributes<HTMLAnchorElement>) {
const { children, href, ...other } = props;
return (
<span className="display-inline-flex-center">
{href && href.startsWith('/') ? (
<Link
rel="noopener noreferrer"
target="_blank"
to={`/documentation/${href.substr(1)}`}
{...other}>
{children}
</Link>
) : (
<a href={href} rel="noopener noreferrer" target="_blank" {...other}>
{children}
</a>
)}
<DetachIcon className="little-spacer-left little-spacer-right" size={12} />
</span>
);
}

+ 8
- 0
server/sonar-web/src/main/js/components/docs/__tests__/DocLink-test.tsx View File

@@ -25,6 +25,14 @@ it('should render simple link', () => {
expect(shallow(<DocLink href="http://sample.com" />)).toMatchSnapshot();
});

it('should render documentation link', () => {
expect(shallow(<DocLink href="/foo/bar" />)).toMatchSnapshot();
});

it('should render sonarcloud link', () => {
expect(shallow(<DocLink href="/#sonarcloud#/foo/bar" />)).toMatchSnapshot();
});

it.skip('should render documentation anchor', () => {
expect(shallow(<DocLink href="#quality-profiles" />)).toMatchSnapshot();
});

+ 30
- 0
server/sonar-web/src/main/js/components/docs/__tests__/DocTooltipLink-test.tsx View File

@@ -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 DocTooltipLink from '../DocTooltipLink';

it('should render simple link', () => {
expect(shallow(<DocTooltipLink href="http://sample.com" />)).toMatchSnapshot();
});

it('should render internal link', () => {
expect(shallow(<DocTooltipLink href="/foo/bar" />)).toMatchSnapshot();
});

+ 21
- 4
server/sonar-web/src/main/js/components/docs/__tests__/__snapshots__/DocLink-test.tsx.snap View File

@@ -1,16 +1,33 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render documentation link 1`] = `
<Link
onlyActiveOnIndex={false}
style={Object {}}
to="/documentation/foo/bar"
/>
`;

exports[`should render simple link 1`] = `
<React.Fragment>
<span
className="display-inline-flex-center"
>
<a
className="text-middle"
href="http://sample.com"
rel="noopener noreferrer"
target="_blank"
/>
<DetachIcon
className="text-middle text-muted little-spacer-left little-spacer-right"
className="text-muted little-spacer-left little-spacer-right"
size={12}
/>
</React.Fragment>
</span>
`;

exports[`should render sonarcloud link 1`] = `
<Link
onlyActiveOnIndex={false}
style={Object {}}
to="/foo/bar"
/>
`;

+ 1
- 0
server/sonar-web/src/main/js/components/docs/__tests__/__snapshots__/DocTooltip-test.tsx.snap View File

@@ -25,6 +25,7 @@ exports[`should render 2`] = `
<LazyLoader
className="cut-margins"
content="this is *bold* text"
isTooltip={true}
/>
</div>
}

+ 35
- 0
server/sonar-web/src/main/js/components/docs/__tests__/__snapshots__/DocTooltipLink-test.tsx.snap View File

@@ -0,0 +1,35 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render internal link 1`] = `
<span
className="display-inline-flex-center"
>
<Link
onlyActiveOnIndex={false}
rel="noopener noreferrer"
style={Object {}}
target="_blank"
to="/documentation/foo/bar"
/>
<DetachIcon
className="little-spacer-left little-spacer-right"
size={12}
/>
</span>
`;

exports[`should render simple link 1`] = `
<span
className="display-inline-flex-center"
>
<a
href="http://sample.com"
rel="noopener noreferrer"
target="_blank"
/>
<DetachIcon
className="little-spacer-left little-spacer-right"
size={12}
/>
</span>
`;

Loading…
Cancel
Save