aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStas Vilchik <stas.vilchik@sonarsource.com>2019-01-02 15:43:44 +0100
committerSonarTech <sonartech@sonarsource.com>2019-01-07 20:21:00 +0100
commita9d11467496a6a30955921a24e88d78f1b165a5b (patch)
treefb3a6119315bbfd92869e6b4cdbdbd534c992370
parentb8484b43efee19dc518b4451fe5879d40f33a3dd (diff)
downloadsonarqube-a9d11467496a6a30955921a24e88d78f1b165a5b.tar.gz
sonarqube-a9d11467496a6a30955921a24e88d78f1b165a5b.zip
drop /component url
-rw-r--r--server/sonar-web/src/main/js/app/utils/startReactApp.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/App.tsx13
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/SourceViewerWrapper.tsx54
-rw-r--r--server/sonar-web/src/main/js/apps/component/components/App.tsx71
-rw-r--r--server/sonar-web/src/main/js/apps/component/components/__tests__/__snapshots__/App-test.tsx.snap22
-rw-r--r--server/sonar-web/src/main/js/apps/component/routes.ts28
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/ComponentBreadcrumbs-test.tsx.snap3
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/SourceViewerBase.tsx35
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/SourceViewerCode.tsx1
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/SourceViewerContext.tsx (renamed from server/sonar-web/src/main/js/apps/component/components/__tests__/App-test.tsx)16
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/SourceViewerHeader.tsx21
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/Line.tsx3
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/LineNumber.tsx8
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/LineOptionsPopup.tsx36
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineNumber-test.tsx20
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineOptionsPopup-test.tsx18
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineNumber-test.tsx.snap1
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineOptionsPopup-test.tsx.snap8
-rw-r--r--server/sonar-web/src/main/js/helpers/urls.ts12
19 files changed, 158 insertions, 214 deletions
diff --git a/server/sonar-web/src/main/js/app/utils/startReactApp.tsx b/server/sonar-web/src/main/js/app/utils/startReactApp.tsx
index 68ae6f8ccb5..781120376f3 100644
--- a/server/sonar-web/src/main/js/app/utils/startReactApp.tsx
+++ b/server/sonar-web/src/main/js/app/utils/startReactApp.tsx
@@ -34,7 +34,6 @@ import accountRoutes from '../../apps/account/routes';
import backgroundTasksRoutes from '../../apps/background-tasks/routes';
import codeRoutes from '../../apps/code/routes';
import codingRulesRoutes from '../../apps/coding-rules/routes';
-import componentRoutes from '../../apps/component/routes';
import componentMeasuresRoutes from '../../apps/component-measures/routes';
import customMeasuresRoutes from '../../apps/custom-measures/routes';
import groupsRoutes from '../../apps/groups/routes';
@@ -172,7 +171,6 @@ export default function startReactApp(
{!isSonarCloud() && (
<RouteWithChildRoutes path="coding_rules" childRoutes={codingRulesRoutes} />
)}
- <RouteWithChildRoutes path="component" childRoutes={componentRoutes} />
<RouteWithChildRoutes path="documentation" childRoutes={documentationRoutes} />
<Route path="explore" component={Explore}>
<Route path="issues" component={ExploreIssues} />
diff --git a/server/sonar-web/src/main/js/apps/code/components/App.tsx b/server/sonar-web/src/main/js/apps/code/components/App.tsx
index 02904305e0c..ca8f1d392fb 100644
--- a/server/sonar-web/src/main/js/apps/code/components/App.tsx
+++ b/server/sonar-web/src/main/js/apps/code/components/App.tsx
@@ -21,13 +21,14 @@ import * as React from 'react';
import * as classNames from 'classnames';
import { connect } from 'react-redux';
import Helmet from 'react-helmet';
+import { Location } from 'history';
import Components from './Components';
import Breadcrumbs from './Breadcrumbs';
import Search from './Search';
+import SourceViewerWrapper from './SourceViewerWrapper';
import { addComponent, addComponentBreadcrumbs, clearBucket } from '../bucket';
import { retrieveComponentChildren, retrieveComponent, loadMoreChildren } from '../utils';
import ListFooter from '../../../components/controls/ListFooter';
-import SourceViewer from '../../../components/SourceViewer/SourceViewer';
import Suggestions from '../../../app/components/embed-docs-modal/Suggestions';
import { fetchMetrics } from '../../../store/rootActions';
import { getMetrics } from '../../../store/rootReducer';
@@ -46,7 +47,7 @@ interface DispatchToProps {
interface OwnProps {
branchLike?: T.BranchLike;
component: T.Component;
- location: { query: { [x: string]: string } };
+ location: Pick<Location, 'query'>;
}
type Props = StateToProps & DispatchToProps & OwnProps;
@@ -175,7 +176,7 @@ export class App extends React.PureComponent<Props, State> {
};
render() {
- const { branchLike, component } = this.props;
+ const { branchLike, component, location } = this.props;
const { loading, baseComponent, components, breadcrumbs, total, sourceViewer } = this.state;
const shouldShowBreadcrumbs = breadcrumbs.length > 1;
@@ -224,7 +225,11 @@ export class App extends React.PureComponent<Props, State> {
{sourceViewer !== undefined && (
<div className="spacer-top">
- <SourceViewer branchLike={branchLike} component={sourceViewer.key} />
+ <SourceViewerWrapper
+ branchLike={branchLike}
+ component={sourceViewer.key}
+ location={location}
+ />
</div>
)}
</div>
diff --git a/server/sonar-web/src/main/js/apps/code/components/SourceViewerWrapper.tsx b/server/sonar-web/src/main/js/apps/code/components/SourceViewerWrapper.tsx
new file mode 100644
index 00000000000..a3797f28d28
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/code/components/SourceViewerWrapper.tsx
@@ -0,0 +1,54 @@
+/*
+ * 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 { Location } from 'history';
+import SourceViewer from '../../../components/SourceViewer/SourceViewer';
+import { scrollToElement } from '../../../helpers/scrolling';
+
+interface Props {
+ branchLike?: T.BranchLike;
+ component: string;
+ location: Pick<Location, 'query'>;
+}
+
+export default function SourceViewerWrapper({ branchLike, component, location }: Props) {
+ const { line } = location.query;
+
+ const scrollToLine = () => {
+ if (line) {
+ const row = document.querySelector(`.source-line[data-line-number="${line}"]`);
+ if (row) {
+ scrollToElement(row, { smooth: false, bottomOffset: window.innerHeight / 2 - 60 });
+ }
+ }
+ };
+
+ const finalLine = line ? Number(line) : undefined;
+
+ return (
+ <SourceViewer
+ aroundLine={finalLine}
+ branchLike={branchLike}
+ component={component}
+ highlightedLine={finalLine}
+ onLoaded={scrollToLine}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/component/components/App.tsx b/server/sonar-web/src/main/js/apps/component/components/App.tsx
deleted file mode 100644
index bef13324450..00000000000
--- a/server/sonar-web/src/main/js/apps/component/components/App.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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 SourceViewer from '../../../components/SourceViewer/SourceViewer';
-import { fillBranchLike } from '../../../helpers/branches';
-
-interface Props {
- location: {
- query: {
- branch?: string;
- id: string;
- line?: string;
- pullRequest?: string;
- };
- };
-}
-
-export default class App extends React.PureComponent<Props> {
- scrollToLine = () => {
- const { line } = this.props.location.query;
- if (line) {
- const row = document.querySelector(`.source-line[data-line-number="${line}"]`);
- if (row) {
- const rect = row.getBoundingClientRect();
- const topOffset = window.innerHeight / 2 - 60;
- const goal = rect.top - topOffset;
- window.scrollTo(0, goal);
- }
- }
- };
-
- render() {
- const { branch, id, line, pullRequest } = this.props.location.query;
-
- const finalLine = line ? Number(line) : undefined;
-
- // TODO find a way to avoid creating this fakeBranchLike
- // probably the best way would be to drop this page completely
- // and redirect to the Code page
- const fakeBranchLike = fillBranchLike(branch, pullRequest);
-
- return (
- <div className="page page-limited">
- <SourceViewer
- aroundLine={finalLine}
- branchLike={fakeBranchLike}
- component={id}
- highlightedLine={finalLine}
- onLoaded={this.scrollToLine}
- />
- </div>
- );
- }
-}
diff --git a/server/sonar-web/src/main/js/apps/component/components/__tests__/__snapshots__/App-test.tsx.snap b/server/sonar-web/src/main/js/apps/component/components/__tests__/__snapshots__/App-test.tsx.snap
deleted file mode 100644
index 80a0c87199c..00000000000
--- a/server/sonar-web/src/main/js/apps/component/components/__tests__/__snapshots__/App-test.tsx.snap
+++ /dev/null
@@ -1,22 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`renders 1`] = `
-<div
- className="page page-limited"
->
- <LazyLoader
- aroundLine={7}
- branchLike={
- Object {
- "isMain": false,
- "mergeBranch": "",
- "name": "b",
- "type": "SHORT",
- }
- }
- component="foo"
- highlightedLine={7}
- onLoaded={[Function]}
- />
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/component/routes.ts b/server/sonar-web/src/main/js/apps/component/routes.ts
deleted file mode 100644
index 094046e8466..00000000000
--- a/server/sonar-web/src/main/js/apps/component/routes.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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 { lazyLoad } from '../../components/lazyLoad';
-
-const routes = [
- {
- indexRoute: { component: lazyLoad(() => import('./components/App')) }
- }
-];
-
-export default routes;
diff --git a/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/ComponentBreadcrumbs-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/ComponentBreadcrumbs-test.tsx.snap
index c0182df2b97..dea3a478dcb 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/ComponentBreadcrumbs-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/ComponentBreadcrumbs-test.tsx.snap
@@ -41,6 +41,7 @@ exports[`renders 1`] = `
"pathname": "/code",
"query": Object {
"id": "proj",
+ "line": undefined,
"selected": "comp",
},
}
@@ -122,6 +123,7 @@ exports[`renders with branch 1`] = `
"query": Object {
"branch": "feature",
"id": "proj",
+ "line": undefined,
"selected": "comp",
},
}
@@ -200,6 +202,7 @@ exports[`renders with sub-project 1`] = `
"pathname": "/code",
"query": Object {
"id": "proj",
+ "line": undefined,
"selected": "comp",
},
}
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerBase.tsx b/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerBase.tsx
index 8d914ec0bec..db24f1d0ceb 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerBase.tsx
+++ b/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerBase.tsx
@@ -22,6 +22,7 @@ import * as classNames from 'classnames';
import { intersection, uniqBy } from 'lodash';
import SourceViewerHeader from './SourceViewerHeader';
import SourceViewerCode from './SourceViewerCode';
+import { SourceViewerContext } from './SourceViewerContext';
import DuplicationPopup from './components/DuplicationPopup';
import defaultLoadIssues from './helpers/loadIssues';
import getCoverageStatus from './helpers/getCoverageStatus';
@@ -703,23 +704,25 @@ export default class SourceViewerBase extends React.PureComponent<Props, State>
});
return (
- <div className={className} ref={node => (this.node = node)}>
- <WorkspaceContext.Consumer>
- {({ openComponent }) => (
- <SourceViewerHeader
- branchLike={this.props.branchLike}
- openComponent={openComponent}
- sourceViewerFile={component}
- />
+ <SourceViewerContext.Provider value={{ branchLike: this.props.branchLike, file: component }}>
+ <div className={className} ref={node => (this.node = node)}>
+ <WorkspaceContext.Consumer>
+ {({ openComponent }) => (
+ <SourceViewerHeader
+ branchLike={this.props.branchLike}
+ openComponent={openComponent}
+ sourceViewerFile={component}
+ />
+ )}
+ </WorkspaceContext.Consumer>
+ {sourceRemoved && (
+ <Alert className="spacer-top" variant="warning">
+ {translate('code_viewer.no_source_code_displayed_due_to_source_removed')}
+ </Alert>
)}
- </WorkspaceContext.Consumer>
- {sourceRemoved && (
- <Alert className="spacer-top" variant="warning">
- {translate('code_viewer.no_source_code_displayed_due_to_source_removed')}
- </Alert>
- )}
- {!sourceRemoved && sources !== undefined && this.renderCode(sources)}
- </div>
+ {!sourceRemoved && sources !== undefined && this.renderCode(sources)}
+ </div>
+ </SourceViewerContext.Provider>
);
}
}
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerCode.tsx b/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerCode.tsx
index 4b0c419e2ba..923f3b6ff9f 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerCode.tsx
+++ b/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerCode.tsx
@@ -156,7 +156,6 @@ export default class SourceViewerCode extends React.PureComponent<Props> {
return (
<Line
branchLike={this.props.branchLike}
- componentKey={this.props.componentKey}
displayAllIssues={this.props.displayAllIssues}
displayCoverage={displayCoverage}
displayDuplications={displayDuplications}
diff --git a/server/sonar-web/src/main/js/apps/component/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerContext.tsx
index 935c1680a6a..a546d03802f 100644
--- a/server/sonar-web/src/main/js/apps/component/components/__tests__/App-test.tsx
+++ b/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerContext.tsx
@@ -18,11 +18,13 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { shallow } from 'enzyme';
-import App from '../App';
-it('renders', () => {
- expect(
- shallow(<App location={{ query: { branch: 'b', id: 'foo', line: '7' } }} />)
- ).toMatchSnapshot();
-});
+interface SourceViewerContextShape {
+ branchLike?: T.BranchLike;
+ file: T.SourceViewerFile;
+}
+
+export const SourceViewerContext = React.createContext({
+ branchLike: {},
+ file: {}
+}) as React.Context<SourceViewerContextShape>;
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerHeader.tsx b/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerHeader.tsx
index 53e896eff12..bd11c8c615d 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerHeader.tsx
+++ b/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerHeader.tsx
@@ -32,7 +32,8 @@ import {
getPathUrlAsString,
getBranchLikeUrl,
getComponentIssuesUrl,
- getBaseUrl
+ getBaseUrl,
+ getCodeUrl
} from '../../helpers/urls';
import { collapsedDirFromPath, fileFromPath } from '../../helpers/path';
import { translate } from '../../helpers/l10n';
@@ -136,15 +137,13 @@ export default class SourceViewerHeader extends React.PureComponent<Props, State
</a>
</li>
<li>
- <a
+ <Link
className="js-new-window"
- href={getPathUrlAsString({
- pathname: '/component',
- query: { id: key, ...getBranchLikeQuery(this.props.branchLike) }
- })}
- target="_blank">
+ rel="noopener noreferrer"
+ target="_blank"
+ to={getCodeUrl(this.props.sourceViewerFile.project, this.props.branchLike, key)}>
{translate('component_viewer.new_window')}
- </a>
+ </Link>
</li>
{!workspace && (
<li>
@@ -154,7 +153,11 @@ export default class SourceViewerHeader extends React.PureComponent<Props, State
</li>
)}
<li>
- <a className="js-raw-source" href={rawSourcesLink} target="_blank">
+ <a
+ className="js-raw-source"
+ href={rawSourcesLink}
+ rel="noopener noreferrer"
+ target="_blank">
{translate('component_viewer.show_raw_source')}
</a>
</li>
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/Line.tsx b/server/sonar-web/src/main/js/components/SourceViewer/components/Line.tsx
index e7f315caec5..7955f4df686 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/components/Line.tsx
+++ b/server/sonar-web/src/main/js/components/SourceViewer/components/Line.tsx
@@ -30,7 +30,6 @@ import LineCode from './LineCode';
interface Props {
branchLike: T.BranchLike | undefined;
- componentKey: string;
displayAllIssues?: boolean;
displayCoverage: boolean;
displayDuplications: boolean;
@@ -112,8 +111,6 @@ export default class Line extends React.PureComponent<Props> {
return (
<tr className={className} data-line-number={line.line}>
<LineNumber
- branchLike={this.props.branchLike}
- componentKey={this.props.componentKey}
line={line}
onPopupToggle={this.props.onLinePopupToggle}
popupOpen={this.isPopupOpen('line-number')}
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/LineNumber.tsx b/server/sonar-web/src/main/js/components/SourceViewer/components/LineNumber.tsx
index c190e5a004c..5d90fb7acdd 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/components/LineNumber.tsx
+++ b/server/sonar-web/src/main/js/components/SourceViewer/components/LineNumber.tsx
@@ -22,8 +22,6 @@ import LineOptionsPopup from './LineOptionsPopup';
import Toggler from '../../controls/Toggler';
interface Props {
- branchLike: T.BranchLike | undefined;
- componentKey: string;
line: T.SourceLine;
onPopupToggle: (x: { index?: number; line: number; name: string; open?: boolean }) => void;
popupOpen: boolean;
@@ -46,7 +44,7 @@ export default class LineNumber extends React.PureComponent<Props> {
};
render() {
- const { branchLike, componentKey, line, popupOpen } = this.props;
+ const { line, popupOpen } = this.props;
const { line: lineNumber } = line;
const hasLineNumber = !!lineNumber;
return hasLineNumber ? (
@@ -60,9 +58,7 @@ export default class LineNumber extends React.PureComponent<Props> {
<Toggler
onRequestClose={this.closePopup}
open={popupOpen}
- overlay={
- <LineOptionsPopup branchLike={branchLike} componentKey={componentKey} line={line} />
- }
+ overlay={<LineOptionsPopup line={line} />}
/>
</td>
) : (
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/LineOptionsPopup.tsx b/server/sonar-web/src/main/js/components/SourceViewer/components/LineOptionsPopup.tsx
index 24875c9404d..5beb76d4c8c 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/components/LineOptionsPopup.tsx
+++ b/server/sonar-web/src/main/js/components/SourceViewer/components/LineOptionsPopup.tsx
@@ -22,26 +22,32 @@ import { Link } from 'react-router';
import { DropdownOverlay } from '../../controls/Dropdown';
import { PopupPlacement } from '../../ui/popups';
import { translate } from '../../../helpers/l10n';
-import { getBranchLikeQuery } from '../../../helpers/branches';
+import { getCodeUrl } from '../../../helpers/urls';
+import { SourceViewerContext } from '../SourceViewerContext';
interface Props {
- branchLike: T.BranchLike | undefined;
- componentKey: string;
line: T.SourceLine;
}
-export default function LineOptionsPopup({ branchLike, componentKey, line }: Props) {
- const permalink = {
- pathname: '/component',
- query: { id: componentKey, line: line.line, ...getBranchLikeQuery(branchLike) }
- };
+export default function LineOptionsPopup({ line }: Props) {
return (
- <DropdownOverlay placement={PopupPlacement.RightTop}>
- <div className="source-viewer-bubble-popup nowrap">
- <Link className="js-get-permalink" to={permalink}>
- {translate('component_viewer.get_permalink')}
- </Link>
- </div>
- </DropdownOverlay>
+ <SourceViewerContext.Consumer>
+ {({ branchLike, file }) => (
+ <DropdownOverlay placement={PopupPlacement.RightTop}>
+ <div className="source-viewer-bubble-popup nowrap">
+ <Link
+ className="js-get-permalink"
+ onClick={event => {
+ event.stopPropagation();
+ }}
+ rel="noopener noreferrer"
+ target="_blank"
+ to={getCodeUrl(file.project, branchLike, file.key, line.line)}>
+ {translate('component_viewer.get_permalink')}
+ </Link>
+ </div>
+ </DropdownOverlay>
+ )}
+ </SourceViewerContext.Consumer>
);
}
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineNumber-test.tsx b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineNumber-test.tsx
index 03cdce961f5..2b4a7453a9f 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineNumber-test.tsx
+++ b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineNumber-test.tsx
@@ -24,29 +24,13 @@ import LineNumber from '../LineNumber';
it('render line 3', () => {
const line = { line: 3 };
- const wrapper = shallow(
- <LineNumber
- branchLike={undefined}
- componentKey="foo"
- line={line}
- onPopupToggle={jest.fn()}
- popupOpen={false}
- />
- );
+ const wrapper = shallow(<LineNumber line={line} onPopupToggle={jest.fn()} popupOpen={false} />);
expect(wrapper).toMatchSnapshot();
click(wrapper);
});
it('render line 0', () => {
const line = { line: 0 };
- const wrapper = shallow(
- <LineNumber
- branchLike={undefined}
- componentKey="foo"
- line={line}
- onPopupToggle={jest.fn()}
- popupOpen={false}
- />
- );
+ const wrapper = shallow(<LineNumber line={line} onPopupToggle={jest.fn()} popupOpen={false} />);
expect(wrapper).toMatchSnapshot();
});
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineOptionsPopup-test.tsx b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineOptionsPopup-test.tsx
index a3fce5c3135..4bdb873af4f 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineOptionsPopup-test.tsx
+++ b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineOptionsPopup-test.tsx
@@ -21,14 +21,18 @@ import * as React from 'react';
import { shallow } from 'enzyme';
import LineOptionsPopup from '../LineOptionsPopup';
+jest.mock('../../SourceViewerContext', () => ({
+ SourceViewerContext: {
+ Consumer: (props: any) =>
+ props.children({
+ branchLike: { isMain: false, name: 'feature', type: 'SHORT' },
+ file: { project: 'prj', key: 'foo' }
+ })
+ }
+}));
+
it('should render', () => {
const line = { line: 3 };
- const branch: T.ShortLivingBranch = {
- isMain: false,
- mergeBranch: 'master',
- name: 'feature',
- type: 'SHORT'
- };
- const wrapper = shallow(<LineOptionsPopup branchLike={branch} componentKey="foo" line={line} />);
+ const wrapper = shallow(<LineOptionsPopup line={line} />).dive();
expect(wrapper).toMatchSnapshot();
});
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineNumber-test.tsx.snap b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineNumber-test.tsx.snap
index 985f1f51937..a6bb1627d08 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineNumber-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineNumber-test.tsx.snap
@@ -19,7 +19,6 @@ exports[`render line 3 1`] = `
open={false}
overlay={
<LineOptionsPopup
- componentKey="foo"
line={
Object {
"line": 3,
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineOptionsPopup-test.tsx.snap b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineOptionsPopup-test.tsx.snap
index 96b360bb80a..936909da9dc 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineOptionsPopup-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineOptionsPopup-test.tsx.snap
@@ -9,15 +9,19 @@ exports[`should render 1`] = `
>
<Link
className="js-get-permalink"
+ onClick={[Function]}
onlyActiveOnIndex={false}
+ rel="noopener noreferrer"
style={Object {}}
+ target="_blank"
to={
Object {
- "pathname": "/component",
+ "pathname": "/code",
"query": Object {
"branch": "feature",
- "id": "foo",
+ "id": "prj",
"line": 3,
+ "selected": "foo",
},
}
}
diff --git a/server/sonar-web/src/main/js/helpers/urls.ts b/server/sonar-web/src/main/js/helpers/urls.ts
index f899255e255..c7090bc6770 100644
--- a/server/sonar-web/src/main/js/helpers/urls.ts
+++ b/server/sonar-web/src/main/js/helpers/urls.ts
@@ -219,8 +219,16 @@ export function getMarkdownHelpUrl(): string {
return getBaseUrl() + '/markdown/help';
}
-export function getCodeUrl(project: string, branchLike?: T.BranchLike, selected?: string) {
- return { pathname: '/code', query: { id: project, ...getBranchLikeQuery(branchLike), selected } };
+export function getCodeUrl(
+ project: string,
+ branchLike?: T.BranchLike,
+ selected?: string,
+ line?: number
+) {
+ return {
+ pathname: '/code',
+ query: { id: project, ...getBranchLikeQuery(branchLike), selected, line }
+ };
}
export function getOrganizationUrl(organization: string) {