diff options
author | Stas Vilchik <stas.vilchik@sonarsource.com> | 2018-05-09 09:17:16 +0200 |
---|---|---|
committer | SonarTech <sonartech@sonarsource.com> | 2018-05-09 20:20:46 +0200 |
commit | 09b3d167fa8f399e18a37d56e7c8cbb61f68f97f (patch) | |
tree | 415072b29720bdd0c5293a898eb4ed10b807859e /server/sonar-web/src/main/js/components/issue | |
parent | 302775229e9cc6debd58804446cb98c2ea563bd4 (diff) | |
download | sonarqube-09b3d167fa8f399e18a37d56e7c8cbb61f68f97f.tar.gz sonarqube-09b3d167fa8f399e18a37d56e7c8cbb61f68f97f.zip |
SONAR-10664 Improve dropdown UI/UX consistency (#217)
Diffstat (limited to 'server/sonar-web/src/main/js/components/issue')
48 files changed, 1017 insertions, 1029 deletions
diff --git a/server/sonar-web/src/main/js/components/issue/IssueView.js b/server/sonar-web/src/main/js/components/issue/IssueView.js index 5f814a5347e..0598c4f9f07 100644 --- a/server/sonar-web/src/main/js/components/issue/IssueView.js +++ b/server/sonar-web/src/main/js/components/issue/IssueView.js @@ -51,15 +51,14 @@ export default class IssueView extends React.PureComponent { handleCheck = (event /*: Event */) => { event.preventDefault(); - event.stopPropagation(); if (this.props.onCheck) { this.props.onCheck(this.props.issue.key, event); } }; handleClick = (event /*: Event & { target: HTMLElement } */) => { - event.preventDefault(); - if (this.props.onClick) { + if (!isClickable(event.target) && this.props.onClick) { + event.preventDefault(); this.props.onClick(this.props.issue.key); } }; @@ -100,12 +99,12 @@ export default class IssueView extends React.PureComponent { togglePopup={this.props.togglePopup} /> <IssueActionsBar - issue={issue} currentPopup={this.props.currentPopup} + issue={issue} onAssign={this.props.onAssign} + onChange={this.props.onChange} onFail={this.props.onFail} togglePopup={this.props.togglePopup} - onChange={this.props.onChange} /> {issue.comments && issue.comments.length > 0 && ( @@ -114,8 +113,8 @@ export default class IssueView extends React.PureComponent { <IssueCommentLine comment={comment} key={comment.key} - onEdit={this.editComment} onDelete={this.deleteComment} + onEdit={this.editComment} /> ))} </div> @@ -137,3 +136,12 @@ export default class IssueView extends React.PureComponent { ); } } + +function isClickable(node /*: any */) { + if (!node) { + return false; + } + const clickableTags = ['A', 'BUTTON', 'INPUT', 'TEXTAREA']; + const tagName = (node.tagName || '').toUpperCase(); + return clickableTags.includes(tagName) || isClickable(node.parentNode); +} diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueAssign.js b/server/sonar-web/src/main/js/components/issue/components/IssueAssign.js index 3f7d5e84ac7..783fdec65b0 100644 --- a/server/sonar-web/src/main/js/components/issue/components/IssueAssign.js +++ b/server/sonar-web/src/main/js/components/issue/components/IssueAssign.js @@ -19,9 +19,11 @@ */ // @flow import React from 'react'; -import Avatar from '../../../components/ui/Avatar'; -import BubblePopupHelper from '../../../components/common/BubblePopupHelper'; import SetAssigneePopup from '../popups/SetAssigneePopup'; +import Avatar from '../../../components/ui/Avatar'; +import Toggler from '../../../components/controls/Toggler'; +import DropdownIcon from '../../../components/icons-components/DropdownIcon'; +import { Button } from '../../../components/ui/buttons'; import { translate } from '../../../helpers/l10n'; /*:: import type { Issue } from '../types'; */ @@ -43,6 +45,10 @@ export default class IssueAssign extends React.PureComponent { this.props.togglePopup('assign', open); }; + handleClose = () => { + this.toggleAssign(false); + }; + renderAssignee() { const { issue } = this.props; return ( @@ -67,24 +73,26 @@ export default class IssueAssign extends React.PureComponent { render() { if (this.props.canAssign) { return ( - <BubblePopupHelper - isOpen={this.props.isOpen && this.props.canAssign} - position="bottomleft" - togglePopup={this.toggleAssign} - popup={ - <SetAssigneePopup - issue={this.props.issue} - onFail={this.props.onFail} - onSelect={this.props.onAssign} - /> - }> - <button - className="button-link issue-action issue-action-with-options js-issue-assign" - onClick={this.toggleAssign}> - {this.renderAssignee()} - <i className="little-spacer-left icon-dropdown" /> - </button> - </BubblePopupHelper> + <div className="dropdown"> + <Toggler + closeOnEscape={true} + onRequestClose={this.handleClose} + open={this.props.isOpen && this.props.canAssign} + overlay={ + <SetAssigneePopup + issue={this.props.issue} + onFail={this.props.onFail} + onSelect={this.props.onAssign} + /> + }> + <Button + className="button-link issue-action issue-action-with-options js-issue-assign" + onClick={this.toggleAssign}> + {this.renderAssignee()} + <DropdownIcon className="little-spacer-left" /> + </Button> + </Toggler> + </div> ); } else { return this.renderAssignee(); diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueChangelog.js b/server/sonar-web/src/main/js/components/issue/components/IssueChangelog.js index 43f5adc2d91..a70f6ca5a33 100644 --- a/server/sonar-web/src/main/js/components/issue/components/IssueChangelog.js +++ b/server/sonar-web/src/main/js/components/issue/components/IssueChangelog.js @@ -19,11 +19,12 @@ */ // @flow import React from 'react'; -import BubblePopupHelper from '../../../components/common/BubblePopupHelper'; import ChangelogPopup from '../popups/ChangelogPopup'; import DateFromNow from '../../../components/intl/DateFromNow'; import DateTimeFormatter from '../../../components/intl/DateTimeFormatter'; +import Toggler from '../../../components/controls/Toggler'; import Tooltip from '../../../components/controls/Tooltip'; +import { Button } from '../../../components/ui/buttons'; /*:: import type { Issue } from '../types'; */ /*:: @@ -39,35 +40,39 @@ type Props = { export default class IssueChangelog extends React.PureComponent { /*:: props: Props; */ - handleClick = (evt /*: SyntheticInputEvent */) => { - evt.preventDefault(); + toggleChangelog = (open /*: boolean | void */) => { + this.props.togglePopup('changelog', open); + }; + + handleClick = () => { this.toggleChangelog(); }; - toggleChangelog = (open /*: boolean | void */) => { - this.props.togglePopup('changelog', open); + handleClose = () => { + this.toggleChangelog(false); }; render() { return ( - <BubblePopupHelper - isOpen={this.props.isOpen} - position="bottomright" - togglePopup={this.toggleChangelog} - popup={<ChangelogPopup issue={this.props.issue} onFail={this.props.onFail} />}> - <Tooltip - mouseEnterDelay={0.5} - overlay={<DateTimeFormatter date={this.props.creationDate} />}> - <button - className="button-link issue-action issue-action-with-options js-issue-show-changelog" - onClick={this.handleClick}> - <span className="issue-meta-label"> - <DateFromNow date={this.props.creationDate} /> - </span> - <i className="icon-dropdown little-spacer-left" /> - </button> - </Tooltip> - </BubblePopupHelper> + <div className="dropdown"> + <Toggler + onRequestClose={this.handleClose} + open={this.props.isOpen} + overlay={<ChangelogPopup issue={this.props.issue} onFail={this.props.onFail} />}> + <Tooltip + mouseEnterDelay={0.5} + overlay={<DateTimeFormatter date={this.props.creationDate} />}> + <Button + className="button-link issue-action issue-action-with-options js-issue-show-changelog" + onClick={this.handleClick}> + <span className="issue-meta-label"> + <DateFromNow date={this.props.creationDate} /> + </span> + <i className="icon-dropdown little-spacer-left" /> + </Button> + </Tooltip> + </Toggler> + </div> ); } } diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueCommentAction.js b/server/sonar-web/src/main/js/components/issue/components/IssueCommentAction.js index 01ba1f984a1..814d4053cf4 100644 --- a/server/sonar-web/src/main/js/components/issue/components/IssueCommentAction.js +++ b/server/sonar-web/src/main/js/components/issue/components/IssueCommentAction.js @@ -20,7 +20,8 @@ // @flow import React from 'react'; import { updateIssue } from '../actions'; -import BubblePopupHelper from '../../../components/common/BubblePopupHelper'; +import Toggler from '../../../components/controls/Toggler'; +import { Button } from '../../../components/ui/buttons'; import CommentPopup from '../popups/CommentPopup'; import { addIssueComment } from '../../../api/issues'; import { translate } from '../../../helpers/l10n'; @@ -49,29 +50,33 @@ export default class IssueCommentAction extends React.PureComponent { this.props.toggleComment(false); }; - handleCommentClick = () => this.props.toggleComment(); + handleCommentClick = () => { + this.props.toggleComment(); + }; + + handleClose = () => { + this.props.toggleComment(false); + }; render() { return ( - <li className="issue-meta"> - <BubblePopupHelper - isOpen={this.props.currentPopup === 'comment'} - position="bottomleft" - togglePopup={this.props.toggleComment} - popup={ + <li className="issue-meta dropdown"> + <Toggler + onRequestClose={this.handleClose} + open={this.props.currentPopup === 'comment'} + overlay={ <CommentPopup - customClass="issue-comment-bubble-popup" - placeholder={this.props.commentPlaceholder} onComment={this.addComment} + placeholder={this.props.commentPlaceholder} toggleComment={this.props.toggleComment} /> }> - <button + <Button className="button-link issue-action js-issue-comment" onClick={this.handleCommentClick}> <span className="issue-meta-label">{translate('issue.comment.formlink')}</span> - </button> - </BubblePopupHelper> + </Button> + </Toggler> </li> ); } diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueCommentLine.js b/server/sonar-web/src/main/js/components/issue/components/IssueCommentLine.js index 1773bd16a0c..3a0910f6552 100644 --- a/server/sonar-web/src/main/js/components/issue/components/IssueCommentLine.js +++ b/server/sonar-web/src/main/js/components/issue/components/IssueCommentLine.js @@ -20,7 +20,7 @@ // @flow import React from 'react'; import Avatar from '../../../components/ui/Avatar'; -import BubblePopupHelper from '../../../components/common/BubblePopupHelper'; +import Toggler from '../../../components/controls/Toggler'; import EditIcon from '../../../components/icons-components/EditIcon'; import { EditButton, DeleteButton } from '../../../components/ui/buttons'; import CommentDeletePopup from '../popups/CommentDeletePopup'; @@ -48,12 +48,6 @@ export default class IssueCommentLine extends React.PureComponent { openPopup: '' }; - handleCommentClick = (event /*: Event & {target: HTMLElement}*/) => { - if (event.target.tagName === 'A') { - event.stopPropagation(); - } - }; - handleEdit = (text /*: string */) => { this.props.onEdit(this.props.comment.key, text); this.toggleEditPopup(false); @@ -75,9 +69,17 @@ export default class IssueCommentLine extends React.PureComponent { }); }; - toggleDeletePopup = (force /*: ?boolean */) => this.togglePopup('delete', force); + toggleDeletePopup = (force /*: ?boolean */) => { + this.togglePopup('delete', force); + }; - toggleEditPopup = (force /*: ?boolean */) => this.togglePopup('edit', force); + toggleEditPopup = (force /*: ?boolean */) => { + this.togglePopup('edit', force); + }; + + closePopups = () => { + this.setState({ openPopup: '' }); + }; render() { const { comment } = this.props; @@ -95,49 +97,45 @@ export default class IssueCommentLine extends React.PureComponent { <div className="issue-comment-text markdown" dangerouslySetInnerHTML={{ __html: comment.htmlText }} - onClick={this.handleCommentClick} - role="Listitem" - tabIndex={0} /> <div className="issue-comment-age"> <DateFromNow date={comment.createdAt} /> </div> <div className="issue-comment-actions"> {comment.updatable && ( - <BubblePopupHelper - className="bubble-popup-helper-inline" - isOpen={this.state.openPopup === 'edit'} - offset={{ vertical: 0, horizontal: -6 }} - position="bottomright" - togglePopup={this.toggleDeletePopup} - popup={ - <CommentPopup - comment={comment} - customClass="issue-edit-comment-bubble-popup" - onComment={this.handleEdit} - placeholder="" - toggleComment={this.toggleEditPopup} + <div className="dropdown"> + <Toggler + className="display-inline-block" + onRequestClose={this.closePopups} + open={this.state.openPopup === 'edit'} + overlay={ + <CommentPopup + comment={comment} + onComment={this.handleEdit} + placeholder="" + toggleComment={this.toggleEditPopup} + /> + }> + <EditButton + className="js-issue-comment-edit button-small" + onClick={this.toggleEditPopup} /> - }> - <EditButton - className="js-issue-comment-edit button-small" - onClick={this.toggleEditPopup} - /> - </BubblePopupHelper> + </Toggler> + </div> )} {comment.updatable && ( - <BubblePopupHelper - className="bubble-popup-helper-inline" - isOpen={this.state.openPopup === 'delete'} - offset={{ vertical: 0, horizontal: -10 }} - position="bottomright" - togglePopup={this.toggleDeletePopup} - popup={<CommentDeletePopup onDelete={this.handleDelete} />}> - <DeleteButton - className="js-issue-comment-delete button-small" - onClick={this.toggleDeletePopup} - /> - </BubblePopupHelper> + <div className="dropdown"> + <Toggler + className="display-inline-block" + onRequestClose={this.closePopups} + open={this.state.openPopup === 'delete'} + overlay={<CommentDeletePopup onDelete={this.handleDelete} />}> + <DeleteButton + className="js-issue-comment-delete button-small" + onClick={this.toggleDeletePopup} + /> + </Toggler> + </div> )} </div> </div> diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueMessage.js b/server/sonar-web/src/main/js/components/issue/components/IssueMessage.js index 888d7f2d521..1f8a9cb2ae5 100644 --- a/server/sonar-web/src/main/js/components/issue/components/IssueMessage.js +++ b/server/sonar-web/src/main/js/components/issue/components/IssueMessage.js @@ -38,7 +38,6 @@ export default class IssueMessage extends React.PureComponent { handleClick = (e /*: MouseEvent */) => { e.preventDefault(); - e.stopPropagation(); this.context.workspace.openRule({ key: this.props.rule, organization: this.props.organization diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueSeverity.js b/server/sonar-web/src/main/js/components/issue/components/IssueSeverity.js index 326e64e64ab..f75e0e8f3c7 100644 --- a/server/sonar-web/src/main/js/components/issue/components/IssueSeverity.js +++ b/server/sonar-web/src/main/js/components/issue/components/IssueSeverity.js @@ -19,10 +19,12 @@ */ // @flow import React from 'react'; -import BubblePopupHelper from '../../../components/common/BubblePopupHelper'; import SetSeverityPopup from '../popups/SetSeverityPopup'; -import SeverityHelper from '../../../components/shared/SeverityHelper'; import { setIssueSeverity } from '../../../api/issues'; +import Toggler from '../../../components/controls/Toggler'; +import DropdownIcon from '../../../components/icons-components/DropdownIcon'; +import SeverityHelper from '../../../components/shared/SeverityHelper'; +import { Button } from '../../../components/ui/buttons'; /*:: import type { Issue } from '../types'; */ /*:: @@ -42,28 +44,31 @@ export default class IssueSeverity extends React.PureComponent { this.props.togglePopup('set-severity', open); }; - setSeverity = (severity /*: string */) => + setSeverity = (severity /*: string */) => { this.props.setIssueProperty('severity', 'set-severity', setIssueSeverity, severity); + }; + + handleClose = () => { + this.toggleSetSeverity(false); + }; render() { const { issue } = this.props; if (this.props.canSetSeverity) { return ( - <BubblePopupHelper - isOpen={this.props.isOpen && this.props.canSetSeverity} - position="bottomleft" - togglePopup={this.toggleSetSeverity} - popup={<SetSeverityPopup issue={issue} onSelect={this.setSeverity} />}> - <button - className="button-link issue-action issue-action-with-options js-issue-set-severity" - onClick={this.toggleSetSeverity}> - <SeverityHelper - className="issue-meta-label little-spacer-right" - severity={issue.severity} - /> - <i className="little-spacer-left icon-dropdown" /> - </button> - </BubblePopupHelper> + <div className="dropdown"> + <Toggler + onRequestClose={this.handleClose} + open={this.props.isOpen && this.props.canSetSeverity} + overlay={<SetSeverityPopup issue={issue} onSelect={this.setSeverity} />}> + <Button + className="button-link issue-action issue-action-with-options js-issue-set-severity" + onClick={this.toggleSetSeverity}> + <SeverityHelper className="issue-meta-label" severity={issue.severity} /> + <DropdownIcon className="little-spacer-left" /> + </Button> + </Toggler> + </div> ); } else { return <SeverityHelper className="issue-meta-label" severity={issue.severity} />; diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueTags.js b/server/sonar-web/src/main/js/components/issue/components/IssueTags.js index fb183a7f8b7..b9f1fcba0be 100644 --- a/server/sonar-web/src/main/js/components/issue/components/IssueTags.js +++ b/server/sonar-web/src/main/js/components/issue/components/IssueTags.js @@ -20,10 +20,11 @@ // @flow import React from 'react'; import { updateIssue } from '../actions'; -import BubblePopupHelper from '../../../components/common/BubblePopupHelper'; import SetIssueTagsPopup from '../popups/SetIssueTagsPopup'; -import TagsList from '../../../components/tags/TagsList'; import { setIssueTags } from '../../../api/issues'; +import Toggler from '../../../components/controls/Toggler'; +import TagsList from '../../../components/tags/TagsList'; +import { Button } from '../../../components/ui/buttons'; import { translate } from '../../../helpers/l10n'; /*:: import type { Issue } from '../types'; */ @@ -57,32 +58,39 @@ export default class IssueTags extends React.PureComponent { ); }; + handleClose = () => { + this.toggleSetTags(false); + }; + render() { const { issue } = this.props; const { tags = [] } = issue; if (this.props.canSetTags) { return ( - <BubblePopupHelper - isOpen={this.props.isOpen} - popup={ - <SetIssueTagsPopup - organization={issue.projectOrganization} - selectedTags={tags} - setTags={this.setTags} - /> - } - position="bottomright" - togglePopup={this.toggleSetTags}> - <button - className={'js-issue-edit-tags button-link issue-action issue-action-with-options'} - onClick={this.toggleSetTags}> - <TagsList - allowUpdate={this.props.canSetTags} - tags={issue.tags && issue.tags.length > 0 ? issue.tags : [translate('issue.no_tag')]} - /> - </button> - </BubblePopupHelper> + <div className="dropdown"> + <Toggler + onRequestClose={this.handleClose} + open={this.props.isOpen} + overlay={ + <SetIssueTagsPopup + organization={issue.projectOrganization} + selectedTags={tags} + setTags={this.setTags} + /> + }> + <Button + className={'js-issue-edit-tags button-link issue-action issue-action-with-options'} + onClick={this.toggleSetTags}> + <TagsList + allowUpdate={this.props.canSetTags} + tags={ + issue.tags && issue.tags.length > 0 ? issue.tags : [translate('issue.no_tag')] + } + /> + </Button> + </Toggler> + </div> ); } else { return ( diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueTitleBar.js b/server/sonar-web/src/main/js/components/issue/components/IssueTitleBar.js index 0dad342b1b5..781eebd1f06 100644 --- a/server/sonar-web/src/main/js/components/issue/components/IssueTitleBar.js +++ b/server/sonar-web/src/main/js/components/issue/components/IssueTitleBar.js @@ -45,8 +45,6 @@ type Props = {| |}; */ -const stopPropagation = (event /*: Event */) => event.stopPropagation(); - export default function IssueTitleBar(props /*: Props */) { const { issue } = props; const hasSimilarIssuesFilter = props.onFilter != null; @@ -103,7 +101,7 @@ export default function IssueTitleBar(props /*: Props */) { {displayLocations && ( <li className="issue-meta"> {props.displayLocationsLink ? ( - <Link onClick={stopPropagation} target="_blank" to={issueUrl}> + <Link target="_blank" to={issueUrl}> {locationsBadge} </Link> ) : ( @@ -112,11 +110,7 @@ export default function IssueTitleBar(props /*: Props */) { </li> )} <li className="issue-meta"> - <Link - className="js-issue-permalink link-no-underline" - onClick={stopPropagation} - target="_blank" - to={issueUrl}> + <Link className="js-issue-permalink link-no-underline" target="_blank" to={issueUrl}> <LinkIcon /> </Link> </li> diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueTransition.js b/server/sonar-web/src/main/js/components/issue/components/IssueTransition.js index 28379cde1cd..3aced6f8685 100644 --- a/server/sonar-web/src/main/js/components/issue/components/IssueTransition.js +++ b/server/sonar-web/src/main/js/components/issue/components/IssueTransition.js @@ -20,10 +20,12 @@ // @flow import React from 'react'; import { updateIssue } from '../actions'; -import BubblePopupHelper from '../../../components/common/BubblePopupHelper'; import SetTransitionPopup from '../popups/SetTransitionPopup'; -import StatusHelper from '../../../components/shared/StatusHelper'; import { setIssueTransition } from '../../../api/issues'; +import Toggler from '../../../components/controls/Toggler'; +import DropdownIcon from '../../../components/icons-components/DropdownIcon'; +import StatusHelper from '../../../components/shared/StatusHelper'; +import { Button } from '../../../components/ui/buttons'; /*:: import type { Issue } from '../types'; */ /*:: @@ -53,36 +55,41 @@ export default class IssueTransition extends React.PureComponent { this.props.togglePopup('transition', open); }; + handleClose = () => { + this.toggleSetTransition(false); + }; + render() { const { issue } = this.props; if (this.props.hasTransitions) { return ( - <BubblePopupHelper - isOpen={this.props.isOpen && this.props.hasTransitions} - position="bottomleft" - togglePopup={this.toggleSetTransition} - popup={ - <SetTransitionPopup transitions={issue.transitions} onSelect={this.setTransition} /> - }> - <button - className="button-link issue-action issue-action-with-options js-issue-transition" - onClick={this.toggleSetTransition}> - <StatusHelper - className="issue-meta-label little-spacer-right" - status={issue.status} - resolution={issue.resolution} - /> - <i className="little-spacer-left icon-dropdown" /> - </button> - </BubblePopupHelper> + <div className="dropdown"> + <Toggler + onRequestClose={this.handleClose} + open={this.props.isOpen && this.props.hasTransitions} + overlay={ + <SetTransitionPopup onSelect={this.setTransition} transitions={issue.transitions} /> + }> + <Button + className="button-link issue-action issue-action-with-options js-issue-transition" + onClick={this.toggleSetTransition}> + <StatusHelper + className="issue-meta-label" + resolution={issue.resolution} + status={issue.status} + /> + <DropdownIcon className="little-spacer-left" /> + </Button> + </Toggler> + </div> ); } else { return ( <StatusHelper className="issue-meta-label" - status={issue.status} resolution={issue.resolution} + status={issue.status} /> ); } diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueType.js b/server/sonar-web/src/main/js/components/issue/components/IssueType.js index 60941288e4b..380e8668a32 100644 --- a/server/sonar-web/src/main/js/components/issue/components/IssueType.js +++ b/server/sonar-web/src/main/js/components/issue/components/IssueType.js @@ -19,10 +19,12 @@ */ // @flow import React from 'react'; -import BubblePopupHelper from '../../../components/common/BubblePopupHelper'; -import IssueTypeIcon from '../../../components/ui/IssueTypeIcon'; import SetTypePopup from '../popups/SetTypePopup'; import { setIssueType } from '../../../api/issues'; +import Toggler from '../../../components/controls/Toggler'; +import DropdownIcon from '../../../components/icons-components/DropdownIcon'; +import { Button } from '../../../components/ui/buttons'; +import IssueTypeIcon from '../../../components/ui/IssueTypeIcon'; import { translate } from '../../../helpers/l10n'; /*:: import type { Issue } from '../types'; */ @@ -43,26 +45,32 @@ export default class IssueType extends React.PureComponent { this.props.togglePopup('set-type', open); }; - setType = (type /*: string */) => + setType = (type /*: string */) => { this.props.setIssueProperty('type', 'set-type', setIssueType, type); + }; + + handleClose = () => { + this.toggleSetType(false); + }; render() { const { issue } = this.props; if (this.props.canSetSeverity) { return ( - <BubblePopupHelper - isOpen={this.props.isOpen && this.props.canSetSeverity} - position="bottomleft" - togglePopup={this.toggleSetType} - popup={<SetTypePopup issue={issue} onSelect={this.setType} />}> - <button - className="button-link issue-action issue-action-with-options js-issue-set-type" - onClick={this.toggleSetType}> - <IssueTypeIcon className="little-spacer-right" query={issue.type} /> - {translate('issue.type', issue.type)} - <i className="little-spacer-left icon-dropdown" /> - </button> - </BubblePopupHelper> + <div className="dropdown"> + <Toggler + onRequestClose={this.handleClose} + open={this.props.isOpen && this.props.canSetSeverity} + overlay={<SetTypePopup issue={issue} onSelect={this.setType} />}> + <Button + className="button-link issue-action issue-action-with-options js-issue-set-type" + onClick={this.toggleSetType}> + <IssueTypeIcon className="little-spacer-right" query={issue.type} /> + {translate('issue.type', issue.type)} + <DropdownIcon className="little-spacer-left" /> + </Button> + </Toggler> + </div> ); } else { return ( diff --git a/server/sonar-web/src/main/js/components/issue/components/SimilarIssuesFilter.js b/server/sonar-web/src/main/js/components/issue/components/SimilarIssuesFilter.js index b9da955025a..5b900ac2e48 100644 --- a/server/sonar-web/src/main/js/components/issue/components/SimilarIssuesFilter.js +++ b/server/sonar-web/src/main/js/components/issue/components/SimilarIssuesFilter.js @@ -19,8 +19,10 @@ */ // @flow import React from 'react'; -import BubblePopupHelper from '../../../components/common/BubblePopupHelper'; import SimilarIssuesPopup from '../popups/SimilarIssuesPopup'; +import Toggler from '../../../components/controls/Toggler'; +import DropdownIcon from '../../../components/icons-components/DropdownIcon'; +import { Button } from '../../../components/ui/buttons'; import { translate } from '../../../helpers/l10n'; /*:: import type { Issue } from '../types'; */ @@ -51,20 +53,26 @@ export default class SimilarIssuesFilter extends React.PureComponent { this.props.togglePopup('similarIssues', open); }; + handleClose = () => { + this.togglePopup(false); + }; + render() { return ( - <BubblePopupHelper - isOpen={this.props.isOpen} - position="bottomright" - togglePopup={this.togglePopup} - popup={<SimilarIssuesPopup issue={this.props.issue} onFilter={this.handleFilter} />}> - <button - className="js-issue-filter button-link issue-action issue-action-with-options" - aria-label={translate('issue.filter_similar_issues')} - onClick={this.handleClick}> - <i className="icon-filter icon-half-transparent" /> <i className="icon-dropdown" /> - </button> - </BubblePopupHelper> + <div className="dropdown"> + <Toggler + onRequestClose={this.handleClose} + open={this.props.isOpen} + overlay={<SimilarIssuesPopup issue={this.props.issue} onFilter={this.handleFilter} />}> + <Button + aria-label={translate('issue.filter_similar_issues')} + className="js-issue-filter button-link issue-action issue-action-with-options" + onClick={this.handleClick}> + <i className="icon-filter icon-half-transparent" /> + <DropdownIcon className="little-spacer-left" /> + </Button> + </Toggler> + </div> ); } } diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueAssign-test.js b/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueAssign-test.js index cdd7cfeb6f1..ad39643695e 100644 --- a/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueAssign-test.js +++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueAssign-test.js @@ -34,8 +34,8 @@ it('should render without the action when the correct rights are missing', () => canAssign={false} isOpen={false} issue={issue} - onFail={jest.fn()} onAssign={jest.fn()} + onFail={jest.fn()} togglePopup={jest.fn()} /> ); @@ -48,8 +48,8 @@ it('should render with the action', () => { canAssign={true} isOpen={false} issue={issue} - onFail={jest.fn()} onAssign={jest.fn()} + onFail={jest.fn()} togglePopup={jest.fn()} /> ); @@ -63,12 +63,12 @@ it('should open the popup when the button is clicked', () => { canAssign={true} isOpen={false} issue={issue} - onFail={jest.fn()} onAssign={jest.fn()} + onFail={jest.fn()} togglePopup={toggle} /> ); - click(element.find('button')); + click(element.find('Button')); expect(toggle.mock.calls).toMatchSnapshot(); element.setProps({ isOpen: true }); expect(element).toMatchSnapshot(); diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueChangelog-test.js b/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueChangelog-test.js index 0b2b9071cd8..8e03fdef42a 100644 --- a/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueChangelog-test.js +++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueChangelog-test.js @@ -52,7 +52,7 @@ it('should open the popup when the button is clicked', () => { togglePopup={toggle} /> ); - click(element.find('button')); + click(element.find('Button')); expect(toggle.mock.calls).toMatchSnapshot(); element.setProps({ isOpen: true }); expect(element).toMatchSnapshot(); diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueCommentAction-test.js b/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueCommentAction-test.js index d54334a61f9..2114f366226 100644 --- a/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueCommentAction-test.js +++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueCommentAction-test.js @@ -25,8 +25,8 @@ import { click } from '../../../../helpers/testUtils'; it('should render correctly', () => { const element = shallow( <IssueCommentAction - issueKey="issue-key" currentPopup={null} + issueKey="issue-key" onFail={jest.fn()} onIssueChange={jest.fn()} toggleComment={jest.fn()} @@ -39,14 +39,14 @@ it('should open the popup when the button is clicked', () => { const toggle = jest.fn(); const element = shallow( <IssueCommentAction - issueKey="issue-key" currentPopup={null} + issueKey="issue-key" onFail={jest.fn()} onIssueChange={jest.fn()} toggleComment={toggle} /> ); - click(element.find('button')); + click(element.find('Button')); expect(toggle.mock.calls.length).toBe(1); element.setProps({ currentPopup: 'comment' }); expect(element).toMatchSnapshot(); diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueCommentLine-test.js b/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueCommentLine-test.js index a321192729a..90cc0e1d8dc 100644 --- a/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueCommentLine-test.js +++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueCommentLine-test.js @@ -53,9 +53,9 @@ it('should open the right popups when the buttons are clicked', () => { const element = shallow( <IssueCommentLine comment={comment} onDelete={jest.fn()} onEdit={jest.fn()} /> ); - element.find('.js-issue-comment-edit').prop('onClick')(); + click(element.find('.js-issue-comment-edit')); expect(element.state()).toMatchSnapshot(); - element.find('.js-issue-comment-delete').prop('onClick')(); + click(element.find('.js-issue-comment-delete')); expect(element.state()).toMatchSnapshot(); element.update(); expect(element).toMatchSnapshot(); diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueSeverity-test.js b/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueSeverity-test.js index 98e4cf45c5b..08b00404347 100644 --- a/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueSeverity-test.js +++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueSeverity-test.js @@ -63,7 +63,7 @@ it('should open the popup when the button is clicked', () => { togglePopup={toggle} /> ); - click(element.find('button')); + click(element.find('Button')); expect(toggle.mock.calls).toMatchSnapshot(); element.setProps({ isOpen: true }); expect(element).toMatchSnapshot(); diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueTags-test.js b/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueTags-test.js index aa353241a8a..81d9a512a45 100644 --- a/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueTags-test.js +++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueTags-test.js @@ -71,7 +71,7 @@ it('should open the popup when the button is clicked', () => { togglePopup={toggle} /> ); - click(element.find('button')); + click(element.find('Button')); expect(toggle.mock.calls).toMatchSnapshot(); element.setProps({ isOpen: true }); expect(element).toMatchSnapshot(); diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueTransition-test.js b/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueTransition-test.js index 9bfc5d7cd85..98da66e0449 100644 --- a/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueTransition-test.js +++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueTransition-test.js @@ -84,7 +84,7 @@ it('should open the popup when the button is clicked', () => { togglePopup={toggle} /> ); - click(element.find('button')); + click(element.find('Button')); expect(toggle.mock.calls).toMatchSnapshot(); element.setProps({ isOpen: true }); expect(element).toMatchSnapshot(); diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueType-test.js b/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueType-test.js index de6dcde42bf..51132f338ca 100644 --- a/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueType-test.js +++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueType-test.js @@ -63,7 +63,7 @@ it('should open the popup when the button is clicked', () => { togglePopup={toggle} /> ); - click(element.find('button')); + click(element.find('Button')); expect(toggle.mock.calls).toMatchSnapshot(); element.setProps({ isOpen: true }); expect(element).toMatchSnapshot(); diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueAssign-test.js.snap b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueAssign-test.js.snap index d4ff90579c6..d768c0c852d 100644 --- a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueAssign-test.js.snap +++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueAssign-test.js.snap @@ -4,112 +4,111 @@ exports[`should open the popup when the button is clicked 1`] = ` Array [ Array [ "assign", - Object { - "currentTarget": Object { - "blur": [Function], - }, - "preventDefault": [Function], - "stopPropagation": [Function], - "target": Object { - "blur": [Function], - }, - }, + undefined, ], ] `; exports[`should open the popup when the button is clicked 2`] = ` -<BubblePopupHelper - isOpen={true} - popup={ - <Connect(SetAssigneePopup) - issue={ - Object { - "assignee": "john", - "assigneeAvatar": "gravatarhash", - "assigneeName": "John Doe", - } - } - onFail={[MockFunction]} - onSelect={[MockFunction]} - /> - } - position="bottomleft" - togglePopup={[Function]} +<div + className="dropdown" > - <button - className="button-link issue-action issue-action-with-options js-issue-assign" - onClick={[Function]} + <Toggler + closeOnEscape={true} + onRequestClose={[Function]} + open={true} + overlay={ + <Connect(SetAssigneePopup) + issue={ + Object { + "assignee": "john", + "assigneeAvatar": "gravatarhash", + "assigneeName": "John Doe", + } + } + onFail={[MockFunction]} + onSelect={[MockFunction]} + /> + } > - <span> - <span - className="text-top" - > - <Connect(Avatar) - className="little-spacer-right" - hash="gravatarhash" - name="John Doe" - size={16} - /> + <Button + className="button-link issue-action issue-action-with-options js-issue-assign" + onClick={[Function]} + > + <span> + <span + className="text-top" + > + <Connect(Avatar) + className="little-spacer-right" + hash="gravatarhash" + name="John Doe" + size={16} + /> + </span> + <span + className="issue-meta-label" + > + John Doe + </span> </span> - <span - className="issue-meta-label" - > - John Doe - </span> - </span> - <i - className="little-spacer-left icon-dropdown" - /> - </button> -</BubblePopupHelper> + <DropdownIcon + className="little-spacer-left" + /> + </Button> + </Toggler> +</div> `; exports[`should render with the action 1`] = ` -<BubblePopupHelper - isOpen={false} - popup={ - <Connect(SetAssigneePopup) - issue={ - Object { - "assignee": "john", - "assigneeAvatar": "gravatarhash", - "assigneeName": "John Doe", - } - } - onFail={[MockFunction]} - onSelect={[MockFunction]} - /> - } - position="bottomleft" - togglePopup={[Function]} +<div + className="dropdown" > - <button - className="button-link issue-action issue-action-with-options js-issue-assign" - onClick={[Function]} + <Toggler + closeOnEscape={true} + onRequestClose={[Function]} + open={false} + overlay={ + <Connect(SetAssigneePopup) + issue={ + Object { + "assignee": "john", + "assigneeAvatar": "gravatarhash", + "assigneeName": "John Doe", + } + } + onFail={[MockFunction]} + onSelect={[MockFunction]} + /> + } > - <span> - <span - className="text-top" - > - <Connect(Avatar) - className="little-spacer-right" - hash="gravatarhash" - name="John Doe" - size={16} - /> + <Button + className="button-link issue-action issue-action-with-options js-issue-assign" + onClick={[Function]} + > + <span> + <span + className="text-top" + > + <Connect(Avatar) + className="little-spacer-right" + hash="gravatarhash" + name="John Doe" + size={16} + /> + </span> + <span + className="issue-meta-label" + > + John Doe + </span> </span> - <span - className="issue-meta-label" - > - John Doe - </span> - </span> - <i - className="little-spacer-left icon-dropdown" - /> - </button> -</BubblePopupHelper> + <DropdownIcon + className="little-spacer-left" + /> + </Button> + </Toggler> +</div> `; exports[`should render without the action when the correct rights are missing 1`] = ` diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueChangelog-test.js.snap b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueChangelog-test.js.snap index 97f8c58d32a..0628de785bf 100644 --- a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueChangelog-test.js.snap +++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueChangelog-test.js.snap @@ -10,91 +10,97 @@ Array [ `; exports[`should open the popup when the button is clicked 2`] = ` -<BubblePopupHelper - isOpen={true} - popup={ - <ChangelogPopup - issue={ - Object { - "author": "john.david.dalton@gmail.com", - "creationDate": "2017-03-01T09:36:01+0100", - "key": "issuekey", - } - } - onFail={[MockFunction]} - /> - } - position="bottomright" - togglePopup={[Function]} +<div + className="dropdown" > - <Tooltip - mouseEnterDelay={0.5} + <Toggler + onRequestClose={[Function]} + open={true} overlay={ - <DateTimeFormatter - date="2017-03-01T09:36:01+0100" + <ChangelogPopup + issue={ + Object { + "author": "john.david.dalton@gmail.com", + "creationDate": "2017-03-01T09:36:01+0100", + "key": "issuekey", + } + } + onFail={[MockFunction]} /> } > - <button - className="button-link issue-action issue-action-with-options js-issue-show-changelog" - onClick={[Function]} + <Tooltip + mouseEnterDelay={0.5} + overlay={ + <DateTimeFormatter + date="2017-03-01T09:36:01+0100" + /> + } > - <span - className="issue-meta-label" + <Button + className="button-link issue-action issue-action-with-options js-issue-show-changelog" + onClick={[Function]} > - <DateFromNow - date="2017-03-01T09:36:01+0100" + <span + className="issue-meta-label" + > + <DateFromNow + date="2017-03-01T09:36:01+0100" + /> + </span> + <i + className="icon-dropdown little-spacer-left" /> - </span> - <i - className="icon-dropdown little-spacer-left" - /> - </button> - </Tooltip> -</BubblePopupHelper> + </Button> + </Tooltip> + </Toggler> +</div> `; exports[`should render correctly 1`] = ` -<BubblePopupHelper - isOpen={false} - popup={ - <ChangelogPopup - issue={ - Object { - "author": "john.david.dalton@gmail.com", - "creationDate": "2017-03-01T09:36:01+0100", - "key": "issuekey", - } - } - onFail={[MockFunction]} - /> - } - position="bottomright" - togglePopup={[Function]} +<div + className="dropdown" > - <Tooltip - mouseEnterDelay={0.5} + <Toggler + onRequestClose={[Function]} + open={false} overlay={ - <DateTimeFormatter - date="2017-03-01T09:36:01+0100" + <ChangelogPopup + issue={ + Object { + "author": "john.david.dalton@gmail.com", + "creationDate": "2017-03-01T09:36:01+0100", + "key": "issuekey", + } + } + onFail={[MockFunction]} /> } > - <button - className="button-link issue-action issue-action-with-options js-issue-show-changelog" - onClick={[Function]} + <Tooltip + mouseEnterDelay={0.5} + overlay={ + <DateTimeFormatter + date="2017-03-01T09:36:01+0100" + /> + } > - <span - className="issue-meta-label" + <Button + className="button-link issue-action issue-action-with-options js-issue-show-changelog" + onClick={[Function]} > - <DateFromNow - date="2017-03-01T09:36:01+0100" + <span + className="issue-meta-label" + > + <DateFromNow + date="2017-03-01T09:36:01+0100" + /> + </span> + <i + className="icon-dropdown little-spacer-left" /> - </span> - <i - className="icon-dropdown little-spacer-left" - /> - </button> - </Tooltip> -</BubblePopupHelper> + </Button> + </Tooltip> + </Toggler> +</div> `; diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueCommentAction-test.js.snap b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueCommentAction-test.js.snap index 7305e5d8472..3c62307690e 100644 --- a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueCommentAction-test.js.snap +++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueCommentAction-test.js.snap @@ -2,13 +2,13 @@ exports[`should open the popup when the button is clicked 1`] = ` <li - className="issue-meta" + className="issue-meta dropdown" > - <BubblePopupHelper - isOpen={true} - popup={ + <Toggler + onRequestClose={[Function]} + open={true} + overlay={ <CommentPopup - customClass="issue-comment-bubble-popup" onComment={[Function]} placeholder={undefined} toggleComment={ @@ -20,16 +20,8 @@ exports[`should open the popup when the button is clicked 1`] = ` } /> } - position="bottomleft" - togglePopup={ - [MockFunction] { - "calls": Array [ - Array [], - ], - } - } > - <button + <Button className="button-link issue-action js-issue-comment" onClick={[Function]} > @@ -38,29 +30,27 @@ exports[`should open the popup when the button is clicked 1`] = ` > issue.comment.formlink </span> - </button> - </BubblePopupHelper> + </Button> + </Toggler> </li> `; exports[`should render correctly 1`] = ` <li - className="issue-meta" + className="issue-meta dropdown" > - <BubblePopupHelper - isOpen={false} - popup={ + <Toggler + onRequestClose={[Function]} + open={false} + overlay={ <CommentPopup - customClass="issue-comment-bubble-popup" onComment={[Function]} placeholder={undefined} toggleComment={[MockFunction]} /> } - position="bottomleft" - togglePopup={[MockFunction]} > - <button + <Button className="button-link issue-action js-issue-comment" onClick={[Function]} > @@ -69,7 +59,7 @@ exports[`should render correctly 1`] = ` > issue.comment.formlink </span> - </button> - </BubblePopupHelper> + </Button> + </Toggler> </li> `; diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueCommentLine-test.js.snap b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueCommentLine-test.js.snap index 71f4ef39f22..4db5ed5aa9c 100644 --- a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueCommentLine-test.js.snap +++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueCommentLine-test.js.snap @@ -35,9 +35,6 @@ exports[`should open the right popups when the buttons are clicked 3`] = ` "__html": "<b>test</b>", } } - onClick={[Function]} - role="Listitem" - tabIndex={0} /> <div className="issue-comment-age" @@ -49,63 +46,56 @@ exports[`should open the right popups when the buttons are clicked 3`] = ` <div className="issue-comment-actions" > - <BubblePopupHelper - className="bubble-popup-helper-inline" - isOpen={false} - offset={ - Object { - "horizontal": -6, - "vertical": 0, - } - } - popup={ - <CommentPopup - comment={ - Object { - "authorAvatar": "gravatarhash", - "authorName": "John Doe", - "createdAt": "2017-03-01T09:36:01+0100", - "htmlText": "<b>test</b>", - "key": "comment-key", - "updatable": true, + <div + className="dropdown" + > + <Toggler + className="display-inline-block" + onRequestClose={[Function]} + open={false} + overlay={ + <CommentPopup + comment={ + Object { + "authorAvatar": "gravatarhash", + "authorName": "John Doe", + "createdAt": "2017-03-01T09:36:01+0100", + "htmlText": "<b>test</b>", + "key": "comment-key", + "updatable": true, + } } - } - customClass="issue-edit-comment-bubble-popup" - onComment={[Function]} - placeholder="" - toggleComment={[Function]} + onComment={[Function]} + placeholder="" + toggleComment={[Function]} + /> + } + > + <EditButton + className="js-issue-comment-edit button-small" + onClick={[Function]} /> - } - position="bottomright" - togglePopup={[Function]} + </Toggler> + </div> + <div + className="dropdown" > - <EditButton - className="js-issue-comment-edit button-small" - onClick={[Function]} - /> - </BubblePopupHelper> - <BubblePopupHelper - className="bubble-popup-helper-inline" - isOpen={true} - offset={ - Object { - "horizontal": -10, - "vertical": 0, + <Toggler + className="display-inline-block" + onRequestClose={[Function]} + open={true} + overlay={ + <CommentDeletePopup + onDelete={[Function]} + /> } - } - popup={ - <CommentDeletePopup - onDelete={[Function]} + > + <DeleteButton + className="js-issue-comment-delete button-small" + onClick={[Function]} /> - } - position="bottomright" - togglePopup={[Function]} - > - <DeleteButton - className="js-issue-comment-delete button-small" - onClick={[Function]} - /> - </BubblePopupHelper> + </Toggler> + </div> </div> </div> `; @@ -133,9 +123,6 @@ exports[`should render correctly a comment that is not updatable 1`] = ` "__html": "<b>test</b>", } } - onClick={[Function]} - role="Listitem" - tabIndex={0} /> <div className="issue-comment-age" @@ -173,9 +160,6 @@ exports[`should render correctly a comment that is updatable 1`] = ` "__html": "<b>test</b>", } } - onClick={[Function]} - role="Listitem" - tabIndex={0} /> <div className="issue-comment-age" @@ -187,63 +171,56 @@ exports[`should render correctly a comment that is updatable 1`] = ` <div className="issue-comment-actions" > - <BubblePopupHelper - className="bubble-popup-helper-inline" - isOpen={false} - offset={ - Object { - "horizontal": -6, - "vertical": 0, - } - } - popup={ - <CommentPopup - comment={ - Object { - "authorAvatar": "gravatarhash", - "authorName": "John Doe", - "createdAt": "2017-03-01T09:36:01+0100", - "htmlText": "<b>test</b>", - "key": "comment-key", - "updatable": true, + <div + className="dropdown" + > + <Toggler + className="display-inline-block" + onRequestClose={[Function]} + open={false} + overlay={ + <CommentPopup + comment={ + Object { + "authorAvatar": "gravatarhash", + "authorName": "John Doe", + "createdAt": "2017-03-01T09:36:01+0100", + "htmlText": "<b>test</b>", + "key": "comment-key", + "updatable": true, + } } - } - customClass="issue-edit-comment-bubble-popup" - onComment={[Function]} - placeholder="" - toggleComment={[Function]} + onComment={[Function]} + placeholder="" + toggleComment={[Function]} + /> + } + > + <EditButton + className="js-issue-comment-edit button-small" + onClick={[Function]} /> - } - position="bottomright" - togglePopup={[Function]} + </Toggler> + </div> + <div + className="dropdown" > - <EditButton - className="js-issue-comment-edit button-small" - onClick={[Function]} - /> - </BubblePopupHelper> - <BubblePopupHelper - className="bubble-popup-helper-inline" - isOpen={false} - offset={ - Object { - "horizontal": -10, - "vertical": 0, + <Toggler + className="display-inline-block" + onRequestClose={[Function]} + open={false} + overlay={ + <CommentDeletePopup + onDelete={[Function]} + /> } - } - popup={ - <CommentDeletePopup - onDelete={[Function]} + > + <DeleteButton + className="js-issue-comment-delete button-small" + onClick={[Function]} /> - } - position="bottomright" - togglePopup={[Function]} - > - <DeleteButton - className="js-issue-comment-delete button-small" - onClick={[Function]} - /> - </BubblePopupHelper> + </Toggler> + </div> </div> </div> `; diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueSeverity-test.js.snap b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueSeverity-test.js.snap index 98eb8644b75..898a75fd3d6 100644 --- a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueSeverity-test.js.snap +++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueSeverity-test.js.snap @@ -4,80 +4,77 @@ exports[`should open the popup when the button is clicked 1`] = ` Array [ Array [ "set-severity", - Object { - "currentTarget": Object { - "blur": [Function], - }, - "preventDefault": [Function], - "stopPropagation": [Function], - "target": Object { - "blur": [Function], - }, - }, + undefined, ], ] `; exports[`should open the popup when the button is clicked 2`] = ` -<BubblePopupHelper - isOpen={true} - popup={ - <SetSeverityPopup - issue={ - Object { - "severity": "BLOCKER", - } - } - onSelect={[Function]} - /> - } - position="bottomleft" - togglePopup={[Function]} +<div + className="dropdown" > - <button - className="button-link issue-action issue-action-with-options js-issue-set-severity" - onClick={[Function]} + <Toggler + onRequestClose={[Function]} + open={true} + overlay={ + <SetSeverityPopup + issue={ + Object { + "severity": "BLOCKER", + } + } + onSelect={[Function]} + /> + } > - <SeverityHelper - className="issue-meta-label little-spacer-right" - severity="BLOCKER" - /> - <i - className="little-spacer-left icon-dropdown" - /> - </button> -</BubblePopupHelper> + <Button + className="button-link issue-action issue-action-with-options js-issue-set-severity" + onClick={[Function]} + > + <SeverityHelper + className="issue-meta-label" + severity="BLOCKER" + /> + <DropdownIcon + className="little-spacer-left" + /> + </Button> + </Toggler> +</div> `; exports[`should render with the action 1`] = ` -<BubblePopupHelper - isOpen={false} - popup={ - <SetSeverityPopup - issue={ - Object { - "severity": "BLOCKER", - } - } - onSelect={[Function]} - /> - } - position="bottomleft" - togglePopup={[Function]} +<div + className="dropdown" > - <button - className="button-link issue-action issue-action-with-options js-issue-set-severity" - onClick={[Function]} + <Toggler + onRequestClose={[Function]} + open={false} + overlay={ + <SetSeverityPopup + issue={ + Object { + "severity": "BLOCKER", + } + } + onSelect={[Function]} + /> + } > - <SeverityHelper - className="issue-meta-label little-spacer-right" - severity="BLOCKER" - /> - <i - className="little-spacer-left icon-dropdown" - /> - </button> -</BubblePopupHelper> + <Button + className="button-link issue-action issue-action-with-options js-issue-set-severity" + onClick={[Function]} + > + <SeverityHelper + className="issue-meta-label" + severity="BLOCKER" + /> + <DropdownIcon + className="little-spacer-left" + /> + </Button> + </Toggler> +</div> `; exports[`should render without the action when the correct rights are missing 1`] = ` diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTags-test.js.snap b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTags-test.js.snap index fa30aff7c20..ca33fb7f1ec 100644 --- a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTags-test.js.snap +++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTags-test.js.snap @@ -4,88 +4,85 @@ exports[`should open the popup when the button is clicked 1`] = ` Array [ Array [ "edit-tags", - Object { - "currentTarget": Object { - "blur": [Function], - }, - "preventDefault": [Function], - "stopPropagation": [Function], - "target": Object { - "blur": [Function], - }, - }, + undefined, ], ] `; exports[`should open the popup when the button is clicked 2`] = ` -<BubblePopupHelper - isOpen={true} - popup={ - <SetIssueTagsPopup - organization="foo" - selectedTags={ - Array [ - "mytag", - "test", - ] - } - setTags={[Function]} - /> - } - position="bottomright" - togglePopup={[Function]} +<div + className="dropdown" > - <button - className="js-issue-edit-tags button-link issue-action issue-action-with-options" - onClick={[Function]} + <Toggler + onRequestClose={[Function]} + open={true} + overlay={ + <SetIssueTagsPopup + organization="foo" + selectedTags={ + Array [ + "mytag", + "test", + ] + } + setTags={[Function]} + /> + } > - <TagsList - allowUpdate={true} - tags={ - Array [ - "mytag", - "test", - ] - } - /> - </button> -</BubblePopupHelper> + <Button + className="js-issue-edit-tags button-link issue-action issue-action-with-options" + onClick={[Function]} + > + <TagsList + allowUpdate={true} + tags={ + Array [ + "mytag", + "test", + ] + } + /> + </Button> + </Toggler> +</div> `; exports[`should render with the action 1`] = ` -<BubblePopupHelper - isOpen={false} - popup={ - <SetIssueTagsPopup - organization="foo" - selectedTags={ - Array [ - "mytag", - "test", - ] - } - setTags={[Function]} - /> - } - position="bottomright" - togglePopup={[Function]} +<div + className="dropdown" > - <button - className="js-issue-edit-tags button-link issue-action issue-action-with-options" - onClick={[Function]} + <Toggler + onRequestClose={[Function]} + open={false} + overlay={ + <SetIssueTagsPopup + organization="foo" + selectedTags={ + Array [ + "mytag", + "test", + ] + } + setTags={[Function]} + /> + } > - <TagsList - allowUpdate={true} - tags={ - Array [ - "mytag", - "test", - ] - } - /> - </button> -</BubblePopupHelper> + <Button + className="js-issue-edit-tags button-link issue-action issue-action-with-options" + onClick={[Function]} + > + <TagsList + allowUpdate={true} + tags={ + Array [ + "mytag", + "test", + ] + } + /> + </Button> + </Toggler> +</div> `; exports[`should render without the action when the correct rights are missing 1`] = ` diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTitleBar-test.js.snap b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTitleBar-test.js.snap index 30fc9e84c33..8f3694dd8d0 100644 --- a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTitleBar-test.js.snap +++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTitleBar-test.js.snap @@ -68,7 +68,6 @@ exports[`should render the titlebar correctly 1`] = ` > <Link className="js-issue-permalink link-no-underline" - onClick={[Function]} onlyActiveOnIndex={false} style={Object {}} target="_blank" @@ -152,7 +151,6 @@ exports[`should render the titlebar with the filter 1`] = ` > <Link className="js-issue-permalink link-no-underline" - onClick={[Function]} onlyActiveOnIndex={false} style={Object {}} target="_blank" diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTransition-test.js.snap b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTransition-test.js.snap index cd14db53dd2..dcfe4ff35ea 100644 --- a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTransition-test.js.snap +++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTransition-test.js.snap @@ -4,118 +4,118 @@ exports[`should open the popup when the button is clicked 1`] = ` Array [ Array [ "transition", - Object { - "currentTarget": Object { - "blur": [Function], - }, - "preventDefault": [Function], - "stopPropagation": [Function], - "target": Object { - "blur": [Function], - }, - }, + undefined, ], ] `; exports[`should open the popup when the button is clicked 2`] = ` -<BubblePopupHelper - isOpen={true} - popup={ - <SetTransitionPopup - onSelect={[Function]} - transitions={ - Array [ - "confirm", - "resolve", - "falsepositive", - "wontfix", - ] - } - /> - } - position="bottomleft" - togglePopup={[Function]} +<div + className="dropdown" > - <button - className="button-link issue-action issue-action-with-options js-issue-transition" - onClick={[Function]} + <Toggler + onRequestClose={[Function]} + open={true} + overlay={ + <SetTransitionPopup + onSelect={[Function]} + transitions={ + Array [ + "confirm", + "resolve", + "falsepositive", + "wontfix", + ] + } + /> + } > - <StatusHelper - className="issue-meta-label little-spacer-right" - status="OPEN" - /> - <i - className="little-spacer-left icon-dropdown" - /> - </button> -</BubblePopupHelper> + <Button + className="button-link issue-action issue-action-with-options js-issue-transition" + onClick={[Function]} + > + <StatusHelper + className="issue-meta-label" + status="OPEN" + /> + <DropdownIcon + className="little-spacer-left" + /> + </Button> + </Toggler> +</div> `; exports[`should render with a resolution 1`] = ` -<BubblePopupHelper - isOpen={false} - popup={ - <SetTransitionPopup - onSelect={[Function]} - transitions={ - Array [ - "reopen", - ] - } - /> - } - position="bottomleft" - togglePopup={[Function]} +<div + className="dropdown" > - <button - className="button-link issue-action issue-action-with-options js-issue-transition" - onClick={[Function]} + <Toggler + onRequestClose={[Function]} + open={false} + overlay={ + <SetTransitionPopup + onSelect={[Function]} + transitions={ + Array [ + "reopen", + ] + } + /> + } > - <StatusHelper - className="issue-meta-label little-spacer-right" - resolution="FIXED" - status="RESOLVED" - /> - <i - className="little-spacer-left icon-dropdown" - /> - </button> -</BubblePopupHelper> + <Button + className="button-link issue-action issue-action-with-options js-issue-transition" + onClick={[Function]} + > + <StatusHelper + className="issue-meta-label" + resolution="FIXED" + status="RESOLVED" + /> + <DropdownIcon + className="little-spacer-left" + /> + </Button> + </Toggler> +</div> `; exports[`should render with the action 1`] = ` -<BubblePopupHelper - isOpen={false} - popup={ - <SetTransitionPopup - onSelect={[Function]} - transitions={ - Array [ - "confirm", - "resolve", - "falsepositive", - "wontfix", - ] - } - /> - } - position="bottomleft" - togglePopup={[Function]} +<div + className="dropdown" > - <button - className="button-link issue-action issue-action-with-options js-issue-transition" - onClick={[Function]} + <Toggler + onRequestClose={[Function]} + open={false} + overlay={ + <SetTransitionPopup + onSelect={[Function]} + transitions={ + Array [ + "confirm", + "resolve", + "falsepositive", + "wontfix", + ] + } + /> + } > - <StatusHelper - className="issue-meta-label little-spacer-right" - status="OPEN" - /> - <i - className="little-spacer-left icon-dropdown" - /> - </button> -</BubblePopupHelper> + <Button + className="button-link issue-action issue-action-with-options js-issue-transition" + onClick={[Function]} + > + <StatusHelper + className="issue-meta-label" + status="OPEN" + /> + <DropdownIcon + className="little-spacer-left" + /> + </Button> + </Toggler> +</div> `; exports[`should render without the action when there is no transitions 1`] = ` diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueType-test.js.snap b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueType-test.js.snap index 77988d1f003..1eb2844dbf6 100644 --- a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueType-test.js.snap +++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueType-test.js.snap @@ -4,82 +4,79 @@ exports[`should open the popup when the button is clicked 1`] = ` Array [ Array [ "set-type", - Object { - "currentTarget": Object { - "blur": [Function], - }, - "preventDefault": [Function], - "stopPropagation": [Function], - "target": Object { - "blur": [Function], - }, - }, + undefined, ], ] `; exports[`should open the popup when the button is clicked 2`] = ` -<BubblePopupHelper - isOpen={true} - popup={ - <SetTypePopup - issue={ - Object { - "type": "bug", - } - } - onSelect={[Function]} - /> - } - position="bottomleft" - togglePopup={[Function]} +<div + className="dropdown" > - <button - className="button-link issue-action issue-action-with-options js-issue-set-type" - onClick={[Function]} + <Toggler + onRequestClose={[Function]} + open={true} + overlay={ + <SetTypePopup + issue={ + Object { + "type": "bug", + } + } + onSelect={[Function]} + /> + } > - <IssueTypeIcon - className="little-spacer-right" - query="bug" - /> - issue.type.bug - <i - className="little-spacer-left icon-dropdown" - /> - </button> -</BubblePopupHelper> + <Button + className="button-link issue-action issue-action-with-options js-issue-set-type" + onClick={[Function]} + > + <IssueTypeIcon + className="little-spacer-right" + query="bug" + /> + issue.type.bug + <DropdownIcon + className="little-spacer-left" + /> + </Button> + </Toggler> +</div> `; exports[`should render with the action 1`] = ` -<BubblePopupHelper - isOpen={false} - popup={ - <SetTypePopup - issue={ - Object { - "type": "bug", - } - } - onSelect={[Function]} - /> - } - position="bottomleft" - togglePopup={[Function]} +<div + className="dropdown" > - <button - className="button-link issue-action issue-action-with-options js-issue-set-type" - onClick={[Function]} + <Toggler + onRequestClose={[Function]} + open={false} + overlay={ + <SetTypePopup + issue={ + Object { + "type": "bug", + } + } + onSelect={[Function]} + /> + } > - <IssueTypeIcon - className="little-spacer-right" - query="bug" - /> - issue.type.bug - <i - className="little-spacer-left icon-dropdown" - /> - </button> -</BubblePopupHelper> + <Button + className="button-link issue-action issue-action-with-options js-issue-set-type" + onClick={[Function]} + > + <IssueTypeIcon + className="little-spacer-right" + query="bug" + /> + issue.type.bug + <DropdownIcon + className="little-spacer-left" + /> + </Button> + </Toggler> +</div> `; exports[`should render without the action when the correct rights are missing 1`] = ` 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 index b3f230810fe..fcfd515b587 100644 --- a/server/sonar-web/src/main/js/components/issue/popups/ChangelogPopup.js +++ b/server/sonar-web/src/main/js/components/issue/popups/ChangelogPopup.js @@ -22,9 +22,9 @@ import React from 'react'; import { getIssueChangelog } from '../../../api/issues'; import { translate } from '../../../helpers/l10n'; import Avatar from '../../../components/ui/Avatar'; -import BubblePopup from '../../../components/common/BubblePopup'; import DateTimeFormatter from '../../../components/intl/DateTimeFormatter'; import IssueChangelogDiff from '../components/IssueChangelogDiff'; +import { DropdownOverlay } from '../../controls/Dropdown'; /*:: import type { ChangelogDiff } from '../components/IssueChangelogDiff'; */ /*:: import type { Issue } from '../types'; */ @@ -80,8 +80,8 @@ export default class ChangelogPopup extends React.PureComponent { const { issue } = this.props; const { author } = issue; return ( - <BubblePopup position={this.props.popupPosition} customClass="bubble-popup-bottom-right"> - <div className="issue-changelog"> + <DropdownOverlay> + <div className="menu is-container issue-changelog"> <table className="spaced"> <tbody> <tr> @@ -110,14 +110,14 @@ export default class ChangelogPopup extends React.PureComponent { {item.userName} </p> )} - {item.diffs.map(diff => <IssueChangelogDiff key={diff.key} diff={diff} />)} + {item.diffs.map(diff => <IssueChangelogDiff diff={diff} key={diff.key} />)} </td> </tr> ))} </tbody> </table> </div> - </BubblePopup> + </DropdownOverlay> ); } } 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 index 9776ab00253..c7fd5d89ae8 100644 --- a/server/sonar-web/src/main/js/components/issue/popups/CommentDeletePopup.js +++ b/server/sonar-web/src/main/js/components/issue/popups/CommentDeletePopup.js @@ -19,8 +19,9 @@ */ // @flow import React from 'react'; +import { Button } from '../../../components/ui/buttons'; import { translate } from '../../../helpers/l10n'; -import BubblePopup from '../../../components/common/BubblePopup'; +import { DropdownOverlay } from '../../controls/Dropdown'; /*:: type Props = { @@ -31,13 +32,13 @@ type Props = { export default function CommentDeletePopup(props /*: Props */) { return ( - <BubblePopup position={props.popupPosition} customClass="bubble-popup-bottom-right"> - <div className="text-right"> + <DropdownOverlay> + <div className="menu is-container"> <div className="spacer-bottom">{translate('issue.comment.delete_confirm_message')}</div> - <button className="button-red" onClick={props.onDelete}> + <Button className="button-red" onClick={props.onDelete}> {translate('delete')} - </button> + </Button> </div> - </BubblePopup> + </DropdownOverlay> ); } 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 index 2a17f71383b..bee719a083e 100644 --- a/server/sonar-web/src/main/js/components/issue/popups/CommentPopup.js +++ b/server/sonar-web/src/main/js/components/issue/popups/CommentPopup.js @@ -19,16 +19,15 @@ */ // @flow import React from 'react'; -import classNames from 'classnames'; -import BubblePopup from '../../../components/common/BubblePopup'; import MarkdownTips from '../../../components/common/MarkdownTips'; +import { Button, ResetButtonLink } from '../../../components/ui/buttons'; import { translate } from '../../../helpers/l10n'; +import { DropdownOverlay } from '../../controls/Dropdown'; /*:: import type { IssueComment } from '../types'; */ /*:: type Props = { comment?: IssueComment, - customClass?: string, onComment: string => void, toggleComment: boolean => void, placeholder: string, @@ -63,8 +62,7 @@ export default class CommentPopup extends React.PureComponent { } }; - handleCancelClick = (evt /*: MouseEvent */) => { - evt.preventDefault(); + handleCancelClick = () => { this.props.toggleComment(false); }; @@ -81,37 +79,37 @@ export default class CommentPopup extends React.PureComponent { 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> + <DropdownOverlay> + <div className="issue-comment-bubble-popup"> + <div className="issue-comment-form-text"> + <textarea + autoFocus={true} + onChange={this.handleCommentChange} + onKeyDown={this.handleKeyboard} + placeholder={this.props.placeholder} + rows="2" + value={this.state.textComment} + /> </div> - <div className="issue-comment-form-tips"> - <MarkdownTips /> + <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> + <ResetButtonLink className="js-issue-comment-cancel" onClick={this.handleCancelClick}> + {translate('cancel')} + </ResetButtonLink> + </div> + <div className="issue-comment-form-tips"> + <MarkdownTips /> + </div> </div> </div> - </BubblePopup> + </DropdownOverlay> ); } } 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 index 270cbe04158..b5658c997d3 100644 --- a/server/sonar-web/src/main/js/components/issue/popups/SetAssigneePopup.js +++ b/server/sonar-web/src/main/js/components/issue/popups/SetAssigneePopup.js @@ -23,7 +23,6 @@ import { map } from 'lodash'; import { connect } from 'react-redux'; import * as PropTypes from 'prop-types'; 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 SearchBox from '../../../components/controls/SearchBox'; @@ -31,6 +30,7 @@ import { searchMembers } from '../../../api/organizations'; import { searchUsers } from '../../../api/users'; import { translate } from '../../../helpers/l10n'; import { getCurrentUser } from '../../../store/rootReducer'; +import { DropdownOverlay } from '../../controls/Dropdown'; /*:: import type { Issue } from '../types'; */ /*:: @@ -123,9 +123,7 @@ class SetAssigneePopup extends React.PureComponent { render() { return ( - <BubblePopup - customClass="bubble-popup-menu bubble-popup-bottom" - position={this.props.popupPosition}> + <DropdownOverlay noPadding={true}> <div className="multi-select"> <div className="menu-search"> <SearchBox @@ -142,7 +140,7 @@ class SetAssigneePopup extends React.PureComponent { items={map(this.state.users, 'login')} onSelect={this.props.onSelect}> {this.state.users.map(user => ( - <SelectListItem key={user.login} item={user.login}> + <SelectListItem item={user.login} key={user.login}> {!!user.login && ( <Avatar className="spacer-right" hash={user.avatar} name={user.name} size={16} /> )} @@ -155,7 +153,7 @@ class SetAssigneePopup extends React.PureComponent { ))} </SelectList> </div> - </BubblePopup> + </DropdownOverlay> ); } } diff --git a/server/sonar-web/src/main/js/components/issue/popups/SetIssueTagsPopup.tsx b/server/sonar-web/src/main/js/components/issue/popups/SetIssueTagsPopup.tsx index 90b8af5f628..c41c216e8b6 100644 --- a/server/sonar-web/src/main/js/components/issue/popups/SetIssueTagsPopup.tsx +++ b/server/sonar-web/src/main/js/components/issue/popups/SetIssueTagsPopup.tsx @@ -19,12 +19,12 @@ */ import * as React from 'react'; import { difference, without } from 'lodash'; -import { BubblePopupPosition } from '../../../components/common/BubblePopup'; import TagsSelector from '../../../components/tags/TagsSelector'; import { searchIssueTags } from '../../../api/issues'; +import { DropdownOverlay } from '../../controls/Dropdown'; +import { PopupPlacement } from '../../ui/popups'; interface Props { - popupPosition: BubblePopupPosition; organization: string; selectedTags: string[]; setTags: (tags: string[]) => void; @@ -74,15 +74,16 @@ export default class SetIssueTagsPopup extends React.PureComponent<Props, State> render() { const availableTags = difference(this.state.searchResult, this.props.selectedTags); return ( - <TagsSelector - listSize={LIST_SIZE} - onSearch={this.onSearch} - onSelect={this.onSelect} - onUnselect={this.onUnselect} - position={this.props.popupPosition} - selectedTags={this.props.selectedTags} - tags={availableTags} - /> + <DropdownOverlay placement={PopupPlacement.BottomRight}> + <TagsSelector + listSize={LIST_SIZE} + onSearch={this.onSearch} + onSelect={this.onSelect} + onUnselect={this.onUnselect} + selectedTags={this.props.selectedTags} + tags={availableTags} + /> + </DropdownOverlay> ); } } 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 index cc03b290475..2d3c2232f88 100644 --- a/server/sonar-web/src/main/js/components/issue/popups/SetSeverityPopup.js +++ b/server/sonar-web/src/main/js/components/issue/popups/SetSeverityPopup.js @@ -20,17 +20,16 @@ // @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 { DropdownOverlay } from '../../controls/Dropdown'; /*:: import type { Issue } from '../types'; */ /*:: type Props = { issue: Issue, onSelect: string => void, - popupPosition?: {} }; */ @@ -41,21 +40,19 @@ export default class SetSeverityPopup extends React.PureComponent { render() { return ( - <BubblePopup - position={this.props.popupPosition} - customClass="bubble-popup-menu bubble-popup-bottom"> + <DropdownOverlay> <SelectList - items={SEVERITY} currentItem={this.props.issue.severity} + items={SEVERITY} onSelect={this.props.onSelect}> {SEVERITY.map(severity => ( - <SelectListItem key={severity} item={severity}> + <SelectListItem item={severity} key={severity}> <SeverityIcon className="little-spacer-right" severity={severity} /> {translate('severity', severity)} </SelectListItem> ))} </SelectList> - </BubblePopup> + </DropdownOverlay> ); } } 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 index 44bf942253d..37d342f7eb2 100644 --- a/server/sonar-web/src/main/js/components/issue/popups/SetTransitionPopup.js +++ b/server/sonar-web/src/main/js/components/issue/popups/SetTransitionPopup.js @@ -19,16 +19,15 @@ */ // @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'; +import { DropdownOverlay } from '../../controls/Dropdown'; /*:: type Props = { transitions: Array<string>, onSelect: string => void, - popupPosition?: {} }; */ @@ -38,22 +37,20 @@ export default class SetTransitionPopup extends React.PureComponent { 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}> + <DropdownOverlay> + <SelectList currentItem={transitions[0]} items={transitions} onSelect={this.props.onSelect}> {transitions.map(transition => { return ( <SelectListItem - key={transition} item={transition} + key={transition} title={translate('issue.transition', transition, 'description')}> {translate('issue.transition', transition)} </SelectListItem> ); })} </SelectList> - </BubblePopup> + </DropdownOverlay> ); } } 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 index f623afccd08..f69e07cf141 100644 --- a/server/sonar-web/src/main/js/components/issue/popups/SetTypePopup.js +++ b/server/sonar-web/src/main/js/components/issue/popups/SetTypePopup.js @@ -20,17 +20,16 @@ // @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 { DropdownOverlay } from '../../controls/Dropdown'; /*:: import type { Issue } from '../types'; */ /*:: type Props = { issue: Issue, onSelect: string => void, - popupPosition?: {} }; */ @@ -41,21 +40,19 @@ export default class SetTypePopup extends React.PureComponent { render() { return ( - <BubblePopup - position={this.props.popupPosition} - customClass="bubble-popup-menu bubble-popup-bottom"> + <DropdownOverlay> <SelectList - items={TYPES} currentItem={this.props.issue.type} + items={TYPES} onSelect={this.props.onSelect}> {TYPES.map(type => ( - <SelectListItem key={type} item={type}> + <SelectListItem item={type} key={type}> <IssueTypeIcon className="little-spacer-right" query={type} /> {translate('issue.type', type)} </SelectListItem> ))} </SelectList> - </BubblePopup> + </DropdownOverlay> ); } } diff --git a/server/sonar-web/src/main/js/components/issue/popups/SimilarIssuesPopup.js b/server/sonar-web/src/main/js/components/issue/popups/SimilarIssuesPopup.js index fb9b24780e3..7a28c6594ed 100644 --- a/server/sonar-web/src/main/js/components/issue/popups/SimilarIssuesPopup.js +++ b/server/sonar-web/src/main/js/components/issue/popups/SimilarIssuesPopup.js @@ -19,9 +19,9 @@ */ // @flow import React from 'react'; -import BubblePopup from '../../../components/common/BubblePopup'; import SelectList from '../../../components/common/SelectList'; import SelectListItem from '../../../components/common/SelectListItem'; +import { DropdownOverlay } from '../../../components/controls/Dropdown'; import SeverityHelper from '../../../components/shared/SeverityHelper'; import StatusHelper from '../../../components/shared/StatusHelper'; import QualifierIcon from '../../../components/shared/QualifierIcon'; @@ -35,7 +35,6 @@ import { fileFromPath, limitComponentName } from '../../../helpers/path'; type Props = {| issue: Issue, onFilter: (property: string, issue: Issue) => void, - popupPosition?: {} |}; */ @@ -64,9 +63,7 @@ export default class SimilarIssuesPopup extends React.PureComponent { ].filter(item => item); return ( - <BubblePopup - position={this.props.popupPosition} - customClass="bubble-popup-menu bubble-popup-bottom-right"> + <DropdownOverlay noPadding={true}> <header className="menu-search"> <h6>{translate('issue.filter_similar_issues')}</h6> </header> @@ -118,7 +115,7 @@ export default class SimilarIssuesPopup extends React.PureComponent { {issue.tags != null && issue.tags.map(tag => ( - <SelectListItem key={`tag###${tag}`} item={`tag###${tag}`}> + <SelectListItem item={`tag###${tag}`} key={`tag###${tag}`}> <i className="icon-tags icon-half-transparent little-spacer-right" /> {tag} </SelectListItem> @@ -143,7 +140,7 @@ export default class SimilarIssuesPopup extends React.PureComponent { {fileFromPath(issue.componentLongName)} </SelectListItem> </SelectList> - </BubblePopup> + </DropdownOverlay> ); } } 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 index 2256e3fb35a..be0c222335d 100644 --- 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 @@ -26,6 +26,6 @@ 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')); + 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 index 7716fe669a3..6cfd0dcdf64 100644 --- 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 @@ -25,10 +25,10 @@ import { click } from '../../../../helpers/testUtils'; it('should render the comment popup correctly without existing comment', () => { const element = shallow( <CommentPopup + customClass="myclass" onComment={jest.fn()} - toggleComment={jest.fn()} placeholder="placeholder test" - customClass="myclass" + toggleComment={jest.fn()} /> ); expect(element).toMatchSnapshot(); @@ -37,12 +37,10 @@ it('should render the comment popup correctly without existing comment', () => { it('should render the comment popup correctly when changing a comment', () => { const element = shallow( <CommentPopup - comment={{ - markdown: '*test*' - }} + comment={{ markdown: '*test*' }} onComment={jest.fn()} - toggleComment={jest.fn()} placeholder="" + toggleComment={jest.fn()} /> ); expect(element).toMatchSnapshot(); @@ -52,15 +50,15 @@ it('should render not allow to send comment with only spaces', () => { const onComment = jest.fn(); const element = shallow( <CommentPopup + customClass="myclass" onComment={onComment} - toggleComment={jest.fn()} placeholder="placeholder test" - customClass="myclass" + toggleComment={jest.fn()} /> ); - click(element.find('button.js-issue-comment-submit')); + click(element.find('.js-issue-comment-submit')); expect(onComment.mock.calls.length).toBe(0); element.setState({ textComment: 'mycomment' }); - click(element.find('button.js-issue-comment-submit')); + click(element.find('.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.tsx b/server/sonar-web/src/main/js/components/issue/popups/__tests__/SetIssueTagsPopup-test.tsx index f66e329ec87..00b85fd1817 100644 --- a/server/sonar-web/src/main/js/components/issue/popups/__tests__/SetIssueTagsPopup-test.tsx +++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/SetIssueTagsPopup-test.tsx @@ -23,12 +23,7 @@ import SetIssueTagsPopup from '../SetIssueTagsPopup'; it('should render tags popup correctly', () => { const element = shallow( - <SetIssueTagsPopup - organization="foo" - popupPosition={{}} - selectedTags={['mytag']} - setTags={jest.fn()} - /> + <SetIssueTagsPopup organization="foo" 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__/__snapshots__/ChangelogPopup-test.js.snap b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/ChangelogPopup-test.js.snap index 65cade73cf8..f54cfd88b77 100644 --- 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 @@ -1,11 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should render the changelog popup correctly 1`] = ` -<BubblePopup - customClass="bubble-popup-bottom-right" -> +<DropdownOverlay> <div - className="issue-changelog" + className="menu is-container issue-changelog" > <table className="spaced" @@ -62,5 +60,5 @@ exports[`should render the changelog popup correctly 1`] = ` </tbody> </table> </div> -</BubblePopup> +</DropdownOverlay> `; 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 index 634b35907be..43c00928943 100644 --- 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 @@ -1,23 +1,21 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should render the comment delete popup correctly 1`] = ` -<BubblePopup - customClass="bubble-popup-bottom-right" -> +<DropdownOverlay> <div - className="text-right" + className="menu is-container" > <div className="spacer-bottom" > issue.comment.delete_confirm_message </div> - <button + <Button className="button-red" onClick={[MockFunction]} > delete - </button> + </Button> </div> -</BubblePopup> +</DropdownOverlay> `; 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 index d33898199b7..8ed5e9eb55a 100644 --- 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 @@ -1,93 +1,95 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should render the comment popup correctly when changing a comment 1`] = ` -<BubblePopup - customClass="bubble-popup-bottom-right" -> +<DropdownOverlay> <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" + className="issue-comment-bubble-popup" > <div - className="issue-comment-form-actions" + className="issue-comment-form-text" > - <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> + <textarea + autoFocus={true} + onChange={[Function]} + onKeyDown={[Function]} + placeholder="" + rows="2" + value="*test*" + /> </div> <div - className="issue-comment-form-tips" + className="issue-comment-form-footer" > - <MarkdownTips /> + <div + className="issue-comment-form-actions" + > + <Button + className="js-issue-comment-submit little-spacer-right" + disabled={false} + onClick={[Function]} + > + save + </Button> + <ResetButtonLink + className="js-issue-comment-cancel" + onClick={[Function]} + > + cancel + </ResetButtonLink> + </div> + <div + className="issue-comment-form-tips" + > + <MarkdownTips /> + </div> </div> </div> -</BubblePopup> +</DropdownOverlay> `; exports[`should render the comment popup correctly without existing comment 1`] = ` -<BubblePopup - customClass="myclass bubble-popup-bottom-right" -> +<DropdownOverlay> <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" + className="issue-comment-bubble-popup" > <div - className="issue-comment-form-actions" + className="issue-comment-form-text" > - <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> + <textarea + autoFocus={true} + onChange={[Function]} + onKeyDown={[Function]} + placeholder="placeholder test" + rows="2" + value="" + /> </div> <div - className="issue-comment-form-tips" + className="issue-comment-form-footer" > - <MarkdownTips /> + <div + className="issue-comment-form-actions" + > + <Button + className="js-issue-comment-submit little-spacer-right" + disabled={true} + onClick={[Function]} + > + issue.comment.submit + </Button> + <ResetButtonLink + className="js-issue-comment-cancel" + onClick={[Function]} + > + cancel + </ResetButtonLink> + </div> + <div + className="issue-comment-form-tips" + > + <MarkdownTips /> + </div> </div> </div> -</BubblePopup> +</DropdownOverlay> `; diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetIssueTagsPopup-test.tsx.snap b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetIssueTagsPopup-test.tsx.snap index 57990df2131..a5eb2aafcac 100644 --- a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetIssueTagsPopup-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetIssueTagsPopup-test.tsx.snap @@ -1,22 +1,25 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should render tags popup correctly 1`] = ` -<TagsSelector - listSize={10} - onSearch={[Function]} - onSelect={[Function]} - onUnselect={[Function]} - position={Object {}} - selectedTags={ - Array [ - "mytag", - ] - } - tags={ - Array [ - "test", - "second", - ] - } -/> +<DropdownOverlay + placement="bottom-right" +> + <TagsSelector + listSize={10} + onSearch={[Function]} + onSelect={[Function]} + onUnselect={[Function]} + selectedTags={ + Array [ + "mytag", + ] + } + tags={ + Array [ + "test", + "second", + ] + } + /> +</DropdownOverlay> `; 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 index f5cc9cfed81..f8818f53cab 100644 --- 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 @@ -1,9 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should render tags popup correctly 1`] = ` -<BubblePopup - customClass="bubble-popup-menu bubble-popup-bottom" -> +<DropdownOverlay> <SelectList currentItem="MAJOR" items={ @@ -68,5 +66,5 @@ exports[`should render tags popup correctly 1`] = ` severity.INFO </SelectListItem> </SelectList> -</BubblePopup> +</DropdownOverlay> `; 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 index 2b0be175cbe..a9728b1687a 100644 --- 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 @@ -1,9 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should render tags popup correctly 1`] = ` -<BubblePopup - customClass="bubble-popup-menu bubble-popup-bottom" -> +<DropdownOverlay> <SelectList currentItem="confirm" items={ @@ -45,5 +43,5 @@ exports[`should render tags popup correctly 1`] = ` issue.transition.wontfix </SelectListItem> </SelectList> -</BubblePopup> +</DropdownOverlay> `; 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 index 190d91cb61b..5055d807449 100644 --- 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 @@ -1,9 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should render tags popup correctly 1`] = ` -<BubblePopup - customClass="bubble-popup-menu bubble-popup-bottom" -> +<DropdownOverlay> <SelectList currentItem="BUG" items={ @@ -46,5 +44,5 @@ exports[`should render tags popup correctly 1`] = ` issue.type.CODE_SMELL </SelectListItem> </SelectList> -</BubblePopup> +</DropdownOverlay> `; |