Improve Horizon nav sidebar
- Make menu responsive (hides on smaller screens, e,g, 1/2 laptop screen, tablet, mobile) - Add aria parameters for accessibility - Show current focus when navigating (accessibility) - Remove the blue outline when clicking links in Chrome - Makes menu less hideous Change-Id: I1cdfa079f0b371d1afddefa67d8a21e93abde9ee Implements: blueprint navigation-improvements Closes-Bug: 1315488 Closes-Bug: 1628274
This commit is contained in:
parent
c46b5014c8
commit
837587fe73
|
@ -35,14 +35,15 @@ horizon.addInitFunction(horizon.selenium.init = function() {
|
|||
});
|
||||
|
||||
horizon.selenium.initSideBarHelpers = function() {
|
||||
var $activeEntry = $('li.openstack-dashboard.active > ul.panel-collapse.in');
|
||||
var dashboardLoc = 'li.openstack-dashboard';
|
||||
var groupLoc = 'li.nav-header.panel';
|
||||
var $activeEntry = $('.openstack-dashboard-active > ul.panel-collapse.in');
|
||||
var dashboardLoc = '.openstack-dashboard';
|
||||
var groupLoc = 'li.openstack-panel-group';
|
||||
var activeCls = horizon.selenium.ACTIVE_CLS;
|
||||
|
||||
var $activeDashboard = $activeEntry.closest(dashboardLoc).toggleClass(activeCls);
|
||||
var $activeGroup = $activeEntry.find(
|
||||
'li.nav-header.panel > ul.panel-collapse.in').closest(groupLoc).toggleClass(activeCls);
|
||||
'li.openstack-panel-group > ul.panel-collapse.in'
|
||||
).closest(groupLoc).toggleClass(activeCls);
|
||||
|
||||
function toggleActiveDashboard($dashboard) {
|
||||
if ($activeDashboard) {
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* (c) Copyright 2015 Hewlett-Packard Development Company, L.P.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
$(document).ready(function () {
|
||||
'use strict';
|
||||
|
||||
var $sidenav = $('#sidebar');
|
||||
var $mask = $(document.createElement('div'))
|
||||
.prop('id', 'sidebar-mask')
|
||||
.appendTo($('#content_body'));
|
||||
|
||||
// Hamburger Happiness !!!
|
||||
$(document).on('click', '#sidebar-toggle', function () {
|
||||
$mask.toggleClass('on-screen');
|
||||
$sidenav.toggleClass('on-screen');
|
||||
});
|
||||
});
|
|
@ -3,11 +3,11 @@
|
|||
<ul id="sidebar-accordion" class="nav nav-pills nav-stacked">
|
||||
{% for dashboard, panel_info in components %}
|
||||
{% if user|has_permissions:dashboard %}
|
||||
<li class="panel openstack-dashboard{% if current.slug == dashboard.slug %} active{% endif %}">
|
||||
<li class="panel openstack-dashboard">
|
||||
<a data-toggle="collapse"
|
||||
data-parent="#sidebar-accordion"
|
||||
data-target="#sidebar-accordion-{{ dashboard.slug }}"
|
||||
href="javascript:;"
|
||||
aria-controls="sidebar-accordion-{{ dashboard.slug }}"
|
||||
{% if current.slug != dashboard.slug %}
|
||||
class="collapsed"
|
||||
{% endif %}>
|
||||
|
@ -20,34 +20,30 @@
|
|||
{% with panels|has_permissions_on_list:user as filtered_panels %}
|
||||
{% if filtered_panels %}
|
||||
{% if group.name %}
|
||||
<li class="nav-header panel">
|
||||
<li class="panel openstack-panel-group">
|
||||
<a data-toggle="collapse"
|
||||
data-parent="#sidebar-accordion-{{ dashboard.slug }}"
|
||||
data-target="#sidebar-accordion-{{ dashboard.slug }}-{{ group.slug }}"
|
||||
href="javascript:;"
|
||||
aria-controls="sidebar-accordion-{{ dashboard.slug }}-{{ group.slug }}"
|
||||
{% if current.slug == dashboard.slug and current_panel_group != group.slug %}class="collapsed"
|
||||
{% elif current.slug != dashboard.slug and forloop.counter0 != 0 %}class="collapsed"{% endif %}>
|
||||
<span class="nav-header-title">
|
||||
{{ group.name }}
|
||||
<span class="openstack-toggle fa pull-right"></span>
|
||||
</span>
|
||||
{{ group.name }}
|
||||
<span class="openstack-toggle fa pull-right"></span>
|
||||
</a>
|
||||
<ul id="sidebar-accordion-{{ dashboard.slug }}-{{ group.slug }}"
|
||||
class="nav collapse panel-collapse
|
||||
{% endif %}
|
||||
<div id="sidebar-accordion-{{ dashboard.slug }}-{{ group.slug }}"
|
||||
class="list-group collapse
|
||||
{% if current.slug == dashboard.slug and current_panel_group == group.slug %} in
|
||||
{% elif current.slug != dashboard.slug and forloop.counter0 == 0 %} in{% endif %}">
|
||||
{% endif %}
|
||||
{% for panel in filtered_panels %}
|
||||
<li class="panel openstack-panel{% if current.slug == dashboard.slug and current_panel == panel.slug %} active{% endif %}">
|
||||
<a class="openstack-spin" href="{{ panel.get_absolute_url }}"
|
||||
<a class="openstack-spin list-group-item openstack-panel {% if current.slug == dashboard.slug and current_panel == panel.slug %}active{% endif %}" href="{{ panel.get_absolute_url }}"
|
||||
target="_self"
|
||||
tabindex="{{ forloop.counter }}" >
|
||||
{{ panel.name }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% if group.name %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% load branding horizon i18n %}
|
||||
|
||||
<div id='sidebar'>
|
||||
<nav id='sidebar'>
|
||||
{% horizon_nav %}
|
||||
</div>
|
||||
</nav>
|
||||
|
|
|
@ -13,20 +13,20 @@ $navbar-border-size: 1px !default;
|
|||
$navbar-true-height: $navbar-height + $navbar-border-size !default;
|
||||
|
||||
#main_content {
|
||||
height: 100%;
|
||||
min-width: $main-content-min-width;
|
||||
padding-top: $navbar-true-height;
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
position: fixed;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#content_body {
|
||||
width: 100%;
|
||||
padding-left: $sidebar-width;
|
||||
// Always show the side nav on larger screens
|
||||
@media(min-width: $screen-sm-min) {
|
||||
#content_body {
|
||||
padding-left: $sidebar-width;
|
||||
}
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-top: $padding-base-horizontal;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,63 +1,110 @@
|
|||
/*
|
||||
* This file defines the styling for side navigation in Horizon, which uses
|
||||
* nested panels to utilise Bootstraps native JS handling for accordion menus
|
||||
* in panels. However, Bootstrap does *not* natively support nested panels
|
||||
* in its markup; to work around this, we remove the panel styling and inherit
|
||||
* list group styling.
|
||||
*/
|
||||
|
||||
#sidebar-accordion {
|
||||
width: $sidebar-width;
|
||||
}
|
||||
|
||||
#sidebar-mask {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
height: 100%;
|
||||
left: 0;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
width: 100%;
|
||||
z-index: 2;
|
||||
transition: all 0.3s ease 0s;
|
||||
|
||||
@media (max-width: $screen-sm-min) {
|
||||
&.on-screen {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#sidebar {
|
||||
min-width: $sidebar-width;
|
||||
z-index: 0;
|
||||
width: $sidebar-width;
|
||||
|
||||
// Make sure the side nav is always shown at larger screen sizes,
|
||||
// regardless of previous state
|
||||
@media (min-width: $screen-sm-min) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media (max-width: $screen-sm-min) {
|
||||
transition: all 0.3s ease 0s;
|
||||
position: fixed;
|
||||
top: $navbar-height;
|
||||
bottom: 0;
|
||||
z-index: 3;
|
||||
left: -$sidebar-width;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
&.on-screen {
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Sets the arrow toggles for each dashboard list
|
||||
[data-toggle="collapse"] {
|
||||
.openstack-toggle.fa {
|
||||
line-height: $line-height-computed;
|
||||
width: $line-height-computed;
|
||||
height: $line-height-computed;
|
||||
text-align: center;
|
||||
@include transition(transform 0.3s ease 0s);
|
||||
@extend .fa-chevron-down;
|
||||
.openstack-toggle.fa {
|
||||
line-height: $line-height-computed;
|
||||
text-align: center;
|
||||
@include transition(transform 0.3s ease 0s);
|
||||
@extend .fa-chevron-down;
|
||||
}
|
||||
|
||||
// Rotate the arrow toggle for closed panels
|
||||
.collapsed > .openstack-toggle.fa {
|
||||
@include rotate(-90deg);
|
||||
}
|
||||
|
||||
// Remove panel default styling for the side nav only
|
||||
.panel {
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
cursor: pointer;
|
||||
|
||||
.list-group-item {
|
||||
border-radius: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
&.collapsed {
|
||||
.openstack-toggle.fa {
|
||||
@include rotate(-90deg);
|
||||
// Use the list group styling for consistency. We use panels in the markup
|
||||
// for accordion, but style should be list-group.
|
||||
> a {
|
||||
color: $list-group-link-color;
|
||||
background: $list-group-bg;
|
||||
|
||||
&:hover {
|
||||
background: $list-group-hover-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Styles for the Dashboard Names
|
||||
.openstack-dashboard {
|
||||
& > a {
|
||||
border-radius: $border-radius-base $border-radius-base 0 0;
|
||||
}
|
||||
// Remove Chromes glowing blue border for focus. This should not affect
|
||||
// accessibility, as the tabs already have a focus effect.
|
||||
a:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
// Styles for the Panel Names
|
||||
// Center align panel groups
|
||||
.openstack-panel-group {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
// Right align panels
|
||||
.openstack-panel {
|
||||
& > a {
|
||||
text-align: right;
|
||||
color: $text-color;
|
||||
}
|
||||
|
||||
&.active > a {
|
||||
color: $brand-primary;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.panel {
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
@include box-shadow(none);
|
||||
margin-bottom: 0;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
// Bootstrap 3 removed nav headers, lets add them back
|
||||
.nav .nav-header > a > .nav-header-title {
|
||||
font-size: $font-size-base;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
<head>
|
||||
<meta content='IE=edge' http-equiv='X-UA-Compatible' />
|
||||
<meta content='text/html; charset=utf-8' http-equiv='Content-Type' />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
{% include "horizon/_custom_meta.html" %}
|
||||
<title>{% block title %}{% endblock %} - {% site_branding %}</title>
|
||||
{% comment %} Load CSS sheets before Javascript {% endcomment %}
|
||||
|
|
|
@ -5,13 +5,21 @@
|
|||
<div class="container-fluid">
|
||||
<!-- Brand and toggle get grouped for better mobile display -->
|
||||
<div class="navbar-header">
|
||||
{% include "header/_brand.html" %}
|
||||
|
||||
<button id="sidebar-toggle" type="button" class="navbar-toggle pull-left">
|
||||
<span class="sr-only">{% trans "Toggle navigation" %}</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse">
|
||||
<span class="sr-only">{% trans "Toggle navigation" %}</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
{% include "header/_brand.html" %}
|
||||
</div>
|
||||
|
||||
<!-- Collect the nav links, forms, and other content for toggling -->
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
<script src='{{ STATIC_URL }}horizon/js/horizon.d3barchart.js'></script>
|
||||
<script src='{{ STATIC_URL }}horizon/js/horizon.firewalls.js'></script>
|
||||
<script src='{{ STATIC_URL }}horizon/js/horizon.volumes.js'></script>
|
||||
<script src='{{ STATIC_URL }}horizon/js/horizon.sidebar.js'></script>
|
||||
|
||||
{% for file in HORIZON_CONFIG.js_files %}
|
||||
<script src='{{ STATIC_URL }}{{ file }}'></script>
|
||||
|
|
|
@ -30,15 +30,15 @@ class NavigationAccordionRegion(baseregion.BaseRegion):
|
|||
return self._get_element(*self._project_bar_locator)
|
||||
|
||||
_first_level_item_selected_locator = (
|
||||
by.By.CSS_SELECTOR, 'li.openstack-dashboard.selenium-active > a')
|
||||
by.By.CSS_SELECTOR, '.openstack-dashboard-active.selenium-active > a')
|
||||
_second_level_item_selected_locator = (
|
||||
by.By.CSS_SELECTOR, 'li.nav-header.selenium-active > a')
|
||||
by.By.CSS_SELECTOR, 'li.openstack-panel-group.selenium-active > a')
|
||||
|
||||
_first_level_item_xpath_template = (
|
||||
"//li[contains(concat('', @class, ''), 'openstack-dashboard') "
|
||||
"and contains(., '%s')]/a")
|
||||
_second_level_item_xpath_template = (
|
||||
"//li[contains(concat('', @class, ''), 'nav-header') "
|
||||
"//li[contains(concat('', @class, ''), 'openstack-panel-group') "
|
||||
"and contains(., '%s')]/a")
|
||||
_third_level_item_xpath_template = (
|
||||
".//li[contains(concat('', @class, ''), 'openstack-panel') and "
|
||||
|
|
|
@ -52,6 +52,10 @@
|
|||
.openstack-panel > a {
|
||||
padding: $padding-small-horizontal $font-size-h4 $padding-small-horizontal ($font-size-h1 - $padding-small-horizontal);
|
||||
text-align: left;
|
||||
|
||||
&.active {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.openstack-toggle {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<ul id="sidebar-drawer" class="nav nav-pills nav-stacked">
|
||||
{% for dashboard, panel_info in components %}
|
||||
{% if user|has_permissions:dashboard %}
|
||||
<li class="openstack-dashboard{% if current.slug == dashboard.slug %} active{% endif %}">
|
||||
<li class="openstack-dashboard">
|
||||
<a data-toggle="collapse"
|
||||
data-parent="#sidebar-drawer"
|
||||
data-target="#sidebar-drawer-{{ dashboard.slug }}"
|
||||
|
@ -39,8 +39,8 @@
|
|||
{% elif current.slug != dashboard.slug and forloop.counter0 == 0 %} in{% endif %}">
|
||||
{% endif %}
|
||||
{% for panel in filtered_panels %}
|
||||
<li class="openstack-panel{% if current.slug == dashboard.slug and current_panel == panel.slug %} active{% endif %}">
|
||||
<a class="openstack-spin" href="{{ panel.get_absolute_url }}"
|
||||
<li class="openstack-panel">
|
||||
<a class="openstack-spin {% if current.slug == dashboard.slug and current_panel == panel.slug %}active{% endif %}" href="{{ panel.get_absolute_url }}"
|
||||
target="_self"
|
||||
tabindex="{{ forloop.counter }}" >
|
||||
{{ panel.name }}
|
||||
|
|
Loading…
Reference in New Issue