aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/components
diff options
context:
space:
mode:
authorStas Vilchik <stas-vilchik@users.noreply.github.com>2017-04-26 21:09:55 +0200
committerGitHub <noreply@github.com>2017-04-26 21:09:55 +0200
commit3e72937a66a0ca25d8cf2872f614b8edf179c2a8 (patch)
tree2896361963013aed8c7b792f31b064ee0954ef67 /server/sonar-web/src/main/js/components
parent439d0750b2ec547f6829cbeadcb9fed995c6eb05 (diff)
downloadsonarqube-3e72937a66a0ca25d8cf2872f614b8edf179c2a8.tar.gz
sonarqube-3e72937a66a0ca25d8cf2872f614b8edf179c2a8.zip
apply feedback for issues page (#1980)
Diffstat (limited to 'server/sonar-web/src/main/js/components')
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/SourceViewerBase.js2
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/SourceViewerHeader.js4
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/Line.js97
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/LineCoverage.js17
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/LineDuplicationBlock.js12
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/LineDuplications.js13
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineCoverage-test.js4
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineDuplicationBlock-test.js2
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineDuplications-test.js2
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineCoverage-test.js.snap50
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineDuplicationBlock-test.js.snap27
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineDuplications-test.js.snap23
-rw-r--r--server/sonar-web/src/main/js/components/common/EmptySearch.css7
-rw-r--r--server/sonar-web/src/main/js/components/common/EmptySearch.js11
-rw-r--r--server/sonar-web/src/main/js/components/common/SelectList.js13
-rw-r--r--server/sonar-web/src/main/js/components/controls/Checkbox.js4
-rw-r--r--server/sonar-web/src/main/js/components/issue/Issue.js57
-rw-r--r--server/sonar-web/src/main/js/components/issue/components/IssueTitleBar.js6
-rw-r--r--server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTitleBar-test.js.snap4
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/SetAssigneePopup.js8
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/SetIssueTagsPopup.js10
-rw-r--r--server/sonar-web/src/main/js/components/layout/Page.js42
-rw-r--r--server/sonar-web/src/main/js/components/layout/PageFilters.js34
-rw-r--r--server/sonar-web/src/main/js/components/layout/PageMain.js34
-rw-r--r--server/sonar-web/src/main/js/components/layout/PageMainInner.js34
-rw-r--r--server/sonar-web/src/main/js/components/layout/PageSide.js73
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>
- );
-}