aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src
diff options
context:
space:
mode:
authorJeremy Davis <jeremy.davis@sonarsource.com>2019-04-03 14:07:18 +0200
committersonartech <sonartech@sonarsource.com>2019-05-06 11:01:14 +0200
commit570316af531be1479cb7fa504c9ba9777babbd03 (patch)
tree6834118303f4292851c4fd667f71a659527a25d6 /server/sonar-web/src
parenta9cb1fca0c0b6ec2fa1f11a6a9068469fe955173 (diff)
downloadsonarqube-570316af531be1479cb7fa504c9ba9777babbd03.tar.gz
sonarqube-570316af531be1479cb7fa504c9ba9777babbd03.zip
SONAR-11897 Increase sidebar issue box usability
Diffstat (limited to 'server/sonar-web/src')
-rw-r--r--server/sonar-web/src/main/js/app/theme.js3
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/App.tsx24
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/LocationNavigationKeyboardShortcuts.tsx40
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/__tests__/App-test.tsx41
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/__tests__/LocationNavigationKeyboardShortcuts-test.tsx38
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/LocationNavigationKeyboardShortcuts-test.tsx.snap23
-rw-r--r--server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssueBox.tsx39
-rw-r--r--server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssueLocations.tsx28
-rw-r--r--server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssueLocationsNavigator.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssueLocationsNavigatorLocation.tsx23
-rw-r--r--server/sonar-web/src/main/js/apps/issues/conciseIssuesList/CrossFileLocationsNavigator.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/ConciseIssueBox-test.tsx48
-rw-r--r--server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/ConciseIssueLocationsNavigator-test.tsx24
-rw-r--r--server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/ConciseIssueLocationsNavigatorLocation-test.tsx46
-rw-r--r--server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/CrossFileLocationsNavigator-test.tsx8
-rw-r--r--server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssueBox-test.tsx.snap476
-rw-r--r--server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssueLocations-test.tsx.snap42
-rw-r--r--server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssueLocationsNavigator-test.tsx.snap37
-rw-r--r--server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssueLocationsNavigatorLocation-test.tsx.snap89
-rw-r--r--server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/CrossFileLocationsNavigator-test.tsx.snap4
-rw-r--r--server/sonar-web/src/main/js/apps/issues/styles.css40
-rw-r--r--server/sonar-web/src/main/js/helpers/testMocks.ts20
22 files changed, 989 insertions, 112 deletions
diff --git a/server/sonar-web/src/main/js/app/theme.js b/server/sonar-web/src/main/js/app/theme.js
index 62053ce4d4d..b4371f19e0a 100644
--- a/server/sonar-web/src/main/js/app/theme.js
+++ b/server/sonar-web/src/main/js/app/theme.js
@@ -37,6 +37,8 @@ module.exports = {
lineCoverageRed: '#a4030f',
purple: '#9139d4',
+ conciseIssueRed: '#d18582',
+
gray94: '#efefef',
gray80: '#cdcdcd',
gray71: '#b4b4b4',
@@ -45,6 +47,7 @@ module.exports = {
gray40: '#404040',
transparentWhite: 'rgba(255,255,255,0.62)',
+ transparentGray: 'rgba(200, 200, 200, 0.5)',
disableGrayText: '#bbb',
disableGrayBorder: '#ddd',
diff --git a/server/sonar-web/src/main/js/apps/issues/components/App.tsx b/server/sonar-web/src/main/js/apps/issues/components/App.tsx
index 159bff63ae6..0b7307f2ce5 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/App.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/components/App.tsx
@@ -1036,29 +1036,6 @@ export class App extends React.PureComponent<Props, State> {
);
}
- renderShortcutsForLocations() {
- const { openIssue } = this.state;
- if (!openIssue || (!openIssue.secondaryLocations.length && !openIssue.flows.length)) {
- return null;
- }
- const hasSeveralFlows = openIssue.flows.length > 1;
- return (
- <div className="pull-right note">
- <span className="shortcut-button little-spacer-right">alt</span>
- <span className="little-spacer-right">{'+'}</span>
- <span className="shortcut-button little-spacer-right">↑</span>
- <span className="shortcut-button little-spacer-right">↓</span>
- {hasSeveralFlows && (
- <span>
- <span className="shortcut-button little-spacer-right">←</span>
- <span className="shortcut-button little-spacer-right">→</span>
- </span>
- )}
- {translate('issues.to_navigate_issue_locations')}
- </div>
- );
- }
-
renderPage() {
const { checkAll, loading, openIssue, paging } = this.state;
return (
@@ -1134,7 +1111,6 @@ export class App extends React.PureComponent<Props, State> {
selectedIndex={selectedIndex}
/>
)}
- {this.renderShortcutsForLocations()}
</div>
</div>
</div>
diff --git a/server/sonar-web/src/main/js/apps/issues/components/LocationNavigationKeyboardShortcuts.tsx b/server/sonar-web/src/main/js/apps/issues/components/LocationNavigationKeyboardShortcuts.tsx
new file mode 100644
index 00000000000..069ff623c27
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/issues/components/LocationNavigationKeyboardShortcuts.tsx
@@ -0,0 +1,40 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { translate } from '../../../helpers/l10n';
+
+export interface Props {
+ issue: Pick<T.Issue, 'flows' | 'secondaryLocations'> | undefined;
+}
+
+export default function LocationNavigationKeyboardShortcuts({ issue }: Props) {
+ if (!issue || (!issue.secondaryLocations.length && !issue.flows.length)) {
+ return null;
+ }
+ const hasSeveralFlows = issue.flows.length > 1;
+ return (
+ <div className="navigation-keyboard-shortcuts big-spacer-top text-center">
+ <span>
+ alt + ↑ ↓ {hasSeveralFlows && <>←→</>}
+ {translate('issues.to_navigate_issue_locations')}
+ </span>
+ </div>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/issues/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/issues/components/__tests__/App-test.tsx
index da45c568f93..2b473a7f88e 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/__tests__/App-test.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/components/__tests__/App-test.tsx
@@ -24,9 +24,17 @@ import {
mockLoggedInUser,
mockRouter,
mockIssue,
- mockLocation
+ mockLocation,
+ mockEvent
} from '../../../../helpers/testMocks';
import { waitAndUpdate } from '../../../../helpers/testUtils';
+import {
+ enableLocationsNavigator,
+ selectNextLocation,
+ selectPreviousLocation,
+ selectNextFlow,
+ selectPreviousFlow
+} from '../../actions';
const ISSUES = [
{ key: 'foo' } as T.Issue,
@@ -181,6 +189,37 @@ it('should fetch issues until defined', async () => {
expect(result.paging.pageIndex).toBe(3);
});
+describe('keydown event handler', () => {
+ const wrapper = shallowRender();
+ const instance = wrapper.instance();
+ jest.spyOn(instance, 'setState');
+
+ beforeEach(() => {
+ jest.resetAllMocks();
+ });
+
+ it('should handle alt', () => {
+ instance.handleKeyDown(mockEvent({ keyCode: 18 }));
+ expect(instance.setState).toHaveBeenCalledWith(enableLocationsNavigator);
+ });
+ it('should handle alt+↓', () => {
+ instance.handleKeyDown(mockEvent({ altKey: true, keyCode: 40 }));
+ expect(instance.setState).toHaveBeenCalledWith(selectNextLocation);
+ });
+ it('should handle alt+↑', () => {
+ instance.handleKeyDown(mockEvent({ altKey: true, keyCode: 38 }));
+ expect(instance.setState).toHaveBeenCalledWith(selectPreviousLocation);
+ });
+ it('should handle alt+←', () => {
+ instance.handleKeyDown(mockEvent({ altKey: true, keyCode: 37 }));
+ expect(instance.setState).toHaveBeenCalledWith(selectPreviousFlow);
+ });
+ it('should handle alt+→', () => {
+ instance.handleKeyDown(mockEvent({ altKey: true, keyCode: 39 }));
+ expect(instance.setState).toHaveBeenCalledWith(selectNextFlow);
+ });
+});
+
function fetchIssuesMockFactory(keyCount = 0, lineCount = 1) {
return jest.fn().mockImplementation(({ p }: any) =>
Promise.resolve({
diff --git a/server/sonar-web/src/main/js/apps/issues/components/__tests__/LocationNavigationKeyboardShortcuts-test.tsx b/server/sonar-web/src/main/js/apps/issues/components/__tests__/LocationNavigationKeyboardShortcuts-test.tsx
new file mode 100644
index 00000000000..75d37689718
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/issues/components/__tests__/LocationNavigationKeyboardShortcuts-test.tsx
@@ -0,0 +1,38 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import LocationNavigationKeyboardShortcuts, { Props } from '../LocationNavigationKeyboardShortcuts';
+import { mockFlowLocation } from '../../../../helpers/testMocks';
+
+it('should render correctly', () => {
+ expect(shallowRender().type()).toBeNull();
+ expect(shallowRender({ issue: { flows: [], secondaryLocations: [] } }).type()).toBeNull();
+ expect(
+ shallowRender({ issue: { flows: [], secondaryLocations: [mockFlowLocation()] } })
+ ).toMatchSnapshot();
+ expect(
+ shallowRender({ issue: { flows: [[mockFlowLocation()]], secondaryLocations: [] } })
+ ).toMatchSnapshot();
+});
+
+const shallowRender = (props: Partial<Props> = {}) => {
+ return shallow(<LocationNavigationKeyboardShortcuts issue={undefined} {...props} />);
+};
diff --git a/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/LocationNavigationKeyboardShortcuts-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/LocationNavigationKeyboardShortcuts-test.tsx.snap
new file mode 100644
index 00000000000..e99259298a2
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/LocationNavigationKeyboardShortcuts-test.tsx.snap
@@ -0,0 +1,23 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<div
+ className="navigation-keyboard-shortcuts big-spacer-top text-center"
+>
+ <span>
+ alt + ↑ ↓
+ issues.to_navigate_issue_locations
+ </span>
+</div>
+`;
+
+exports[`should render correctly 2`] = `
+<div
+ className="navigation-keyboard-shortcuts big-spacer-top text-center"
+>
+ <span>
+ alt + ↑ ↓
+ issues.to_navigate_issue_locations
+ </span>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssueBox.tsx b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssueBox.tsx
index be24582def8..a2f93116784 100644
--- a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssueBox.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssueBox.tsx
@@ -21,7 +21,7 @@ import * as React from 'react';
import * as classNames from 'classnames';
import ConciseIssueLocations from './ConciseIssueLocations';
import ConciseIssueLocationsNavigator from './ConciseIssueLocationsNavigator';
-import SeverityHelper from '../../../components/shared/SeverityHelper';
+import LocationNavigationKeyboardShortcuts from '../components/LocationNavigationKeyboardShortcuts';
import TypeHelper from '../../../components/shared/TypeHelper';
interface Props {
@@ -51,16 +51,16 @@ export default class ConciseIssueBox extends React.PureComponent<Props> {
}
}
+ handleClick = (event: React.MouseEvent<HTMLElement>) => {
+ event.preventDefault();
+ this.props.onClick(this.props.issue.key);
+ };
+
handleScroll = () => {
const { selectedFlowIndex } = this.props;
const { flows, secondaryLocations } = this.props.issue;
- const locations =
- selectedFlowIndex !== undefined
- ? flows[selectedFlowIndex]
- : flows.length > 0
- ? flows[0]
- : secondaryLocations;
+ const locations = flows.length > 0 ? flows[selectedFlowIndex || 0] : secondaryLocations;
if (!locations || locations.length < 15) {
// if there are no locations, or there are just few
@@ -72,11 +72,6 @@ export default class ConciseIssueBox extends React.PureComponent<Props> {
}
};
- handleClick = (event: React.MouseEvent<HTMLElement>) => {
- event.preventDefault();
- this.props.onClick(this.props.issue.key);
- };
-
render() {
const { issue, selected } = this.props;
@@ -100,8 +95,7 @@ export default class ConciseIssueBox extends React.PureComponent<Props> {
{issue.message}
</div>
<div className="concise-issue-box-attributes">
- <TypeHelper type={issue.type} />
- <SeverityHelper className="big-spacer-left" severity={issue.severity} />
+ <TypeHelper className="display-block little-spacer-right" type={issue.type} />
<ConciseIssueLocations
issue={issue}
onFlowSelect={this.props.onFlowSelect}
@@ -109,13 +103,16 @@ export default class ConciseIssueBox extends React.PureComponent<Props> {
/>
</div>
{selected && (
- <ConciseIssueLocationsNavigator
- issue={issue}
- onLocationSelect={this.props.onLocationSelect}
- scroll={this.props.scroll}
- selectedFlowIndex={this.props.selectedFlowIndex}
- selectedLocationIndex={this.props.selectedLocationIndex}
- />
+ <>
+ <ConciseIssueLocationsNavigator
+ issue={issue}
+ onLocationSelect={this.props.onLocationSelect}
+ scroll={this.props.scroll}
+ selectedFlowIndex={this.props.selectedFlowIndex}
+ selectedLocationIndex={this.props.selectedLocationIndex}
+ />
+ <LocationNavigationKeyboardShortcuts issue={issue} />
+ </>
)}
</div>
);
diff --git a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssueLocations.tsx b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssueLocations.tsx
index cb9675a30cc..ff99a7558f7 100644
--- a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssueLocations.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssueLocations.tsx
@@ -19,6 +19,7 @@
*/
import * as React from 'react';
import ConciseIssueLocationBadge from './ConciseIssueLocationBadge';
+import { Button } from '../../../components/ui/buttons';
interface Props {
issue: Pick<T.Issue, 'flows' | 'secondaryLocations'>;
@@ -30,21 +31,22 @@ interface State {
collapsed: boolean;
}
-const LIMIT = 3;
+const LIMIT = 8;
export default class ConciseIssueLocations extends React.PureComponent<Props, State> {
state: State = { collapsed: true };
- handleExpandClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
- event.preventDefault();
+ handleExpandClick = () => {
this.setState({ collapsed: false });
};
renderExpandButton() {
return (
- <a className="little-spacer-left link-no-underline" href="#" onClick={this.handleExpandClick}>
+ <Button
+ className="concise-issue-expand location-index link-no-underline"
+ onClick={this.handleExpandClick}>
...
- </a>
+ </Button>
);
}
@@ -74,13 +76,15 @@ export default class ConciseIssueLocations extends React.PureComponent<Props, St
);
});
- return this.state.collapsed ? (
- <div className="concise-issue-locations pull-right">
- {badges.slice(0, LIMIT)}
- {badges.length > LIMIT && this.renderExpandButton()}
- </div>
- ) : (
- <div className="concise-issue-locations spacer-top">{badges}</div>
+ if (!this.state.collapsed || badges.length <= LIMIT) {
+ return badges;
+ }
+
+ return (
+ <>
+ {badges.slice(0, LIMIT - 1)}
+ {this.renderExpandButton()}
+ </>
);
}
}
diff --git a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssueLocationsNavigator.tsx b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssueLocationsNavigator.tsx
index eb1b76f8356..0def805f2fa 100644
--- a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssueLocationsNavigator.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssueLocationsNavigator.tsx
@@ -24,7 +24,7 @@ import CrossFileLocationsNavigator from './CrossFileLocationsNavigator';
import { getLocations } from '../utils';
interface Props {
- issue: Pick<T.Issue, 'component' | 'key' | 'flows' | 'secondaryLocations'>;
+ issue: Pick<T.Issue, 'component' | 'key' | 'flows' | 'secondaryLocations' | 'type'>;
onLocationSelect: (index: number) => void;
scroll: (element: Element) => void;
selectedFlowIndex: number | undefined;
@@ -61,11 +61,13 @@ export default class ConciseIssueLocationsNavigator extends React.PureComponent<
{locations.map((location, index) => (
<ConciseIssueLocationsNavigatorLocation
index={index}
+ issueType={this.props.issue.type}
key={index}
message={location.msg}
onClick={this.props.onLocationSelect}
scroll={this.props.scroll}
selected={index === this.props.selectedLocationIndex}
+ totalCount={locations.length}
/>
))}
</div>
diff --git a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssueLocationsNavigatorLocation.tsx b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssueLocationsNavigatorLocation.tsx
index ce2cb55cedd..af3539ecc15 100644
--- a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssueLocationsNavigatorLocation.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssueLocationsNavigatorLocation.tsx
@@ -23,10 +23,12 @@ import LocationMessage from '../../../components/common/LocationMessage';
interface Props {
index: number;
+ issueType: T.IssueType;
message: string | undefined;
onClick: (index: number) => void;
scroll: (element: Element) => void;
selected: boolean;
+ totalCount: number;
}
export default class ConciseIssueLocationsNavigatorLocation extends React.PureComponent<Props> {
@@ -49,15 +51,32 @@ export default class ConciseIssueLocationsNavigatorLocation extends React.PureCo
this.props.onClick(this.props.index);
};
+ prefixMessage(index: number, message = '', totalCount: number) {
+ switch (index) {
+ case 0:
+ return 'source: ' + message;
+ case totalCount - 1:
+ return 'sink: ' + message;
+ default:
+ return message;
+ }
+ }
+
render() {
+ const { index, issueType, message, selected, totalCount } = this.props;
+
return (
<div className="little-spacer-top" ref={node => (this.node = node)}>
<a
className="concise-issue-locations-navigator-location"
href="#"
onClick={this.handleClick}>
- <LocationIndex selected={this.props.selected}>{this.props.index + 1}</LocationIndex>
- <LocationMessage selected={this.props.selected}>{this.props.message}</LocationMessage>
+ <LocationIndex selected={selected}>{index + 1}</LocationIndex>
+ <LocationMessage selected={selected}>
+ {issueType === 'VULNERABILITY'
+ ? this.prefixMessage(index, message, totalCount)
+ : message}
+ </LocationMessage>
</a>
</div>
);
diff --git a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/CrossFileLocationsNavigator.tsx b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/CrossFileLocationsNavigator.tsx
index 912a94f1aa4..268c9f3b51a 100644
--- a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/CrossFileLocationsNavigator.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/CrossFileLocationsNavigator.tsx
@@ -23,7 +23,7 @@ import { translateWithParameters } from '../../../helpers/l10n';
import { collapsePath } from '../../../helpers/path';
interface Props {
- issue: Pick<T.Issue, 'key'>;
+ issue: Pick<T.Issue, 'key' | 'type'>;
locations: T.FlowLocation[];
onLocationSelect: (index: number) => void;
scroll: (element: Element) => void;
@@ -111,11 +111,13 @@ export default class CrossFileLocationsNavigator extends React.PureComponent<Pro
return (
<ConciseIssueLocationsNavigatorLocation
index={index}
+ issueType={this.props.issue.type}
key={index}
message={message}
onClick={this.props.onLocationSelect}
scroll={this.props.scroll}
selected={index === this.props.selectedLocationIndex}
+ totalCount={this.props.locations.length}
/>
);
};
diff --git a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/ConciseIssueBox-test.tsx b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/ConciseIssueBox-test.tsx
new file mode 100644
index 00000000000..b7f18bfdfc5
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/ConciseIssueBox-test.tsx
@@ -0,0 +1,48 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import ConciseIssueBox from '../ConciseIssueBox';
+import { mockIssue } from '../../../../helpers/testMocks';
+import { waitAndUpdate } from '../../../../helpers/testUtils';
+
+it('should render correctly', async () => {
+ const wrapper = shallowRender();
+ await waitAndUpdate(wrapper);
+ expect(wrapper).toMatchSnapshot();
+
+ expect(shallowRender({ issue: mockIssue(true) })).toMatchSnapshot();
+});
+
+const shallowRender = (props: Partial<ConciseIssueBox['props']> = {}) => {
+ return shallow(
+ <ConciseIssueBox
+ issue={mockIssue()}
+ onClick={jest.fn()}
+ onFlowSelect={jest.fn()}
+ onLocationSelect={jest.fn()}
+ scroll={jest.fn()}
+ selected={true}
+ selectedFlowIndex={0}
+ selectedLocationIndex={0}
+ {...props}
+ />
+ );
+};
diff --git a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/ConciseIssueLocationsNavigator-test.tsx b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/ConciseIssueLocationsNavigator-test.tsx
index 0e037e9e912..28da9ead72b 100644
--- a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/ConciseIssueLocationsNavigator-test.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/ConciseIssueLocationsNavigator-test.tsx
@@ -20,6 +20,7 @@
import * as React from 'react';
import { shallow } from 'enzyme';
import ConciseIssueLocationsNavigator from '../ConciseIssueLocationsNavigator';
+import { mockIssue } from '../../../../helpers/testMocks';
const location1: T.FlowLocation = {
component: 'foo',
@@ -43,12 +44,12 @@ const location3: T.FlowLocation = {
};
it('should render secondary locations in the same file', () => {
- const issue = {
+ const issue = mockIssue(false, {
component: 'foo',
key: '',
flows: [],
secondaryLocations: [location1, location2]
- };
+ });
expect(
shallow(
<ConciseIssueLocationsNavigator
@@ -63,12 +64,12 @@ it('should render secondary locations in the same file', () => {
});
it('should render flow locations in the same file', () => {
- const issue = {
+ const issue = mockIssue(false, {
component: 'foo',
key: '',
flows: [[location1, location2]],
secondaryLocations: []
- };
+ });
expect(
shallow(
<ConciseIssueLocationsNavigator
@@ -83,12 +84,12 @@ it('should render flow locations in the same file', () => {
});
it('should render selected flow locations in the same file', () => {
- const issue = {
+ const issue = mockIssue(false, {
component: 'foo',
key: '',
flows: [[location1, location2]],
secondaryLocations: [location1]
- };
+ });
expect(
shallow(
<ConciseIssueLocationsNavigator
@@ -103,12 +104,12 @@ it('should render selected flow locations in the same file', () => {
});
it('should render flow locations in different file', () => {
- const issue = {
+ const issue = mockIssue(false, {
component: 'foo',
key: '',
flows: [[location1, location3]],
secondaryLocations: []
- };
+ });
expect(
shallow(
<ConciseIssueLocationsNavigator
@@ -123,7 +124,12 @@ it('should render flow locations in different file', () => {
});
it('should not render locations', () => {
- const issue = { component: 'foo', key: '', flows: [], secondaryLocations: [] };
+ const issue = mockIssue(false, {
+ component: 'foo',
+ key: '',
+ flows: [],
+ secondaryLocations: []
+ });
const wrapper = shallow(
<ConciseIssueLocationsNavigator
issue={issue}
diff --git a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/ConciseIssueLocationsNavigatorLocation-test.tsx b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/ConciseIssueLocationsNavigatorLocation-test.tsx
new file mode 100644
index 00000000000..e65843ee726
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/ConciseIssueLocationsNavigatorLocation-test.tsx
@@ -0,0 +1,46 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import ConciseIssueLocationsNavigatorLocation from '../ConciseIssueLocationsNavigatorLocation';
+
+it('should render correctly', () => {
+ expect(shallowRender()).toMatchSnapshot();
+});
+it('should render vulnerabilities correctly', () => {
+ expect(shallowRender({ index: 0, issueType: 'VULNERABILITY', totalCount: 4 })).toMatchSnapshot();
+ expect(shallowRender({ index: 1, issueType: 'VULNERABILITY', totalCount: 4 })).toMatchSnapshot();
+ expect(shallowRender({ index: 3, issueType: 'VULNERABILITY', totalCount: 4 })).toMatchSnapshot();
+});
+
+const shallowRender = (props: Partial<ConciseIssueLocationsNavigatorLocation['props']> = {}) => {
+ return shallow(
+ <ConciseIssueLocationsNavigatorLocation
+ index={0}
+ issueType={'BUG'}
+ message={''}
+ onClick={jest.fn()}
+ scroll={jest.fn()}
+ selected={true}
+ totalCount={5}
+ {...props}
+ />
+ );
+};
diff --git a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/CrossFileLocationsNavigator-test.tsx b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/CrossFileLocationsNavigator-test.tsx
index bf4e4f9a698..2705d36ae5f 100644
--- a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/CrossFileLocationsNavigator-test.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/CrossFileLocationsNavigator-test.tsx
@@ -46,7 +46,7 @@ const location3: T.FlowLocation = {
it('should render', () => {
const wrapper = shallow(
<CrossFileLocationsNavigator
- issue={{ key: 'abcd' }}
+ issue={{ key: 'abcd', type: 'BUG' }}
locations={[location1, location2, location3]}
onLocationSelect={jest.fn()}
scroll={jest.fn()}
@@ -63,7 +63,7 @@ it('should render', () => {
it('should render all locations', () => {
const wrapper = shallow(
<CrossFileLocationsNavigator
- issue={{ key: 'abcd' }}
+ issue={{ key: 'abcd', type: 'BUG' }}
locations={[location1, location2]}
onLocationSelect={jest.fn()}
scroll={jest.fn()}
@@ -76,7 +76,7 @@ it('should render all locations', () => {
it('should expand all locations', () => {
const wrapper = shallow(
<CrossFileLocationsNavigator
- issue={{ key: 'abcd' }}
+ issue={{ key: 'abcd', type: 'BUG' }}
locations={[location1, location2, location3]}
onLocationSelect={jest.fn()}
scroll={jest.fn()}
@@ -92,7 +92,7 @@ it('should expand all locations', () => {
it('should collapse locations when issue changes', () => {
const wrapper = shallow(
<CrossFileLocationsNavigator
- issue={{ key: 'abcd' }}
+ issue={{ key: 'abcd', type: 'BUG' }}
locations={[location1, location2, location3]}
onLocationSelect={jest.fn()}
scroll={jest.fn()}
diff --git a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssueBox-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssueBox-test.tsx.snap
new file mode 100644
index 00000000000..4acba10db82
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssueBox-test.tsx.snap
@@ -0,0 +1,476 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<div
+ className="concise-issue-box clearfix selected"
+>
+ <div
+ className="concise-issue-box-message"
+ onClick={[Function]}
+ role="listitem"
+ tabIndex={0}
+ >
+ Reduce the number of conditional operators (4) used in the expression
+ </div>
+ <div
+ className="concise-issue-box-attributes"
+ >
+ <TypeHelper
+ className="display-block little-spacer-right"
+ type="BUG"
+ />
+ <ConciseIssueLocations
+ issue={
+ Object {
+ "actions": Array [],
+ "component": "main.js",
+ "componentLongName": "main.js",
+ "componentQualifier": "FIL",
+ "componentUuid": "foo1234",
+ "creationDate": "2017-03-01T09:36:01+0100",
+ "flows": Array [],
+ "fromHotspot": false,
+ "key": "AVsae-CQS-9G3txfbFN2",
+ "line": 25,
+ "message": "Reduce the number of conditional operators (4) used in the expression",
+ "organization": "myorg",
+ "project": "myproject",
+ "projectKey": "foo",
+ "projectName": "Foo",
+ "projectOrganization": "org",
+ "rule": "javascript:S1067",
+ "ruleName": "foo",
+ "secondaryLocations": Array [],
+ "severity": "MAJOR",
+ "status": "OPEN",
+ "textRange": Object {
+ "endLine": 26,
+ "endOffset": 15,
+ "startLine": 25,
+ "startOffset": 0,
+ },
+ "transitions": Array [],
+ "type": "BUG",
+ }
+ }
+ onFlowSelect={[MockFunction]}
+ selectedFlowIndex={0}
+ />
+ </div>
+ <ConciseIssueLocationsNavigator
+ issue={
+ Object {
+ "actions": Array [],
+ "component": "main.js",
+ "componentLongName": "main.js",
+ "componentQualifier": "FIL",
+ "componentUuid": "foo1234",
+ "creationDate": "2017-03-01T09:36:01+0100",
+ "flows": Array [],
+ "fromHotspot": false,
+ "key": "AVsae-CQS-9G3txfbFN2",
+ "line": 25,
+ "message": "Reduce the number of conditional operators (4) used in the expression",
+ "organization": "myorg",
+ "project": "myproject",
+ "projectKey": "foo",
+ "projectName": "Foo",
+ "projectOrganization": "org",
+ "rule": "javascript:S1067",
+ "ruleName": "foo",
+ "secondaryLocations": Array [],
+ "severity": "MAJOR",
+ "status": "OPEN",
+ "textRange": Object {
+ "endLine": 26,
+ "endOffset": 15,
+ "startLine": 25,
+ "startOffset": 0,
+ },
+ "transitions": Array [],
+ "type": "BUG",
+ }
+ }
+ onLocationSelect={[MockFunction]}
+ scroll={[MockFunction]}
+ selectedFlowIndex={0}
+ selectedLocationIndex={0}
+ />
+ <LocationNavigationKeyboardShortcuts
+ issue={
+ Object {
+ "actions": Array [],
+ "component": "main.js",
+ "componentLongName": "main.js",
+ "componentQualifier": "FIL",
+ "componentUuid": "foo1234",
+ "creationDate": "2017-03-01T09:36:01+0100",
+ "flows": Array [],
+ "fromHotspot": false,
+ "key": "AVsae-CQS-9G3txfbFN2",
+ "line": 25,
+ "message": "Reduce the number of conditional operators (4) used in the expression",
+ "organization": "myorg",
+ "project": "myproject",
+ "projectKey": "foo",
+ "projectName": "Foo",
+ "projectOrganization": "org",
+ "rule": "javascript:S1067",
+ "ruleName": "foo",
+ "secondaryLocations": Array [],
+ "severity": "MAJOR",
+ "status": "OPEN",
+ "textRange": Object {
+ "endLine": 26,
+ "endOffset": 15,
+ "startLine": 25,
+ "startOffset": 0,
+ },
+ "transitions": Array [],
+ "type": "BUG",
+ }
+ }
+ />
+</div>
+`;
+
+exports[`should render correctly 2`] = `
+<div
+ className="concise-issue-box clearfix selected"
+>
+ <div
+ className="concise-issue-box-message"
+ onClick={[Function]}
+ role="listitem"
+ tabIndex={0}
+ >
+ Reduce the number of conditional operators (4) used in the expression
+ </div>
+ <div
+ className="concise-issue-box-attributes"
+ >
+ <TypeHelper
+ className="display-block little-spacer-right"
+ type="BUG"
+ />
+ <ConciseIssueLocations
+ issue={
+ Object {
+ "actions": Array [],
+ "component": "main.js",
+ "componentLongName": "main.js",
+ "componentQualifier": "FIL",
+ "componentUuid": "foo1234",
+ "creationDate": "2017-03-01T09:36:01+0100",
+ "flows": Array [
+ Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ ],
+ "fromHotspot": false,
+ "key": "AVsae-CQS-9G3txfbFN2",
+ "line": 25,
+ "message": "Reduce the number of conditional operators (4) used in the expression",
+ "organization": "myorg",
+ "project": "myproject",
+ "projectKey": "foo",
+ "projectName": "Foo",
+ "projectOrganization": "org",
+ "rule": "javascript:S1067",
+ "ruleName": "foo",
+ "secondaryLocations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ "severity": "MAJOR",
+ "status": "OPEN",
+ "textRange": Object {
+ "endLine": 26,
+ "endOffset": 15,
+ "startLine": 25,
+ "startOffset": 0,
+ },
+ "transitions": Array [],
+ "type": "BUG",
+ }
+ }
+ onFlowSelect={[MockFunction]}
+ selectedFlowIndex={0}
+ />
+ </div>
+ <ConciseIssueLocationsNavigator
+ issue={
+ Object {
+ "actions": Array [],
+ "component": "main.js",
+ "componentLongName": "main.js",
+ "componentQualifier": "FIL",
+ "componentUuid": "foo1234",
+ "creationDate": "2017-03-01T09:36:01+0100",
+ "flows": Array [
+ Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ ],
+ "fromHotspot": false,
+ "key": "AVsae-CQS-9G3txfbFN2",
+ "line": 25,
+ "message": "Reduce the number of conditional operators (4) used in the expression",
+ "organization": "myorg",
+ "project": "myproject",
+ "projectKey": "foo",
+ "projectName": "Foo",
+ "projectOrganization": "org",
+ "rule": "javascript:S1067",
+ "ruleName": "foo",
+ "secondaryLocations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ "severity": "MAJOR",
+ "status": "OPEN",
+ "textRange": Object {
+ "endLine": 26,
+ "endOffset": 15,
+ "startLine": 25,
+ "startOffset": 0,
+ },
+ "transitions": Array [],
+ "type": "BUG",
+ }
+ }
+ onLocationSelect={[MockFunction]}
+ scroll={[MockFunction]}
+ selectedFlowIndex={0}
+ selectedLocationIndex={0}
+ />
+ <LocationNavigationKeyboardShortcuts
+ issue={
+ Object {
+ "actions": Array [],
+ "component": "main.js",
+ "componentLongName": "main.js",
+ "componentQualifier": "FIL",
+ "componentUuid": "foo1234",
+ "creationDate": "2017-03-01T09:36:01+0100",
+ "flows": Array [
+ Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ ],
+ "fromHotspot": false,
+ "key": "AVsae-CQS-9G3txfbFN2",
+ "line": 25,
+ "message": "Reduce the number of conditional operators (4) used in the expression",
+ "organization": "myorg",
+ "project": "myproject",
+ "projectKey": "foo",
+ "projectName": "Foo",
+ "projectOrganization": "org",
+ "rule": "javascript:S1067",
+ "ruleName": "foo",
+ "secondaryLocations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ "severity": "MAJOR",
+ "status": "OPEN",
+ "textRange": Object {
+ "endLine": 26,
+ "endOffset": 15,
+ "startLine": 25,
+ "startOffset": 0,
+ },
+ "transitions": Array [],
+ "type": "BUG",
+ }
+ }
+ />
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssueLocations-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssueLocations-test.tsx.snap
index f9973b06e0c..cd78e9dfdaf 100644
--- a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssueLocations-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssueLocations-test.tsx.snap
@@ -1,51 +1,41 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`should render one flow 1`] = `
-<div
- className="concise-issue-locations pull-right"
->
- <ConciseIssueLocationBadge
- count={3}
- key="0"
- onClick={[Function]}
- selected={false}
- />
-</div>
+<ConciseIssueLocationBadge
+ count={3}
+ key="0"
+ onClick={[Function]}
+ selected={false}
+/>
`;
exports[`should render secondary locations 1`] = `
-<div
- className="concise-issue-locations pull-right"
->
- <ConciseIssueLocationBadge
- count={3}
- key="-1"
- selected={true}
- />
-</div>
+<ConciseIssueLocationBadge
+ count={3}
+ key="-1"
+ selected={true}
+/>
`;
exports[`should render several flows 1`] = `
-<div
- className="concise-issue-locations pull-right"
->
+Array [
<ConciseIssueLocationBadge
count={3}
key="0"
onClick={[Function]}
selected={false}
- />
+ />,
<ConciseIssueLocationBadge
count={2}
key="1"
onClick={[Function]}
selected={false}
- />
+ />,
<ConciseIssueLocationBadge
count={3}
key="2"
onClick={[Function]}
selected={false}
- />
-</div>
+ />,
+]
`;
diff --git a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssueLocationsNavigator-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssueLocationsNavigator-test.tsx.snap
index f2512176f96..6a9dc041d25 100644
--- a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssueLocationsNavigator-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssueLocationsNavigator-test.tsx.snap
@@ -4,7 +4,12 @@ exports[`should render flow locations in different file 1`] = `
<CrossFileLocationsNavigator
issue={
Object {
+ "actions": Array [],
"component": "foo",
+ "componentLongName": "main.js",
+ "componentQualifier": "FIL",
+ "componentUuid": "foo1234",
+ "creationDate": "2017-03-01T09:36:01+0100",
"flows": Array [
Array [
Object {
@@ -31,8 +36,28 @@ exports[`should render flow locations in different file 1`] = `
},
],
],
+ "fromHotspot": false,
"key": "",
+ "line": 25,
+ "message": "Reduce the number of conditional operators (4) used in the expression",
+ "organization": "myorg",
+ "project": "myproject",
+ "projectKey": "foo",
+ "projectName": "Foo",
+ "projectOrganization": "org",
+ "rule": "javascript:S1067",
+ "ruleName": "foo",
"secondaryLocations": Array [],
+ "severity": "MAJOR",
+ "status": "OPEN",
+ "textRange": Object {
+ "endLine": 26,
+ "endOffset": 15,
+ "startLine": 25,
+ "startOffset": 0,
+ },
+ "transitions": Array [],
+ "type": "BUG",
}
}
locations={
@@ -72,19 +97,23 @@ exports[`should render flow locations in the same file 1`] = `
>
<ConciseIssueLocationsNavigatorLocation
index={0}
+ issueType="BUG"
key="0"
message="Do not use foo"
onClick={[MockFunction]}
scroll={[MockFunction]}
selected={false}
+ totalCount={2}
/>
<ConciseIssueLocationsNavigatorLocation
index={1}
+ issueType="BUG"
key="1"
message="Do not use foo"
onClick={[MockFunction]}
scroll={[MockFunction]}
selected={false}
+ totalCount={2}
/>
</div>
`;
@@ -95,19 +124,23 @@ exports[`should render secondary locations in the same file 1`] = `
>
<ConciseIssueLocationsNavigatorLocation
index={0}
+ issueType="BUG"
key="0"
message="Do not use foo"
onClick={[MockFunction]}
scroll={[MockFunction]}
selected={false}
+ totalCount={2}
/>
<ConciseIssueLocationsNavigatorLocation
index={1}
+ issueType="BUG"
key="1"
message="Do not use foo"
onClick={[MockFunction]}
scroll={[MockFunction]}
selected={false}
+ totalCount={2}
/>
</div>
`;
@@ -118,19 +151,23 @@ exports[`should render selected flow locations in the same file 1`] = `
>
<ConciseIssueLocationsNavigatorLocation
index={0}
+ issueType="BUG"
key="0"
message="Do not use foo"
onClick={[MockFunction]}
scroll={[MockFunction]}
selected={false}
+ totalCount={2}
/>
<ConciseIssueLocationsNavigatorLocation
index={1}
+ issueType="BUG"
key="1"
message="Do not use foo"
onClick={[MockFunction]}
scroll={[MockFunction]}
selected={false}
+ totalCount={2}
/>
</div>
`;
diff --git a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssueLocationsNavigatorLocation-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssueLocationsNavigatorLocation-test.tsx.snap
new file mode 100644
index 00000000000..9ca42296767
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssueLocationsNavigatorLocation-test.tsx.snap
@@ -0,0 +1,89 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<div
+ className="little-spacer-top"
+>
+ <a
+ className="concise-issue-locations-navigator-location"
+ href="#"
+ onClick={[Function]}
+ >
+ <LocationIndex
+ selected={true}
+ >
+ 1
+ </LocationIndex>
+ <LocationMessage
+ selected={true}
+ />
+ </a>
+</div>
+`;
+
+exports[`should render vulnerabilities correctly 1`] = `
+<div
+ className="little-spacer-top"
+>
+ <a
+ className="concise-issue-locations-navigator-location"
+ href="#"
+ onClick={[Function]}
+ >
+ <LocationIndex
+ selected={true}
+ >
+ 1
+ </LocationIndex>
+ <LocationMessage
+ selected={true}
+ >
+ source:
+ </LocationMessage>
+ </a>
+</div>
+`;
+
+exports[`should render vulnerabilities correctly 2`] = `
+<div
+ className="little-spacer-top"
+>
+ <a
+ className="concise-issue-locations-navigator-location"
+ href="#"
+ onClick={[Function]}
+ >
+ <LocationIndex
+ selected={true}
+ >
+ 2
+ </LocationIndex>
+ <LocationMessage
+ selected={true}
+ />
+ </a>
+</div>
+`;
+
+exports[`should render vulnerabilities correctly 3`] = `
+<div
+ className="little-spacer-top"
+>
+ <a
+ className="concise-issue-locations-navigator-location"
+ href="#"
+ onClick={[Function]}
+ >
+ <LocationIndex
+ selected={true}
+ >
+ 4
+ </LocationIndex>
+ <LocationMessage
+ selected={true}
+ >
+ sink:
+ </LocationMessage>
+ </a>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/CrossFileLocationsNavigator-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/CrossFileLocationsNavigator-test.tsx.snap
index e1b734ee3e5..86cb38f4c5f 100644
--- a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/CrossFileLocationsNavigator-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/CrossFileLocationsNavigator-test.tsx.snap
@@ -21,11 +21,13 @@ exports[`should render 1`] = `
>
<ConciseIssueLocationsNavigatorLocation
index={0}
+ issueType="BUG"
key="0"
message="Do not use foo"
onClick={[MockFunction]}
scroll={[MockFunction]}
selected={false}
+ totalCount={3}
/>
</div>
</div>
@@ -64,11 +66,13 @@ exports[`should render 1`] = `
>
<ConciseIssueLocationsNavigatorLocation
index={2}
+ issueType="BUG"
key="2"
message="Do not use bar"
onClick={[MockFunction]}
scroll={[MockFunction]}
selected={false}
+ totalCount={3}
/>
</div>
</div>
diff --git a/server/sonar-web/src/main/js/apps/issues/styles.css b/server/sonar-web/src/main/js/apps/issues/styles.css
index a6687a9e5b0..1f2dba6d6a5 100644
--- a/server/sonar-web/src/main/js/apps/issues/styles.css
+++ b/server/sonar-web/src/main/js/apps/issues/styles.css
@@ -99,6 +99,10 @@
margin-top: var(--gridSize);
line-height: 16px;
font-size: var(--smallFontSize);
+ display: flex;
+ align-items: flex-start;
+ flex-wrap: wrap;
+ justify-content: flex-start;
}
.concise-issue-box:not(.selected) .location-index {
@@ -106,13 +110,30 @@
}
.concise-issue-locations {
- margin-right: -4px;
+ display: inline-block;
margin-bottom: -4px;
+ margin-left: var(--gridSize);
}
-.concise-issue-locations .location-index {
- margin-right: 4px;
+.concise-issue-box-attributes > .location-index {
margin-bottom: 4px;
+ margin-right: 4px;
+}
+
+.concise-issue-box-attributes > .concise-issue-expand {
+ background-color: transparent;
+ border: 1px solid var(--conciseIssueRed);
+ height: 16px;
+ color: var(--conciseIssueRed);
+ font-weight: bold;
+ font-size: 16px;
+ line-height: 8px;
+ padding-bottom: 6px;
+}
+
+.concise-issue-box-attributes > .concise-issue-expand:hover {
+ background-color: var(--conciseIssueRed);
+ color: white;
}
.concise-issue-locations-navigator-location {
@@ -139,7 +160,7 @@
top: 13px;
bottom: calc(-2 * var(--gridSize));
left: 4px;
- border-left: 1px dotted #d18582;
+ border-left: 1px dotted var(--conciseIssueRed);
content: '';
}
@@ -162,7 +183,7 @@
display: inline-block;
width: calc(1px + var(--gridSize));
height: calc(1px + var(--gridSize));
- border: 1px solid #d18582;
+ border: 1px solid var(--conciseIssueRed);
border-radius: 100%;
box-sizing: border-box;
background-color: var(--issueBgColor);
@@ -278,3 +299,12 @@
.bulk-change-radio-button:hover {
background-color: var(--barBackgroundColor);
}
+
+.navigation-keyboard-shortcuts > span {
+ background-color: var(--transparentGray);
+ border-radius: 16px;
+ display: inline-block;
+ font-size: var(--smallFontSize);
+ height: 16px;
+ padding: calc(var(--gridSize) / 2) var(--gridSize);
+}
diff --git a/server/sonar-web/src/main/js/helpers/testMocks.ts b/server/sonar-web/src/main/js/helpers/testMocks.ts
index dd21ec1fec2..cc30abeb85e 100644
--- a/server/sonar-web/src/main/js/helpers/testMocks.ts
+++ b/server/sonar-web/src/main/js/helpers/testMocks.ts
@@ -211,12 +211,7 @@ export function mockIssue(withLocations = false, overrides: Partial<T.Issue> = {
type: 'BUG'
};
- function loc(): T.FlowLocation {
- return {
- component: 'main.js',
- textRange: { startLine: 1, startOffset: 1, endLine: 2, endOffset: 2 }
- };
- }
+ const loc = mockFlowLocation;
if (withLocations) {
issue.flows = [[loc(), loc(), loc()], [loc(), loc()]];
@@ -517,3 +512,16 @@ export function mockLanguage(overrides: Partial<T.Language> = {}): T.Language {
...overrides
};
}
+
+export function mockFlowLocation(overrides: Partial<T.FlowLocation> = {}): T.FlowLocation {
+ return {
+ component: 'main.js',
+ textRange: {
+ startLine: 1,
+ startOffset: 1,
+ endLine: 2,
+ endOffset: 2
+ },
+ ...overrides
+ };
+}