aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorStas Vilchik <stas.vilchik@sonarsource.com>2018-02-15 17:20:18 +0100
committerStas Vilchik <stas.vilchik@sonarsource.com>2018-03-02 13:17:32 +0100
commitf9b743b8b4c974d5c08829c98e7dc5c52f52eed1 (patch)
tree94f4276c0bfb7cdb572a63c83f5df5a45789e6c4 /server
parenta6e5fdbdaabb92324857dd5c452d8f9b4ffff85b (diff)
downloadsonarqube-f9b743b8b4c974d5c08829c98e7dc5c52f52eed1.tar.gz
sonarqube-f9b743b8b4c974d5c08829c98e7dc5c52f52eed1.zip
SONAR-10423 display home page selector (#3065)
Diffstat (limited to 'server')
-rw-r--r--server/sonar-web/src/main/js/app/components/Landing.tsx2
-rw-r--r--server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx43
-rw-r--r--server/sonar-web/src/main/js/app/types.ts32
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/App.js8
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/PageActions.js5
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigationMeta.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigationMeta-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/PageHeader.tsx7
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.tsx.snap24
-rw-r--r--server/sonar-web/src/main/js/components/controls/HomePageSelect.tsx18
-rw-r--r--server/sonar-web/src/main/js/components/controls/__tests__/HomePageSelect-test.tsx66
-rw-r--r--server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/HomePageSelect-test.tsx.snap33
-rw-r--r--server/sonar-web/src/main/js/helpers/urls.ts15
13 files changed, 214 insertions, 47 deletions
diff --git a/server/sonar-web/src/main/js/app/components/Landing.tsx b/server/sonar-web/src/main/js/app/components/Landing.tsx
index ed401366a98..bf257783c72 100644
--- a/server/sonar-web/src/main/js/app/components/Landing.tsx
+++ b/server/sonar-web/src/main/js/app/components/Landing.tsx
@@ -37,7 +37,7 @@ class Landing extends React.PureComponent<Props> {
componentDidMount() {
const { currentUser, onSonarCloud } = this.props;
if (isLoggedIn(currentUser)) {
- if (onSonarCloud && currentUser.homepage) {
+ if (currentUser.homepage) {
const homepage = getHomePageUrl(currentUser.homepage);
this.context.router.replace(homepage);
} else {
diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx
index 42ab6e09378..9bee1a224fe 100644
--- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx
+++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx
@@ -19,13 +19,17 @@
*/
import * as React from 'react';
import { connect } from 'react-redux';
-import { Branch, Component, CurrentUser, isLoggedIn, HomePageType } from '../../../types';
+import { Branch, Component, CurrentUser, isLoggedIn, HomePageType, HomePage } from '../../../types';
import BranchStatus from '../../../../components/common/BranchStatus';
import DateTimeFormatter from '../../../../components/intl/DateTimeFormatter';
import Favorite from '../../../../components/controls/Favorite';
import HomePageSelect from '../../../../components/controls/HomePageSelect';
import Tooltip from '../../../../components/controls/Tooltip';
-import { isShortLivingBranch } from '../../../../helpers/branches';
+import {
+ isShortLivingBranch,
+ isLongLivingBranch,
+ getBranchName
+} from '../../../../helpers/branches';
import { translate } from '../../../../helpers/l10n';
import { getCurrentUser } from '../../../../store/rootReducer';
@@ -41,6 +45,20 @@ interface Props extends StateProps {
export function ComponentNavMeta({ branch, component, currentUser }: Props) {
const shortBranch = isShortLivingBranch(branch);
const mainBranch = !branch || branch.isMain;
+ const longBranch = isLongLivingBranch(branch);
+
+ let currentPage: HomePage | undefined;
+ if (component.qualifier === 'VW' || component.qualifier === 'SVW') {
+ currentPage = { type: HomePageType.Portfolio, component: component.key };
+ } else if (component.qualifier === 'APP') {
+ currentPage = { type: HomePageType.Application, component: component.key };
+ } else if (component.qualifier === 'TRK') {
+ currentPage = {
+ type: HomePageType.Project,
+ component: component.key,
+ branch: getBranchName(branch)
+ };
+ }
return (
<div className="navbar-context-meta">
@@ -51,26 +69,27 @@ export function ComponentNavMeta({ branch, component, currentUser }: Props) {
)}
{component.version &&
!shortBranch && (
- <Tooltip overlay={`${translate('version')} ${component.version}`} mouseEnterDelay={0.5}>
+ <Tooltip mouseEnterDelay={0.5} overlay={`${translate('version')} ${component.version}`}>
<div className="spacer-left text-limited">
{translate('version')} {component.version}
</div>
</Tooltip>
)}
- {isLoggedIn(currentUser) &&
- mainBranch && (
- <div className="navbar-context-meta-secondary">
+ {isLoggedIn(currentUser) && (
+ <div className="navbar-context-meta-secondary">
+ {mainBranch && (
<Favorite
component={component.key}
favorite={Boolean(component.isFavorite)}
qualifier={component.qualifier}
/>
- <HomePageSelect
- className="spacer-left"
- currentPage={{ type: HomePageType.Project, parameter: component.key }}
- />
- </div>
- )}
+ )}
+ {(mainBranch || longBranch) &&
+ currentPage !== undefined && (
+ <HomePageSelect className="spacer-left" currentPage={currentPage} />
+ )}
+ </div>
+ )}
{shortBranch && (
<div className="navbar-context-meta-secondary">
<BranchStatus branch={branch!} />
diff --git a/server/sonar-web/src/main/js/app/types.ts b/server/sonar-web/src/main/js/app/types.ts
index c2fe0d91b1c..252f6c135a2 100644
--- a/server/sonar-web/src/main/js/app/types.ts
+++ b/server/sonar-web/src/main/js/app/types.ts
@@ -130,16 +130,27 @@ export interface Group {
name: string;
}
-export interface HomePage {
- parameter?: string;
- type: HomePageType;
-}
+export type HomePage =
+ | { type: HomePageType.Application; component: string }
+ | { type: HomePageType.Issues }
+ | { type: HomePageType.MyIssues }
+ | { type: HomePageType.MyProjects }
+ | { type: HomePageType.Organization; organization: string }
+ | { type: HomePageType.Portfolio; component: string }
+ | { type: HomePageType.Portfolios }
+ | { type: HomePageType.Project; branch: string | undefined; component: string }
+ | { type: HomePageType.Projects };
export enum HomePageType {
- Project = 'PROJECT',
- Organization = 'ORGANIZATION',
+ Application = 'APPLICATION',
+ Issues = 'ISSUES',
+ MyIssues = 'MY_ISSUES',
MyProjects = 'MY_PROJECTS',
- MyIssues = 'MY_ISSUES'
+ Organization = 'ORGANIZATION',
+ Portfolio = 'PORTFOLIO',
+ Portfolios = 'PORTFOLIOS',
+ Project = 'PROJECT',
+ Projects = 'PROJECTS'
}
export interface IdentityProvider {
@@ -155,7 +166,12 @@ export function isLoggedIn(user: CurrentUser): user is LoggedInUser {
}
export function isSameHomePage(a: HomePage, b: HomePage) {
- return a.type === b.type && a.parameter === b.parameter;
+ return (
+ a.type === b.type &&
+ (a as any).branch === (b as any).branch &&
+ (a as any).component === (b as any).component &&
+ (a as any).organization === (b as any).organization
+ );
}
export interface LightComponent {
diff --git a/server/sonar-web/src/main/js/apps/issues/components/App.js b/server/sonar-web/src/main/js/apps/issues/components/App.js
index a6068133e94..edd359159ad 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/App.js
+++ b/server/sonar-web/src/main/js/apps/issues/components/App.js
@@ -55,7 +55,6 @@ import {
CurrentUser
} from '../utils'; */
import handleRequiredAuthentication from '../../../app/utils/handleRequiredAuthentication';
-import { isLoggedIn } from '../../../app/types';
import ListFooter from '../../../components/controls/ListFooter';
import EmptySearch from '../../../components/common/EmptySearch';
import FiltersHeader from '../../../components/common/FiltersHeader';
@@ -932,14 +931,13 @@ export default class App extends React.PureComponent {
) : (
<PageActions
canSetHome={
- this.props.onSonarCloud &&
- isLoggedIn(this.props.currentUser) &&
- this.props.myIssues &&
!this.props.organization &&
- !this.props.component
+ !this.props.component &&
+ (!this.props.onSonarCloud || this.props.myIssues)
}
loading={this.state.loading}
onReload={this.handleReload}
+ onSonarCloud={this.props.onSonarCloud}
paging={paging}
selectedIndex={selectedIndex}
/>
diff --git a/server/sonar-web/src/main/js/apps/issues/components/PageActions.js b/server/sonar-web/src/main/js/apps/issues/components/PageActions.js
index 328b61679ae..b194b24fecf 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/PageActions.js
+++ b/server/sonar-web/src/main/js/apps/issues/components/PageActions.js
@@ -32,6 +32,7 @@ type Props = {|
canSetHome: bool,
loading: boolean,
onReload: () => void,
+ onSonarCloud: bool,
paging: ?Paging,
selectedIndex: ?number
|};
@@ -77,7 +78,9 @@ export default class PageActions extends React.PureComponent {
{this.props.canSetHome && (
<HomePageSelect
className="huge-spacer-left"
- currentPage={{ type: HomePageType.MyIssues }}
+ currentPage={{
+ type: this.props.onSonarCloud ? HomePageType.MyIssues : HomePageType.Issues
+ }}
/>
)}
</div>
diff --git a/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigationMeta.tsx b/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigationMeta.tsx
index f9097f7b6e9..ba47200eb0e 100644
--- a/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigationMeta.tsx
+++ b/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigationMeta.tsx
@@ -39,8 +39,8 @@ export function OrganizationNavigationMeta({ onSonarCloud, organization }: Props
<a
className="spacer-right text-limited"
href={organization.url}
- title={organization.url}
- rel="nofollow">
+ rel="nofollow"
+ title={organization.url}>
{organization.url}
</a>
)}
@@ -50,7 +50,7 @@ export function OrganizationNavigationMeta({ onSonarCloud, organization }: Props
{onSonarCloud && (
<div className="navbar-context-meta-secondary">
<HomePageSelect
- currentPage={{ type: HomePageType.Organization, parameter: organization.key }}
+ currentPage={{ type: HomePageType.Organization, organization: organization.key }}
/>
</div>
)}
diff --git a/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigationMeta-test.tsx.snap b/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigationMeta-test.tsx.snap
index 207f8fe1faf..f70dd859642 100644
--- a/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigationMeta-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigationMeta-test.tsx.snap
@@ -20,7 +20,7 @@ exports[`renders 1`] = `
<Connect(HomePageSelect)
currentPage={
Object {
- "parameter": "foo",
+ "organization": "foo",
"type": "ORGANIZATION",
}
}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/PageHeader.tsx b/server/sonar-web/src/main/js/apps/projects/components/PageHeader.tsx
index cbc15e22bad..c0aeb6f835b 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/PageHeader.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/PageHeader.tsx
@@ -50,6 +50,7 @@ export default function PageHeader(props: Props) {
const { loading, total, projects, currentUser, view } = props;
const limitReached = projects != null && total != null && projects.length < total;
const defaultOption = isLoggedIn(currentUser) ? 'name' : 'analysis_date';
+ const showHomePageSelect = !props.onSonarCloud || props.isFavorite;
return (
<header className="page-header projects-topbar-items">
@@ -101,10 +102,12 @@ export default function PageHeader(props: Props) {
)}
</div>
- {props.isFavorite && (
+ {showHomePageSelect && (
<HomePageSelect
className="huge-spacer-left"
- currentPage={{ type: HomePageType.MyProjects }}
+ currentPage={
+ props.onSonarCloud ? { type: HomePageType.MyProjects } : { type: HomePageType.Projects }
+ }
/>
)}
</header>
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.tsx.snap
index 0f745872f11..5941322b8ae 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.tsx.snap
@@ -37,6 +37,14 @@ exports[`should render correctly 1`] = `
projects._projects
</span>
</div>
+ <Connect(HomePageSelect)
+ className="huge-spacer-left"
+ currentPage={
+ Object {
+ "type": "PROJECTS",
+ }
+ }
+ />
</header>
`;
@@ -80,6 +88,14 @@ exports[`should render correctly while loading 1`] = `
projects._projects
</span>
</div>
+ <Connect(HomePageSelect)
+ className="huge-spacer-left"
+ currentPage={
+ Object {
+ "type": "PROJECTS",
+ }
+ }
+ />
</header>
`;
@@ -120,6 +136,14 @@ exports[`should render disabled sorting options for visualizations 1`] = `
<div
className="projects-topbar-item is-last"
/>
+ <Connect(HomePageSelect)
+ className="huge-spacer-left"
+ currentPage={
+ Object {
+ "type": "PROJECTS",
+ }
+ }
+ />
</header>
`;
diff --git a/server/sonar-web/src/main/js/components/controls/HomePageSelect.tsx b/server/sonar-web/src/main/js/components/controls/HomePageSelect.tsx
index f534362e128..cf84c84e776 100644
--- a/server/sonar-web/src/main/js/components/controls/HomePageSelect.tsx
+++ b/server/sonar-web/src/main/js/components/controls/HomePageSelect.tsx
@@ -24,12 +24,11 @@ import Tooltip from './Tooltip';
import HomeIcon from '../icons-components/HomeIcon';
import { CurrentUser, isLoggedIn, HomePage, isSameHomePage } from '../../app/types';
import { translate } from '../../helpers/l10n';
-import { getCurrentUser, getGlobalSettingValue } from '../../store/rootReducer';
+import { getCurrentUser } from '../../store/rootReducer';
import { setHomePage } from '../../store/users/actions';
interface StateProps {
currentUser: CurrentUser;
- onSonarCloud: boolean;
}
interface DispatchProps {
@@ -49,9 +48,9 @@ class HomePageSelect extends React.PureComponent<Props> {
};
render() {
- const { currentPage, currentUser, onSonarCloud } = this.props;
+ const { currentPage, currentUser } = this.props;
- if (!isLoggedIn(currentUser) || !onSonarCloud) {
+ if (!isLoggedIn(currentUser)) {
return null;
}
@@ -82,14 +81,9 @@ class HomePageSelect extends React.PureComponent<Props> {
}
}
-const mapStateToProps = (state: any): StateProps => {
- const sonarCloudSetting = getGlobalSettingValue(state, 'sonar.sonarcloud.enabled');
-
- return {
- currentUser: getCurrentUser(state),
- onSonarCloud: Boolean(sonarCloudSetting && sonarCloudSetting.value === 'true')
- };
-};
+const mapStateToProps = (state: any): StateProps => ({
+ currentUser: getCurrentUser(state)
+});
const mapDispatchToProps: DispatchProps = { setHomePage };
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/HomePageSelect-test.tsx b/server/sonar-web/src/main/js/components/controls/__tests__/HomePageSelect-test.tsx
new file mode 100644
index 00000000000..4d5af634c91
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/controls/__tests__/HomePageSelect-test.tsx
@@ -0,0 +1,66 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 { shallow } from 'enzyme';
+import HomePageSelect from '../HomePageSelect';
+import { setHomePage } from '../../../api/users';
+import { HomePageType, HomePage, LoggedInUser } from '../../../app/types';
+import { click } from '../../../helpers/testUtils';
+import rootReducer, { getCurrentUser } from '../../../store/rootReducer';
+import configureStore from '../../../store/utils/configureStore';
+
+jest.mock('../../../api/users', () => ({
+ setHomePage: jest.fn(() => Promise.resolve())
+}));
+
+const homepage: HomePage = { type: HomePageType.Projects };
+
+it('should render unchecked', () => {
+ const store = configureStore(rootReducer, { users: { currentUser: { isLoggedIn: true } } });
+ expect(getWrapper(homepage, store)).toMatchSnapshot();
+});
+
+it('should render checked', () => {
+ const store = configureStore(rootReducer, {
+ users: { currentUser: { isLoggedIn: true, homepage } }
+ });
+ expect(getWrapper(homepage, store)).toMatchSnapshot();
+});
+
+it('should set new home page', async () => {
+ const store = configureStore(rootReducer, { users: { currentUser: { isLoggedIn: true } } });
+ const wrapper = getWrapper(homepage, store);
+ click(wrapper.find('a'));
+ await new Promise(setImmediate);
+ const currentUser = getCurrentUser(store.getState()) as LoggedInUser;
+ expect(currentUser.homepage).toEqual(homepage);
+ expect(setHomePage).toBeCalledWith(homepage);
+});
+
+it('should not render for anonymous', () => {
+ const store = configureStore(rootReducer, { users: { currentUser: { isLoggedIn: false } } });
+ expect(getWrapper(homepage, store).type()).toBeNull();
+});
+
+function getWrapper(currentPage: HomePage, store: any) {
+ return shallow(<HomePageSelect currentPage={currentPage} />, {
+ context: { store }
+ }).dive();
+}
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/HomePageSelect-test.tsx.snap b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/HomePageSelect-test.tsx.snap
new file mode 100644
index 00000000000..4ccff84cbdc
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/HomePageSelect-test.tsx.snap
@@ -0,0 +1,33 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render checked 1`] = `
+<Tooltip
+ overlay="homepage.current"
+ placement="left"
+>
+ <span
+ className="display-inline-block"
+ >
+ <HomeIcon
+ filled={true}
+ />
+ </span>
+</Tooltip>
+`;
+
+exports[`should render unchecked 1`] = `
+<Tooltip
+ overlay="homepage.check"
+ placement="left"
+>
+ <a
+ className="link-no-underline display-inline-block"
+ href="#"
+ onClick={[Function]}
+ >
+ <HomeIcon
+ filled={false}
+ />
+ </a>
+</Tooltip>
+`;
diff --git a/server/sonar-web/src/main/js/helpers/urls.ts b/server/sonar-web/src/main/js/helpers/urls.ts
index 2bde3a1184e..ae125724761 100644
--- a/server/sonar-web/src/main/js/helpers/urls.ts
+++ b/server/sonar-web/src/main/js/helpers/urls.ts
@@ -48,6 +48,10 @@ export function getProjectUrl(key: string, branch?: string): Location {
return { pathname: '/dashboard', query: { id: key, branch } };
}
+export function getPortfolioUrl(key: string): Location {
+ return { pathname: '/portfolio', query: { id: key } };
+}
+
export function getComponentBackgroundTaskUrl(componentKey: string, status?: string): Location {
return { pathname: '/project/background_tasks', query: { id: componentKey, status } };
}
@@ -178,12 +182,19 @@ export function getOrganizationUrl(organization: string) {
export function getHomePageUrl(homepage: HomePage) {
switch (homepage.type) {
+ case HomePageType.Application:
+ return getProjectUrl(homepage.component);
case HomePageType.Project:
- return getProjectUrl(homepage.parameter!);
+ return getProjectUrl(homepage.component, homepage.branch);
case HomePageType.Organization:
- return getOrganizationUrl(homepage.parameter!);
+ return getOrganizationUrl(homepage.organization);
+ case HomePageType.Portfolio:
+ return getPortfolioUrl(homepage.component);
+ case HomePageType.Portfolios:
+ return '/portfolios';
case HomePageType.MyProjects:
return '/projects';
+ case HomePageType.Issues:
case HomePageType.MyIssues:
return { pathname: '/issues', query: { resolved: 'false' } };
}