From: Stas Vilchik Date: Thu, 7 Dec 2017 13:27:05 +0000 (+0100) Subject: SONAR-9554 Make "Analyze a project" and "Create an org" more discoverable X-Git-Tag: 7.0-RC1~84 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=7260d343ea6a7289695a8c509860cbcf726e433c;p=sonarqube.git SONAR-9554 Make "Analyze a project" and "Create an org" more discoverable --- diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.css b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.css index ac02282d585..48a78705251 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.css +++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.css @@ -27,13 +27,19 @@ border: none !important; } -.navbar-help { +.navbar-help, +.navbar-plus { + display: inline-block; height: var(--globalNavHeight); padding: calc(var(--globalNavHeight) - var(--globalNavContentHeight)) 12px !important; border-bottom: none !important; color: #fff !important; } +.navbar-plus { + margin-right: calc(-1 * var(--gridSize)); +} + .global-navbar-menu { display: flex; align-items: center; @@ -56,7 +62,8 @@ .navbar-brand:focus, .global-navbar-menu > li > a.active, .global-navbar-menu > li > a:hover, -.global-navbar-menu > li > a:focus { +.global-navbar-menu > li > a:focus, +.global-navbar-menu > .dropdown.open > a { background-color: #020202; } diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.js b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.js index 317077eb1af..564775c0179 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.js +++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.js @@ -24,9 +24,11 @@ import GlobalNavBranding from './GlobalNavBranding'; import GlobalNavMenu from './GlobalNavMenu'; import GlobalNavExplore from './GlobalNavExplore'; import GlobalNavUserContainer from './GlobalNavUserContainer'; +import GlobalNavPlus from './GlobalNavPlus'; import Search from '../../search/Search'; import GlobalHelp from '../../help/GlobalHelp'; import * as theme from '../../../theme'; +import { isLoggedIn } from '../../../types'; import NavBar from '../../../../components/nav/NavBar'; import Tooltip from '../../../../components/controls/Tooltip'; import HelpIcon from '../../../../components/icons-components/HelpIcon'; @@ -130,6 +132,10 @@ class GlobalNav extends React.PureComponent { + {isLoggedIn(this.props.currentUser) && + this.props.onSonarCloud && ( + + )} diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavPlus.tsx b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavPlus.tsx new file mode 100644 index 00000000000..12e1d701ba2 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavPlus.tsx @@ -0,0 +1,101 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:contact 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 * as classNames from 'classnames'; +import * as PropTypes from 'prop-types'; +import CreateOrganizationForm from '../../../../apps/account/organizations/CreateOrganizationForm'; +import PlusIcon from '../../../../components/icons-components/PlusIcon'; +import Dropdown from '../../../../components/controls/Dropdown'; +import { translate } from '../../../../helpers/l10n'; + +interface Props { + openOnboardingTutorial: () => void; +} + +interface State { + createOrganization: boolean; +} + +export default class GlobalNavPlus extends React.PureComponent { + static contextTypes = { + router: PropTypes.object + }; + + constructor(props: Props) { + super(props); + this.state = { createOrganization: false }; + } + + handleNewProjectClick = (event: React.SyntheticEvent) => { + event.preventDefault(); + this.props.openOnboardingTutorial(); + }; + + openCreateOrganizationForm = () => this.setState({ createOrganization: true }); + + closeCreateOrganizationForm = () => this.setState({ createOrganization: false }); + + handleNewOrganizationClick = (event: React.SyntheticEvent) => { + event.preventDefault(); + event.currentTarget.blur(); + this.openCreateOrganizationForm(); + }; + + handleCreateOrganization = ({ key }: { key: string }) => { + this.closeCreateOrganizationForm(); + this.context.router.push(`/organizations/${key}`); + }; + + render() { + return ( + + {({ onToggleClick, open }) => ( +
  • + + + + + {this.state.createOrganization && ( + + )} +
  • + )} +
    + ); + } +} diff --git a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavPlus-test.tsx b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavPlus-test.tsx new file mode 100644 index 00000000000..e315be5676f --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/GlobalNavPlus-test.tsx @@ -0,0 +1,38 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:contact 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 GlobalNavPlus from '../GlobalNavPlus'; +import { click } from '../../../../../helpers/testUtils'; + +it('render', () => { + const wrapper = shallow(); + expect(wrapper.is('Dropdown')).toBe(true); + expect(wrapper.find('Dropdown').shallow()).toMatchSnapshot(); +}); + +it('opens onboarding', () => { + const openOnboardingTutorial = jest.fn(); + const wrapper = shallow() + .find('Dropdown') + .shallow(); + click(wrapper.find('.js-new-project')); + expect(openOnboardingTutorial).toBeCalled(); +}); diff --git a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavPlus-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavPlus-test.tsx.snap new file mode 100644 index 00000000000..4400a02b250 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavPlus-test.tsx.snap @@ -0,0 +1,40 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`render 1`] = ` +
  • + + + + +
  • +`; diff --git a/server/sonar-web/src/main/js/apps/account/organizations/CreateOrganizationForm.js b/server/sonar-web/src/main/js/apps/account/organizations/CreateOrganizationForm.js deleted file mode 100644 index bd6b0c854fa..00000000000 --- a/server/sonar-web/src/main/js/apps/account/organizations/CreateOrganizationForm.js +++ /dev/null @@ -1,243 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 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. - */ -// @flow -import React from 'react'; -import { debounce } from 'lodash'; -import { connect } from 'react-redux'; -import { withRouter } from 'react-router'; -import Modal from '../../../components/controls/Modal'; -import { translate } from '../../../helpers/l10n'; -import { createOrganization } from '../../organizations/actions'; - -/*:: -type State = { - loading: boolean, - avatar: string, - avatarImage: string, - description: string, - key: string, - name: string, - url: string -}; -*/ - -class CreateOrganizationForm extends React.PureComponent { - /*:: mounted: boolean; */ - /*:: state: State; */ - /*:: props: { - createOrganization: (fields: {}) => Promise<*>, - router: { push: string => void } - }; -*/ - - constructor(props) { - super(props); - this.state = { - loading: false, - avatar: '', - avatarImage: '', - description: '', - key: '', - name: '', - url: '' - }; - this.changeAvatarImage = debounce(this.changeAvatarImage, 500); - } - - componentDidMount() { - this.mounted = true; - } - - componentWillUnmount() { - this.mounted = false; - } - - closeForm = () => { - this.props.router.push('/account/organizations'); - }; - - stopProcessing = () => { - if (this.mounted) { - this.setState({ loading: false }); - } - }; - - stopProcessingAndClose = () => { - if (this.mounted) { - this.setState({ loading: false }); - } - this.closeForm(); - }; - - handleAvatarInputChange = (e /*: Object */) => { - const { value } = e.target; - this.setState({ avatar: value }); - this.changeAvatarImage(value); - }; - - changeAvatarImage = (value /*: string */) => { - this.setState({ avatarImage: value }); - }; - - handleSubmit = (e /*: Object */) => { - e.preventDefault(); - const organization /*: Object */ = { name: this.state.name }; - if (this.state.avatar) { - Object.assign(organization, { avatar: this.state.avatar }); - } - if (this.state.description) { - Object.assign(organization, { description: this.state.description }); - } - if (this.state.key) { - Object.assign(organization, { key: this.state.key }); - } - if (this.state.url) { - Object.assign(organization, { url: this.state.url }); - } - this.setState({ loading: true }); - this.props - .createOrganization(organization) - .then(this.stopProcessingAndClose, this.stopProcessing); - }; - - render() { - return ( - -
    -

    {translate('my_account.create_organization')}

    -
    - -
    -
    -
    - - this.setState({ name: e.target.value })} - /> -
    - {translate('organization.name.description')} -
    -
    -
    - - this.setState({ key: e.target.value })} - /> -
    - {translate('organization.key.description')} -
    -
    -
    - - -
    - {translate('organization.avatar.description')} -
    - {!!this.state.avatarImage && ( -
    -
    - {translate('organization.avatar.preview')} - {':'} -
    - -
    - )} -
    -
    - -