@@ -23,13 +23,12 @@ import { differenceBy } from 'lodash'; | |||
import { ComponentContext } from './ComponentContext'; | |||
import ComponentContainerNotFound from './ComponentContainerNotFound'; | |||
import ComponentNav from './nav/component/ComponentNav'; | |||
import handleRequiredAuthorization from '../utils/handleRequiredAuthorization'; | |||
import { getBranches, getPullRequests } from '../../api/branches'; | |||
import { getTasksForComponent, getAnalysisStatus } from '../../api/ce'; | |||
import { getComponentData } from '../../api/components'; | |||
import { getMeasures } from '../../api/measures'; | |||
import { getComponentNavigation } from '../../api/nav'; | |||
import { fetchOrganization } from '../../store/rootActions'; | |||
import { fetchOrganization, requireAuthorization } from '../../store/rootActions'; | |||
import { STATUSES } from '../../apps/background-tasks/constants'; | |||
import { | |||
isPullRequest, | |||
@@ -39,15 +38,15 @@ import { | |||
isShortLivingBranch, | |||
getBranchLikeQuery | |||
} from '../../helpers/branches'; | |||
import { Store, getAppState } from '../../store/rootReducer'; | |||
import { isSonarCloud } from '../../helpers/system'; | |||
import { withRouter, Router, Location } from '../../components/hoc/withRouter'; | |||
interface Props { | |||
appState: Pick<T.AppState, 'organizationsEnabled'>; | |||
children: any; | |||
children: React.ReactElement<any>; | |||
fetchOrganization: (organization: string) => void; | |||
location: { | |||
query: { branch?: string; id: string; pullRequest?: string }; | |||
}; | |||
location: Pick<Location, 'query'>; | |||
requireAuthorization: (router: Pick<Router, 'replace'>) => void; | |||
router: Pick<Router, 'replace'>; | |||
} | |||
interface State { | |||
@@ -74,13 +73,13 @@ export class ComponentContainer extends React.PureComponent<Props, State> { | |||
this.fetchComponent(); | |||
} | |||
componentWillReceiveProps(nextProps: Props) { | |||
componentDidUpdate(prevProps: Props) { | |||
if ( | |||
nextProps.location.query.id !== this.props.location.query.id || | |||
nextProps.location.query.branch !== this.props.location.query.branch || | |||
nextProps.location.query.pullRequest !== this.props.location.query.pullRequest | |||
prevProps.location.query.id !== this.props.location.query.id || | |||
prevProps.location.query.branch !== this.props.location.query.branch || | |||
prevProps.location.query.pullRequest !== this.props.location.query.pullRequest | |||
) { | |||
this.fetchComponent(nextProps); | |||
this.fetchComponent(); | |||
} | |||
} | |||
@@ -94,16 +93,16 @@ export class ComponentContainer extends React.PureComponent<Props, State> { | |||
qualifier: component.breadcrumbs[component.breadcrumbs.length - 1].qualifier | |||
}); | |||
fetchComponent(props = this.props) { | |||
const { branch, id: key, pullRequest } = props.location.query; | |||
fetchComponent() { | |||
const { branch, id: key, pullRequest } = this.props.location.query; | |||
this.setState({ loading: true }); | |||
const onError = (response?: Response) => { | |||
if (this.mounted) { | |||
if (response && response.status === 403) { | |||
handleRequiredAuthorization(); | |||
this.props.requireAuthorization(this.props.router); | |||
} else { | |||
this.setState({ loading: false }); | |||
this.setState({ component: undefined, loading: false }); | |||
} | |||
} | |||
}; | |||
@@ -115,7 +114,7 @@ export class ComponentContainer extends React.PureComponent<Props, State> { | |||
.then(([nav, data]) => { | |||
const component = this.addQualifier({ ...nav, ...data }); | |||
if (this.props.appState.organizationsEnabled) { | |||
if (isSonarCloud()) { | |||
this.props.fetchOrganization(component.organization); | |||
} | |||
return component; | |||
@@ -375,13 +374,11 @@ export class ComponentContainer extends React.PureComponent<Props, State> { | |||
} | |||
} | |||
const mapStateToProps = (state: Store) => ({ | |||
appState: getAppState(state) | |||
}); | |||
const mapDispatchToProps = { fetchOrganization }; | |||
const mapDispatchToProps = { fetchOrganization, requireAuthorization }; | |||
export default connect( | |||
mapStateToProps, | |||
mapDispatchToProps | |||
)(ComponentContainer); | |||
export default withRouter( | |||
connect( | |||
null, | |||
mapDispatchToProps | |||
)(ComponentContainer) | |||
); |
@@ -18,7 +18,8 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import { shallow, mount } from 'enzyme'; | |||
import { shallow } from 'enzyme'; | |||
import { Location } from 'history'; | |||
import { ComponentContainer } from '../ComponentContainer'; | |||
import { getBranches, getPullRequests } from '../../../api/branches'; | |||
import { getTasksForComponent } from '../../../api/ce'; | |||
@@ -27,6 +28,7 @@ import { getComponentNavigation } from '../../../api/nav'; | |||
import { STATUSES } from '../../../apps/background-tasks/constants'; | |||
import { waitAndUpdate } from '../../../helpers/testUtils'; | |||
import { getMeasures } from '../../../api/measures'; | |||
import { isSonarCloud } from '../../../helpers/system'; | |||
jest.mock('../../../api/branches', () => ({ | |||
getBranches: jest.fn().mockResolvedValue([]), | |||
@@ -58,6 +60,10 @@ jest.mock('../../../api/nav', () => ({ | |||
}) | |||
})); | |||
jest.mock('../../../helpers/system', () => ({ | |||
isSonarCloud: jest.fn() | |||
})); | |||
// mock this, because some of its children are using redux store | |||
jest.mock('../nav/component/ComponentNav', () => ({ | |||
default: () => null | |||
@@ -65,6 +71,8 @@ jest.mock('../nav/component/ComponentNav', () => ({ | |||
const Inner = () => <div />; | |||
const mainBranch: T.MainBranch = { isMain: true, name: 'master' }; | |||
beforeEach(() => { | |||
(getBranches as jest.Mock).mockClear(); | |||
(getPullRequests as jest.Mock).mockClear(); | |||
@@ -72,18 +80,11 @@ beforeEach(() => { | |||
(getComponentNavigation as jest.Mock).mockClear(); | |||
(getTasksForComponent as jest.Mock).mockClear(); | |||
(getMeasures as jest.Mock).mockClear(); | |||
(isSonarCloud as jest.Mock).mockReturnValue(false).mockClear(); | |||
}); | |||
it('changes component', () => { | |||
const wrapper = shallow<ComponentContainer>( | |||
<ComponentContainer | |||
appState={{ organizationsEnabled: false }} | |||
fetchOrganization={jest.fn()} | |||
location={{ query: { id: 'foo' } }}> | |||
<Inner /> | |||
</ComponentContainer> | |||
); | |||
wrapper.instance().mounted = true; | |||
const wrapper = shallowRender(); | |||
wrapper.setState({ | |||
branchLikes: [{ isMain: true, name: 'master' }], | |||
component: { qualifier: 'TRK', visibility: 'public' } as T.Component, | |||
@@ -94,40 +95,8 @@ it('changes component', () => { | |||
expect(wrapper.state().component).toEqual({ qualifier: 'TRK', visibility: 'private' }); | |||
}); | |||
it("loads branches for module's project", async () => { | |||
(getComponentNavigation as jest.Mock<any>).mockResolvedValueOnce({ | |||
breadcrumbs: [ | |||
{ key: 'projectKey', name: 'project', qualifier: 'TRK' }, | |||
{ key: 'moduleKey', name: 'module', qualifier: 'BRC' } | |||
] | |||
}); | |||
mount( | |||
<ComponentContainer | |||
appState={{ organizationsEnabled: false }} | |||
fetchOrganization={jest.fn()} | |||
location={{ query: { id: 'moduleKey' } }}> | |||
<Inner /> | |||
</ComponentContainer> | |||
); | |||
await new Promise(setImmediate); | |||
expect(getBranches).toBeCalledWith('projectKey'); | |||
expect(getPullRequests).toBeCalledWith('projectKey'); | |||
expect(getComponentData).toBeCalledWith({ component: 'moduleKey', branch: undefined }); | |||
expect(getComponentNavigation).toBeCalledWith({ component: 'moduleKey', branch: undefined }); | |||
}); | |||
it("doesn't load branches portfolio", async () => { | |||
const wrapper = mount( | |||
<ComponentContainer | |||
appState={{ organizationsEnabled: false }} | |||
fetchOrganization={jest.fn()} | |||
location={{ query: { id: 'portfolioKey' } }}> | |||
<Inner /> | |||
</ComponentContainer> | |||
); | |||
const wrapper = shallowRender({ location: { query: { id: 'portfolioKey' } } as Location }); | |||
await new Promise(setImmediate); | |||
expect(getBranches).not.toBeCalled(); | |||
expect(getPullRequests).not.toBeCalled(); | |||
@@ -138,21 +107,15 @@ it("doesn't load branches portfolio", async () => { | |||
}); | |||
it('updates branches on change', () => { | |||
const wrapper = shallow( | |||
<ComponentContainer | |||
appState={{ organizationsEnabled: false }} | |||
fetchOrganization={jest.fn()} | |||
location={{ query: { id: 'portfolioKey' } }}> | |||
<Inner /> | |||
</ComponentContainer> | |||
); | |||
(wrapper.instance() as ComponentContainer).mounted = true; | |||
const wrapper = shallowRender({ location: { query: { id: 'portfolioKey' } } as Location }); | |||
wrapper.setState({ | |||
branches: [{ isMain: true }], | |||
component: { breadcrumbs: [{ key: 'projectKey', name: 'project', qualifier: 'TRK' }] }, | |||
branchLikes: [mainBranch], | |||
component: { | |||
breadcrumbs: [{ key: 'projectKey', name: 'project', qualifier: 'TRK' }] | |||
} as T.Component, | |||
loading: false | |||
}); | |||
(wrapper.find(Inner).prop('onBranchesChange') as Function)(); | |||
wrapper.find(Inner).prop<Function>('onBranchesChange')(); | |||
expect(getBranches).toBeCalledWith('projectKey'); | |||
expect(getPullRequests).toBeCalledWith('projectKey'); | |||
}); | |||
@@ -166,18 +129,12 @@ it('updates the branch measures', async () => { | |||
{ isMain: false, mergeBranch: 'master', name: 'feature', type: 'SHORT' } | |||
]); | |||
(getPullRequests as jest.Mock<any>).mockResolvedValueOnce([]); | |||
const wrapper = shallow( | |||
<ComponentContainer | |||
appState={{ organizationsEnabled: false }} | |||
fetchOrganization={jest.fn()} | |||
location={{ query: { id: 'foo', branch: 'feature' } }}> | |||
<Inner /> | |||
</ComponentContainer> | |||
); | |||
(wrapper.instance() as ComponentContainer).mounted = true; | |||
const wrapper = shallowRender({ | |||
location: { query: { id: 'foo', branch: 'feature' } } as Location | |||
}); | |||
wrapper.setState({ | |||
branches: [{ isMain: true }], | |||
component: { breadcrumbs: [{ key: 'foo', name: 'Foo', qualifier: 'TRK' }] }, | |||
branchLikes: [mainBranch], | |||
component: { breadcrumbs: [{ key: 'foo', name: 'Foo', qualifier: 'TRK' }] } as T.Component, | |||
loading: false | |||
}); | |||
@@ -193,18 +150,11 @@ it('updates the branch measures', async () => { | |||
}); | |||
it('loads organization', async () => { | |||
(isSonarCloud as jest.Mock).mockReturnValue(true); | |||
(getComponentData as jest.Mock<any>).mockResolvedValueOnce({ organization: 'org' }); | |||
const fetchOrganization = jest.fn(); | |||
mount( | |||
<ComponentContainer | |||
appState={{ organizationsEnabled: true }} | |||
fetchOrganization={fetchOrganization} | |||
location={{ query: { id: 'foo' } }}> | |||
<Inner /> | |||
</ComponentContainer> | |||
); | |||
shallowRender({ fetchOrganization }); | |||
await new Promise(setImmediate); | |||
expect(fetchOrganization).toBeCalledWith('org'); | |||
}); | |||
@@ -212,30 +162,14 @@ it('loads organization', async () => { | |||
it('fetches status', async () => { | |||
(getComponentData as jest.Mock<any>).mockResolvedValueOnce({ organization: 'org' }); | |||
mount( | |||
<ComponentContainer | |||
appState={{ organizationsEnabled: true }} | |||
fetchOrganization={jest.fn()} | |||
location={{ query: { id: 'foo' } }}> | |||
<Inner /> | |||
</ComponentContainer> | |||
); | |||
shallowRender(); | |||
await new Promise(setImmediate); | |||
expect(getTasksForComponent).toBeCalledWith('portfolioKey'); | |||
}); | |||
it('filters correctly the pending tasks for a main branch', () => { | |||
const wrapper = shallow( | |||
<ComponentContainer | |||
appState={{ organizationsEnabled: false }} | |||
fetchOrganization={jest.fn()} | |||
location={{ query: { id: 'foo' } }}> | |||
<Inner /> | |||
</ComponentContainer> | |||
); | |||
const component = wrapper.instance() as ComponentContainer; | |||
const wrapper = shallowRender(); | |||
const component = wrapper.instance(); | |||
const mainBranch: T.MainBranch = { isMain: true, name: 'master' }; | |||
const shortBranch: T.ShortLivingBranch = { | |||
isMain: false, | |||
@@ -294,14 +228,7 @@ it('reload component after task progress finished', async () => { | |||
jest.useFakeTimers(); | |||
const inProgressTask = { id: 'foo', status: STATUSES.IN_PROGRESS } as T.Task; | |||
(getTasksForComponent as jest.Mock<any>).mockResolvedValueOnce({ queue: [inProgressTask] }); | |||
const wrapper = shallow( | |||
<ComponentContainer | |||
appState={{ organizationsEnabled: false }} | |||
fetchOrganization={jest.fn()} | |||
location={{ query: { id: 'foo' } }}> | |||
<Inner /> | |||
</ComponentContainer> | |||
); | |||
const wrapper = shallowRender(); | |||
await waitAndUpdate(wrapper); | |||
expect(getComponentNavigation).toHaveBeenCalledTimes(1); | |||
expect(getTasksForComponent).toHaveBeenCalledTimes(1); | |||
@@ -317,3 +244,16 @@ it('reload component after task progress finished', async () => { | |||
expect(getComponentNavigation).toHaveBeenCalledTimes(2); | |||
expect(getTasksForComponent).toHaveBeenCalledTimes(3); | |||
}); | |||
function shallowRender(props: Partial<ComponentContainer['props']> = {}) { | |||
return shallow<ComponentContainer>( | |||
<ComponentContainer | |||
fetchOrganization={jest.fn()} | |||
location={{ query: { id: 'foo' } }} | |||
requireAuthorization={jest.fn()} | |||
router={{ replace: jest.fn() }} | |||
{...props}> | |||
<Inner /> | |||
</ComponentContainer> | |||
); | |||
} |
@@ -27,7 +27,6 @@ import OrganizationAvatar from '../../../../components/common/OrganizationAvatar | |||
import OrganizationHelmet from '../../../../components/common/OrganizationHelmet'; | |||
import OrganizationLink from '../../../../components/ui/OrganizationLink'; | |||
import { sanitizeAlmId } from '../../../../helpers/almIntegrations'; | |||
import { collapsePath } from '../../../../helpers/path'; | |||
import { getProjectUrl, getBaseUrl } from '../../../../helpers/urls'; | |||
import { isSonarCloud } from '../../../../helpers/system'; | |||
import { isMainBranch } from '../../../../helpers/branches'; | |||
@@ -102,9 +101,6 @@ export function ComponentNavHeader(props: Props) { | |||
function renderBreadcrumbs(breadcrumbs: T.Breadcrumb[], shouldLinkLast: boolean) { | |||
const lastItem = breadcrumbs[breadcrumbs.length - 1]; | |||
return breadcrumbs.map((item, index) => { | |||
const isPath = item.qualifier === 'DIR'; | |||
const itemName = isPath ? collapsePath(item.name, 15) : item.name; | |||
return ( | |||
<React.Fragment key={item.key}> | |||
{index === 0 && <QualifierIcon className="spacer-right" qualifier={lastItem.qualifier} />} | |||
@@ -113,11 +109,11 @@ function renderBreadcrumbs(breadcrumbs: T.Breadcrumb[], shouldLinkLast: boolean) | |||
className="navbar-context-header-breadcrumb-link link-base-color link-no-underline" | |||
title={item.name} | |||
to={getProjectUrl(item.key)}> | |||
{itemName} | |||
{item.name} | |||
</Link> | |||
) : ( | |||
<span className="navbar-context-header-breadcrumb-link" title={item.name}> | |||
{itemName} | |||
{item.name} | |||
</span> | |||
)} | |||
{index < breadcrumbs.length - 1 && <span className="slash-separator" />} |
@@ -182,11 +182,7 @@ export class ComponentNavMenu extends React.PureComponent<Props> { | |||
} | |||
renderSecurityReports() { | |||
const { branchLike, component } = this.props; | |||
if (component.qualifier === 'BRC' || component.qualifier === 'DIR') { | |||
return null; | |||
} | |||
const { branchLike } = this.props; | |||
if (isShortLivingBranch(branchLike) || isPullRequest(branchLike)) { | |||
return null; |
@@ -114,8 +114,8 @@ it('should work for long-living branches', () => { | |||
}); | |||
it('should work for all qualifiers', () => { | |||
['TRK', 'BRC', 'VW', 'SVW', 'APP'].forEach(checkWithQualifier); | |||
expect.assertions(5); | |||
['TRK', 'VW', 'SVW', 'APP'].forEach(checkWithQualifier); | |||
expect.assertions(4); | |||
function checkWithQualifier(qualifier: string) { | |||
const component = { ...baseComponent, configuration: { showSettings: true }, qualifier }; |
@@ -218,127 +218,6 @@ exports[`should work for all qualifiers 1`] = ` | |||
`; | |||
exports[`should work for all qualifiers 2`] = ` | |||
<NavBarTabs> | |||
<li> | |||
<Link | |||
activeClassName="active" | |||
onlyActiveOnIndex={false} | |||
style={Object {}} | |||
to={ | |||
Object { | |||
"pathname": "/dashboard", | |||
"query": Object { | |||
"id": "foo", | |||
}, | |||
} | |||
} | |||
> | |||
overview.page | |||
</Link> | |||
</li> | |||
<li> | |||
<Link | |||
activeClassName="active" | |||
className="" | |||
onlyActiveOnIndex={false} | |||
style={Object {}} | |||
to={ | |||
Object { | |||
"pathname": "/project/issues", | |||
"query": Object { | |||
"id": "foo", | |||
"resolved": "false", | |||
}, | |||
} | |||
} | |||
> | |||
issues.page | |||
</Link> | |||
</li> | |||
<li> | |||
<Link | |||
activeClassName="active" | |||
onlyActiveOnIndex={false} | |||
style={Object {}} | |||
to={ | |||
Object { | |||
"pathname": "/component_measures", | |||
"query": Object { | |||
"id": "foo", | |||
}, | |||
} | |||
} | |||
> | |||
layout.measures | |||
</Link> | |||
</li> | |||
<li> | |||
<Link | |||
activeClassName="active" | |||
onlyActiveOnIndex={false} | |||
style={Object {}} | |||
to={ | |||
Object { | |||
"pathname": "/code", | |||
"query": Object { | |||
"id": "foo", | |||
}, | |||
} | |||
} | |||
> | |||
code.page | |||
</Link> | |||
</li> | |||
<li> | |||
<Link | |||
activeClassName="active" | |||
onlyActiveOnIndex={false} | |||
style={Object {}} | |||
to={ | |||
Object { | |||
"pathname": "/project/activity", | |||
"query": Object { | |||
"id": "foo", | |||
}, | |||
} | |||
} | |||
> | |||
project_activity.page | |||
</Link> | |||
</li> | |||
<Dropdown | |||
data-test="administration" | |||
overlay={ | |||
<ul | |||
className="menu" | |||
> | |||
<li> | |||
<Link | |||
activeClassName="active" | |||
onlyActiveOnIndex={false} | |||
style={Object {}} | |||
to={ | |||
Object { | |||
"pathname": "/project/settings", | |||
"query": Object { | |||
"id": "foo", | |||
}, | |||
} | |||
} | |||
> | |||
project_settings.page | |||
</Link> | |||
</li> | |||
</ul> | |||
} | |||
tagName="li" | |||
> | |||
<Component /> | |||
</Dropdown> | |||
</NavBarTabs> | |||
`; | |||
exports[`should work for all qualifiers 3`] = ` | |||
<NavBarTabs> | |||
<li> | |||
<Link | |||
@@ -504,7 +383,7 @@ exports[`should work for all qualifiers 3`] = ` | |||
</NavBarTabs> | |||
`; | |||
exports[`should work for all qualifiers 4`] = ` | |||
exports[`should work for all qualifiers 3`] = ` | |||
<NavBarTabs> | |||
<li> | |||
<Link | |||
@@ -641,7 +520,7 @@ exports[`should work for all qualifiers 4`] = ` | |||
</NavBarTabs> | |||
`; | |||
exports[`should work for all qualifiers 5`] = ` | |||
exports[`should work for all qualifiers 4`] = ` | |||
<NavBarTabs> | |||
<li> | |||
<Link |
@@ -33,7 +33,7 @@ import { lazyLoad } from '../../../components/lazyLoad'; | |||
import { getSuggestions } from '../../../api/components'; | |||
import { translate, translateWithParameters } from '../../../helpers/l10n'; | |||
import { scrollToElement } from '../../../helpers/scrolling'; | |||
import { getProjectUrl } from '../../../helpers/urls'; | |||
import { getProjectUrl, getCodeUrl } from '../../../helpers/urls'; | |||
import './Search.css'; | |||
const SearchResults = lazyLoad(() => import('./SearchResults')); | |||
@@ -162,13 +162,21 @@ export class Search extends React.PureComponent<Props, State> { | |||
return next; | |||
}, []); | |||
mergeWithRecentlyBrowsed = (components: ComponentResult[]) => { | |||
const recentlyBrowsed = RecentHistory.get().map(component => ({ | |||
...component, | |||
isRecentlyBrowsed: true, | |||
qualifier: component.icon.toUpperCase() | |||
})); | |||
return uniqBy([...components, ...recentlyBrowsed], 'key'); | |||
findFile = (key: string) => { | |||
const findInResults = (results: ComponentResult[] | undefined) => | |||
results && results.find(r => r.key === key); | |||
const file = findInResults(this.state.results['FIL']); | |||
if (file) { | |||
return file; | |||
} | |||
const test = findInResults(this.state.results['UTS']); | |||
if (test) { | |||
return test; | |||
} | |||
return undefined; | |||
}; | |||
stopLoading = () => { | |||
@@ -268,11 +276,17 @@ export class Search extends React.PureComponent<Props, State> { | |||
openSelected = () => { | |||
const { selected } = this.state; | |||
if (selected) { | |||
if (selected.startsWith('qualifier###')) { | |||
this.searchMore(selected.substr(12)); | |||
} else { | |||
this.props.router.push(getProjectUrl(selected)); | |||
const file = this.findFile(selected); | |||
if (file) { | |||
this.props.router.push(getCodeUrl(file.project!, undefined, file.key)); | |||
} else { | |||
this.props.router.push(getProjectUrl(selected)); | |||
} | |||
this.closeSearch(); | |||
} | |||
} |
@@ -24,7 +24,7 @@ import FavoriteIcon from '../../../components/icons-components/FavoriteIcon'; | |||
import QualifierIcon from '../../../components/icons-components/QualifierIcon'; | |||
import ClockIcon from '../../../components/icons-components/ClockIcon'; | |||
import Tooltip from '../../../components/controls/Tooltip'; | |||
import { getProjectUrl } from '../../../helpers/urls'; | |||
import { getProjectUrl, getCodeUrl } from '../../../helpers/urls'; | |||
interface Props { | |||
appState: Pick<T.AppState, 'organizationsEnabled'>; | |||
@@ -111,6 +111,11 @@ export default class SearchResult extends React.PureComponent<Props, State> { | |||
render() { | |||
const { component } = this.props; | |||
const isFile = component.qualifier === 'FIL' || component.qualifier === 'UTS'; | |||
const to = isFile | |||
? getCodeUrl(component.project!, undefined, component.key) | |||
: getProjectUrl(component.key); | |||
return ( | |||
<li | |||
className={this.props.selected ? 'active' : undefined} | |||
@@ -121,10 +126,7 @@ export default class SearchResult extends React.PureComponent<Props, State> { | |||
overlay={component.key} | |||
placement="left" | |||
visible={this.state.tooltipVisible}> | |||
<Link | |||
data-key={component.key} | |||
onClick={this.props.onClose} | |||
to={getProjectUrl(component.key)}> | |||
<Link data-key={component.key} onClick={this.props.onClose} to={to}> | |||
<span className="navbar-search-item-link" onMouseEnter={this.handleMouseEnter}> | |||
<span className="navbar-search-item-icons little-spacer-right"> | |||
{component.isFavorite && <FavoriteIcon favorite={true} size={12} />} |
@@ -21,7 +21,6 @@ import * as classNames from 'classnames'; | |||
import * as React from 'react'; | |||
import ComponentName from './ComponentName'; | |||
import ComponentMeasure from './ComponentMeasure'; | |||
import ComponentLink from './ComponentLink'; | |||
import ComponentPin from './ComponentPin'; | |||
import { WorkspaceContext } from '../../../components/workspace/context'; | |||
@@ -85,34 +84,25 @@ export default class Component extends React.PureComponent<Props> { | |||
selected = false | |||
} = this.props; | |||
let componentAction = null; | |||
if (!component.refKey || component.qualifier === 'SVW') { | |||
switch (component.qualifier) { | |||
case 'FIL': | |||
case 'UTS': | |||
componentAction = ( | |||
<WorkspaceContext.Consumer> | |||
{({ openComponent }) => ( | |||
<ComponentPin | |||
branchLike={branchLike} | |||
component={component} | |||
openComponent={openComponent} | |||
/> | |||
)} | |||
</WorkspaceContext.Consumer> | |||
); | |||
break; | |||
default: | |||
componentAction = <ComponentLink branchLike={branchLike} component={component} />; | |||
} | |||
} | |||
const isFile = component.qualifier === 'FIL' || component.qualifier === 'UTS'; | |||
return ( | |||
<tr className={classNames({ selected })} ref={node => (this.node = node)}> | |||
<td className="blank" /> | |||
<td className="thin nowrap"> | |||
<span className="spacer-right">{componentAction}</span> | |||
<span className="spacer-right"> | |||
{isFile && ( | |||
<WorkspaceContext.Consumer> | |||
{({ openComponent }) => ( | |||
<ComponentPin | |||
branchLike={branchLike} | |||
component={component} | |||
openComponent={openComponent} | |||
/> | |||
)} | |||
</WorkspaceContext.Consumer> | |||
)} | |||
</span> | |||
</td> | |||
<td className="code-name-cell"> | |||
<ComponentName |
@@ -1,40 +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 { Link } from 'react-router'; | |||
import LinkIcon from '../../../components/icons-components/LinkIcon'; | |||
import { translate } from '../../../helpers/l10n'; | |||
import { getBranchLikeUrl } from '../../../helpers/urls'; | |||
interface Props { | |||
branchLike?: T.BranchLike; | |||
component: T.ComponentMeasure; | |||
} | |||
export default function ComponentLink({ component, branchLike }: Props) { | |||
return ( | |||
<Link | |||
className="link-no-underline" | |||
title={translate('code.open_component_page')} | |||
to={getBranchLikeUrl(component.refKey || component.key, branchLike)}> | |||
<LinkIcon /> | |||
</Link> | |||
); | |||
} |
@@ -28,7 +28,6 @@ import QualifierIcon from '../../../components/icons-components/QualifierIcon'; | |||
import TreeMap, { TreeMapItem } from '../../../components/charts/TreeMap'; | |||
import { translate, translateWithParameters, getLocalizedMetricName } from '../../../helpers/l10n'; | |||
import { formatMeasure, isDiffMetric } from '../../../helpers/measures'; | |||
import { getBranchLikeUrl } from '../../../helpers/urls'; | |||
import { isDefined } from '../../../helpers/types'; | |||
interface Props { | |||
@@ -54,13 +53,13 @@ export default class TreeMapView extends React.PureComponent<Props, State> { | |||
this.state = { treemapItems: this.getTreemapComponents(props) }; | |||
} | |||
componentWillReceiveProps(nextProps: Props) { | |||
if (nextProps.components !== this.props.components || nextProps.metric !== this.props.metric) { | |||
this.setState({ treemapItems: this.getTreemapComponents(nextProps) }); | |||
componentDidUpdate(prevProps: Props) { | |||
if (prevProps.components !== this.props.components || prevProps.metric !== this.props.metric) { | |||
this.setState({ treemapItems: this.getTreemapComponents(this.props) }); | |||
} | |||
} | |||
getTreemapComponents = ({ branchLike, components, metric }: Props) => { | |||
getTreemapComponents = ({ components, metric }: Props) => { | |||
const colorScale = this.getColorScale(metric); | |||
return components | |||
.map(component => { | |||
@@ -85,7 +84,6 @@ export default class TreeMapView extends React.PureComponent<Props, State> { | |||
icon: <QualifierIcon fill={theme.baseFontColor} qualifier={component.qualifier} />, | |||
key: component.refKey || component.key, | |||
label: component.name, | |||
link: getBranchLikeUrl(component.refKey || component.key, branchLike), | |||
size: sizeValue, | |||
tooltip: this.getTooltip({ | |||
colorMetric: metric, |
@@ -1110,7 +1110,6 @@ export class App extends React.PureComponent<Props, State> { | |||
{openIssue ? ( | |||
<div className="pull-left width-60"> | |||
<ComponentBreadcrumbs | |||
branchLike={this.props.branchLike} | |||
component={component} | |||
issue={openIssue} | |||
organization={this.props.organization} |
@@ -18,14 +18,11 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import { Link } from 'react-router'; | |||
import { getSelectedLocation } from '../utils'; | |||
import Organization from '../../../components/shared/Organization'; | |||
import { collapsePath, limitComponentName } from '../../../helpers/path'; | |||
import { getBranchLikeUrl, getCodeUrl } from '../../../helpers/urls'; | |||
interface Props { | |||
branchLike?: T.BranchLike; | |||
component?: T.Component; | |||
issue: Pick< | |||
T.Issue, | |||
@@ -46,9 +43,7 @@ interface Props { | |||
} | |||
export default function ComponentBreadcrumbs({ | |||
branchLike, | |||
component, | |||
link = true, | |||
issue, | |||
organization, | |||
selectedFlowIndex, | |||
@@ -60,28 +55,15 @@ export default function ComponentBreadcrumbs({ | |||
const displaySubProject = !component || !['BRC', 'DIR'].includes(component.qualifier); | |||
const selectedLocation = getSelectedLocation(issue, selectedFlowIndex, selectedLocationIndex); | |||
const componentKey = selectedLocation ? selectedLocation.component : issue.component; | |||
const componentName = selectedLocation ? selectedLocation.componentName : issue.componentLongName; | |||
return ( | |||
<div className="component-name text-ellipsis"> | |||
{displayOrganization && ( | |||
<Organization | |||
link={link} | |||
linkClassName="link-no-underline" | |||
organizationKey={issue.organization} | |||
/> | |||
)} | |||
{displayOrganization && <Organization link={false} organizationKey={issue.organization} />} | |||
{displayProject && ( | |||
<span title={issue.projectName}> | |||
{link ? ( | |||
<Link className="link-no-underline" to={getBranchLikeUrl(issue.project, branchLike)}> | |||
{limitComponentName(issue.projectName)} | |||
</Link> | |||
) : ( | |||
limitComponentName(issue.projectName) | |||
)} | |||
{limitComponentName(issue.projectName)} | |||
<span className="slash-separator" /> | |||
</span> | |||
)} | |||
@@ -90,28 +72,12 @@ export default function ComponentBreadcrumbs({ | |||
issue.subProject !== undefined && | |||
issue.subProjectName !== undefined && ( | |||
<span title={issue.subProjectName}> | |||
{link ? ( | |||
<Link | |||
className="link-no-underline" | |||
to={getBranchLikeUrl(issue.subProject, branchLike)}> | |||
{limitComponentName(issue.subProjectName)} | |||
</Link> | |||
) : ( | |||
limitComponentName(issue.subProjectName) | |||
)} | |||
{limitComponentName(issue.subProjectName)} | |||
<span className="slash-separator" /> | |||
</span> | |||
)} | |||
{link ? ( | |||
<Link | |||
className="link-no-underline" | |||
to={getCodeUrl(issue.project, branchLike, componentKey)}> | |||
<span title={componentName}>{collapsePath(componentName || '')}</span> | |||
</Link> | |||
) : ( | |||
collapsePath(componentName || '') | |||
)} | |||
{collapsePath(componentName || '')} | |||
</div> | |||
); | |||
} |
@@ -105,10 +105,8 @@ export default class ListItem extends React.PureComponent<Props, State> { | |||
{displayComponent && ( | |||
<div className="issues-workspace-list-component note"> | |||
<ComponentBreadcrumbs | |||
branchLike={branchLike} | |||
component={component} | |||
issue={this.props.issue} | |||
link={false} | |||
organization={this.props.organization} | |||
/> | |||
</div> |
@@ -34,12 +34,7 @@ const baseIssue = { | |||
it('renders', () => { | |||
expect( | |||
shallow( | |||
<ComponentBreadcrumbs | |||
branchLike={undefined} | |||
component={undefined} | |||
issue={baseIssue} | |||
organization={undefined} | |||
/> | |||
<ComponentBreadcrumbs component={undefined} issue={baseIssue} organization={undefined} /> | |||
) | |||
).toMatchSnapshot(); | |||
}); | |||
@@ -47,33 +42,6 @@ it('renders', () => { | |||
it('renders with sub-project', () => { | |||
const issue = { ...baseIssue, subProject: 'sub-proj', subProjectName: 'sub-proj-name' }; | |||
expect( | |||
shallow( | |||
<ComponentBreadcrumbs | |||
branchLike={undefined} | |||
component={undefined} | |||
issue={issue} | |||
organization={undefined} | |||
/> | |||
) | |||
).toMatchSnapshot(); | |||
}); | |||
it('renders with branch', () => { | |||
const issue = { ...baseIssue, subProject: 'sub-proj', subProjectName: 'sub-proj-name' }; | |||
const shortBranch: T.ShortLivingBranch = { | |||
isMain: false, | |||
mergeBranch: '', | |||
name: 'feature', | |||
type: 'SHORT' | |||
}; | |||
expect( | |||
shallow( | |||
<ComponentBreadcrumbs | |||
branchLike={shortBranch} | |||
component={undefined} | |||
issue={issue} | |||
organization={undefined} | |||
/> | |||
) | |||
shallow(<ComponentBreadcrumbs component={undefined} issue={issue} organization={undefined} />) | |||
).toMatchSnapshot(); | |||
}); |
@@ -5,136 +5,18 @@ exports[`renders 1`] = ` | |||
className="component-name text-ellipsis" | |||
> | |||
<Connect(Organization) | |||
link={true} | |||
linkClassName="link-no-underline" | |||
link={false} | |||
organizationKey="org" | |||
/> | |||
<span | |||
title="proj-name" | |||
> | |||
<Link | |||
className="link-no-underline" | |||
onlyActiveOnIndex={false} | |||
style={Object {}} | |||
to={ | |||
Object { | |||
"pathname": "/dashboard", | |||
"query": Object { | |||
"branch": undefined, | |||
"id": "proj", | |||
}, | |||
} | |||
} | |||
> | |||
proj-name | |||
</Link> | |||
proj-name | |||
<span | |||
className="slash-separator" | |||
/> | |||
</span> | |||
<Link | |||
className="link-no-underline" | |||
onlyActiveOnIndex={false} | |||
style={Object {}} | |||
to={ | |||
Object { | |||
"pathname": "/code", | |||
"query": Object { | |||
"id": "proj", | |||
"line": undefined, | |||
"selected": "comp", | |||
}, | |||
} | |||
} | |||
> | |||
<span | |||
title="comp-name" | |||
> | |||
comp-name | |||
</span> | |||
</Link> | |||
</div> | |||
`; | |||
exports[`renders with branch 1`] = ` | |||
<div | |||
className="component-name text-ellipsis" | |||
> | |||
<Connect(Organization) | |||
link={true} | |||
linkClassName="link-no-underline" | |||
organizationKey="org" | |||
/> | |||
<span | |||
title="proj-name" | |||
> | |||
<Link | |||
className="link-no-underline" | |||
onlyActiveOnIndex={false} | |||
style={Object {}} | |||
to={ | |||
Object { | |||
"pathname": "/project/issues", | |||
"query": Object { | |||
"branch": "feature", | |||
"id": "proj", | |||
"resolved": "false", | |||
}, | |||
} | |||
} | |||
> | |||
proj-name | |||
</Link> | |||
<span | |||
className="slash-separator" | |||
/> | |||
</span> | |||
<span | |||
title="sub-proj-name" | |||
> | |||
<Link | |||
className="link-no-underline" | |||
onlyActiveOnIndex={false} | |||
style={Object {}} | |||
to={ | |||
Object { | |||
"pathname": "/project/issues", | |||
"query": Object { | |||
"branch": "feature", | |||
"id": "sub-proj", | |||
"resolved": "false", | |||
}, | |||
} | |||
} | |||
> | |||
sub-proj-name | |||
</Link> | |||
<span | |||
className="slash-separator" | |||
/> | |||
</span> | |||
<Link | |||
className="link-no-underline" | |||
onlyActiveOnIndex={false} | |||
style={Object {}} | |||
to={ | |||
Object { | |||
"pathname": "/code", | |||
"query": Object { | |||
"branch": "feature", | |||
"id": "proj", | |||
"line": undefined, | |||
"selected": "comp", | |||
}, | |||
} | |||
} | |||
> | |||
<span | |||
title="comp-name" | |||
> | |||
comp-name | |||
</span> | |||
</Link> | |||
comp-name | |||
</div> | |||
`; | |||
@@ -143,29 +25,13 @@ exports[`renders with sub-project 1`] = ` | |||
className="component-name text-ellipsis" | |||
> | |||
<Connect(Organization) | |||
link={true} | |||
linkClassName="link-no-underline" | |||
link={false} | |||
organizationKey="org" | |||
/> | |||
<span | |||
title="proj-name" | |||
> | |||
<Link | |||
className="link-no-underline" | |||
onlyActiveOnIndex={false} | |||
style={Object {}} | |||
to={ | |||
Object { | |||
"pathname": "/dashboard", | |||
"query": Object { | |||
"branch": undefined, | |||
"id": "proj", | |||
}, | |||
} | |||
} | |||
> | |||
proj-name | |||
</Link> | |||
proj-name | |||
<span | |||
className="slash-separator" | |||
/> | |||
@@ -173,46 +39,11 @@ exports[`renders with sub-project 1`] = ` | |||
<span | |||
title="sub-proj-name" | |||
> | |||
<Link | |||
className="link-no-underline" | |||
onlyActiveOnIndex={false} | |||
style={Object {}} | |||
to={ | |||
Object { | |||
"pathname": "/dashboard", | |||
"query": Object { | |||
"branch": undefined, | |||
"id": "sub-proj", | |||
}, | |||
} | |||
} | |||
> | |||
sub-proj-name | |||
</Link> | |||
sub-proj-name | |||
<span | |||
className="slash-separator" | |||
/> | |||
</span> | |||
<Link | |||
className="link-no-underline" | |||
onlyActiveOnIndex={false} | |||
style={Object {}} | |||
to={ | |||
Object { | |||
"pathname": "/code", | |||
"query": Object { | |||
"id": "proj", | |||
"line": undefined, | |||
"selected": "comp", | |||
}, | |||
} | |||
} | |||
> | |||
<span | |||
title="comp-name" | |||
> | |||
comp-name | |||
</span> | |||
</Link> | |||
comp-name | |||
</div> | |||
`; |
@@ -26,7 +26,6 @@ import Suggestions from '../../../app/components/embed-docs-modal/Suggestions'; | |||
import { isShortLivingBranch } from '../../../helpers/branches'; | |||
import { | |||
getShortLivingBranchUrl, | |||
getCodeUrl, | |||
getProjectUrl, | |||
getBaseUrl, | |||
getPathUrlAsString | |||
@@ -53,10 +52,6 @@ export class App extends React.PureComponent<Props> { | |||
pathname: '/portfolio', | |||
query: { id: component.key } | |||
}); | |||
} else if (this.isFile()) { | |||
this.props.router.replace( | |||
getCodeUrl(component.breadcrumbs[0].key, branchLike, component.key) | |||
); | |||
} else if (isShortLivingBranch(branchLike)) { | |||
this.props.router.replace(getShortLivingBranchUrl(component.key, branchLike.name)); | |||
} | |||
@@ -64,12 +59,10 @@ export class App extends React.PureComponent<Props> { | |||
isPortfolio = () => ['VW', 'SVW'].includes(this.props.component.qualifier); | |||
isFile = () => ['FIL', 'UTS'].includes(this.props.component.qualifier); | |||
render() { | |||
const { branchLike, branchLikes, component } = this.props; | |||
if (this.isPortfolio() || this.isFile() || isShortLivingBranch(branchLike)) { | |||
if (this.isPortfolio() || isShortLivingBranch(branchLike)) { | |||
return null; | |||
} | |||
@@ -18,7 +18,7 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import { mount, shallow } from 'enzyme'; | |||
import { shallow } from 'enzyme'; | |||
import { App } from '../App'; | |||
import { isSonarCloud } from '../../../../helpers/system'; | |||
@@ -64,33 +64,7 @@ it('should render SonarCloudEmptyOverview', () => { | |||
).toBeTruthy(); | |||
}); | |||
it('redirects on Code page for files', () => { | |||
const branch: T.LongLivingBranch = { isMain: false, name: 'b', type: 'LONG' }; | |||
const newComponent = { | |||
...component, | |||
breadcrumbs: [ | |||
{ key: 'project', name: 'Project', qualifier: 'TRK' }, | |||
{ key: 'foo', name: 'Foo', qualifier: 'DIR' } | |||
], | |||
qualifier: 'FIL' | |||
}; | |||
const replace = jest.fn(); | |||
mount( | |||
<App | |||
branchLike={branch} | |||
branchLikes={[branch]} | |||
component={newComponent} | |||
onComponentChange={jest.fn()} | |||
router={{ replace }} | |||
/> | |||
); | |||
expect(replace).toBeCalledWith({ | |||
pathname: '/code', | |||
query: { branch: 'b', id: 'project', selected: 'foo' } | |||
}); | |||
}); | |||
function getWrapper(props: Partial<App['props']> = {}) { | |||
function getWrapper(props = {}) { | |||
return shallow( | |||
<App | |||
branchLikes={[]} |
@@ -103,11 +103,7 @@ export default class SourceViewerHeader extends React.PureComponent<Props, State | |||
{subProject != null && ( | |||
<div className="component-name-parent"> | |||
<a | |||
className="link-with-icon" | |||
href={getPathUrlAsString(getBranchLikeUrl(subProject, this.props.branchLike))}> | |||
<QualifierIcon qualifier="BRC" /> <span>{subProjectName}</span> | |||
</a> | |||
<QualifierIcon qualifier="BRC" /> <span>{subProjectName}</span> | |||
</div> | |||
)} | |||
@@ -132,9 +132,7 @@ export default class DuplicationPopup extends React.PureComponent<Props> { | |||
duplication.file.subProjectName && ( | |||
<div className="component-name-parent"> | |||
<QualifierIcon className="little-spacer-right" qualifier="BRC" /> | |||
<Link to={getProjectUrl(duplication.file.subProject)}> | |||
{duplication.file.subProjectName} | |||
</Link> | |||
{duplication.file.subProjectName} | |||
</div> | |||
)} | |||
</> |
@@ -400,14 +400,12 @@ export default class MeasuresOverlay extends React.PureComponent<Props, State> { | |||
{sourceViewerFile.subProject && ( | |||
<> | |||
<QualifierIcon className="big-spacer-left little-spacer-right" qualifier="BRC" /> | |||
<Link to={getBranchLikeUrl(sourceViewerFile.subProject, branchLike)}> | |||
{sourceViewerFile.subProjectName} | |||
</Link> | |||
{sourceViewerFile.subProjectName} | |||
</> | |||
)} | |||
</div> | |||
<div className="source-viewer-header-component-name"> | |||
<div className="source-viewer-header-component-name display-flex-center little-spacer-top"> | |||
<QualifierIcon className="little-spacer-right" qualifier={sourceViewerFile.q} /> | |||
{sourceViewerFile.path} | |||
</div> |
@@ -39,25 +39,10 @@ exports[`should render source file 1`] = ` | |||
className="big-spacer-left little-spacer-right" | |||
qualifier="BRC" | |||
/> | |||
<Link | |||
onlyActiveOnIndex={false} | |||
style={Object {}} | |||
to={ | |||
Object { | |||
"pathname": "/project/issues", | |||
"query": Object { | |||
"branch": "feature", | |||
"id": "sub-project-key", | |||
"resolved": "false", | |||
}, | |||
} | |||
} | |||
> | |||
Sub-Project Name | |||
</Link> | |||
Sub-Project Name | |||
</div> | |||
<div | |||
className="source-viewer-header-component-name" | |||
className="source-viewer-header-component-name display-flex-center little-spacer-top" | |||
> | |||
<QualifierIcon | |||
className="little-spacer-right" | |||
@@ -414,25 +399,10 @@ exports[`should render source file 2`] = ` | |||
className="big-spacer-left little-spacer-right" | |||
qualifier="BRC" | |||
/> | |||
<Link | |||
onlyActiveOnIndex={false} | |||
style={Object {}} | |||
to={ | |||
Object { | |||
"pathname": "/project/issues", | |||
"query": Object { | |||
"branch": "feature", | |||
"id": "sub-project-key", | |||
"resolved": "false", | |||
}, | |||
} | |||
} | |||
> | |||
Sub-Project Name | |||
</Link> | |||
Sub-Project Name | |||
</div> | |||
<div | |||
className="source-viewer-header-component-name" | |||
className="source-viewer-header-component-name display-flex-center little-spacer-top" | |||
> | |||
<QualifierIcon | |||
className="little-spacer-right" | |||
@@ -1395,25 +1365,10 @@ exports[`should render test file 1`] = ` | |||
className="big-spacer-left little-spacer-right" | |||
qualifier="BRC" | |||
/> | |||
<Link | |||
onlyActiveOnIndex={false} | |||
style={Object {}} | |||
to={ | |||
Object { | |||
"pathname": "/project/issues", | |||
"query": Object { | |||
"branch": "feature", | |||
"id": "sub-project-key", | |||
"resolved": "false", | |||
}, | |||
} | |||
} | |||
> | |||
Sub-Project Name | |||
</Link> | |||
Sub-Project Name | |||
</div> | |||
<div | |||
className="source-viewer-header-component-name" | |||
className="source-viewer-header-component-name display-flex-center little-spacer-top" | |||
> | |||
<QualifierIcon | |||
className="little-spacer-right" |
@@ -290,12 +290,11 @@ | |||
} | |||
.source-viewer-header-component-project { | |||
color: var(--secondFontColor); | |||
font-size: var(--smallFontSize); | |||
} | |||
.source-viewer-header-component-name { | |||
font-weight: 600; | |||
font-size: var(--smallFontSize); | |||
} | |||
.source-viewer-header-favorite { |
@@ -18,6 +18,8 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { Dispatch } from 'redux'; | |||
import { InjectedRouter } from 'react-router'; | |||
import { requireAuthorization as requireAuthorizationAction } from './appState'; | |||
import { addGlobalErrorMessage } from './globalMessages'; | |||
import { receiveLanguages } from './languages'; | |||
import { receiveMetrics } from './metrics'; | |||
@@ -84,3 +86,9 @@ export function doLogout() { | |||
} | |||
); | |||
} | |||
export function requireAuthorization(router: Pick<InjectedRouter, 'replace'>) { | |||
const returnTo = window.location.pathname + window.location.search + window.location.hash; | |||
router.replace({ pathname: '/sessions/new', query: { return_to: returnTo } }); | |||
return requireAuthorizationAction(); | |||
} |
@@ -351,16 +351,9 @@ qualifier.UTS=Test File | |||
qualifier.DEV=Developer | |||
qualifier.configuration.TRK=Project Configuration | |||
qualifier.configuration.BRC=Sub-project Configuration | |||
qualifier.configuration.DIR=Directory Configuration | |||
qualifier.configuration.PAC=Package Configuration | |||
qualifier.configuration.VW=Portfolio Configuration | |||
qualifier.configuration.SVW=Portfolio Configuration | |||
qualifier.configuration.APP=Application Configuration | |||
qualifier.configuration.FIL=File Configuration | |||
qualifier.configuration.CLA=File Configuration | |||
qualifier.configuration.UTS=Test File Configuration | |||
qualifier.configuration.DEV=Developer Configuration | |||
qualifiers.TRK=Projects | |||
qualifiers.BRC=Sub-projects | |||
@@ -2430,17 +2423,11 @@ overview.on_new_code=On New Code | |||
overview.about_this_portfolio=About This Portfolio | |||
overview.about_this_project.APP=About This Application | |||
overview.about_this_project.TRK=About This Project | |||
overview.about_this_project.BRC=About This Sub-Project | |||
overview.about_this_project.DIR=About This Directory | |||
overview.project_activity.APP=Application Activity | |||
overview.project_activity.TRK=Project Activity | |||
overview.project_activity.BRC=Sub-Project Activity | |||
overview.project_activity.DIR=Directory Activity | |||
overview.external_links=External Links | |||
overview.project_key.APP=Application Key | |||
overview.project_key.TRK=Project Key | |||
overview.project_key.BRC=Sub-Project Key | |||
overview.project_key.DIR=Directory Key | |||
overview.project.no_lines_of_code=This project as no lines of code. | |||
overview.project.empty=This project is empty. | |||
@@ -3100,25 +3087,17 @@ homepage.check=Check to make the current page your homepage | |||
# | |||
#------------------------------------------------------------------------------ | |||
favorite.check.TRK=Click to mark this project as favorite. | |||
favorite.check.BRC=Click to mark this sub-project as favorite. | |||
favorite.check.DIR=Click to mark this directory as favorite. | |||
favorite.check.PAC=Click to mark this package as favorite. | |||
favorite.check.VW=Click to mark this portfolio as favorite. | |||
favorite.check.SVW=Click to mark this sub-ortfolio as favorite. | |||
favorite.check.APP=Click to mark this application as favorite. | |||
favorite.check.FIL=Click to mark this file as favorite. | |||
favorite.check.CLA=Click to mark this file as favorite. | |||
favorite.check.UTS=Click to mark this test file as favorite. | |||
favorite.current.TRK=This project is marked as favorite. | |||
favorite.current.BRC=This sub-project is marked as favorite. | |||
favorite.current.DIR=This directory is marked as favorite. | |||
favorite.current.PAC=This package is marked as favorite. | |||
favorite.current.VW=This portfolio is marked as favorite. | |||
favorite.current.SVW=This sub-ortfolio is marked as favorite. | |||
favorite.current.APP=This application is marked as favorite. | |||
favorite.current.FIL=This file is marked as favorite. | |||
favorite.current.CLA=This file is marked as favorite. | |||
favorite.current.UTS=This test file is marked as favorite. | |||