]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9867 Add button promoting governance on background tasks page
authorStas Vilchik <stas.vilchik@sonarsource.com>
Wed, 11 Oct 2017 14:18:48 +0000 (16:18 +0200)
committerStas Vilchik <stas.vilchik@sonarsource.com>
Thu, 12 Oct 2017 08:23:25 +0000 (10:23 +0200)
20 files changed:
server/sonar-web/src/main/js/api/ce.ts
server/sonar-web/src/main/js/app/components/nav/component/NoBranchSupportPopup.tsx
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/NoBranchSupportPopup-test.tsx.snap
server/sonar-web/src/main/js/apps/background-tasks/components/Header.js [deleted file]
server/sonar-web/src/main/js/apps/background-tasks/components/Header.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/background-tasks/components/NoWorkersSupportPopup.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/background-tasks/components/Workers.js [deleted file]
server/sonar-web/src/main/js/apps/background-tasks/components/Workers.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/background-tasks/components/WorkersForm.js [deleted file]
server/sonar-web/src/main/js/apps/background-tasks/components/WorkersForm.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/Workers-test.js [deleted file]
server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/Workers-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/WorkersForm-test.js [deleted file]
server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/WorkersForm-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/Workers-test.js.snap [deleted file]
server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/Workers-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/WorkersForm-test.js.snap [deleted file]
server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/WorkersForm-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/less/components/bubble-popup.less
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index a61b0e2051b484aa669575cb74ad446238a622ab..33be5602977bf19783fa59ffd9f99f8e236adeae 100644 (file)
@@ -81,7 +81,7 @@ export function getTypes(): Promise<any> {
   return getJSON('/api/ce/task_types').then(r => r.taskTypes);
 }
 
