diff options
author | Mathieu Suen <mathieu.suen@sonarsource.com> | 2020-02-18 14:26:04 +0100 |
---|---|---|
committer | SonarTech <sonartech@sonarsource.com> | 2020-02-21 20:46:20 +0100 |
commit | 5568a67a2899635e8498798118dea73daa3934f6 (patch) | |
tree | 64fed4f16be4c1d705c8a9b707e3be536e4e69b3 /server | |
parent | 9bc1ac6000521a9a64c08c69100000d1f49cd383 (diff) | |
download | sonarqube-5568a67a2899635e8498798118dea73daa3934f6.tar.gz sonarqube-5568a67a2899635e8498798118dea73daa3934f6.zip |
SONAR-13059 Add delete button on comment.
Diffstat (limited to 'server')
10 files changed, 117 insertions, 26 deletions
diff --git a/server/sonar-web/src/main/js/api/security-hotspots.ts b/server/sonar-web/src/main/js/api/security-hotspots.ts index 0a54022b6db..d9a1213caed 100644 --- a/server/sonar-web/src/main/js/api/security-hotspots.ts +++ b/server/sonar-web/src/main/js/api/security-hotspots.ts @@ -51,6 +51,10 @@ export function commentSecurityHotspot(hotspotKey: string, comment: string): Pro ); } +export function deleteCommentSecurityHotspot(comment: string): Promise<void> { + return post('/api/hotspots/delete_comment', { comment }).catch(throwGlobalError); +} + export function getSecurityHotspots( data: { projectKey: string; diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotReviewHistory.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotReviewHistory.tsx index 1b94b1a590e..154db9dc547 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotReviewHistory.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotReviewHistory.tsx @@ -20,6 +20,9 @@ import * as classNames from 'classnames'; import { sanitize } from 'dompurify'; import * as React from 'react'; +import { Button, DeleteButton } from 'sonar-ui-common/components/controls/buttons'; +import Dropdown from 'sonar-ui-common/components/controls/Dropdown'; +import { PopupPlacement } from 'sonar-ui-common/components/ui/popups'; import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n'; import DateTimeFormatter from '../../../components/intl/DateTimeFormatter'; import IssueChangelogDiff from '../../../components/issue/components/IssueChangelogDiff'; @@ -29,15 +32,17 @@ import { getHotspotReviewHistory } from '../utils'; export interface HotspotReviewHistoryProps { hotspot: Hotspot; + onDeleteComment: (key: string) => void; } export default function HotspotReviewHistory(props: HotspotReviewHistoryProps) { - const reviewHistory = getHotspotReviewHistory(props.hotspot); + const { hotspot } = props; + const reviewHistory = getHotspotReviewHistory(hotspot); return ( <> {reviewHistory.map((historyElt, historyIndex) => { - const { user, type, diffs, date, html } = historyElt; + const { user, type, diffs, date, html, key, updatable } = historyElt; return ( <div className={classNames('padded', { 'bordered-top': historyIndex > 0 })} @@ -78,11 +83,28 @@ export default function HotspotReviewHistory(props: HotspotReviewHistoryProps) { </div> )} - {type === ReviewHistoryType.Comment && html && ( - <div - className="spacer-top markdown" - dangerouslySetInnerHTML={{ __html: sanitize(html) }} - /> + {type === ReviewHistoryType.Comment && key && html && ( + <div className="spacer-top display-flex-space-between"> + <div className="markdown" dangerouslySetInnerHTML={{ __html: sanitize(html) }} /> + {updatable && ( + <div className="dropdown"> + <Dropdown + overlay={ + <div className="padded abs-width-150"> + <p>{translate('issue.comment.delete_confirm_message')}</p> + <Button + className="button-red big-spacer-top pull-right" + onClick={() => props.onDeleteComment(key)}> + {translate('delete')} + </Button> + </div> + } + overlayPlacement={PopupPlacement.BottomRight}> + <DeleteButton className="it__hotspots-comment-delete button-small" /> + </Dropdown> + </div> + )} + </div> )} </div> ); diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotReviewHistoryAndComments.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotReviewHistoryAndComments.tsx index c7d73b89a6b..a2adc0af84e 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotReviewHistoryAndComments.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotReviewHistoryAndComments.tsx @@ -21,7 +21,10 @@ import * as classNames from 'classnames'; import * as React from 'react'; import { Button, ResetButtonLink } from 'sonar-ui-common/components/controls/buttons'; import { translate } from 'sonar-ui-common/helpers/l10n'; -import { commentSecurityHotspot } from '../../../api/security-hotspots'; +import { + commentSecurityHotspot, + deleteCommentSecurityHotspot +} from '../../../api/security-hotspots'; import MarkdownTips from '../../../components/common/MarkdownTips'; import { isLoggedIn } from '../../../helpers/users'; import { Hotspot } from '../../../types/security-hotspots'; @@ -74,6 +77,12 @@ export default class HotspotReviewHistoryAndComments extends React.PureComponent }); }; + handleDeleteComment = (key: string) => { + return deleteCommentSecurityHotspot(key).then(() => { + this.props.onCommentUpdate(); + }); + }; + render() { const { currentUser, hotspot, commentTextRef, commentVisible } = this.props; const { comment } = this.state; @@ -81,7 +90,7 @@ export default class HotspotReviewHistoryAndComments extends React.PureComponent <> <h1>{translate('hotspot.section.activity')}</h1> <div className="padded"> - <HotspotReviewHistory hotspot={hotspot} /> + <HotspotReviewHistory hotspot={hotspot} onDeleteComment={this.handleDeleteComment} /> {isLoggedIn(currentUser) && ( <> diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotReviewHistory-test.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotReviewHistory-test.tsx index 1488c45d1c9..4dfcf73f49d 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotReviewHistory-test.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotReviewHistory-test.tsx @@ -56,7 +56,7 @@ function shallowRender(props?: Partial<HotspotReviewHistoryProps>) { createdAt: '2018-09-11', htmlText: '<strong>TEST</strong>', markdown: '*TEST*', - updatable: true, + updatable: false, login: 'dude-2', user: mockUser({ login: 'dude-2' }) }; @@ -65,5 +65,5 @@ function shallowRender(props?: Partial<HotspotReviewHistoryProps>) { changelog: [changelogElement], comment: [commentElement, commentElement1] }); - return shallow(<HotspotReviewHistory hotspot={hotspot} {...props} />); + return shallow(<HotspotReviewHistory hotspot={hotspot} onDeleteComment={jest.fn()} {...props} />); } diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotReviewHistoryAndComments-test.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotReviewHistoryAndComments-test.tsx index f1d78b6c18a..e9f2b17b913 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotReviewHistoryAndComments-test.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotReviewHistoryAndComments-test.tsx @@ -20,14 +20,19 @@ import { shallow } from 'enzyme'; import * as React from 'react'; import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; -import { commentSecurityHotspot } from '../../../../api/security-hotspots'; +import { + commentSecurityHotspot, + deleteCommentSecurityHotspot +} from '../../../../api/security-hotspots'; import { mockHotspot } from '../../../../helpers/mocks/security-hotspots'; import { mockCurrentUser } from '../../../../helpers/testMocks'; import { isLoggedIn } from '../../../../helpers/users'; +import HotspotReviewHistory from '../HotspotReviewHistory'; import HotspotReviewHistoryAndComments from '../HotspotReviewHistoryAndComments'; jest.mock('../../../../api/security-hotspots', () => ({ - commentSecurityHotspot: jest.fn().mockResolvedValue({}) + commentSecurityHotspot: jest.fn().mockResolvedValue({}), + deleteCommentSecurityHotspot: jest.fn().mockResolvedValue({}) })); jest.mock('../../../../helpers/users', () => ({ isLoggedIn: jest.fn(() => true) })); @@ -95,6 +100,12 @@ it('should reset on change hotspot', () => { expect(wrapper.state().comment).toBe(''); }); +it('should delete comment', () => { + const wrapper = shallowRender(); + wrapper.find(HotspotReviewHistory).simulate('deleteComment', 'me1'); + expect(deleteCommentSecurityHotspot).toHaveBeenCalledWith('me1'); +}); + function shallowRender(props?: Partial<HotspotReviewHistoryAndComments['props']>) { return shallow<HotspotReviewHistoryAndComments>( <HotspotReviewHistoryAndComments diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotReviewHistory-test.tsx.snap b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotReviewHistory-test.tsx.snap index 395669fefb7..f1ebd04f7c4 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotReviewHistory-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotReviewHistory-test.tsx.snap @@ -62,13 +62,43 @@ exports[`should render correctly 1`] = ` /> </div> <div - className="spacer-top markdown" - dangerouslySetInnerHTML={ - Object { - "__html": "<strong>TEST</strong>", + className="spacer-top display-flex-space-between" + > + <div + className="markdown" + dangerouslySetInnerHTML={ + Object { + "__html": "<strong>TEST</strong>", + } } - } - /> + /> + <div + className="dropdown" + > + <Dropdown + overlay={ + <div + className="padded abs-width-150" + > + <p> + issue.comment.delete_confirm_message + </p> + <Button + className="button-red big-spacer-top pull-right" + onClick={[Function]} + > + delete + </Button> + </div> + } + overlayPlacement="bottom-right" + > + <DeleteButton + className="js-issue-comment-delete button-small" + /> + </Dropdown> + </div> + </div> </div> <div className="padded bordered-top" @@ -100,13 +130,17 @@ exports[`should render correctly 1`] = ` /> </div> <div - className="spacer-top markdown" - dangerouslySetInnerHTML={ - Object { - "__html": "<strong>TEST</strong>", + className="spacer-top display-flex-space-between" + > + <div + className="markdown" + dangerouslySetInnerHTML={ + Object { + "__html": "<strong>TEST</strong>", + } } - } - /> + /> + </div> </div> <div className="padded bordered-top" diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotReviewHistoryAndComments-test.tsx.snap b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotReviewHistoryAndComments-test.tsx.snap index e71ea8d94c3..787a71d7458 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotReviewHistoryAndComments-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotReviewHistoryAndComments-test.tsx.snap @@ -108,6 +108,7 @@ exports[`should render correctly 1`] = ` ], } } + onDeleteComment={[Function]} /> <hr /> <div @@ -271,6 +272,7 @@ exports[`should render correctly without user 1`] = ` ], } } + onDeleteComment={[Function]} /> </div> </Fragment> diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/styles.css b/server/sonar-web/src/main/js/apps/security-hotspots/styles.css index 69d689e6df0..f3fb14651d6 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/styles.css +++ b/server/sonar-web/src/main/js/apps/security-hotspots/styles.css @@ -62,3 +62,8 @@ .invisible { visibility: hidden; } + +#security_hotspots .review-comment { + display: flex; + justify-content: space-between; +} diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/utils.ts b/server/sonar-web/src/main/js/apps/security-hotspots/utils.ts index 06c3898631b..81f1c6a6cba 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/utils.ts +++ b/server/sonar-web/src/main/js/apps/security-hotspots/utils.ts @@ -115,11 +115,13 @@ export function getHotspotReviewHistory(hotspot: Hotspot): ReviewHistoryElement[ ...hotspot.comment.map(comment => ({ type: ReviewHistoryType.Comment, date: comment.createdAt, + updatable: comment.updatable, user: { ...comment.user, name: comment.user.name || comment.user.login }, - html: comment.htmlText + html: comment.htmlText, + key: comment.key })) ); } diff --git a/server/sonar-web/src/main/js/types/security-hotspots.ts b/server/sonar-web/src/main/js/types/security-hotspots.ts index 6ab8963e914..ad9179545e9 100644 --- a/server/sonar-web/src/main/js/types/security-hotspots.ts +++ b/server/sonar-web/src/main/js/types/security-hotspots.ts @@ -126,6 +126,8 @@ export interface ReviewHistoryElement { user: Pick<T.UserBase, 'active' | 'avatar' | 'name'>; diffs?: T.IssueChangelogDiff[]; html?: string; + key?: string; + updatable?: boolean; } export enum ReviewHistoryType { |