diff options
author | Stas Vilchik <stas-vilchik@users.noreply.github.com> | 2017-04-26 21:09:55 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-04-26 21:09:55 +0200 |
commit | 3e72937a66a0ca25d8cf2872f614b8edf179c2a8 (patch) | |
tree | 2896361963013aed8c7b792f31b064ee0954ef67 /server/sonar-web/src/main/js/components | |
parent | 439d0750b2ec547f6829cbeadcb9fed995c6eb05 (diff) | |
download | sonarqube-3e72937a66a0ca25d8cf2872f614b8edf179c2a8.tar.gz sonarqube-3e72937a66a0ca25d8cf2872f614b8edf179c2a8.zip |
apply feedback for issues page (#1980)
Diffstat (limited to 'server/sonar-web/src/main/js/components')
26 files changed, 205 insertions, 385 deletions
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerBase.js b/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerBase.js index c7cc3387c2a..8f29e6a59c7 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerBase.js +++ b/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerBase.js @@ -180,7 +180,7 @@ export default class SourceViewerBase extends React.PureComponent { `.source-line-code[data-line-number="${line}"] .source-line-issue-locations` ); if (lineElement) { - scrollToElement(lineElement, 125, 75); + scrollToElement(lineElement, { topOffset: 125, bottomOffset: 75 }); } } diff --git a/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerHeader.js b/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerHeader.js index d97bc5a3b4a..d44df72f472 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerHeader.js +++ b/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerHeader.js @@ -22,7 +22,7 @@ import React from 'react'; import { Link } from 'react-router'; import QualifierIcon from '../shared/QualifierIcon'; import FavoriteContainer from '../controls/FavoriteContainer'; -import { getProjectUrl, getIssuesUrl } from '../../helpers/urls'; +import { getProjectUrl, getComponentIssuesUrl } from '../../helpers/urls'; import { collapsedDirFromPath, fileFromPath } from '../../helpers/path'; import { translate } from '../../helpers/l10n'; import { formatMeasure } from '../../helpers/measures'; @@ -171,7 +171,7 @@ export default class SourceViewerHeader extends React.PureComponent { <div className="source-viewer-header-measure"> <span className="source-viewer-header-measure-value"> <Link - to={getIssuesUrl({ resolved: 'false', fileUuids: uuid })} + to={getComponentIssuesUrl(project, { resolved: 'false', fileUuids: uuid })} className="source-viewer-header-external-link" target="_blank"> {measures.issues != null ? formatMeasure(measures.issues, 'SHORT_INT') : 0} diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/Line.js b/server/sonar-web/src/main/js/components/SourceViewer/components/Line.js index 30ba67f06f3..01ce80da9e9 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/components/Line.js +++ b/server/sonar-web/src/main/js/components/SourceViewer/components/Line.js @@ -28,7 +28,6 @@ import LineDuplications from './LineDuplications'; import LineDuplicationBlock from './LineDuplicationBlock'; import LineIssuesIndicator from './LineIssuesIndicator'; import LineCode from './LineCode'; -import { TooltipsContainer } from '../../mixins/tooltips-mixin'; import type { SourceLine } from '../types'; import type { LinearIssueLocation } from '../helpers/indexing'; import type { Issue } from '../../issue/types'; @@ -99,62 +98,60 @@ export default class Line extends React.PureComponent { }); return ( - <TooltipsContainer> - <tr className={className} data-line-number={line.line}> - <LineNumber line={line} onClick={this.props.onClick} /> + <tr className={className} data-line-number={line.line}> + <LineNumber line={line} onClick={this.props.onClick} /> - <LineSCM - line={line} - onClick={this.props.onSCMClick} - previousLine={this.props.previousLine} - /> - - {this.props.displayCoverage && - <LineCoverage line={line} onClick={this.props.onCoverageClick} />} - - {this.props.displayDuplications && - <LineDuplications line={line} onClick={this.props.loadDuplications} />} + <LineSCM + line={line} + onClick={this.props.onSCMClick} + previousLine={this.props.previousLine} + /> - {times(duplicationsCount).map(index => ( - <LineDuplicationBlock - duplicated={duplications.includes(index)} - index={index} - key={index} - line={this.props.line} - onClick={this.props.onDuplicationClick} - /> - ))} + {this.props.displayCoverage && + <LineCoverage line={line} onClick={this.props.onCoverageClick} />} - {this.props.displayIssues && - !this.props.displayAllIssues && - <LineIssuesIndicator - issues={this.props.issues} - line={line} - onClick={this.handleIssuesIndicatorClick} - />} + {this.props.displayDuplications && + <LineDuplications line={line} onClick={this.props.loadDuplications} />} - {this.props.displayFiltered && - <td className="source-meta source-line-filtered-container" data-line-number={line.line}> - <div className="source-line-bar" /> - </td>} + {times(duplicationsCount).map(index => ( + <LineDuplicationBlock + duplicated={duplications.includes(index)} + index={index} + key={index} + line={this.props.line} + onClick={this.props.onDuplicationClick} + /> + ))} - <LineCode - highlightedLocationMessage={this.props.highlightedLocationMessage} - highlightedSymbols={this.props.highlightedSymbols} + {this.props.displayIssues && + !this.props.displayAllIssues && + <LineIssuesIndicator issues={this.props.issues} - issueLocations={this.props.issueLocations} line={line} - onIssueChange={this.props.onIssueChange} - onIssueSelect={this.props.onIssueSelect} - onLocationSelect={this.props.onLocationSelect} - onSymbolClick={this.props.onSymbolClick} - scroll={this.props.scroll} - secondaryIssueLocations={this.props.secondaryIssueLocations} - selectedIssue={this.props.selectedIssue} - showIssues={this.props.openIssues || this.props.displayAllIssues} - /> - </tr> - </TooltipsContainer> + onClick={this.handleIssuesIndicatorClick} + />} + + {this.props.displayFiltered && + <td className="source-meta source-line-filtered-container" data-line-number={line.line}> + <div className="source-line-bar" /> + </td>} + + <LineCode + highlightedLocationMessage={this.props.highlightedLocationMessage} + highlightedSymbols={this.props.highlightedSymbols} + issues={this.props.issues} + issueLocations={this.props.issueLocations} + line={line} + onIssueChange={this.props.onIssueChange} + onIssueSelect={this.props.onIssueSelect} + onLocationSelect={this.props.onLocationSelect} + onSymbolClick={this.props.onSymbolClick} + scroll={this.props.scroll} + secondaryIssueLocations={this.props.secondaryIssueLocations} + selectedIssue={this.props.selectedIssue} + showIssues={this.props.openIssues || this.props.displayAllIssues} + /> + </tr> ); } } diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/LineCoverage.js b/server/sonar-web/src/main/js/components/SourceViewer/components/LineCoverage.js index 73b0da7db15..f069427afe0 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/components/LineCoverage.js +++ b/server/sonar-web/src/main/js/components/SourceViewer/components/LineCoverage.js @@ -19,6 +19,7 @@ */ // @flow import React from 'react'; +import Tooltip from '../../controls/Tooltip'; import { translate } from '../../../helpers/l10n'; import type { SourceLine } from '../types'; @@ -40,21 +41,23 @@ export default class LineCoverage extends React.PureComponent { const className = 'source-meta source-line-coverage' + (line.coverageStatus != null ? ` source-line-${line.coverageStatus}` : ''); - const title = line.coverageStatus != null - ? translate('source_viewer.tooltip', line.coverageStatus) - : undefined; - return ( + const cell = ( <td className={className} data-line-number={line.line} - title={title} - data-placement={line.coverageStatus != null ? 'right' : undefined} - data-toggle={line.coverageStatus != null ? 'tooltip' : undefined} role={line.coverageStatus != null ? 'button' : undefined} tabIndex={line.coverageStatus != null ? 0 : undefined} onClick={line.coverageStatus != null ? this.handleClick : undefined}> <div className="source-line-bar" /> </td> ); + + return line.coverageStatus != null + ? <Tooltip + overlay={translate('source_viewer.tooltip', line.coverageStatus)} + placement="right"> + {cell} + </Tooltip> + : cell; } } diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/LineDuplicationBlock.js b/server/sonar-web/src/main/js/components/SourceViewer/components/LineDuplicationBlock.js index 4edaca1c4c8..ee1373fdde5 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/components/LineDuplicationBlock.js +++ b/server/sonar-web/src/main/js/components/SourceViewer/components/LineDuplicationBlock.js @@ -20,6 +20,7 @@ // @flow import React from 'react'; import classNames from 'classnames'; +import Tooltip from '../../controls/Tooltip'; import { translate } from '../../../helpers/l10n'; import type { SourceLine } from '../types'; @@ -44,20 +45,23 @@ export default class LineDuplicationBlock extends React.PureComponent { 'source-line-duplicated': duplicated }); - return ( + const cell = ( <td key={index} className={className} data-line-number={line.line} data-index={index} - title={duplicated ? translate('source_viewer.tooltip.duplicated_block') : undefined} - data-placement={duplicated ? 'right' : undefined} - data-toggle={duplicated ? 'tooltip' : undefined} role={duplicated ? 'button' : undefined} tabIndex={duplicated ? '0' : undefined} onClick={duplicated ? this.handleClick : undefined}> <div className="source-line-bar" /> </td> ); + + return duplicated + ? <Tooltip overlay={translate('source_viewer.tooltip.duplicated_block')} placement="right"> + {cell} + </Tooltip> + : cell; } } diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/LineDuplications.js b/server/sonar-web/src/main/js/components/SourceViewer/components/LineDuplications.js index 5f0a2936859..85cc046b1c8 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/components/LineDuplications.js +++ b/server/sonar-web/src/main/js/components/SourceViewer/components/LineDuplications.js @@ -20,6 +20,7 @@ // @flow import React from 'react'; import classNames from 'classnames'; +import Tooltip from '../../controls/Tooltip'; import { translate } from '../../../helpers/l10n'; import type { SourceLine } from '../types'; @@ -41,19 +42,21 @@ export default class LineDuplications extends React.PureComponent { const className = classNames('source-meta', 'source-line-duplications', { 'source-line-duplicated': line.duplicated }); - const title = line.duplicated ? translate('source_viewer.tooltip.duplicated_line') : undefined; - return ( + const cell = ( <td className={className} - title={title} - data-placement={line.duplicated ? 'right' : undefined} - data-toggle={line.duplicated ? 'tooltip' : undefined} role={line.duplicated ? 'button' : undefined} tabIndex={line.duplicated ? 0 : undefined} onClick={line.duplicated ? this.handleClick : undefined}> <div className="source-line-bar" /> </td> ); + + return line.duplicated + ? <Tooltip overlay={translate('source_viewer.tooltip.duplicated_line')} placement="right"> + {cell} + </Tooltip> + : cell; } } diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineCoverage-test.js b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineCoverage-test.js index aacd16d7866..b657ec6e493 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineCoverage-test.js +++ b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineCoverage-test.js @@ -27,7 +27,7 @@ it('render covered line', () => { const onClick = jest.fn(); const wrapper = shallow(<LineCoverage line={line} onClick={onClick} />); expect(wrapper).toMatchSnapshot(); - click(wrapper); + click(wrapper.find('[tabIndex]')); expect(onClick).toHaveBeenCalled(); }); @@ -36,7 +36,7 @@ it('render uncovered line', () => { const onClick = jest.fn(); const wrapper = shallow(<LineCoverage line={line} onClick={onClick} />); expect(wrapper).toMatchSnapshot(); - click(wrapper); + click(wrapper.find('[tabIndex]')); expect(onClick).toHaveBeenCalled(); }); diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineDuplicationBlock-test.js b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineDuplicationBlock-test.js index cd0baf595d0..9075badf989 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineDuplicationBlock-test.js +++ b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineDuplicationBlock-test.js @@ -29,7 +29,7 @@ it('render duplicated line', () => { <LineDuplicationBlock index={1} duplicated={true} line={line} onClick={onClick} /> ); expect(wrapper).toMatchSnapshot(); - click(wrapper); + click(wrapper.find('[tabIndex]')); expect(onClick).toHaveBeenCalled(); }); diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineDuplications-test.js b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineDuplications-test.js index b2aa124a64a..52c22486bd0 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineDuplications-test.js +++ b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineDuplications-test.js @@ -27,7 +27,7 @@ it('render duplicated line', () => { const onClick = jest.fn(); const wrapper = shallow(<LineDuplications line={line} onClick={onClick} />); expect(wrapper).toMatchSnapshot(); - click(wrapper); + click(wrapper.find('[tabIndex]')); expect(onClick).toHaveBeenCalled(); }); diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineCoverage-test.js.snap b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineCoverage-test.js.snap index ccf5c4d3c4f..d9fc3840499 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineCoverage-test.js.snap +++ b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineCoverage-test.js.snap @@ -1,16 +1,17 @@ exports[`test render covered line 1`] = ` -<td - className="source-meta source-line-coverage source-line-covered" - data-line-number={3} - data-placement="right" - data-toggle="tooltip" - onClick={[Function]} - role="button" - tabIndex={0} - title="source_viewer.tooltip.covered"> - <div - className="source-line-bar" /> -</td> +<Tooltip + overlay="source_viewer.tooltip.covered" + placement="right"> + <td + className="source-meta source-line-coverage source-line-covered" + data-line-number={3} + onClick={[Function]} + role="button" + tabIndex={0}> + <div + className="source-line-bar" /> + </td> +</Tooltip> `; exports[`test render line with unknown coverage 1`] = ` @@ -23,16 +24,17 @@ exports[`test render line with unknown coverage 1`] = ` `; exports[`test render uncovered line 1`] = ` -<td - className="source-meta source-line-coverage source-line-uncovered" - data-line-number={3} - data-placement="right" - data-toggle="tooltip" - onClick={[Function]} - role="button" - tabIndex={0} - title="source_viewer.tooltip.uncovered"> - <div - className="source-line-bar" /> -</td> +<Tooltip + overlay="source_viewer.tooltip.uncovered" + placement="right"> + <td + className="source-meta source-line-coverage source-line-uncovered" + data-line-number={3} + onClick={[Function]} + role="button" + tabIndex={0}> + <div + className="source-line-bar" /> + </td> +</Tooltip> `; diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineDuplicationBlock-test.js.snap b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineDuplicationBlock-test.js.snap index b94d4b3bc09..ee28d4ae2fb 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineDuplicationBlock-test.js.snap +++ b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineDuplicationBlock-test.js.snap @@ -1,17 +1,18 @@ exports[`test render duplicated line 1`] = ` -<td - className="source-meta source-line-duplications-extra source-line-duplicated" - data-index={1} - data-line-number={3} - data-placement="right" - data-toggle="tooltip" - onClick={[Function]} - role="button" - tabIndex="0" - title="source_viewer.tooltip.duplicated_block"> - <div - className="source-line-bar" /> -</td> +<Tooltip + overlay="source_viewer.tooltip.duplicated_block" + placement="right"> + <td + className="source-meta source-line-duplications-extra source-line-duplicated" + data-index={1} + data-line-number={3} + onClick={[Function]} + role="button" + tabIndex="0"> + <div + className="source-line-bar" /> + </td> +</Tooltip> `; exports[`test render not duplicated line 1`] = ` diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineDuplications-test.js.snap b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineDuplications-test.js.snap index 7e977c88442..ebf65159849 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineDuplications-test.js.snap +++ b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineDuplications-test.js.snap @@ -1,15 +1,16 @@ exports[`test render duplicated line 1`] = ` -<td - className="source-meta source-line-duplications source-line-duplicated" - data-placement="right" - data-toggle="tooltip" - onClick={[Function]} - role="button" - tabIndex={0} - title="source_viewer.tooltip.duplicated_line"> - <div - className="source-line-bar" /> -</td> +<Tooltip + overlay="source_viewer.tooltip.duplicated_line" + placement="right"> + <td + className="source-meta source-line-duplications source-line-duplicated" + onClick={[Function]} + role="button" + tabIndex={0}> + <div + className="source-line-bar" /> + </td> +</Tooltip> `; exports[`test render not duplicated line 1`] = ` diff --git a/server/sonar-web/src/main/js/components/common/EmptySearch.css b/server/sonar-web/src/main/js/components/common/EmptySearch.css new file mode 100644 index 00000000000..2ad32dd05e7 --- /dev/null +++ b/server/sonar-web/src/main/js/components/common/EmptySearch.css @@ -0,0 +1,7 @@ +.empty-search { + padding: 60px 0; + border: 1px solid #e6e6e6; + border-radius: 2px; + color: #777; + text-align: center; +}
\ No newline at end of file diff --git a/server/sonar-web/src/main/js/components/common/EmptySearch.js b/server/sonar-web/src/main/js/components/common/EmptySearch.js index 904a6b2cbad..719a2239c90 100644 --- a/server/sonar-web/src/main/js/components/common/EmptySearch.js +++ b/server/sonar-web/src/main/js/components/common/EmptySearch.js @@ -19,18 +19,11 @@ */ // @flow import React from 'react'; -import { css } from 'glamor'; import { translate } from '../../helpers/l10n'; +import './EmptySearch.css'; const EmptySearch = () => ( - <div - className={css({ - padding: '60px 0', - border: '1px solid #e6e6e6', - borderRadius: 2, - textAlign: 'center', - color: '#777' - })}> + <div className="empty-search"> <h3>{translate('no_results_search')}</h3> <p className="big-spacer-top">{translate('no_results_search.2')}</p> </div> diff --git a/server/sonar-web/src/main/js/components/common/SelectList.js b/server/sonar-web/src/main/js/components/common/SelectList.js index d5695f82a22..cbfadc9307a 100644 --- a/server/sonar-web/src/main/js/components/common/SelectList.js +++ b/server/sonar-web/src/main/js/components/common/SelectList.js @@ -36,6 +36,7 @@ type State = { export default class SelectList extends React.PureComponent { currentKeyScope: string; + previousFilter: Function; previousKeyScope: string; props: Props; state: State; @@ -66,9 +67,18 @@ export default class SelectList extends React.PureComponent { attachShortcuts = () => { this.previousKeyScope = key.getScope(); + this.previousFilter = key.filter; this.currentKeyScope = uniqueId('key-scope'); key.setScope(this.currentKeyScope); + // sometimes there is a *focused* search field next to the SelectList component + // we need to allow shortcuts in this case, but only for the used keys + key.filter = (event: KeyboardEvent & { target: HTMLElement }) => { + const tagName = (event.target || event.srcElement).tagName; + const isInput = tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA'; + return [13, 38, 40].includes(event.keyCode) || !isInput; + }; + key('down', this.currentKeyScope, () => { this.setState(this.selectNextElement); return false; @@ -80,7 +90,7 @@ export default class SelectList extends React.PureComponent { }); key('return', this.currentKeyScope, () => { - if (this.state.active) { + if (this.state.active != null) { this.handleSelect(this.state.active); } return false; @@ -90,6 +100,7 @@ export default class SelectList extends React.PureComponent { detachShortcuts = () => { key.setScope(this.previousKeyScope); key.deleteScope(this.currentKeyScope); + key.filter = this.previousFilter; }; handleSelect = (item: string) => { diff --git a/server/sonar-web/src/main/js/components/controls/Checkbox.js b/server/sonar-web/src/main/js/components/controls/Checkbox.js index 5762cff7c6b..57a5d6969a5 100644 --- a/server/sonar-web/src/main/js/components/controls/Checkbox.js +++ b/server/sonar-web/src/main/js/components/controls/Checkbox.js @@ -44,9 +44,7 @@ export default class Checkbox extends React.PureComponent { } render() { - const className = classNames('icon-checkbox', { - // trick to work with glamor - [this.props.className]: true, + const className = classNames('icon-checkbox', this.props.className, { 'icon-checkbox-checked': this.props.checked, 'icon-checkbox-single': this.props.thirdState }); diff --git a/server/sonar-web/src/main/js/components/issue/Issue.js b/server/sonar-web/src/main/js/components/issue/Issue.js index f45671c6f74..cd44f80e5b7 100644 --- a/server/sonar-web/src/main/js/components/issue/Issue.js +++ b/server/sonar-web/src/main/js/components/issue/Issue.js @@ -19,6 +19,7 @@ */ // @flow import React from 'react'; +import key from 'keymaster'; import IssueView from './IssueView'; import { updateIssue } from './actions'; import { setIssueAssignee } from '../../api/issues'; @@ -86,11 +87,39 @@ export default class BaseIssue extends React.PureComponent { } bindShortcuts() { - document.addEventListener('keypress', this.handleKeyPress); + key('f', 'issues', () => { + this.togglePopup('transition'); + return false; + }); + key('a', 'issues', () => { + this.togglePopup('assign'); + return false; + }); + key('m', 'issues', () => { + this.props.issue.actions.includes('assign_to_me') && this.handleAssignement('_me'); + return false; + }); + key('i', 'issues', () => { + this.togglePopup('set-severity'); + return false; + }); + key('c', 'issues', () => { + this.togglePopup('comment'); + return false; + }); + key('t', 'issues', () => { + this.togglePopup('edit-tags'); + return false; + }); } unbindShortcuts() { - document.removeEventListener('keypress', this.handleKeyPress); + key.unbind('f', 'issues'); + key.unbind('a', 'issues'); + key.unbind('m', 'issues'); + key.unbind('i', 'issues'); + key.unbind('c', 'issues'); + key.unbind('t', 'issues'); } togglePopup = (popupName: string, open?: boolean) => { @@ -118,30 +147,6 @@ export default class BaseIssue extends React.PureComponent { onFail(this.context.store.dispatch)(error); }; - handleKeyPress = (e: Object) => { - const tagName = e.target.tagName.toUpperCase(); - const shouldHandle = tagName !== 'INPUT' && tagName !== 'TEXTAREA' && tagName !== 'BUTTON'; - - if (shouldHandle) { - switch (e.key) { - case 'f': - return this.togglePopup('transition'); - case 'a': - return this.togglePopup('assign'); - case 'm': - return this.props.issue.actions.includes('assign_to_me') && this.handleAssignement('_me'); - case 'p': - return this.togglePopup('plan'); - case 'i': - return this.togglePopup('set-severity'); - case 'c': - return this.togglePopup('comment'); - case 't': - return this.togglePopup('edit-tags'); - } - } - }; - render() { return ( <IssueView 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 90ddd3a73c6..c6ca72d5747 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 @@ -94,7 +94,10 @@ export default function IssueTitleBar(props: Props) { <li className="issue-meta"> {onIssuesPage ? locationsBadge - : <Link onClick={stopPropagation} to={getSingleIssueUrl(issue.key)}> + : <Link + onClick={stopPropagation} + target="_blank" + to={getSingleIssueUrl(issue.key)}> {locationsBadge} </Link>} </li>} @@ -102,6 +105,7 @@ export default function IssueTitleBar(props: Props) { <Link className="js-issue-permalink icon-link" onClick={stopPropagation} + target="_blank" to={getSingleIssueUrl(issue.key)} /> </li> 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 a0f131c9fc5..d2de34493cb 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 @@ -49,11 +49,13 @@ exports[`test should render the titlebar correctly 1`] = ` onClick={[Function]} onlyActiveOnIndex={false} style={Object {}} + target="_blank" to={ Object { "pathname": "/issues", "query": Object { "issues": "AVsae-CQS-9G3txfbFN2", + "open": "AVsae-CQS-9G3txfbFN2", }, } } /> @@ -116,11 +118,13 @@ exports[`test should render the titlebar with the filter 1`] = ` onClick={[Function]} onlyActiveOnIndex={false} style={Object {}} + target="_blank" to={ Object { "pathname": "/issues", "query": Object { "issues": "AVsae-CQS-9G3txfbFN2", + "open": "AVsae-CQS-9G3txfbFN2", }, } } /> 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 df38baef06a..933a43c818e 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 @@ -19,8 +19,6 @@ */ // @flow import React from 'react'; -import classNames from 'classnames'; -import { css } from 'glamor'; import { debounce, map } from 'lodash'; import Avatar from '../../../components/ui/Avatar'; import BubblePopup from '../../../components/common/BubblePopup'; @@ -54,7 +52,6 @@ type State = { }; const LIST_SIZE = 10; -const USER_MARGIN = css({ marginLeft: '24px' }); export default class SetAssigneePopup extends React.PureComponent { defaultUsersArray: Array<User>; @@ -152,9 +149,8 @@ export default class SetAssigneePopup extends React.PureComponent { size={16} />} <span - className={classNames('vertical-middle', { - [USER_MARGIN]: !(user.avatar || user.email) - })}> + className="vertical-middle" + style={{ marginLeft: !user.avatar && !user.email ? 24 : undefined }}> {user.name} </span> </SelectListItem> diff --git a/server/sonar-web/src/main/js/components/issue/popups/SetIssueTagsPopup.js b/server/sonar-web/src/main/js/components/issue/popups/SetIssueTagsPopup.js index 04b1a31ae28..ca155303323 100644 --- a/server/sonar-web/src/main/js/components/issue/popups/SetIssueTagsPopup.js +++ b/server/sonar-web/src/main/js/components/issue/popups/SetIssueTagsPopup.js @@ -37,6 +37,7 @@ type State = { const LIST_SIZE = 10; export default class SetIssueTagsPopup extends React.PureComponent { + mounted: boolean; props: Props; state: State; @@ -47,15 +48,22 @@ export default class SetIssueTagsPopup extends React.PureComponent { } componentDidMount() { + this.mounted = true; this.onSearch(''); } + componentWillUnmount() { + this.mounted = false; + } + onSearch = (query: string) => { searchIssueTags({ q: query || '', ps: Math.min(this.props.selectedTags.length - 1 + LIST_SIZE, 100) }).then((tags: Array<string>) => { - this.setState({ searchResult: tags }); + if (this.mounted) { + this.setState({ searchResult: tags }); + } }, this.props.onFail); }; diff --git a/server/sonar-web/src/main/js/components/layout/Page.js b/server/sonar-web/src/main/js/components/layout/Page.js deleted file mode 100644 index 4ae98a6918f..00000000000 --- a/server/sonar-web/src/main/js/components/layout/Page.js +++ /dev/null @@ -1,42 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -// @flow -import React from 'react'; -import { css } from 'glamor'; - -type Props = { - className?: string, - children?: React.Element<*> -}; - -const styles = css({ - display: 'flex', - alignItems: 'stretch', - width: '100%', - flexGrow: 1 -}); - -export default function Page({ className, children, ...other }: Props) { - return ( - <div className={styles + (className ? ` ${className}` : '')} {...other}> - {children} - </div> - ); -} diff --git a/server/sonar-web/src/main/js/components/layout/PageFilters.js b/server/sonar-web/src/main/js/components/layout/PageFilters.js deleted file mode 100644 index d5f181ffc35..00000000000 --- a/server/sonar-web/src/main/js/components/layout/PageFilters.js +++ /dev/null @@ -1,34 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -// @flow -import React from 'react'; -import { css } from 'glamor'; - -type Props = { - children?: React.Element<*> -}; - -export default function PageSide(props: Props) { - return ( - <div className={css({ width: 260, padding: 20 })}> - {props.children} - </div> - ); -} diff --git a/server/sonar-web/src/main/js/components/layout/PageMain.js b/server/sonar-web/src/main/js/components/layout/PageMain.js deleted file mode 100644 index 85a63058139..00000000000 --- a/server/sonar-web/src/main/js/components/layout/PageMain.js +++ /dev/null @@ -1,34 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -// @flow -import React from 'react'; -import { css } from 'glamor'; - -type Props = { - children?: React.Element<*> -}; - -export default function PageMain(props: Props) { - return ( - <div className={css({ flexGrow: 1, minWidth: 740, padding: 20 })}> - {props.children} - </div> - ); -} diff --git a/server/sonar-web/src/main/js/components/layout/PageMainInner.js b/server/sonar-web/src/main/js/components/layout/PageMainInner.js deleted file mode 100644 index f4c07cd9c41..00000000000 --- a/server/sonar-web/src/main/js/components/layout/PageMainInner.js +++ /dev/null @@ -1,34 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -// @flow -import React from 'react'; -import { css } from 'glamor'; - -type Props = { - children?: React.Element<*> -}; - -export default function PageMainInner(props: Props) { - return ( - <div className={css({ minWidth: 740, maxWidth: 980 })}> - {props.children} - </div> - ); -} diff --git a/server/sonar-web/src/main/js/components/layout/PageSide.js b/server/sonar-web/src/main/js/components/layout/PageSide.js deleted file mode 100644 index a647d83c0c1..00000000000 --- a/server/sonar-web/src/main/js/components/layout/PageSide.js +++ /dev/null @@ -1,73 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -// @flow -import React from 'react'; -import { css, media } from 'glamor'; - -type Props = { - children?: React.Element<*>, - top?: number -}; - -const width = css( - { - width: 'calc(50vw - 360px)' - }, - media('(max-width: 1320px)', { width: 300 }) -); - -const sideStyles = css(width, { - flexGrow: 0, - flexShrink: 0, - backgroundColor: '#f3f3f3' -}); - -const sideStickyStyles = css(width, { - position: 'fixed', - zIndex: 40, - top: 0, - bottom: 0, - left: 0, - borderRight: '1px solid #e6e6e6', - overflowY: 'auto', - overflowX: 'hidden', - backgroundColor: '#f3f3f3' -}); - -const sideInnerStyles = css( - { - width: 300, - marginLeft: 'calc(50vw - 660px)', - backgroundColor: '#f3f3f3' - }, - media('(max-width: 1320px)', { marginLeft: 0 }) -); - -export default function PageSide(props: Props) { - return ( - <div className={sideStyles}> - <div className={`layout-page-side ${sideStickyStyles}`} style={{ top: props.top || 30 }}> - <div className={sideInnerStyles}> - {props.children} - </div> - </div> - </div> - ); -} |