aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorWouter Admiraal <wouter.admiraal@sonarsource.com>2021-05-28 10:00:03 +0200
committersonartech <sonartech@sonarsource.com>2021-06-10 20:03:26 +0000
commit06681cd89f2a24144ac5706ca97a07374f1b6c72 (patch)
treee12385fd74f5b33d2730394c52abca0e1bebd5bc /server
parenta9ff34a88b68a2f1df68bc1fe54ebf5b17f4e70a (diff)
downloadsonarqube-06681cd89f2a24144ac5706ca97a07374f1b6c72.tar.gz
sonarqube-06681cd89f2a24144ac5706ca97a07374f1b6c72.zip
SONAR-14873 Ease comprehension of PR Decoration settings form
Diffstat (limited to 'server')
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/AlmSpecificForm.tsx181
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBindingRenderer.tsx54
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/AlmSpecificForm-test.tsx18
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/AlmSpecificForm-test.tsx.snap1082
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/PRDecorationBindingRenderer-test.tsx.snap583
-rw-r--r--server/sonar-web/src/main/js/components/tutorials/utils.ts8
-rw-r--r--server/sonar-web/src/main/js/helpers/__tests__/urls-test.ts20
-rw-r--r--server/sonar-web/src/main/js/helpers/urls.ts10
8 files changed, 1293 insertions, 663 deletions
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/AlmSpecificForm.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/AlmSpecificForm.tsx
index d476039cc8b..4f4672e6bf1 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/AlmSpecificForm.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/AlmSpecificForm.tsx
@@ -20,52 +20,80 @@
import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import { Link } from 'react-router';
-import HelpTooltip from 'sonar-ui-common/components/controls/HelpTooltip';
import { Alert } from 'sonar-ui-common/components/ui/Alert';
import MandatoryFieldMarker from 'sonar-ui-common/components/ui/MandatoryFieldMarker';
import { translate } from 'sonar-ui-common/helpers/l10n';
import { ALM_DOCUMENTATION_PATHS } from '../../../../helpers/constants';
-import { AlmKeys, ProjectAlmBindingResponse } from '../../../../types/alm-settings';
+import { convertGithubApiUrlToLink, stripTrailingSlash } from '../../../../helpers/urls';
+import {
+ AlmKeys,
+ AlmSettingsInstance,
+ ProjectAlmBindingResponse
+} from '../../../../types/alm-settings';
import InputForBoolean from '../inputs/InputForBoolean';
export interface AlmSpecificFormProps {
alm: AlmKeys;
+ instances: AlmSettingsInstance[];
formData: T.Omit<ProjectAlmBindingResponse, 'alm'>;
onFieldChange: (id: keyof ProjectAlmBindingResponse, value: string | boolean) => void;
monorepoEnabled: boolean;
}
interface LabelProps {
- help?: boolean;
- helpParams?: T.Dict<string | JSX.Element>;
id: string;
optional?: boolean;
}
interface CommonFieldProps extends LabelProps {
+ help?: boolean;
+ helpParams?: T.Dict<string | JSX.Element>;
+ helpExample?: JSX.Element;
onFieldChange: (id: keyof ProjectAlmBindingResponse, value: string | boolean) => void;
propKey: keyof ProjectAlmBindingResponse;
}
+function renderFieldWrapper(
+ label: React.ReactNode,
+ input: React.ReactNode,
+ help?: React.ReactNode
+) {
+ return (
+ <div className="settings-definition">
+ <div className="settings-definition-left">
+ {label}
+ {help && <div className="markdown small spacer-top">{help}</div>}
+ </div>
+ <div className="settings-definition-right padded-top">{input}</div>
+ </div>
+ );
+}
+
+function renderHelp({ help, helpExample, helpParams, id }: CommonFieldProps) {
+ return (
+ help && (
+ <>
+ <FormattedMessage
+ defaultMessage={translate('settings.pr_decoration.binding.form', id, 'help')}
+ id={`settings.pr_decoration.binding.form.${id}.help`}
+ values={helpParams}
+ />
+ {helpExample && (
+ <div className="spacer-top nowrap">
+ {translate('example')}: <em>{helpExample}</em>
+ </div>
+ )}
+ </>
+ )
+ );
+}
+
function renderLabel(props: LabelProps) {
- const { help, helpParams, optional, id } = props;
+ const { optional, id } = props;
return (
- <label className="display-flex-center" htmlFor={id}>
+ <label className="h3" htmlFor={id}>
{translate('settings.pr_decoration.binding.form', id)}
{!optional && <MandatoryFieldMarker />}
- {help && (
- <HelpTooltip
- className="spacer-left"
- overlay={
- <FormattedMessage
- defaultMessage={translate('settings.pr_decoration.binding.form', id, 'help')}
- id={`settings.pr_decoration.binding.form.${id}.help`}
- values={helpParams}
- />
- }
- placement="right"
- />
- )}
</label>
);
}
@@ -77,19 +105,18 @@ function renderBooleanField(
}
) {
const { id, value, onFieldChange, propKey, inputExtra } = props;
- return (
- <div className="form-field">
- {renderLabel({ ...props, optional: true })}
- <div className="display-flex-center">
- <InputForBoolean
- isDefault={true}
- name={id}
- onChange={v => onFieldChange(propKey, v)}
- value={value}
- />
- {inputExtra}
- </div>
- </div>
+ return renderFieldWrapper(
+ renderLabel({ ...props, optional: true }),
+ <div className="display-flex-center big-spacer-top">
+ <InputForBoolean
+ isDefault={true}
+ name={id}
+ onChange={v => onFieldChange(propKey, v)}
+ value={value}
+ />
+ {inputExtra}
+ </div>,
+ renderHelp(props)
);
}
@@ -99,30 +126,31 @@ function renderField(
}
) {
const { id, propKey, value, onFieldChange } = props;
- return (
- <div className="form-field">
- {renderLabel(props)}
- <input
- className="input-super-large"
- id={id}
- maxLength={256}
- name={id}
- onChange={e => onFieldChange(propKey, e.currentTarget.value)}
- type="text"
- value={value}
- />
- </div>
+ return renderFieldWrapper(
+ renderLabel(props),
+ <input
+ className="input-super-large big-spacer-top"
+ id={id}
+ maxLength={256}
+ name={id}
+ onChange={e => onFieldChange(propKey, e.currentTarget.value)}
+ type="text"
+ value={value}
+ />,
+ renderHelp(props)
);
}
export default function AlmSpecificForm(props: AlmSpecificFormProps) {
const {
alm,
+ instances,
formData: { repository, slug, summaryCommentEnabled, monorepo },
monorepoEnabled
} = props;
let formFields: JSX.Element;
+ const instance = instances.find(i => i.alm === alm);
switch (alm) {
case AlmKeys.Azure:
@@ -130,6 +158,7 @@ export default function AlmSpecificForm(props: AlmSpecificFormProps) {
<>
{renderField({
help: true,
+ helpExample: <strong>My Project</strong>,
id: 'azure.project',
onFieldChange: props.onFieldChange,
propKey: 'slug',
@@ -137,6 +166,7 @@ export default function AlmSpecificForm(props: AlmSpecificFormProps) {
})}
{renderField({
help: true,
+ helpExample: <strong>My Repository</strong>,
id: 'azure.repository',
onFieldChange: props.onFieldChange,
propKey: 'repository',
@@ -150,15 +180,15 @@ export default function AlmSpecificForm(props: AlmSpecificFormProps) {
<>
{renderField({
help: true,
- helpParams: {
- example: (
- <>
- {'.../projects/'}
- <strong>{'{KEY}'}</strong>
- {'/repos/{SLUG}/browse'}
- </>
- )
- },
+ helpExample: (
+ <>
+ {instance?.url
+ ? `${stripTrailingSlash(instance.url)}/projects/`
+ : 'https://bb.company.com/projects/'}
+ <strong>{'MY_PROJECT_KEY'}</strong>
+ {'/repos/my-repository-slug/browse'}
+ </>
+ ),
id: 'bitbucket.repository',
onFieldChange: props.onFieldChange,
propKey: 'repository',
@@ -166,15 +196,15 @@ export default function AlmSpecificForm(props: AlmSpecificFormProps) {
})}
{renderField({
help: true,
- helpParams: {
- example: (
- <>
- {'.../projects/{KEY}/repos/'}
- <strong>{'{SLUG}'}</strong>
- {'/browse'}
- </>
- )
- },
+ helpExample: (
+ <>
+ {instance?.url
+ ? `${stripTrailingSlash(instance.url)}/projects/MY_PROJECT_KEY/repos/`
+ : 'https://bb.company.com/projects/MY_PROJECT_KEY/repos/'}
+ <strong>{'my-repository-slug'}</strong>
+ {'/browse'}
+ </>
+ ),
id: 'bitbucket.slug',
onFieldChange: props.onFieldChange,
propKey: 'slug',
@@ -188,14 +218,12 @@ export default function AlmSpecificForm(props: AlmSpecificFormProps) {
<>
{renderField({
help: true,
- helpParams: {
- example: (
- <>
- {'https://bitbucket.org/{workspace}/'}
- <strong>{'{repository}'}</strong>
- </>
- )
- },
+ helpExample: (
+ <>
+ {'https://bitbucket.org/my-workspace/'}
+ <strong>{'my-repository-slug'}</strong>
+ </>
+ ),
id: 'bitbucketcloud.repository',
onFieldChange: props.onFieldChange,
propKey: 'repository',
@@ -209,7 +237,14 @@ export default function AlmSpecificForm(props: AlmSpecificFormProps) {
<>
{renderField({
help: true,
- helpParams: { example: 'SonarSource/sonarqube' },
+ helpExample: (
+ <>
+ {instance?.url
+ ? `${stripTrailingSlash(convertGithubApiUrlToLink(instance.url))}/`
+ : 'https://github.com/'}
+ <strong>{'sonarsource/sonarqube'}</strong>
+ </>
+ ),
id: 'github.repository',
onFieldChange: props.onFieldChange,
propKey: 'repository',
@@ -229,6 +264,8 @@ export default function AlmSpecificForm(props: AlmSpecificFormProps) {
formFields = (
<>
{renderField({
+ help: true,
+ helpExample: <strong>123456</strong>,
id: 'gitlab.repository',
onFieldChange: props.onFieldChange,
propKey: 'repository',
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBindingRenderer.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBindingRenderer.tsx
index d2d2c62bf56..7fc2133fa00 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBindingRenderer.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBindingRenderer.tsx
@@ -114,40 +114,48 @@ export default function PRDecorationBindingRenderer(props: PRDecorationBindingRe
}}>
<MandatoryFieldsExplanation className="form-field" />
- <div className="form-field">
- <label htmlFor="name">
- {translate('settings.pr_decoration.binding.form.name')}
- <MandatoryFieldMarker className="spacer-right" />
- </label>
- <Select
- autosize={true}
- className="abs-width-400"
- clearable={false}
- id="name"
- menuContainerStyle={{
- maxWidth: '210%' /* Allow double the width of the select */,
- width: 'auto'
- }}
- onChange={(instance: AlmSettingsInstance) => props.onFieldChange('key', instance.key)}
- optionRenderer={optionRenderer}
- options={instances}
- searchable={false}
- value={formData.key}
- valueKey="key"
- valueRenderer={optionRenderer}
- />
+ <div className="settings-definition big-spacer-bottom">
+ <div className="settings-definition-left">
+ <label className="h3" htmlFor="name">
+ {translate('settings.pr_decoration.binding.form.name')}
+ <MandatoryFieldMarker className="spacer-right" />
+ </label>
+ <div className="markdown small spacer-top">
+ {translate('settings.pr_decoration.binding.form.name.help')}
+ </div>
+ </div>
+ <div className="settings-definition-right">
+ <Select
+ autosize={true}
+ className="abs-width-400 big-spacer-top"
+ clearable={false}
+ id="name"
+ menuContainerStyle={{
+ maxWidth: '210%' /* Allow double the width of the select */,
+ width: 'auto'
+ }}
+ onChange={(instance: AlmSettingsInstance) => props.onFieldChange('key', instance.key)}
+ optionRenderer={optionRenderer}
+ options={instances}
+ searchable={false}
+ value={formData.key}
+ valueKey="key"
+ valueRenderer={optionRenderer}
+ />
+ </div>
</div>
{alm && (
<AlmSpecificForm
alm={alm}
+ instances={instances}
formData={formData}
onFieldChange={props.onFieldChange}
monorepoEnabled={monorepoEnabled}
/>
)}
- <div className="display-flex-center">
+ <div className="display-flex-center big-spacer-top">
<DeferredSpinner className="spacer-right" loading={saving} />
{isChanged && (
<SubmitButton className="spacer-right button-success" disabled={saving || !isValid}>
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/AlmSpecificForm-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/AlmSpecificForm-test.tsx
index 5f118c3f225..fb76261471e 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/AlmSpecificForm-test.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/AlmSpecificForm-test.tsx
@@ -19,7 +19,8 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
-import { AlmKeys } from '../../../../../types/alm-settings';
+import { mockAlmSettingsInstance } from '../../../../../helpers/mocks/alm-settings';
+import { AlmKeys, AlmSettingsInstance } from '../../../../../types/alm-settings';
import AlmSpecificForm, { AlmSpecificFormProps } from '../AlmSpecificForm';
it.each([
@@ -32,6 +33,20 @@ it.each([
expect(shallowRender(alm)).toMatchSnapshot();
});
+it.each([
+ [
+ AlmKeys.BitbucketServer,
+ [mockAlmSettingsInstance({ alm: AlmKeys.BitbucketServer, url: 'http://bbs.example.com' })]
+ ],
+ [AlmKeys.GitHub, [mockAlmSettingsInstance({ url: 'http://example.com/api/v3' })]],
+ [AlmKeys.GitHub, [mockAlmSettingsInstance({ url: 'http://api.github.com' })]]
+])(
+ 'it should render correctly for %s if an instance URL is provided',
+ (alm: AlmKeys, instances: AlmSettingsInstance[]) => {
+ expect(shallowRender(alm, { instances })).toMatchSnapshot();
+ }
+);
+
it('should render the monorepo field when the feature is supported', () => {
expect(shallowRender(AlmKeys.Azure, { monorepoEnabled: true })).toMatchSnapshot();
});
@@ -40,6 +55,7 @@ function shallowRender(alm: AlmKeys, props: Partial<AlmSpecificFormProps> = {})
return shallow(
<AlmSpecificForm
alm={alm}
+ instances={[]}
formData={{
key: '',
repository: '',
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/AlmSpecificForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/AlmSpecificForm-test.tsx.snap
index 1ce4d32cfe7..1cb1269f6dd 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/AlmSpecificForm-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/AlmSpecificForm-test.tsx.snap
@@ -3,66 +3,100 @@
exports[`it should render correctly for azure 1`] = `
<Fragment>
<div
- className="form-field"
+ className="settings-definition"
>
- <label
- className="display-flex-center"
- htmlFor="azure.project"
- >
- settings.pr_decoration.binding.form.azure.project
- <MandatoryFieldMarker />
- <HelpTooltip
- className="spacer-left"
- overlay={
- <FormattedMessage
- defaultMessage="settings.pr_decoration.binding.form.azure.project.help"
- id="settings.pr_decoration.binding.form.azure.project.help"
- values={Object {}}
- />
- }
- placement="right"
+ <div
+ className="settings-definition-left"
+ >
+ <label
+ className="h3"
+ htmlFor="azure.project"
+ >
+ settings.pr_decoration.binding.form.azure.project
+ <MandatoryFieldMarker />
+ </label>
+ <div
+ className="markdown small spacer-top"
+ >
+ <FormattedMessage
+ defaultMessage="settings.pr_decoration.binding.form.azure.project.help"
+ id="settings.pr_decoration.binding.form.azure.project.help"
+ values={Object {}}
+ />
+ <div
+ className="spacer-top nowrap"
+ >
+ example
+ :
+ <em>
+ <strong>
+ My Project
+ </strong>
+ </em>
+ </div>
+ </div>
+ </div>
+ <div
+ className="settings-definition-right padded-top"
+ >
+ <input
+ className="input-super-large big-spacer-top"
+ id="azure.project"
+ maxLength={256}
+ name="azure.project"
+ onChange={[Function]}
+ type="text"
+ value=""
/>
- </label>
- <input
- className="input-super-large"
- id="azure.project"
- maxLength={256}
- name="azure.project"
- onChange={[Function]}
- type="text"
- value=""
- />
+ </div>
</div>
<div
- className="form-field"
+ className="settings-definition"
>
- <label
- className="display-flex-center"
- htmlFor="azure.repository"
- >
- settings.pr_decoration.binding.form.azure.repository
- <MandatoryFieldMarker />
- <HelpTooltip
- className="spacer-left"
- overlay={
- <FormattedMessage
- defaultMessage="settings.pr_decoration.binding.form.azure.repository.help"
- id="settings.pr_decoration.binding.form.azure.repository.help"
- values={Object {}}
- />
- }
- placement="right"
+ <div
+ className="settings-definition-left"
+ >
+ <label
+ className="h3"
+ htmlFor="azure.repository"
+ >
+ settings.pr_decoration.binding.form.azure.repository
+ <MandatoryFieldMarker />
+ </label>
+ <div
+ className="markdown small spacer-top"
+ >
+ <FormattedMessage
+ defaultMessage="settings.pr_decoration.binding.form.azure.repository.help"
+ id="settings.pr_decoration.binding.form.azure.repository.help"
+ values={Object {}}
+ />
+ <div
+ className="spacer-top nowrap"
+ >
+ example
+ :
+ <em>
+ <strong>
+ My Repository
+ </strong>
+ </em>
+ </div>
+ </div>
+ </div>
+ <div
+ className="settings-definition-right padded-top"
+ >
+ <input
+ className="input-super-large big-spacer-top"
+ id="azure.repository"
+ maxLength={256}
+ name="azure.repository"
+ onChange={[Function]}
+ type="text"
+ value=""
/>
- </label>
- <input
- className="input-super-large"
- id="azure.repository"
- maxLength={256}
- name="azure.repository"
- onChange={[Function]}
- type="text"
- value=""
- />
+ </div>
</div>
</Fragment>
`;
@@ -70,86 +104,209 @@ exports[`it should render correctly for azure 1`] = `
exports[`it should render correctly for bitbucket 1`] = `
<Fragment>
<div
- className="form-field"
+ className="settings-definition"
>
- <label
- className="display-flex-center"
- htmlFor="bitbucket.repository"
- >
- settings.pr_decoration.binding.form.bitbucket.repository
- <MandatoryFieldMarker />
- <HelpTooltip
- className="spacer-left"
- overlay={
- <FormattedMessage
- defaultMessage="settings.pr_decoration.binding.form.bitbucket.repository.help"
- id="settings.pr_decoration.binding.form.bitbucket.repository.help"
- values={
- Object {
- "example": <React.Fragment>
- .../projects/
- <strong>
- {KEY}
- </strong>
- /repos/{SLUG}/browse
- </React.Fragment>,
- }
- }
- />
- }
- placement="right"
+ <div
+ className="settings-definition-left"
+ >
+ <label
+ className="h3"
+ htmlFor="bitbucket.repository"
+ >
+ settings.pr_decoration.binding.form.bitbucket.repository
+ <MandatoryFieldMarker />
+ </label>
+ <div
+ className="markdown small spacer-top"
+ >
+ <FormattedMessage
+ defaultMessage="settings.pr_decoration.binding.form.bitbucket.repository.help"
+ id="settings.pr_decoration.binding.form.bitbucket.repository.help"
+ values={Object {}}
+ />
+ <div
+ className="spacer-top nowrap"
+ >
+ example
+ :
+ <em>
+ https://bb.company.com/projects/
+ <strong>
+ MY_PROJECT_KEY
+ </strong>
+ /repos/my-repository-slug/browse
+ </em>
+ </div>
+ </div>
+ </div>
+ <div
+ className="settings-definition-right padded-top"
+ >
+ <input
+ className="input-super-large big-spacer-top"
+ id="bitbucket.repository"
+ maxLength={256}
+ name="bitbucket.repository"
+ onChange={[Function]}
+ type="text"
+ value=""
/>
- </label>
- <input
- className="input-super-large"
- id="bitbucket.repository"
- maxLength={256}
- name="bitbucket.repository"
- onChange={[Function]}
- type="text"
- value=""
- />
+ </div>
</div>
<div
- className="form-field"
+ className="settings-definition"
>
- <label
- className="display-flex-center"
- htmlFor="bitbucket.slug"
- >
- settings.pr_decoration.binding.form.bitbucket.slug
- <MandatoryFieldMarker />
- <HelpTooltip
- className="spacer-left"
- overlay={
- <FormattedMessage
- defaultMessage="settings.pr_decoration.binding.form.bitbucket.slug.help"
- id="settings.pr_decoration.binding.form.bitbucket.slug.help"
- values={
- Object {
- "example": <React.Fragment>
- .../projects/{KEY}/repos/
- <strong>
- {SLUG}
- </strong>
- /browse
- </React.Fragment>,
- }
- }
- />
- }
- placement="right"
+ <div
+ className="settings-definition-left"
+ >
+ <label
+ className="h3"
+ htmlFor="bitbucket.slug"
+ >
+ settings.pr_decoration.binding.form.bitbucket.slug
+ <MandatoryFieldMarker />
+ </label>
+ <div
+ className="markdown small spacer-top"
+ >
+ <FormattedMessage
+ defaultMessage="settings.pr_decoration.binding.form.bitbucket.slug.help"
+ id="settings.pr_decoration.binding.form.bitbucket.slug.help"
+ values={Object {}}
+ />
+ <div
+ className="spacer-top nowrap"
+ >
+ example
+ :
+ <em>
+ https://bb.company.com/projects/MY_PROJECT_KEY/repos/
+ <strong>
+ my-repository-slug
+ </strong>
+ /browse
+ </em>
+ </div>
+ </div>
+ </div>
+ <div
+ className="settings-definition-right padded-top"
+ >
+ <input
+ className="input-super-large big-spacer-top"
+ id="bitbucket.slug"
+ maxLength={256}
+ name="bitbucket.slug"
+ onChange={[Function]}
+ type="text"
+ value=""
+ />
+ </div>
+ </div>
+</Fragment>
+`;
+
+exports[`it should render correctly for bitbucket if an instance URL is provided 1`] = `
+<Fragment>
+ <div
+ className="settings-definition"
+ >
+ <div
+ className="settings-definition-left"
+ >
+ <label
+ className="h3"
+ htmlFor="bitbucket.repository"
+ >
+ settings.pr_decoration.binding.form.bitbucket.repository
+ <MandatoryFieldMarker />
+ </label>
+ <div
+ className="markdown small spacer-top"
+ >
+ <FormattedMessage
+ defaultMessage="settings.pr_decoration.binding.form.bitbucket.repository.help"
+ id="settings.pr_decoration.binding.form.bitbucket.repository.help"
+ values={Object {}}
+ />
+ <div
+ className="spacer-top nowrap"
+ >
+ example
+ :
+ <em>
+ http://bbs.example.com/projects/
+ <strong>
+ MY_PROJECT_KEY
+ </strong>
+ /repos/my-repository-slug/browse
+ </em>
+ </div>
+ </div>
+ </div>
+ <div
+ className="settings-definition-right padded-top"
+ >
+ <input
+ className="input-super-large big-spacer-top"
+ id="bitbucket.repository"
+ maxLength={256}
+ name="bitbucket.repository"
+ onChange={[Function]}
+ type="text"
+ value=""
/>
- </label>
- <input
- className="input-super-large"
- id="bitbucket.slug"
- maxLength={256}
- name="bitbucket.slug"
- onChange={[Function]}
- type="text"
- value=""
- />
+ </div>
+ </div>
+ <div
+ className="settings-definition"
+ >
+ <div
+ className="settings-definition-left"
+ >
+ <label
+ className="h3"
+ htmlFor="bitbucket.slug"
+ >
+ settings.pr_decoration.binding.form.bitbucket.slug
+ <MandatoryFieldMarker />
+ </label>
+ <div
+ className="markdown small spacer-top"
+ >
+ <FormattedMessage
+ defaultMessage="settings.pr_decoration.binding.form.bitbucket.slug.help"
+ id="settings.pr_decoration.binding.form.bitbucket.slug.help"
+ values={Object {}}
+ />
+ <div
+ className="spacer-top nowrap"
+ >
+ example
+ :
+ <em>
+ http://bbs.example.com/projects/MY_PROJECT_KEY/repos/
+ <strong>
+ my-repository-slug
+ </strong>
+ /browse
+ </em>
+ </div>
+ </div>
+ </div>
+ <div
+ className="settings-definition-right padded-top"
+ >
+ <input
+ className="input-super-large big-spacer-top"
+ id="bitbucket.slug"
+ maxLength={256}
+ name="bitbucket.slug"
+ onChange={[Function]}
+ type="text"
+ value=""
+ />
+ </div>
</div>
</Fragment>
`;
@@ -157,44 +314,53 @@ exports[`it should render correctly for bitbucket 1`] = `
exports[`it should render correctly for bitbucketcloud 1`] = `
<Fragment>
<div
- className="form-field"
+ className="settings-definition"
>
- <label
- className="display-flex-center"
- htmlFor="bitbucketcloud.repository"
- >
- settings.pr_decoration.binding.form.bitbucketcloud.repository
- <MandatoryFieldMarker />
- <HelpTooltip
- className="spacer-left"
- overlay={
- <FormattedMessage
- defaultMessage="settings.pr_decoration.binding.form.bitbucketcloud.repository.help"
- id="settings.pr_decoration.binding.form.bitbucketcloud.repository.help"
- values={
- Object {
- "example": <React.Fragment>
- https://bitbucket.org/{workspace}/
- <strong>
- {repository}
- </strong>
- </React.Fragment>,
- }
- }
- />
- }
- placement="right"
+ <div
+ className="settings-definition-left"
+ >
+ <label
+ className="h3"
+ htmlFor="bitbucketcloud.repository"
+ >
+ settings.pr_decoration.binding.form.bitbucketcloud.repository
+ <MandatoryFieldMarker />
+ </label>
+ <div
+ className="markdown small spacer-top"
+ >
+ <FormattedMessage
+ defaultMessage="settings.pr_decoration.binding.form.bitbucketcloud.repository.help"
+ id="settings.pr_decoration.binding.form.bitbucketcloud.repository.help"
+ values={Object {}}
+ />
+ <div
+ className="spacer-top nowrap"
+ >
+ example
+ :
+ <em>
+ https://bitbucket.org/my-workspace/
+ <strong>
+ my-repository-slug
+ </strong>
+ </em>
+ </div>
+ </div>
+ </div>
+ <div
+ className="settings-definition-right padded-top"
+ >
+ <input
+ className="input-super-large big-spacer-top"
+ id="bitbucketcloud.repository"
+ maxLength={256}
+ name="bitbucketcloud.repository"
+ onChange={[Function]}
+ type="text"
+ value=""
/>
- </label>
- <input
- className="input-super-large"
- id="bitbucketcloud.repository"
- maxLength={256}
- name="bitbucketcloud.repository"
- onChange={[Function]}
- type="text"
- value=""
- />
+ </div>
</div>
</Fragment>
`;
@@ -202,95 +368,325 @@ exports[`it should render correctly for bitbucketcloud 1`] = `
exports[`it should render correctly for github 1`] = `
<Fragment>
<div
- className="form-field"
+ className="settings-definition"
>
- <label
- className="display-flex-center"
- htmlFor="github.repository"
- >
- settings.pr_decoration.binding.form.github.repository
- <MandatoryFieldMarker />
- <HelpTooltip
- className="spacer-left"
- overlay={
- <FormattedMessage
- defaultMessage="settings.pr_decoration.binding.form.github.repository.help"
- id="settings.pr_decoration.binding.form.github.repository.help"
- values={
- Object {
- "example": "SonarSource/sonarqube",
- }
- }
- />
- }
- placement="right"
+ <div
+ className="settings-definition-left"
+ >
+ <label
+ className="h3"
+ htmlFor="github.repository"
+ >
+ settings.pr_decoration.binding.form.github.repository
+ <MandatoryFieldMarker />
+ </label>
+ <div
+ className="markdown small spacer-top"
+ >
+ <FormattedMessage
+ defaultMessage="settings.pr_decoration.binding.form.github.repository.help"
+ id="settings.pr_decoration.binding.form.github.repository.help"
+ values={Object {}}
+ />
+ <div
+ className="spacer-top nowrap"
+ >
+ example
+ :
+ <em>
+ https://github.com/
+ <strong>
+ sonarsource/sonarqube
+ </strong>
+ </em>
+ </div>
+ </div>
+ </div>
+ <div
+ className="settings-definition-right padded-top"
+ >
+ <input
+ className="input-super-large big-spacer-top"
+ id="github.repository"
+ maxLength={256}
+ name="github.repository"
+ onChange={[Function]}
+ type="text"
+ value=""
/>
- </label>
- <input
- className="input-super-large"
- id="github.repository"
- maxLength={256}
- name="github.repository"
- onChange={[Function]}
- type="text"
- value=""
- />
+ </div>
+ </div>
+ <div
+ className="settings-definition"
+ >
+ <div
+ className="settings-definition-left"
+ >
+ <label
+ className="h3"
+ htmlFor="github.summary_comment_setting"
+ >
+ settings.pr_decoration.binding.form.github.summary_comment_setting
+ </label>
+ <div
+ className="markdown small spacer-top"
+ >
+ <FormattedMessage
+ defaultMessage="settings.pr_decoration.binding.form.github.summary_comment_setting.help"
+ id="settings.pr_decoration.binding.form.github.summary_comment_setting.help"
+ values={Object {}}
+ />
+ </div>
+ </div>
+ <div
+ className="settings-definition-right padded-top"
+ >
+ <div
+ className="display-flex-center big-spacer-top"
+ >
+ <InputForBoolean
+ isDefault={true}
+ name="github.summary_comment_setting"
+ onChange={[Function]}
+ value={true}
+ />
+ </div>
+ </div>
</div>
+</Fragment>
+`;
+
+exports[`it should render correctly for github if an instance URL is provided 1`] = `
+<Fragment>
<div
- className="form-field"
+ className="settings-definition"
>
- <label
- className="display-flex-center"
- htmlFor="github.summary_comment_setting"
- >
- settings.pr_decoration.binding.form.github.summary_comment_setting
- <HelpTooltip
- className="spacer-left"
- overlay={
- <FormattedMessage
- defaultMessage="settings.pr_decoration.binding.form.github.summary_comment_setting.help"
- id="settings.pr_decoration.binding.form.github.summary_comment_setting.help"
- values={Object {}}
- />
- }
- placement="right"
+ <div
+ className="settings-definition-left"
+ >
+ <label
+ className="h3"
+ htmlFor="github.repository"
+ >
+ settings.pr_decoration.binding.form.github.repository
+ <MandatoryFieldMarker />
+ </label>
+ <div
+ className="markdown small spacer-top"
+ >
+ <FormattedMessage
+ defaultMessage="settings.pr_decoration.binding.form.github.repository.help"
+ id="settings.pr_decoration.binding.form.github.repository.help"
+ values={Object {}}
+ />
+ <div
+ className="spacer-top nowrap"
+ >
+ example
+ :
+ <em>
+ http://example.com/
+ <strong>
+ sonarsource/sonarqube
+ </strong>
+ </em>
+ </div>
+ </div>
+ </div>
+ <div
+ className="settings-definition-right padded-top"
+ >
+ <input
+ className="input-super-large big-spacer-top"
+ id="github.repository"
+ maxLength={256}
+ name="github.repository"
+ onChange={[Function]}
+ type="text"
+ value=""
/>
- </label>
+ </div>
+ </div>
+ <div
+ className="settings-definition"
+ >
+ <div
+ className="settings-definition-left"
+ >
+ <label
+ className="h3"
+ htmlFor="github.summary_comment_setting"
+ >
+ settings.pr_decoration.binding.form.github.summary_comment_setting
+ </label>
+ <div
+ className="markdown small spacer-top"
+ >
+ <FormattedMessage
+ defaultMessage="settings.pr_decoration.binding.form.github.summary_comment_setting.help"
+ id="settings.pr_decoration.binding.form.github.summary_comment_setting.help"
+ values={Object {}}
+ />
+ </div>
+ </div>
+ <div
+ className="settings-definition-right padded-top"
+ >
+ <div
+ className="display-flex-center big-spacer-top"
+ >
+ <InputForBoolean
+ isDefault={true}
+ name="github.summary_comment_setting"
+ onChange={[Function]}
+ value={true}
+ />
+ </div>
+ </div>
+ </div>
+</Fragment>
+`;
+
+exports[`it should render correctly for github if an instance URL is provided 2`] = `
+<Fragment>
+ <div
+ className="settings-definition"
+ >
+ <div
+ className="settings-definition-left"
+ >
+ <label
+ className="h3"
+ htmlFor="github.repository"
+ >
+ settings.pr_decoration.binding.form.github.repository
+ <MandatoryFieldMarker />
+ </label>
+ <div
+ className="markdown small spacer-top"
+ >
+ <FormattedMessage
+ defaultMessage="settings.pr_decoration.binding.form.github.repository.help"
+ id="settings.pr_decoration.binding.form.github.repository.help"
+ values={Object {}}
+ />
+ <div
+ className="spacer-top nowrap"
+ >
+ example
+ :
+ <em>
+ https://github.com/
+ <strong>
+ sonarsource/sonarqube
+ </strong>
+ </em>
+ </div>
+ </div>
+ </div>
<div
- className="display-flex-center"
+ className="settings-definition-right padded-top"
>
- <InputForBoolean
- isDefault={true}
- name="github.summary_comment_setting"
+ <input
+ className="input-super-large big-spacer-top"
+ id="github.repository"
+ maxLength={256}
+ name="github.repository"
onChange={[Function]}
- value={true}
+ type="text"
+ value=""
/>
</div>
</div>
+ <div
+ className="settings-definition"
+ >
+ <div
+ className="settings-definition-left"
+ >
+ <label
+ className="h3"
+ htmlFor="github.summary_comment_setting"
+ >
+ settings.pr_decoration.binding.form.github.summary_comment_setting
+ </label>
+ <div
+ className="markdown small spacer-top"
+ >
+ <FormattedMessage
+ defaultMessage="settings.pr_decoration.binding.form.github.summary_comment_setting.help"
+ id="settings.pr_decoration.binding.form.github.summary_comment_setting.help"
+ values={Object {}}
+ />
+ </div>
+ </div>
+ <div
+ className="settings-definition-right padded-top"
+ >
+ <div
+ className="display-flex-center big-spacer-top"
+ >
+ <InputForBoolean
+ isDefault={true}
+ name="github.summary_comment_setting"
+ onChange={[Function]}
+ value={true}
+ />
+ </div>
+ </div>
+ </div>
</Fragment>
`;
exports[`it should render correctly for gitlab 1`] = `
<Fragment>
<div
- className="form-field"
+ className="settings-definition"
>
- <label
- className="display-flex-center"
- htmlFor="gitlab.repository"
- >
- settings.pr_decoration.binding.form.gitlab.repository
- <MandatoryFieldMarker />
- </label>
- <input
- className="input-super-large"
- id="gitlab.repository"
- maxLength={256}
- name="gitlab.repository"
- onChange={[Function]}
- type="text"
- value=""
- />
+ <div
+ className="settings-definition-left"
+ >
+ <label
+ className="h3"
+ htmlFor="gitlab.repository"
+ >
+ settings.pr_decoration.binding.form.gitlab.repository
+ <MandatoryFieldMarker />
+ </label>
+ <div
+ className="markdown small spacer-top"
+ >
+ <FormattedMessage
+ defaultMessage="settings.pr_decoration.binding.form.gitlab.repository.help"
+ id="settings.pr_decoration.binding.form.gitlab.repository.help"
+ values={Object {}}
+ />
+ <div
+ className="spacer-top nowrap"
+ >
+ example
+ :
+ <em>
+ <strong>
+ 123456
+ </strong>
+ </em>
+ </div>
+ </div>
+ </div>
+ <div
+ className="settings-definition-right padded-top"
+ >
+ <input
+ className="input-super-large big-spacer-top"
+ id="gitlab.repository"
+ maxLength={256}
+ name="gitlab.repository"
+ onChange={[Function]}
+ type="text"
+ value=""
+ />
+ </div>
</div>
</Fragment>
`;
@@ -298,107 +694,147 @@ exports[`it should render correctly for gitlab 1`] = `
exports[`should render the monorepo field when the feature is supported 1`] = `
<Fragment>
<div
- className="form-field"
+ className="settings-definition"
>
- <label
- className="display-flex-center"
- htmlFor="azure.project"
- >
- settings.pr_decoration.binding.form.azure.project
- <MandatoryFieldMarker />
- <HelpTooltip
- className="spacer-left"
- overlay={
- <FormattedMessage
- defaultMessage="settings.pr_decoration.binding.form.azure.project.help"
- id="settings.pr_decoration.binding.form.azure.project.help"
- values={Object {}}
- />
- }
- placement="right"
+ <div
+ className="settings-definition-left"
+ >
+ <label
+ className="h3"
+ htmlFor="azure.project"
+ >
+ settings.pr_decoration.binding.form.azure.project
+ <MandatoryFieldMarker />
+ </label>
+ <div
+ className="markdown small spacer-top"
+ >
+ <FormattedMessage
+ defaultMessage="settings.pr_decoration.binding.form.azure.project.help"
+ id="settings.pr_decoration.binding.form.azure.project.help"
+ values={Object {}}
+ />
+ <div
+ className="spacer-top nowrap"
+ >
+ example
+ :
+ <em>
+ <strong>
+ My Project
+ </strong>
+ </em>
+ </div>
+ </div>
+ </div>
+ <div
+ className="settings-definition-right padded-top"
+ >
+ <input
+ className="input-super-large big-spacer-top"
+ id="azure.project"
+ maxLength={256}
+ name="azure.project"
+ onChange={[Function]}
+ type="text"
+ value=""
/>
- </label>
- <input
- className="input-super-large"
- id="azure.project"
- maxLength={256}
- name="azure.project"
- onChange={[Function]}
- type="text"
- value=""
- />
+ </div>
</div>
<div
- className="form-field"
+ className="settings-definition"
>
- <label
- className="display-flex-center"
- htmlFor="azure.repository"
- >
- settings.pr_decoration.binding.form.azure.repository
- <MandatoryFieldMarker />
- <HelpTooltip
- className="spacer-left"
- overlay={
- <FormattedMessage
- defaultMessage="settings.pr_decoration.binding.form.azure.repository.help"
- id="settings.pr_decoration.binding.form.azure.repository.help"
- values={Object {}}
- />
- }
- placement="right"
+ <div
+ className="settings-definition-left"
+ >
+ <label
+ className="h3"
+ htmlFor="azure.repository"
+ >
+ settings.pr_decoration.binding.form.azure.repository
+ <MandatoryFieldMarker />
+ </label>
+ <div
+ className="markdown small spacer-top"
+ >
+ <FormattedMessage
+ defaultMessage="settings.pr_decoration.binding.form.azure.repository.help"
+ id="settings.pr_decoration.binding.form.azure.repository.help"
+ values={Object {}}
+ />
+ <div
+ className="spacer-top nowrap"
+ >
+ example
+ :
+ <em>
+ <strong>
+ My Repository
+ </strong>
+ </em>
+ </div>
+ </div>
+ </div>
+ <div
+ className="settings-definition-right padded-top"
+ >
+ <input
+ className="input-super-large big-spacer-top"
+ id="azure.repository"
+ maxLength={256}
+ name="azure.repository"
+ onChange={[Function]}
+ type="text"
+ value=""
/>
- </label>
- <input
- className="input-super-large"
- id="azure.repository"
- maxLength={256}
- name="azure.repository"
- onChange={[Function]}
- type="text"
- value=""
- />
+ </div>
</div>
<div
- className="form-field"
+ className="settings-definition"
>
- <label
- className="display-flex-center"
- htmlFor="monorepo"
- >
- settings.pr_decoration.binding.form.monorepo
- <HelpTooltip
- className="spacer-left"
- overlay={
- <FormattedMessage
- defaultMessage="settings.pr_decoration.binding.form.monorepo.help"
- id="settings.pr_decoration.binding.form.monorepo.help"
- values={
- Object {
- "doc_link": <Link
- onlyActiveOnIndex={false}
- style={Object {}}
- target="_blank"
- to="/documentation/analysis/azuredevops-integration/"
- >
- learn_more
- </Link>,
- }
+ <div
+ className="settings-definition-left"
+ >
+ <label
+ className="h3"
+ htmlFor="monorepo"
+ >
+ settings.pr_decoration.binding.form.monorepo
+ </label>
+ <div
+ className="markdown small spacer-top"
+ >
+ <FormattedMessage
+ defaultMessage="settings.pr_decoration.binding.form.monorepo.help"
+ id="settings.pr_decoration.binding.form.monorepo.help"
+ values={
+ Object {
+ "doc_link": <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ target="_blank"
+ to="/documentation/analysis/azuredevops-integration/"
+ >
+ learn_more
+ </Link>,
}
- />
- }
- placement="right"
- />
- </label>
+ }
+ />
+ </div>
+ </div>
<div
- className="display-flex-center"
+ className="settings-definition-right padded-top"
>
- <InputForBoolean
- isDefault={true}
- name="monorepo"
- onChange={[Function]}
- value={false}
- />
+ <div
+ className="display-flex-center big-spacer-top"
+ >
+ <InputForBoolean
+ isDefault={true}
+ name="monorepo"
+ onChange={[Function]}
+ value={false}
+ />
+ </div>
</div>
</div>
</Fragment>
diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/PRDecorationBindingRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/PRDecorationBindingRenderer-test.tsx.snap
index cd949808fd3..b231621bdbc 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/PRDecorationBindingRenderer-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/__snapshots__/PRDecorationBindingRenderer-test.tsx.snap
@@ -23,46 +23,60 @@ exports[`should display action state correctly 1`] = `
className="form-field"
/>
<div
- className="form-field"
+ className="settings-definition big-spacer-bottom"
>
- <label
- htmlFor="name"
+ <div
+ className="settings-definition-left"
>
- settings.pr_decoration.binding.form.name
- <MandatoryFieldMarker
- className="spacer-right"
- />
- </label>
- <Select
- autosize={true}
- className="abs-width-400"
- clearable={false}
- id="name"
- menuContainerStyle={
- Object {
- "maxWidth": "210%",
- "width": "auto",
- }
- }
- onChange={[Function]}
- optionRenderer={[Function]}
- options={
- Array [
+ <label
+ className="h3"
+ htmlFor="name"
+ >
+ settings.pr_decoration.binding.form.name
+ <MandatoryFieldMarker
+ className="spacer-right"
+ />
+ </label>
+ <div
+ className="markdown small spacer-top"
+ >
+ settings.pr_decoration.binding.form.name.help
+ </div>
+ </div>
+ <div
+ className="settings-definition-right"
+ >
+ <Select
+ autosize={true}
+ className="abs-width-400 big-spacer-top"
+ clearable={false}
+ id="name"
+ menuContainerStyle={
Object {
- "alm": "github",
- "key": "key",
- "url": "http://url.com",
- },
- ]
- }
- searchable={false}
- value=""
- valueKey="key"
- valueRenderer={[Function]}
- />
+ "maxWidth": "210%",
+ "width": "auto",
+ }
+ }
+ onChange={[Function]}
+ optionRenderer={[Function]}
+ options={
+ Array [
+ Object {
+ "alm": "github",
+ "key": "key",
+ "url": "http://url.com",
+ },
+ ]
+ }
+ searchable={false}
+ value=""
+ valueKey="key"
+ valueRenderer={[Function]}
+ />
+ </div>
</div>
<div
- className="display-flex-center"
+ className="display-flex-center big-spacer-top"
>
<DeferredSpinner
className="spacer-right"
@@ -96,46 +110,60 @@ exports[`should display action state correctly 2`] = `
className="form-field"
/>
<div
- className="form-field"
+ className="settings-definition big-spacer-bottom"
>
- <label
- htmlFor="name"
+ <div
+ className="settings-definition-left"
>
- settings.pr_decoration.binding.form.name
- <MandatoryFieldMarker
- className="spacer-right"
- />
- </label>
- <Select
- autosize={true}
- className="abs-width-400"
- clearable={false}
- id="name"
- menuContainerStyle={
- Object {
- "maxWidth": "210%",
- "width": "auto",
- }
- }
- onChange={[Function]}
- optionRenderer={[Function]}
- options={
- Array [
+ <label
+ className="h3"
+ htmlFor="name"
+ >
+ settings.pr_decoration.binding.form.name
+ <MandatoryFieldMarker
+ className="spacer-right"
+ />
+ </label>
+ <div
+ className="markdown small spacer-top"
+ >
+ settings.pr_decoration.binding.form.name.help
+ </div>
+ </div>
+ <div
+ className="settings-definition-right"
+ >
+ <Select
+ autosize={true}
+ className="abs-width-400 big-spacer-top"
+ clearable={false}
+ id="name"
+ menuContainerStyle={
Object {
- "alm": "github",
- "key": "key",
- "url": "http://url.com",
- },
- ]
- }
- searchable={false}
- value=""
- valueKey="key"
- valueRenderer={[Function]}
- />
+ "maxWidth": "210%",
+ "width": "auto",
+ }
+ }
+ onChange={[Function]}
+ optionRenderer={[Function]}
+ options={
+ Array [
+ Object {
+ "alm": "github",
+ "key": "key",
+ "url": "http://url.com",
+ },
+ ]
+ }
+ searchable={false}
+ value=""
+ valueKey="key"
+ valueRenderer={[Function]}
+ />
+ </div>
</div>
<div
- className="display-flex-center"
+ className="display-flex-center big-spacer-top"
>
<DeferredSpinner
className="spacer-right"
@@ -177,46 +205,60 @@ exports[`should display action state correctly 3`] = `
className="form-field"
/>
<div
- className="form-field"
+ className="settings-definition big-spacer-bottom"
>
- <label
- htmlFor="name"
+ <div
+ className="settings-definition-left"
>
- settings.pr_decoration.binding.form.name
- <MandatoryFieldMarker
- className="spacer-right"
- />
- </label>
- <Select
- autosize={true}
- className="abs-width-400"
- clearable={false}
- id="name"
- menuContainerStyle={
- Object {
- "maxWidth": "210%",
- "width": "auto",
- }
- }
- onChange={[Function]}
- optionRenderer={[Function]}
- options={
- Array [
+ <label
+ className="h3"
+ htmlFor="name"
+ >
+ settings.pr_decoration.binding.form.name
+ <MandatoryFieldMarker
+ className="spacer-right"
+ />
+ </label>
+ <div
+ className="markdown small spacer-top"
+ >
+ settings.pr_decoration.binding.form.name.help
+ </div>
+ </div>
+ <div
+ className="settings-definition-right"
+ >
+ <Select
+ autosize={true}
+ className="abs-width-400 big-spacer-top"
+ clearable={false}
+ id="name"
+ menuContainerStyle={
Object {
- "alm": "github",
- "key": "key",
- "url": "http://url.com",
- },
- ]
- }
- searchable={false}
- value=""
- valueKey="key"
- valueRenderer={[Function]}
- />
+ "maxWidth": "210%",
+ "width": "auto",
+ }
+ }
+ onChange={[Function]}
+ optionRenderer={[Function]}
+ options={
+ Array [
+ Object {
+ "alm": "github",
+ "key": "key",
+ "url": "http://url.com",
+ },
+ ]
+ }
+ searchable={false}
+ value=""
+ valueKey="key"
+ valueRenderer={[Function]}
+ />
+ </div>
</div>
<div
- className="display-flex-center"
+ className="display-flex-center big-spacer-top"
>
<DeferredSpinner
className="spacer-right"
@@ -277,60 +319,74 @@ exports[`should render multiple instances correctly 1`] = `
className="form-field"
/>
<div
- className="form-field"
+ className="settings-definition big-spacer-bottom"
>
- <label
- htmlFor="name"
+ <div
+ className="settings-definition-left"
>
- settings.pr_decoration.binding.form.name
- <MandatoryFieldMarker
- className="spacer-right"
- />
- </label>
- <Select
- autosize={true}
- className="abs-width-400"
- clearable={false}
- id="name"
- menuContainerStyle={
- Object {
- "maxWidth": "210%",
- "width": "auto",
- }
- }
- onChange={[Function]}
- optionRenderer={[Function]}
- options={
- Array [
- Object {
- "alm": "github",
- "key": "i1",
- "url": "http://github.enterprise.com",
- },
- Object {
- "alm": "github",
- "key": "i2",
- "url": "http://github.enterprise.com",
- },
- Object {
- "alm": "bitbucket",
- "key": "i3",
- "url": "http://bbs.enterprise.com",
- },
+ <label
+ className="h3"
+ htmlFor="name"
+ >
+ settings.pr_decoration.binding.form.name
+ <MandatoryFieldMarker
+ className="spacer-right"
+ />
+ </label>
+ <div
+ className="markdown small spacer-top"
+ >
+ settings.pr_decoration.binding.form.name.help
+ </div>
+ </div>
+ <div
+ className="settings-definition-right"
+ >
+ <Select
+ autosize={true}
+ className="abs-width-400 big-spacer-top"
+ clearable={false}
+ id="name"
+ menuContainerStyle={
Object {
- "alm": "azure",
- "key": "i4",
- },
- ]
- }
- searchable={false}
- value=""
- valueKey="key"
- valueRenderer={[Function]}
- />
+ "maxWidth": "210%",
+ "width": "auto",
+ }
+ }
+ onChange={[Function]}
+ optionRenderer={[Function]}
+ options={
+ Array [
+ Object {
+ "alm": "github",
+ "key": "i1",
+ "url": "http://github.enterprise.com",
+ },
+ Object {
+ "alm": "github",
+ "key": "i2",
+ "url": "http://github.enterprise.com",
+ },
+ Object {
+ "alm": "bitbucket",
+ "key": "i3",
+ "url": "http://bbs.enterprise.com",
+ },
+ Object {
+ "alm": "azure",
+ "key": "i4",
+ },
+ ]
+ }
+ searchable={false}
+ value=""
+ valueKey="key"
+ valueRenderer={[Function]}
+ />
+ </div>
</div>
<div
- className="display-flex-center"
+ className="display-flex-center big-spacer-top"
>
<DeferredSpinner
className="spacer-right"
@@ -364,57 +420,71 @@ exports[`should render multiple instances correctly 2`] = `
className="form-field"
/>
<div
- className="form-field"
+ className="settings-definition big-spacer-bottom"
>
- <label
- htmlFor="name"
+ <div
+ className="settings-definition-left"
>
- settings.pr_decoration.binding.form.name
- <MandatoryFieldMarker
- className="spacer-right"
- />
- </label>
- <Select
- autosize={true}
- className="abs-width-400"
- clearable={false}
- id="name"
- menuContainerStyle={
- Object {
- "maxWidth": "210%",
- "width": "auto",
- }
- }
- onChange={[Function]}
- optionRenderer={[Function]}
- options={
- Array [
- Object {
- "alm": "github",
- "key": "i1",
- "url": "http://github.enterprise.com",
- },
- Object {
- "alm": "github",
- "key": "i2",
- "url": "http://github.enterprise.com",
- },
- Object {
- "alm": "bitbucket",
- "key": "i3",
- "url": "http://bbs.enterprise.com",
- },
+ <label
+ className="h3"
+ htmlFor="name"
+ >
+ settings.pr_decoration.binding.form.name
+ <MandatoryFieldMarker
+ className="spacer-right"
+ />
+ </label>
+ <div
+ className="markdown small spacer-top"
+ >
+ settings.pr_decoration.binding.form.name.help
+ </div>
+ </div>
+ <div
+ className="settings-definition-right"
+ >
+ <Select
+ autosize={true}
+ className="abs-width-400 big-spacer-top"
+ clearable={false}
+ id="name"
+ menuContainerStyle={
Object {
- "alm": "azure",
- "key": "i4",
- },
- ]
- }
- searchable={false}
- value="i1"
- valueKey="key"
- valueRenderer={[Function]}
- />
+ "maxWidth": "210%",
+ "width": "auto",
+ }
+ }
+ onChange={[Function]}
+ optionRenderer={[Function]}
+ options={
+ Array [
+ Object {
+ "alm": "github",
+ "key": "i1",
+ "url": "http://github.enterprise.com",
+ },
+ Object {
+ "alm": "github",
+ "key": "i2",
+ "url": "http://github.enterprise.com",
+ },
+ Object {
+ "alm": "bitbucket",
+ "key": "i3",
+ "url": "http://bbs.enterprise.com",
+ },
+ Object {
+ "alm": "azure",
+ "key": "i4",
+ },
+ ]
+ }
+ searchable={false}
+ value="i1"
+ valueKey="key"
+ valueRenderer={[Function]}
+ />
+ </div>
</div>
<AlmSpecificForm
alm="github"
@@ -425,11 +495,34 @@ exports[`should render multiple instances correctly 2`] = `
"repository": "account/repo",
}
}
+ instances={
+ Array [
+ Object {
+ "alm": "github",
+ "key": "i1",
+ "url": "http://github.enterprise.com",
+ },
+ Object {
+ "alm": "github",
+ "key": "i2",
+ "url": "http://github.enterprise.com",
+ },
+ Object {
+ "alm": "bitbucket",
+ "key": "i3",
+ "url": "http://bbs.enterprise.com",
+ },
+ Object {
+ "alm": "azure",
+ "key": "i4",
+ },
+ ]
+ }
monorepoEnabled={false}
onFieldChange={[MockFunction]}
/>
<div
- className="display-flex-center"
+ className="display-flex-center big-spacer-top"
>
<DeferredSpinner
className="spacer-right"
@@ -493,46 +586,60 @@ exports[`should render single instance correctly 1`] = `
className="form-field"
/>
<div
- className="form-field"
+ className="settings-definition big-spacer-bottom"
>
- <label
- htmlFor="name"
+ <div
+ className="settings-definition-left"
>
- settings.pr_decoration.binding.form.name
- <MandatoryFieldMarker
- className="spacer-right"
- />
- </label>
- <Select
- autosize={true}
- className="abs-width-400"
- clearable={false}
- id="name"
- menuContainerStyle={
- Object {
- "maxWidth": "210%",
- "width": "auto",
- }
- }
- onChange={[Function]}
- optionRenderer={[Function]}
- options={
- Array [
+ <label
+ className="h3"
+ htmlFor="name"
+ >
+ settings.pr_decoration.binding.form.name
+ <MandatoryFieldMarker
+ className="spacer-right"
+ />
+ </label>
+ <div
+ className="markdown small spacer-top"
+ >
+ settings.pr_decoration.binding.form.name.help
+ </div>
+ </div>
+ <div
+ className="settings-definition-right"
+ >
+ <Select
+ autosize={true}
+ className="abs-width-400 big-spacer-top"
+ clearable={false}
+ id="name"
+ menuContainerStyle={
Object {
- "alm": "github",
- "key": "single",
- "url": "http://single.url",
- },
- ]
- }
- searchable={false}
- value=""
- valueKey="key"
- valueRenderer={[Function]}
- />
+ "maxWidth": "210%",
+ "width": "auto",
+ }
+ }
+ onChange={[Function]}
+ optionRenderer={[Function]}
+ options={
+ Array [
+ Object {
+ "alm": "github",
+ "key": "single",
+ "url": "http://single.url",
+ },
+ ]
+ }
+ searchable={false}
+ value=""
+ valueKey="key"
+ valueRenderer={[Function]}
+ />
+ </div>
</div>
<div
- className="display-flex-center"
+ className="display-flex-center big-spacer-top"
>
<DeferredSpinner
className="spacer-right"
diff --git a/server/sonar-web/src/main/js/components/tutorials/utils.ts b/server/sonar-web/src/main/js/components/tutorials/utils.ts
index 4ce77e21f2f..cfb550e852b 100644
--- a/server/sonar-web/src/main/js/components/tutorials/utils.ts
+++ b/server/sonar-web/src/main/js/components/tutorials/utils.ts
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { convertGithubApiUrlToLink, stripTrailingSlash } from '../../helpers/urls';
import { AlmSettingsInstance, ProjectAlmBindingResponse } from '../../types/alm-settings';
export function quote(os: string): (s: string) => string {
@@ -64,10 +65,7 @@ export function buildGithubLink(
}
// strip the api path:
- const urlRoot = almBinding.url
- .replace(/\/api\/v\d+\/?$/, '') // GH Enterprise
- .replace(/^https?:\/\/api\.github\.com/, 'https://github.com') // GH.com
- .replace(/\/$/, '');
+ const urlRoot = convertGithubApiUrlToLink(almBinding.url);
- return `${urlRoot}/${projectBinding.repository}`;
+ return `${stripTrailingSlash(urlRoot)}/${projectBinding.repository}`;
}
diff --git a/server/sonar-web/src/main/js/helpers/__tests__/urls-test.ts b/server/sonar-web/src/main/js/helpers/__tests__/urls-test.ts
index fcd47d20572..bed9b1df8fb 100644
--- a/server/sonar-web/src/main/js/helpers/__tests__/urls-test.ts
+++ b/server/sonar-web/src/main/js/helpers/__tests__/urls-test.ts
@@ -20,19 +20,37 @@
import { ComponentQualifier } from '../../types/component';
import { IssueType } from '../../types/issues';
import {
+ convertGithubApiUrlToLink,
getComponentDrilldownUrl,
getComponentIssuesUrl,
getComponentOverviewUrl,
getComponentSecurityHotspotsUrl,
getIssuesUrl,
getQualityGatesUrl,
- getQualityGateUrl
+ getQualityGateUrl,
+ stripTrailingSlash
} from '../urls';
const SIMPLE_COMPONENT_KEY = 'sonarqube';
const COMPLEX_COMPONENT_KEY = 'org.sonarsource.sonarqube:sonarqube';
const METRIC = 'coverage';
+describe('#convertGithubApiUrlToLink', () => {
+ it('should correctly convert a GitHub API URL to a Web URL', () => {
+ expect(convertGithubApiUrlToLink('https://api.github.com')).toBe('https://github.com');
+ expect(convertGithubApiUrlToLink('https://company.github.com/api/v3')).toBe(
+ 'https://company.github.com'
+ );
+ });
+});
+
+describe('#stripTrailingSlash', () => {
+ it('should correctly strip trailing slashes from any URL', () => {
+ expect(stripTrailingSlash('https://example.com/')).toBe('https://example.com');
+ expect(convertGithubApiUrlToLink('https://example.com')).toBe('https://example.com');
+ });
+});
+
describe('#getComponentIssuesUrl', () => {
it('should work without parameters', () => {
expect(getComponentIssuesUrl(SIMPLE_COMPONENT_KEY, {})).toEqual({
diff --git a/server/sonar-web/src/main/js/helpers/urls.ts b/server/sonar-web/src/main/js/helpers/urls.ts
index 3d255640269..1adb6ee8ec6 100644
--- a/server/sonar-web/src/main/js/helpers/urls.ts
+++ b/server/sonar-web/src/main/js/helpers/urls.ts
@@ -280,3 +280,13 @@ export function getHomePageUrl(homepage: T.HomePage) {
// should never happen, but just in case...
return '/projects';
}
+
+export function convertGithubApiUrlToLink(url: string) {
+ return url
+ .replace(/^https?:\/\/api\.github\.com/, 'https://github.com') // GH.com
+ .replace(/\/api\/v\d+\/?$/, ''); // GH Enterprise
+}
+
+export function stripTrailingSlash(url: string) {
+ return url.replace(/\/$/, '');
+}