From 36969ad381fb8ff7f93daf9c40d801b69e3a1bac Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Wed, 21 Feb 2018 14:24:05 +0100 Subject: [PATCH] rewrite remaining backbone modals in react --- .../qa/util/pageobjects/ProjectKeyPage.java | 54 +++++---- .../sonar-web/src/main/js/api/projectLinks.ts | 29 ++--- server/sonar-web/src/main/js/app/types.ts | 7 ++ .../src/main/js/app/utils/exposeLibraries.ts | 5 +- .../main/js/apps/overview/meta/MetaLink.tsx | 2 +- .../main/js/apps/overview/meta/MetaLinks.tsx | 4 +- .../src/main/js/apps/project-admin/key/Key.js | 61 +++++----- .../js/apps/project-admin/key/UpdateForm.js | 87 -------------- .../js/apps/project-admin/key/UpdateForm.tsx | 85 ++++++++++++++ .../project-admin/key/UpdateKeyConfirm.tsx | 65 ++++++++++ .../apps/project-admin/key/UpdateKeyForm.js | 89 -------------- .../apps/project-admin/key/UpdateKeyForm.tsx | 75 ++++++++++++ .../key/views/UpdateKeyConfirmation.hbs | 30 ----- .../key/views/UpdateKeyConfirmation.js | 41 ------- .../project-admin/links/CreationModal.tsx | 111 ++++++++++++++++++ .../js/apps/project-admin/links/Header.js | 51 -------- .../js/apps/project-admin/links/Header.tsx | 73 ++++++++++++ .../links/{LinkRow.js => LinkRow.tsx} | 59 ++++++---- .../main/js/apps/project-admin/links/Links.js | 20 +--- .../main/js/apps/project-admin/links/Table.js | 8 +- .../links/views/CreationModal.js | 42 ------- .../links/views/CreationModalTemplate.hbs | 22 ---- .../links/views/DeletionModal.js | 46 -------- .../links/views/DeletionModalTemplate.hbs | 13 -- .../js/apps/project-admin/store/actions.js | 35 ++++-- .../apps/quality-gates/components/Projects.js | 65 +++++----- .../quality-gates/views/gate-projects-view.js | 73 ------------ .../main/js/components/RestartModal/index.js | 47 -------- .../RestartModal/templates/restarting.hbs | 14 --- .../RestartModal/templates/template.hbs | 15 --- .../main/js/components/common/modal-form.js | 99 ---------------- .../src/main/js/components/common/modals.js | 92 --------------- .../common/selectable-collection-view.js | 82 ------------- .../js/components/controls/ConfirmButton.tsx | 19 ++- .../project/ProjectKeyUpdatePageTest.java | 2 +- .../tests/project/ProjectLinksTest.java | 10 +- 36 files changed, 616 insertions(+), 1016 deletions(-) delete mode 100644 server/sonar-web/src/main/js/apps/project-admin/key/UpdateForm.js create mode 100644 server/sonar-web/src/main/js/apps/project-admin/key/UpdateForm.tsx create mode 100644 server/sonar-web/src/main/js/apps/project-admin/key/UpdateKeyConfirm.tsx delete mode 100644 server/sonar-web/src/main/js/apps/project-admin/key/UpdateKeyForm.js create mode 100644 server/sonar-web/src/main/js/apps/project-admin/key/UpdateKeyForm.tsx delete mode 100644 server/sonar-web/src/main/js/apps/project-admin/key/views/UpdateKeyConfirmation.hbs delete mode 100644 server/sonar-web/src/main/js/apps/project-admin/key/views/UpdateKeyConfirmation.js create mode 100644 server/sonar-web/src/main/js/apps/project-admin/links/CreationModal.tsx delete mode 100644 server/sonar-web/src/main/js/apps/project-admin/links/Header.js create mode 100644 server/sonar-web/src/main/js/apps/project-admin/links/Header.tsx rename server/sonar-web/src/main/js/apps/project-admin/links/{LinkRow.js => LinkRow.tsx} (67%) delete mode 100644 server/sonar-web/src/main/js/apps/project-admin/links/views/CreationModal.js delete mode 100644 server/sonar-web/src/main/js/apps/project-admin/links/views/CreationModalTemplate.hbs delete mode 100644 server/sonar-web/src/main/js/apps/project-admin/links/views/DeletionModal.js delete mode 100644 server/sonar-web/src/main/js/apps/project-admin/links/views/DeletionModalTemplate.hbs delete mode 100644 server/sonar-web/src/main/js/apps/quality-gates/views/gate-projects-view.js delete mode 100644 server/sonar-web/src/main/js/components/RestartModal/index.js delete mode 100644 server/sonar-web/src/main/js/components/RestartModal/templates/restarting.hbs delete mode 100644 server/sonar-web/src/main/js/components/RestartModal/templates/template.hbs delete mode 100644 server/sonar-web/src/main/js/components/common/modal-form.js delete mode 100644 server/sonar-web/src/main/js/components/common/modals.js delete mode 100644 server/sonar-web/src/main/js/components/common/selectable-collection-view.js diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectKeyPage.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectKeyPage.java index 2c3fcf48893..e8b5492b3e7 100644 --- a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectKeyPage.java +++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectKeyPage.java @@ -20,82 +20,86 @@ package org.sonarqube.qa.util.pageobjects; import com.codeborne.selenide.Condition; -import com.codeborne.selenide.Selenide; import com.codeborne.selenide.SelenideElement; +import static com.codeborne.selenide.Condition.visible; +import static com.codeborne.selenide.Selenide.$; + public class ProjectKeyPage { public ProjectKeyPage() { - Selenide.$("#project-key").should(Condition.exist); + $("#project-key").should(Condition.exist); } public ProjectKeyPage assertSimpleUpdate() { - Selenide.$("#update-key-new-key").shouldBe(Condition.visible); - Selenide.$("#update-key-submit").shouldBe(Condition.visible); + $("#update-key-new-key").shouldBe(visible); + $("#update-key-submit").shouldBe(visible); return this; } public ProjectKeyPage trySimpleUpdate(String newKey) { - Selenide.$("#update-key-new-key").val(newKey); - Selenide.$("#update-key-submit").click(); - Selenide.$("#update-key-confirm").click(); + $("#update-key-new-key").val(newKey); + $("#update-key-submit").click(); + $(".modal").shouldBe(visible); + $(".modal button[type=\"submit\"]").click(); return this; } public ProjectKeyPage openFineGrainedUpdate() { - Selenide.$("#update-key-tab-fine").click(); - Selenide.$("#project-key-fine-grained-update").shouldBe(Condition.visible); + $("#update-key-tab-fine").click(); + $("#project-key-fine-grained-update").shouldBe(visible); return this; } public ProjectKeyPage tryFineGrainedUpdate(String key, String newKey) { - SelenideElement form = Selenide.$(".js-fine-grained-update[data-key=\"" + key + "\"]"); - form.shouldBe(Condition.visible); + SelenideElement form = $(".js-fine-grained-update[data-key=\"" + key + "\"]"); + form.shouldBe(visible); form.$("input").val(newKey); form.$("button").click(); - Selenide.$("#update-key-confirm").click(); + $(".modal").shouldBe(visible); + $(".modal button[type=\"submit\"]").click(); return this; } public ProjectKeyPage assertBulkChange() { - Selenide.$("#bulk-update-replace").shouldBe(Condition.visible); - Selenide.$("#bulk-update-by").shouldBe(Condition.visible); - Selenide.$("#bulk-update-see-results").shouldBe(Condition.visible); + $("#bulk-update-replace").shouldBe(visible); + $("#bulk-update-by").shouldBe(visible); + $("#bulk-update-see-results").shouldBe(visible); return this; } public ProjectKeyPage simulateBulkChange(String replace, String by) { - Selenide.$("#bulk-update-replace").val(replace); - Selenide.$("#bulk-update-by").val(by); - Selenide.$("#bulk-update-see-results").click(); + $("#bulk-update-replace").val(replace); + $("#bulk-update-by").val(by); + $("#bulk-update-see-results").click(); - Selenide.$("#bulk-update-simulation").shouldBe(Condition.visible); + $("#bulk-update-simulation").shouldBe(visible); return this; } public ProjectKeyPage assertBulkChangeSimulationResult(String oldKey, String newKey) { - SelenideElement row = Selenide.$("#bulk-update-results").$("[data-key=\"" + oldKey + "\"]"); + SelenideElement row = $("#bulk-update-results").$("[data-key=\"" + oldKey + "\"]"); row.$(".js-old-key").should(Condition.text(oldKey)); row.$(".js-new-key").should(Condition.text(newKey)); return this; } public ProjectKeyPage assertDuplicated(String oldKey) { - SelenideElement row = Selenide.$("#bulk-update-results").$("[data-key=\"" + oldKey + "\"]"); - row.$(".js-new-key").$(".badge-danger").shouldBe(Condition.visible); + SelenideElement row = $("#bulk-update-results").$("[data-key=\"" + oldKey + "\"]"); + row.$(".js-new-key").$(".badge-danger").shouldBe(visible); return this; } public ProjectKeyPage confirmBulkUpdate() { - Selenide.$("#bulk-update-confirm").click(); + $("#bulk-update-confirm").click(); return this; } public ProjectKeyPage assertSuccessfulBulkUpdate() { - Selenide.$(".process-spinner") - .shouldBe(Condition.visible) + $(".process-spinner") + .shouldBe(visible) .shouldHave(Condition.text("The key has successfully been updated for all required resources")); return this; } diff --git a/server/sonar-web/src/main/js/api/projectLinks.ts b/server/sonar-web/src/main/js/api/projectLinks.ts index e91b200c4fe..22c5fba17fd 100644 --- a/server/sonar-web/src/main/js/api/projectLinks.ts +++ b/server/sonar-web/src/main/js/api/projectLinks.ts @@ -17,30 +17,21 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { getJSON, post, postJSON } from '../helpers/request'; +import { ProjectLink } from '../app/types'; import throwGlobalError from '../app/utils/throwGlobalError'; - -export interface ProjectLink { - id: string; - name: string; - type: string; - url: string; -} +import { getJSON, post, postJSON } from '../helpers/request'; export function getProjectLinks(projectKey: string): Promise { - const url = '/api/project_links/search'; - const data = { projectKey }; - return getJSON(url, data).then(r => r.links, throwGlobalError); + return getJSON('/api/project_links/search', { projectKey }).then(r => r.links, throwGlobalError); } -export function deleteLink(linkId: string): Promise { - const url = '/api/project_links/delete'; - const data = { id: linkId }; - return post(url, data); +export function deleteLink(linkId: string) { + return post('/api/project_links/delete', { id: linkId }).catch(throwGlobalError); } -export function createLink(projectKey: string, name: string, url: string): Promise { - const apiURL = '/api/project_links/create'; - const data = { projectKey, name, url }; - return postJSON(apiURL, data).then(r => r.link); +export function createLink(projectKey: string, name: string, url: string): Promise { + return postJSON('/api/project_links/create', { projectKey, name, url }).then( + r => r.link, + throwGlobalError + ); } diff --git a/server/sonar-web/src/main/js/app/types.ts b/server/sonar-web/src/main/js/app/types.ts index dddeec8424e..13b8bac9f66 100644 --- a/server/sonar-web/src/main/js/app/types.ts +++ b/server/sonar-web/src/main/js/app/types.ts @@ -245,6 +245,13 @@ export interface PermissionTemplate { }>; } +export interface ProjectLink { + id: string; + name: string; + type: string; + url: string; +} + export interface Rule { isTemplate?: boolean; key: string; diff --git a/server/sonar-web/src/main/js/app/utils/exposeLibraries.ts b/server/sonar-web/src/main/js/app/utils/exposeLibraries.ts index b19336572b0..4e853881db2 100644 --- a/server/sonar-web/src/main/js/app/utils/exposeLibraries.ts +++ b/server/sonar-web/src/main/js/app/utils/exposeLibraries.ts @@ -33,7 +33,6 @@ import Modal from '../../components/controls/Modal'; import SearchBox from '../../components/controls/SearchBox'; import Select from '../../components/controls/Select'; import Tooltip from '../../components/controls/Tooltip'; -import ModalForm from '../../components/common/modal-form'; import SelectList from '../../components/SelectList'; import CoverageRating from '../../components/ui/CoverageRating'; import DuplicationsRating from '../../components/ui/DuplicationsRating'; @@ -63,9 +62,7 @@ const exposeLibraries = () => { Tooltip, Select, SelectList, - SearchBox, - // deprecated, used in Governance - ModalForm_deprecated: ModalForm + SearchBox }; }; diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaLink.tsx b/server/sonar-web/src/main/js/apps/overview/meta/MetaLink.tsx index e4c9a697009..d909bc0faff 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/MetaLink.tsx +++ b/server/sonar-web/src/main/js/apps/overview/meta/MetaLink.tsx @@ -19,8 +19,8 @@ */ import * as React from 'react'; import { isProvided, getLinkName } from '../../project-admin/links/utils'; +import { ProjectLink } from '../../../app/types'; import BugTrackerIcon from '../../../components/ui/BugTrackerIcon'; -import { ProjectLink } from '../../../api/projectLinks'; interface Props { link: ProjectLink; diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaLinks.tsx b/server/sonar-web/src/main/js/apps/overview/meta/MetaLinks.tsx index ed778b4cef7..d9db8d5b150 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/MetaLinks.tsx +++ b/server/sonar-web/src/main/js/apps/overview/meta/MetaLinks.tsx @@ -19,9 +19,9 @@ */ import * as React from 'react'; import MetaLink from './MetaLink'; -import { getProjectLinks, ProjectLink } from '../../../api/projectLinks'; +import { getProjectLinks } from '../../../api/projectLinks'; import { orderLinks } from '../../project-admin/links/utils'; -import { LightComponent } from '../../../app/types'; +import { LightComponent, ProjectLink } from '../../../app/types'; import { translate } from '../../../helpers/l10n'; interface Props { diff --git a/server/sonar-web/src/main/js/apps/project-admin/key/Key.js b/server/sonar-web/src/main/js/apps/project-admin/key/Key.js index 52faac1bdc0..d1688ed106e 100644 --- a/server/sonar-web/src/main/js/apps/project-admin/key/Key.js +++ b/server/sonar-web/src/main/js/apps/project-admin/key/Key.js @@ -26,14 +26,13 @@ import UpdateForm from './UpdateForm'; import BulkUpdate from './BulkUpdate'; import FineGrainedUpdate from './FineGrainedUpdate'; import { reloadUpdateKeyPage } from './utils'; -import { fetchProjectModules, changeKey } from '../store/actions'; +import { changeKey, fetchProjectModules } from '../store/actions'; import { translate } from '../../../helpers/l10n'; import { addGlobalErrorMessage, - closeAllGlobalMessages, - addGlobalSuccessMessage + addGlobalSuccessMessage, + closeAllGlobalMessages } from '../../../store/globalMessages/duck'; -import { parseError } from '../../../helpers/request'; import RecentHistory from '../../../app/components/RecentHistory'; import { getProjectAdminProjectModules } from '../../../store/rootReducer'; @@ -55,29 +54,25 @@ class Key extends React.PureComponent { this.props.fetchProjectModules(this.props.component.key); } - handleChangeKey(key, newKey) { - return this.props - .changeKey(key, newKey) - .then(() => { - if (key === this.props.component.key) { - this.props.addGlobalSuccessMessage(translate('update_key.key_updated.reload')); - RecentHistory.remove(key); - reloadUpdateKeyPage(newKey); - } else { - this.props.addGlobalSuccessMessage(translate('update_key.key_updated')); - } - }) - .catch(e => { - parseError(e).then(this.props.addGlobalErrorMessage); - }); - } + handleChangeKey = (key, newKey) => { + return this.props.changeKey(key, newKey).then(() => { + if (key === this.props.component.key) { + this.props.addGlobalSuccessMessage(translate('update_key.key_updated.reload')); + RecentHistory.remove(key); + reloadUpdateKeyPage(newKey); + } else { + this.props.addGlobalSuccessMessage(translate('update_key.key_updated')); + } + }); + }; - handleChangeTab(tab, e) { - e.preventDefault(); - e.target.blur(); + handleChangeTab = event => { + event.preventDefault(); + event.currentTarget.blur(); + const { tab } = event.currentTarget.dataset; this.setState({ tab }); this.props.closeAllGlobalMessages(); - } + }; render() { const { component, modules } = this.props; @@ -88,7 +83,7 @@ class Key extends React.PureComponent { const { tab } = this.state; return ( -
+
@@ -96,7 +91,7 @@ class Key extends React.PureComponent { {noModules && (
- +
)} @@ -106,19 +101,21 @@ class Key extends React.PureComponent {
diff --git a/server/sonar-web/src/main/js/apps/project-admin/key/UpdateForm.js b/server/sonar-web/src/main/js/apps/project-admin/key/UpdateForm.js deleted file mode 100644 index 54564f82c27..00000000000 --- a/server/sonar-web/src/main/js/apps/project-admin/key/UpdateForm.js +++ /dev/null @@ -1,87 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import React from 'react'; -import PropTypes from 'prop-types'; -import UpdateKeyConfirmation from './views/UpdateKeyConfirmation'; -import { translate } from '../../../helpers/l10n'; - -export default class UpdateForm extends React.PureComponent { - static propTypes = { - component: PropTypes.object.isRequired, - onKeyChange: PropTypes.func.isRequired - }; - - state = { newKey: null }; - - handleSubmit(e) { - e.preventDefault(); - - const newKey = this.refs.newKey.value; - - new UpdateKeyConfirmation({ - newKey, - component: this.props.component, - onChange: this.props.onKeyChange - }).render(); - } - - handleChange(e) { - const newKey = e.target.value; - this.setState({ newKey }); - } - - handleReset(e) { - e.preventDefault(); - this.setState({ newKey: null }); - } - - render() { - const value = this.state.newKey != null ? this.state.newKey : this.props.component.key; - - const hasChanged = value !== this.props.component.key; - - return ( -
- - -
- {' '} - -
-
- ); - } -} diff --git a/server/sonar-web/src/main/js/apps/project-admin/key/UpdateForm.tsx b/server/sonar-web/src/main/js/apps/project-admin/key/UpdateForm.tsx new file mode 100644 index 00000000000..99dc58c1208 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/project-admin/key/UpdateForm.tsx @@ -0,0 +1,85 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import UpdateKeyConfirm from './UpdateKeyConfirm'; +import { Button, SubmitButton } from '../../../components/ui/buttons'; +import { translate } from '../../../helpers/l10n'; + +interface Props { + component: { key: string; name: string }; + onKeyChange: (oldKey: string, newKey: string) => Promise; +} + +interface State { + newKey?: string; +} + +export default class UpdateForm extends React.PureComponent { + state: State = {}; + + handleChange = (event: React.ChangeEvent) => { + const newKey = event.currentTarget.value; + this.setState({ newKey }); + }; + + handleReset = () => { + this.setState({ newKey: undefined }); + }; + + render() { + const { component } = this.props; + const { newKey } = this.state; + const value = newKey != null ? newKey : component.key; + const hasChanged = value !== component.key; + + return ( + + {({ onFormSubmit }) => ( +
+ + +
+ + {translate('update_verb')} + + + +
+
+ )} +
+ ); + } +} diff --git a/server/sonar-web/src/main/js/apps/project-admin/key/UpdateKeyConfirm.tsx b/server/sonar-web/src/main/js/apps/project-admin/key/UpdateKeyConfirm.tsx new file mode 100644 index 00000000000..6bb2bcc7990 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/project-admin/key/UpdateKeyConfirm.tsx @@ -0,0 +1,65 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import ConfirmButton, { ChildrenProps } from '../../../components/controls/ConfirmButton'; +import { translate, translateWithParameters } from '../../../helpers/l10n'; + +interface Props { + children: (props: ChildrenProps) => React.ReactNode; + component: { key: string; name: string }; + newKey: string | undefined; + onConfirm: (oldKey: string, newKey: string) => Promise; +} + +export default class UpdateKeyConfirm extends React.PureComponent { + handleConfirm = () => { + return this.props.newKey + ? this.props.onConfirm(this.props.component.key, this.props.newKey) + : Promise.reject(undefined); + }; + + render() { + const { children, component, newKey } = this.props; + + return ( + + {translateWithParameters('update_key.are_you_sure_to_change_key', component.name)} +
+ {translate('update_key.old_key')} + {': '} + {component.key} +
+
+ {translate('update_key.new_key')} + {': '} + {newKey} +
+ + } + modalHeader={translate('update_key.page')} + onConfirm={this.handleConfirm}> + {children} +
+ ); + } +} diff --git a/server/sonar-web/src/main/js/apps/project-admin/key/UpdateKeyForm.js b/server/sonar-web/src/main/js/apps/project-admin/key/UpdateKeyForm.js deleted file mode 100644 index 4f4cf8ec976..00000000000 --- a/server/sonar-web/src/main/js/apps/project-admin/key/UpdateKeyForm.js +++ /dev/null @@ -1,89 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import React from 'react'; -import PropTypes from 'prop-types'; -import UpdateKeyConfirmation from './views/UpdateKeyConfirmation'; -import { translate } from '../../../helpers/l10n'; - -export default class UpdateKeyForm extends React.PureComponent { - static propTypes = { - component: PropTypes.object.isRequired - }; - - state = {}; - - componentWillMount() { - this.handleInputChange = this.handleInputChange.bind(this); - this.handleUpdateClick = this.handleUpdateClick.bind(this); - this.handleResetClick = this.handleResetClick.bind(this); - } - - handleInputChange(e) { - const key = e.target.value; - this.setState({ key }); - } - - handleUpdateClick(e) { - e.preventDefault(); - e.target.blur(); - - const newKey = this.refs.newKey.value; - - new UpdateKeyConfirmation({ - newKey, - component: this.props.component, - onChange: this.props.onKeyChange - }).render(); - } - - handleResetClick(e) { - e.preventDefault(); - e.target.blur(); - this.setState({ key: null }); - } - - render() { - const { component } = this.props; - - const value = this.state.key != null ? this.state.key : component.key; - - const hasChanged = this.state.key != null && this.state.key !== component.key; - - return ( -
- - - - - -
- ); - } -} diff --git a/server/sonar-web/src/main/js/apps/project-admin/key/UpdateKeyForm.tsx b/server/sonar-web/src/main/js/apps/project-admin/key/UpdateKeyForm.tsx new file mode 100644 index 00000000000..48345dfaaff --- /dev/null +++ b/server/sonar-web/src/main/js/apps/project-admin/key/UpdateKeyForm.tsx @@ -0,0 +1,75 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import UpdateKeyConfirm from './UpdateKeyConfirm'; +import { Button } from '../../../components/ui/buttons'; +import { translate } from '../../../helpers/l10n'; + +interface Props { + component: { key: string; name: string }; + onKeyChange: (oldKey: string, newKey: string) => Promise; +} + +interface State { + newKey?: string; +} + +export default class UpdateKeyForm extends React.PureComponent { + state: State = {}; + + handleInputChange = (event: React.ChangeEvent) => { + const newKey = event.currentTarget.value; + this.setState({ newKey }); + }; + + handleResetClick = () => { + this.setState({ newKey: undefined }); + }; + + render() { + const { component } = this.props; + const { newKey } = this.state; + const value = newKey !== undefined ? newKey : component.key; + const hasChanged = newKey !== undefined && newKey !== component.key; + + return ( +
+ + + + {({ onClick }) => ( + + )} + + + +
+ ); + } +} diff --git a/server/sonar-web/src/main/js/apps/project-admin/key/views/UpdateKeyConfirmation.hbs b/server/sonar-web/src/main/js/apps/project-admin/key/views/UpdateKeyConfirmation.hbs deleted file mode 100644 index bec83b7693e..00000000000 --- a/server/sonar-web/src/main/js/apps/project-admin/key/views/UpdateKeyConfirmation.hbs +++ /dev/null @@ -1,30 +0,0 @@ -
- - - -
diff --git a/server/sonar-web/src/main/js/apps/project-admin/key/views/UpdateKeyConfirmation.js b/server/sonar-web/src/main/js/apps/project-admin/key/views/UpdateKeyConfirmation.js deleted file mode 100644 index afdc1ccf3d7..00000000000 --- a/server/sonar-web/src/main/js/apps/project-admin/key/views/UpdateKeyConfirmation.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import Template from './UpdateKeyConfirmation.hbs'; -import ModalForm from '../../../../components/common/modal-form'; - -export default ModalForm.extend({ - template: Template, - - onFormSubmit() { - ModalForm.prototype.onFormSubmit.apply(this, arguments); - this.disableForm(); - this.showSpinner(); - - this.options.onChange(this.options.component.key, this.options.newKey); - this.destroy(); - }, - - serializeData() { - return { - component: this.options.component, - newKey: this.options.newKey - }; - } -}); diff --git a/server/sonar-web/src/main/js/apps/project-admin/links/CreationModal.tsx b/server/sonar-web/src/main/js/apps/project-admin/links/CreationModal.tsx new file mode 100644 index 00000000000..c87ae12e438 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/project-admin/links/CreationModal.tsx @@ -0,0 +1,111 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import DeferredSpinner from '../../../components/common/DeferredSpinner'; +import SimpleModal from '../../../components/controls/SimpleModal'; +import { SubmitButton, ResetButtonLink } from '../../../components/ui/buttons'; +import { translate } from '../../../helpers/l10n'; + +interface Props { + onClose: () => void; + onSubmit: (name: string, url: string) => Promise; +} + +interface State { + name: string; + url: string; +} + +export default class CreationModal extends React.PureComponent { + state: State = { name: '', url: '' }; + + handleSubmit = () => { + return this.props.onSubmit(this.state.name, this.state.url).then(this.props.onClose); + }; + + handleNameChange = (event: React.ChangeEvent) => { + this.setState({ name: event.currentTarget.value }); + }; + + handleUrlChange = (event: React.ChangeEvent) => { + this.setState({ url: event.currentTarget.value }); + }; + + render() { + const header = translate('project_links.create_new_project_link'); + + return ( + + {({ onCloseClick, onFormSubmit, submitting }) => ( +
+
+

{header}

+
+ +
+
+ + +
+ +
+ + +
+
+ +
+ + + {translate('create')} + + + {translate('cancel')} + +
+
+ )} +
+ ); + } +} diff --git a/server/sonar-web/src/main/js/apps/project-admin/links/Header.js b/server/sonar-web/src/main/js/apps/project-admin/links/Header.js deleted file mode 100644 index 4ed55a4bb2d..00000000000 --- a/server/sonar-web/src/main/js/apps/project-admin/links/Header.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import React from 'react'; -import PropTypes from 'prop-types'; -import CreationModal from './views/CreationModal'; -import { translate } from '../../../helpers/l10n'; - -export default class Header extends React.PureComponent { - static propTypes = { - onCreate: PropTypes.func.isRequired - }; - - handleCreateClick(e) { - e.preventDefault(); - e.target.blur(); - new CreationModal({ - onCreate: this.props.onCreate - }).render(); - } - - render() { - return ( -
-

{translate('project_links.page')}

-
- -
-
{translate('project_links.page.description')}
-
- ); - } -} diff --git a/server/sonar-web/src/main/js/apps/project-admin/links/Header.tsx b/server/sonar-web/src/main/js/apps/project-admin/links/Header.tsx new file mode 100644 index 00000000000..b8e07c56fbb --- /dev/null +++ b/server/sonar-web/src/main/js/apps/project-admin/links/Header.tsx @@ -0,0 +1,73 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import * as React from 'react'; +import CreationModal from './CreationModal'; +import { Button } from '../../../components/ui/buttons'; +import { translate } from '../../../helpers/l10n'; + +interface Props { + onCreate: (name: string, url: string) => Promise; +} + +interface State { + creationModal: boolean; +} + +export default class Header extends React.PureComponent { + mounted = false; + state: State = { creationModal: false }; + + componentDidMount() { + this.mounted = true; + } + + componentWillUnmount() { + this.mounted = false; + } + + handleCreateClick = () => { + this.setState({ creationModal: true }); + }; + + handleCreationModalClose = () => { + if (this.mounted) { + this.setState({ creationModal: false }); + } + }; + + render() { + return ( + <> +
+

{translate('project_links.page')}

+
+ +
+
{translate('project_links.page.description')}
+
+ {this.state.creationModal && ( + + )} + + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/project-admin/links/LinkRow.js b/server/sonar-web/src/main/js/apps/project-admin/links/LinkRow.tsx similarity index 67% rename from server/sonar-web/src/main/js/apps/project-admin/links/LinkRow.js rename to server/sonar-web/src/main/js/apps/project-admin/links/LinkRow.tsx index 3124fb9c8af..74ea4df05ee 100644 --- a/server/sonar-web/src/main/js/apps/project-admin/links/LinkRow.js +++ b/server/sonar-web/src/main/js/apps/project-admin/links/LinkRow.tsx @@ -17,25 +17,21 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import React from 'react'; -import PropTypes from 'prop-types'; +import * as React from 'react'; import { isProvided, getLinkName } from './utils'; -import { translate } from '../../../helpers/l10n'; +import { ProjectLink } from '../../../app/types'; +import ConfirmButton from '../../../components/controls/ConfirmButton'; import BugTrackerIcon from '../../../components/ui/BugTrackerIcon'; +import { Button } from '../../../components/ui/buttons'; +import { translate, translateWithParameters } from '../../../helpers/l10n'; -export default class LinkRow extends React.PureComponent { - static propTypes = { - link: PropTypes.object.isRequired, - onDelete: PropTypes.func.isRequired - }; - - handleDeleteClick(e) { - e.preventDefault(); - e.target.blur(); - this.props.onDelete(); - } +interface Props { + link: ProjectLink; + onDelete: (linkId: string) => Promise; +} - renderIcon(iconClassName) { +export default class LinkRow extends React.PureComponent { + renderIcon = (iconClassName: string) => { if (iconClassName === 'icon-issue') { return (
@@ -49,9 +45,9 @@ export default class LinkRow extends React.PureComponent {
); - } + }; - renderNameForProvided(link) { + renderNameForProvided = (link: ProjectLink) => { return (
{this.renderIcon(`icon-${link.type}`)} @@ -65,9 +61,9 @@ export default class LinkRow extends React.PureComponent {
); - } + }; - renderName(link) { + renderName = (link: ProjectLink) => { if (isProvided(link)) { return this.renderNameForProvided(link); } @@ -80,19 +76,32 @@ export default class LinkRow extends React.PureComponent { ); - } + }; - renderDeleteButton(link) { + renderDeleteButton = (link: ProjectLink) => { if (isProvided(link)) { return null; } return ( - + + {({ onClick }) => ( + + )} + ); - } + }; render() { const { link } = this.props; diff --git a/server/sonar-web/src/main/js/apps/project-admin/links/Links.js b/server/sonar-web/src/main/js/apps/project-admin/links/Links.js index e8a5318ba99..fa77e03c6b8 100644 --- a/server/sonar-web/src/main/js/apps/project-admin/links/Links.js +++ b/server/sonar-web/src/main/js/apps/project-admin/links/Links.js @@ -23,7 +23,6 @@ import Helmet from 'react-helmet'; import { connect } from 'react-redux'; import Header from './Header'; import Table from './Table'; -import DeletionModal from './views/DeletionModal'; import { fetchProjectLinks, deleteProjectLink, createProjectLink } from '../store/actions'; import { getProjectAdminProjectLinks } from '../../../store/rootReducer'; import { translate } from '../../../helpers/l10n'; @@ -34,26 +33,17 @@ class Links extends React.PureComponent { links: PropTypes.array }; - componentWillMount() { - this.handleCreateLink = this.handleCreateLink.bind(this); - this.handleDeleteLink = this.handleDeleteLink.bind(this); - } - componentDidMount() { this.props.fetchProjectLinks(this.props.component.key); } - handleCreateLink(name, url) { + handleCreateLink = (name, url) => { return this.props.createProjectLink(this.props.component.key, name, url); - } + }; - handleDeleteLink(link) { - new DeletionModal({ link }) - .on('done', () => { - this.props.deleteProjectLink(this.props.component.key, link.id); - }) - .render(); - } + handleDeleteLink = linkId => { + return this.props.deleteProjectLink(this.props.component.key, linkId); + }; render() { return ( diff --git a/server/sonar-web/src/main/js/apps/project-admin/links/Table.js b/server/sonar-web/src/main/js/apps/project-admin/links/Table.js index ad5ea09f9ba..8c8c80788fb 100644 --- a/server/sonar-web/src/main/js/apps/project-admin/links/Table.js +++ b/server/sonar-web/src/main/js/apps/project-admin/links/Table.js @@ -29,10 +29,6 @@ export default class Table extends React.PureComponent { onDelete: PropTypes.func.isRequired }; - handleDeleteLink(link) { - this.props.onDelete(link); - } - renderHeader() { // keep empty cell for actions return ( @@ -50,12 +46,12 @@ export default class Table extends React.PureComponent { const orderedLinks = orderLinks(this.props.links); const linkRows = orderedLinks.map(link => ( - + )); return (
- + {this.renderHeader()} {linkRows} diff --git a/server/sonar-web/src/main/js/apps/project-admin/links/views/CreationModal.js b/server/sonar-web/src/main/js/apps/project-admin/links/views/CreationModal.js deleted file mode 100644 index d00cb68e3be..00000000000 --- a/server/sonar-web/src/main/js/apps/project-admin/links/views/CreationModal.js +++ /dev/null @@ -1,42 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import Template from './CreationModalTemplate.hbs'; -import ModalForm from '../../../../components/common/modal-form'; -import { parseError } from '../../../../helpers/request'; - -export default ModalForm.extend({ - template: Template, - - onFormSubmit() { - ModalForm.prototype.onFormSubmit.apply(this, arguments); - this.disableForm(); - - const name = this.$('#create-link-name').val(); - const url = this.$('#create-link-url').val(); - - this.options - .onCreate(name, url) - .then(() => this.destroy()) - .catch(e => { - parseError(e).then(msg => this.showSingleError(msg)); - this.enableForm(); - }); - } -}); diff --git a/server/sonar-web/src/main/js/apps/project-admin/links/views/CreationModalTemplate.hbs b/server/sonar-web/src/main/js/apps/project-admin/links/views/CreationModalTemplate.hbs deleted file mode 100644 index 6d7f25084b5..00000000000 --- a/server/sonar-web/src/main/js/apps/project-admin/links/views/CreationModalTemplate.hbs +++ /dev/null @@ -1,22 +0,0 @@ -
- - - -
diff --git a/server/sonar-web/src/main/js/apps/project-admin/links/views/DeletionModal.js b/server/sonar-web/src/main/js/apps/project-admin/links/views/DeletionModal.js deleted file mode 100644 index 7b12ab08d84..00000000000 --- a/server/sonar-web/src/main/js/apps/project-admin/links/views/DeletionModal.js +++ /dev/null @@ -1,46 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import Template from './DeletionModalTemplate.hbs'; -import ModalForm from '../../../../components/common/modal-form'; -import { deleteLink } from '../../../../api/projectLinks'; -import { parseError } from '../../../../helpers/request'; - -export default ModalForm.extend({ - template: Template, - - onFormSubmit() { - ModalForm.prototype.onFormSubmit.apply(this, arguments); - this.disableForm(); - - deleteLink(this.options.link.id) - .then(() => { - this.trigger('done'); - this.destroy(); - }) - .catch(e => { - parseError(e).then(msg => this.showSingleError(msg)); - this.enableForm(); - }); - }, - - serializeData() { - return { link: this.options.link }; - } -}); diff --git a/server/sonar-web/src/main/js/apps/project-admin/links/views/DeletionModalTemplate.hbs b/server/sonar-web/src/main/js/apps/project-admin/links/views/DeletionModalTemplate.hbs deleted file mode 100644 index b8d744c83d9..00000000000 --- a/server/sonar-web/src/main/js/apps/project-admin/links/views/DeletionModalTemplate.hbs +++ /dev/null @@ -1,13 +0,0 @@ -
- - - -
diff --git a/server/sonar-web/src/main/js/apps/project-admin/store/actions.js b/server/sonar-web/src/main/js/apps/project-admin/store/actions.js index 11c64002edc..189832011e1 100644 --- a/server/sonar-web/src/main/js/apps/project-admin/store/actions.js +++ b/server/sonar-web/src/main/js/apps/project-admin/store/actions.js @@ -17,8 +17,9 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { getProjectLinks, createLink } from '../../../api/projectLinks'; +import { getProjectLinks, createLink, deleteLink } from '../../../api/projectLinks'; import { getTree, changeKey as changeKeyApi } from '../../../api/components'; +import throwGlobalError from '../../../app/utils/throwGlobalError'; export const RECEIVE_PROJECT_LINKS = 'projectAdmin/RECEIVE_PROJECT_LINKS'; export const receiveProjectLinks = (projectKey, links) => ({ @@ -28,9 +29,12 @@ export const receiveProjectLinks = (projectKey, links) => ({ }); export const fetchProjectLinks = projectKey => dispatch => { - getProjectLinks(projectKey).then(links => { - dispatch(receiveProjectLinks(projectKey, links)); - }); + getProjectLinks(projectKey).then( + links => { + dispatch(receiveProjectLinks(projectKey, links)); + }, + () => {} + ); }; export const ADD_PROJECT_LINK = 'projectAdmin/ADD_PROJECT_LINK'; @@ -47,12 +51,19 @@ export const createProjectLink = (projectKey, name, url) => dispatch => { }; export const DELETE_PROJECT_LINK = 'projectAdmin/DELETE_PROJECT_LINK'; -export const deleteProjectLink = (projectKey, linkId) => ({ +export const deleteProjectLinkAction = (projectKey, linkId) => ({ type: DELETE_PROJECT_LINK, projectKey, linkId }); +export function deleteProjectLink(projectKey, linkId) { + return dispatch => + deleteLink(linkId).then(() => { + dispatch(deleteProjectLinkAction(projectKey, linkId)); + }); +} + export const RECEIVE_PROJECT_MODULES = 'projectAdmin/RECEIVE_PROJECT_MODULES'; const receiveProjectModules = (projectKey, modules) => ({ type: RECEIVE_PROJECT_MODULES, @@ -62,9 +73,12 @@ const receiveProjectModules = (projectKey, modules) => ({ export const fetchProjectModules = projectKey => dispatch => { const options = { qualifiers: 'BRC', s: 'name', ps: 500 }; - getTree(projectKey, options).then(r => { - dispatch(receiveProjectModules(projectKey, r.components)); - }); + getTree(projectKey, options).then( + r => { + dispatch(receiveProjectModules(projectKey, r.components)); + }, + () => {} + ); }; export const CHANGE_KEY = 'projectAdmin/CHANGE_KEY'; @@ -75,5 +89,8 @@ const changeKeyAction = (key, newKey) => ({ }); export const changeKey = (key, newKey) => dispatch => { - return changeKeyApi(key, newKey).then(() => dispatch(changeKeyAction(key, newKey))); + return changeKeyApi(key, newKey).then( + () => dispatch(changeKeyAction(key, newKey)), + throwGlobalError + ); }; diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/Projects.js b/server/sonar-web/src/main/js/apps/quality-gates/components/Projects.js index de38009e128..09d8baf401d 100644 --- a/server/sonar-web/src/main/js/apps/quality-gates/components/Projects.js +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/Projects.js @@ -18,44 +18,55 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import React from 'react'; -import ProjectsView from '../views/gate-projects-view'; +import escapeHtml from 'escape-html'; +import SelectList from '../../../components/SelectList'; +import { translate } from '../../../helpers/l10n'; +import { getBaseUrl } from '../../../helpers/urls'; export default class Projects extends React.PureComponent { componentDidMount() { - this.renderView(); + this.renderSelectList(); } - componentWillUpdate() { - this.destroyView(); - } - - componentDidUpdate() { - this.renderView(); - } + renderSelectList = () => { + if (!this.container) return; - componentWillUnmount() { - this.destroyView(); - } + const { qualityGate, edit, organization } = this.props; - destroyView() { - if (this.projectsView) { - this.projectsView.destroy(); + const extra = { gateId: qualityGate.id }; + let orgQuery = ''; + if (organization) { + extra.organization = organization; + orgQuery = '&organization=' + organization; } - } - - renderView() { - const { qualityGate, edit, organization } = this.props; - this.projectsView = new ProjectsView({ - qualityGate, - edit, - container: this.refs.container, - organization + // eslint-disable-next-line no-new + new SelectList({ + el: this.container, + width: '100%', + readOnly: !edit, + focusSearch: false, + dangerouslyUnescapedHtmlFormat: item => escapeHtml(item.name), + searchUrl: getBaseUrl() + `/api/qualitygates/search?gateId=${qualityGate.id}${orgQuery}`, + selectUrl: getBaseUrl() + '/api/qualitygates/select', + deselectUrl: getBaseUrl() + '/api/qualitygates/deselect', + extra, + selectParameter: 'projectId', + selectParameterValue: 'id', + labels: { + selected: translate('quality_gates.projects.with'), + deselected: translate('quality_gates.projects.without'), + all: translate('quality_gates.projects.all'), + noResults: translate('quality_gates.projects.noResults') + }, + tooltips: { + select: translate('quality_gates.projects.select_hint'), + deselect: translate('quality_gates.projects.deselect_hint') + } }); - this.projectsView.render(); - } + }; render() { - return
; + return
(this.container = node)} />; } } diff --git a/server/sonar-web/src/main/js/apps/quality-gates/views/gate-projects-view.js b/server/sonar-web/src/main/js/apps/quality-gates/views/gate-projects-view.js deleted file mode 100644 index 85ab920f067..00000000000 --- a/server/sonar-web/src/main/js/apps/quality-gates/views/gate-projects-view.js +++ /dev/null @@ -1,73 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import Marionette from 'backbone.marionette'; -import escapeHtml from 'escape-html'; -import SelectList from '../../../components/SelectList'; -import { translate } from '../../../helpers/l10n'; - -export default Marionette.ItemView.extend({ - template: () => {}, - - onRender() { - const { qualityGate, organization } = this.options; - - const extra = { - gateId: qualityGate.id - }; - let orgQuery = ''; - if (organization) { - extra.organization = organization; - orgQuery = '&organization=' + organization; - } - - new SelectList({ - el: this.options.container, - width: '100%', - readOnly: !this.options.edit, - focusSearch: false, - dangerouslyUnescapedHtmlFormat(item) { - return escapeHtml(item.name); - }, - searchUrl: `${window.baseUrl}/api/qualitygates/search?gateId=${qualityGate.id}${orgQuery}`, - selectUrl: window.baseUrl + '/api/qualitygates/select', - deselectUrl: window.baseUrl + '/api/qualitygates/deselect', - extra, - selectParameter: 'projectId', - selectParameterValue: 'id', - labels: { - selected: translate('quality_gates.projects.with'), - deselected: translate('quality_gates.projects.without'), - all: translate('quality_gates.projects.all'), - noResults: translate('quality_gates.projects.noResults') - }, - tooltips: { - select: translate('quality_gates.projects.select_hint'), - deselect: translate('quality_gates.projects.deselect_hint') - } - }); - }, - - serializeData() { - return { - ...Marionette.ItemView.prototype.serializeData.apply(this, arguments), - canEdit: this.options.edit - }; - } -}); diff --git a/server/sonar-web/src/main/js/components/RestartModal/index.js b/server/sonar-web/src/main/js/components/RestartModal/index.js deleted file mode 100644 index c3d3f2669a4..00000000000 --- a/server/sonar-web/src/main/js/components/RestartModal/index.js +++ /dev/null @@ -1,47 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import Template from './templates/template.hbs'; -import RestartingTemplate from './templates/restarting.hbs'; -import ModalForm from '../common/modal-form'; -import { restartAndWait } from '../../api/system'; - -const RestartModal = ModalForm.extend({ - template: Template, - restartingTemplate: RestartingTemplate, - - initialize() { - this.restarting = false; - }, - - getTemplate() { - return this.restarting ? this.restartingTemplate : this.template; - }, - - onFormSubmit() { - ModalForm.prototype.onFormSubmit.apply(this, arguments); - this.restarting = true; - this.render(); - restartAndWait().then(() => { - document.location.reload(); - }); - } -}); - -export default RestartModal; diff --git a/server/sonar-web/src/main/js/components/RestartModal/templates/restarting.hbs b/server/sonar-web/src/main/js/components/RestartModal/templates/restarting.hbs deleted file mode 100644 index bab7b505ab7..00000000000 --- a/server/sonar-web/src/main/js/components/RestartModal/templates/restarting.hbs +++ /dev/null @@ -1,14 +0,0 @@ -
- - -
diff --git a/server/sonar-web/src/main/js/components/RestartModal/templates/template.hbs b/server/sonar-web/src/main/js/components/RestartModal/templates/template.hbs deleted file mode 100644 index 6058532191c..00000000000 --- a/server/sonar-web/src/main/js/components/RestartModal/templates/template.hbs +++ /dev/null @@ -1,15 +0,0 @@ -
- - - -
diff --git a/server/sonar-web/src/main/js/components/common/modal-form.js b/server/sonar-web/src/main/js/components/common/modal-form.js deleted file mode 100644 index b201fbf7f1f..00000000000 --- a/server/sonar-web/src/main/js/components/common/modal-form.js +++ /dev/null @@ -1,99 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import ModalView from './modals'; - -export default ModalView.extend({ - ui() { - return { - messagesContainer: '.js-modal-messages' - }; - }, - - events() { - return { - ...ModalView.prototype.events.apply(this, arguments), - 'keydown input,textarea,select': 'onInputKeydown', - 'submit form': 'onFormSubmit' - }; - }, - - onRender() { - ModalView.prototype.onRender.apply(this, arguments); - const that = this; - setTimeout(() => { - that - .$(':tabbable') - .first() - .focus(); - }, 0); - }, - - onInputKeydown(e) { - if (e.keyCode === 27) { - // escape - this.destroy(); - } - }, - - onFormSubmit(e) { - e.preventDefault(); - }, - - showErrors(errors, warnings) { - const container = this.ui.messagesContainer.empty(); - if (Array.isArray(errors)) { - errors.forEach(error => { - const html = `
${error.msg}
`; - container.append(html); - }); - } - if (Array.isArray(warnings)) { - warnings.forEach(warn => { - const html = `
${warn.msg}
`; - container.append(html); - }); - } - this.ui.messagesContainer.scrollParent().scrollTop(0); - }, - - showSingleError(msg) { - this.showErrors([{ msg }], []); - }, - - disableForm() { - const form = this.$('form'); - this.disabledFields = form.find(':input:not(:disabled)'); - this.disabledFields.prop('disabled', true); - }, - - enableForm() { - if (this.disabledFields != null) { - this.disabledFields.prop('disabled', false); - } - }, - - showSpinner() { - this.$('.js-modal-spinner').removeClass('hidden'); - }, - - hideSpinner() { - this.$('.js-modal-spinner').addClass('hidden'); - } -}); diff --git a/server/sonar-web/src/main/js/components/common/modals.js b/server/sonar-web/src/main/js/components/common/modals.js deleted file mode 100644 index 6251b772ed0..00000000000 --- a/server/sonar-web/src/main/js/components/common/modals.js +++ /dev/null @@ -1,92 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import $ from 'jquery'; -import Marionette from 'backbone.marionette'; -import key from 'keymaster'; - -const EVENT_SCOPE = 'modal'; - -export default Marionette.ItemView.extend({ - className: 'modal', - overlayClassName: 'modal-overlay', - htmlClassName: 'modal-open', - - events() { - return { - 'click .js-modal-close': 'onCloseClick' - }; - }, - - onRender() { - const that = this; - this.$el.detach().appendTo($('body')); - $('html').addClass(this.htmlClassName); - this.renderOverlay(); - this.keyScope = key.getScope(); - key.setScope('modal'); - key('escape', 'modal', () => { - that.destroy(); - return false; - }); - this.show(); - if (this.options.large) { - this.$el.addClass('modal-large'); - } - }, - - show() { - const that = this; - setTimeout(() => { - that.$el.addClass('in'); - $('.' + that.overlayClassName).addClass('in'); - }, 0); - }, - - onDestroy() { - $('html').removeClass(this.htmlClassName); - this.removeOverlay(); - key.deleteScope('modal'); - key.setScope(this.keyScope); - }, - - onCloseClick(e) { - e.preventDefault(); - this.destroy(); - }, - - renderOverlay() { - const overlay = $('.' + this.overlayClassName); - if (overlay.length === 0) { - $(`
`).appendTo($('body')); - } - }, - - removeOverlay() { - $('.' + this.overlayClassName).remove(); - }, - - attachCloseEvents() { - const that = this; - $('body').on('click.' + EVENT_SCOPE, () => { - $('body').off('click.' + EVENT_SCOPE); - that.destroy(); - }); - } -}); diff --git a/server/sonar-web/src/main/js/components/common/selectable-collection-view.js b/server/sonar-web/src/main/js/components/common/selectable-collection-view.js deleted file mode 100644 index 41ce5d2f13b..00000000000 --- a/server/sonar-web/src/main/js/components/common/selectable-collection-view.js +++ /dev/null @@ -1,82 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import Marionette from 'backbone.marionette'; - -export default Marionette.CollectionView.extend({ - initialize() { - this.resetSelectedIndex(); - this.listenTo(this.collection, 'reset', this.resetSelectedIndex); - }, - - childViewOptions(model, index) { - return { index }; - }, - - resetSelectedIndex() { - this.selectedIndex = 0; - }, - - onRender() { - this.selectCurrent(); - }, - - submitCurrent() { - const view = this.children.findByIndex(this.selectedIndex); - if (view != null) { - view.submit(); - } - }, - - selectCurrent() { - this.selectItem(this.selectedIndex); - }, - - selectNext() { - if (this.selectedIndex < this.collection.length - 1) { - this.deselectItem(this.selectedIndex); - this.selectedIndex++; - this.selectItem(this.selectedIndex); - } - }, - - selectPrev() { - if (this.selectedIndex > 0) { - this.deselectItem(this.selectedIndex); - this.selectedIndex--; - this.selectItem(this.selectedIndex); - } - }, - - selectItem(index) { - if (index >= 0 && index < this.collection.length) { - const view = this.children.findByIndex(index); - if (view != null) { - view.select(); - } - } - }, - - deselectItem(index) { - const view = this.children.findByIndex(index); - if (view != null) { - view.deselect(); - } - } -}); diff --git a/server/sonar-web/src/main/js/components/controls/ConfirmButton.tsx b/server/sonar-web/src/main/js/components/controls/ConfirmButton.tsx index 0f05db2ac90..ab766a9eed6 100644 --- a/server/sonar-web/src/main/js/components/controls/ConfirmButton.tsx +++ b/server/sonar-web/src/main/js/components/controls/ConfirmButton.tsx @@ -23,8 +23,13 @@ import DeferredSpinner from '../common/DeferredSpinner'; import { translate } from '../../helpers/l10n'; import { SubmitButton, ResetButtonLink } from '../ui/buttons'; +export interface ChildrenProps { + onClick: () => void; + onFormSubmit: (event: React.FormEvent) => void; +} + interface Props { - children: (props: { onClick: () => void }) => React.ReactNode; + children: (props: ChildrenProps) => React.ReactNode; confirmButtonText: string; confirmData?: string; isDestructive?: boolean; @@ -53,6 +58,13 @@ export default class ConfirmButton extends React.PureComponent { this.setState({ modal: true }); }; + handleFormSubmit = (event?: React.FormEvent) => { + if (event) { + event.preventDefault(); + } + this.setState({ modal: true }); + }; + handleSubmit = () => { const result = this.props.onConfirm(this.props.confirmData); if (result) { @@ -74,7 +86,10 @@ export default class ConfirmButton extends React.PureComponent { return ( <> - {this.props.children({ onClick: this.handleButtonClick })} + {this.props.children({ + onClick: this.handleButtonClick, + onFormSubmit: this.handleFormSubmit + })} {this.state.modal && (