]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-16383 Change secondary issues in file to allow focus on the current issue
authorMathieu Suen <mathieu.suen@sonarsource.com>
Wed, 25 May 2022 09:22:26 +0000 (11:22 +0200)
committersonartech <sonartech@sonarsource.com>
Wed, 25 May 2022 20:03:17 +0000 (20:03 +0000)
18 files changed:
server/sonar-web/src/main/js/api/mocks/IssuesServiceMock.ts
server/sonar-web/src/main/js/apps/issues/__tests__/IssueApp-it.tsx
server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx
server/sonar-web/src/main/js/apps/issues/components/IssuesSourceViewer.tsx
server/sonar-web/src/main/js/apps/issues/components/__tests__/IssuesSourceViewer-test.tsx
server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/IssuesSourceViewer-test.tsx.snap
server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/ComponentSourceSnippetGroupViewer.tsx
server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewerWrapper.tsx
server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.tsx
server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/ComponentSourceSnippetGroupViewer-test.tsx
server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/CrossComponentSourceViewerWrapper-test.tsx
server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/SnippetViewer-test.tsx
server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/ComponentSourceSnippetGroupViewer-test.tsx.snap
server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/CrossComponentSourceViewerWrapper-test.tsx.snap
server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/SnippetViewer-test.tsx.snap
server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetContainerRenderer.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotSnippetContainerRenderer-test.tsx.snap
server/sonar-web/src/main/js/components/issue/SecondaryIssue.tsx [new file with mode: 0644]

index e961edf5b669a4953ea9199f6a3003375d4c497c..65128a0e82e7971a29a946650efc008b070e9ea9 100644 (file)
@@ -158,6 +158,30 @@ export default class IssuesServiceMock {
           ],
           'component.key'
         )
+      },
+      {
+        issue: mockRawIssue(false, {
+          key: 'issue3',
+          component: 'project:file.bar',
+          message: 'Second issue',
+          rule: 'other',
+          textRange: {
+            startLine: 28,
+            endLine: 28,
+            startOffset: 0,
+            endOffset: 1
+          }
+        }),
+        snippets: keyBy(
+          [
+            mockSnippetsByComponent(
+              'file.bar',
+              'project',
+              times(40, i => i + 20)
+            )
+          ],
+          'component.key'
+        )
       }
     ];
     (searchIssues as jest.Mock).mockImplementation(this.handleSearchIssues);
index 8153bf395790558b392a26cb3e739b44d708d212..65066ba08451c0a29c86cfec0ea362161df7eb8f 100644 (file)
@@ -79,6 +79,15 @@ it('should open issue and navigate', async () => {
   ).toBeInTheDocument();
 });
 