-export function getWorkers(): Promise<{ canSetWorkerCount: boolean; value: number } | Response> {
+export function getWorkers(): Promise<{ canSetWorkerCount: boolean; value: number }> {
   return getJSON('/api/ce/worker_count').catch(throwGlobalError);
 }
 
index 70cfb0edb289471af5d845c669e037c2708318cb..35ac2dbcdd4fe3756a510c08adfb6bf6e432150a 100644 (file)
@@ -33,7 +33,7 @@ export default function NoBranchSupportPopup(props: Props) {
         <p className="big-spacer-bottom markdown">{translate('branches.no_support.header.text')}</p>
         <p>
           <a href="https://redirect.sonarsource.com/doc/branches.html" target="_blank">
-            {translate('branches.learn_more')}
+            {translate('learn_more')}
           </a>
           <a
             className="button spacer-left"
index 8fb3b04224f3e158d5745fafb3de7ecce56e27d0..c7563e9c83b28c42f593d6da27a1fe26e790721e 100644 (file)
@@ -22,7 +22,7 @@ exports[`renders 1`] = `
         href="https://redirect.sonarsource.com/doc/branches.html"
         target="_blank"
       >
-        branches.learn_more
+        learn_more
       </a>
       <a
         className="button spacer-left"
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/Header.js b/server/sonar-web/src/main/js/apps/background-tasks/components/Header.js
deleted file mode 100644 (file)
index 6aa13b2..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.
- */
-/* @flow */
-import React from 'react';
-import Workers from './Workers';
-import { translate } from '../../../helpers/l10n';
-
-/*::
-type Props = {
-  component?: Object
-};
-*/
-
-export default function Header(props /*: Props */) {
-  return (
-    <header className="page-header">
-      <h1 className="page-title">{translate('background_tasks.page')}</h1>
-      {!props.component && (
-        <div className="page-actions">
-          <Workers />
-        </div>
-      )}
-      <p className="page-description">{translate('background_tasks.page.description')}</p>
-    </header>
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/Header.tsx b/server/sonar-web/src/main/js/apps/background-tasks/components/Header.tsx
new file mode 100644 (file)
index 0000000..c92de39
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 Workers from './Workers';
+import { translate } from '../../../helpers/l10n';
+
+interface Props {
+  component?: any;
+}
+
+export default function Header(props: Props) {
+  return (
+    <header className="page-header">
+      <h1 className="page-title">{translate('background_tasks.page')}</h1>
+      {!props.component && (
+        <div className="page-actions">
+          <Workers />
+        </div>
+      )}
+      <p className="page-description">{translate('background_tasks.page.description')}</p>
+    </header>
+  );
+}
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/NoWorkersSupportPopup.tsx b/server/sonar-web/src/main/js/apps/background-tasks/components/NoWorkersSupportPopup.tsx
new file mode 100644 (file)
index 0000000..c6e18f5
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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 BubblePopup from '../../../components/common/BubblePopup';
+import { translate } from '../../../helpers/l10n';
+
+interface Props {
+  popupPosition?: any;
+}
+
+export default function NoWorkersSupportPopup(props: Props) {
+  return (
+    <BubblePopup position={props.popupPosition} customClass="bubble-popup-bottom-right">
+      <div className="abs-width-400">
+        <h6 className="spacer-bottom">{translate('background_tasks.add_more_workers')}</h6>
+        <p className="big-spacer-bottom markdown">
+          {translate('background_tasks.add_more_workers.text')}
+        </p>
+        <p>
+          <a href="https://redirect.sonarsource.com/plugins/governance.html" target="_blank">
+            {translate('learn_more')}
+          </a>
+        </p>
+      </div>
+    </BubblePopup>
+  );
+}
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/Workers.js b/server/sonar-web/src/main/js/apps/background-tasks/components/Workers.js
deleted file mode 100644 (file)
index 670cca4..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.
- */
-// @flow
-import React from 'react';
-import WorkersForm from './WorkersForm';
-import Tooltip from '../../../components/controls/Tooltip';
-import { getWorkers } from '../../../api/ce';
-import { translate } from '../../../helpers/l10n';
-
-/*::
-type State = {
-  canSetWorkerCount: boolean,
-  formOpen: boolean,
-  loading: boolean,
-  workerCount: number
-};
-*/
-
-export default class Workers extends React.PureComponent {
-  /*:: mounted: boolean; */
-  state /*: State */ = {
-    canSetWorkerCount: false,
-    formOpen: false,
-    loading: true,
-    workerCount: 1
-  };
-
-  componentDidMount() {
-    this.mounted = true;
-    this.loadWorkers();
-  }
-
-  componentWillUnmount() {
-    this.mounted = false;
-  }
-
-  loadWorkers = () => {
-    this.setState({ loading: true });
-    getWorkers().then(({ canSetWorkerCount, value }) => {
-      if (this.mounted) {
-        this.setState({
-          canSetWorkerCount,
-          loading: false,
-          workerCount: value
-        });
-      }
-    });
-  };
-
-  closeForm = (newWorkerCount /*: ?number */) =>
-    newWorkerCount
-      ? this.setState({ formOpen: false, workerCount: newWorkerCount })
-      : this.setState({ formOpen: false });
-
-  handleChangeClick = (event /*: Event */) => {
-    event.preventDefault();
-    this.setState({ formOpen: true });
-  };
-
-  render() {
-    const { canSetWorkerCount, formOpen, loading, workerCount } = this.state;
-
-    return (
-      <div>
-        {!loading &&
-        workerCount > 1 && (
-          <Tooltip overlay={translate('background_tasks.number_of_workers.warning')}>
-            <i className="icon-alert-warn little-spacer-right bt-workers-warning-icon" />
-          </Tooltip>
-        )}
-
-        {translate('background_tasks.number_of_workers')}
-
-        {loading ? (
-          <i className="spinner little-spacer-left" />
-        ) : (
-          <strong className="little-spacer-left">{workerCount}</strong>
-        )}
-
-        {!loading &&
-        canSetWorkerCount && (
-          <Tooltip overlay={translate('background_tasks.change_number_of_workers')}>
-            <a className="icon-edit spacer-left" href="#" onClick={this.handleChangeClick} />
-          </Tooltip>
-        )}
-
-        {formOpen && <WorkersForm onClose={this.closeForm} workerCount={this.state.workerCount} />}
-      </div>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/Workers.tsx b/server/sonar-web/src/main/js/apps/background-tasks/components/Workers.tsx
new file mode 100644 (file)
index 0000000..9c51b4a
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 WorkersForm from './WorkersForm';
+import NoWorkersSupportPopup from './NoWorkersSupportPopup';
+import Tooltip from '../../../components/controls/Tooltip';
+import { getWorkers } from '../../../api/ce';
+import { translate } from '../../../helpers/l10n';
+import HelpIcon from '../../../components/icons-components/HelpIcon';
+import BubblePopupHelper from '../../../components/common/BubblePopupHelper';
+
+interface State {
+  canSetWorkerCount: boolean;
+  formOpen: boolean;
+  loading: boolean;
+  noSupportPopup: boolean;
+  workerCount: number;
+}
+
+export default class Workers extends React.PureComponent<{}, State> {
+  mounted: boolean;
+  state: State = {
+    canSetWorkerCount: false,
+    formOpen: false,
+    loading: true,
+    noSupportPopup: false,
+    workerCount: 1
+  };
+
+  componentDidMount() {
+    this.mounted = true;
+    this.loadWorkers();
+  }
+
+  componentWillUnmount() {
+    this.mounted = false;
+  }
+
+  loadWorkers = () => {
+    this.setState({ loading: true });
+    getWorkers().then(({ canSetWorkerCount, value }) => {
+      if (this.mounted) {
+        this.setState({
+          canSetWorkerCount,
+          loading: false,
+          workerCount: value
+        });
+      }
+    });
+  };
+
+  closeForm = (newWorkerCount?: number) =>
+    newWorkerCount
+      ? this.setState({ formOpen: false, workerCount: newWorkerCount })
+      : this.setState({ formOpen: false });
+
+  handleChangeClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
+    event.preventDefault();
+    this.setState({ formOpen: true });
+  };
+
+  handleHelpClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
+    event.preventDefault();
+    event.stopPropagation();
+    this.toggleNoSupportPopup();
+  };
+
+  toggleNoSupportPopup = (show?: boolean) => {
+    if (show != undefined) {
+      this.setState({ noSupportPopup: show });
+    } else {
+      this.setState(state => ({ noSupportPopup: !state.noSupportPopup }));
+    }
+  };
+
+  render() {
+    const { canSetWorkerCount, formOpen, loading, workerCount } = this.state;
+
+    return (
+      <div>
+        {!loading &&
+        workerCount > 1 && (
+          <Tooltip overlay={translate('background_tasks.number_of_workers.warning')}>
+            <i className="icon-alert-warn little-spacer-right bt-workers-warning-icon" />
+          </Tooltip>
+        )}
+
+        {translate('background_tasks.number_of_workers')}
+
+        {loading ? (
+          <i className="spinner little-spacer-left" />
+        ) : (
+          <strong className="little-spacer-left">{workerCount}</strong>
+        )}
+
+        {!loading &&
+        canSetWorkerCount && (
+          <Tooltip overlay={translate('background_tasks.change_number_of_workers')}>
+            <a className="icon-edit spacer-left" href="#" onClick={this.handleChangeClick} />
+          </Tooltip>
+        )}
+
+        {!loading &&
+        !canSetWorkerCount && (
+          <span className="spacer-left">
+            <a className="link-no-underline" href="#" onClick={this.handleHelpClick}>
+              <HelpIcon className="text-text-bottom" fill="#cdcdcd" />
+            </a>
+            <BubblePopupHelper
+              isOpen={this.state.noSupportPopup}
+              position="bottomright"
+              popup={<NoWorkersSupportPopup />}
+              togglePopup={this.toggleNoSupportPopup}
+            />
+          </span>
+        )}
+
+        {formOpen && <WorkersForm onClose={this.closeForm} workerCount={this.state.workerCount} />}
+      </div>
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/WorkersForm.js b/server/sonar-web/src/main/js/apps/background-tasks/components/WorkersForm.js
deleted file mode 100644 (file)
index ded18f7..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.
- */
-// @flow
-import React from 'react';
-import Modal from 'react-modal';
-import Select from 'react-select';
-import { times } from 'lodash';
-import { setWorkerCount } from '../../../api/ce';
-import { translate } from '../../../helpers/l10n';
-
-const MAX_WORKERS = 10;
-
-/*::
-type Props = {
-  onClose: (newWorkerCount?: number) => void,
-  workerCount: number
-};
-*/
-
-/*::
-type State = {
-  newWorkerCount: number,
-  submitting: boolean
-};
-*/
-
-export default class WorkersForm extends React.PureComponent {
-  /*:: mounted: boolean; */
-  /*:: props: Props; */
-  /*:: state: State; */
-
-  constructor(props /*: Props */) {
-    super(props);
-    this.state = {
-      newWorkerCount: props.workerCount,
-      submitting: false
-    };
-  }
-
-  componentDidMount() {
-    this.mounted = true;
-  }
-
-  componentWillUnmount() {
-    this.mounted = false;
-  }
-
-  handleClose = () => this.props.onClose();
-
-  handleWorkerCountChange = (option /*: { value: number } */) =>
-    this.setState({ newWorkerCount: option.value });
-
-  handleSubmit = (event /*: Event */) => {
-    event.preventDefault();
-    this.setState({ submitting: true });
-    const { newWorkerCount } = this.state;
-    setWorkerCount(newWorkerCount).then(
-      () => {
-        if (this.mounted) {
-          this.props.onClose(newWorkerCount);
-        }
-      },
-      () => {
-        if (this.mounted) {
-          this.setState({ submitting: false });
-        }
-      }
-    );
-  };
-
-  render() {
-    const options = times(MAX_WORKERS).map((_, i) => ({ label: i + 1, value: i + 1 }));
-
-    return (
-      <Modal
-        isOpen={true}
-        contentLabel={translate('background_tasks.change_number_of_workers')}
-        className="modal"
-        overlayClassName="modal-overlay"
-        onRequestClose={this.handleClose}>
-        <header className="modal-head">
-          <h2>{translate('background_tasks.change_number_of_workers')}</h2>
-        </header>
-        <form onSubmit={this.handleSubmit}>
-          <div className="modal-body">
-            <Select
-              className="input-tiny spacer-top"
-              clearable={false}
-              onChange={this.handleWorkerCountChange}
-              options={options}
-              searchable={false}
-              value={this.state.newWorkerCount}
-            />
-            <div className="big-spacer-top alert alert-success markdown">
-              {translate('background_tasks.change_number_of_workers.hint')}
-            </div>
-          </div>
-          <footer className="modal-foot">
-            <div>
-              {this.state.submitting && <i className="spinner spacer-right" />}
-              <button disabled={this.state.submitting} type="submit">
-                {translate('save')}
-              </button>
-              <button type="reset" className="button-link" onClick={this.handleClose}>
-                {translate('cancel')}
-              </button>
-            </div>
-          </footer>
-        </form>
-      </Modal>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/WorkersForm.tsx b/server/sonar-web/src/main/js/apps/background-tasks/components/WorkersForm.tsx
new file mode 100644 (file)
index 0000000..1d4854b
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 Modal from 'react-modal';
+import * as Select from 'react-select';
+import { times } from 'lodash';
+import { setWorkerCount } from '../../../api/ce';
+import { translate } from '../../../helpers/l10n';
+
+const MAX_WORKERS = 10;
+
+interface Props {
+  onClose: (newWorkerCount?: number) => void;
+  workerCount: number;
+}
+
+interface State {
+  newWorkerCount: number;
+  submitting: boolean;
+}
+
+export default class WorkersForm extends React.PureComponent<Props, State> {
+  mounted: boolean;
+
+  constructor(props: Props) {
+    super(props);
+    this.state = {
+      newWorkerCount: props.workerCount,
+      submitting: false
+    };
+  }
+
+  componentDidMount() {
+    this.mounted = true;
+  }
+
+  componentWillUnmount() {
+    this.mounted = false;
+  }
+
+  handleClose = () => this.props.onClose();
+
+  handleWorkerCountChange = (option: { value: number }) =>
+    this.setState({ newWorkerCount: option.value });
+
+  handleSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
+    event.preventDefault();
+    this.setState({ submitting: true });
+    const { newWorkerCount } = this.state;
+    setWorkerCount(newWorkerCount).then(
+      () => {
+        if (this.mounted) {
+          this.props.onClose(newWorkerCount);
+        }
+      },
+      () => {
+        if (this.mounted) {
+          this.setState({ submitting: false });
+        }
+      }
+    );
+  };
+
+  render() {
+    const options = times(MAX_WORKERS).map((_, i) => ({ label: String(i + 1), value: i + 1 }));
+
+    return (
+      <Modal
+        isOpen={true}
+        contentLabel={translate('background_tasks.change_number_of_workers')}
+        className="modal"
+        overlayClassName="modal-overlay"
+        onRequestClose={this.handleClose}>
+        <header className="modal-head">
+          <h2>{translate('background_tasks.change_number_of_workers')}</h2>
+        </header>
+        <form onSubmit={this.handleSubmit}>
+          <div className="modal-body">
+            <Select
+              className="input-tiny spacer-top"
+              clearable={false}
+              onChange={this.handleWorkerCountChange}
+              options={options}
+              searchable={false}
+              value={this.state.newWorkerCount}
+            />
+            <div className="big-spacer-top alert alert-success markdown">
+              {translate('background_tasks.change_number_of_workers.hint')}
+            </div>
+          </div>
+          <footer className="modal-foot">
+            <div>
+              {this.state.submitting && <i className="spinner spacer-right" />}
+              <button disabled={this.state.submitting} type="submit">
+                {translate('save')}
+              </button>
+              <button type="reset" className="button-link" onClick={this.handleClose}>
+                {translate('cancel')}
+              </button>
+            </div>
+          </footer>
+        </form>
+      </Modal>
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/Workers-test.js b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/Workers-test.js
deleted file mode 100644 (file)
index 5ad0643..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.
- */
-// @flow
-import React from 'react';
-import { shallow } from 'enzyme';
-import Workers from '../Workers';
-import { click } from '../../../../helpers/testUtils';
-
-it('renders', () => {
-  const wrapper = shallow(<Workers />);
-  expect(wrapper).toMatchSnapshot();
-
-  wrapper.setState({
-    canSetWorkerCount: true,
-    loading: false,
-    workerCount: 1
-  });
-  expect(wrapper).toMatchSnapshot();
-
-  wrapper.setState({ canSetWorkerCount: false });
-  expect(wrapper).toMatchSnapshot();
-
-  wrapper.setState({ workerCount: 2 });
-  expect(wrapper).toMatchSnapshot();
-});
-
-it('opens form', () => {
-  const wrapper = shallow(<Workers />);
-
-  wrapper.setState({
-    canSetWorkerCount: true,
-    loading: false,
-    workerCount: 1
-  });
-  expect(wrapper).toMatchSnapshot();
-
-  click(wrapper.find('.icon-edit'));
-  expect(wrapper).toMatchSnapshot();
-});
-
-it('updates worker count', () => {
-  const wrapper = shallow(<Workers />);
-
-  wrapper.setState({
-    canSetWorkerCount: true,
-    formOpen: true,
-    loading: false,
-    workerCount: 1
-  });
-  expect(wrapper).toMatchSnapshot();
-
-  wrapper.find('WorkersForm').prop('onClose')(7);
-  wrapper.update();
-  expect(wrapper).toMatchSnapshot();
-});
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/Workers-test.tsx b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/Workers-test.tsx
new file mode 100644 (file)
index 0000000..953e79d
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 Workers from '../Workers';
+import { click } from '../../../../helpers/testUtils';
+
+it('renders', () => {
+  const wrapper = shallow(<Workers />);
+  expect(wrapper).toMatchSnapshot();
+
+  wrapper.setState({
+    canSetWorkerCount: true,
+    loading: false,
+    workerCount: 1
+  });
+  expect(wrapper).toMatchSnapshot();
+
+  wrapper.setState({ workerCount: 2 });
+  expect(wrapper).toMatchSnapshot();
+
+  wrapper.setState({ canSetWorkerCount: false });
+  expect(wrapper).toMatchSnapshot();
+});
+
+it('opens form', () => {
+  const wrapper = shallow(<Workers />);
+
+  wrapper.setState({
+    canSetWorkerCount: true,
+    loading: false,
+    workerCount: 1
+  });
+  expect(wrapper).toMatchSnapshot();
+
+  click(wrapper.find('.icon-edit'));
+  expect(wrapper).toMatchSnapshot();
+});
+
+it('updates worker count', () => {
+  const wrapper = shallow(<Workers />);
+
+  wrapper.setState({
+    canSetWorkerCount: true,
+    formOpen: true,
+    loading: false,
+    workerCount: 1
+  });
+  expect(wrapper).toMatchSnapshot();
+
+  wrapper.find('WorkersForm').prop<Function>('onClose')(7);
+  wrapper.update();
+  expect(wrapper).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/WorkersForm-test.js b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/WorkersForm-test.js
deleted file mode 100644 (file)
index f821ba6..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 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.
- */
-// @flow
-import React from 'react';
-import { shallow } from 'enzyme';
-import WorkersForm from '../WorkersForm';
-import { submit, doAsync } from '../../../../helpers/testUtils';
-
-jest.mock('../../../../api/ce', () => ({
-  setWorkerCount: () => Promise.resolve()
-}));
-
-it('changes select', () => {
-  const wrapper = shallow(<WorkersForm onClose={jest.fn()} workerCount={1} />);
-  expect(wrapper).toMatchSnapshot();
-
-  wrapper.find('Select').prop('onChange')({ value: 7 });
-  wrapper.update();
-  expect(wrapper).toMatchSnapshot();
-});
-
-it('returns new worker count', () => {
-  const onClose = jest.fn();
-  const wrapper = shallow(<WorkersForm onClose={onClose} workerCount={1} />);
-  // $FlowFixMe
-  wrapper.instance().mounted = true;
-  wrapper.find('Select').prop('onChange')({ value: 7 });
-
-  wrapper.update();
-  submit(wrapper.find('form'));
-
-  return doAsync(() => {
-    expect(onClose).toBeCalled();
-  });
-});
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/WorkersForm-test.tsx b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/WorkersForm-test.tsx
new file mode 100644 (file)
index 0000000..813eff2
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.
+ */
+jest.mock('../../../../api/ce', () => ({
+  setWorkerCount: () => Promise.resolve()
+}));
+
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import WorkersForm from '../WorkersForm';
+import { submit } from '../../../../helpers/testUtils';
+
+it('changes select', () => {
+  const wrapper = shallow(<WorkersForm onClose={jest.fn()} workerCount={1} />);
+  expect(wrapper).toMatchSnapshot();
+
+  wrapper.find('Select').prop<Function>('onChange')({ value: 7 });
+  wrapper.update();
+  expect(wrapper).toMatchSnapshot();
+});
+
+it('returns new worker count', async () => {
+  const onClose = jest.fn();
+  const wrapper = shallow(<WorkersForm onClose={onClose} workerCount={1} />);
+  (wrapper.instance() as WorkersForm).mounted = true;
+  wrapper.find('Select').prop<Function>('onChange')({ value: 7 });
+
+  wrapper.update();
+  submit(wrapper.find('form'));
+
+  await new Promise(setImmediate);
+  expect(onClose).toBeCalled();
+});
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/Workers-test.js.snap b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/Workers-test.js.snap
deleted file mode 100644 (file)
index 0a54eb4..0000000
+++ /dev/null
@@ -1,161 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`opens form 1`] = `
-<div>
-  background_tasks.number_of_workers
-  <strong
-    className="little-spacer-left"
-  >
-    1
-  </strong>
-  <Tooltip
-    overlay="background_tasks.change_number_of_workers"
-    placement="bottom"
-  >
-    <a
-      className="icon-edit spacer-left"
-      href="#"
-      onClick={[Function]}
-    />
-  </Tooltip>
-</div>
-`;
-
-exports[`opens form 2`] = `
-<div>
-  background_tasks.number_of_workers
-  <strong
-    className="little-spacer-left"
-  >
-    1
-  </strong>
-  <Tooltip
-    overlay="background_tasks.change_number_of_workers"
-    placement="bottom"
-  >
-    <a
-      className="icon-edit spacer-left"
-      href="#"
-      onClick={[Function]}
-    />
-  </Tooltip>
-  <WorkersForm
-    onClose={[Function]}
-    workerCount={1}
-  />
-</div>
-`;
-
-exports[`renders 1`] = `
-<div>
-  background_tasks.number_of_workers
-  <i
-    className="spinner little-spacer-left"
-  />
-</div>
-`;
-
-exports[`renders 2`] = `
-<div>
-  background_tasks.number_of_workers
-  <strong
-    className="little-spacer-left"
-  >
-    1
-  </strong>
-  <Tooltip
-    overlay="background_tasks.change_number_of_workers"
-    placement="bottom"
-  >
-    <a
-      className="icon-edit spacer-left"
-      href="#"
-      onClick={[Function]}
-    />
-  </Tooltip>
-</div>
-`;
-
-exports[`renders 3`] = `
-<div>
-  background_tasks.number_of_workers
-  <strong
-    className="little-spacer-left"
-  >
-    1
-  </strong>
-</div>
-`;
-
-exports[`renders 4`] = `
-<div>
-  <Tooltip
-    overlay="background_tasks.number_of_workers.warning"
-    placement="bottom"
-  >
-    <i
-      className="icon-alert-warn little-spacer-right bt-workers-warning-icon"
-    />
-  </Tooltip>
-  background_tasks.number_of_workers
-  <strong
-    className="little-spacer-left"
-  >
-    2
-  </strong>
-</div>
-`;
-
-exports[`updates worker count 1`] = `
-<div>
-  background_tasks.number_of_workers
-  <strong
-    className="little-spacer-left"
-  >
-    1
-  </strong>
-  <Tooltip
-    overlay="background_tasks.change_number_of_workers"
-    placement="bottom"
-  >
-    <a
-      className="icon-edit spacer-left"
-      href="#"
-      onClick={[Function]}
-    />
-  </Tooltip>
-  <WorkersForm
-    onClose={[Function]}
-    workerCount={1}
-  />
-</div>
-`;
-
-exports[`updates worker count 2`] = `
-<div>
-  <Tooltip
-    overlay="background_tasks.number_of_workers.warning"
-    placement="bottom"
-  >
-    <i
-      className="icon-alert-warn little-spacer-right bt-workers-warning-icon"
-    />
-  </Tooltip>
-  background_tasks.number_of_workers
-  <strong
-    className="little-spacer-left"
-  >
-    7
-  </strong>
-  <Tooltip
-    overlay="background_tasks.change_number_of_workers"
-    placement="bottom"
-  >
-    <a
-      className="icon-edit spacer-left"
-      href="#"
-      onClick={[Function]}
-    />
-  </Tooltip>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/Workers-test.tsx.snap b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/Workers-test.tsx.snap
new file mode 100644 (file)
index 0000000..d9d6b6f
--- /dev/null
@@ -0,0 +1,199 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`opens form 1`] = `
+<div>
+  background_tasks.number_of_workers
+  <strong
+    className="little-spacer-left"
+  >
+    1
+  </strong>
+  <Tooltip
+    overlay="background_tasks.change_number_of_workers"
+    placement="bottom"
+  >
+    <a
+      className="icon-edit spacer-left"
+      href="#"
+      onClick={[Function]}
+    />
+  </Tooltip>
+</div>
+`;
+
+exports[`opens form 2`] = `
+<div>
+  background_tasks.number_of_workers
+  <strong
+    className="little-spacer-left"
+  >
+    1
+  </strong>
+  <Tooltip
+    overlay="background_tasks.change_number_of_workers"
+    placement="bottom"
+  >
+    <a
+      className="icon-edit spacer-left"
+      href="#"
+      onClick={[Function]}
+    />
+  </Tooltip>
+  <WorkersForm
+    onClose={[Function]}
+    workerCount={1}
+  />
+</div>
+`;
+
+exports[`renders 1`] = `
+<div>
+  background_tasks.number_of_workers
+  <i
+    className="spinner little-spacer-left"
+  />
+</div>
+`;
+
+exports[`renders 2`] = `
+<div>
+  background_tasks.number_of_workers
+  <strong
+    className="little-spacer-left"
+  >
+    1
+  </strong>
+  <Tooltip
+    overlay="background_tasks.change_number_of_workers"
+    placement="bottom"
+  >
+    <a
+      className="icon-edit spacer-left"
+      href="#"
+      onClick={[Function]}
+    />
+  </Tooltip>
+</div>
+`;
+
+exports[`renders 3`] = `
+<div>
+  <Tooltip
+    overlay="background_tasks.number_of_workers.warning"
+    placement="bottom"
+  >
+    <i
+      className="icon-alert-warn little-spacer-right bt-workers-warning-icon"
+    />
+  </Tooltip>
+  background_tasks.number_of_workers
+  <strong
+    className="little-spacer-left"
+  >
+    2
+  </strong>
+  <Tooltip
+    overlay="background_tasks.change_number_of_workers"
+    placement="bottom"
+  >
+    <a
+      className="icon-edit spacer-left"
+      href="#"
+      onClick={[Function]}
+    />
+  </Tooltip>
+</div>
+`;
+
+exports[`renders 4`] = `
+<div>
+  <Tooltip
+    overlay="background_tasks.number_of_workers.warning"
+    placement="bottom"
+  >
+    <i
+      className="icon-alert-warn little-spacer-right bt-workers-warning-icon"
+    />
+  </Tooltip>
+  background_tasks.number_of_workers
+  <strong
+    className="little-spacer-left"
+  >
+    2
+  </strong>
+  <span
+    className="spacer-left"
+  >
+    <a
+      className="link-no-underline"
+      href="#"
+      onClick={[Function]}
+    >
+      <HelpIcon
+        className="text-text-bottom"
+        fill="#cdcdcd"
+      />
+    </a>
+    <BubblePopupHelper
+      isOpen={false}
+      popup={<NoWorkersSupportPopup />}
+      position="bottomright"
+      togglePopup={[Function]}
+    />
+  </span>
+</div>
+`;
+
+exports[`updates worker count 1`] = `
+<div>
+  background_tasks.number_of_workers
+  <strong
+    className="little-spacer-left"
+  >
+    1
+  </strong>
+  <Tooltip
+    overlay="background_tasks.change_number_of_workers"
+    placement="bottom"
+  >
+    <a
+      className="icon-edit spacer-left"
+      href="#"
+      onClick={[Function]}
+    />
+  </Tooltip>
+  <WorkersForm
+    onClose={[Function]}
+    workerCount={1}
+  />
+</div>
+`;
+
+exports[`updates worker count 2`] = `
+<div>
+  <Tooltip
+    overlay="background_tasks.number_of_workers.warning"
+    placement="bottom"
+  >
+    <i
+      className="icon-alert-warn little-spacer-right bt-workers-warning-icon"
+    />
+  </Tooltip>
+  background_tasks.number_of_workers
+  <strong
+    className="little-spacer-left"
+  >
+    7
+  </strong>
+  <Tooltip
+    overlay="background_tasks.change_number_of_workers"
+    placement="bottom"
+  >
+    <a
+      className="icon-edit spacer-left"
+      href="#"
+      onClick={[Function]}
+    />
+  </Tooltip>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/WorkersForm-test.js.snap b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/WorkersForm-test.js.snap
deleted file mode 100644 (file)
index 4bf13d5..0000000
+++ /dev/null
@@ -1,287 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`changes select 1`] = `
-<Modal
-  ariaHideApp={true}
-  bodyOpenClassName="ReactModal__Body--open"
-  className="modal"
-  closeTimeoutMS={0}
-  contentLabel="background_tasks.change_number_of_workers"
-  isOpen={true}
-  onRequestClose={[Function]}
-  overlayClassName="modal-overlay"
-  parentSelector={[Function]}
-  portalClassName="ReactModalPortal"
-  shouldCloseOnOverlayClick={true}
->
-  <header
-    className="modal-head"
-  >
-    <h2>
-      background_tasks.change_number_of_workers
-    </h2>
-  </header>
-  <form
-    onSubmit={[Function]}
-  >
-    <div
-      className="modal-body"
-    >
-      <Select
-        addLabelText="Add \\"{label}\\"?"
-        arrowRenderer={[Function]}
-        autosize={true}
-        backspaceRemoves={true}
-        backspaceToRemoveMessage="Press backspace to remove {label}"
-        className="input-tiny spacer-top"
-        clearAllText="Clear all"
-        clearRenderer={[Function]}
-        clearValueText="Clear value"
-        clearable={false}
-        deleteRemoves={true}
-        delimiter=","
-        disabled={false}
-        escapeClearsValue={true}
-        filterOptions={[Function]}
-        ignoreAccents={true}
-        ignoreCase={true}
-        inputProps={Object {}}
-        isLoading={false}
-        joinValues={false}
-        labelKey="label"
-        matchPos="any"
-        matchProp="any"
-        menuBuffer={0}
-        menuRenderer={[Function]}
-        multi={false}
-        noResultsText="No results found"
-        onBlurResetsInput={true}
-        onChange={[Function]}
-        onCloseResetsInput={true}
-        optionComponent={[Function]}
-        options={
-          Array [
-            Object {
-              "label": 1,
-              "value": 1,
-            },
-            Object {
-              "label": 2,
-              "value": 2,
-            },
-            Object {
-              "label": 3,
-              "value": 3,
-            },
-            Object {
-              "label": 4,
-              "value": 4,
-            },
-            Object {
-              "label": 5,
-              "value": 5,
-            },
-            Object {
-              "label": 6,
-              "value": 6,
-            },
-            Object {
-              "label": 7,
-              "value": 7,
-            },
-            Object {
-              "label": 8,
-              "value": 8,
-            },
-            Object {
-              "label": 9,
-              "value": 9,
-            },
-            Object {
-              "label": 10,
-              "value": 10,
-            },
-          ]
-        }
-        pageSize={5}
-        placeholder="Select..."
-        required={false}
-        scrollMenuIntoView={true}
-        searchable={false}
-        simpleValue={false}
-        tabSelectsValue={true}
-        value={1}
-        valueComponent={[Function]}
-        valueKey="value"
-      />
-      <div
-        className="big-spacer-top alert alert-success markdown"
-      >
-        background_tasks.change_number_of_workers.hint
-      </div>
-    </div>
-    <footer
-      className="modal-foot"
-    >
-      <div>
-        <button
-          disabled={false}
-          type="submit"
-        >
-          save
-        </button>
-        <button
-          className="button-link"
-          onClick={[Function]}
-          type="reset"
-        >
-          cancel
-        </button>
-      </div>
-    </footer>
-  </form>
-</Modal>
-`;
-
-exports[`changes select 2`] = `
-<Modal
-  ariaHideApp={true}
-  bodyOpenClassName="ReactModal__Body--open"
-  className="modal"
-  closeTimeoutMS={0}
-  contentLabel="background_tasks.change_number_of_workers"
-  isOpen={true}
-  onRequestClose={[Function]}
-  overlayClassName="modal-overlay"
-  parentSelector={[Function]}
-  portalClassName="ReactModalPortal"
-  shouldCloseOnOverlayClick={true}
->
-  <header
-    className="modal-head"
-  >
-    <h2>
-      background_tasks.change_number_of_workers
-    </h2>
-  </header>
-  <form
-    onSubmit={[Function]}
-  >
-    <div
-      className="modal-body"
-    >
-      <Select
-        addLabelText="Add \\"{label}\\"?"
-        arrowRenderer={[Function]}
-        autosize={true}
-        backspaceRemoves={true}
-        backspaceToRemoveMessage="Press backspace to remove {label}"
-        className="input-tiny spacer-top"
-        clearAllText="Clear all"
-        clearRenderer={[Function]}
-        clearValueText="Clear value"
-        clearable={false}
-        deleteRemoves={true}
-        delimiter=","
-        disabled={false}
-        escapeClearsValue={true}
-        filterOptions={[Function]}
-        ignoreAccents={true}
-        ignoreCase={true}
-        inputProps={Object {}}
-        isLoading={false}
-        joinValues={false}
-        labelKey="label"
-        matchPos="any"
-        matchProp="any"
-        menuBuffer={0}
-        menuRenderer={[Function]}
-        multi={false}
-        noResultsText="No results found"
-        onBlurResetsInput={true}
-        onChange={[Function]}
-        onCloseResetsInput={true}
-        optionComponent={[Function]}
-        options={
-          Array [
-            Object {
-              "label": 1,
-              "value": 1,
-            },
-            Object {
-              "label": 2,
-              "value": 2,
-            },
-            Object {
-              "label": 3,
-              "value": 3,
-            },
-            Object {
-              "label": 4,
-              "value": 4,
-            },
-            Object {
-              "label": 5,
-              "value": 5,
-            },
-            Object {
-              "label": 6,
-              "value": 6,
-            },
-            Object {
-              "label": 7,
-              "value": 7,
-            },
-            Object {
-              "label": 8,
-              "value": 8,
-            },
-            Object {
-              "label": 9,
-              "value": 9,
-            },
-            Object {
-              "label": 10,
-              "value": 10,
-            },
-          ]
-        }
-        pageSize={5}
-        placeholder="Select..."
-        required={false}
-        scrollMenuIntoView={true}
-        searchable={false}
-        simpleValue={false}
-        tabSelectsValue={true}
-        value={7}
-        valueComponent={[Function]}
-        valueKey="value"
-      />
-      <div
-        className="big-spacer-top alert alert-success markdown"
-      >
-        background_tasks.change_number_of_workers.hint
-      </div>
-    </div>
-    <footer
-      className="modal-foot"
-    >
-      <div>
-        <button
-          disabled={false}
-          type="submit"
-        >
-          save
-        </button>
-        <button
-          className="button-link"
-          onClick={[Function]}
-          type="reset"
-        >
-          cancel
-        </button>
-      </div>
-    </footer>
-  </form>
-</Modal>
-`;
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/WorkersForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/WorkersForm-test.tsx.snap
new file mode 100644 (file)
index 0000000..8ff615e
--- /dev/null
@@ -0,0 +1,287 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`changes select 1`] = `
+<Modal
+  ariaHideApp={true}
+  bodyOpenClassName="ReactModal__Body--open"
+  className="modal"
+  closeTimeoutMS={0}
+  contentLabel="background_tasks.change_number_of_workers"
+  isOpen={true}
+  onRequestClose={[Function]}
+  overlayClassName="modal-overlay"
+  parentSelector={[Function]}
+  portalClassName="ReactModalPortal"
+  shouldCloseOnOverlayClick={true}
+>
+  <header
+    className="modal-head"
+  >
+    <h2>
+      background_tasks.change_number_of_workers
+    </h2>
+  </header>
+  <form
+    onSubmit={[Function]}
+  >
+    <div
+      className="modal-body"
+    >
+      <Select
+        addLabelText="Add \\"{label}\\"?"
+        arrowRenderer={[Function]}
+        autosize={true}
+        backspaceRemoves={true}
+        backspaceToRemoveMessage="Press backspace to remove {label}"
+        className="input-tiny spacer-top"
+        clearAllText="Clear all"
+        clearRenderer={[Function]}
+        clearValueText="Clear value"
+        clearable={false}
+        deleteRemoves={true}
+        delimiter=","
+        disabled={false}
+        escapeClearsValue={true}
+        filterOptions={[Function]}
+        ignoreAccents={true}
+        ignoreCase={true}
+        inputProps={Object {}}
+        isLoading={false}
+        joinValues={false}
+        labelKey="label"
+        matchPos="any"
+        matchProp="any"
+        menuBuffer={0}
+        menuRenderer={[Function]}
+        multi={false}
+        noResultsText="No results found"
+        onBlurResetsInput={true}
+        onChange={[Function]}
+        onCloseResetsInput={true}
+        optionComponent={[Function]}
+        options={
+          Array [
+            Object {
+              "label": "1",
+              "value": 1,
+            },
+            Object {
+              "label": "2",
+              "value": 2,
+            },
+            Object {
+              "label": "3",
+              "value": 3,
+            },
+            Object {
+              "label": "4",
+              "value": 4,
+            },
+            Object {
+              "label": "5",
+              "value": 5,
+            },
+            Object {
+              "label": "6",
+              "value": 6,
+            },
+            Object {
+              "label": "7",
+              "value": 7,
+            },
+            Object {
+              "label": "8",
+              "value": 8,
+            },
+            Object {
+              "label": "9",
+              "value": 9,
+            },
+            Object {
+              "label": "10",
+              "value": 10,
+            },
+          ]
+        }
+        pageSize={5}
+        placeholder="Select..."
+        required={false}
+        scrollMenuIntoView={true}
+        searchable={false}
+        simpleValue={false}
+        tabSelectsValue={true}
+        value={1}
+        valueComponent={[Function]}
+        valueKey="value"
+      />
+      <div
+        className="big-spacer-top alert alert-success markdown"
+      >
+        background_tasks.change_number_of_workers.hint
+      </div>
+    </div>
+    <footer
+      className="modal-foot"
+    >
+      <div>
+        <button
+          disabled={false}
+          type="submit"
+        >
+          save
+        </button>
+        <button
+          className="button-link"
+          onClick={[Function]}
+          type="reset"
+        >
+          cancel
+        </button>
+      </div>
+    </footer>
+  </form>
+</Modal>
+`;
+
+exports[`changes select 2`] = `
+<Modal
+  ariaHideApp={true}
+  bodyOpenClassName="ReactModal__Body--open"
+  className="modal"
+  closeTimeoutMS={0}
+  contentLabel="background_tasks.change_number_of_workers"
+  isOpen={true}
+  onRequestClose={[Function]}
+  overlayClassName="modal-overlay"
+  parentSelector={[Function]}
+  portalClassName="ReactModalPortal"
+  shouldCloseOnOverlayClick={true}
+>
+  <header
+    className="modal-head"
+  >
+    <h2>
+      background_tasks.change_number_of_workers
+    </h2>
+  </header>
+  <form
+    onSubmit={[Function]}
+  >
+    <div
+      className="modal-body"
+    >
+      <Select
+        addLabelText="Add \\"{label}\\"?"
+        arrowRenderer={[Function]}
+        autosize={true}
+        backspaceRemoves={true}
+        backspaceToRemoveMessage="Press backspace to remove {label}"
+        className="input-tiny spacer-top"
+        clearAllText="Clear all"
+        clearRenderer={[Function]}
+        clearValueText="Clear value"
+        clearable={false}
+        deleteRemoves={true}
+        delimiter=","
+        disabled={false}
+        escapeClearsValue={true}
+        filterOptions={[Function]}
+        ignoreAccents={true}
+        ignoreCase={true}
+        inputProps={Object {}}
+        isLoading={false}
+        joinValues={false}
+        labelKey="label"
+        matchPos="any"
+        matchProp="any"
+        menuBuffer={0}
+        menuRenderer={[Function]}
+        multi={false}
+        noResultsText="No results found"
+        onBlurResetsInput={true}
+        onChange={[Function]}
+        onCloseResetsInput={true}
+        optionComponent={[Function]}
+        options={
+          Array [
+            Object {
+              "label": "1",
+              "value": 1,
+            },
+            Object {
+              "label": "2",
+              "value": 2,
+            },
+            Object {
+              "label": "3",
+              "value": 3,
+            },
+            Object {
+              "label": "4",
+              "value": 4,
+            },
+            Object {
+              "label": "5",
+              "value": 5,
+            },
+            Object {
+              "label": "6",
+              "value": 6,
+            },
+            Object {
+              "label": "7",
+              "value": 7,
+            },
+            Object {
+              "label": "8",
+              "value": 8,
+            },
+            Object {
+              "label": "9",
+              "value": 9,
+            },
+            Object {
+              "label": "10",
+              "value": 10,
+            },
+          ]
+        }
+        pageSize={5}
+        placeholder="Select..."
+        required={false}
+        scrollMenuIntoView={true}
+        searchable={false}
+        simpleValue={false}
+        tabSelectsValue={true}
+        value={7}
+        valueComponent={[Function]}
+        valueKey="value"
+      />
+      <div
+        className="big-spacer-top alert alert-success markdown"
+      >
+        background_tasks.change_number_of_workers.hint
+      </div>
+    </div>
+    <footer
+      className="modal-foot"
+    >
+      <div>
+        <button
+          disabled={false}
+          type="submit"
+        >
+          save
+        </button>
+        <button
+          className="button-link"
+          onClick={[Function]}
+          type="reset"
+        >
+          cancel
+        </button>
+      </div>
+    </footer>
+  </form>
+</Modal>
+`;
index 25c7072b53021118d3d356f7ad480f4cf244a0ac..04dd6fb7d937dd48e1957dea10aebf1d85159f19 100644 (file)
 .bubble-popup-bottom-right {
   .bubble-popup-bottom;
   margin-left: 0;
-  margin-right: -@popupArrowSize;
+  margin-right: -16px;
 
   .bubble-popup-arrow {
     left: auto;
     right: 15px;
-    border-right-width: 0;
-    border-left-color: barBorderColor;
   }
 }
 
index 0e3f376dfb671ee1048054b7d85d4d6415ef201d..ecd566e2aa73a3027b37313f8826813991d3dfd5 100644 (file)
@@ -76,6 +76,7 @@ issues=Issues
 inheritance=Inheritance
 key=Key
 language=Language
+learn_more=Learn More
 library=Library
 line_number=Line Number
 links=Links
@@ -2211,7 +2212,8 @@ background_tasks.number_of_workers=Number of Workers:
 background_tasks.number_of_workers.warning=Configuring additional workers without first vertically scaling your server could have negative performance impacts.
 background_tasks.change_number_of_workers=Edit CE Workers
 background_tasks.change_number_of_workers.hint=If your queue backs up behind the analysis reports from large projects, increasing the number of Compute Engine workers will allow you to take full advantage of having configured increased Compute Engine memory on a multi-core server (vertical scaling).
-background_tasks.add_more_with_governance=Add more with Governance
+background_tasks.add_more_workers=Speed up your analysis by adding more Workers
+background_tasks.add_more_workers.text=Increase the number of Compute Engine Workers with the Governance product. Available in our commercial editions.
 background_tasks.search_by_task_or_component=Search by Task or Component
 background_tasks.failing_count=Count of projects where processing of most recent analysis report failed
 
@@ -2580,7 +2582,6 @@ branches.set_leak_period=Set Leak Period
 branches.last_analysis_date=Last Analysis Date
 branches.no_support.header=Get the most out of SonarQube with branches analysis
 branches.no_support.header.text=Analyze each branch of your project separately with our Developer Pack.
-branches.learn_more=Learn More
 branches.buy_developer_pack=Buy Developer Pack