import backgroundTasksRoutes from '../../apps/background-tasks/routes'; | import backgroundTasksRoutes from '../../apps/background-tasks/routes'; | ||||
import codeRoutes from '../../apps/code/routes'; | import codeRoutes from '../../apps/code/routes'; | ||||
import codingRulesRoutes from '../../apps/coding-rules/routes'; | import codingRulesRoutes from '../../apps/coding-rules/routes'; | ||||
import componentRoutes from '../../apps/component/routes'; | |||||
import componentMeasuresRoutes from '../../apps/component-measures/routes'; | import componentMeasuresRoutes from '../../apps/component-measures/routes'; | ||||
import customMeasuresRoutes from '../../apps/custom-measures/routes'; | import customMeasuresRoutes from '../../apps/custom-measures/routes'; | ||||
import groupsRoutes from '../../apps/groups/routes'; | import groupsRoutes from '../../apps/groups/routes'; | ||||
{!isSonarCloud() && ( | {!isSonarCloud() && ( | ||||
<RouteWithChildRoutes path="coding_rules" childRoutes={codingRulesRoutes} /> | <RouteWithChildRoutes path="coding_rules" childRoutes={codingRulesRoutes} /> | ||||
)} | )} | ||||
<RouteWithChildRoutes path="component" childRoutes={componentRoutes} /> | |||||
<RouteWithChildRoutes path="documentation" childRoutes={documentationRoutes} /> | <RouteWithChildRoutes path="documentation" childRoutes={documentationRoutes} /> | ||||
<Route path="explore" component={Explore}> | <Route path="explore" component={Explore}> | ||||
<Route path="issues" component={ExploreIssues} /> | <Route path="issues" component={ExploreIssues} /> |
import * as classNames from 'classnames'; | import * as classNames from 'classnames'; | ||||
import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||
import Helmet from 'react-helmet'; | import Helmet from 'react-helmet'; | ||||
import { Location } from 'history'; | |||||
import Components from './Components'; | import Components from './Components'; | ||||
import Breadcrumbs from './Breadcrumbs'; | import Breadcrumbs from './Breadcrumbs'; | ||||
import Search from './Search'; | import Search from './Search'; | ||||
import SourceViewerWrapper from './SourceViewerWrapper'; | |||||
import { addComponent, addComponentBreadcrumbs, clearBucket } from '../bucket'; | import { addComponent, addComponentBreadcrumbs, clearBucket } from '../bucket'; | ||||
import { retrieveComponentChildren, retrieveComponent, loadMoreChildren } from '../utils'; | import { retrieveComponentChildren, retrieveComponent, loadMoreChildren } from '../utils'; | ||||
import ListFooter from '../../../components/controls/ListFooter'; | import ListFooter from '../../../components/controls/ListFooter'; | ||||
import SourceViewer from '../../../components/SourceViewer/SourceViewer'; | |||||
import Suggestions from '../../../app/components/embed-docs-modal/Suggestions'; | import Suggestions from '../../../app/components/embed-docs-modal/Suggestions'; | ||||
import { fetchMetrics } from '../../../store/rootActions'; | import { fetchMetrics } from '../../../store/rootActions'; | ||||
import { getMetrics } from '../../../store/rootReducer'; | import { getMetrics } from '../../../store/rootReducer'; | ||||
interface OwnProps { | interface OwnProps { | ||||
branchLike?: T.BranchLike; | branchLike?: T.BranchLike; | ||||
component: T.Component; | component: T.Component; | ||||
location: { query: { [x: string]: string } }; | |||||
location: Pick<Location, 'query'>; | |||||
} | } | ||||
type Props = StateToProps & DispatchToProps & OwnProps; | type Props = StateToProps & DispatchToProps & OwnProps; | ||||
}; | }; | ||||
render() { | render() { | ||||
const { branchLike, component } = this.props; | |||||
const { branchLike, component, location } = this.props; | |||||
const { loading, baseComponent, components, breadcrumbs, total, sourceViewer } = this.state; | const { loading, baseComponent, components, breadcrumbs, total, sourceViewer } = this.state; | ||||
const shouldShowBreadcrumbs = breadcrumbs.length > 1; | const shouldShowBreadcrumbs = breadcrumbs.length > 1; | ||||
{sourceViewer !== undefined && ( | {sourceViewer !== undefined && ( | ||||
<div className="spacer-top"> | <div className="spacer-top"> | ||||
<SourceViewer branchLike={branchLike} component={sourceViewer.key} /> | |||||
<SourceViewerWrapper | |||||
branchLike={branchLike} | |||||
component={sourceViewer.key} | |||||
location={location} | |||||
/> | |||||
</div> | </div> | ||||
)} | )} | ||||
</div> | </div> |
/* | |||||
* 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} | |||||
/> | |||||
); | |||||
} |
/* | |||||
* 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> | |||||
); | |||||
} | |||||
} |
// 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> | |||||
`; |
/* | |||||
* 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; |
"pathname": "/code", | "pathname": "/code", | ||||
"query": Object { | "query": Object { | ||||
"id": "proj", | "id": "proj", | ||||
"line": undefined, | |||||
"selected": "comp", | "selected": "comp", | ||||
}, | }, | ||||
} | } | ||||
"query": Object { | "query": Object { | ||||
"branch": "feature", | "branch": "feature", | ||||
"id": "proj", | "id": "proj", | ||||
"line": undefined, | |||||
"selected": "comp", | "selected": "comp", | ||||
}, | }, | ||||
} | } | ||||
"pathname": "/code", | "pathname": "/code", | ||||
"query": Object { | "query": Object { | ||||
"id": "proj", | "id": "proj", | ||||
"line": undefined, | |||||
"selected": "comp", | "selected": "comp", | ||||
}, | }, | ||||
} | } |
import { intersection, uniqBy } from 'lodash'; | import { intersection, uniqBy } from 'lodash'; | ||||
import SourceViewerHeader from './SourceViewerHeader'; | import SourceViewerHeader from './SourceViewerHeader'; | ||||
import SourceViewerCode from './SourceViewerCode'; | import SourceViewerCode from './SourceViewerCode'; | ||||
import { SourceViewerContext } from './SourceViewerContext'; | |||||
import DuplicationPopup from './components/DuplicationPopup'; | import DuplicationPopup from './components/DuplicationPopup'; | ||||
import defaultLoadIssues from './helpers/loadIssues'; | import defaultLoadIssues from './helpers/loadIssues'; | ||||
import getCoverageStatus from './helpers/getCoverageStatus'; | import getCoverageStatus from './helpers/getCoverageStatus'; | ||||
}); | }); | ||||
return ( | 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> | |||||
); | ); | ||||
} | } | ||||
} | } |
return ( | return ( | ||||
<Line | <Line | ||||
branchLike={this.props.branchLike} | branchLike={this.props.branchLike} | ||||
componentKey={this.props.componentKey} | |||||
displayAllIssues={this.props.displayAllIssues} | displayAllIssues={this.props.displayAllIssues} | ||||
displayCoverage={displayCoverage} | displayCoverage={displayCoverage} | ||||
displayDuplications={displayDuplications} | displayDuplications={displayDuplications} |
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
*/ | */ | ||||
import * as React from 'react'; | 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>; |
getPathUrlAsString, | getPathUrlAsString, | ||||
getBranchLikeUrl, | getBranchLikeUrl, | ||||
getComponentIssuesUrl, | getComponentIssuesUrl, | ||||
getBaseUrl | |||||
getBaseUrl, | |||||
getCodeUrl | |||||
} from '../../helpers/urls'; | } from '../../helpers/urls'; | ||||
import { collapsedDirFromPath, fileFromPath } from '../../helpers/path'; | import { collapsedDirFromPath, fileFromPath } from '../../helpers/path'; | ||||
import { translate } from '../../helpers/l10n'; | import { translate } from '../../helpers/l10n'; | ||||
</a> | </a> | ||||
</li> | </li> | ||||
<li> | <li> | ||||
<a | |||||
<Link | |||||
className="js-new-window" | 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')} | {translate('component_viewer.new_window')} | ||||
</a> | |||||
</Link> | |||||
</li> | </li> | ||||
{!workspace && ( | {!workspace && ( | ||||
<li> | <li> | ||||
</li> | </li> | ||||
)} | )} | ||||
<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')} | {translate('component_viewer.show_raw_source')} | ||||
</a> | </a> | ||||
</li> | </li> |
interface Props { | interface Props { | ||||
branchLike: T.BranchLike | undefined; | branchLike: T.BranchLike | undefined; | ||||
componentKey: string; | |||||
displayAllIssues?: boolean; | displayAllIssues?: boolean; | ||||
displayCoverage: boolean; | displayCoverage: boolean; | ||||
displayDuplications: boolean; | displayDuplications: boolean; | ||||
return ( | return ( | ||||
<tr className={className} data-line-number={line.line}> | <tr className={className} data-line-number={line.line}> | ||||
<LineNumber | <LineNumber | ||||
branchLike={this.props.branchLike} | |||||
componentKey={this.props.componentKey} | |||||
line={line} | line={line} | ||||
onPopupToggle={this.props.onLinePopupToggle} | onPopupToggle={this.props.onLinePopupToggle} | ||||
popupOpen={this.isPopupOpen('line-number')} | popupOpen={this.isPopupOpen('line-number')} |
import Toggler from '../../controls/Toggler'; | import Toggler from '../../controls/Toggler'; | ||||
interface Props { | interface Props { | ||||
branchLike: T.BranchLike | undefined; | |||||
componentKey: string; | |||||
line: T.SourceLine; | line: T.SourceLine; | ||||
onPopupToggle: (x: { index?: number; line: number; name: string; open?: boolean }) => void; | onPopupToggle: (x: { index?: number; line: number; name: string; open?: boolean }) => void; | ||||
popupOpen: boolean; | popupOpen: boolean; | ||||
}; | }; | ||||
render() { | render() { | ||||
const { branchLike, componentKey, line, popupOpen } = this.props; | |||||
const { line, popupOpen } = this.props; | |||||
const { line: lineNumber } = line; | const { line: lineNumber } = line; | ||||
const hasLineNumber = !!lineNumber; | const hasLineNumber = !!lineNumber; | ||||
return hasLineNumber ? ( | return hasLineNumber ? ( | ||||
<Toggler | <Toggler | ||||
onRequestClose={this.closePopup} | onRequestClose={this.closePopup} | ||||
open={popupOpen} | open={popupOpen} | ||||
overlay={ | |||||
<LineOptionsPopup branchLike={branchLike} componentKey={componentKey} line={line} /> | |||||
} | |||||
overlay={<LineOptionsPopup line={line} />} | |||||
/> | /> | ||||
</td> | </td> | ||||
) : ( | ) : ( |
import { DropdownOverlay } from '../../controls/Dropdown'; | import { DropdownOverlay } from '../../controls/Dropdown'; | ||||
import { PopupPlacement } from '../../ui/popups'; | import { PopupPlacement } from '../../ui/popups'; | ||||
import { translate } from '../../../helpers/l10n'; | import { translate } from '../../../helpers/l10n'; | ||||
import { getBranchLikeQuery } from '../../../helpers/branches'; | |||||
import { getCodeUrl } from '../../../helpers/urls'; | |||||
import { SourceViewerContext } from '../SourceViewerContext'; | |||||
interface Props { | interface Props { | ||||
branchLike: T.BranchLike | undefined; | |||||
componentKey: string; | |||||
line: T.SourceLine; | 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 ( | 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> | |||||
); | ); | ||||
} | } |
it('render line 3', () => { | it('render line 3', () => { | ||||
const line = { 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(); | expect(wrapper).toMatchSnapshot(); | ||||
click(wrapper); | click(wrapper); | ||||
}); | }); | ||||
it('render line 0', () => { | it('render line 0', () => { | ||||
const line = { 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(); | expect(wrapper).toMatchSnapshot(); | ||||
}); | }); |
import { shallow } from 'enzyme'; | import { shallow } from 'enzyme'; | ||||
import LineOptionsPopup from '../LineOptionsPopup'; | 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', () => { | it('should render', () => { | ||||
const line = { line: 3 }; | 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(); | expect(wrapper).toMatchSnapshot(); | ||||
}); | }); |
open={false} | open={false} | ||||
overlay={ | overlay={ | ||||
<LineOptionsPopup | <LineOptionsPopup | ||||
componentKey="foo" | |||||
line={ | line={ | ||||
Object { | Object { | ||||
"line": 3, | "line": 3, |
> | > | ||||
<Link | <Link | ||||
className="js-get-permalink" | className="js-get-permalink" | ||||
onClick={[Function]} | |||||
onlyActiveOnIndex={false} | onlyActiveOnIndex={false} | ||||
rel="noopener noreferrer" | |||||
style={Object {}} | style={Object {}} | ||||
target="_blank" | |||||
to={ | to={ | ||||
Object { | Object { | ||||
"pathname": "/component", | |||||
"pathname": "/code", | |||||
"query": Object { | "query": Object { | ||||
"branch": "feature", | "branch": "feature", | ||||
"id": "foo", | |||||
"id": "prj", | |||||
"line": 3, | "line": 3, | ||||
"selected": "foo", | |||||
}, | }, | ||||
} | } | ||||
} | } |
return getBaseUrl() + '/markdown/help'; | 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) { | export function getOrganizationUrl(organization: string) { |