Browse Source

SONAR-16536 Navigating to secondary location from any tab

tags/9.6.0.59041
Revanshu Paliwal 1 year ago
parent
commit
ab5fe3cabc

+ 2
- 0
server/sonar-web/src/main/js/api/mocks/IssuesServiceMock.ts View File

@@ -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,

+ 43
- 0
server/sonar-web/src/main/js/apps/issues/__tests__/IssueApp-it.tsx View File

@@ -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}`);

+ 1
- 21
server/sonar-web/src/main/js/apps/issues/__tests__/actions-test.ts View File

@@ -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();
});
});

+ 0
- 18
server/sonar-web/src/main/js/apps/issues/actions.ts View File

@@ -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'>
) {

+ 13
- 1
server/sonar-web/src/main/js/apps/issues/components/IssuesApp.tsx View File

@@ -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 = () => {

+ 10
- 0
server/sonar-web/src/main/js/components/SourceViewer/components/LineCode.tsx View File

@@ -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 &&

+ 6
- 1
server/sonar-web/src/main/js/components/rules/TabViewer.tsx View File

@@ -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) {

Loading…
Cancel
Save