Browse Source

SONAR-9583 Use fixed width layout for navigation

tags/6.6-RC1
Stas Vilchik 6 years ago
parent
commit
dccf95202c
50 changed files with 1204 additions and 1389 deletions
  1. 1
    1
      server/sonar-web/pom.xml
  2. 1
    1
      server/sonar-web/src/main/js/app/components/GlobalContainer.js
  3. 3
    4
      server/sonar-web/src/main/js/app/components/SimpleContainer.js
  4. 11
    0
      server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.css
  5. 25
    27
      server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.js
  6. 6
    4
      server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBreadcrumbs.js
  7. 7
    6
      server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.js
  8. 9
    9
      server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBreadcrumbs-test.js.snap
  9. 8
    16
      server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.js.snap
  10. 67
    0
      server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.css
  11. 24
    24
      server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.js
  12. 3
    5
      server/sonar-web/src/main/js/app/components/nav/global/GlobalNavBranding.js
  13. 2
    2
      server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.js
  14. 1
    1
      server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUser.js
  15. 3
    3
      server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavMenu-test.js.snap
  16. 2
    0
      server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.js.snap
  17. 110
    114
      server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.js
  18. 177
    191
      server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.js.snap
  19. 97
    0
      server/sonar-web/src/main/js/app/components/search/Search.css
  20. 1
    0
      server/sonar-web/src/main/js/app/components/search/Search.js
  21. 3
    2
      server/sonar-web/src/main/js/app/utils/startReactApp.js
  22. 50
    52
      server/sonar-web/src/main/js/apps/about/components/AboutApp.js
  23. 2
    2
      server/sonar-web/src/main/js/apps/about/components/AboutAppForSonarQubeDotCom.js
  24. 1
    1
      server/sonar-web/src/main/js/apps/about/components/AboutRulesForSonarQubeDotCom.js
  25. 6
    2
      server/sonar-web/src/main/js/apps/about/sonarqube-dot-com-styles.css
  26. 0
    10
      server/sonar-web/src/main/js/apps/about/styles.css
  27. 0
    10
      server/sonar-web/src/main/js/apps/account/account.css
  28. 4
    3
      server/sonar-web/src/main/js/apps/account/components/Nav.js
  29. 25
    0
      server/sonar-web/src/main/js/apps/component-measures/styles.css
  30. 0
    16
      server/sonar-web/src/main/js/apps/issues/styles.css
  31. 73
    70
      server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.js
  32. 264
    288
      server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.js.snap
  33. 9
    2
      server/sonar-web/src/main/js/components/icons-components/OrganizationIcon.js
  34. 36
    0
      server/sonar-web/src/main/js/components/nav/ContextNavBar.css
  35. 33
    0
      server/sonar-web/src/main/js/components/nav/ContextNavBar.js
  36. 24
    0
      server/sonar-web/src/main/js/components/nav/NavBar.css
  37. 41
    0
      server/sonar-web/src/main/js/components/nav/NavBar.js
  38. 28
    0
      server/sonar-web/src/main/js/components/nav/NavBarTabs.css
  39. 36
    0
      server/sonar-web/src/main/js/components/nav/NavBarTabs.js
  40. 1
    1
      server/sonar-web/src/main/js/libs/third-party/bootstrap/dropdown.js
  41. 0
    363
      server/sonar-web/src/main/less/components/navbar.less
  42. 4
    12
      server/sonar-web/src/main/less/components/page.less
  43. 2
    8
      server/sonar-web/src/main/less/components/search-navigator.less
  44. 0
    103
      server/sonar-web/src/main/less/components/ui.less
  45. 0
    10
      server/sonar-web/src/main/less/print.less
  46. 0
    1
      server/sonar-web/src/main/less/sonar.less
  47. 1
    21
      server/sonar-web/src/main/less/variables.less
  48. 1
    1
      tests/src/test/java/org/sonarqube/pageobjects/organization/MembersPage.java
  49. 1
    1
      tests/src/test/java/org/sonarqube/tests/ui/OrganizationUiExtensionsTest.java
  50. 1
    2
      tests/src/test/java/org/sonarqube/tests/ui/UiExtensionsTest.java

+ 1
- 1
server/sonar-web/pom.xml View File

@@ -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>

+ 1
- 1
server/sonar-web/src/main/js/app/components/GlobalContainer.js View File

@@ -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 />

+ 3
- 4
server/sonar-web/src/main/js/app/components/SimpleContainer.js View File

@@ -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">

+ 11
- 0
server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.css View File

@@ -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;
}

+ 25
- 27
server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.js View File

@@ -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>
);
}
}

+ 6
- 4
server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBreadcrumbs.js View File

@@ -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>
);
}
}

+ 7
- 6
server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.js View File

@@ -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>
);
}
}

+ 9
- 9
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBreadcrumbs-test.js.snap View File

@@ -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>
`;

+ 8
- 16
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.js.snap View File

@@ -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>
`;

+ 67
- 0
server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.css View File

@@ -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;
}
}

+ 24
- 24
server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.js View File

@@ -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>
);
}
}

+ 3
- 5
server/sonar-web/src/main/js/app/components/nav/global/GlobalNavBranding.js View File

@@ -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>

+ 2
- 2
server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.js View File

@@ -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()}

+ 1
- 1
server/sonar-web/src/main/js/app/components/nav/global/GlobalNavUser.js View File

@@ -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>

+ 3
- 3
server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavMenu-test.js.snap View File

@@ -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

