diff options
author | Stas Vilchik <stas.vilchik@sonarsource.com> | 2018-02-15 17:20:18 +0100 |
---|---|---|
committer | Stas Vilchik <stas.vilchik@sonarsource.com> | 2018-03-02 13:17:32 +0100 |
commit | f9b743b8b4c974d5c08829c98e7dc5c52f52eed1 (patch) | |
tree | 94f4276c0bfb7cdb572a63c83f5df5a45789e6c4 /server | |
parent | a6e5fdbdaabb92324857dd5c452d8f9b4ffff85b (diff) | |
download | sonarqube-f9b743b8b4c974d5c08829c98e7dc5c52f52eed1.tar.gz sonarqube-f9b743b8b4c974d5c08829c98e7dc5c52f52eed1.zip |
SONAR-10423 display home page selector (#3065)
Diffstat (limited to 'server')
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' } }; } |