diff options
author | Stas Vilchik <stas.vilchik@sonarsource.com> | 2017-07-19 16:56:38 +0200 |
---|---|---|
committer | Stas Vilchik <stas.vilchik@sonarsource.com> | 2017-07-21 16:14:07 +0200 |
commit | dccf95202c171899a045fd414f256e7ca22dadc2 (patch) | |
tree | 227b88cd5532313dd9bc21b4b99de71909b58f65 /server | |
parent | 04d40d224374526638084d837c6c30819463b511 (diff) | |
download | sonarqube-dccf95202c171899a045fd414f256e7ca22dadc2.tar.gz sonarqube-dccf95202c171899a045fd414f256e7ca22dadc2.zip |
SONAR-9583 Use fixed width layout for navigation
Diffstat (limited to 'server')
47 files changed, 1201 insertions, 1385 deletions
diff --git a/server/sonar-web/pom.xml b/server/sonar-web/pom.xml index 8ddd3bcd8d4..f3b2803ddfa 100644 --- a/server/sonar-web/pom.xml +++ b/server/sonar-web/pom.xml @@ -230,7 +230,7 @@ <dependency> <groupId>com.sonarsource</groupId> <artifactId>sonarsource-branding</artifactId> - <version>1.3.0.304</version> + <version>1.3.0.307</version> <type>war</type> <scope>runtime</scope> </dependency> diff --git a/server/sonar-web/src/main/js/app/components/GlobalContainer.js b/server/sonar-web/src/main/js/app/components/GlobalContainer.js index 284cba8d436..68c4c0f7374 100644 --- a/server/sonar-web/src/main/js/app/components/GlobalContainer.js +++ b/server/sonar-web/src/main/js/app/components/GlobalContainer.js @@ -28,7 +28,7 @@ export default function GlobalContainer(props: Object) { return ( <div className="global-container"> - <div className="page-wrapper page-wrapper-global" id="container"> + <div className="page-wrapper" id="container"> <div className="page-container"> <GlobalNav location={props.location} /> <GlobalMessagesContainer /> diff --git a/server/sonar-web/src/main/js/app/components/SimpleContainer.js b/server/sonar-web/src/main/js/app/components/SimpleContainer.js index 7d9396e7bf0..a2d22afea2d 100644 --- a/server/sonar-web/src/main/js/app/components/SimpleContainer.js +++ b/server/sonar-web/src/main/js/app/components/SimpleContainer.js @@ -20,6 +20,7 @@ // @flow import React from 'react'; import GlobalFooterContainer from './GlobalFooterContainer'; +import NavBar from '../../components/nav/NavBar'; type Props = { children?: React.Element<*> | Array<React.Element<*>>, @@ -46,10 +47,8 @@ export default class SimpleContainer extends React.PureComponent { render() { return ( <div className="global-container"> - <div className="page-wrapper page-wrapper-global" id="container"> - <nav className="navbar navbar-global page-container" id="global-navigation"> - <div className="navbar-header" /> - </nav> + <div className="page-wrapper" id="container"> + <NavBar className="navbar-global" id="global-navigation" height={30} /> <div id="bd" className="page-wrapper-simple"> <div id="nonav" className="page-simple"> diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.css b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.css new file mode 100644 index 00000000000..22ec0d36ea1 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.css @@ -0,0 +1,11 @@ +.navbar-context-favorite { + float: left; + padding: 7px 10px 0 0; +} + +.navbar-context-title-qualifier { + display: inline-block; + line-height: 16px; + padding-top: 5px; + box-sizing: border-box; +} diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.js b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.js index 23b08126fa7..d9963b913ac 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.js +++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.js @@ -23,9 +23,11 @@ import ComponentNavBreadcrumbs from './ComponentNavBreadcrumbs'; import ComponentNavMeta from './ComponentNavMeta'; import ComponentNavMenu from './ComponentNavMenu'; import RecentHistory from '../../RecentHistory'; +import ContextNavBar from '../../../../components/nav/ContextNavBar'; import { TooltipsContainer } from '../../../../components/mixins/tooltips-mixin'; import { getTasksForComponent } from '../../../../api/ce'; import { STATUSES } from '../../../../apps/background-tasks/constants'; +import './ComponentNav.css'; export default class ComponentNav extends React.PureComponent { componentDidMount() { @@ -66,36 +68,32 @@ export default class ComponentNav extends React.PureComponent { render() { return ( - <nav className="navbar navbar-context page-container" id="context-navigation"> - <div className="navbar-context-inner"> - <div className="container"> - <ComponentNavFavorite - component={this.props.component.key} - favorite={this.props.component.isFavorite} - /> + <ContextNavBar id="context-navigation" height={65}> + <ComponentNavFavorite + component={this.props.component.key} + favorite={this.props.component.isFavorite} + /> - <ComponentNavBreadcrumbs - component={this.props.component} - breadcrumbs={this.props.component.breadcrumbs} - /> + <ComponentNavBreadcrumbs + component={this.props.component} + breadcrumbs={this.props.component.breadcrumbs} + /> - <TooltipsContainer options={{ delay: { show: 0, hide: 2000 } }}> - <ComponentNavMeta - {...this.props} - {...this.state} - version={this.props.component.version} - analysisDate={this.props.component.analysisDate} - /> - </TooltipsContainer> + <TooltipsContainer options={{ delay: { show: 0, hide: 2000 } }}> + <ComponentNavMeta + {...this.props} + {...this.state} + version={this.props.component.version} + analysisDate={this.props.component.analysisDate} + /> + </TooltipsContainer> - <ComponentNavMenu - component={this.props.component} - conf={this.props.conf} - location={this.props.location} - /> - </div> - </div> - </nav> + <ComponentNavMenu + component={this.props.component} + conf={this.props.conf} + location={this.props.location} + /> + </ContextNavBar> ); } } diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBreadcrumbs.js b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBreadcrumbs.js index c719a568c70..c7f17f3a9cd 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBreadcrumbs.js +++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBreadcrumbs.js @@ -59,7 +59,7 @@ class ComponentNavBreadcrumbs extends React.PureComponent { <Link title={item.name} to={{ pathname: '/dashboard', query: { id: item.key } }} - className="link-base-color"> + className="link-base-color link-no-underline"> {index === breadcrumbs.length - 1 ? <strong> {itemName} @@ -74,7 +74,7 @@ class ComponentNavBreadcrumbs extends React.PureComponent { }); return ( - <h2 className="navbar-context-title"> + <h1 className="navbar-context-header"> <OrganizationHelmet title={component.name} organization={displayOrganization ? organization : null} @@ -84,14 +84,16 @@ class ComponentNavBreadcrumbs extends React.PureComponent { <span className="navbar-context-title-qualifier little-spacer-right"> <QualifierIcon qualifier={lastItem.qualifier} /> </span> - <OrganizationLink organization={organization} className="link-base-color"> + <OrganizationLink + organization={organization} + className="link-base-color link-no-underline"> {organization.name} </OrganizationLink> <span className="slash-separator" /> </span>} {items} {component.visibility === 'private' && <PrivateBadge className="spacer-left" />} - </h2> + </h1> ); } } diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.js b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.js index 0903d35eaa9..c484fa2b50c 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.js +++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.js @@ -19,6 +19,8 @@ */ import React from 'react'; import { Link } from 'react-router'; +import classNames from 'classnames'; +import NavBarTabs from '../../../../components/nav/NavBarTabs'; import { translate } from '../../../../helpers/l10n'; const SETTINGS_URLS = [ @@ -59,7 +61,7 @@ export default class ComponentNavMenu extends React.PureComponent { return ( <li> <Link to={{ pathname, query: { id: this.props.component.key } }} activeClassName="active"> - <i className="icon-home" /> + {translate('overview.page')} </Link> </li> ); @@ -131,11 +133,10 @@ export default class ComponentNavMenu extends React.PureComponent { } const isSettingsActive = SETTINGS_URLS.some(url => window.location.href.indexOf(url) !== -1); - const className = 'dropdown' + (isSettingsActive ? ' active' : ''); return ( - <li className={className}> + <li className="dropdown"> <a - className="dropdown-toggle navbar-admin-link" + className={classNames('dropdown-toggle', 'is-admin', { active: isSettingsActive })} id="component-navigation-admin" data-toggle="dropdown" href="#"> @@ -348,7 +349,7 @@ export default class ComponentNavMenu extends React.PureComponent { render() { return ( - <ul className="nav navbar-nav nav-tabs"> + <NavBarTabs> {this.renderDashboardLink()} {this.renderIssuesLink()} {this.renderComponentMeasuresLink()} @@ -356,7 +357,7 @@ export default class ComponentNavMenu extends React.PureComponent { {this.renderActivityLink()} {this.renderAdministration()} {this.renderExtensions()} - </ul> + </NavBarTabs> ); } } diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBreadcrumbs-test.js.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBreadcrumbs-test.js.snap index 385012e7de0..1588ae83e8a 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBreadcrumbs-test.js.snap +++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBreadcrumbs-test.js.snap @@ -1,8 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should not render breadcrumbs with one element 1`] = ` -<h2 - className="navbar-context-title" +<h1 + className="navbar-context-header" > <OrganizationHelmet organization={null} @@ -17,7 +17,7 @@ exports[`should not render breadcrumbs with one element 1`] = ` /> </span> <Link - className="link-base-color" + className="link-base-color link-no-underline" onlyActiveOnIndex={false} style={Object {}} title="My Project" @@ -35,12 +35,12 @@ exports[`should not render breadcrumbs with one element 1`] = ` </strong> </Link> </span> -</h2> +</h1> `; exports[`should render organization 1`] = ` -<h2 - className="navbar-context-title" +<h1 + className="navbar-context-header" > <OrganizationHelmet organization={ @@ -60,7 +60,7 @@ exports[`should render organization 1`] = ` /> </span> <OrganizationLink - className="link-base-color" + className="link-base-color link-no-underline" organization={ Object { "key": "foo", @@ -76,7 +76,7 @@ exports[`should render organization 1`] = ` </span> <span> <Link - className="link-base-color" + className="link-base-color link-no-underline" onlyActiveOnIndex={false} style={Object {}} title="My Project" @@ -94,5 +94,5 @@ exports[`should render organization 1`] = ` </strong> </Link> </span> -</h2> +</h1> `; diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.js.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.js.snap index 9b9a24ab6fe..1772a834780 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.js.snap +++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.js.snap @@ -1,9 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should work with extensions 1`] = ` -<ul - className="nav navbar-nav nav-tabs" -> +<NavBarTabs> <li> <Link activeClassName="active" @@ -18,9 +16,7 @@ exports[`should work with extensions 1`] = ` } } > - <i - className="icon-home" - /> + overview.page </Link> </li> <li> @@ -96,7 +92,7 @@ exports[`should work with extensions 1`] = ` className="dropdown" > <a - className="dropdown-toggle navbar-admin-link" + className="dropdown-toggle is-admin" data-toggle="dropdown" href="#" id="component-navigation-admin" @@ -200,13 +196,11 @@ exports[`should work with extensions 1`] = ` </li> </ul> </li> -</ul> +</NavBarTabs> `; exports[`should work with multiple extensions 1`] = ` -<ul - className="nav navbar-nav nav-tabs" -> +<NavBarTabs> <li> <Link activeClassName="active" @@ -221,9 +215,7 @@ exports[`should work with multiple extensions 1`] = ` } } > - <i - className="icon-home" - /> + overview.page </Link> </li> <li> @@ -299,7 +291,7 @@ exports[`should work with multiple extensions 1`] = ` className="dropdown" > <a - className="dropdown-toggle navbar-admin-link" + className="dropdown-toggle is-admin" data-toggle="dropdown" href="#" id="component-navigation-admin" @@ -437,5 +429,5 @@ exports[`should work with multiple extensions 1`] = ` </li> </ul> </li> -</ul> +</NavBarTabs> `; 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 new file mode 100644 index 00000000000..bdb8e019611 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.css @@ -0,0 +1,67 @@ +.navbar-global, +.navbar-global .navbar-inner { + background-color: #262626; + z-index: 421; +} + +.navbar-brand { + display: block; + margin-left: -10px; + padding-left: 10px; + padding-right: 10px; + border: none; +} + +.navbar-brand:hover, +.navbar-brand:focus { + background-color: #000; +} + +.navbar-login { + margin-right: -10px; +} + +.navbar-avatar { + margin-right: -3px !important; + padding: 3px !important; +} + +.navbar-help { + line-height: 16px !important; + padding: 7px !important; +} + +.global-navbar-menu { + display: flex; + align-items: center; +} + +.global-navbar-menu > li > a { + display: block; + padding: 8px 10px; + line-height: 14px; + border: none; + color: #ccc; + font-size: 12px; + letter-spacing: 0.05em; + transition: none; +} + +.global-navbar-menu > li > a.active, +.global-navbar-menu > li > a:hover, +.global-navbar-menu > li > a:focus { + background-color: #4b9fd5; + color: #fff; +} + +.global-navbar-menu > li > a.is-admin.active, +.global-navbar-menu > li > a.is-admin:hover, +.global-navbar-menu > li > a.is-admin:focus { + background-color: #ed7d20; +} + +@media print { + .navbar-global { + display: none !important; + } +} 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 5f6de29a8f7..5ad2c836f37 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 @@ -25,11 +25,13 @@ import GlobalNavMenu from './GlobalNavMenu'; import GlobalNavUserContainer from './GlobalNavUserContainer'; import Search from '../../search/Search'; import GlobalHelp from '../../help/GlobalHelp'; +import NavBar from '../../../../components/nav/NavBar'; import Tooltip from '../../../../components/controls/Tooltip'; import HelpIcon from '../../../../components/icons-components/HelpIcon'; import OnboardingModal from '../../../../apps/tutorials/onboarding/OnboardingModal'; import { getCurrentUser, getAppState, getSettingValue } from '../../../../store/rootReducer'; import { translate } from '../../../../helpers/l10n'; +import './GlobalNav.css'; type Props = { appState: { organizationsEnabled: boolean }, @@ -96,29 +98,27 @@ class GlobalNav extends React.PureComponent { render() { return ( - <nav className="navbar navbar-global page-container" id="global-navigation"> - <div className="container"> - <GlobalNavBranding /> - - <GlobalNavMenu {...this.props} /> - - <ul className="nav navbar-nav navbar-right"> - <Search appState={this.props.appState} currentUser={this.props.currentUser} /> - <li> - <a className="navbar-help" onClick={this.handleHelpClick} href="#"> - {this.state.onboardingTutorialTooltip - ? <Tooltip - defaultVisible={true} - overlay={translate('tutorials.follow_later')} - trigger="manual"> - <HelpIcon /> - </Tooltip> - : <HelpIcon />} - </a> - </li> - <GlobalNavUserContainer {...this.props} /> - </ul> - </div> + <NavBar className="navbar-global" id="global-navigation" height={30}> + <GlobalNavBranding /> + + <GlobalNavMenu {...this.props} /> + + <ul className="global-navbar-menu pull-right"> + <Search appState={this.props.appState} currentUser={this.props.currentUser} /> + <li> + <a className="navbar-help" onClick={this.handleHelpClick} href="#"> + {this.state.onboardingTutorialTooltip + ? <Tooltip + defaultVisible={true} + overlay={translate('tutorials.follow_later')} + trigger="manual"> + <HelpIcon /> + </Tooltip> + : <HelpIcon />} + </a> + </li> + <GlobalNavUserContainer {...this.props} /> + </ul> {this.state.helpOpen && <GlobalHelp @@ -130,7 +130,7 @@ class GlobalNav extends React.PureComponent { {this.state.onboardingTutorialOpen && <OnboardingModal onFinish={this.closeOnboardingTutorial} />} - </nav> + </NavBar> ); } } diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavBranding.js b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavBranding.js index d0bac2720ea..968a9d109eb 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavBranding.js +++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavBranding.js @@ -31,7 +31,7 @@ class GlobalNavBranding extends React.PureComponent { renderLogo() { const url = this.props.customLogoUrl || `${window.baseUrl}/images/logo.svg`; - const width = this.props.customLogoWidth || 100; + const width = this.props.customLogoUrl ? this.props.customLogoWidth || 100 : 83; const height = 30; const title = translate('layout.sonar.slogan'); return <img src={url} width={width} height={height} alt={title} title={title} />; @@ -39,11 +39,9 @@ class GlobalNavBranding extends React.PureComponent { render() { const homeController = this.props.currentUser.isLoggedIn ? '/projects' : '/about'; - const homeLinkClassName = - 'navbar-brand' + (this.props.customLogoUrl ? ' navbar-brand-custom' : ''); return ( - <div className="navbar-header"> - <Link to={homeController} className={homeLinkClassName}> + <div className="pull-left"> + <Link to={homeController} className="navbar-brand"> {this.renderLogo()} </Link> </div> diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.js b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.js index 5b99dd06c92..6535201176f 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.js +++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.js @@ -111,7 +111,7 @@ export default class GlobalNavMenu extends React.PureComponent { } return ( <li> - <Link to="/settings" className="navbar-admin-link" activeClassName="active"> + <Link to="/settings" className="is-admin" activeClassName="active"> {translate('layout.settings')} </Link> </li> @@ -152,7 +152,7 @@ export default class GlobalNavMenu extends React.PureComponent { const { organizationsEnabled } = this.props.appState; return ( - <ul className="nav navbar-nav"> + <ul className="global-navbar-menu pull-left"> {this.renderProjects()} {governanceInstalled && this.renderPortfolios()} {this.renderIssuesLink()} diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUser.js b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUser.js index d5d25ec79a6..830a351b05e 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUser.js +++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUser.js @@ -168,7 +168,7 @@ export default class GlobalNavUser extends React.PureComponent { renderAnonymous() { return ( <li> - <a onClick={this.handleLogin} href="#"> + <a className="navbar-login" onClick={this.handleLogin} href="#"> {translate('layout.login')} </a> </li> diff --git a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavMenu-test.js.snap b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavMenu-test.js.snap index bb21d44b23e..3315afa83e2 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavMenu-test.js.snap +++ b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavMenu-test.js.snap @@ -2,7 +2,7 @@ exports[`should show administration menu if the user has the rights 1`] = ` <ul - className="nav navbar-nav" + className="global-navbar-menu pull-left" > <li> <Link @@ -63,7 +63,7 @@ exports[`should show administration menu if the user has the rights 1`] = ` <li> <Link activeClassName="active" - className="navbar-admin-link" + className="is-admin" onlyActiveOnIndex={false} style={Object {}} to="/settings" @@ -76,7 +76,7 @@ exports[`should show administration menu if the user has the rights 1`] = ` exports[`should work with extensions 1`] = ` <ul - className="nav navbar-nav" + className="global-navbar-menu pull-left" > <li> <Link diff --git a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.js.snap b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.js.snap index f3800e5f3ec..f42167519ef 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.js.snap +++ b/server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.js.snap @@ -64,6 +64,7 @@ exports[`should not render the users organizations when they are not activated 1 exports[`should render the right interface for anonymous user 1`] = ` <li> <a + className="navbar-login" href="#" onClick={[Function]} > @@ -322,6 +323,7 @@ exports[`should update the component correctly when the user changes to anonymou exports[`should update the component correctly when the user changes to anonymous 2`] = ` <li> <a + className="navbar-login" href="#" onClick={[Function]} > diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.js b/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.js index 88750547c4a..21540b25a99 100644 --- a/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.js +++ b/server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.js @@ -21,6 +21,8 @@ import React from 'react'; import classNames from 'classnames'; import { IndexLink, Link } from 'react-router'; import { connect } from 'react-redux'; +import ContextNavBar from '../../../../components/nav/ContextNavBar'; +import NavBarTabs from '../../../../components/nav/NavBarTabs'; import { translate } from '../../../../helpers/l10n'; import { areThereCustomOrganizations } from '../../../../store/rootReducer'; @@ -64,135 +66,129 @@ class SettingsNav extends React.PureComponent { const isProjects = this.isProjectsActive(); const isSystem = this.isSystemActive(); - const securityClassName = classNames('dropdown', { active: isSecurity }); - const projectsClassName = classNames('dropdown', { active: isProjects }); - const systemClassName = classNames('dropdown', { active: isSystem }); - const configurationClassNames = classNames('dropdown', { + const securityClassName = classNames('dropdown-toggle', { active: isSecurity }); + const projectsClassName = classNames('dropdown-toggle', { active: isProjects }); + const systemClassName = classNames('dropdown-toggle', { active: isSystem }); + const configurationClassNames = classNames('dropdown-toggle', { active: !isSecurity && !isProjects && !isSystem }); return ( - <nav className="navbar navbar-context page-container" id="context-navigation"> - <div className="navbar-context-inner"> - <div className="container"> - <ul className="nav navbar-nav nav-crumbs"> + <ContextNavBar id="context-navigation" height={65}> + <h1 className="navbar-context-header"> + <strong> + {translate('layout.settings')} + </strong> + </h1> + + <NavBarTabs> + <li className="dropdown"> + <a + className={configurationClassNames} + data-toggle="dropdown" + id="settings-navigation-configuration" + href="#"> + {translate('sidebar.project_settings')} <i className="icon-dropdown" /> + </a> + <ul className="dropdown-menu"> <li> - <IndexLink to="/settings"> - {translate('layout.settings')} + <IndexLink to="/settings" activeClassName="active"> + {translate('settings.page')} </IndexLink> </li> + <li> + <IndexLink to="/settings/licenses" activeClassName="active"> + {translate('property.category.licenses')} + </IndexLink> + </li> + <li> + <IndexLink to="/settings/encryption" activeClassName="active"> + {translate('property.category.security.encryption')} + </IndexLink> + </li> + <li> + <IndexLink to="/settings/server_id" activeClassName="active"> + {translate('property.category.server_id')} + </IndexLink> + </li> + <li> + <IndexLink to="/metrics" activeClassName="active"> + Custom Metrics + </IndexLink> + </li> + {this.props.extensions.map(this.renderExtension)} </ul> + </li> - <ul className="nav navbar-nav nav-tabs"> - <li className={configurationClassNames}> - <a - className="dropdown-toggle" - data-toggle="dropdown" - id="settings-navigation-configuration" - href="#"> - {translate('sidebar.project_settings')} <i className="icon-dropdown" /> - </a> - <ul className="dropdown-menu"> - <li> - <IndexLink to="/settings" activeClassName="active"> - {translate('settings.page')} - </IndexLink> - </li> - <li> - <IndexLink to="/settings/licenses" activeClassName="active"> - {translate('property.category.licenses')} - </IndexLink> - </li> - <li> - <IndexLink to="/settings/encryption" activeClassName="active"> - {translate('property.category.security.encryption')} - </IndexLink> - </li> - <li> - <IndexLink to="/settings/server_id" activeClassName="active"> - {translate('property.category.server_id')} - </IndexLink> - </li> - <li> - <IndexLink to="/metrics" activeClassName="active"> - Custom Metrics - </IndexLink> - </li> - {this.props.extensions.map(this.renderExtension)} - </ul> + <li className="dropdown"> + <a className={securityClassName} data-toggle="dropdown" href="#"> + {translate('sidebar.security')} <i className="icon-dropdown" /> + </a> + <ul className="dropdown-menu"> + <li> + <IndexLink to="/users" activeClassName="active"> + {translate('users.page')} + </IndexLink> </li> - - <li className={securityClassName}> - <a className="dropdown-toggle" data-toggle="dropdown" href="#"> - {translate('sidebar.security')} <i className="icon-dropdown" /> - </a> - <ul className="dropdown-menu"> - <li> - <IndexLink to="/users" activeClassName="active"> - {translate('users.page')} - </IndexLink> - </li> - {!this.props.customOrganizations && - <li> - <IndexLink to="/groups" activeClassName="active"> - {translate('user_groups.page')} - </IndexLink> - </li>} - {!this.props.customOrganizations && - <li> - <IndexLink to="/roles/global" activeClassName="active"> - {translate('global_permissions.page')} - </IndexLink> - </li>} - {!this.props.customOrganizations && - <li> - <IndexLink to="/permission_templates" activeClassName="active"> - {translate('permission_templates')} - </IndexLink> - </li>} - </ul> + {!this.props.customOrganizations && + <li> + <IndexLink to="/groups" activeClassName="active"> + {translate('user_groups.page')} + </IndexLink> + </li>} + {!this.props.customOrganizations && + <li> + <IndexLink to="/roles/global" activeClassName="active"> + {translate('global_permissions.page')} + </IndexLink> + </li>} + {!this.props.customOrganizations && + <li> + <IndexLink to="/permission_templates" activeClassName="active"> + {translate('permission_templates')} + </IndexLink> + </li>} + </ul> + </li> + + <li className="dropdown"> + <a className={projectsClassName} data-toggle="dropdown" href="#"> + {translate('sidebar.projects')} <i className="icon-dropdown" /> + </a> + <ul className="dropdown-menu"> + {!this.props.customOrganizations && + <li> + <IndexLink to="/projects_admin" activeClassName="active"> + Management + </IndexLink> + </li>} + <li> + <IndexLink to="/background_tasks" activeClassName="active"> + {translate('background_tasks.page')} + </IndexLink> </li> + </ul> + </li> - <li className={projectsClassName}> - <a className="dropdown-toggle" data-toggle="dropdown" href="#"> - {translate('sidebar.projects')} <i className="icon-dropdown" /> - </a> - <ul className="dropdown-menu"> - {!this.props.customOrganizations && - <li> - <IndexLink to="/projects_admin" activeClassName="active"> - Management - </IndexLink> - </li>} - <li> - <IndexLink to="/background_tasks" activeClassName="active"> - {translate('background_tasks.page')} - </IndexLink> - </li> - </ul> + <li className="dropdown"> + <a className={systemClassName} data-toggle="dropdown" href="#"> + {translate('sidebar.system')} <i className="icon-dropdown" /> + </a> + <ul className="dropdown-menu"> + <li> + <IndexLink to="/updatecenter" activeClassName="active"> + {translate('update_center.page')} + </IndexLink> </li> - - <li className={systemClassName}> - <a className="dropdown-toggle" data-toggle="dropdown" href="#"> - {translate('sidebar.system')} <i className="icon-dropdown" /> - </a> - <ul className="dropdown-menu"> - <li> - <IndexLink to="/updatecenter" activeClassName="active"> - {translate('update_center.page')} - </IndexLink> - </li> - <li> - <IndexLink to="/system" activeClassName="active"> - {translate('system_info.page')} - </IndexLink> - </li> - </ul> + <li> + <IndexLink to="/system" activeClassName="active"> + {translate('system_info.page')} + </IndexLink> </li> </ul> - </div> - </div> - </nav> + </li> + </NavBarTabs> + </ContextNavBar> ); } } diff --git a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.js.snap b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.js.snap index 1efd658114f..7f285d4906c 100644 --- a/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.js.snap +++ b/server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.js.snap @@ -1,223 +1,209 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should work with extensions 1`] = ` -<nav - className="navbar navbar-context page-container" +<ContextNavBar + height={65} id="context-navigation" > - <div - className="navbar-context-inner" + <h1 + className="navbar-context-header" > - <div - className="container" + <strong> + layout.settings + </strong> + </h1> + <NavBarTabs> + <li + className="dropdown" > + <a + className="dropdown-toggle active" + data-toggle="dropdown" + href="#" + id="settings-navigation-configuration" + > + sidebar.project_settings + + <i + className="icon-dropdown" + /> + </a> <ul - className="nav navbar-nav nav-crumbs" + className="dropdown-menu" > <li> <IndexLink + activeClassName="active" to="/settings" > - layout.settings + settings.page + </IndexLink> + </li> + <li> + <IndexLink + activeClassName="active" + to="/settings/licenses" + > + property.category.licenses + </IndexLink> + </li> + <li> + <IndexLink + activeClassName="active" + to="/settings/encryption" + > + property.category.security.encryption + </IndexLink> + </li> + <li> + <IndexLink + activeClassName="active" + to="/settings/server_id" + > + property.category.server_id </IndexLink> </li> + <li> + <IndexLink + activeClassName="active" + to="/metrics" + > + Custom Metrics + </IndexLink> + </li> + <li> + <Link + activeClassName="active" + onlyActiveOnIndex={false} + style={Object {}} + to="/admin/extension/foo" + > + Foo + </Link> + </li> </ul> + </li> + <li + className="dropdown" + > + <a + className="dropdown-toggle" + data-toggle="dropdown" + href="#" + > + sidebar.security + + <i + className="icon-dropdown" + /> + </a> <ul - className="nav navbar-nav nav-tabs" + className="dropdown-menu" > - <li - className="dropdown active" - > - <a - className="dropdown-toggle" - data-toggle="dropdown" - href="#" - id="settings-navigation-configuration" + <li> + <IndexLink + activeClassName="active" + to="/users" > - sidebar.project_settings - - <i - className="icon-dropdown" - /> - </a> - <ul - className="dropdown-menu" + users.page + </IndexLink> + </li> + <li> + <IndexLink + activeClassName="active" + to="/groups" > - <li> - <IndexLink - activeClassName="active" - to="/settings" - > - settings.page - </IndexLink> - </li> - <li> - <IndexLink - activeClassName="active" - to="/settings/licenses" - > - property.category.licenses - </IndexLink> - </li> - <li> - <IndexLink - activeClassName="active" - to="/settings/encryption" - > - property.category.security.encryption - </IndexLink> - </li> - <li> - <IndexLink - activeClassName="active" - to="/settings/server_id" - > - property.category.server_id - </IndexLink> - </li> - <li> - <IndexLink - activeClassName="active" - to="/metrics" - > - Custom Metrics - </IndexLink> - </li> - <li> - <Link - activeClassName="active" - onlyActiveOnIndex={false} - style={Object {}} - to="/admin/extension/foo" - > - Foo - </Link> - </li> - </ul> + user_groups.page + </IndexLink> </li> - <li - className="dropdown" - > - <a - className="dropdown-toggle" - data-toggle="dropdown" - href="#" + <li> + <IndexLink + activeClassName="active" + to="/roles/global" > - sidebar.security - - <i - className="icon-dropdown" - /> - </a> - <ul - className="dropdown-menu" + global_permissions.page + </IndexLink> + </li> + <li> + <IndexLink + activeClassName="active" + to="/permission_templates" > - <li> - <IndexLink - activeClassName="active" - to="/users" - > - users.page - </IndexLink> - </li> - <li> - <IndexLink - activeClassName="active" - to="/groups" - > - user_groups.page - </IndexLink> - </li> - <li> - <IndexLink - activeClassName="active" - to="/roles/global" - > - global_permissions.page - </IndexLink> - </li> - <li> - <IndexLink - activeClassName="active" - to="/permission_templates" - > - permission_templates - </IndexLink> - </li> - </ul> + permission_templates + </IndexLink> </li> - <li - className="dropdown" - > - <a - className="dropdown-toggle" - data-toggle="dropdown" - href="#" + </ul> + </li> + <li + className="dropdown" + > + <a + className="dropdown-toggle" + data-toggle="dropdown" + href="#" + > + sidebar.projects + + <i + className="icon-dropdown" + /> + </a> + <ul + className="dropdown-menu" + > + <li> + <IndexLink + activeClassName="active" + to="/projects_admin" > - sidebar.projects - - <i - className="icon-dropdown" - /> - </a> - <ul - className="dropdown-menu" + Management + </IndexLink> + </li> + <li> + <IndexLink + activeClassName="active" + to="/background_tasks" > - <li> - <IndexLink - activeClassName="active" - to="/projects_admin" - > - Management - </IndexLink> - </li> - <li> - <IndexLink - activeClassName="active" - to="/background_tasks" - > - background_tasks.page - </IndexLink> - </li> - </ul> + background_tasks.page + </IndexLink> </li> - <li - className="dropdown" - > - <a - className="dropdown-toggle" - data-toggle="dropdown" - href="#" + </ul> + </li> + <li + className="dropdown" + > + <a + className="dropdown-toggle" + data-toggle="dropdown" + href="#" + > + sidebar.system + + <i + className="icon-dropdown" + /> + </a> + <ul + className="dropdown-menu" + > + <li> + <IndexLink + activeClassName="active" + to="/updatecenter" > - sidebar.system - - <i - className="icon-dropdown" - /> - </a> - <ul - className="dropdown-menu" + update_center.page + </IndexLink> + </li> + <li> + <IndexLink + activeClassName="active" + to="/system" > - <li> - <IndexLink - activeClassName="active" - to="/updatecenter" - > - update_center.page - </IndexLink> - </li> - <li> - <IndexLink - activeClassName="active" - to="/system" - > - system_info.page - </IndexLink> - </li> - </ul> + system_info.page + </IndexLink> </li> </ul> - </div> - </div> -</nav> + </li> + </NavBarTabs> +</ContextNavBar> `; diff --git a/server/sonar-web/src/main/js/app/components/search/Search.css b/server/sonar-web/src/main/js/app/components/search/Search.css new file mode 100644 index 00000000000..f9297eabb83 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/search/Search.css @@ -0,0 +1,97 @@ +.navbar-search { + position: relative; + padding-right: 3px; +} + +.navbar-search-input { + vertical-align: middle; + width: 310px; + margin-top: 3px; + margin-bottom: 3px; + padding-left: 26px !important; +} + +.navbar-search-input-hint { + position: absolute; + top: 4px; + right: 30px; + line-height: 24px; + font-size: 12px; + color: #777; +} +.navbar-search-input-hint.is-shifted { + z-index: 7501; + top: 32px; +} + +.navbar-search-icon { + position: relative; + vertical-align: middle; + width: 16px; + margin-right: -20px; + color: #777; +} + +.navbar-search-icon:before { + font-size: 14px; +} + +.navbar-search-item-link { + display: flex !important; +} + +.navbar-search-item-match { + flex-grow: 5; + overflow: hidden; + text-overflow: ellipsis; +} + +.navbar-search-item-right { + flex-grow: 1; + padding-left: 10px; + text-align: right; +} + +.navbar-search-item-icons { + position: relative; + flex-shrink: 0; + width: 16px; + height: 16px; +} +.navbar-search-item-icons > * { + position: absolute; + z-index: 5; + top: 0; + left: 0; +} + +.navbar-search-item-icons > .icon-star, +.navbar-search-item-icons > .icon-clock { + z-index: 6; + top: -4px; + left: -5px; +} + +.navbar-search-shortcut-hint { + line-height: 16px; + margin-top: 5px; + padding: 5px 10px; + border-top: 1px solid #e6e6e6; + background-color: #f3f3f3; + color: #777; + font-size: 11px; +} + +.navbar-search-no-results { + margin-top: 4px; + padding: 5px 10px; +} + +.global-navbar-search-dropdown { + max-height: 80vh; + width: 440px; + padding: 0; + overflow-y: auto; + overflow-x: hidden; + box-shadow: 0 6px 12px rgba(0, 0, 0, .175); +} diff --git a/server/sonar-web/src/main/js/app/components/search/Search.js b/server/sonar-web/src/main/js/app/components/search/Search.js index 04098668141..808614922d1 100644 --- a/server/sonar-web/src/main/js/app/components/search/Search.js +++ b/server/sonar-web/src/main/js/app/components/search/Search.js @@ -33,6 +33,7 @@ import { getSuggestions } from '../../../api/components'; import { translate, translateWithParameters } from '../../../helpers/l10n'; import { scrollToElement } from '../../../helpers/scrolling'; import { getProjectUrl } from '../../../helpers/urls'; +import './Search.css'; type Props = {| appState: { organizationsEnabled: boolean }, diff --git a/server/sonar-web/src/main/js/app/utils/startReactApp.js b/server/sonar-web/src/main/js/app/utils/startReactApp.js index 18f4ba69ed7..41779d0b4ab 100644 --- a/server/sonar-web/src/main/js/app/utils/startReactApp.js +++ b/server/sonar-web/src/main/js/app/utils/startReactApp.js @@ -29,7 +29,6 @@ import GlobalContainer from '../components/GlobalContainer'; import SimpleContainer from '../components/SimpleContainer'; import SimpleSessionsContainer from '../../apps/sessions/components/SimpleSessionsContainer'; import Landing from '../components/Landing'; -import ProjectContainer from '../components/ProjectContainer'; import ProjectAdminContainer from '../components/ProjectAdminContainer'; import ProjectPageExtension from '../components/extensions/ProjectPageExtension'; import ProjectAdminPageExtension from '../components/extensions/ProjectAdminPageExtension'; @@ -165,7 +164,9 @@ const startReactApp = () => { <Route path="profiles" childRoutes={qualityProfilesRoutes} /> <Route path="web_api" childRoutes={webAPIRoutes} /> - <Route component={ProjectContainer}> + <Route + getComponent={() => + import('../components/ProjectContainer').then(i => i.default)}> <Route path="code" childRoutes={codeRoutes} /> <Route path="component_measures" childRoutes={componentMeasuresRoutes} /> <Route path="custom_measures" childRoutes={customMeasuresRoutes} /> diff --git a/server/sonar-web/src/main/js/apps/about/components/AboutApp.js b/server/sonar-web/src/main/js/apps/about/components/AboutApp.js index f22509ca1d5..715543e99a4 100644 --- a/server/sonar-web/src/main/js/apps/about/components/AboutApp.js +++ b/server/sonar-web/src/main/js/apps/about/components/AboutApp.js @@ -132,67 +132,65 @@ class AboutApp extends React.PureComponent { } return ( - <div id="about-page" className="about-page"> - <div className="about-page-container"> - <div className="about-page-entry"> - <div className="about-page-intro"> - <h1 className="big-spacer-bottom"> - {translate('layout.sonar.slogan')} - </h1> - {!this.props.currentUser.isLoggedIn && - <Link to="/sessions/new" className="button button-active big-spacer-right"> - {translate('layout.login')} - </Link>} - <a - className="button" - href="https://redirect.sonarsource.com/doc/home.html" - target="_blank"> - {translate('about_page.read_documentation')} - </a> - </div> - - <div className="about-page-instance"> - <AboutProjects count={projectsCount} loading={loading} /> - <EntryIssueTypes - bugs={bugs} - codeSmells={codeSmells} - loading={loading} - vulnerabilities={vulnerabilities} - /> - </div> + <div id="about-page" className="page page-limited about-page"> + <div className="about-page-entry"> + <div className="about-page-intro"> + <h1 className="big-spacer-bottom"> + {translate('layout.sonar.slogan')} + </h1> + {!this.props.currentUser.isLoggedIn && + <Link to="/sessions/new" className="button button-active big-spacer-right"> + {translate('layout.login')} + </Link>} + <a + className="button" + href="https://redirect.sonarsource.com/doc/home.html" + target="_blank"> + {translate('about_page.read_documentation')} + </a> </div> - {customText != null && - customText.value && - <div - className="about-page-section" - dangerouslySetInnerHTML={{ __html: customText.value }} - />} + <div className="about-page-instance"> + <AboutProjects count={projectsCount} loading={loading} /> + <EntryIssueTypes + bugs={bugs} + codeSmells={codeSmells} + loading={loading} + vulnerabilities={vulnerabilities} + /> + </div> + </div> - <AboutLanguages /> + {customText != null && + customText.value && + <div + className="about-page-section" + dangerouslySetInnerHTML={{ __html: customText.value }} + />} - <AboutQualityModel /> + <AboutLanguages /> - <div className="flex-columns"> - <div className="flex-column flex-column-half about-page-group-boxes"> - <AboutCleanCode /> - </div> - <div className="flex-column flex-column-half about-page-group-boxes"> - <AboutLeakPeriod /> - </div> - </div> + <AboutQualityModel /> - <div className="flex-columns"> - <div className="flex-column flex-column-half about-page-group-boxes"> - <AboutQualityGates /> - </div> - <div className="flex-column flex-column-half about-page-group-boxes"> - <AboutStandards appState={this.props.appState} /> - </div> + <div className="flex-columns"> + <div className="flex-column flex-column-half about-page-group-boxes"> + <AboutCleanCode /> + </div> + <div className="flex-column flex-column-half about-page-group-boxes"> + <AboutLeakPeriod /> </div> + </div> - <AboutScanners /> + <div className="flex-columns"> + <div className="flex-column flex-column-half about-page-group-boxes"> + <AboutQualityGates /> + </div> + <div className="flex-column flex-column-half about-page-group-boxes"> + <AboutStandards appState={this.props.appState} /> + </div> </div> + + <AboutScanners /> </div> ); } diff --git a/server/sonar-web/src/main/js/apps/about/components/AboutAppForSonarQubeDotCom.js b/server/sonar-web/src/main/js/apps/about/components/AboutAppForSonarQubeDotCom.js index 2e9eee74ed4..80041d540bb 100644 --- a/server/sonar-web/src/main/js/apps/about/components/AboutAppForSonarQubeDotCom.js +++ b/server/sonar-web/src/main/js/apps/about/components/AboutAppForSonarQubeDotCom.js @@ -51,7 +51,7 @@ export default function AboutAppForSonarQubeDotCom(props: Props) { return ( <div id="about-page" className="about-page sqcom-about-page"> <div className="sqcom-about-page-entry"> - <div className="about-page-container"> + <div className="page-limited"> <div className="sqcom-about-page-intro"> <h1 className="big-spacer-bottom"> Continuous Code Quality<br />as a Service @@ -73,7 +73,7 @@ export default function AboutAppForSonarQubeDotCom(props: Props) { <AboutRulesForSonarQubeDotCom appState={props.appState} /> - <div className="about-page-container"> + <div className="page-limited"> {customText != null && customText.value && <div diff --git a/server/sonar-web/src/main/js/apps/about/components/AboutRulesForSonarQubeDotCom.js b/server/sonar-web/src/main/js/apps/about/components/AboutRulesForSonarQubeDotCom.js index 5add8a0eab1..3496a27e3d3 100644 --- a/server/sonar-web/src/main/js/apps/about/components/AboutRulesForSonarQubeDotCom.js +++ b/server/sonar-web/src/main/js/apps/about/components/AboutRulesForSonarQubeDotCom.js @@ -34,7 +34,7 @@ export default function AboutRulesForSonarQubeDotCom(props: Props) { return ( <div className="sqcom-about-rules"> - <div className="about-page-container"> + <div className="page-limited"> <Link to={getRulesUrl(null, organization)} className="sqcom-about-rules-link"> +3,000 rules <span className="spacer-left"> diff --git a/server/sonar-web/src/main/js/apps/about/sonarqube-dot-com-styles.css b/server/sonar-web/src/main/js/apps/about/sonarqube-dot-com-styles.css index dc5a19e0559..88db615075e 100644 --- a/server/sonar-web/src/main/js/apps/about/sonarqube-dot-com-styles.css +++ b/server/sonar-web/src/main/js/apps/about/sonarqube-dot-com-styles.css @@ -16,10 +16,12 @@ color: #4b9fd5; } -.sqcom-about-page-entry .about-page-container { +.sqcom-about-page-entry .page-limited { display: flex; justify-content: space-between; align-items: center; + padding-top: 0; + padding-bottom: 0; } .sqcom-about-page-intro { @@ -89,10 +91,12 @@ background-color: #4193c8; } -.sqcom-about-rules .about-page-container { +.sqcom-about-rules .page-limited { display: flex; justify-content: space-between; align-items: center; + padding-top: 0; + padding-bottom: 0; } .sqcom-about-rules .button { diff --git a/server/sonar-web/src/main/js/apps/about/styles.css b/server/sonar-web/src/main/js/apps/about/styles.css index 46a0e9added..a81e5434c6f 100644 --- a/server/sonar-web/src/main/js/apps/about/styles.css +++ b/server/sonar-web/src/main/js/apps/about/styles.css @@ -22,16 +22,6 @@ padding-bottom: 25px; } -.about-page-container { - position: relative; - width: 1080px; - margin-left: auto; - margin-right: auto; - padding-left: 20px; - padding-right: 20px; - box-sizing: border-box; -} - .about-page-entry { display: flex; justify-content: space-between; diff --git a/server/sonar-web/src/main/js/apps/account/account.css b/server/sonar-web/src/main/js/apps/account/account.css index 10410996517..6050ad05929 100644 --- a/server/sonar-web/src/main/js/apps/account/account.css +++ b/server/sonar-web/src/main/js/apps/account/account.css @@ -16,16 +16,6 @@ padding-top: 11px; } -.account-nav .nav-tabs { - width: 100%; - border-bottom: none; -} - -.account-nav .navbar-nav > li > a { - padding-top: 8px; - padding-bottom: 8px; -} - .account-user { float: left; } diff --git a/server/sonar-web/src/main/js/apps/account/components/Nav.js b/server/sonar-web/src/main/js/apps/account/components/Nav.js index 65c06efeb12..d8d39380cd5 100644 --- a/server/sonar-web/src/main/js/apps/account/components/Nav.js +++ b/server/sonar-web/src/main/js/apps/account/components/Nav.js @@ -20,6 +20,7 @@ // @flow import React from 'react'; import { Link, IndexLink } from 'react-router'; +import NavBarTabs from '../../../components/nav/NavBarTabs'; import { translate } from '../../../helpers/l10n'; type Props = { @@ -28,8 +29,8 @@ type Props = { export default function Nav({ customOrganizations }: Props) { return ( - <nav className="account-nav clearfix"> - <ul className="nav navbar-nav nav-tabs"> + <nav className="account-nav"> + <NavBarTabs> <li> <IndexLink to="/account/" activeClassName="active"> {translate('my_account.profile')} @@ -57,7 +58,7 @@ export default function Nav({ customOrganizations }: Props) { {translate('my_account.organizations')} </Link> </li>} - </ul> + </NavBarTabs> </nav> ); } diff --git a/server/sonar-web/src/main/js/apps/component-measures/styles.css b/server/sonar-web/src/main/js/apps/component-measures/styles.css index 1c6f8c433ba..b72fcf31384 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/styles.css +++ b/server/sonar-web/src/main/js/apps/component-measures/styles.css @@ -377,3 +377,28 @@ border: 1px solid #e6e6e6; background-color: #fff; } + +.nav-pills > ul { + display: flex; + flex-wrap: wrap; +} + +.nav-pills > ul > li > a { + display: inline-block; + vertical-align: middle; + padding: 3px 10px; + border: 1px solid transparent; + border-radius: 24px; + color: #236a97; + transition: none; +} + +.nav-pills > ul > li > a:hover { + border-color: #236a97; +} + +.nav-pills > ul > li.active > a, +.nav-pills > ul > li > a.active { + background-color: #236a97; + color: #fff; +} diff --git a/server/sonar-web/src/main/js/apps/issues/styles.css b/server/sonar-web/src/main/js/apps/issues/styles.css index 3d1a53be793..bb0f41ec65b 100644 --- a/server/sonar-web/src/main/js/apps/issues/styles.css +++ b/server/sonar-web/src/main/js/apps/issues/styles.css @@ -115,22 +115,6 @@ text-align: right; } -.issues .search-navigator-facet-header, -.issues .search-navigator-facet-list { - padding-left: 0; - padding-right: 0; -} - -.issues .search-navigator-facet-header { - padding-top: 8px; - padding-bottom: 8px; -} - -.issues .search-navigator-facet-box:not(.hidden):not(.leak-facet-box) - + .search-navigator-facet-box:not(.leak-facet-box) { - border-top: none; -} - .issues .search-navigator-facet-footer { padding: 0 0 10px 0; } diff --git a/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.js b/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.js index 370b2bc4dfb..e872e6e2ac1 100644 --- a/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.js +++ b/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.js @@ -20,8 +20,11 @@ // @flow import React from 'react'; import { Link } from 'react-router'; +import classNames from 'classnames'; import { translate } from '../../../helpers/l10n'; -import OrganizationIcon from '../../../components/ui/OrganizationIcon'; +import ContextNavBar from '../../../components/nav/ContextNavBar'; +import NavBarTabs from '../../../components/nav/NavBarTabs'; +import OrganizationIcon from '../../../components/icons-components/OrganizationIcon'; import type { Organization } from '../../../store/organizations/duck'; const ADMIN_PATHS = [ @@ -43,8 +46,11 @@ export default class OrganizationNavigation extends React.PureComponent { const { organization } = this.props; return ( - <li className={adminActive ? 'active' : ''}> - <a className="dropdown-toggle navbar-admin-link" data-toggle="dropdown" href="#"> + <li className="dropdown"> + <a + className={classNames('dropdown-toggle', 'is-admin', { active: adminActive })} + data-toggle="dropdown" + href="#"> {translate('layout.settings')} <i className="icon-dropdown" /> </a> <ul className="dropdown-menu"> @@ -145,75 +151,72 @@ export default class OrganizationNavigation extends React.PureComponent { const moreActive = !adminActive && location.pathname.includes('/extension/'); return ( - <nav className="navbar navbar-context page-container" id="context-navigation"> - <div className="navbar-context-inner"> - <div className="container"> - <h2 className="navbar-context-title"> - <span className="navbar-context-title-qualifier little-spacer-right"> - <OrganizationIcon /> - </span> - <Link to={`/organizations/${organization.key}`} className="link-base-color"> - <strong> - {organization.name} - </strong> - </Link> - </h2> - - {organization.description != null && - <div className="navbar-context-description"> - <p className="text-limited text-top" title={organization.description}> - {organization.description} - </p> - </div>} - - <div className="navbar-context-meta"> - {!!organization.avatar && - <img src={organization.avatar} height={30} alt={organization.name} />} - {organization.url != null && - <div> - <p className="text-limited text-top"> - <a - className="link-underline" - href={organization.url} - title={organization.url} - rel="nofollow"> - {organization.url} - </a> - </p> - </div>} - </div> + <ContextNavBar id="context-navigation" height={65}> + <div className="navbar-context-header"> + <h1 className="display-inline-block"> + <OrganizationIcon className="little-spacer-right" /> + <Link + to={`/organizations/${organization.key}`} + className="link-base-color link-no-underline"> + <strong> + {organization.name} + </strong> + </Link> + </h1> + {organization.description != null && + <div className="navbar-context-description"> + <p className="text-limited text-top" title={organization.description}> + {organization.description} + </p> + </div>} + </div> - <ul className="nav navbar-nav nav-tabs"> - <li> - <Link - to={`/organizations/${organization.key}/projects`} - className={isHomeActive ? 'active' : ''}> - {translate('projects.page')} - </Link> - </li> - <li> - <Link to={`/organizations/${organization.key}/members`} activeClassName="active"> - {translate('organization.members.page')} - </Link> - </li> - <li> - <Link - to={`/organizations/${organization.key}/quality_profiles`} - activeClassName="active"> - {translate('quality_profiles.page')} - </Link> - </li> - <li> - <Link to={`/organizations/${organization.key}/rules`} activeClassName="active"> - {translate('coding_rules.page')} - </Link> - </li> - {this.renderExtensions(moreActive)} - {organization.canAdmin && this.renderAdministration(adminActive)} - </ul> - </div> + <div className="navbar-context-meta"> + {!!organization.avatar && + <img src={organization.avatar} height={30} alt={organization.name} />} + {organization.url != null && + <div> + <p className="text-limited text-top"> + <a + className="link-underline" + href={organization.url} + title={organization.url} + rel="nofollow"> + {organization.url} + </a> + </p> + </div>} </div> - </nav> + + <NavBarTabs> + <li> + <Link + to={`/organizations/${organization.key}/projects`} + className={isHomeActive ? 'active' : ''}> + {translate('projects.page')} + </Link> + </li> + <li> + <Link to={`/organizations/${organization.key}/members`} activeClassName="active"> + {translate('organization.members.page')} + </Link> + </li> + <li> + <Link + to={`/organizations/${organization.key}/quality_profiles`} + activeClassName="active"> + {translate('quality_profiles.page')} + </Link> + </li> + <li> + <Link to={`/organizations/${organization.key}/rules`} activeClassName="active"> + {translate('coding_rules.page')} + </Link> + </li> + {this.renderExtensions(moreActive)} + {organization.canAdmin && this.renderAdministration(adminActive)} + </NavBarTabs> + </ContextNavBar> ); } } diff --git a/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.js.snap b/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.js.snap index 06ffb28889b..05104c2e4d8 100644 --- a/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.js.snap +++ b/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.js.snap @@ -1,59 +1,100 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`admin 1`] = ` -<nav - className="navbar navbar-context page-container" +<ContextNavBar + height={65} id="context-navigation" > <div - className="navbar-context-inner" + className="navbar-context-header" > - <div - className="container" + <h1 + className="display-inline-block" > - <h2 - className="navbar-context-title" - > - <span - className="navbar-context-title-qualifier little-spacer-right" - > - <OrganizationIcon /> - </span> - <Link - className="link-base-color" - onlyActiveOnIndex={false} - style={Object {}} - to="/organizations/foo" - > - <strong> - Foo - </strong> - </Link> - </h2> - <div - className="navbar-context-meta" + <OrganizationIcon + className="little-spacer-right" /> + <Link + className="link-base-color link-no-underline" + onlyActiveOnIndex={false} + style={Object {}} + to="/organizations/foo" + > + <strong> + Foo + </strong> + </Link> + </h1> + </div> + <div + className="navbar-context-meta" + /> + <NavBarTabs> + <li> + <Link + className="" + onlyActiveOnIndex={false} + style={Object {}} + to="/organizations/foo/projects" + > + projects.page + </Link> + </li> + <li> + <Link + activeClassName="active" + onlyActiveOnIndex={false} + style={Object {}} + to="/organizations/foo/members" + > + organization.members.page + </Link> + </li> + <li> + <Link + activeClassName="active" + onlyActiveOnIndex={false} + style={Object {}} + to="/organizations/foo/quality_profiles" + > + quality_profiles.page + </Link> + </li> + <li> + <Link + activeClassName="active" + onlyActiveOnIndex={false} + style={Object {}} + to="/organizations/foo/rules" + > + coding_rules.page + </Link> + </li> + <li + className="dropdown" + > + <a + className="dropdown-toggle is-admin" + data-toggle="dropdown" + href="#" + > + layout.settings + + <i + className="icon-dropdown" + /> + </a> <ul - className="nav navbar-nav nav-tabs" + className="dropdown-menu" > <li> <Link - className="" - onlyActiveOnIndex={false} - style={Object {}} - to="/organizations/foo/projects" - > - projects.page - </Link> - </li> - <li> - <Link activeClassName="active" onlyActiveOnIndex={false} style={Object {}} - to="/organizations/foo/members" + to="/organizations/foo/groups" > - organization.members.page + user_groups.page </Link> </li> <li> @@ -61,9 +102,9 @@ exports[`admin 1`] = ` activeClassName="active" onlyActiveOnIndex={false} style={Object {}} - to="/organizations/foo/quality_profiles" + to="/organizations/foo/permissions" > - quality_profiles.page + permissions.page </Link> </li> <li> @@ -71,140 +112,9 @@ exports[`admin 1`] = ` activeClassName="active" onlyActiveOnIndex={false} style={Object {}} - to="/organizations/foo/rules" + to="/organizations/foo/permission_templates" > - coding_rules.page - </Link> - </li> - <li - className="" - > - <a - className="dropdown-toggle navbar-admin-link" - data-toggle="dropdown" - href="#" - > - layout.settings - - <i - className="icon-dropdown" - /> - </a> - <ul - className="dropdown-menu" - > - <li> - <Link - activeClassName="active" - onlyActiveOnIndex={false} - style={Object {}} - to="/organizations/foo/groups" - > - user_groups.page - </Link> - </li> - <li> - <Link - activeClassName="active" - onlyActiveOnIndex={false} - style={Object {}} - to="/organizations/foo/permissions" - > - permissions.page - </Link> - </li> - <li> - <Link - activeClassName="active" - onlyActiveOnIndex={false} - style={Object {}} - to="/organizations/foo/permission_templates" - > - permission_templates - </Link> - </li> - <li> - <Link - activeClassName="active" - onlyActiveOnIndex={false} - style={Object {}} - to="/organizations/foo/projects_management" - > - projects_management - </Link> - </li> - <li> - <Link - activeClassName="active" - onlyActiveOnIndex={false} - style={Object {}} - to="/organizations/foo/edit" - > - edit - </Link> - </li> - <li> - <Link - activeClassName="active" - onlyActiveOnIndex={false} - style={Object {}} - to="/organizations/foo/delete" - > - delete - </Link> - </li> - </ul> - </li> - </ul> - </div> - </div> -</nav> -`; - -exports[`regular user 1`] = ` -<nav - className="navbar navbar-context page-container" - id="context-navigation" -> - <div - className="navbar-context-inner" - > - <div - className="container" - > - <h2 - className="navbar-context-title" - > - <span - className="navbar-context-title-qualifier little-spacer-right" - > - <OrganizationIcon /> - </span> - <Link - className="link-base-color" - onlyActiveOnIndex={false} - style={Object {}} - to="/organizations/foo" - > - <strong> - Foo - </strong> - </Link> - </h2> - <div - className="navbar-context-meta" - /> - <ul - className="nav navbar-nav nav-tabs" - > - <li> - <Link - className="" - onlyActiveOnIndex={false} - style={Object {}} - to="/organizations/foo/projects" - > - projects.page + permission_templates </Link> </li> <li> @@ -212,9 +122,9 @@ exports[`regular user 1`] = ` activeClassName="active" onlyActiveOnIndex={false} style={Object {}} - to="/organizations/foo/members" + to="/organizations/foo/projects_management" > - organization.members.page + projects_management </Link> </li> <li> @@ -222,9 +132,9 @@ exports[`regular user 1`] = ` activeClassName="active" onlyActiveOnIndex={false} style={Object {}} - to="/organizations/foo/quality_profiles" + to="/organizations/foo/edit" > - quality_profiles.page + edit </Link> </li> <li> @@ -232,61 +142,186 @@ exports[`regular user 1`] = ` activeClassName="active" onlyActiveOnIndex={false} style={Object {}} - to="/organizations/foo/rules" + to="/organizations/foo/delete" > - coding_rules.page + delete </Link> </li> </ul> - </div> + </li> + </NavBarTabs> +</ContextNavBar> +`; + +exports[`regular user 1`] = ` +<ContextNavBar + height={65} + id="context-navigation" +> + <div + className="navbar-context-header" + > + <h1 + className="display-inline-block" + > + <OrganizationIcon + className="little-spacer-right" + /> + <Link + className="link-base-color link-no-underline" + onlyActiveOnIndex={false} + style={Object {}} + to="/organizations/foo" + > + <strong> + Foo + </strong> + </Link> + </h1> </div> -</nav> + <div + className="navbar-context-meta" + /> + <NavBarTabs> + <li> + <Link + className="" + onlyActiveOnIndex={false} + style={Object {}} + to="/organizations/foo/projects" + > + projects.page + </Link> + </li> + <li> + <Link + activeClassName="active" + onlyActiveOnIndex={false} + style={Object {}} + to="/organizations/foo/members" + > + organization.members.page + </Link> + </li> + <li> + <Link + activeClassName="active" + onlyActiveOnIndex={false} + style={Object {}} + to="/organizations/foo/quality_profiles" + > + quality_profiles.page + </Link> + </li> + <li> + <Link + activeClassName="active" + onlyActiveOnIndex={false} + style={Object {}} + to="/organizations/foo/rules" + > + coding_rules.page + </Link> + </li> + </NavBarTabs> +</ContextNavBar> `; exports[`undeletable org 1`] = ` -<nav - className="navbar navbar-context page-container" +<ContextNavBar + height={65} id="context-navigation" > <div - className="navbar-context-inner" + className="navbar-context-header" > - <div - className="container" + <h1 + className="display-inline-block" > - <h2 - className="navbar-context-title" - > - <span - className="navbar-context-title-qualifier little-spacer-right" - > - <OrganizationIcon /> - </span> - <Link - className="link-base-color" - onlyActiveOnIndex={false} - style={Object {}} - to="/organizations/foo" - > - <strong> - Foo - </strong> - </Link> - </h2> - <div - className="navbar-context-meta" + <OrganizationIcon + className="little-spacer-right" /> + <Link + className="link-base-color link-no-underline" + onlyActiveOnIndex={false} + style={Object {}} + to="/organizations/foo" + > + <strong> + Foo + </strong> + </Link> + </h1> + </div> + <div + className="navbar-context-meta" + /> + <NavBarTabs> + <li> + <Link + className="" + onlyActiveOnIndex={false} + style={Object {}} + to="/organizations/foo/projects" + > + projects.page + </Link> + </li> + <li> + <Link + activeClassName="active" + onlyActiveOnIndex={false} + style={Object {}} + to="/organizations/foo/members" + > + organization.members.page + </Link> + </li> + <li> + <Link + activeClassName="active" + onlyActiveOnIndex={false} + style={Object {}} + to="/organizations/foo/quality_profiles" + > + quality_profiles.page + </Link> + </li> + <li> + <Link + activeClassName="active" + onlyActiveOnIndex={false} + style={Object {}} + to="/organizations/foo/rules" + > + coding_rules.page + </Link> + </li> + <li + className="dropdown" + > + <a + className="dropdown-toggle is-admin" + data-toggle="dropdown" + href="#" + > + layout.settings + + <i + className="icon-dropdown" + /> + </a> <ul - className="nav navbar-nav nav-tabs" + className="dropdown-menu" > <li> <Link - className="" + activeClassName="active" onlyActiveOnIndex={false} style={Object {}} - to="/organizations/foo/projects" + to="/organizations/foo/groups" > - projects.page + user_groups.page </Link> </li> <li> @@ -294,9 +329,9 @@ exports[`undeletable org 1`] = ` activeClassName="active" onlyActiveOnIndex={false} style={Object {}} - to="/organizations/foo/members" + to="/organizations/foo/permissions" > - organization.members.page + permissions.page </Link> </li> <li> @@ -304,9 +339,9 @@ exports[`undeletable org 1`] = ` activeClassName="active" onlyActiveOnIndex={false} style={Object {}} - to="/organizations/foo/quality_profiles" + to="/organizations/foo/permission_templates" > - quality_profiles.page + permission_templates </Link> </li> <li> @@ -314,82 +349,23 @@ exports[`undeletable org 1`] = ` activeClassName="active" onlyActiveOnIndex={false} style={Object {}} - to="/organizations/foo/rules" + to="/organizations/foo/projects_management" > - coding_rules.page + projects_management </Link> </li> - <li - className="" - > - <a - className="dropdown-toggle navbar-admin-link" - data-toggle="dropdown" - href="#" - > - layout.settings - - <i - className="icon-dropdown" - /> - </a> - <ul - className="dropdown-menu" + <li> + <Link + activeClassName="active" + onlyActiveOnIndex={false} + style={Object {}} + to="/organizations/foo/edit" > - <li> - <Link - activeClassName="active" - onlyActiveOnIndex={false} - style={Object {}} - to="/organizations/foo/groups" - > - user_groups.page - </Link> - </li> - <li> - <Link - activeClassName="active" - onlyActiveOnIndex={false} - style={Object {}} - to="/organizations/foo/permissions" - > - permissions.page - </Link> - </li> - <li> - <Link - activeClassName="active" - onlyActiveOnIndex={false} - style={Object {}} - to="/organizations/foo/permission_templates" - > - permission_templates - </Link> - </li> - <li> - <Link - activeClassName="active" - onlyActiveOnIndex={false} - style={Object {}} - to="/organizations/foo/projects_management" - > - projects_management - </Link> - </li> - <li> - <Link - activeClassName="active" - onlyActiveOnIndex={false} - style={Object {}} - to="/organizations/foo/edit" - > - edit - </Link> - </li> - </ul> + edit + </Link> </li> </ul> - </div> - </div> -</nav> + </li> + </NavBarTabs> +</ContextNavBar> `; diff --git a/server/sonar-web/src/main/js/components/ui/OrganizationIcon.js b/server/sonar-web/src/main/js/components/icons-components/OrganizationIcon.js index 4b0de907c2c..225b3f92b5c 100644 --- a/server/sonar-web/src/main/js/components/ui/OrganizationIcon.js +++ b/server/sonar-web/src/main/js/components/icons-components/OrganizationIcon.js @@ -20,10 +20,17 @@ // @flow import React from 'react'; -export default function OrganizationIcon() { +type Props = { className?: string, size?: number }; + +export default function OrganizationIcon({ className, size = 16 }: Props) { /* eslint-disable max-len */ return ( - <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"> + <svg + xmlns="http://www.w3.org/2000/svg" + className={className} + height={size} + width={size} + viewBox="0 0 16 16"> <path style={{ fill: '#4b9fd5' }} d="M13.5 6c-.4 0-.7.1-1.1.2L11 4.8v-.3C11 3.1 9.9 2 8.5 2S6 3.1 6 4.5v.2L4.5 6.2c-.3-.1-.7-.2-1-.2C2.1 6 1 7.1 1 8.5S2.1 11 3.5 11 6 9.9 6 8.5c0-.7-.3-1.3-.7-1.7l1-1c.4.6 1 1 1.7 1.1V9c-1.1.2-2 1.2-2 2.4C6 12.9 7.1 14 8.5 14s2.5-1.1 2.5-2.5c0-1.2-.9-2.2-2-2.4V6.9c.7-.1 1.2-.5 1.6-1.1l1 1c-.4.4-.6 1-.6 1.6 0 1.4 1.1 2.5 2.5 2.5s2.5-1 2.5-2.4S14.9 6 13.5 6zm-10 4C2.7 10 2 9.3 2 8.5S2.7 7 3.5 7 5 7.7 5 8.5 4.3 10 3.5 10zm6.5 1.5c0 .8-.7 1.5-1.5 1.5S7 12.3 7 11.5 7.7 10 8.5 10s1.5.7 1.5 1.5zM8.5 6C7.7 6 7 5.3 7 4.5S7.7 3 8.5 3s1.5.7 1.5 1.5S9.3 6 8.5 6zm5 4c-.8 0-1.5-.7-1.5-1.5S12.7 7 13.5 7s1.5.7 1.5 1.5-.7 1.5-1.5 1.5z" diff --git a/server/sonar-web/src/main/js/components/nav/ContextNavBar.css b/server/sonar-web/src/main/js/components/nav/ContextNavBar.css new file mode 100644 index 00000000000..ebebf69464a --- /dev/null +++ b/server/sonar-web/src/main/js/components/nav/ContextNavBar.css @@ -0,0 +1,36 @@ +.navbar-context, +.navbar-context .navbar-inner { + background-color: #f3f3f3; + z-index: 420; +} + +.navbar-context .navbar-inner { + padding-top: 5px; + border-bottom: 1px solid #e6e6e6; +} + +.navbar-context-header { + line-height: 30px; + font-size: 15px; +} + +.navbar-context-meta { + position: absolute; + top: 0; + right: 0; + line-height: 30px; + padding: 0 10px; + color: #777; + font-size: 12px; + text-align: right; +} + +.navbar-context-description { + display: inline-block; + line-height: 24px; + margin-left: 16px; + padding-top: 4px; + padding-left: 4px; + color: #777; + font-size: 12px; +} diff --git a/server/sonar-web/src/main/js/components/nav/ContextNavBar.js b/server/sonar-web/src/main/js/components/nav/ContextNavBar.js new file mode 100644 index 00000000000..003a0614810 --- /dev/null +++ b/server/sonar-web/src/main/js/components/nav/ContextNavBar.js @@ -0,0 +1,33 @@ +/* + * 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 classNames from 'classnames'; +import NavBar from './NavBar'; +import './ContextNavBar.css'; + +type Props = { + className?: string, + height: number +}; + +export default function ContextNavBar({ className, ...other }: Props) { + return <NavBar className={classNames('navbar-context', className)} {...other} />; +} diff --git a/server/sonar-web/src/main/js/components/nav/NavBar.css b/server/sonar-web/src/main/js/components/nav/NavBar.css new file mode 100644 index 00000000000..c74c0929f39 --- /dev/null +++ b/server/sonar-web/src/main/js/components/nav/NavBar.css @@ -0,0 +1,24 @@ +.navbar, +[class^="navbar-"], +[class*=" navbar-"] { + box-sizing: border-box; +} + +.navbar { +} + +.navbar-inner { + position: fixed; + left: 0; + right: 0; +} + +.navbar-limited { + position: relative; + min-width: 1100px; + max-width: 1320px; + margin-left: auto; + margin-right: auto; + padding-left: 20px; + padding-right: 20px; +} diff --git a/server/sonar-web/src/main/js/components/nav/NavBar.js b/server/sonar-web/src/main/js/components/nav/NavBar.js new file mode 100644 index 00000000000..beb4466edbc --- /dev/null +++ b/server/sonar-web/src/main/js/components/nav/NavBar.js @@ -0,0 +1,41 @@ +/* + * 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 classNames from 'classnames'; +import './NavBar.css'; + +type Props = { + children?: React.Element<*>, + className?: string, + height: number +}; + +export default function NavBar({ children, className, height, ...other }: Props) { + return ( + <nav {...other} className={classNames('navbar', className)} style={{ height }}> + <div className="navbar-inner" style={{ height }}> + <div className="navbar-limited clearfix"> + {children} + </div> + </div> + </nav> + ); +} diff --git a/server/sonar-web/src/main/js/components/nav/NavBarTabs.css b/server/sonar-web/src/main/js/components/nav/NavBarTabs.css new file mode 100644 index 00000000000..b716dad62a3 --- /dev/null +++ b/server/sonar-web/src/main/js/components/nav/NavBarTabs.css @@ -0,0 +1,28 @@ +.navbar-tabs { + display: flex; + align-items: center; +} + +.navbar-tabs > li + li { + margin-left: 20px; +} + +.navbar-tabs > li > a { + display: block; + padding: 7px 0 4px; + border-bottom: 3px solid transparent; + color: #444; + transition: none; +} + +.navbar-tabs > li > a.active, +.navbar-tabs > li > a:hover, +.navbar-tabs > li > a:focus { + border-bottom-color: #4b9fd5; +} + +.navbar-tabs > li > a.is-admin.active, +.navbar-tabs > li > a.is-admin:hover, +.navbar-tabs > li > a.is-admin:focus { + border-bottom-color: #ed7d20; +} diff --git a/server/sonar-web/src/main/js/components/nav/NavBarTabs.js b/server/sonar-web/src/main/js/components/nav/NavBarTabs.js new file mode 100644 index 00000000000..f1d8e296278 --- /dev/null +++ b/server/sonar-web/src/main/js/components/nav/NavBarTabs.js @@ -0,0 +1,36 @@ +/* + * 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 classNames from 'classnames'; +import './NavBarTabs.css'; + +type Props = { + children?: React.Element<*>, + className?: string +}; + +export default function NavBarTabs({ children, className, ...other }: Props) { + return ( + <ul {...other} className={classNames('navbar-tabs', className)}> + {children} + </ul> + ); +} diff --git a/server/sonar-web/src/main/js/libs/third-party/bootstrap/dropdown.js b/server/sonar-web/src/main/js/libs/third-party/bootstrap/dropdown.js index 9dd4a8b75f4..05606b9ea88 100644 --- a/server/sonar-web/src/main/js/libs/third-party/bootstrap/dropdown.js +++ b/server/sonar-web/src/main/js/libs/third-party/bootstrap/dropdown.js @@ -42,7 +42,7 @@ clearMenus() if (!isActive) { - if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { + if ('ontouchstart' in document.documentElement) { // if mobile we use a backdrop because click events don't delegate $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus) } diff --git a/server/sonar-web/src/main/less/components/navbar.less b/server/sonar-web/src/main/less/components/navbar.less deleted file mode 100644 index 3573ab3bc4f..00000000000 --- a/server/sonar-web/src/main/less/components/navbar.less +++ /dev/null @@ -1,363 +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. - */ -@import (reference) "../mixins"; -@import (reference) "../variables"; -@import (reference) "ui"; - -@navbarGlobalBackground: #262626; -@navbarContextBackground: @barBackgroundColor; - -@navbarLineHeight: 20px; -@navbarTopPadding: (@navbarGlobalHeight - @navbarLineHeight) / 2; - -.navbar, -[class^="navbar-"], -[class*=" navbar-"] { - box-sizing: border-box; -} - -.navbar { - position: fixed; - left: 0; - right: 0; - .clearfix; - height: @navbarGlobalHeight; -} - -.navbar-fade { - .navbar-nav { - opacity: 0; - transition: opacity 0.2s ease; - } - - .navbar-favorite { - margin-right: -23px; - transition: margin 0.2s ease; - } - - &.in { - .navbar-nav { - opacity: 1; - } - - .navbar-favorite { - margin-right: 0; - } - } -} - -.navbar a { - .link-no-underline; - transition: none; -} - -.navbar-header { - float: left; -} - -.navbar-brand { - display: block; - - img { - vertical-align: top; - } -} - -.navbar-brand-custom { - padding: 0 10px; -} - -.navbar-nav { - float: left; -} - -.navbar-nav > li { - float: left; -} - -.navbar-nav > li > a { - padding: @navbarTopPadding 10px; - line-height: @navbarLineHeight; - - &.navbar-avatar { - margin-right: 7px; - padding: 3px; - } - - &.navbar-help { - line-height: 16px; - padding: 7px; - } -} - -.navbar-nav > li.navbar-more > a { - padding-right: 17px; -} - -.navbar-nav > li.navbar-more + li { - margin-left: -17px; -} - -.navbar-nav > li.navbar-more + li > a { - padding-left: 5px; - padding-right: 5px; -} - -.navbar-icon:before { - color: #fff !important; - font-size: @iconFontSize; -} - -.navbar-favorite { - position: relative; - top: -2px; -} - -.navbar-right { - float: right; -} - -.navbar-search { - position: relative; - padding-right: 3px; -} - -.navbar-search-input { - vertical-align: middle; - width: 310px; - margin-top: 3px; - margin-bottom: 3px; - padding-left: 26px !important; -} - -.navbar-search-input-hint { - position: absolute; - top: 4px; - right: 30px; - line-height: @formControlHeight; - font-size: 12px; - color: @secondFontColor; - - &.is-shifted { - z-index: @dropdown-menu-z-index + 1; - top: 32px; - } -} - -.navbar-search-icon { - position: relative; - vertical-align: middle; - width: 16px; - margin-right: -20px; - color: @secondFontColor; - - &:before { - font-size: @iconSmallFontSize; - } -} - -.navbar-search-item-link { - display: flex !important; -} - -.navbar-search-item-match { - flex-grow: 5; - overflow: hidden; - text-overflow: ellipsis; -} - -.navbar-search-item-right { - flex-grow: 1; - padding-left: 10px; - text-align: right; -} - -.navbar-search-item-icons { - position: relative; - flex-shrink: 0; - width: 16px; - height: 16px; - - > * { - position: absolute; - z-index: 5; - top: 0; - left: 0; - } - - > .icon-star, - > .icon-clock { - z-index: 6; - top: -4px; - left: -5px; - } -} - -.navbar-search-shortcut-hint { - line-height: 16px; - margin-top: 5px; - padding: 5px 10px; - border-top: 1px solid #e6e6e6; - background-color: #f3f3f3; - color: #777; - font-size: 11px; -} - -.navbar-search-no-results { - margin-top: 4px; - padding: 5px 10px; -} - -.navbar-global { - top: 0; - z-index: @navbar-global-z-index; - background-color: @navbarGlobalBackground; - - .navbar-nav > li > a { - color: #ccc; - font-size: 12px; - letter-spacing: 0.05em; - - &:hover { - color: #fff; - } - } - - .navbar-nav > .active > a, - .navbar-nav > li > a.active, - .navbar-nav > .dropdown.open > a { - color: #fff; - } - - .navbar-brand:hover, - .navbar-brand:focus { - background-color: darken(@navbarGlobalBackground, 20%); - } - - .navbar-nav > li > a:hover, - .navbar-nav > .active > a, - .navbar-nav > li > a.active, - .navbar-nav > .dropdown.open > a { - background-color: @blue; - } - - .navbar-search-dropdown { - background-color: @blue !important; - } - - .navbar-admin-link:hover, - .active > .navbar-admin-link, - .active.navbar-admin-link { - background-color: @orange !important; - } -} - -.navbar-global-login-github { - margin-top: 3px; - margin-right: 4px; -} - -.navbar-context { - position: static; - top: @navbarGlobalHeight; - z-index: @navbar-context-z-index; - height: @navbarContextHeight; - padding: 0; - background-color: @navbarContextBackground; - - .nav-tabs { - width: 100%; - } - - .navbar-nav > li > a { - padding-top: 3px; - padding-bottom: 3px; - } - - .navbar-admin-link:hover, - .navbar-admin-link:focus, - .active > .navbar-admin-link { - border-color: @orange !important; - } -} - -.navbar-context-favorite { - float: left; - padding: 7px 0 0 10px; -} - -.navbar-context-meta { - position: absolute; - top: 0; - right: 0; - line-height: @navbarGlobalHeight; - padding: 5px 10px 0; - color: @secondFontColor; - font-size: @smallFontSize; - text-align: right; -} - -.navbar-context-description { - float: left; - line-height: @formControlHeight; - padding-top: 4px; - padding-left: 4px; - color: @secondFontColor; - font-size: @smallFontSize; -} - -.navbar-context-inner { - position: fixed; - z-index: 420; - left: 0; - right: 0; - height: 65px; - padding-top: 5px; - box-sizing: border-box; - background-color: #f3f3f3; -} - -.navbar-context-title { - float: left; - min-height: 30px; - padding: 2px 10px 0; -} - -.navbar-context-title-qualifier { - display: inline-block; - line-height: 16px; - padding-top: 5px; - box-sizing: border-box; -} - -.navbar-side { - padding: 10px; - border-bottom: 1px solid @barBorderColor; - background-color: #e5f1f9; -} - -.global-navbar-search-dropdown { - max-height: 80vh; - width: 440px; - padding: 0; - overflow-y: auto; - overflow-x: hidden; - box-shadow: @defaultShadow; -} diff --git a/server/sonar-web/src/main/less/components/page.less b/server/sonar-web/src/main/less/components/page.less index 57f76aa02ac..22fce0f068f 100644 --- a/server/sonar-web/src/main/less/components/page.less +++ b/server/sonar-web/src/main/less/components/page.less @@ -58,14 +58,6 @@ margin: 100px 0; } -.page-wrapper-global { - padding-top: @navbarGlobalHeight; -} - -.page-wrapper-context { - padding-top: @navbarGlobalHeight + @navbarContextHeight; -} - .page-simple { width: 400px; padding: 40px; @@ -220,7 +212,7 @@ } .layout-page-side-outer { - width: ~"calc(50vw - 360px)"; + width: ~"calc(50vw - 370px)"; flex-grow: 0; flex-shrink: 0; background-color: #f3f3f3; @@ -232,7 +224,7 @@ top: 30px; bottom: 0; left: 0; - width: ~"calc(50vw - 360px)"; + width: ~"calc(50vw - 370px)"; border-right: 1px solid #e6e6e6; overflow-y: auto; overflow-x: hidden; @@ -241,7 +233,7 @@ .layout-page-side-inner { width: 300px; - margin-left: ~"calc(50vw - 660px)"; + margin-left: ~"calc(50vw - 670px)"; background-color: #f3f3f3; } @@ -274,7 +266,7 @@ } .layout-page-main-header-inner { - left: ~"calc(50vw - 360px + 1px)"; + left: ~"calc(50vw - 370px + 1px)"; right: 0; padding-left: 20px; padding-right: 20px; diff --git a/server/sonar-web/src/main/less/components/search-navigator.less b/server/sonar-web/src/main/less/components/search-navigator.less index eebff4f1c0b..97961b89988 100644 --- a/server/sonar-web/src/main/less/components/search-navigator.less +++ b/server/sonar-web/src/main/less/components/search-navigator.less @@ -78,11 +78,6 @@ } } -.search-navigator-facet-box:not(.hidden):not(.leak-facet-box) - + .search-navigator-facet-box:not(.leak-facet-box) { - border-top: 1px solid @barBorderColor; -} - .leak-facet-box:not(.hidden) + .leak-facet-box { border-top: none; } @@ -361,7 +356,7 @@ .search-navigator-facet-header { display: block; - padding: 6px 10px; + padding: 8px 0; border-bottom: none; color: @baseFontColor; font-weight: 600; @@ -377,8 +372,7 @@ } .search-navigator-facet-list { - margin: 0 0 0 0; - padding: 0 10px 10px; + padding-bottom: 10px; font-size: 0; } diff --git a/server/sonar-web/src/main/less/components/ui.less b/server/sonar-web/src/main/less/components/ui.less index 16f8d9e45c0..b36989335d5 100644 --- a/server/sonar-web/src/main/less/components/ui.less +++ b/server/sonar-web/src/main/less/components/ui.less @@ -114,109 +114,6 @@ margin-right: 4px; } -.nav { - margin: 0; - padding: 0; - list-style: none; -} - -.nav > li { - position: relative; - display: block; -} - -.nav > li > a { - position: relative; - display: inline-block; - vertical-align: middle; - padding: 10px 15px; -} - -.nav-crumbs { - padding: 2px 0; - - a { - color: @baseFontColorLight; - - &:hover, - &:focus { - color: @blue !important; - } - } - - > li { - font-size: 15px; - font-weight: 400; - } - - > li + li:before { - content: "/"; - float: left; - padding: 4px 0; - color: fade(@baseFontColor, 30%); - } - - [class^="icon-"], - [class*=" icon-"] { - position: relative; - top: 3px; - } -} - -.nav-tabs { - padding-top: 1px; - border-bottom: 1px solid @barBorderColor; - - > li { - float: left; - margin-bottom: -1px; - } - - > li > a { - padding-top: 2px; - padding-bottom: 2px; - margin-right: 2px; - border-bottom: 3px solid transparent; - color: @baseFontColorLight; - - &:hover { - border-color: @blue; - } - } - - > li.active > a, - > li > a.active { - border-color: @blue; - } -} - -.nav-pills { - & > ul { - display: flex; - flex-wrap: wrap; - - & > li > a { - display: inline-block; - vertical-align: middle; - padding: 3px 10px; - border: 1px solid transparent; - border-radius: 24px; - color: @darkBlue; - transition: none; - - &:hover { - border-color: @darkBlue; - } - } - - & > li.active > a, - & > li > a.active { - background-color: @darkBlue; - color: #fff; - } - } -} - .flash { background-color: transparent; transition: all 0.5s ease; diff --git a/server/sonar-web/src/main/less/print.less b/server/sonar-web/src/main/less/print.less index 99338f742a5..9390b7e905d 100644 --- a/server/sonar-web/src/main/less/print.less +++ b/server/sonar-web/src/main/less/print.less @@ -22,7 +22,6 @@ @import (reference) "init/links"; @media print { - .noprint { display: none !important; } @@ -32,14 +31,6 @@ .link-no-underline; } - .navbar-global { - .noprint; - } - - .page-wrapper-global { - padding-top: 0; - } - .page-footer { .noprint; } @@ -54,5 +45,4 @@ .widget tfoot { display: table-row-group; } - } diff --git a/server/sonar-web/src/main/less/sonar.less b/server/sonar-web/src/main/less/sonar.less index 29e5875e709..f13e4cd339a 100644 --- a/server/sonar-web/src/main/less/sonar.less +++ b/server/sonar-web/src/main/less/sonar.less @@ -47,7 +47,6 @@ @import "components/page"; @import "components/navigator"; @import "components/component-name"; -@import "components/navbar"; @import "components/select-list"; @import "components/graphics"; @import "components/list-groups"; diff --git a/server/sonar-web/src/main/less/variables.less b/server/sonar-web/src/main/less/variables.less index ca3a1a87f0d..8364fdf9c78 100644 --- a/server/sonar-web/src/main/less/variables.less +++ b/server/sonar-web/src/main/less/variables.less @@ -28,7 +28,6 @@ @baseFontSize: 13px; @baseLineHeight: unit(16px / @baseFontSize); - /* * Code */ @@ -37,7 +36,6 @@ @monoFontSize: 12px; @monoLineHeight: 18px; - /* * Font Sizes */ @@ -46,20 +44,16 @@ @mediumFontSize: 14px; @bigFontSize: 16px; - - @secondFontColor: #777; @headerFontSize: 16px; - - /* * Colors */ @black: #000000; @white: #ffffff; -@grey: #efefef; +@grey: #efefef; @darkGrey: #cdcdcd; @middleGrey: #b4b4b4; @@ -98,8 +92,6 @@ @formControlHeight: 24px; - - /* * Headers */ @@ -125,8 +117,6 @@ @h5-font-weight: 600; @h6-font-weight: 600; - - /* * Icons */ @@ -151,24 +141,18 @@ @resolutionFalsePositiveColor: @baseFontColor; @resolutionRemovedColor: @baseFontColor; - - /* * Shadows */ @defaultShadow: 0 6px 12px rgba(0, 0, 0, .175); - - /* * Transitions */ @defaultTransitionOptions: .2s ease; - - /* * Page */ @@ -177,7 +161,6 @@ @navbarContextHeight: 65px; @pageFooterHeight: 60px; - /* * Navigator */ @@ -185,8 +168,6 @@ @navigatorPadding: 10px; @navigatorHeaderHeight: 40px; - - /* * z-index * ======= @@ -218,7 +199,6 @@ @dashboard-transparent-z-index: 100; -@navbar-global-z-index: 421; @navbar-context-z-index: 420; @workspace-nav-z-index: 451; |