3 * Copyright (C) 2009-2020 SonarSource SA
4 * mailto:info AT sonarsource DOT com
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 import * as React from 'react';
21 import { FormattedMessage } from 'react-intl';
22 import { Link } from 'react-router';
23 import { Button, SubmitButton } from 'sonar-ui-common/components/controls/buttons';
24 import HelpTooltip from 'sonar-ui-common/components/controls/HelpTooltip';
25 import Select from 'sonar-ui-common/components/controls/Select';
26 import AlertSuccessIcon from 'sonar-ui-common/components/icons/AlertSuccessIcon';
27 import { Alert } from 'sonar-ui-common/components/ui/Alert';
28 import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
29 import { translate } from 'sonar-ui-common/helpers/l10n';
33 ProjectAlmBindingResponse
34 } from '../../../../types/alm-settings';
35 import InputForBoolean from '../inputs/InputForBoolean';
37 export interface PRDecorationBindingRendererProps {
38 formData: T.Omit<ProjectAlmBindingResponse, 'alm'>;
39 instances: AlmSettingsInstance[];
41 isConfigured: boolean;
44 onFieldChange: (id: keyof ProjectAlmBindingResponse, value: string | boolean) => void;
51 interface LabelProps {
53 helpParams?: T.Dict<string | JSX.Element>;
58 interface CommonFieldProps extends LabelProps {
59 onFieldChange: (id: keyof ProjectAlmBindingResponse, value: string | boolean) => void;
60 propKey: keyof ProjectAlmBindingResponse;
63 function optionRenderer(instance: AlmSettingsInstance) {
64 return instance.url ? (
66 <span>{instance.key} — </span>
67 <span className="text-muted">{instance.url}</span>
70 <span>{instance.key}</span>
74 function renderLabel(props: LabelProps) {
75 const { help, helpParams, optional, id } = props;
77 <label className="display-flex-center" htmlFor={id}>
78 {translate('settings.pr_decoration.binding.form', id)}
79 {!optional && <em className="mandatory">*</em>}
82 className="spacer-left"
85 defaultMessage={translate('settings.pr_decoration.binding.form', id, 'help')}
86 id={`settings.pr_decoration.binding.form.${id}.help`}
97 function renderBooleanField(
98 props: Omit<CommonFieldProps, 'optional'> & {
102 const { id, value, onFieldChange, propKey } = props;
104 <div className="form-field">
105 {renderLabel({ ...props, optional: true })}
109 onChange={v => onFieldChange(propKey, v)}
116 function renderField(
117 props: CommonFieldProps & {
121 const { id, propKey, value, onFieldChange } = props;
123 <div className="form-field">
126 className="input-super-large"
130 onChange={e => onFieldChange(propKey, e.currentTarget.value)}
138 export default function PRDecorationBindingRenderer(props: PRDecorationBindingRendererProps) {
140 formData: { key, repository, slug, summaryCommentEnabled },
151 return <DeferredSpinner />;
154 if (instances.length < 1) {
157 <Alert className="spacer-top huge-spacer-bottom" variant="info">
159 defaultMessage={translate('settings.pr_decoration.binding.no_bindings')}
160 id="settings.pr_decoration.binding.no_bindings"
163 <Link to="/documentation/analysis/pull-request/#pr-decoration">
164 {translate('learn_more')}
174 const selected = key && instances.find(i => i.key === key);
175 const alm = selected && selected.alm;
179 <header className="page-header">
180 <h1 className="page-title">{translate('settings.pr_decoration.binding.title')}</h1>
183 <div className="markdown small spacer-top big-spacer-bottom">
184 {translate('settings.pr_decoration.binding.description')}
188 onSubmit={(event: React.SyntheticEvent<HTMLFormElement>) => {
189 event.preventDefault();
192 <div className="form-field">
193 <label htmlFor="name">
194 {translate('settings.pr_decoration.binding.form.name')}
195 <em className="mandatory spacer-right">*</em>
199 className="abs-width-400"
202 menuContainerStyle={{
203 maxWidth: '210%' /* Allow double the width of the select */,
206 onChange={(instance: AlmSettingsInstance) => props.onFieldChange('key', instance.key)}
207 optionRenderer={optionRenderer}
212 valueRenderer={optionRenderer}
216 {alm === AlmKeys.Bitbucket && (
224 <strong>{'{KEY}'}</strong>
225 {'/repos/{SLUG}/browse'}
229 id: 'bitbucket.repository',
230 onFieldChange: props.onFieldChange,
231 propKey: 'repository',
232 value: repository || ''
239 {'.../projects/{KEY}/repos/'}
240 <strong>{'{SLUG}'}</strong>
245 id: 'bitbucket.slug',
246 onFieldChange: props.onFieldChange,
253 {alm === AlmKeys.GitHub && (
257 helpParams: { example: 'SonarSource/sonarqube' },
258 id: 'github.repository',
259 onFieldChange: props.onFieldChange,
260 propKey: 'repository',
261 value: repository || ''
263 {renderBooleanField({
265 id: 'github.summary_comment_setting',
266 onFieldChange: props.onFieldChange,
267 propKey: 'summaryCommentEnabled',
268 value: summaryCommentEnabled === undefined ? true : summaryCommentEnabled
273 {alm === AlmKeys.GitLab &&
276 id: 'gitlab.repository',
277 onFieldChange: props.onFieldChange,
279 propKey: 'repository',
280 value: repository || ''
283 <div className="display-flex-center">
284 <DeferredSpinner className="spacer-right" loading={saving} />
286 <SubmitButton className="spacer-right button-success" disabled={saving || !isValid}>
287 <span data-test="project-settings__alm-save">{translate('save')}</span>
291 <Button className="spacer-right" onClick={props.onReset}>
292 <span data-test="project-settings__alm-reset">{translate('reset_verb')}</span>
295 {!saving && success && (
296 <span className="text-success">
297 <AlertSuccessIcon className="spacer-right" />
298 {translate('settings.state.saved')}