Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

AlmSpecificForm.tsx 8.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2021 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  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.
  10. *
  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.
  15. *
  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.
  19. */
  20. import * as React from 'react';
  21. import { FormattedMessage } from 'react-intl';
  22. import { Link } from 'react-router';
  23. import Toggle from '../../../../components/controls/Toggle';
  24. import { Alert } from '../../../../components/ui/Alert';
  25. import MandatoryFieldMarker from '../../../../components/ui/MandatoryFieldMarker';
  26. import { ALM_DOCUMENTATION_PATHS } from '../../../../helpers/constants';
  27. import { translate } from '../../../../helpers/l10n';
  28. import { convertGithubApiUrlToLink, stripTrailingSlash } from '../../../../helpers/urls';
  29. import {
  30. AlmKeys,
  31. AlmSettingsInstance,
  32. ProjectAlmBindingResponse
  33. } from '../../../../types/alm-settings';
  34. export interface AlmSpecificFormProps {
  35. alm: AlmKeys;
  36. instances: AlmSettingsInstance[];
  37. formData: T.Omit<ProjectAlmBindingResponse, 'alm'>;
  38. onFieldChange: (id: keyof ProjectAlmBindingResponse, value: string | boolean) => void;
  39. monorepoEnabled: boolean;
  40. }
  41. interface LabelProps {
  42. id: string;
  43. optional?: boolean;
  44. }
  45. interface CommonFieldProps extends LabelProps {
  46. help?: boolean;
  47. helpParams?: T.Dict<string | JSX.Element>;
  48. helpExample?: JSX.Element;
  49. onFieldChange: (id: keyof ProjectAlmBindingResponse, value: string | boolean) => void;
  50. propKey: keyof ProjectAlmBindingResponse;
  51. }
  52. function renderFieldWrapper(
  53. label: React.ReactNode,
  54. input: React.ReactNode,
  55. help?: React.ReactNode
  56. ) {
  57. return (
  58. <div className="settings-definition">
  59. <div className="settings-definition-left">
  60. {label}
  61. {help && <div className="markdown small spacer-top">{help}</div>}
  62. </div>
  63. <div className="settings-definition-right padded-top">{input}</div>
  64. </div>
  65. );
  66. }
  67. function renderHelp({ help, helpExample, helpParams, id }: CommonFieldProps) {
  68. return (
  69. help && (
  70. <>
  71. <FormattedMessage
  72. defaultMessage={translate('settings.pr_decoration.binding.form', id, 'help')}
  73. id={`settings.pr_decoration.binding.form.${id}.help`}
  74. values={helpParams}
  75. />
  76. {helpExample && (
  77. <div className="spacer-top nowrap">
  78. {translate('example')}: <em>{helpExample}</em>
  79. </div>
  80. )}
  81. </>
  82. )
  83. );
  84. }
  85. function renderLabel(props: LabelProps) {
  86. const { optional, id } = props;
  87. return (
  88. <label className="h3" htmlFor={id}>
  89. {translate('settings.pr_decoration.binding.form', id)}
  90. {!optional && <MandatoryFieldMarker />}
  91. </label>
  92. );
  93. }
  94. function renderBooleanField(
  95. props: Omit<CommonFieldProps, 'optional'> & {
  96. value: boolean;
  97. inputExtra?: React.ReactNode;
  98. }
  99. ) {
  100. const { id, value, onFieldChange, propKey, inputExtra } = props;
  101. return renderFieldWrapper(
  102. renderLabel({ ...props, optional: true }),
  103. <div className="display-flex-center big-spacer-top">
  104. <div className="display-inline-block text-top">
  105. <Toggle name={id} onChange={v => onFieldChange(propKey, v)} value={value} />
  106. {value == null && <span className="spacer-left note">{translate('settings.not_set')}</span>}
  107. </div>
  108. {inputExtra}
  109. </div>,
  110. renderHelp(props)
  111. );
  112. }
  113. function renderField(
  114. props: CommonFieldProps & {
  115. value: string;
  116. }
  117. ) {
  118. const { id, propKey, value, onFieldChange } = props;
  119. return renderFieldWrapper(
  120. renderLabel(props),
  121. <input
  122. className="input-super-large big-spacer-top"
  123. id={id}
  124. maxLength={256}
  125. name={id}
  126. onChange={e => onFieldChange(propKey, e.currentTarget.value)}
  127. type="text"
  128. value={value}
  129. />,
  130. renderHelp(props)
  131. );
  132. }
  133. export default function AlmSpecificForm(props: AlmSpecificFormProps) {
  134. const {
  135. alm,
  136. instances,
  137. formData: { repository, slug, summaryCommentEnabled, monorepo },
  138. monorepoEnabled
  139. } = props;
  140. let formFields: JSX.Element;
  141. const instance = instances.find(i => i.alm === alm);
  142. switch (alm) {
  143. case AlmKeys.Azure:
  144. formFields = (
  145. <>
  146. {renderField({
  147. help: true,
  148. helpExample: <strong>My Project</strong>,
  149. id: 'azure.project',
  150. onFieldChange: props.onFieldChange,
  151. propKey: 'slug',
  152. value: slug || ''
  153. })}
  154. {renderField({
  155. help: true,
  156. helpExample: <strong>My Repository</strong>,
  157. id: 'azure.repository',
  158. onFieldChange: props.onFieldChange,
  159. propKey: 'repository',
  160. value: repository || ''
  161. })}
  162. </>
  163. );
  164. break;
  165. case AlmKeys.BitbucketServer:
  166. formFields = (
  167. <>
  168. {renderField({
  169. help: true,
  170. helpExample: (
  171. <>
  172. {instance?.url
  173. ? `${stripTrailingSlash(instance.url)}/projects/`
  174. : 'https://bb.company.com/projects/'}
  175. <strong>{'MY_PROJECT_KEY'}</strong>
  176. {'/repos/my-repository-slug/browse'}
  177. </>
  178. ),
  179. id: 'bitbucket.repository',
  180. onFieldChange: props.onFieldChange,
  181. propKey: 'repository',
  182. value: repository || ''
  183. })}
  184. {renderField({
  185. help: true,
  186. helpExample: (
  187. <>
  188. {instance?.url
  189. ? `${stripTrailingSlash(instance.url)}/projects/MY_PROJECT_KEY/repos/`
  190. : 'https://bb.company.com/projects/MY_PROJECT_KEY/repos/'}
  191. <strong>{'my-repository-slug'}</strong>
  192. {'/browse'}
  193. </>
  194. ),
  195. id: 'bitbucket.slug',
  196. onFieldChange: props.onFieldChange,
  197. propKey: 'slug',
  198. value: slug || ''
  199. })}
  200. </>
  201. );
  202. break;
  203. case AlmKeys.BitbucketCloud:
  204. formFields = (
  205. <>
  206. {renderField({
  207. help: true,
  208. helpExample: (
  209. <>
  210. {'https://bitbucket.org/my-workspace/'}
  211. <strong>{'my-repository-slug'}</strong>
  212. </>
  213. ),
  214. id: 'bitbucketcloud.repository',
  215. onFieldChange: props.onFieldChange,
  216. propKey: 'repository',
  217. value: repository || ''
  218. })}
  219. </>
  220. );
  221. break;
  222. case AlmKeys.GitHub:
  223. formFields = (
  224. <>
  225. {renderField({
  226. help: true,
  227. helpExample: (
  228. <>
  229. {instance?.url
  230. ? `${stripTrailingSlash(convertGithubApiUrlToLink(instance.url))}/`
  231. : 'https://github.com/'}
  232. <strong>{'sonarsource/sonarqube'}</strong>
  233. </>
  234. ),
  235. id: 'github.repository',
  236. onFieldChange: props.onFieldChange,
  237. propKey: 'repository',
  238. value: repository || ''
  239. })}
  240. {renderBooleanField({
  241. help: true,
  242. id: 'github.summary_comment_setting',
  243. onFieldChange: props.onFieldChange,
  244. propKey: 'summaryCommentEnabled',
  245. value: summaryCommentEnabled === undefined ? true : summaryCommentEnabled
  246. })}
  247. </>
  248. );
  249. break;
  250. case AlmKeys.GitLab:
  251. formFields = (
  252. <>
  253. {renderField({
  254. help: true,
  255. helpExample: <strong>123456</strong>,
  256. id: 'gitlab.repository',
  257. onFieldChange: props.onFieldChange,
  258. propKey: 'repository',
  259. value: repository || ''
  260. })}
  261. </>
  262. );
  263. break;
  264. }
  265. return (
  266. <>
  267. {formFields}
  268. {monorepoEnabled &&
  269. renderBooleanField({
  270. help: true,
  271. helpParams: {
  272. doc_link: (
  273. <Link to={ALM_DOCUMENTATION_PATHS[alm]} target="_blank">
  274. {translate('learn_more')}
  275. </Link>
  276. )
  277. },
  278. id: 'monorepo',
  279. onFieldChange: props.onFieldChange,
  280. propKey: 'monorepo',
  281. value: monorepo,
  282. inputExtra: monorepo && (
  283. <Alert className="no-margin-bottom spacer-left" variant="warning" display="inline">
  284. {translate('settings.pr_decoration.binding.form.monorepo.warning')}
  285. </Alert>
  286. )
  287. })}
  288. </>
  289. );
  290. }