diff options
author | Revanshu Paliwal <revanshu.paliwal@sonarsource.com> | 2022-08-04 14:12:18 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2022-08-09 20:03:19 +0000 |
commit | ab5fe3cabc421afc11d1bcb1deae4439ba150a43 (patch) | |
tree | e9722de08c87f8696b00978282fc6fb709229943 /server/sonar-web | |
parent | 71c6caf8e97af7015c265b2fe237fbc0df196694 (diff) | |
download | sonarqube-ab5fe3cabc421afc11d1bcb1deae4439ba150a43.tar.gz sonarqube-ab5fe3cabc421afc11d1bcb1deae4439ba150a43.zip |
SONAR-16536 Navigating to secondary location from any tab
Diffstat (limited to 'server/sonar-web')
7 files changed, 75 insertions, 41 deletions
diff --git a/server/sonar-web/src/main/js/api/mocks/IssuesServiceMock.ts b/server/sonar-web/src/main/js/api/mocks/IssuesServiceMock.ts index 7236a4c7272..d0e9abd2661 100644 --- a/server/sonar-web/src/main/js/api/mocks/IssuesServiceMock.ts +++ b/server/sonar-web/src/main/js/api/mocks/IssuesServiceMock.ts @@ -122,6 +122,7 @@ export default class IssuesServiceMock { locations: [ { component: 'project:file.foo', + msg: 'location 1', textRange: { startLine: 1, endLine: 1, @@ -135,6 +136,7 @@ export default class IssuesServiceMock { locations: [ { component: 'project:file.bar', + msg: 'location 2', textRange: { startLine: 20, endLine: 20, diff --git a/server/sonar-web/src/main/js/apps/issues/__tests__/IssueApp-it.tsx b/server/sonar-web/src/main/js/apps/issues/__tests__/IssueApp-it.tsx index 9e4d644cd42..533446d99cd 100644 --- a/server/sonar-web/src/main/js/apps/issues/__tests__/IssueApp-it.tsx +++ b/server/sonar-web/src/main/js/apps/issues/__tests__/IssueApp-it.tsx @@ -402,6 +402,49 @@ it('should open the actions popup using keyboard shortcut', async () => { expect(screen.getByRole('searchbox', { name: 'search_verb' })).toBeInTheDocument(); }); +it('should show code tabs when any secondary location is selected', async () => { + const user = userEvent.setup(); + renderIssueApp(); + + await user.click(await screen.findByRole('region', { name: 'Fix this' })); + expect(screen.getByText('location 1')).toBeInTheDocument(); + expect(screen.getByText('location 2')).toBeInTheDocument(); + + // Select the "why is this an issue" tab + await user.click( + screen.getByRole('button', { name: 'coding_rules.description_section.title.root_cause' }) + ); + expect( + screen.queryByRole('row', { + name: '2 source_viewer.tooltip.covered import java.util. ArrayList ;' + }) + ).not.toBeInTheDocument(); + + await user.click(screen.getByRole('link', { name: '1 location 1' })); + expect( + screen.getByRole('row', { + name: '2 source_viewer.tooltip.covered import java.util. ArrayList ;' + }) + ).toBeInTheDocument(); + + // selecting the same selected hotspot location should also navigate back to code page + await user.click( + screen.getByRole('button', { name: 'coding_rules.description_section.title.root_cause' }) + ); + expect( + screen.queryByRole('row', { + name: '2 source_viewer.tooltip.covered import java.util. ArrayList ;' + }) + ).not.toBeInTheDocument(); + + await user.click(screen.getByRole('link', { name: '1 location 1' })); + expect( + screen.getByRole('row', { + name: '2 source_viewer.tooltip.covered import java.util. ArrayList ;' + }) + ).toBeInTheDocument(); +}); + describe('redirects', () => { it('should work for hotspots', () => { renderProjectIssuesApp(`project/issues?types=${IssueType.SecurityHotspot}`); diff --git a/server/sonar-web/src/main/js/apps/issues/__tests__/actions-test.ts b/server/sonar-web/src/main/js/apps/issues/__tests__/actions-test.ts index 4a4e212c399..9fd0d4f26fc 100644 --- a/server/sonar-web/src/main/js/apps/issues/__tests__/actions-test.ts +++ b/server/sonar-web/src/main/js/apps/issues/__tests__/actions-test.ts @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { mockIssue } from '../../../helpers/testMocks'; -import { enableLocationsNavigator, selectFlow, selectLocation } from '../actions'; +import { enableLocationsNavigator, selectFlow } from '../actions'; import { State } from '../components/IssuesApp'; describe('selectFlow', () => { @@ -97,23 +97,3 @@ describe('enableLocationsNavigator', () => { expect(enableLocationsNavigator({} as State)).toBeNull(); }); }); - -describe('selectLocation', () => { - it('should select location and enable locations navigator', () => { - expect(selectLocation(5)({ openIssue: mockIssue() })).toEqual({ - locationsNavigator: true, - selectedLocationIndex: 5 - }); - }); - - it('should deselect location when clicked again', () => { - expect(selectLocation(5)({ openIssue: mockIssue(), selectedLocationIndex: 5 })).toEqual({ - locationsNavigator: false, - selectedLocationIndex: undefined - }); - }); - - it('should ignore if no open issue', () => { - expect(selectLocation(5)({ openIssue: undefined })).toBeNull(); - }); -}); diff --git a/server/sonar-web/src/main/js/apps/issues/actions.ts b/server/sonar-web/src/main/js/apps/issues/actions.ts index 18ce6916045..94a84475d07 100644 --- a/server/sonar-web/src/main/js/apps/issues/actions.ts +++ b/server/sonar-web/src/main/js/apps/issues/actions.ts @@ -40,24 +40,6 @@ export function disableLocationsNavigator() { return { locationsNavigator: false }; } -export function selectLocation(nextIndex: number) { - return (state: Pick<State, 'selectedLocationIndex' | 'openIssue'>) => { - const { selectedLocationIndex: index, openIssue } = state; - if (openIssue) { - if (index === nextIndex) { - // disable locations when selecting (clicking) the same location - return { - locationsNavigator: false, - selectedLocationIndex: undefined - }; - } else { - return { locationsNavigator: true, selectedLocationIndex: nextIndex }; - } - } - return null; - }; -} - export function selectNextLocation( state: Pick<State, 'selectedFlowIndex' | 'selectedLocationIndex' | 'openIssue'> ) { diff --git a/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx b/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx index 2ec1d67ee9a..b3c2305d7ad 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx +++ b/server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx @@ -797,7 +797,19 @@ export class App extends React.PureComponent<Props, State> { }; selectLocation = (index: number) => { - this.setState(actions.selectLocation(index)); + const { selectedLocationIndex } = this.state; + if (index === selectedLocationIndex) { + this.setState({ selectedLocationIndex: undefined }, () => { + this.setState({ selectedLocationIndex: index }); + }); + } else { + this.setState(({ openIssue }) => { + if (openIssue) { + return { locationsNavigator: true, selectedLocationIndex: index }; + } + return null; + }); + } }; selectNextLocation = () => { diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/LineCode.tsx b/server/sonar-web/src/main/js/components/SourceViewer/components/LineCode.tsx index de2f6ebc605..498b2df1e9a 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/components/LineCode.tsx +++ b/server/sonar-web/src/main/js/components/SourceViewer/components/LineCode.tsx @@ -41,6 +41,16 @@ export default class LineCode extends React.PureComponent<React.PropsWithChildre activeMarkerNode?: HTMLElement | null; symbols?: NodeListOf<HTMLElement>; + componentDidMount() { + if (this.activeMarkerNode) { + this.activeMarkerNode.scrollIntoView({ + behavior: 'smooth', + block: 'center', + inline: 'center' + }); + } + } + componentDidUpdate(prevProps: Props) { if ( this.props.highlightedLocationMessage && diff --git a/server/sonar-web/src/main/js/components/rules/TabViewer.tsx b/server/sonar-web/src/main/js/components/rules/TabViewer.tsx index 92c90d1e4e4..c54cea37523 100644 --- a/server/sonar-web/src/main/js/components/rules/TabViewer.tsx +++ b/server/sonar-web/src/main/js/components/rules/TabViewer.tsx @@ -94,7 +94,12 @@ export class TabViewer extends React.PureComponent<TabViewerProps, State> { prevProps.codeTabContent !== codeTabContent || prevProps.currentUser !== currentUser ) { - this.setState(pState => this.computeState(pState, prevProps.ruleDetails !== ruleDetails)); + this.setState(pState => + this.computeState( + pState, + prevProps.ruleDetails !== ruleDetails || prevProps.codeTabContent !== codeTabContent + ) + ); } if (selectedTab?.key === TabKeys.MoreInfo) { |