aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/components/issue/popups
diff options
context:
space:
mode:
Diffstat (limited to 'server/sonar-web/src/main/js/components/issue/popups')
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/ChangelogPopup.js116
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/CommentDeletePopup.js39
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/CommentPopup.js113
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/SetAssigneePopup.js167
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/SetIssueTagsPopup.js86
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/SetSeverityPopup.js59
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/SetTransitionPopup.js57
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/SetTypePopup.js59
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/__tests__/ChangelogPopup-test.js46
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/__tests__/CommentDeletePopup-test.js31
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/__tests__/CommentPopup-test.js66
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/__tests__/SetIssueTagsPopup-test.js30
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/__tests__/SetSeverityPopup-test.js27
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/__tests__/SetTransitionPopup-test.js32
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/__tests__/SetTypePopup-test.js27
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/ChangelogPopup-test.js.snap50
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/CommentDeletePopup-test.js.snap17
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/CommentPopup-test.js.snap75
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetIssueTagsPopup-test.js.snap15
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetSeverityPopup-test.js.snap53
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetTransitionPopup-test.js.snap37
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetTypePopup-test.js.snap37
22 files changed, 1239 insertions, 0 deletions
diff --git a/server/sonar-web/src/main/js/components/issue/popups/ChangelogPopup.js b/server/sonar-web/src/main/js/components/issue/popups/ChangelogPopup.js
new file mode 100644
index 00000000000..49bdee8218e
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/issue/popups/ChangelogPopup.js
@@ -0,0 +1,116 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+// @flow
+import React from 'react';
+import moment from 'moment';
+import { getIssueChangelog } from '../../../api/issues';
+import { translate } from '../../../helpers/l10n';
+import Avatar from '../../../components/ui/Avatar';
+import BubblePopup from '../../../components/common/BubblePopup';
+import IssueChangelogDiff from '../components/IssueChangelogDiff';
+import type { ChangelogDiff } from '../components/IssueChangelogDiff';
+import type { Issue } from '../types';
+
+type Changelog = {
+ avatar?: string,
+ creationDate: string,
+ diffs: Array<ChangelogDiff>,
+ user: string,
+ userName: string
+};
+
+type Props = {
+ issue: Issue,
+ onFail: (Error) => void,
+ popupPosition?: {}
+};
+
+type State = {
+ changelogs: Array<Changelog>
+};
+
+export default class ChangelogPopup extends React.PureComponent {
+ mounted: boolean;
+ props: Props;
+ state: State = {
+ changelogs: []
+ };
+
+ componentDidMount() {
+ this.mounted = true;
+ this.loadChangelog();
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ loadChangelog() {
+ getIssueChangelog(this.props.issue.key).then(
+ changelogs => {
+ if (this.mounted) {
+ this.setState({ changelogs });
+ }
+ },
+ this.props.onFail
+ );
+ }
+
+ render() {
+ const { issue } = this.props;
+ const { author } = issue;
+ return (
+ <BubblePopup position={this.props.popupPosition} customClass="bubble-popup-bottom-right">
+ <div className="issue-changelog">
+ <table className="spaced">
+ <tbody>
+ <tr>
+ <td className="thin text-left text-top nowrap">
+ {moment(issue.creationDate).format('LLL')}
+ </td>
+ <td className="thin text-left text-top nowrap" />
+ <td className="text-left text-top">
+ {author ? `${translate('created_by')} ${author}` : translate('created')}
+ </td>
+ </tr>
+
+ {this.state.changelogs.map((item, idx) => (
+ <tr key={idx}>
+ <td className="thin text-left text-top nowrap">
+ {moment(item.creationDate).format('LLL')}
+ </td>
+ <td className="thin text-left text-top nowrap">
+ {item.userName &&
+ item.avatar &&
+ <Avatar className="little-spacer-right" hash={item.avatar} size={16} />}
+ {item.userName}
+ </td>
+ <td className="text-left text-top">
+ {item.diffs.map(diff => <IssueChangelogDiff key={diff.key} diff={diff} />)}
+ </td>
+ </tr>
+ ))}
+ </tbody>
+ </table>
+ </div>
+ </BubblePopup>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/components/issue/popups/CommentDeletePopup.js b/server/sonar-web/src/main/js/components/issue/popups/CommentDeletePopup.js
new file mode 100644
index 00000000000..3e06c95c07d
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/issue/popups/CommentDeletePopup.js
@@ -0,0 +1,39 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+// @flow
+import React from 'react';
+import { translate } from '../../../helpers/l10n';
+import BubblePopup from '../../../components/common/BubblePopup';
+
+type Props = {
+ onDelete: () => void,
+ popupPosition?: {}
+};
+
+export default function CommentDeletePopup(props: Props) {
+ return (
+ <BubblePopup position={props.popupPosition} customClass="bubble-popup-bottom-right">
+ <div className="text-right">
+ <div className="spacer-bottom">{translate('issue.comment.delete_confirm_message')}</div>
+ <button className="button-red" onClick={props.onDelete}>{translate('delete')}</button>
+ </div>
+ </BubblePopup>
+ );
+}
diff --git a/server/sonar-web/src/main/js/components/issue/popups/CommentPopup.js b/server/sonar-web/src/main/js/components/issue/popups/CommentPopup.js
new file mode 100644
index 00000000000..cb079327d53
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/issue/popups/CommentPopup.js
@@ -0,0 +1,113 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+// @flow
+import React from 'react';
+import classNames from 'classnames';
+import BubblePopup from '../../../components/common/BubblePopup';
+import MarkdownTips from '../../../components/common/MarkdownTips';
+import { translate } from '../../../helpers/l10n';
+import type { IssueComment } from '../types';
+
+type Props = {
+ comment?: IssueComment,
+ customClass?: string,
+ onComment: (string) => void,
+ toggleComment: (boolean) => void,
+ placeholder: string,
+ popupPosition?: {}
+};
+
+type State = {
+ textComment: string
+};
+
+export default class CommentPopup extends React.PureComponent {
+ props: Props;
+ state: State;
+
+ constructor(props: Props) {
+ super(props);
+ this.state = {
+ textComment: props.comment ? props.comment.markdown : ''
+ };
+ }
+
+ handleCommentChange = (evt: SyntheticInputEvent) => {
+ this.setState({ textComment: evt.target.value });
+ };
+
+ handleCommentClick = () => {
+ if (this.state.textComment.trim().length > 0) {
+ this.props.onComment(this.state.textComment);
+ }
+ };
+
+ handleCancelClick = (evt: MouseEvent) => {
+ evt.preventDefault();
+ this.props.toggleComment(false);
+ };
+
+ handleKeyboard = (evt: KeyboardEvent) => {
+ if (evt.keyCode === 13 && (evt.metaKey || evt.ctrlKey)) {
+ // Ctrl + Enter
+ this.handleCommentClick();
+ } else if ([37, 38, 39, 40].includes(evt.keyCode)) {
+ // Arrow keys
+ evt.stopPropagation();
+ }
+ };
+
+ render() {
+ const { comment } = this.props;
+ return (
+ <BubblePopup
+ position={this.props.popupPosition}
+ customClass={classNames(this.props.customClass, 'bubble-popup-bottom-right')}>
+ <div className="issue-comment-form-text">
+ <textarea
+ autoFocus={true}
+ placeholder={this.props.placeholder}
+ onChange={this.handleCommentChange}
+ onKeyDown={this.handleKeyboard}
+ value={this.state.textComment}
+ rows="2"
+ />
+ </div>
+ <div className="issue-comment-form-footer">
+ <div className="issue-comment-form-actions">
+ <button
+ className="js-issue-comment-submit little-spacer-right"
+ disabled={this.state.textComment.trim().length < 1}
+ onClick={this.handleCommentClick}>
+ {comment && translate('save')}
+ {!comment && translate('issue.comment.submit')}
+ </button>
+ <a href="#" className="js-issue-comment-cancel" onClick={this.handleCancelClick}>
+ {translate('cancel')}
+ </a>
+ </div>
+ <div className="issue-comment-form-tips">
+ <MarkdownTips />
+ </div>
+ </div>
+ </BubblePopup>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/components/issue/popups/SetAssigneePopup.js b/server/sonar-web/src/main/js/components/issue/popups/SetAssigneePopup.js
new file mode 100644
index 00000000000..c700c3a341f
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/issue/popups/SetAssigneePopup.js
@@ -0,0 +1,167 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+// @flow
+import React from 'react';
+import classNames from 'classnames';
+import { css } from 'glamor';
+import { debounce, map } from 'lodash';
+import Avatar from '../../../components/ui/Avatar';
+import BubblePopup from '../../../components/common/BubblePopup';
+import SelectList from '../../../components/common/SelectList';
+import SelectListItem from '../../../components/common/SelectListItem';
+import getCurrentUserFromStore from '../../../app/utils/getCurrentUserFromStore';
+import { areThereCustomOrganizations } from '../../../store/organizations/utils';
+import { searchMembers } from '../../../api/organizations';
+import { searchUsers } from '../../../api/users';
+import { translate } from '../../../helpers/l10n';
+import type { Issue } from '../types';
+
+type User = {
+ avatar?: string,
+ email?: string,
+ login: string,
+ name: string
+};
+
+type Props = {
+ issue: Issue,
+ onFail: (Error) => void,
+ onSelect: (string) => void,
+ popupPosition?: {}
+};
+
+type State = {
+ query: string,
+ users: Array<User>,
+ currentUser: string
+};
+
+const LIST_SIZE = 10;
+const USER_MARGIN = css({ marginLeft: '24px' });
+
+export default class SetAssigneePopup extends React.PureComponent {
+ defaultUsersArray: Array<User>;
+ organizationEnabled: boolean;
+ props: Props;
+ state: State;
+
+ constructor(props: Props) {
+ super(props);
+ this.organizationEnabled = areThereCustomOrganizations();
+ this.searchUsers = debounce(this.searchUsers, 250);
+ this.searchMembers = debounce(this.searchMembers, 250);
+ this.defaultUsersArray = [{ login: '', name: translate('unassigned') }];
+
+ const currentUser = getCurrentUserFromStore();
+ if (currentUser != null) {
+ this.defaultUsersArray = [currentUser, ...this.defaultUsersArray];
+ }
+
+ this.state = {
+ query: '',
+ users: this.defaultUsersArray,
+ currentUser: currentUser.login
+ };
+ }
+
+ searchMembers = (query: string) => {
+ searchMembers({
+ organization: this.props.issue.projectOrganization,
+ q: query,
+ ps: LIST_SIZE
+ }).then(this.handleSearchResult, this.props.onFail);
+ };
+
+ searchUsers = (query: string) => {
+ searchUsers(query, LIST_SIZE).then(this.handleSearchResult, this.props.onFail);
+ };
+
+ handleSearchResult = (data: Object) => {
+ this.setState({
+ users: data.users,
+ currentUser: data.users.length > 0 ? data.users[0].login : ''
+ });
+ };
+
+ handleSearchChange = (evt: SyntheticInputEvent) => {
+ const query = evt.target.value;
+ if (query.length < 2) {
+ this.setState({
+ query,
+ users: this.defaultUsersArray,
+ currentUser: this.defaultUsersArray[0].login
+ });
+ } else {
+ this.setState({ query });
+ if (this.organizationEnabled) {
+ this.searchMembers(query);
+ } else {
+ this.searchUsers(query);
+ }
+ }
+ };
+
+ render() {
+ return (
+ <BubblePopup
+ position={this.props.popupPosition}
+ customClass="bubble-popup-menu bubble-popup-bottom">
+ <div className="multi-select">
+ <div className="search-box menu-search">
+ <button className="search-box-submit button-clean">
+ <i className="icon-search-new" />
+ </button>
+ <input
+ type="search"
+ value={this.state.query}
+ className="search-box-input"
+ placeholder={translate('search_verb')}
+ onChange={this.handleSearchChange}
+ autoComplete="off"
+ autoFocus={true}
+ />
+ </div>
+ <SelectList
+ items={map(this.state.users, 'login')}
+ currentItem={this.state.currentUser}
+ onSelect={this.props.onSelect}>
+ {this.state.users.map(user => (
+ <SelectListItem key={user.login} item={user.login}>
+ {(user.avatar || user.email) &&
+ <Avatar
+ className="spacer-right"
+ email={user.email}
+ hash={user.avatar}
+ size={16}
+ />}
+ <span
+ className={classNames('vertical-middle', {
+ [USER_MARGIN]: !(user.avatar || user.email)
+ })}>
+ {user.name}
+ </span>
+ </SelectListItem>
+ ))}
+ </SelectList>
+ </div>
+ </BubblePopup>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/components/issue/popups/SetIssueTagsPopup.js b/server/sonar-web/src/main/js/components/issue/popups/SetIssueTagsPopup.js
new file mode 100644
index 00000000000..f8584f59f07
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/issue/popups/SetIssueTagsPopup.js
@@ -0,0 +1,86 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+//@flow
+import React from 'react';
+import { debounce, without } from 'lodash';
+import TagsSelector from '../../../components/tags/TagsSelector';
+import { searchIssueTags } from '../../../api/issues';
+
+type Props = {
+ popupPosition?: {},
+ onFail: (Error) => void,
+ selectedTags: Array<string>,
+ setTags: (Array<string>) => void
+};
+
+type State = {
+ searchResult: Array<string>
+};
+
+const LIST_SIZE = 10;
+
+export default class SetIssueTagsPopup extends React.PureComponent {
+ props: Props;
+ state: State;
+
+ constructor(props: Props) {
+ super(props);
+ this.state = { searchResult: [] };
+ this.onSearch = debounce(this.onSearch, 250);
+ }
+
+ componentDidMount() {
+ this.onSearch('');
+ }
+
+ onSearch = (query: string) => {
+ searchIssueTags({
+ q: query || '',
+ ps: Math.min(this.props.selectedTags.length - 1 + LIST_SIZE, 100)
+ }).then(
+ (tags: Array<string>) => {
+ this.setState({ searchResult: tags });
+ },
+ this.props.onFail
+ );
+ };
+
+ onSelect = (tag: string) => {
+ this.props.setTags([...this.props.selectedTags, tag]);
+ };
+
+ onUnselect = (tag: string) => {
+ this.props.setTags(without(this.props.selectedTags, tag));
+ };
+
+ render() {
+ return (
+ <TagsSelector
+ position={this.props.popupPosition}
+ tags={this.state.searchResult}
+ selectedTags={this.props.selectedTags}
+ listSize={LIST_SIZE}
+ onSearch={this.onSearch}
+ onSelect={this.onSelect}
+ onUnselect={this.onUnselect}
+ />
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/components/issue/popups/SetSeverityPopup.js b/server/sonar-web/src/main/js/components/issue/popups/SetSeverityPopup.js
new file mode 100644
index 00000000000..bef971615d5
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/issue/popups/SetSeverityPopup.js
@@ -0,0 +1,59 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+// @flow
+import React from 'react';
+import { translate } from '../../../helpers/l10n';
+import BubblePopup from '../../../components/common/BubblePopup';
+import SelectList from '../../../components/common/SelectList';
+import SelectListItem from '../../../components/common/SelectListItem';
+import SeverityIcon from '../../../components/shared/SeverityIcon';
+import type { Issue } from '../types';
+
+type Props = {
+ issue: Issue,
+ onSelect: (string) => void,
+ popupPosition?: {}
+};
+
+const SEVERITY = ['BLOCKER', 'CRITICAL', 'MAJOR', 'MINOR', 'INFO'];
+
+export default class SetSeverityPopup extends React.PureComponent {
+ props: Props;
+
+ render() {
+ return (
+ <BubblePopup
+ position={this.props.popupPosition}
+ customClass="bubble-popup-menu bubble-popup-bottom">
+ <SelectList
+ items={SEVERITY}
+ currentItem={this.props.issue.severity}
+ onSelect={this.props.onSelect}>
+ {SEVERITY.map(severity => (
+ <SelectListItem key={severity} item={severity}>
+ <SeverityIcon className="little-spacer-right" severity={severity} />
+ {translate('severity', severity)}
+ </SelectListItem>
+ ))}
+ </SelectList>
+ </BubblePopup>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/components/issue/popups/SetTransitionPopup.js b/server/sonar-web/src/main/js/components/issue/popups/SetTransitionPopup.js
new file mode 100644
index 00000000000..d1c83099551
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/issue/popups/SetTransitionPopup.js
@@ -0,0 +1,57 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+// @flow
+import React from 'react';
+import BubblePopup from '../../../components/common/BubblePopup';
+import SelectList from '../../../components/common/SelectList';
+import SelectListItem from '../../../components/common/SelectListItem';
+import { translate } from '../../../helpers/l10n';
+
+type Props = {
+ transitions: Array<string>,
+ onSelect: (string) => void,
+ popupPosition?: {}
+};
+
+export default class SetTransitionPopup extends React.PureComponent {
+ props: Props;
+
+ render() {
+ const { transitions } = this.props;
+ return (
+ <BubblePopup
+ position={this.props.popupPosition}
+ customClass="bubble-popup-menu bubble-popup-bottom">
+ <SelectList items={transitions} currentItem={transitions[0]} onSelect={this.props.onSelect}>
+ {transitions.map(transition => {
+ return (
+ <SelectListItem
+ key={transition}
+ item={transition}
+ title={translate('issue.transition', transition, 'description')}>
+ {translate('issue.transition', transition)}
+ </SelectListItem>
+ );
+ })}
+ </SelectList>
+ </BubblePopup>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/components/issue/popups/SetTypePopup.js b/server/sonar-web/src/main/js/components/issue/popups/SetTypePopup.js
new file mode 100644
index 00000000000..7bd10d57ede
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/issue/popups/SetTypePopup.js
@@ -0,0 +1,59 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+// @flow
+import React from 'react';
+import { translate } from '../../../helpers/l10n';
+import BubblePopup from '../../../components/common/BubblePopup';
+import IssueTypeIcon from '../../../components/ui/IssueTypeIcon';
+import SelectList from '../../../components/common/SelectList';
+import SelectListItem from '../../../components/common/SelectListItem';
+import type { Issue } from '../types';
+
+type Props = {
+ issue: Issue,
+ onSelect: (string) => void,
+ popupPosition?: {}
+};
+
+const TYPES = ['BUG', 'VULNERABILITY', 'CODE_SMELL'];
+
+export default class SetTypePopup extends React.PureComponent {
+ props: Props;
+
+ render() {
+ return (
+ <BubblePopup
+ position={this.props.popupPosition}
+ customClass="bubble-popup-menu bubble-popup-bottom">
+ <SelectList
+ items={TYPES}
+ currentItem={this.props.issue.type}
+ onSelect={this.props.onSelect}>
+ {TYPES.map(type => (
+ <SelectListItem key={type} item={type}>
+ <IssueTypeIcon className="little-spacer-right" query={type} />
+ {translate('issue.type', type)}
+ </SelectListItem>
+ ))}
+ </SelectList>
+ </BubblePopup>
+ );
+ }
+}
diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/ChangelogPopup-test.js b/server/sonar-web/src/main/js/components/issue/popups/__tests__/ChangelogPopup-test.js
new file mode 100644
index 00000000000..6c4f9d5977e
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/ChangelogPopup-test.js
@@ -0,0 +1,46 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import React from 'react';
+import ChangelogPopup from '../ChangelogPopup';
+
+it('should render the changelog popup correctly', () => {
+ const element = shallow(
+ <ChangelogPopup
+ issue={{
+ key: 'issuekey',
+ author: 'john.david.dalton@gmail.com',
+ creationDate: '2017-03-01T09:36:01+0100'
+ }}
+ onFail={jest.fn()}
+ />
+ );
+ element.setState({
+ changelogs: [
+ {
+ creationDate: '2017-03-01T09:36:01+0100',
+ userName: 'john.doe',
+ avatar: 'gravatarhash',
+ diffs: [{ key: 'severity', newValue: 'MINOR', oldValue: 'CRITICAL' }]
+ }
+ ]
+ });
+ expect(element).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/CommentDeletePopup-test.js b/server/sonar-web/src/main/js/components/issue/popups/__tests__/CommentDeletePopup-test.js
new file mode 100644
index 00000000000..64a42433849
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/CommentDeletePopup-test.js
@@ -0,0 +1,31 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import React from 'react';
+import CommentDeletePopup from '../CommentDeletePopup';
+import { click } from '../../../../helpers/testUtils';
+
+it('should render the comment delete popup correctly', () => {
+ const onDelete = jest.fn();
+ const element = shallow(<CommentDeletePopup onDelete={onDelete} />);
+ expect(element).toMatchSnapshot();
+ click(element.find('button'));
+ expect(onDelete.mock.calls.length).toBe(1);
+});
diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/CommentPopup-test.js b/server/sonar-web/src/main/js/components/issue/popups/__tests__/CommentPopup-test.js
new file mode 100644
index 00000000000..2eac23a4e32
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/CommentPopup-test.js
@@ -0,0 +1,66 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import React from 'react';
+import CommentPopup from '../CommentPopup';
+import { click } from '../../../../helpers/testUtils';
+
+it('should render the comment popup correctly without existing comment', () => {
+ const element = shallow(
+ <CommentPopup
+ onComment={jest.fn()}
+ toggleComment={jest.fn()}
+ placeholder="placeholder test"
+ customClass="myclass"
+ />
+ );
+ expect(element).toMatchSnapshot();
+});
+
+it('should render the comment popup correctly when changing a comment', () => {
+ const element = shallow(
+ <CommentPopup
+ comment={{
+ markdown: '*test*'
+ }}
+ onComment={jest.fn()}
+ toggleComment={jest.fn()}
+ placeholder=""
+ />
+ );
+ expect(element).toMatchSnapshot();
+});
+
+it('should render not allow to send comment with only spaces', () => {
+ const onComment = jest.fn();
+ const element = shallow(
+ <CommentPopup
+ onComment={onComment}
+ toggleComment={jest.fn()}
+ placeholder="placeholder test"
+ customClass="myclass"
+ />
+ );
+ click(element.find('button.js-issue-comment-submit'));
+ expect(onComment.mock.calls.length).toBe(0);
+ element.setState({ textComment: 'mycomment' });
+ click(element.find('button.js-issue-comment-submit'));
+ expect(onComment.mock.calls.length).toBe(1);
+});
diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/SetIssueTagsPopup-test.js b/server/sonar-web/src/main/js/components/issue/popups/__tests__/SetIssueTagsPopup-test.js
new file mode 100644
index 00000000000..c9d1c0a4e23
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/SetIssueTagsPopup-test.js
@@ -0,0 +1,30 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import React from 'react';
+import SetIssueTagsPopup from '../SetIssueTagsPopup';
+
+it('should render tags popup correctly', () => {
+ const element = shallow(
+ <SetIssueTagsPopup onFail={jest.fn()} selectedTags="mytag" setTags={jest.fn()} />
+ );
+ element.setState({ searchResult: ['mytag', 'test', 'second'] });
+ expect(element).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/SetSeverityPopup-test.js b/server/sonar-web/src/main/js/components/issue/popups/__tests__/SetSeverityPopup-test.js
new file mode 100644
index 00000000000..ed0f67680b8
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/SetSeverityPopup-test.js
@@ -0,0 +1,27 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import React from 'react';
+import SetSeverityPopup from '../SetSeverityPopup';
+
+it('should render tags popup correctly', () => {
+ const element = shallow(<SetSeverityPopup issue={{ severity: 'MAJOR' }} onSelect={jest.fn()} />);
+ expect(element).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/SetTransitionPopup-test.js b/server/sonar-web/src/main/js/components/issue/popups/__tests__/SetTransitionPopup-test.js
new file mode 100644
index 00000000000..4d54d66b413
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/SetTransitionPopup-test.js
@@ -0,0 +1,32 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import React from 'react';
+import SetTransitionPopup from '../SetTransitionPopup';
+
+it('should render tags popup correctly', () => {
+ const element = shallow(
+ <SetTransitionPopup
+ onSelect={jest.fn()}
+ transitions={['confirm', 'resolve', 'falsepositive', 'wontfix']}
+ />
+ );
+ expect(element).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/SetTypePopup-test.js b/server/sonar-web/src/main/js/components/issue/popups/__tests__/SetTypePopup-test.js
new file mode 100644
index 00000000000..70b7f7a95a6
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/SetTypePopup-test.js
@@ -0,0 +1,27 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import { shallow } from 'enzyme';
+import React from 'react';
+import SetTypePopup from '../SetTypePopup';
+
+it('should render tags popup correctly', () => {
+ const element = shallow(<SetTypePopup issue={{ type: 'BUG' }} onSelect={jest.fn()} />);
+ expect(element).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/ChangelogPopup-test.js.snap b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/ChangelogPopup-test.js.snap
new file mode 100644
index 00000000000..aa5c866b59c
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/ChangelogPopup-test.js.snap
@@ -0,0 +1,50 @@
+exports[`test should render the changelog popup correctly 1`] = `
+<BubblePopup
+ customClass="bubble-popup-bottom-right">
+ <div
+ className="issue-changelog">
+ <table
+ className="spaced">
+ <tbody>
+ <tr>
+ <td
+ className="thin text-left text-top nowrap">
+ March 1, 2017 9:36 AM
+ </td>
+ <td
+ className="thin text-left text-top nowrap" />
+ <td
+ className="text-left text-top">
+ created_by john.david.dalton@gmail.com
+ </td>
+ </tr>
+ <tr>
+ <td
+ className="thin text-left text-top nowrap">
+ March 1, 2017 9:36 AM
+ </td>
+ <td
+ className="thin text-left text-top nowrap">
+ <Connect(Avatar)
+ className="little-spacer-right"
+ hash="gravatarhash"
+ size={16} />
+ john.doe
+ </td>
+ <td
+ className="text-left text-top">
+ <IssueChangelogDiff
+ diff={
+ Object {
+ "key": "severity",
+ "newValue": "MINOR",
+ "oldValue": "CRITICAL",
+ }
+ } />
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+</BubblePopup>
+`;
diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/CommentDeletePopup-test.js.snap b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/CommentDeletePopup-test.js.snap
new file mode 100644
index 00000000000..23e0864e519
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/CommentDeletePopup-test.js.snap
@@ -0,0 +1,17 @@
+exports[`test should render the comment delete popup correctly 1`] = `
+<BubblePopup
+ customClass="bubble-popup-bottom-right">
+ <div
+ className="text-right">
+ <div
+ className="spacer-bottom">
+ issue.comment.delete_confirm_message
+ </div>
+ <button
+ className="button-red"
+ onClick={[Function]}>
+ delete
+ </button>
+ </div>
+</BubblePopup>
+`;
diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/CommentPopup-test.js.snap b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/CommentPopup-test.js.snap
new file mode 100644
index 00000000000..b4ab112f14f
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/CommentPopup-test.js.snap
@@ -0,0 +1,75 @@
+exports[`test should render the comment popup correctly when changing a comment 1`] = `
+<BubblePopup
+ customClass="bubble-popup-bottom-right">
+ <div
+ className="issue-comment-form-text">
+ <textarea
+ autoFocus={true}
+ onChange={[Function]}
+ onKeyDown={[Function]}
+ placeholder=""
+ rows="2"
+ value="*test*" />
+ </div>
+ <div
+ className="issue-comment-form-footer">
+ <div
+ className="issue-comment-form-actions">
+ <button
+ className="js-issue-comment-submit little-spacer-right"
+ disabled={false}
+ onClick={[Function]}>
+ save
+ </button>
+ <a
+ className="js-issue-comment-cancel"
+ href="#"
+ onClick={[Function]}>
+ cancel
+ </a>
+ </div>
+ <div
+ className="issue-comment-form-tips">
+ <MarkdownTips />
+ </div>
+ </div>
+</BubblePopup>
+`;
+
+exports[`test should render the comment popup correctly without existing comment 1`] = `
+<BubblePopup
+ customClass="myclass bubble-popup-bottom-right">
+ <div
+ className="issue-comment-form-text">
+ <textarea
+ autoFocus={true}
+ onChange={[Function]}
+ onKeyDown={[Function]}
+ placeholder="placeholder test"
+ rows="2"
+ value="" />
+ </div>
+ <div
+ className="issue-comment-form-footer">
+ <div
+ className="issue-comment-form-actions">
+ <button
+ className="js-issue-comment-submit little-spacer-right"
+ disabled={true}
+ onClick={[Function]}>
+ issue.comment.submit
+ </button>
+ <a
+ className="js-issue-comment-cancel"
+ href="#"
+ onClick={[Function]}>
+ cancel
+ </a>
+ </div>
+ <div
+ className="issue-comment-form-tips">
+ <MarkdownTips />
+ </div>
+ </div>
+</BubblePopup>
+`;
diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetIssueTagsPopup-test.js.snap b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetIssueTagsPopup-test.js.snap
new file mode 100644
index 00000000000..91a5c0b4d6a
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetIssueTagsPopup-test.js.snap
@@ -0,0 +1,15 @@
+exports[`test should render tags popup correctly 1`] = `
+<TagsSelector
+ listSize={10}
+ onSearch={[Function]}
+ onSelect={[Function]}
+ onUnselect={[Function]}
+ selectedTags="mytag"
+ tags={
+ Array [
+ "mytag",
+ "test",
+ "second",
+ ]
+ } />
+`;
diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetSeverityPopup-test.js.snap b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetSeverityPopup-test.js.snap
new file mode 100644
index 00000000000..0c329d01adc
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetSeverityPopup-test.js.snap
@@ -0,0 +1,53 @@
+exports[`test should render tags popup correctly 1`] = `
+<BubblePopup
+ customClass="bubble-popup-menu bubble-popup-bottom">
+ <SelectList
+ currentItem="MAJOR"
+ items={
+ Array [
+ "BLOCKER",
+ "CRITICAL",
+ "MAJOR",
+ "MINOR",
+ "INFO",
+ ]
+ }
+ onSelect={[Function]}>
+ <SelectListItem
+ item="BLOCKER">
+ <SeverityIcon
+ className="little-spacer-right"
+ severity="BLOCKER" />
+ severity.BLOCKER
+ </SelectListItem>
+ <SelectListItem
+ item="CRITICAL">
+ <SeverityIcon
+ className="little-spacer-right"
+ severity="CRITICAL" />
+ severity.CRITICAL
+ </SelectListItem>
+ <SelectListItem
+ item="MAJOR">
+ <SeverityIcon
+ className="little-spacer-right"
+ severity="MAJOR" />
+ severity.MAJOR
+ </SelectListItem>
+ <SelectListItem
+ item="MINOR">
+ <SeverityIcon
+ className="little-spacer-right"
+ severity="MINOR" />
+ severity.MINOR
+ </SelectListItem>
+ <SelectListItem
+ item="INFO">
+ <SeverityIcon
+ className="little-spacer-right"
+ severity="INFO" />
+ severity.INFO
+ </SelectListItem>
+ </SelectList>
+</BubblePopup>
+`;
diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetTransitionPopup-test.js.snap b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetTransitionPopup-test.js.snap
new file mode 100644
index 00000000000..08072d269b9
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetTransitionPopup-test.js.snap
@@ -0,0 +1,37 @@
+exports[`test should render tags popup correctly 1`] = `
+<BubblePopup
+ customClass="bubble-popup-menu bubble-popup-bottom">
+ <SelectList
+ currentItem="confirm"
+ items={
+ Array [
+ "confirm",
+ "resolve",
+ "falsepositive",
+ "wontfix",
+ ]
+ }
+ onSelect={[Function]}>
+ <SelectListItem
+ item="confirm"
+ title="issue.transition.confirm.description">
+ issue.transition.confirm
+ </SelectListItem>
+ <SelectListItem
+ item="resolve"
+ title="issue.transition.resolve.description">
+ issue.transition.resolve
+ </SelectListItem>
+ <SelectListItem
+ item="falsepositive"
+ title="issue.transition.falsepositive.description">
+ issue.transition.falsepositive
+ </SelectListItem>
+ <SelectListItem
+ item="wontfix"
+ title="issue.transition.wontfix.description">
+ issue.transition.wontfix
+ </SelectListItem>
+ </SelectList>
+</BubblePopup>
+`;
diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetTypePopup-test.js.snap b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetTypePopup-test.js.snap
new file mode 100644
index 00000000000..a6719f89bf5
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetTypePopup-test.js.snap
@@ -0,0 +1,37 @@
+exports[`test should render tags popup correctly 1`] = `
+<BubblePopup
+ customClass="bubble-popup-menu bubble-popup-bottom">
+ <SelectList
+ currentItem="BUG"
+ items={
+ Array [
+ "BUG",
+ "VULNERABILITY",
+ "CODE_SMELL",
+ ]
+ }
+ onSelect={[Function]}>
+ <SelectListItem
+ item="BUG">
+ <IssueTypeIcon
+ className="little-spacer-right"
+ query="BUG" />
+ issue.type.BUG
+ </SelectListItem>
+ <SelectListItem
+ item="VULNERABILITY">
+ <IssueTypeIcon
+ className="little-spacer-right"
+ query="VULNERABILITY" />
+ issue.type.VULNERABILITY
+ </SelectListItem>
+ <SelectListItem
+ item="CODE_SMELL">
+ <IssueTypeIcon
+ className="little-spacer-right"
+ query="CODE_SMELL" />
+ issue.type.CODE_SMELL
+ </SelectListItem>
+ </SelectList>
+</BubblePopup>
+`;