+ 2
- 0
server/sonar-web/src/main/js/app/components/nav/global/__tests__/__snapshots__/GlobalNavUser-test.js.snap View File

@@ -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]}
>

+ 110
- 114
server/sonar-web/src/main/js/app/components/nav/settings/SettingsNav.js View File

@@ -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>
);
}
}

+ 177
- 191
server/sonar-web/src/main/js/app/components/nav/settings/__tests__/__snapshots__/SettingsNav-test.js.snap View File

@@ -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>
`;

+ 97
- 0
server/sonar-web/src/main/js/app/components/search/Search.css View File

@@ -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);
}

+ 1
- 0
server/sonar-web/src/main/js/app/components/search/Search.js View File

@@ -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 },

+ 3
- 2
server/sonar-web/src/main/js/app/utils/startReactApp.js View File

@@ -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} />

+ 50
- 52
server/sonar-web/src/main/js/apps/about/components/AboutApp.js View File

@@ -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>
);
}

+ 2
- 2
server/sonar-web/src/main/js/apps/about/components/AboutAppForSonarQubeDotCom.js View File

@@ -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

+ 1
- 1
server/sonar-web/src/main/js/apps/about/components/AboutRulesForSonarQubeDotCom.js View File

@@ -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">

+ 6
- 2
server/sonar-web/src/main/js/apps/about/sonarqube-dot-com-styles.css View File

@@ -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 {

+ 0
- 10
server/sonar-web/src/main/js/apps/about/styles.css View File

@@ -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;

+ 0
- 10
server/sonar-web/src/main/js/apps/account/account.css View File

@@ -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;
}

+ 4
- 3
server/sonar-web/src/main/js/apps/account/components/Nav.js View File

@@ -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>
);
}

+ 25
- 0
server/sonar-web/src/main/js/apps/component-measures/styles.css View File

@@ -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;
}

+ 0
- 16
server/sonar-web/src/main/js/apps/issues/styles.css View File

@@ -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;
}

+ 73
- 70
server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.js View File

@@ -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')}&nbsp;<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>
);
}
}

+ 264
- 288
server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.js.snap View File

@@ -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>
`;

server/sonar-web/src/main/js/components/ui/OrganizationIcon.js → server/sonar-web/src/main/js/components/icons-components/OrganizationIcon.js View File

@@ -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"

+ 36
- 0
server/sonar-web/src/main/js/components/nav/ContextNavBar.css View File

@@ -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;
}

+ 33
- 0
server/sonar-web/src/main/js/components/nav/ContextNavBar.js View File

@@ -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} />;
}

+ 24
- 0
server/sonar-web/src/main/js/components/nav/NavBar.css View File

@@ -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;
}

+ 41
- 0
server/sonar-web/src/main/js/components/nav/NavBar.js View File

@@ -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>
);
}

+ 28
- 0
server/sonar-web/src/main/js/components/nav/NavBarTabs.css View File

@@ -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;
}

+ 36
- 0
server/sonar-web/src/main/js/components/nav/NavBarTabs.js View File

@@ -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>
);
}

+ 1
- 1
server/sonar-web/src/main/js/libs/third-party/bootstrap/dropdown.js View File

@@ -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)
}

+ 0
- 363
server/sonar-web/src/main/less/components/navbar.less View File

@@ -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;
}

+ 4
- 12
server/sonar-web/src/main/less/components/page.less View File

@@ -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;

+ 2
- 8
server/sonar-web/src/main/less/components/search-navigator.less View File

@@ -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;
}


+ 0
- 103
server/sonar-web/src/main/less/components/ui.less View File

@@ -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;

+ 0
- 10
server/sonar-web/src/main/less/print.less View File

@@ -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;
}

}

+ 0
- 1
server/sonar-web/src/main/less/sonar.less View File

@@ -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";

+ 1
- 21
server/sonar-web/src/main/less/variables.less View File

@@ -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;

+ 1
- 1
tests/src/test/java/org/sonarqube/pageobjects/organization/MembersPage.java View File

@@ -32,7 +32,7 @@ import static com.codeborne.selenide.Selenide.$$;
public class MembersPage {

public MembersPage() {
$(".nav-tabs a.active").shouldBe(visible).shouldHave(text("Members"));
$(".navbar-tabs a.active").shouldBe(visible).shouldHave(text("Members"));
}

public ElementsCollection getMembers() {

+ 1
- 1
tests/src/test/java/org/sonarqube/tests/ui/OrganizationUiExtensionsTest.java View File

@@ -63,7 +63,7 @@ public class OrganizationUiExtensionsTest {
.logIn().submitCredentials(administrator.getLogin())
.open("/organizations/" + organization.getKey() + "/projects");

$("#context-navigation a.navbar-admin-link").click();
$("#context-navigation a.is-admin").click();
$(By.linkText("Organization Admin Page")).shouldBe(Condition.visible).click();

assertThat(url()).contains("uiextensionsplugin/organization_admin_page");

+ 1
- 2
tests/src/test/java/org/sonarqube/tests/ui/UiExtensionsTest.java View File

@@ -66,9 +66,8 @@ public class UiExtensionsTest {
User administrator = tester.users().generateAdministrator();
tester.openBrowser()
.logIn().submitCredentials(administrator.getLogin())
.open("/about");
.open("/settings");

$(".navbar-admin-link").click();
$("#settings-navigation-configuration").click();
$(By.linkText("Global Admin Page")).click();


Loading…
Cancel
Save