Browse Source

drop /component url

tags/7.6
Stas Vilchik 5 years ago
parent
commit
a9d1146749
19 changed files with 158 additions and 214 deletions
  1. 0
    2
      server/sonar-web/src/main/js/app/utils/startReactApp.tsx
  2. 9
    4
      server/sonar-web/src/main/js/apps/code/components/App.tsx
  3. 54
    0
      server/sonar-web/src/main/js/apps/code/components/SourceViewerWrapper.tsx
  4. 0
    71
      server/sonar-web/src/main/js/apps/component/components/App.tsx
  5. 0
    22
      server/sonar-web/src/main/js/apps/component/components/__tests__/__snapshots__/App-test.tsx.snap
  6. 0
    28
      server/sonar-web/src/main/js/apps/component/routes.ts
  7. 3
    0
      server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/ComponentBreadcrumbs-test.tsx.snap
  8. 19
    16
      server/sonar-web/src/main/js/components/SourceViewer/SourceViewerBase.tsx
  9. 0
    1
      server/sonar-web/src/main/js/components/SourceViewer/SourceViewerCode.tsx
  10. 9
    7
      server/sonar-web/src/main/js/components/SourceViewer/SourceViewerContext.tsx
  11. 12
    9
      server/sonar-web/src/main/js/components/SourceViewer/SourceViewerHeader.tsx
  12. 0
    3
      server/sonar-web/src/main/js/components/SourceViewer/components/Line.tsx
  13. 2
    6
      server/sonar-web/src/main/js/components/SourceViewer/components/LineNumber.tsx
  14. 21
    15
      server/sonar-web/src/main/js/components/SourceViewer/components/LineOptionsPopup.tsx
  15. 2
    18
      server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineNumber-test.tsx
  16. 11
    7
      server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineOptionsPopup-test.tsx
  17. 0
    1
      server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineNumber-test.tsx.snap
  18. 6
    2
      server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineOptionsPopup-test.tsx.snap
  19. 10
    2
      server/sonar-web/src/main/js/helpers/urls.ts

+ 0
- 2
server/sonar-web/src/main/js/app/utils/startReactApp.tsx View File

@@ -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} />

+ 9
- 4
server/sonar-web/src/main/js/apps/code/components/App.tsx View File

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

+ 54
- 0
server/sonar-web/src/main/js/apps/code/components/SourceViewerWrapper.tsx View File

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

+ 0
- 71
server/sonar-web/src/main/js/apps/component/components/App.tsx View File

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

+ 0
- 22
server/sonar-web/src/main/js/apps/component/components/__tests__/__snapshots__/App-test.tsx.snap View File

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

+ 0
- 28
server/sonar-web/src/main/js/apps/component/routes.ts View File

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

+ 3
- 0
server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/ComponentBreadcrumbs-test.tsx.snap View File

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

+ 19
- 16
server/sonar-web/src/main/js/components/SourceViewer/SourceViewerBase.tsx View File

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

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

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

server/sonar-web/src/main/js/apps/component/components/__tests__/App-test.tsx → server/sonar-web/src/main/js/components/SourceViewer/SourceViewerContext.tsx View File

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

+ 12
- 9
server/sonar-web/src/main/js/components/SourceViewer/SourceViewerHeader.tsx View File

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

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

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

+ 2
- 6
server/sonar-web/src/main/js/components/SourceViewer/components/LineNumber.tsx View File

@@ -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>
) : (

+ 21
- 15
server/sonar-web/src/main/js/components/SourceViewer/components/LineOptionsPopup.tsx View File

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

+ 2
- 18
server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineNumber-test.tsx View File

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

+ 11
- 7
server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineOptionsPopup-test.tsx View File

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

+ 0
- 1
server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineNumber-test.tsx.snap View File

@@ -19,7 +19,6 @@ exports[`render line 3 1`] = `
open={false}
overlay={
<LineOptionsPopup
componentKey="foo"
line={
Object {
"line": 3,

+ 6
- 2
server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineOptionsPopup-test.tsx.snap View File

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

+ 10
- 2
server/sonar-web/src/main/js/helpers/urls.ts View File

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

Loading…
Cancel
Save