From: Stas Vilchik Date: Thu, 26 Nov 2015 14:58:04 +0000 (+0100) Subject: SONAR-7059 Improve navigation between dashboards inside the project space X-Git-Tag: 5.3-RC1~149 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=7c2cc24167ef1463f58668941f1424fcfb1185f4;p=sonarqube.git SONAR-7059 Improve navigation between dashboards inside the project space --- diff --git a/server/sonar-web/npm-debug.log.d92601cb81ea7493b0878a7dda333587 b/server/sonar-web/npm-debug.log.d92601cb81ea7493b0878a7dda333587 deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/server/sonar-web/src/main/js/components/dashboards/dashboard-sidebar.js b/server/sonar-web/src/main/js/components/dashboards/dashboard-sidebar.js new file mode 100644 index 00000000000..f68aa7b9c89 --- /dev/null +++ b/server/sonar-web/src/main/js/components/dashboards/dashboard-sidebar.js @@ -0,0 +1,124 @@ +import qs from 'querystring'; +import _ from 'underscore'; +import classNames from 'classnames'; +import React from 'react'; + +import { getLocalizedDashboardName } from '../../helpers/l10n'; +import { getComponentDashboardUrl, getComponentFixedDashboardUrl, getComponentDashboardManagementUrl } from '../../helpers/urls'; + + +const FIXED_DASHBOARDS = [ + { link: '', name: 'overview.page' }, + { link: '/issues', name: 'overview.domain.debt' }, + { link: '/tests', name: 'overview.domain.coverage' }, + { link: '/duplications', name: 'overview.domain.duplications' }, + { link: '/size', name: 'overview.domain.size' } +]; + +const CUSTOM_DASHBOARDS_LIMIT = 1; + + +export const DashboardSidebar = React.createClass({ + propTypes: { + component: React.PropTypes.object.isRequired, + customDashboards: React.PropTypes.arrayOf(React.PropTypes.object).isRequired + }, + + periodParameter() { + let params = qs.parse(window.location.search.substr(1)); + return params.period ? `&period=${params.period}` : ''; + }, + + getPeriod() { + let params = qs.parse(window.location.search.substr(1)); + return params.period; + }, + + isFixedDashboardActive(fixedDashboard) { + let path = window.location.pathname; + return path === `${window.baseUrl}/overview${fixedDashboard.link}`; + }, + + isCustomDashboardActive(customDashboard) { + let path = window.location.pathname, + params = qs.parse(window.location.search.substr(1)); + return path.indexOf(`${window.baseUrl}/dashboard`) === 0 && params['did'] === `${customDashboard.key}`; + }, + + isMoreCustomDashboardsActive () { + let dashboards = _.rest(this.props.customDashboards, CUSTOM_DASHBOARDS_LIMIT); + return _.any(dashboards, this.isCustomDashboardActive); + }, + + isDashboardManagementActive () { + let path = window.location.pathname; + return path.indexOf(`${window.baseUrl}/dashboards`) === 0; + }, + + renderFixedDashboards() { + return FIXED_DASHBOARDS.map(fixedDashboard => { + let key = 'fixed-dashboard-' + fixedDashboard.link.substr(1); + let url = getComponentFixedDashboardUrl(this.props.component.key, fixedDashboard.link); + let name = window.t(fixedDashboard.name); + let className = classNames({ active: this.isFixedDashboardActive(fixedDashboard) }); + return
  • + {name} +
  • ; + }); + }, + + renderCustomDashboards() { + let dashboards = _.first(this.props.customDashboards, CUSTOM_DASHBOARDS_LIMIT); + return dashboards.map(this.renderCustomDashboard); + }, + + renderCustomDashboard(customDashboard) { + let key = 'custom-dashboard-' + customDashboard.key; + let url = getComponentDashboardUrl(this.props.component.key, customDashboard.key, this.getPeriod()); + let name = getLocalizedDashboardName(customDashboard.name); + let className = classNames({ active: this.isCustomDashboardActive(customDashboard) }); + return
  • + {name} +
  • ; + }, + + renderMoreCustomDashboards() { + if (this.props.customDashboards.length <= CUSTOM_DASHBOARDS_LIMIT) { + return null; + } + let dashboards = _.rest(this.props.customDashboards, CUSTOM_DASHBOARDS_LIMIT) + .map(this.renderCustomDashboard); + let className = classNames('dropdown', { active: this.isMoreCustomDashboardsActive() }); + return
  • + + More  + + +
      {dashboards}
    +
  • ; + }, + + renderDashboardsManagementLink() { + if (!window.SS.user) { + return null; + } + let key = 'dashboard-management'; + let url = getComponentDashboardManagementUrl(this.props.component.key); + let name = window.t('dashboard.manage_dashboards'); + let className = classNames('pill-right', { active: this.isDashboardManagementActive() }); + return
  • + {name} +
  • ; + }, + + render() { + return ; + } +}); diff --git a/server/sonar-web/src/main/js/helpers/l10n.js b/server/sonar-web/src/main/js/helpers/l10n.js new file mode 100644 index 00000000000..de950a94b93 --- /dev/null +++ b/server/sonar-web/src/main/js/helpers/l10n.js @@ -0,0 +1,5 @@ +export function getLocalizedDashboardName (baseName) { + var l10nKey = 'dashboard.' + baseName + '.name'; + var l10nLabel = window.t(l10nKey); + return l10nLabel !== l10nKey ? l10nLabel : baseName; +} diff --git a/server/sonar-web/src/main/js/helpers/urls.js b/server/sonar-web/src/main/js/helpers/urls.js index 8280af1c6ea..2dc10eacb31 100644 --- a/server/sonar-web/src/main/js/helpers/urls.js +++ b/server/sonar-web/src/main/js/helpers/urls.js @@ -41,3 +41,41 @@ export function getComponentDrilldownUrl (componentKey, metric, period, highligh } return url; } + + +/** + * Generate URL for a component's dashboard + * @param {string} componentKey + * @param {string} dashboardKey + * @param {string} [period] + * @returns {string} + */ +export function getComponentDashboardUrl (componentKey, dashboardKey, period) { + let url = window.baseUrl + '/dashboard?id=' + encodeURIComponent(componentKey) + + '&did=' + encodeURIComponent(dashboardKey); + if (period) { + url += '&period=' + period; + } + return url; +} + + +/** + * Generate URL for a fixed component's dashboard (overview) + * @param {string} componentKey + * @param {string} dashboardKey + * @returns {string} + */ +export function getComponentFixedDashboardUrl (componentKey, dashboardKey) { + return window.baseUrl + '/overview' + dashboardKey + '?id=' + encodeURIComponent(componentKey); +} + + +/** + * Generate URL for a component's dashboards management page + * @param {string} componentKey + * @returns {string} + */ +export function getComponentDashboardManagementUrl (componentKey) { + return window.baseUrl + '/dashboards?resource=' + encodeURIComponent(componentKey); +} diff --git a/server/sonar-web/src/main/js/main/nav/app.js b/server/sonar-web/src/main/js/main/nav/app.js index af45c03ff02..f4f9644be13 100644 --- a/server/sonar-web/src/main/js/main/nav/app.js +++ b/server/sonar-web/src/main/js/main/nav/app.js @@ -1,9 +1,11 @@ import React from 'react'; import ReactDOM from 'react-dom'; + import GlobalNav from './global/global-nav'; import ComponentNav from './component/component-nav'; import SettingsNav from './settings/settings-nav'; -import {getGlobalNavigation, getComponentNavigation, getSettingsNavigation} from '../../api/nav'; +import { getGlobalNavigation, getComponentNavigation, getSettingsNavigation } from '../../api/nav'; +import { DashboardSidebar } from '../../components/dashboards/dashboard-sidebar'; import '../../components/workspace/main'; import '../../helpers/handlebars-helpers'; @@ -44,15 +46,26 @@ export default class App { } static renderComponentNav (options) { - return getComponentNavigation(options.componentKey).then(r => { + return getComponentNavigation(options.componentKey).then(component => { const el = document.getElementById('context-navigation'); if (el) { - ReactDOM.render(, el); + ReactDOM.render(, el); } - return r; + this.renderSidebarNav(component); + return component; }); } + static renderSidebarNav (component) { + let shouldRender = + window.location.pathname.indexOf(window.baseUrl + '/overview') === 0 || + window.location.pathname.indexOf(window.baseUrl + '/dashboard') === 0; + let el = document.getElementById('sidebar'); + if (shouldRender && el) { + ReactDOM.render(, el); + } + } + static renderSettingsNav (options) { return getSettingsNavigation().then(r => { let el = document.getElementById('context-navigation'); diff --git a/server/sonar-web/src/main/js/main/nav/component/component-nav-menu.js b/server/sonar-web/src/main/js/main/nav/component/component-nav-menu.js index 28818b16c54..a73e466e35a 100644 --- a/server/sonar-web/src/main/js/main/nav/component/component-nav-menu.js +++ b/server/sonar-web/src/main/js/main/nav/component/component-nav-menu.js @@ -17,9 +17,14 @@ export default React.createClass({ return params.period ? `&period=${params.period}` : ''; }, - renderOverviewLink() { + renderDashboardLink() { let url = `/overview?id=${encodeURIComponent(this.props.component.key)}`; - return this.renderLink(url, window.t('overview.page'), '/overview'); + return this.renderLink(url, window.t('layout.dashboards'), () => { + let cond = + window.location.pathname.indexOf(window.baseUrl + '/overview') === 0 || + window.location.pathname.indexOf(window.baseUrl + '/dashboard') === 0; + return cond ? 'active' : null; + }); }, renderComponentsLink() { @@ -55,7 +60,9 @@ export default React.createClass({ return (
  • - {window.t('layout.settings')}  + {window.t('layout.settings')}  + +
      {this.renderSettingsLink()} {this.renderProfilesLink()} @@ -169,69 +176,30 @@ export default React.createClass({ }); }, - renderMore() { - return ( -
    • - - {window.t('more')}  - -
        - {this.renderDashboards()} - {this.renderDashboardManagementLink()} - {this.renderTools()} -
      -
    • - ); - }, - - renderDashboards() { - let dashboards = (this.props.component.dashboards || []).map(d => { - let url = `/dashboard?id=${encodeURIComponent(this.props.component.key)}&did=${d.key}${this.periodParameter()}`; - let name = this.getLocalizedDashboardName(d.name); - return this.renderLink(url, name); - }); - return [
    • {window.t('layout.dashboards')}
    • ].concat(dashboards); - }, - - renderDashboardManagementLink() { - if (!window.SS.user) { - return null; - } - let url = `/dashboards?resource=${encodeURIComponent(this.props.component.key)}`; - let name = window.t('dashboard.manage_dashboards'); - return [ -
    • , - this.renderLink(url, name, '/dashboards') - ]; - }, - renderTools() { let component = this.props.component; if (!component.isComparable && !_.size(component.extensions)) { return null; } - let tools = [ -
    • , -
    • Tools
    • - ]; + let tools = []; + (component.extensions || []).forEach(e => { + tools.push(this.renderLink(e.url, e.name)); + }); if (component.isComparable) { let compareUrl = `/comparison/index?resource=${component.key}`; tools.push(this.renderLink(compareUrl, window.t('comparison.page'))); } - (component.extensions || []).forEach(e => { - tools.push(this.renderLink(e.url, e.name)); - }); return tools; }, render() { return (
        - {this.renderOverviewLink()} + {this.renderDashboardLink()} {this.renderComponentsLink()} {this.renderComponentIssuesLink()} {this.renderAdministration()} - {this.renderMore()} + {this.renderTools()}
      ); } diff --git a/server/sonar-web/src/main/js/main/nav/links-mixin.js b/server/sonar-web/src/main/js/main/nav/links-mixin.js index d2f07300261..73b2a69ba5f 100644 --- a/server/sonar-web/src/main/js/main/nav/links-mixin.js +++ b/server/sonar-web/src/main/js/main/nav/links-mixin.js @@ -11,7 +11,7 @@ export default { let fullUrl = window.baseUrl + url; let check = _.isFunction(highlightUrl) ? highlightUrl : this.activeLink; return ( -
    • +
    • {title}
    • ); diff --git a/server/sonar-web/src/main/less/components.less b/server/sonar-web/src/main/less/components.less index 34d748dc806..464b4260cdf 100644 --- a/server/sonar-web/src/main/less/components.less +++ b/server/sonar-web/src/main/less/components.less @@ -23,3 +23,4 @@ @import "components/columns"; @import "components/workspace"; @import "components/search"; +@import "components/pills"; diff --git a/server/sonar-web/src/main/less/components/navbar.less b/server/sonar-web/src/main/less/components/navbar.less index 25f4552bb7e..1685c6635d9 100644 --- a/server/sonar-web/src/main/less/components/navbar.less +++ b/server/sonar-web/src/main/less/components/navbar.less @@ -219,3 +219,9 @@ color: @secondFontColor; font-size: @smallFontSize; } + +.navbar-side { + padding: 10px; + border-bottom: 1px solid @barBorderColor; + background-color: #e5f1f9; +} diff --git a/server/sonar-web/src/main/less/components/page.less b/server/sonar-web/src/main/less/components/page.less index 77d560efb23..bb510b2ff85 100644 --- a/server/sonar-web/src/main/less/components/page.less +++ b/server/sonar-web/src/main/less/components/page.less @@ -12,7 +12,7 @@ body { .page { .clearfix; position: relative; - padding: 10px; + padding: 10px 20px; } .page-container { diff --git a/server/sonar-web/src/main/less/components/pills.less b/server/sonar-web/src/main/less/components/pills.less new file mode 100644 index 00000000000..88cc5a3c619 --- /dev/null +++ b/server/sonar-web/src/main/less/components/pills.less @@ -0,0 +1,38 @@ +@import (reference) '../variables'; + +.pills { + display: flex; +} + +.pills > li { + +} + +.pills > li + li { + margin-left: 2px; +} + +.pills > li > a { + display: block; + height: @formControlHeight; + max-width: 135px; + line-height: @formControlHeight; + padding: 0 10px; + border: none; + border-radius: @formControlHeight; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + transition: none; +} + +.pills > li.active > a, +.pills > li > a:hover, +.pills > li > a:focus { + background-color: @darkBlue; + color: #fff; +} + +.pill-right { + margin-left: auto !important; +} diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboards/_my_dashboards.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboards/_my_dashboards.html.erb index b055bb15f90..3c2425053b5 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboards/_my_dashboards.html.erb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/dashboards/_my_dashboards.html.erb @@ -21,9 +21,6 @@ <%= link_to h(dashboard.name(true)), {:controller => :dashboard, :action => :index, :did => dashboard.id, :id => (resource_id unless dashboard.global?)}, :id => "view-#{u dashboard.name}" %>
      <%= h dashboard.description -%>
      - <% if index == 0 %> -
      <%= h message('dashboard.default_dashboard') -%>
      - <% end %> <% if (dashboard.shared) %><% end %> diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_layout.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_layout.html.erb index b793d31198e..2af9eb9a60c 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_layout.html.erb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/layouts/_layout.html.erb @@ -17,6 +17,7 @@ <%= yield :header -%>
      +