+it('should be able to navigate to other issue located in the same file', async () => {
+  const user = userEvent.setup();
+  renderIssueApp();
+  await user.click(await screen.findByRole('region', { name: 'Fix that' }));
+  expect(await screen.findByRole('region', { name: 'Second issue' })).toBeInTheDocument();
+  await user.click(await screen.findByRole('region', { name: 'Second issue' }));
+  expect(screen.getByRole('heading', { level: 1, name: 'Second issue' })).toBeInTheDocument();
+});
+
 it('should support OWASP Top 10 version 2021', async () => {
   const user = userEvent.setup();
   renderIssueApp();
index 34864825c2e187c7e08da1b84c11c40b1ca9e420..8d14844683918396d52159d00e45b1434920b183 100644 (file)
@@ -1086,6 +1086,7 @@ export default class App extends React.PureComponent<Props, State> {
                     issues={issues}
                     locationsNavigator={this.state.locationsNavigator}
                     onIssueChange={this.handleIssueChange}
+                    onIssueSelect={this.openIssue}
                     onLocationSelect={this.selectLocation}
                     openIssue={openIssue}
                     selectedFlowIndex={this.state.selectedFlowIndex}
index 649f1693ab0fbe9ab961ec639bd4f634d711621f..a91db3a73f002979ca0f7e47134e92a395c867f6 100644 (file)
@@ -29,6 +29,7 @@ interface Props {
   issues: Issue[];
   locationsNavigator: boolean;
   onIssueChange: (issue: Issue) => void;
+  onIssueSelect: (issueKey: string) => void;
   onLocationSelect: (index: number) => void;
   openIssue: Issue;
   selectedFlowIndex: number | undefined;
@@ -96,6 +97,7 @@ export default class IssuesSourceViewer extends React.PureComponent<Props> {
           issues={this.props.issues}
           locations={locations}
           onIssueChange={this.props.onIssueChange}
+          onIssueSelect={this.props.onIssueSelect}
           onLoaded={this.handleLoaded}
           onLocationSelect={this.props.onLocationSelect}
           scroll={this.handleScroll}
index e1e2a957372277a8a40a1d3e3a9bc7a9874e89c5..3f7941cf59126c5ebd12c429d0c4f3d094cd73fc 100644 (file)
@@ -71,6 +71,7 @@ function shallowRender(props: Partial<IssuesSourceViewer['props']> = {}) {
       issues={[mockIssue()]}
       locationsNavigator={true}
       onIssueChange={jest.fn()}
+      onIssueSelect={jest.fn()}
       onLocationSelect={jest.fn()}
       openIssue={mockIssue()}
       selectedFlowIndex={undefined}
index b5cd58eeb0b0f581a6997114f1bfd3614910c123..395562973cf3dfd90d956e31f3588d1eab92513b 100644 (file)
@@ -211,6 +211,7 @@ exports[`should render CrossComponentSourceViewer correctly 1`] = `
       ]
     }
     onIssueChange={[MockFunction]}
+    onIssueSelect={[MockFunction]}
     onLoaded={[Function]}
     onLocationSelect={[MockFunction]}
     scroll={[Function]}
@@ -449,6 +450,7 @@ exports[`should render SourceViewer correctly: all secondary locations on same l
       ]
     }
     onIssueChange={[MockFunction]}
+    onIssueSelect={[MockFunction]}
     onLoaded={[Function]}
     onLocationSelect={[MockFunction]}
     scroll={[Function]}
@@ -533,6 +535,7 @@ exports[`should render SourceViewer correctly: default 1`] = `
     }
     locations={Array []}
     onIssueChange={[MockFunction]}
+    onIssueSelect={[MockFunction]}
     onLoaded={[Function]}
     onLocationSelect={[MockFunction]}
     scroll={[Function]}
@@ -731,6 +734,7 @@ exports[`should render SourceViewer correctly: single secondary location 1`] = `
       ]
     }
     onIssueChange={[MockFunction]}
+    onIssueSelect={[MockFunction]}
     onLoaded={[Function]}
     onLocationSelect={[MockFunction]}
     scroll={[Function]}
index d54d1f0ad95cb020ecbff96767b5e5767d913238..8aaa4277267e2a8ab5e9a19f6c6d026de500ee88 100644 (file)
@@ -21,7 +21,7 @@ import { noop } from 'lodash';
 import * as React from 'react';
 import { getSources } from '../../../api/components';
 import Issue from '../../../components/issue/Issue';
-import LineIssuesList from '../../../components/SourceViewer/components/LineIssuesList';
+import SecondaryIssue from '../../../components/issue/SecondaryIssue';
 import getCoverageStatus from '../../../components/SourceViewer/helpers/getCoverageStatus';
 import { locationsByLine } from '../../../components/SourceViewer/helpers/indexing';
 import SourceViewerHeaderSlim from '../../../components/SourceViewer/SourceViewerHeaderSlim';
@@ -62,6 +62,7 @@ interface Props {
   loadDuplications: (component: string, line: SourceLine) => void;
   locations: FlowLocation[];
   onIssueChange: (issue: TypeIssue) => void;
+  onIssueSelect: (issueKey: string) => void;
   onIssuePopupToggle: (issue: string, popupName: string, open?: boolean) => void;
   onLocationSelect: (index: number) => void;
   renderDuplicationPopup: (
@@ -77,7 +78,6 @@ interface State {
   additionalLines: { [line: number]: SourceLine };
   highlightedSymbols: string[];
   loading: boolean;
-  openIssuesByLine: Dict<boolean>;
   snippets: Snippet[];
 }
 
@@ -88,7 +88,6 @@ export default class ComponentSourceSnippetGroupViewer extends React.PureCompone
     additionalLines: {},
     highlightedSymbols: [],
     loading: false,
-    openIssuesByLine: {},
     snippets: []
   };
 
@@ -287,18 +286,6 @@ export default class ComponentSourceSnippetGroupViewer extends React.PureCompone
     );
   };
 
-  handleOpenIssues = (line: SourceLine) => {
-    this.setState(state => ({
-      openIssuesByLine: { ...state.openIssuesByLine, [line.line]: true }
-    }));
-  };
-
-  handleCloseIssues = (line: SourceLine) => {
-    this.setState(state => ({
-      openIssuesByLine: { ...state.openIssuesByLine, [line.line]: false }
-    }));
-  };
-
   handleSymbolClick = (clickedSymbols: string[]) => {
     this.setState(({ highlightedSymbols }) => {
       const newHighlightedSymbols = clickedSymbols.filter(
@@ -317,9 +304,13 @@ export default class ComponentSourceSnippetGroupViewer extends React.PureCompone
   };
 
   renderIssuesList = (line: SourceLine) => {
-    const { openIssuesByLine } = this.state;
-
-    const { isLastOccurenceOfPrimaryComponent, issue, issuesByLine, snippetGroup } = this.props;
+    const {
+      isLastOccurenceOfPrimaryComponent,
+      issue,
+      issuesByLine,
+      snippetGroup,
+      branchLike
+    } = this.props;
     const locations =
       issue.component === snippetGroup.component.key && issue.textRange !== undefined
         ? locationsByLine([issue])
@@ -327,38 +318,52 @@ export default class ComponentSourceSnippetGroupViewer extends React.PureCompone
 
     const isFlow = issue.secondaryLocations.length === 0;
     const includeIssueLocation = isFlow ? isLastOccurenceOfPrimaryComponent : true;
-
     const issuesForLine = issuesByLine[line.line] || [];
+    const issueLocations = includeIssueLocation ? locations[line.line] : [];
 
-    const selectedIssue = issuesForLine.find(i => i.key === issue.key)?.key;
-
-    const issueLocationsByLine = includeIssueLocation ? locations : {};
     return (
-      <LineIssuesList
-        displayWhyIsThisAnIssue={false}
-        issueLocationsByLine={issueLocationsByLine}
-        issuesForLine={issuesForLine}
-        line={line}
-        openIssuesByLine={openIssuesByLine}
-        branchLike={this.props.branchLike}
-        issuePopup={this.props.issuePopup}
-        onIssueChange={this.props.onIssueChange}
-        onIssueClick={noop}
-        onIssuePopupToggle={this.props.onIssuePopupToggle}
-        selectedIssue={selectedIssue}
-      />
+      issuesForLine.length > 0 && (
+        <div className="issue-list">
+          {issuesForLine.map(issueToDisplay => {
+            if (issueToDisplay.key === issue.key && issueLocations && issueLocations.length) {
+              return (
+                <Issue
+                  branchLike={branchLike}
+                  displayWhyIsThisAnIssue={false}
+                  issue={issueToDisplay}
+                  key={issueToDisplay.key}
+                  onChange={this.props.onIssueChange}
+                  onClick={noop}
+                  onPopupToggle={this.props.onIssuePopupToggle}
+                  openPopup={
+                    this.props.issuePopup && this.props.issuePopup.issue === issueToDisplay.key
+                      ? this.props.issuePopup.name
+                      : undefined
+                  }
+                  selected={issue.key === issueToDisplay.key}
+                />
+              );
+            }
+            return (
+              <SecondaryIssue
+                key={issueToDisplay.key}
+                issue={issueToDisplay}
+                onClick={this.props.onIssueSelect}
+              />
+            );
+          })}
+        </div>
+      )
     );
   };
 
   renderSnippet({
     index,
-    issuesByLine,
     lastSnippetOfLastGroup,
     locationsByLine,
     snippet
   }: {
     index: number;
-    issuesByLine: IssuesByLine;
     lastSnippetOfLastGroup: boolean;
     locationsByLine: { [line: number]: LinearIssueLocation[] };
     snippet: SourceLine[];
@@ -370,20 +375,16 @@ export default class ComponentSourceSnippetGroupViewer extends React.PureCompone
         duplications={this.props.duplications}
         duplicationsByLine={this.props.duplicationsByLine}
         expandBlock={this.expandBlock}
-        handleCloseIssues={this.handleCloseIssues}
-        handleOpenIssues={this.handleOpenIssues}
         handleSymbolClick={this.handleSymbolClick}
         highlightedLocationMessage={this.props.highlightedLocationMessage}
         highlightedSymbols={this.state.highlightedSymbols}
         index={index}
         issue={this.props.issue}
-        issuesByLine={issuesByLine}
         lastSnippetOfLastGroup={lastSnippetOfLastGroup}
         loadDuplications={this.loadDuplications}
         locations={this.props.locations}
         locationsByLine={locationsByLine}
         onLocationSelect={this.props.onLocationSelect}
-        openIssuesByLine={this.state.openIssuesByLine}
         renderDuplicationPopup={this.renderDuplicationPopup}
         scroll={this.props.scroll}
         snippet={snippet}
@@ -396,7 +397,6 @@ export default class ComponentSourceSnippetGroupViewer extends React.PureCompone
       branchLike,
       isLastOccurenceOfPrimaryComponent,
       issue,
-      issuesByLine,
       issuePopup,
       lastSnippetGroup,
       snippetGroup
@@ -447,7 +447,6 @@ export default class ComponentSourceSnippetGroupViewer extends React.PureCompone
             {this.renderSnippet({
               snippet,
               index: snippets[index].index,
-              issuesByLine,
               locationsByLine: includeIssueLocation ? locations : {},
               lastSnippetOfLastGroup: lastSnippetGroup && index === snippets.length - 1
             })}
index 2d20f25c5acff9941818183f1fc954e82d830977..11dc8ddab0da1311a1617697f7568c802a3f28ca 100644 (file)
@@ -60,6 +60,7 @@ interface Props {
   issues: Issue[];
   locations: FlowLocation[];
   onIssueChange: (issue: Issue) => void;
+  onIssueSelect: (issueKey: string) => void;
   onLoaded?: () => void;
   onLocationSelect: (index: number) => void;
   scroll?: (element: HTMLElement) => void;
@@ -253,6 +254,7 @@ export default class CrossComponentSourceViewerWrapper extends React.PureCompone
                 loadDuplications={this.fetchDuplications}
                 locations={snippetGroup.locations || []}
                 onIssueChange={this.props.onIssueChange}
+                onIssueSelect={this.props.onIssueSelect}
                 onIssuePopupToggle={this.handleIssuePopupToggle}
                 onLocationSelect={this.props.onLocationSelect}
                 renderDuplicationPopup={this.renderDuplicationPopup}
@@ -277,6 +279,7 @@ export default class CrossComponentSourceViewerWrapper extends React.PureCompone
             loadDuplications={this.fetchDuplications}
             locations={[]}
             onIssueChange={this.props.onIssueChange}
+            onIssueSelect={this.props.onIssueSelect}
             onIssuePopupToggle={this.handleIssuePopupToggle}
             onLocationSelect={this.props.onLocationSelect}
             renderDuplicationPopup={this.renderDuplicationPopup}
index 07a0685f534eb0c9a456e741e7fb3c4b5b7246e0..9981db897003698f8886be00ecf3c8aaac62db0a 100644 (file)
@@ -30,18 +30,16 @@ import {
 import { translate } from '../../../helpers/l10n';
 import { scrollHorizontally } from '../../../helpers/scrolling';
 import {
-  Dict,
   Duplication,
   ExpandDirection,
   FlowLocation,
   Issue,
-  IssuesByLine,
   LinearIssueLocation,
   SourceLine,
   SourceViewerFile
 } from '../../../types/types';
 import './SnippetViewer.css';
-import { inSnippet, LINES_BELOW_ISSUE } from './utils';
+import { LINES_BELOW_ISSUE } from './utils';
 
 interface Props {
   component: SourceViewerFile;
@@ -50,20 +48,16 @@ interface Props {
   duplications?: Duplication[];
   duplicationsByLine?: { [line: number]: number[] };
   expandBlock: (snippetIndex: number, direction: ExpandDirection) => Promise<void>;
-  handleCloseIssues: (line: SourceLine) => void;
-  handleOpenIssues: (line: SourceLine) => void;
   handleSymbolClick: (symbols: string[]) => void;
   highlightedLocationMessage: { index: number; text: string | undefined } | undefined;
   highlightedSymbols: string[];
   index: number;
   issue: Pick<Issue, 'key' | 'textRange' | 'line'>;
-  issuesByLine: IssuesByLine;
   lastSnippetOfLastGroup: boolean;
   loadDuplications?: (line: SourceLine) => void;
   locations: FlowLocation[];
   locationsByLine: { [line: number]: LinearIssueLocation[] };
   onLocationSelect: (index: number) => void;
-  openIssuesByLine: Dict<boolean>;
   renderAdditionalChildInLine?: (line: SourceLine) => React.ReactNode | undefined;
   renderDuplicationPopup: (index: number, line: number) => React.ReactNode;
   scroll?: (element: HTMLElement, offset?: number) => void;
@@ -118,7 +112,6 @@ export default class SnippetViewer extends React.PureComponent<Props> {
     displayDuplications,
     displaySCM,
     index,
-    issuesForLine,
     issueLocations,
     line,
     snippet,
@@ -128,7 +121,6 @@ export default class SnippetViewer extends React.PureComponent<Props> {
     displayDuplications: boolean;
     displaySCM?: boolean;
     index: number;
-    issuesForLine: Issue[];
     issueLocations: LinearIssueLocation[];
     line: SourceLine;
     snippet: SourceLine[];
@@ -142,16 +134,14 @@ export default class SnippetViewer extends React.PureComponent<Props> {
     const lineDuplications =
       (duplicationsCount && duplicationsByLine && duplicationsByLine[line.line]) || [];
 
-    const isSinkLine = issuesForLine.some(i => i.key === this.props.issue.key);
     const firstLineNumber = snippet && snippet.length ? snippet[0].line : 0;
     const noop = () => {};
 
     return (
       <Line
-        displayAllIssues={false}
         displayCoverage={true}
         displayDuplications={displayDuplications}
-        displayIssues={!isSinkLine || issuesForLine.length > 1}
+        displayIssues={false}
         displayLineNumberOptions={displayLineNumberOptions}
         displayLocationMarkers={true}
         displaySCM={displaySCM}
@@ -165,18 +155,18 @@ export default class SnippetViewer extends React.PureComponent<Props> {
         )}
         highlightedSymbols={optimizeHighlightedSymbols(symbols, this.props.highlightedSymbols)}
         issueLocations={issueLocations}
-        issues={issuesForLine}
+        issues={[]}
         key={line.line}
         last={false}
         line={line}
         loadDuplications={this.props.loadDuplications || noop}
         onIssueSelect={noop}
         onIssueUnselect={noop}
-        onIssuesClose={this.props.handleCloseIssues}
-        onIssuesOpen={this.props.handleOpenIssues}
+        onIssuesClose={noop}
+        onIssuesOpen={noop}
         onLocationSelect={this.props.onLocationSelect}
         onSymbolClick={this.props.handleSymbolClick}
-        openIssues={this.props.openIssuesByLine[line.line]}
+        openIssues={false}
         previousLine={index > 0 ? snippet[index - 1] : undefined}
         renderDuplicationPopup={this.props.renderDuplicationPopup}
         scroll={this.doScroll}
@@ -192,10 +182,8 @@ export default class SnippetViewer extends React.PureComponent<Props> {
       component,
       displaySCM,
       issue,
-      issuesByLine = {},
       lastSnippetOfLastGroup,
       locationsByLine,
-      openIssuesByLine,
       snippet
     } = this.props;
     const lastLine =
@@ -205,14 +193,11 @@ export default class SnippetViewer extends React.PureComponent<Props> {
 
     const bottomLine = snippet[snippet.length - 1].line;
     const issueLine = issue.textRange ? issue.textRange.endLine : issue.line;
-    const lowestVisibleIssue = Math.max(
-      ...Object.keys(issuesByLine)
-        .map(k => parseInt(k, 10))
-        .filter(l => inSnippet(l, snippet) && (l === issueLine || openIssuesByLine[l]))
-    );
-    const verticalBuffer = lastSnippetOfLastGroup
-      ? Math.max(0, LINES_BELOW_ISSUE - (bottomLine - lowestVisibleIssue))
-      : 0;
+
+    const verticalBuffer =
+      lastSnippetOfLastGroup && issueLine
+        ? Math.max(0, LINES_BELOW_ISSUE - (bottomLine - issueLine))
+        : 0;
 
     const displayDuplications =
       Boolean(this.props.loadDuplications) && snippet.some(s => !!s.duplicated);
@@ -241,7 +226,6 @@ export default class SnippetViewer extends React.PureComponent<Props> {
                   displayDuplications,
                   displaySCM,
                   index,
-                  issuesForLine: issuesByLine[line.line] || [],
                   issueLocations: locationsByLine[line.line] || [],
                   line,
                   snippet,
index cbbe8669da51024f3d48da0b5bd0464b21d041d0..e39ceb01d82afc300df7acbf5cac15499f2c7c03 100644 (file)
@@ -234,16 +234,6 @@ it('should get the right branch when expanding', async () => {
   expect(getSources).toHaveBeenCalledWith({ branch: 'asdf', from: 8, key: 'project:a', to: 67 });
 });
 
-it('should handle correctly open/close issue', () => {
-  const wrapper = shallowRender();
-  const sourceLine = mockSourceLine();
-  expect(wrapper.state('openIssuesByLine')).toEqual({});
-  wrapper.instance().handleOpenIssues(sourceLine);
-  expect(wrapper.state('openIssuesByLine')).toEqual({ [sourceLine.line]: true });
-  wrapper.instance().handleCloseIssues(sourceLine);
-  expect(wrapper.state('openIssuesByLine')).toEqual({ [sourceLine.line]: false });
-});
-
 it('should handle symbol highlighting', () => {
   const wrapper = shallowRender();
   expect(wrapper.state('highlightedSymbols')).toEqual([]);
@@ -294,19 +284,6 @@ it('should correctly handle lines actions', () => {
   );
 });
 
-it('should render correctly line with issue', () => {
-  const issue = mockIssue(false, {
-    textRange: { endLine: 1, startLine: 1, endOffset: 1, startOffset: 0 }
-  });
-  const wrapper = shallowRender({
-    issue,
-    issuesByLine: { '1': [issue] }
-  });
-  wrapper.instance().setState({ openIssuesByLine: { '1': true } });
-  const wrapperLine = shallow(wrapper.instance().renderIssuesList(mockSourceLine({ line: 1 })));
-  expect(wrapperLine).toMatchSnapshot();
-});
-
 describe('getNodes', () => {
   const snippetGroup: SnippetGroup = {
     component: mockSourceViewerFile(),
@@ -324,6 +301,7 @@ describe('getNodes', () => {
       loadDuplications={jest.fn()}
       locations={[]}
       onIssueChange={jest.fn()}
+      onIssueSelect={jest.fn()}
       onIssuePopupToggle={jest.fn()}
       onLocationSelect={jest.fn()}
       renderDuplicationPopup={jest.fn()}
@@ -386,6 +364,7 @@ describe('getHeight', () => {
       loadDuplications={jest.fn()}
       locations={[]}
       onIssueChange={jest.fn()}
+      onIssueSelect={jest.fn()}
       onIssuePopupToggle={jest.fn()}
       onLocationSelect={jest.fn()}
       renderDuplicationPopup={jest.fn()}
@@ -437,6 +416,7 @@ function shallowRender(props: Partial<ComponentSourceSnippetGroupViewer['props']
       loadDuplications={jest.fn()}
       locations={[]}
       onIssueChange={jest.fn()}
+      onIssueSelect={jest.fn()}
       onIssuePopupToggle={jest.fn()}
       onLocationSelect={jest.fn()}
       renderDuplicationPopup={jest.fn()}
index 050f69d8df673ac832a116ef56dfe1125b87d360..95bb1079de3faf41f76888620d7f4fcf52d96e83 100644 (file)
@@ -132,6 +132,7 @@ function shallowRender(props: Partial<CrossComponentSourceViewerWrapper['props']
       locations={[mockFlowLocation()]}
       onIssueChange={jest.fn()}
       onLoaded={jest.fn()}
+      onIssueSelect={jest.fn()}
       onLocationSelect={jest.fn()}
       scroll={jest.fn()}
       selectedFlowIndex={0}
index 579ca3c257bd0d65c4f04fcbe8f5d1598e5cadee..614c63fc735d2b35ebf2d6d2afbf137dc9830cb3 100644 (file)
@@ -62,7 +62,6 @@ it('should render additional child in line', () => {
   wrapper.instance().renderLine({
     displayDuplications: false,
     index: 1,
-    issuesForLine: [],
     issueLocations: [],
     line: sourceline,
     snippet: [sourceline],
@@ -146,20 +145,16 @@ function shallowRender(props: Partial<SnippetViewer['props']> = {}) {
       duplications={undefined}
       duplicationsByLine={undefined}
       expandBlock={jest.fn()}
-      handleCloseIssues={jest.fn()}
-      handleOpenIssues={jest.fn()}
       handleSymbolClick={jest.fn()}
       highlightedLocationMessage={{ index: 0, text: '' }}
       highlightedSymbols={[]}
       index={0}
       issue={mockIssue()}
-      issuesByLine={{}}
       lastSnippetOfLastGroup={false}
       loadDuplications={jest.fn()}
       locations={[]}
       locationsByLine={{}}
       onLocationSelect={jest.fn()}
-      openIssuesByLine={{}}
       renderDuplicationPopup={jest.fn()}
       scroll={jest.fn()}
       snippet={[]}
@@ -175,20 +170,16 @@ function mountRender(props: Partial<SnippetViewer['props']> = {}) {
       duplications={undefined}
       duplicationsByLine={undefined}
       expandBlock={jest.fn()}
-      handleCloseIssues={jest.fn()}
-      handleOpenIssues={jest.fn()}
       handleSymbolClick={jest.fn()}
       highlightedLocationMessage={{ index: 0, text: '' }}
       highlightedSymbols={[]}
       index={0}
       issue={mockIssue()}
-      issuesByLine={{}}
       lastSnippetOfLastGroup={false}
       loadDuplications={jest.fn()}
       locations={[]}
       locationsByLine={{}}
       onLocationSelect={jest.fn()}
-      openIssuesByLine={{}}
       renderDuplicationPopup={jest.fn()}
       scroll={jest.fn()}
       snippet={[mockSourceLine()]}
index c19827b9ef7d00eee3191867e812206c00911e98..58f647c8012cbb4f41de010df79ddbe815ede7b2 100644 (file)
@@ -39,57 +39,3 @@ exports[`should render correctly 1`] = `
   />
 </div>
 `;
-
-exports[`should render correctly line with issue 1`] = `
-<div
-  className="issue-list"
->
-  <Issue
-    branchLike={
-      Object {
-        "analysisDate": "2018-01-01",
-        "excludedFromPurge": true,
-        "isMain": true,
-        "name": "master",
-      }
-    }
-    displayWhyIsThisAnIssue={false}
-    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",
-        "project": "myproject",
-        "projectKey": "foo",
-        "projectName": "Foo",
-        "rule": "javascript:S1067",
-        "ruleName": "foo",
-        "secondaryLocations": Array [],
-        "severity": "MAJOR",
-        "status": "OPEN",
-        "textRange": Object {
-          "endLine": 1,
-          "endOffset": 1,
-          "startLine": 1,
-          "startOffset": 0,
-        },
-        "transitions": Array [],
-        "type": "BUG",
-      }
-    }
-    key="AVsae-CQS-9G3txfbFN2"
-    onChange={[MockFunction]}
-    onClick={[Function]}
-    onPopupToggle={[MockFunction]}
-    selected={true}
-  />
-</div>
-`;
index 7027b2e58a702f31c6bf3b1c0f90adf9385dbb80..f587717fbbcbb004bee333a531594f0cc9ac3ed1 100644 (file)
@@ -171,6 +171,7 @@ exports[`should render correctly 2`] = `
       }
       onIssueChange={[MockFunction]}
       onIssuePopupToggle={[Function]}
+      onIssueSelect={[MockFunction]}
       onLocationSelect={[MockFunction]}
       renderDuplicationPopup={[Function]}
       scroll={[MockFunction]}
@@ -346,6 +347,7 @@ exports[`should render correctly: no component found 1`] = `
       locations={Array []}
       onIssueChange={[MockFunction]}
       onIssuePopupToggle={[Function]}
+      onIssueSelect={[MockFunction]}
       onLocationSelect={[MockFunction]}
       renderDuplicationPopup={[Function]}
       scroll={[MockFunction]}
@@ -506,6 +508,7 @@ exports[`should render correctly: no component found 1`] = `
       }
       onIssueChange={[MockFunction]}
       onIssuePopupToggle={[Function]}
+      onIssueSelect={[MockFunction]}
       onLocationSelect={[MockFunction]}
       renderDuplicationPopup={[Function]}
       scroll={[MockFunction]}
index 804854852ec6ac08e8f9874ae79964fce53d93e6..085138cd9cf2d2fa475a393197bbb3667162d9cc 100644 (file)
@@ -21,10 +21,9 @@ exports[`should render correctly 1`] = `
     >
       <tbody>
         <Line
-          displayAllIssues={false}
           displayCoverage={true}
           displayDuplications={false}
-          displayIssues={true}
+          displayIssues={false}
           displayLocationMarkers={true}
           duplications={Array []}
           duplicationsCount={0}
@@ -50,20 +49,20 @@ exports[`should render correctly 1`] = `
           loadDuplications={[MockFunction]}
           onIssueSelect={[Function]}
           onIssueUnselect={[Function]}
-          onIssuesClose={[MockFunction]}
-          onIssuesOpen={[MockFunction]}
+          onIssuesClose={[Function]}
+          onIssuesOpen={[Function]}
           onLocationSelect={[MockFunction]}
           onSymbolClick={[MockFunction]}
+          openIssues={false}
           renderDuplicationPopup={[MockFunction]}
           scroll={[Function]}
           secondaryIssueLocations={Array []}
           verticalBuffer={0}
         />
         <Line
-          displayAllIssues={false}
           displayCoverage={true}
           displayDuplications={false}
-          displayIssues={true}
+          displayIssues={false}
           displayLocationMarkers={true}
           duplications={Array []}
           duplicationsCount={0}
@@ -89,10 +88,11 @@ exports[`should render correctly 1`] = `
           loadDuplications={[MockFunction]}
           onIssueSelect={[Function]}
           onIssueUnselect={[Function]}
-          onIssuesClose={[MockFunction]}
-          onIssuesOpen={[MockFunction]}
+          onIssuesClose={[Function]}
+          onIssuesOpen={[Function]}
           onLocationSelect={[MockFunction]}
           onSymbolClick={[MockFunction]}
+          openIssues={false}
           previousLine={
             Object {
               "code": "<span class=\\"k\\">import</span> java.util.<span class=\\"sym-9 sym\\">ArrayList</span>;",
@@ -112,10 +112,9 @@ exports[`should render correctly 1`] = `
           verticalBuffer={0}
         />
         <Line
-          displayAllIssues={false}
           displayCoverage={true}
           displayDuplications={false}
-          displayIssues={true}
+          displayIssues={false}
           displayLocationMarkers={true}
           duplications={Array []}
           duplicationsCount={0}
@@ -141,10 +140,11 @@ exports[`should render correctly 1`] = `
           loadDuplications={[MockFunction]}
           onIssueSelect={[Function]}
           onIssueUnselect={[Function]}
-          onIssuesClose={[MockFunction]}
-          onIssuesOpen={[MockFunction]}
+          onIssuesClose={[Function]}
+          onIssuesOpen={[Function]}
           onLocationSelect={[MockFunction]}
           onSymbolClick={[MockFunction]}
+          openIssues={false}
           previousLine={
             Object {
               "code": "<span class=\\"k\\">import</span> java.util.<span class=\\"sym-9 sym\\">ArrayList</span>;",
@@ -201,10 +201,9 @@ exports[`should render correctly when at the bottom of the file 1`] = `
     >
       <tbody>
         <Line
-          displayAllIssues={false}
           displayCoverage={true}
           displayDuplications={false}
-          displayIssues={true}
+          displayIssues={false}
           displayLocationMarkers={true}
           duplications={Array []}
           duplicationsCount={0}
@@ -230,20 +229,20 @@ exports[`should render correctly when at the bottom of the file 1`] = `
           loadDuplications={[MockFunction]}
           onIssueSelect={[Function]}
           onIssueUnselect={[Function]}
-          onIssuesClose={[MockFunction]}
-          onIssuesOpen={[MockFunction]}
+          onIssuesClose={[Function]}
+          onIssuesOpen={[Function]}
           onLocationSelect={[MockFunction]}
           onSymbolClick={[MockFunction]}
+          openIssues={false}
           renderDuplicationPopup={[MockFunction]}
           scroll={[Function]}
           secondaryIssueLocations={Array []}
           verticalBuffer={0}
         />
         <Line
-          displayAllIssues={false}
           displayCoverage={true}
           displayDuplications={false}
-          displayIssues={true}
+          displayIssues={false}
           displayLocationMarkers={true}
           duplications={Array []}
           duplicationsCount={0}
@@ -269,10 +268,11 @@ exports[`should render correctly when at the bottom of the file 1`] = `
           loadDuplications={[MockFunction]}
           onIssueSelect={[Function]}
           onIssueUnselect={[Function]}
-          onIssuesClose={[MockFunction]}
-          onIssuesOpen={[MockFunction]}
+          onIssuesClose={[Function]}
+          onIssuesOpen={[Function]}
           onLocationSelect={[MockFunction]}
           onSymbolClick={[MockFunction]}
+          openIssues={false}
           previousLine={
             Object {
               "code": "<span class=\\"k\\">import</span> java.util.<span class=\\"sym-9 sym\\">ArrayList</span>;",
@@ -292,10 +292,9 @@ exports[`should render correctly when at the bottom of the file 1`] = `
           verticalBuffer={0}
         />
         <Line
-          displayAllIssues={false}
           displayCoverage={true}
           displayDuplications={false}
-          displayIssues={true}
+          displayIssues={false}
           displayLocationMarkers={true}
           duplications={Array []}
           duplicationsCount={0}
@@ -321,10 +320,11 @@ exports[`should render correctly when at the bottom of the file 1`] = `
           loadDuplications={[MockFunction]}
           onIssueSelect={[Function]}
           onIssueUnselect={[Function]}
-          onIssuesClose={[MockFunction]}
-          onIssuesOpen={[MockFunction]}
+          onIssuesClose={[Function]}
+          onIssuesOpen={[Function]}
           onLocationSelect={[MockFunction]}
           onSymbolClick={[MockFunction]}
+          openIssues={false}
           previousLine={
             Object {
               "code": "<span class=\\"k\\">import</span> java.util.<span class=\\"sym-9 sym\\">ArrayList</span>;",
@@ -344,10 +344,9 @@ exports[`should render correctly when at the bottom of the file 1`] = `
           verticalBuffer={0}
         />
         <Line
-          displayAllIssues={false}
           displayCoverage={true}
           displayDuplications={false}
-          displayIssues={true}
+          displayIssues={false}
           displayLocationMarkers={true}
           duplications={Array []}
           duplicationsCount={0}
@@ -373,10 +372,11 @@ exports[`should render correctly when at the bottom of the file 1`] = `
           loadDuplications={[MockFunction]}
           onIssueSelect={[Function]}
           onIssueUnselect={[Function]}
-          onIssuesClose={[MockFunction]}
-          onIssuesOpen={[MockFunction]}
+          onIssuesClose={[Function]}
+          onIssuesOpen={[Function]}
           onLocationSelect={[MockFunction]}
           onSymbolClick={[MockFunction]}
+          openIssues={false}
           previousLine={
             Object {
               "code": "<span class=\\"k\\">import</span> java.util.<span class=\\"sym-9 sym\\">ArrayList</span>;",
@@ -422,10 +422,9 @@ exports[`should render correctly when at the top of the file 1`] = `
     >
       <tbody>
         <Line
-          displayAllIssues={false}
           displayCoverage={true}
           displayDuplications={false}
-          displayIssues={true}
+          displayIssues={false}
           displayLocationMarkers={true}
           duplications={Array []}
           duplicationsCount={0}
@@ -451,20 +450,20 @@ exports[`should render correctly when at the top of the file 1`] = `
           loadDuplications={[MockFunction]}
           onIssueSelect={[Function]}
           onIssueUnselect={[Function]}
-          onIssuesClose={[MockFunction]}
-          onIssuesOpen={[MockFunction]}
+          onIssuesClose={[Function]}
+          onIssuesOpen={[Function]}
           onLocationSelect={[MockFunction]}
           onSymbolClick={[MockFunction]}
+          openIssues={false}
           renderDuplicationPopup={[MockFunction]}
           scroll={[Function]}
           secondaryIssueLocations={Array []}
           verticalBuffer={0}
         />
         <Line
-          displayAllIssues={false}
           displayCoverage={true}
           displayDuplications={false}
-          displayIssues={true}
+          displayIssues={false}
           displayLocationMarkers={true}
           duplications={Array []}
           duplicationsCount={0}
@@ -490,10 +489,11 @@ exports[`should render correctly when at the top of the file 1`] = `
           loadDuplications={[MockFunction]}
           onIssueSelect={[Function]}
           onIssueUnselect={[Function]}
-          onIssuesClose={[MockFunction]}
-          onIssuesOpen={[MockFunction]}
+          onIssuesClose={[Function]}
+          onIssuesOpen={[Function]}
           onLocationSelect={[MockFunction]}
           onSymbolClick={[MockFunction]}
+          openIssues={false}
           previousLine={
             Object {
               "code": "<span class=\\"k\\">import</span> java.util.<span class=\\"sym-9 sym\\">ArrayList</span>;",
@@ -513,10 +513,9 @@ exports[`should render correctly when at the top of the file 1`] = `
           verticalBuffer={0}
         />
         <Line
-          displayAllIssues={false}
           displayCoverage={true}
           displayDuplications={false}
-          displayIssues={true}
+          displayIssues={false}
           displayLocationMarkers={true}
           duplications={Array []}
           duplicationsCount={0}
@@ -542,10 +541,11 @@ exports[`should render correctly when at the top of the file 1`] = `
           loadDuplications={[MockFunction]}
           onIssueSelect={[Function]}
           onIssueUnselect={[Function]}
-          onIssuesClose={[MockFunction]}
-          onIssuesOpen={[MockFunction]}
+          onIssuesClose={[Function]}
+          onIssuesOpen={[Function]}
           onLocationSelect={[MockFunction]}
           onSymbolClick={[MockFunction]}
+          openIssues={false}
           previousLine={
             Object {
               "code": "<span class=\\"k\\">import</span> java.util.<span class=\\"sym-9 sym\\">ArrayList</span>;",
@@ -565,10 +565,9 @@ exports[`should render correctly when at the top of the file 1`] = `
           verticalBuffer={0}
         />
         <Line
-          displayAllIssues={false}
           displayCoverage={true}
           displayDuplications={false}
-          displayIssues={true}
+          displayIssues={false}
           displayLocationMarkers={true}
           duplications={Array []}
           duplicationsCount={0}
@@ -594,10 +593,11 @@ exports[`should render correctly when at the top of the file 1`] = `
           loadDuplications={[MockFunction]}
           onIssueSelect={[Function]}
           onIssueUnselect={[Function]}
-          onIssuesClose={[MockFunction]}
-          onIssuesOpen={[MockFunction]}
+          onIssuesClose={[Function]}
+          onIssuesOpen={[Function]}
           onLocationSelect={[MockFunction]}
           onSymbolClick={[MockFunction]}
+          openIssues={false}
           previousLine={
             Object {
               "code": "<span class=\\"k\\">import</span> java.util.<span class=\\"sym-9 sym\\">ArrayList</span>;",
@@ -617,10 +617,9 @@ exports[`should render correctly when at the top of the file 1`] = `
           verticalBuffer={0}
         />
         <Line
-          displayAllIssues={false}
           displayCoverage={true}
           displayDuplications={false}
-          displayIssues={true}
+          displayIssues={false}
           displayLocationMarkers={true}
           duplications={Array []}
           duplicationsCount={0}
@@ -646,10 +645,11 @@ exports[`should render correctly when at the top of the file 1`] = `
           loadDuplications={[MockFunction]}
           onIssueSelect={[Function]}
           onIssueUnselect={[Function]}
-          onIssuesClose={[MockFunction]}
-          onIssuesOpen={[MockFunction]}
+          onIssuesClose={[Function]}
+          onIssuesOpen={[Function]}
           onLocationSelect={[MockFunction]}
           onSymbolClick={[MockFunction]}
+          openIssues={false}
           previousLine={
             Object {
               "code": "<span class=\\"k\\">import</span> java.util.<span class=\\"sym-9 sym\\">ArrayList</span>;",
@@ -669,10 +669,9 @@ exports[`should render correctly when at the top of the file 1`] = `
           verticalBuffer={0}
         />
         <Line
-          displayAllIssues={false}
           displayCoverage={true}
           displayDuplications={false}
-          displayIssues={true}
+          displayIssues={false}
           displayLocationMarkers={true}
           duplications={Array []}
           duplicationsCount={0}
@@ -698,10 +697,11 @@ exports[`should render correctly when at the top of the file 1`] = `
           loadDuplications={[MockFunction]}
           onIssueSelect={[Function]}
           onIssueUnselect={[Function]}
-          onIssuesClose={[MockFunction]}
-          onIssuesOpen={[MockFunction]}
+          onIssuesClose={[Function]}
+          onIssuesOpen={[Function]}
           onLocationSelect={[MockFunction]}
           onSymbolClick={[MockFunction]}
+          openIssues={false}
           previousLine={
             Object {
               "code": "<span class=\\"k\\">import</span> java.util.<span class=\\"sym-9 sym\\">ArrayList</span>;",
@@ -721,10 +721,9 @@ exports[`should render correctly when at the top of the file 1`] = `
           verticalBuffer={0}
         />
         <Line
-          displayAllIssues={false}
           displayCoverage={true}
           displayDuplications={false}
-          displayIssues={true}
+          displayIssues={false}
           displayLocationMarkers={true}
           duplications={Array []}
           duplicationsCount={0}
@@ -750,10 +749,11 @@ exports[`should render correctly when at the top of the file 1`] = `
           loadDuplications={[MockFunction]}
           onIssueSelect={[Function]}
           onIssueUnselect={[Function]}
-          onIssuesClose={[MockFunction]}
-          onIssuesOpen={[MockFunction]}
+          onIssuesClose={[Function]}
+          onIssuesOpen={[Function]}
           onLocationSelect={[MockFunction]}
           onSymbolClick={[MockFunction]}
+          openIssues={false}
           previousLine={
             Object {
               "code": "<span class=\\"k\\">import</span> java.util.<span class=\\"sym-9 sym\\">ArrayList</span>;",
@@ -810,10 +810,9 @@ exports[`should render correctly with no SCM 1`] = `
     >
       <tbody>
         <Line
-          displayAllIssues={false}
           displayCoverage={true}
           displayDuplications={false}
-          displayIssues={true}
+          displayIssues={false}
           displayLocationMarkers={true}
           displaySCM={false}
           duplications={Array []}
@@ -840,20 +839,20 @@ exports[`should render correctly with no SCM 1`] = `
           loadDuplications={[MockFunction]}
           onIssueSelect={[Function]}
           onIssueUnselect={[Function]}
-          onIssuesClose={[MockFunction]}
-          onIssuesOpen={[MockFunction]}
+          onIssuesClose={[Function]}
+          onIssuesOpen={[Function]}
           onLocationSelect={[MockFunction]}
           onSymbolClick={[MockFunction]}
+          openIssues={false}
           renderDuplicationPopup={[MockFunction]}
           scroll={[Function]}
           secondaryIssueLocations={Array []}
           verticalBuffer={0}
         />
         <Line
-          displayAllIssues={false}
           displayCoverage={true}
           displayDuplications={false}
-          displayIssues={true}
+          displayIssues={false}
           displayLocationMarkers={true}
           displaySCM={false}
           duplications={Array []}
@@ -880,10 +879,11 @@ exports[`should render correctly with no SCM 1`] = `
           loadDuplications={[MockFunction]}
           onIssueSelect={[Function]}
           onIssueUnselect={[Function]}
-          onIssuesClose={[MockFunction]}
-          onIssuesOpen={[MockFunction]}
+          onIssuesClose={[Function]}
+          onIssuesOpen={[Function]}
           onLocationSelect={[MockFunction]}
           onSymbolClick={[MockFunction]}
+          openIssues={false}
           previousLine={
             Object {
               "code": "<span class=\\"k\\">import</span> java.util.<span class=\\"sym-9 sym\\">ArrayList</span>;",
@@ -903,10 +903,9 @@ exports[`should render correctly with no SCM 1`] = `
           verticalBuffer={0}
         />
         <Line
-          displayAllIssues={false}
           displayCoverage={true}
           displayDuplications={false}
-          displayIssues={true}
+          displayIssues={false}
           displayLocationMarkers={true}
           displaySCM={false}
           duplications={Array []}
@@ -933,10 +932,11 @@ exports[`should render correctly with no SCM 1`] = `
           loadDuplications={[MockFunction]}
           onIssueSelect={[Function]}
           onIssueUnselect={[Function]}
-          onIssuesClose={[MockFunction]}
-          onIssuesOpen={[MockFunction]}
+          onIssuesClose={[Function]}
+          onIssuesOpen={[Function]}
           onLocationSelect={[MockFunction]}
           onSymbolClick={[MockFunction]}
+          openIssues={false}
           previousLine={
             Object {
               "code": "<span class=\\"k\\">import</span> java.util.<span class=\\"sym-9 sym\\">ArrayList</span>;",
index 8c6c1ba68fc03898f93137103c77299fb8731d24..e7c24596abce0ce7444e38abb2130e88ffcb92c2 100644 (file)
@@ -185,19 +185,15 @@ export default function HotspotSnippetContainerRenderer(
               expandBlock={(_i, direction) =>
                 animateExpansion(scrollableRef, props.onExpandBlock, direction)
               }
-              handleCloseIssues={noop}
-              handleOpenIssues={noop}
               handleSymbolClick={props.onSymbolClick}
               highlightedLocationMessage={highlightedLocation}
               highlightedSymbols={highlightedSymbols}
               index={0}
               issue={hotspot}
-              issuesByLine={{}}
               lastSnippetOfLastGroup={false}
               locations={secondaryLocations}
               locationsByLine={primaryLocations}
               onLocationSelect={props.onLocationSelect}
-              openIssuesByLine={{}}
               renderAdditionalChildInLine={renderHotspotBoxInLine}
               renderDuplicationPopup={noop}
               snippet={sourceLines}
index a4ade78ebae4e2e330982842e08c7bbf4ef2f714..d65b34bfaa82a41ed5eafd70c26f3a626b3fd5c3 100644 (file)
@@ -423,8 +423,6 @@ exports[`should render correctly: with sourcelines 1`] = `
         displayLineNumberOptions={false}
         displaySCM={false}
         expandBlock={[Function]}
-        handleCloseIssues={[Function]}
-        handleOpenIssues={[Function]}
         handleSymbolClick={[MockFunction]}
         highlightedSymbols={Array []}
         index={0}
@@ -514,12 +512,10 @@ exports[`should render correctly: with sourcelines 1`] = `
             ],
           }
         }
-        issuesByLine={Object {}}
         lastSnippetOfLastGroup={false}
         locations={Array []}
         locationsByLine={Object {}}
         onLocationSelect={[MockFunction]}
-        openIssuesByLine={Object {}}
         renderAdditionalChildInLine={[Function]}
         renderDuplicationPopup={[Function]}
         scroll={[Function]}
diff --git a/server/sonar-web/src/main/js/components/issue/SecondaryIssue.tsx b/server/sonar-web/src/main/js/components/issue/SecondaryIssue.tsx
new file mode 100644 (file)
index 0000000..f56266e
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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 { colors } from '../../app/theme';
+import { Issue as TypeIssue } from '../../types/types';
+import IssueTypeIcon from '../icons/IssueTypeIcon';
+import './Issue.css';
+
+export interface SecondaryIssueProps {
+  issue: TypeIssue;
+  onClick: (issueKey: string) => void;
+}
+
+export default function SecondaryIssue(props: SecondaryIssueProps) {
+  const { issue } = props;
+  return (
+    <div
+      className="issue display-flex-row display-flex-center padded-right"
+      key={issue.key}
+      onClick={() => props.onClick(issue.key)}
+      role="region"
+      aria-label={issue.message}>
+      <IssueTypeIcon
+        className="big-spacer-right spacer-left"
+        fill={colors.baseFontColor}
+        query={issue.type}
+      />
+      {issue.message}
+    </div>
+  );
+}