import throwGlobalError from '../app/utils/throwGlobalError';
import { BranchParameters } from '../types/branch-like';
-export function getGlobalNavigation(): Promise<T.AppState> {
- return getJSON('/api/navigation/global');
-}
-
type NavComponent = T.Omit<T.Component, 'alm' | 'qualifier' | 'leakPeriodDate' | 'path' | 'tags'>;
export function getComponentNavigation(
import Search from '../../search/Search';
import './GlobalNav.css';
import GlobalNavBranding, { SonarCloudNavBranding } from './GlobalNavBranding';
-import GlobalNavExplore from './GlobalNavExplore';
import GlobalNavMenu from './GlobalNavMenu';
import GlobalNavUserContainer from './GlobalNavUserContainer';
setCurrentUserSetting={this.props.setCurrentUserSetting}
/>
)}
- {isSonarCloud() && <GlobalNavExplore location={this.props.location} />}
<EmbedDocsPopupHelper />
<Search appState={appState} currentUser={currentUser} />
{isLoggedIn(currentUser) && (
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 { translate } from 'sonar-ui-common/helpers/l10n';
-
-interface Props {
- location: { pathname: string };
-}
-
-export default function GlobalNavExplore({ location }: Props) {
- const active = location.pathname.startsWith('/explore');
- return (
- <li>
- <Link
- className={active ? 'active' : undefined}
- to={{ pathname: '/explore/projects', query: { sort: '-analysis_date' } }}>
- {translate('explore')}
- </Link>
- </li>
- );
-}
onClick={[Function]}
setCurrentUserSetting={[MockFunction]}
/>
- <GlobalNavExplore
- location={
- Object {
- "pathname": "",
- }
- }
- />
<EmbedDocsPopupHelper />
<withRouter(Search)
appState={
import customMeasuresRoutes from '../../apps/custom-measures/routes';
import customMetricsRoutes from '../../apps/custom-metrics/routes';
import documentationRoutes from '../../apps/documentation/routes';
-import Explore from '../../apps/explore/Explore';
-import ExploreIssues from '../../apps/explore/ExploreIssues';
-import ExploreProjects from '../../apps/explore/ExploreProjects';
import groupsRoutes from '../../apps/groups/routes';
import Issues from '../../apps/issues/components/AppContainer';
import { maintenanceRoutes, setupRoutes } from '../../apps/maintenance/routes';
<RouteWithChildRoutes path="account" childRoutes={accountRoutes} />
<RouteWithChildRoutes path="coding_rules" childRoutes={codingRulesRoutes} />
<RouteWithChildRoutes path="documentation" childRoutes={documentationRoutes} />
- <Route path="explore" component={Explore}>
- <Route path="issues" component={ExploreIssues} />
- <Route path="projects" component={ExploreProjects} />
- </Route>
<Route
path="extension/:pluginKey/:extensionKey"
component={lazyLoadComponent(() =>
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 ContextNavBar from 'sonar-ui-common/components/ui/ContextNavBar';
-import NavBarTabs from 'sonar-ui-common/components/ui/NavBarTabs';
-import { translate } from 'sonar-ui-common/helpers/l10n';
-import { rawSizes } from '../../app/theme';
-
-interface Props {
- children: JSX.Element;
-}
-
-export default function Explore(props: Props) {
- return (
- <div id="explore">
- <ContextNavBar height={rawSizes.contextNavHeightRaw} id="explore-navigation">
- <header className="navbar-context-header">
- <h1>{translate('explore')}</h1>
- </header>
-
- <NavBarTabs>
- <li>
- <Link activeClassName="active" to="/explore/projects">
- {translate('projects.page')}
- </Link>
- </li>
- <li>
- <Link
- activeClassName="active"
- to={{ pathname: '/explore/issues', query: { resolved: 'false' } }}>
- {translate('issues.page')}
- </Link>
- </li>
- </NavBarTabs>
- </ContextNavBar>
-
- {props.children}
- </div>
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 AppContainer from '../issues/components/AppContainer';
-
-interface Props {
- location: { pathname: string; query: T.RawQuery };
-}
-
-export default function ExploreIssues(props: Props) {
- return (
- <AppContainer hideAuthorFacet={true} multiOrganizations={true} myIssues={false} {...props} />
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 AllProjectsContainer from '../projects/components/AllProjectsContainer';
-
-interface Props {
- location: { pathname: string; query: T.RawQuery };
-}
-
-export default function ExploreProjects(props: Props) {
- return (
- <AllProjectsContainer
- isFavorite={false}
- organization={undefined}
- storageOptionsSuffix="explore"
- {...props}
- />
- );
-}
currentUser: T.CurrentUser;
fetchBranchStatus: (branchLike: BranchLike, projectKey: string) => Promise<void>;
fetchIssues: (query: T.RawQuery, requestOrganizations?: boolean) => Promise<FetchIssuesPromise>;
- hideAuthorFacet?: boolean;
location: Location;
- multiOrganizations?: boolean;
- myIssues?: boolean;
onBranchesChange?: () => void;
organization?: { key: string };
router: Pick<Router, 'push' | 'replace'>;
loadingFacets: {},
loadingMore: false,
locationsNavigator: false,
- myIssues: props.myIssues || areMyIssuesSelected(props.location.query),
+ myIssues: areMyIssuesSelected(props.location.query),
openFacets: {
owaspTop10: shouldOpenStandardsChildFacet({}, query, 'owaspTop10'),
sansTop25: shouldOpenStandardsChildFacet({}, query, 'sansTop25'),
}
this.setState({
- myIssues: nextProps.myIssues || areMyIssuesSelected(nextProps.location.query),
+ myIssues: areMyIssuesSelected(nextProps.location.query),
openIssue,
query: parseQuery(nextProps.location.query)
});
}
};
- fetchIssues = (
- additional: T.RawQuery,
- requestFacets = false,
- requestOrganizations = true
- ): Promise<FetchIssuesPromise> => {
+ fetchIssues = (additional: T.RawQuery, requestFacets = false): Promise<FetchIssuesPromise> => {
const { component } = this.props;
const { myIssues, openFacets, query } = this.state;
Object.assign(parameters, { assignees: '__me__' });
}
- return this.props.fetchIssues(
- parameters,
- Boolean(requestOrganizations && this.props.multiOrganizations)
- );
+ return this.props.fetchIssues(parameters, false);
};
fetchFirstIssues() {
};
fetchFacet = (facet: string) => {
- const requestOrganizations = facet === 'projects';
- return this.fetchIssues({ ps: 1, facets: mapFacet(facet) }, false, requestOrganizations).then(
+ return this.fetchIssues({ ps: 1, facets: mapFacet(facet) }, false).then(
({ facets, ...other }) => {
if (this.mounted) {
this.setState(state => ({
userOrganizations.find(o => {
return o.key === organizationKey;
});
- const hideAuthorFacet =
- this.props.hideAuthorFacet || (isSonarCloud() && this.props.myIssues) || !userOrganization;
+ const hideAuthorFacet = !userOrganization;
return (
<div className="layout-page-filters">
{this.renderBulkChange(openIssue)}
<PageActions
- canSetHome={Boolean(
- !this.props.organization &&
- !this.props.component &&
- (!isSonarCloud() || this.props.myIssues)
- )}
+ canSetHome={Boolean(!this.props.organization && !this.props.component)}
effortTotal={this.state.effortTotal}
onReload={this.handleReload}
paging={paging}
import { shallow } from 'enzyme';
import * as React from 'react';
import handleRequiredAuthentication from 'sonar-ui-common/helpers/handleRequiredAuthentication';
+import { KeyCodes } from 'sonar-ui-common/helpers/keycodes';
import { KEYCODE_MAP, keydown, waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
import { mockPullRequest } from '../../../../helpers/mocks/branch-like';
import {
it('should not render for anonymous user', () => {
shallowRender({
currentUser: mockCurrentUser({ isLoggedIn: false }),
- myIssues: true
+ location: mockLocation({ query: { myIssues: true.toString() } })
});
expect(handleRequiredAuthentication).toBeCalled();
});
expect(wrapper.state('selected')).toBe(ISSUES[0].key);
- keydown('down');
+ keydown(KeyCodes.DownArrow);
expect(wrapper.state('selected')).toBe(ISSUES[1].key);
- keydown('up');
- keydown('up');
+ keydown(KeyCodes.UpArrow);
+ keydown(KeyCodes.UpArrow);
expect(wrapper.state('selected')).toBe(ISSUES[0].key);
- keydown('down');
- keydown('down');
- keydown('down');
- keydown('down');
- keydown('down');
- keydown('down');
+ keydown(KeyCodes.DownArrow);
+ keydown(KeyCodes.DownArrow);
+ keydown(KeyCodes.DownArrow);
+ keydown(KeyCodes.DownArrow);
+ keydown(KeyCodes.DownArrow);
+ keydown(KeyCodes.DownArrow);
expect(wrapper.state('selected')).toBe(ISSUES[3].key);
- keydown('right');
+ keydown(KeyCodes.RightArrow);
expect(push).toBeCalledTimes(1);
- keydown('left');
+ keydown(KeyCodes.LeftArrow);
expect(push).toBeCalledTimes(2);
});
organization: T.Organization | undefined;
qualifiers: ComponentQualifier[];
router: Pick<Router, 'push' | 'replace'>;
- storageOptionsSuffix?: string;
}
interface State {
total?: number;
}
-const PROJECTS_SORT = 'sonarqube.projects.sort';
-const PROJECTS_VIEW = 'sonarqube.projects.view';
-const PROJECTS_VISUALIZATION = 'sonarqube.projects.visualization';
+export const LS_PROJECTS_SORT = 'sonarqube.projects.sort';
+export const LS_PROJECTS_VIEW = 'sonarqube.projects.view';
+export const LS_PROJECTS_VISUALIZATION = 'sonarqube.projects.visualization';
export class AllProjects extends React.PureComponent<Props, State> {
mounted = false;
getSort = () => this.state.query.sort || 'name';
getStorageOptions = () => {
- const { storageOptionsSuffix } = this.props;
const options: {
sort?: string;
view?: string;
visualization?: string;
} = {};
- if (get(PROJECTS_SORT, storageOptionsSuffix)) {
- options.sort = get(PROJECTS_SORT, storageOptionsSuffix) || undefined;
+ if (get(LS_PROJECTS_SORT)) {
+ options.sort = get(LS_PROJECTS_SORT) || undefined;
}
- if (get(PROJECTS_VIEW, storageOptionsSuffix)) {
- options.view = get(PROJECTS_VIEW, storageOptionsSuffix) || undefined;
+ if (get(LS_PROJECTS_VIEW)) {
+ options.view = get(LS_PROJECTS_VIEW) || undefined;
}
- if (get(PROJECTS_VISUALIZATION, storageOptionsSuffix)) {
- options.visualization = get(PROJECTS_VISUALIZATION, storageOptionsSuffix) || undefined;
+ if (get(LS_PROJECTS_VISUALIZATION)) {
+ options.visualization = get(LS_PROJECTS_VISUALIZATION) || undefined;
}
return options;
};
};
handlePerspectiveChange = ({ view, visualization }: { view: string; visualization?: string }) => {
- const { storageOptionsSuffix } = this.props;
const query: {
view: string | undefined;
visualization: string | undefined;
this.updateLocationQuery(query);
}
- save(PROJECTS_SORT, query.sort, storageOptionsSuffix);
- save(PROJECTS_VIEW, query.view, storageOptionsSuffix);
- save(PROJECTS_VISUALIZATION, visualization, storageOptionsSuffix);
+ save(LS_PROJECTS_SORT, query.sort);
+ save(LS_PROJECTS_VIEW, query.view);
+ save(LS_PROJECTS_VISUALIZATION, visualization);
};
handleQueryChange(initialMount: boolean) {
handleSortChange = (sort: string, desc: boolean) => {
const asString = (desc ? '-' : '') + sort;
this.updateLocationQuery({ sort: asString });
- save(PROJECTS_SORT, asString, this.props.storageOptionsSuffix);
+ save(LS_PROJECTS_SORT, asString);
};
stopLoading = () => {
state: State = {};
componentDidMount() {
- if (isSonarCloud() && !isLoggedIn(this.props.currentUser)) {
- this.props.router.replace('/explore/projects');
- }
-
if (!isSonarCloud()) {
this.defineIfShouldBeRedirected();
}
import * as React from 'react';
import { get, save } from 'sonar-ui-common/helpers/storage';
import { ComponentQualifier } from '../../../../types/component';
-import { AllProjects } from '../AllProjects';
+import {
+ AllProjects,
+ LS_PROJECTS_SORT,
+ LS_PROJECTS_VIEW,
+ LS_PROJECTS_VISUALIZATION
+} from '../AllProjects';
jest.mock('../ProjectsList', () => ({
// eslint-disable-next-line
});
it('redirects to the saved search', () => {
- (get as jest.Mock).mockImplementation((key: string) =>
- key === 'sonarqube.projects.view' ? 'leak' : null
- );
+ const localeStorageMock: T.Dict<string> = {
+ [LS_PROJECTS_VIEW]: 'leak',
+ [LS_PROJECTS_SORT]: 'coverage',
+ [LS_PROJECTS_VISUALIZATION]: 'security'
+ };
+
+ (get as jest.Mock).mockImplementation((key: string) => localeStorageMock[key]);
const replace = jest.fn();
shallowRender({}, jest.fn(), replace);
- expect(replace).lastCalledWith({ pathname: '/projects', query: { view: 'leak' } });
+
+ expect(replace).lastCalledWith({
+ pathname: '/projects',
+ query: {
+ view: localeStorageMock[LS_PROJECTS_VIEW],
+ sort: localeStorageMock[LS_PROJECTS_SORT],
+ visualization: localeStorageMock[LS_PROJECTS_VISUALIZATION]
+ }
+ });
});
it('changes sort', () => {
const wrapper = shallowRender({}, push);
wrapper.find('PageHeader').prop<Function>('onSortChange')('size', false);
expect(push).lastCalledWith({ pathname: '/projects', query: { sort: 'size' } });
- expect(save).lastCalledWith('sonarqube.projects.sort', 'size', undefined);
+ expect(save).lastCalledWith(LS_PROJECTS_SORT, 'size');
});
it('changes perspective to leak', () => {
pathname: '/projects',
query: { view: 'leak', visualization: undefined }
});
- expect(save).toHaveBeenCalledWith('sonarqube.projects.sort', undefined, undefined);
- expect(save).toHaveBeenCalledWith('sonarqube.projects.view', 'leak', undefined);
- expect(save).toHaveBeenCalledWith('sonarqube.projects.visualization', undefined, undefined);
+ expect(save).toHaveBeenCalledWith(LS_PROJECTS_SORT, undefined);
+ expect(save).toHaveBeenCalledWith(LS_PROJECTS_VIEW, 'leak');
+ expect(save).toHaveBeenCalledWith(LS_PROJECTS_VISUALIZATION, undefined);
});
it('updates sorting when changing perspective from leak', () => {
pathname: '/projects',
query: { sort: 'coverage', view: undefined, visualization: undefined }
});
- expect(save).toHaveBeenCalledWith('sonarqube.projects.sort', 'coverage', undefined);
- expect(save).toHaveBeenCalledWith('sonarqube.projects.view', undefined, undefined);
- expect(save).toHaveBeenCalledWith('sonarqube.projects.visualization', undefined, undefined);
+ expect(save).toHaveBeenCalledWith(LS_PROJECTS_SORT, 'coverage');
+ expect(save).toHaveBeenCalledWith(LS_PROJECTS_VIEW, undefined);
+ expect(save).toHaveBeenCalledWith(LS_PROJECTS_VISUALIZATION, undefined);
});
it('changes perspective to risk visualization', () => {
pathname: '/projects',
query: { view: 'visualizations', visualization: 'risk' }
});
- expect(save).toHaveBeenCalledWith('sonarqube.projects.sort', undefined, undefined);
- expect(save).toHaveBeenCalledWith('sonarqube.projects.view', 'visualizations', undefined);
- expect(save).toHaveBeenCalledWith('sonarqube.projects.visualization', 'risk', undefined);
+ expect(save).toHaveBeenCalledWith(LS_PROJECTS_SORT, undefined);
+ expect(save).toHaveBeenCalledWith(LS_PROJECTS_VIEW, 'visualizations');
+ expect(save).toHaveBeenCalledWith(LS_PROJECTS_VISUALIZATION, 'risk');
});
it('handles favorite projects', () => {