]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10842 Prevent user from submiting empty event names in project activity page
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>
Mon, 4 Jun 2018 08:06:00 +0000 (10:06 +0200)
committerSonarTech <sonartech@sonarsource.com>
Tue, 5 Jun 2018 18:20:51 +0000 (20:20 +0200)
13 files changed:
server/sonar-web/src/main/js/apps/projectActivity/components/Event.js [deleted file]
server/sonar-web/src/main/js/apps/projectActivity/components/Event.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.js [deleted file]
server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeEventForm.js [deleted file]
server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeEventForm.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.js [deleted file]
server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveEventForm.js [deleted file]
server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveEventForm.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/controls/ConfirmModal.tsx
server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/ConfirmModal-test.tsx.snap
server/sonar-web/src/main/js/components/ui/buttons.tsx

diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/Event.js b/server/sonar-web/src/main/js/apps/projectActivity/components/Event.js
deleted file mode 100644 (file)
index 6d25558..0000000
+++ /dev/null
@@ -1,142 +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.
- */
-// @flow
-import React from 'react';
-import EventInner from './EventInner';
-import ChangeEventForm from './forms/ChangeEventForm';
-import RemoveEventForm from './forms/RemoveEventForm';
-import Tooltip from '../../../components/controls/Tooltip';
-import { DeleteButton, EditButton } from '../../../components/ui/buttons';
-import { translate } from '../../../helpers/l10n';
-/*:: import type { Event as EventType } from '../types'; */
-
-/*::
-type Props = {
-  analysis: string,
-  canAdmin: boolean,
-  changeEvent: (event: string, name: string) => Promise<*>,
-  deleteEvent: (analysis: string, event: string) => Promise<*>,
-  event: EventType,
-  isFirst: boolean
-};
-*/
-
-/*::
-type State = {
-  changing: boolean,
-  deleting: boolean
-};
-*/
-
-export default class Event extends React.PureComponent {
-  /*:: mounted: boolean; */
-  /*:: props: Props; */
-  state /*: State */ = {
-    changing: false,
-    deleting: false
-  };
-
-  componentDidMount() {
-    this.mounted = true;
-  }
-
-  componentWillUnmount() {
-    this.mounted = false;
-  }
-
-  startChanging = () => {
-    this.setState({ changing: true });
-  };
-
-  stopChanging = () => {
-    if (this.mounted) {
-      this.setState({ changing: false });
-    }
-  };
-
-  startDeleting = () => {
-    this.setState({ deleting: true });
-  };
-
-  stopDeleting = () => {
-    if (this.mounted) {
-      this.setState({ deleting: false });
-    }
-  };
-
-  render() {
-    const { event, canAdmin } = this.props;
-    const isOther = event.category === 'OTHER';
-    const isVersion = !isOther && event.category === 'VERSION';
-    const canChange = isOther || isVersion;
-    const canDelete = isOther || (isVersion && !this.props.isFirst);
-    const showActions = canAdmin && (canChange || canDelete);
-
-    return (
-      <div className="project-activity-event">
-        <EventInner event={this.props.event} />
-
-        {showActions && (
-          <div className="project-activity-event-actions spacer-left">
-            {canChange && (
-              <Tooltip overlay={translate('project_activity.events.tooltip.edit')}>
-                <EditButton className="js-change-event button-small" onClick={this.startChanging} />
-              </Tooltip>
-            )}
-            {canDelete && (
-              <Tooltip overlay={translate('project_activity.events.tooltip.delete')}>
-                <DeleteButton
-                  className="js-delete-event button-small"
-                  onClick={this.startDeleting}
-                />
-              </Tooltip>
-            )}
-          </div>
-        )}
-
-        {this.state.changing && (
-          <ChangeEventForm
-            changeEventButtonText={
-              'project_activity.' + (isVersion ? 'change_version' : 'change_custom_event')
-            }
-            changeEvent={this.props.changeEvent}
-            event={this.props.event}
-            onClose={this.stopChanging}
-          />
-        )}
-
-        {this.state.deleting && (
-          <RemoveEventForm
-            analysis={this.props.analysis}
-            deleteEvent={this.props.deleteEvent}
-            event={this.props.event}
-            onClose={this.stopDeleting}
-            removeEventButtonText={
-              'project_activity.' + (isVersion ? 'remove_version' : 'remove_custom_event')
-            }
-            removeEventQuestion={`project_activity.${
-              isVersion ? 'remove_version' : 'remove_custom_event'
-            }.question`}
-          />
-        )}
-      </div>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/Event.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/Event.tsx
new file mode 100644 (file)
index 0000000..0079c26
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * 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 EventInner from './EventInner';
+import ChangeEventForm from './forms/ChangeEventForm';
+import RemoveEventForm from './forms/RemoveEventForm';
+import Tooltip from '../../../components/controls/Tooltip';
+import { DeleteButton, EditButton } from '../../../components/ui/buttons';
+import { translate } from '../../../helpers/l10n';
+import { Event as IEvent } from '../../../api/projectActivity';
+
+interface Props {
+  analysis: string;
+  canAdmin: boolean;
+  changeEvent: (event: string, name: string) => Promise<void>;
+  deleteEvent: (analysis: string, event: string) => Promise<void>;
+  event: IEvent;
+  isFirst: boolean;
+}
+
+interface State {
+  changing: boolean;
+  deleting: boolean;
+}
+
+export default class Event extends React.PureComponent<Props, State> {
+  mounted = false;
+  state: State = { changing: false, deleting: false };
+
+  componentDidMount() {
+    this.mounted = true;
+  }
+
+  componentWillUnmount() {
+    this.mounted = false;
+  }
+
+  startChanging = () => {
+    this.setState({ changing: true });
+  };
+
+  stopChanging = () => {
+    if (this.mounted) {
+      this.setState({ changing: false });
+    }
+  };
+
+  startDeleting = () => {
+    this.setState({ deleting: true });
+  };
+
+  stopDeleting = () => {
+    if (this.mounted) {
+      this.setState({ deleting: false });
+    }
+  };
+
+  render() {
+    const { event, canAdmin } = this.props;
+    const isOther = event.category === 'OTHER';
+    const isVersion = !isOther && event.category === 'VERSION';
+    const canChange = isOther || isVersion;
+    const canDelete = isOther || (isVersion && !this.props.isFirst);
+    const showActions = canAdmin && (canChange || canDelete);
+
+    return (
+      <div className="project-activity-event">
+        <EventInner event={this.props.event} />
+
+        {showActions && (
+          <div className="project-activity-event-actions spacer-left">
+            {canChange && (
+              <Tooltip overlay={translate('project_activity.events.tooltip.edit')}>
+                <EditButton className="js-change-event button-small" onClick={this.startChanging} />
+              </Tooltip>
+            )}
+            {canDelete && (
+              <Tooltip overlay={translate('project_activity.events.tooltip.delete')}>
+                <DeleteButton
+                  className="js-delete-event button-small"
+                  onClick={this.startDeleting}
+                />
+              </Tooltip>
+            )}
+          </div>
+        )}
+
+        {this.state.changing && (
+          <ChangeEventForm
+            changeEvent={this.props.changeEvent}
+            event={this.props.event}
+            header={
+              isVersion
+                ? translate('project_activity.change_version')
+                : translate('project_activity.change_custom_event')
+            }
+            onClose={this.stopChanging}
+          />
+        )}
+
+        {this.state.deleting && (
+          <RemoveEventForm
+            analysis={this.props.analysis}
+            deleteEvent={this.props.deleteEvent}
+            event={this.props.event}
+            header={
+              isVersion
+                ? translate('project_activity.remove_version')
+                : translate('project_activity.remove_custom_event')
+            }
+            onClose={this.stopDeleting}
+            removeEventQuestion={`project_activity.${
+              isVersion ? 'remove_version' : 'remove_custom_event'
+            }.question`}
+          />
+        )}
+      </div>
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.js b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.js
deleted file mode 100644 (file)
index e421268..0000000
+++ /dev/null
@@ -1,117 +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.
- */
-// @flow
-import React from 'react';
-import Modal from '../../../../components/controls/Modal';
-import { translate } from '../../../../helpers/l10n';
-import { SubmitButton, ResetButtonLink } from '../../../../components/ui/buttons';
-/*:: import type { Analysis } from '../../types'; */
-
-/*::
-type Props = {
-  addEvent: (analysis: string, name: string, category?: string) => Promise<*>,
-  analysis: Analysis,
-  addEventButtonText: string,
-  onClose: () => void;
-};
-*/
-
-/*::
-type State = {
-  processing: boolean,
-  name: string
-};
-*/
-
-export default class AddEventForm extends React.PureComponent {
-  /*:: mounted: boolean; */
-  /*:: props: Props; */
-  state /*: State */ = {
-    processing: false,
-    name: ''
-  };
-
-  componentDidMount() {
-    this.mounted = true;
-  }
-
-  componentWillUnmount() {
-    this.mounted = false;
-  }
-
-  changeInput = (e /*: Object */) => {
-    if (this.mounted) {
-      this.setState({ name: e.target.value });
-    }
-  };
-
-  stopProcessing = () => {
-    if (this.mounted) {
-      this.setState({ processing: false });
-    }
-  };
-
-  handleSubmit = (e /*: Object */) => {
-    e.preventDefault();
-    this.setState({ processing: true });
-    this.props
-      .addEvent(this.props.analysis.key, this.state.name)
-      .then(this.props.onClose, this.stopProcessing);
-  };
-
-  render() {
-    const header = translate(this.props.addEventButtonText);
-    return (
-      <Modal contentLabel={header} key="add-event-modal" onRequestClose={this.props.onClose}>
-        <header className="modal-head">
-          <h2>{header}</h2>
-        </header>
-
-        <form onSubmit={this.handleSubmit}>
-          <div className="modal-body">
-            <div className="modal-field">
-              <label>{translate('name')}</label>
-              <input
-                autoFocus={true}
-                disabled={this.state.processing}
-                onChange={this.changeInput}
-                type="text"
-                value={this.state.name}
-              />
-            </div>
-          </div>
-
-          <footer className="modal-foot">
-            {this.state.processing ? (
-              <i className="spinner" />
-            ) : (
-              <div>
-                <SubmitButton>{translate('save')}</SubmitButton>
-                <ResetButtonLink onClick={this.props.onClose}>
-                  {translate('cancel')}
-                </ResetButtonLink>
-              </div>
-            )}
-          </footer>
-        </form>
-      </Modal>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.tsx
new file mode 100644 (file)
index 0000000..5b78e19
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * 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 ConfirmModal from '../../../../components/controls/ConfirmModal';
+import { translate } from '../../../../helpers/l10n';
+import { Analysis } from '../../../../api/projectActivity';
+
+interface Props {
+  addEvent: (analysis: string, name: string, category?: string) => Promise<void>;
+  addEventButtonText: string;
+  analysis: Analysis;
+  onClose: () => void;
+}
+
+interface State {
+  name: string;
+}
+
+export default class AddEventForm extends React.PureComponent<Props, State> {
+  state: State = { name: '' };
+
+  handleNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {
+    this.setState({ name: event.target.value });
+  };
+
+  handleSubmit = () => {
+    return this.props.addEvent(this.props.analysis.key, this.state.name);
+  };
+
+  render() {
+    return (
+      <ConfirmModal
+        confirmButtonText={translate('save')}
+        confirmDisable={!this.state.name}
+        header={translate(this.props.addEventButtonText)}
+        onClose={this.props.onClose}
+        onConfirm={this.handleSubmit}>
+        <div className="modal-field">
+          <label>{translate('name')}</label>
+          <input
+            autoFocus={true}
+            onChange={this.handleNameChange}
+            type="text"
+            value={this.state.name}
+          />
+        </div>
+      </ConfirmModal>
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeEventForm.js b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeEventForm.js
deleted file mode 100644 (file)
index 25465a6..0000000
+++ /dev/null
@@ -1,134 +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.
- */
-// @flow
-import React from 'react';
-import Modal from '../../../../components/controls/Modal';
-import { translate } from '../../../../helpers/l10n';
-import { SubmitButton, ResetButtonLink } from '../../../../components/ui/buttons';
-/*:: import type { Event } from '../../types'; */
-
-/*::
-type Props = {
-  changeEvent: (event: string, name: string) => Promise<*>,
-  changeEventButtonText: string,
-  event: Event,
-  onClose: () => void
-};
-*/
-
-/*::
-type State = {
-  processing: boolean,
-  name: string
-};
-*/
-
-export default class ChangeEventForm extends React.PureComponent {
-  /*:: mounted: boolean; */
-  /*:: props: Props; */
-  /*:: state: State; */
-
-  constructor(props /*: Props */) {
-    super(props);
-    this.state = {
-      processing: false,
-      name: props.event.name
-    };
-  }
-
-  componentDidMount() {
-    this.mounted = true;
-  }
-
-  componentWillUnmount() {
-    this.mounted = false;
-  }
-
-  closeForm = () => {
-    if (this.mounted) {
-      this.setState({ name: this.props.event.name });
-    }
-    this.props.onClose();
-  };
-
-  changeInput = (e /*: Object */) => {
-    if (this.mounted) {
-      this.setState({ name: e.target.value });
-    }
-  };
-
-  stopProcessing = () => {
-    if (this.mounted) {
-      this.setState({ processing: false });
-    }
-  };
-
-  stopProcessingAndClose = () => {
-    if (this.mounted) {
-      this.setState({ processing: false });
-    }
-    this.props.onClose();
-  };
-
-  handleSubmit = (e /*: Object */) => {
-    e.preventDefault();
-    this.setState({ processing: true });
-    this.props
-      .changeEvent(this.props.event.key, this.state.name)
-      .then(this.stopProcessingAndClose, this.stopProcessing);
-  };
-
-  render() {
-    const header = translate(this.props.changeEventButtonText);
-    return (
-      <Modal contentLabel={header} onRequestClose={this.closeForm}>
-        <header className="modal-head">
-          <h2>{header}</h2>
-        </header>
-
-        <form onSubmit={this.handleSubmit}>
-          <div className="modal-body">
-            <div className="modal-field">
-              <label>{translate('name')}</label>
-              <input
-                autoFocus={true}
-                disabled={this.state.processing}
-                onChange={this.changeInput}
-                type="text"
-                value={this.state.name}
-              />
-            </div>
-          </div>
-
-          <footer className="modal-foot">
-            {this.state.processing ? (
-              <i className="spinner" />
-            ) : (
-              <div>
-                <SubmitButton>{translate('change_verb')}</SubmitButton>
-                <ResetButtonLink onClick={this.closeForm}>{translate('cancel')}</ResetButtonLink>
-              </div>
-            )}
-          </footer>
-        </form>
-      </Modal>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeEventForm.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeEventForm.tsx
new file mode 100644 (file)
index 0000000..c31910a
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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 { translate } from '../../../../helpers/l10n';
+import { Event } from '../../../../api/projectActivity';
+import ConfirmModal from '../../../../components/controls/ConfirmModal';
+
+interface Props {
+  changeEvent: (event: string, name: string) => Promise<void>;
+  header: string;
+  event: Event;
+  onClose: () => void;
+}
+
+interface State {
+  name: string;
+}
+
+export default class ChangeEventForm extends React.PureComponent<Props, State> {
+  constructor(props: Props) {
+    super(props);
+    this.state = { name: props.event.name };
+  }
+
+  changeInput = (event: React.ChangeEvent<HTMLInputElement>) => {
+    this.setState({ name: event.target.value });
+  };
+
+  handleSubmit = () => {
+    return this.props.changeEvent(this.props.event.key, this.state.name);
+  };
+
+  render() {
+    const { name } = this.state;
+    return (
+      <ConfirmModal
+        confirmButtonText={translate('change_verb')}
+        confirmDisable={!name || name === this.props.event.name}
+        header={this.props.header}
+        onClose={this.props.onClose}
+        onConfirm={this.handleSubmit}>
+        <div className="modal-field">
+          <label>{translate('name')}</label>
+          <input autoFocus={true} onChange={this.changeInput} type="text" value={name} />
+        </div>
+      </ConfirmModal>
+    );
+  }
+}
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.js b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.js
deleted file mode 100644 (file)
index e0e5dd7..0000000
+++ /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.
- */
-// @flow
-import React from 'react';
-import Modal from '../../../../components/controls/Modal';
-import { translate } from '../../../../helpers/l10n';
-import { SubmitButton, ResetButtonLink } from '../../../../components/ui/buttons';
-/*:: import type { Analysis } from '../../types'; */
-
-/*::
-type Props = {
-  analysis: Analysis,
-  deleteAnalysis: (analysis: string) => Promise<*>,
-  onClose: () => void;
-};
-*/
-
-/*::
-type State = {
-  processing: boolean
-};
-*/
-
-export default class RemoveAnalysisForm extends React.PureComponent {
-  /*:: mounted: boolean; */
-  /*:: props: Props; */
-  state /*: State */ = {
-    processing: false
-  };
-
-  componentDidMount() {
-    this.mounted = true;
-  }
-
-  componentWillUnmount() {
-    this.mounted = false;
-  }
-
-  stopProcessing = () => {
-    if (this.mounted) {
-      this.setState({ processing: false });
-    }
-  };
-
-  handleSubmit = (e /*: Event */) => {
-    e.preventDefault();
-    this.setState({ processing: true });
-    this.props
-      .deleteAnalysis(this.props.analysis.key)
-      .then(this.props.onClose, this.stopProcessing);
-  };
-
-  render() {
-    const header = translate('project_activity.delete_analysis');
-    return (
-      <Modal contentLabel={header} key="delete-analysis-modal" onRequestClose={this.props.onClose}>
-        <header className="modal-head">
-          <h2>{header}</h2>
-        </header>
-
-        <form onSubmit={this.handleSubmit}>
-          <div className="modal-body">{translate('project_activity.delete_analysis.question')}</div>
-
-          <footer className="modal-foot">
-            {this.state.processing ? (
-              <i className="spinner" />
-            ) : (
-              <div>
-                <SubmitButton autoFocus={true} className="button-red">
-                  {translate('delete')}
-                </SubmitButton>
-                <ResetButtonLink onClick={this.props.onClose}>
-                  {translate('cancel')}
-                </ResetButtonLink>
-              </div>
-            )}
-          </footer>
-        </form>
-      </Modal>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.tsx
new file mode 100644 (file)
index 0000000..c4eb5fa
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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 { translate } from '../../../../helpers/l10n';
+import { Analysis } from '../../../../api/projectActivity';
+import ConfirmModal from '../../../../components/controls/ConfirmModal';
+
+interface Props {
+  analysis: Analysis;
+  deleteAnalysis: (analysis: string) => Promise<void>;
+  onClose: () => void;
+}
+
+export default function RemoveAnalysisForm({ analysis, deleteAnalysis, onClose }: Props) {
+  return (
+    <ConfirmModal
+      confirmButtonText={translate('delete')}
+      confirmData={analysis.key}
+      header={translate('project_activity.delete_analysis')}
+      isDestructive={true}
+      onClose={onClose}
+      onConfirm={deleteAnalysis}>
+      {translate('project_activity.delete_analysis.question')}
+    </ConfirmModal>
+  );
+}
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveEventForm.js b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveEventForm.js
deleted file mode 100644 (file)
index 5b5db9e..0000000
+++ /dev/null
@@ -1,111 +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.
- */
-// @flow
-import React from 'react';
-import Modal from '../../../../components/controls/Modal';
-import { translate } from '../../../../helpers/l10n';
-import { SubmitButton, ResetButtonLink } from '../../../../components/ui/buttons';
-/*:: import type { Event } from '../../types'; */
-
-/*::
-type Props = {
-  analysis: string,
-  deleteEvent: (analysis: string, event: string) => Promise<*>,
-  event: Event,
-  removeEventButtonText: string,
-  removeEventQuestion: string,
-  onClose: () => void
-};
-*/
-
-/*::
-type State = {
-  processing: boolean
-};
-*/
-
-export default class RemoveEventForm extends React.PureComponent {
-  /*:: mounted: boolean; */
-  /*:: props: Props; */
-  state /*: State */ = {
-    processing: false
-  };
-
-  componentDidMount() {
-    this.mounted = true;
-  }
-
-  componentWillUnmount() {
-    this.mounted = false;
-  }
-
-  closeForm = () => {
-    this.props.onClose();
-  };
-
-  stopProcessing = () => {
-    if (this.mounted) {
-      this.setState({ processing: false });
-    }
-  };
-
-  stopProcessingAndClose = () => {
-    if (this.mounted) {
-      this.setState({ processing: false });
-    }
-    this.props.onClose();
-  };
-
-  handleSubmit = (e /*: Object */) => {
-    e.preventDefault();
-    this.setState({ processing: true });
-    this.props
-      .deleteEvent(this.props.analysis, this.props.event.key)
-      .then(this.stopProcessingAndClose, this.stopProcessing);
-  };
-
-  render() {
-    const header = translate(this.props.removeEventButtonText);
-    return (
-      <Modal contentLabel={header} onRequestClose={this.closeForm}>
-        <header className="modal-head">
-          <h2>{header}</h2>
-        </header>
-
-        <form onSubmit={this.handleSubmit}>
-          <div className="modal-body">{translate(this.props.removeEventQuestion)}</div>
-
-          <footer className="modal-foot">
-            {this.state.processing ? (
-              <i className="spinner" />
-            ) : (
-              <div>
-                <SubmitButton autoFocus={true} className="button-red">
-                  {translate('delete')}
-                </SubmitButton>
-                <ResetButtonLink onClick={this.closeForm}>{translate('cancel')}</ResetButtonLink>
-              </div>
-            )}
-          </footer>
-        </form>
-      </Modal>
-    );
-  }
-}
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveEventForm.tsx b/server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveEventForm.tsx
new file mode 100644 (file)
index 0000000..df25f6f
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * 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 { translate } from '../../../../helpers/l10n';
+import { Event } from '../../../../api/projectActivity';
+import ConfirmModal from '../../../../components/controls/ConfirmModal';
+
+interface Props {
+  analysis: string;
+  deleteEvent: (analysis: string, event: string) => Promise<void>;
+  event: Event;
+  header: string;
+  removeEventQuestion: string;
+  onClose: () => void;
+}
+
+export default class RemoveEventForm extends React.PureComponent<Props> {
+  handleSubmit = () => {
+    return this.props.deleteEvent(this.props.analysis, this.props.event.key);
+  };
+
+  render() {
+    return (
+      <ConfirmModal
+        confirmButtonText={translate('delete')}
+        header={this.props.header}
+        isDestructive={true}
+        onClose={this.props.onClose}
+        onConfirm={this.handleSubmit}>
+        {translate(this.props.removeEventQuestion)}
+      </ConfirmModal>
+    );
+  }
+}
index 6456e5a790cfd5f5d559afb62013163c1e124ed1..cd34fcafb0bc8456040a9ef832b379063816816d 100644 (file)
@@ -66,6 +66,7 @@ export default class ConfirmModal<T = string> extends React.PureComponent<Props<
         <footer className="modal-foot">
           <DeferredSpinner className="spacer-right" loading={submitting} />
           <SubmitButton
+            autoFocus={true}
             className={isDestructive ? 'button-red' : undefined}
             disabled={submitting || confirmDisable}>
             {confirmButtonText}
index 800e634ed8a488294e89049a6a496571759fabe5..96172d41b936805de57e7c6068acb5ef24c3dc06 100644 (file)
@@ -10,6 +10,7 @@ exports[`should confirm and close after confirm 1`] = `
     timeout={100}
   />
   <SubmitButton
+    autoFocus={true}
     disabled={true}
   >
     confirm
@@ -61,7 +62,9 @@ exports[`should render correctly 2`] = `
         loading={false}
         timeout={100}
       />
-      <SubmitButton>
+      <SubmitButton
+        autoFocus={true}
+      >
         confirm
       </SubmitButton>
       <ResetButtonLink
index 1ede3bb8ce13e52e80ad13c2a2b59a5bca339fcb..f3644d39b590d779316dae1a5ddf1aa8f397e71a 100644 (file)
@@ -27,6 +27,7 @@ import Tooltip from '../controls/Tooltip';
 import './buttons.css';
 
 interface ButtonProps {
+  autoFocus?: boolean;
   className?: string;
   children?: React.ReactNode;
   disabled?: boolean;