From 053b5f30d7b2c4b5ba6d885dc28723744e5b22db Mon Sep 17 00:00:00 2001 From: Diana Whitten Date: Thu, 8 Oct 2015 12:22:08 -0700 Subject: [PATCH] Horizon Dropdown now inherits from Bootstrap Theme Horizon dropdowns now use proper Bootstrap markup to allow for theme inheritance. It was noticed during this effort that Horizon was using a mixture of and + {% else %} + + {% if action.icon != None %} + + {% endif %} + {% else %} + href="{{ action.bound_url }}"> + {% endif %} + {{ action.verbose_name }} + + {% endif %} +{% endminifyspace %} diff --git a/horizon/templates/horizon/common/_data_table_row_action_dropdown.html b/horizon/templates/horizon/common/_data_table_row_action_dropdown.html deleted file mode 100644 index aee336f6b..000000000 --- a/horizon/templates/horizon/common/_data_table_row_action_dropdown.html +++ /dev/null @@ -1,5 +0,0 @@ -{% if action.method != "GET" %} - -{% else %} - {{ action.verbose_name }} -{% endif %} diff --git a/horizon/templates/horizon/common/_data_table_row_actions_dropdown.html b/horizon/templates/horizon/common/_data_table_row_actions_dropdown.html index 37afa0ff0..9b62e9dd2 100644 --- a/horizon/templates/horizon/common/_data_table_row_actions_dropdown.html +++ b/horizon/templates/horizon/common/_data_table_row_actions_dropdown.html @@ -1,27 +1,28 @@ {% load horizon i18n %} {% spaceless %} {# This makes sure whitespace doesn't affect positioning for dropdown. #} -{% if row_actions|length > 1 %} -
- {% for action in row_actions %} + + {% if row_actions|length == 1 %} + {% include "horizon/common/_data_table_action.html" with action=row_actions.0 is_single=1 %} + {% elif row_actions|length > 1 %} +
+ {% for action in row_actions %} {% if forloop.first %} - {% include "horizon/common/_data_table_row_action_dropdown.html" %} + {% include "horizon/common/_data_table_action.html" with is_small=1 is_single=1 %} -
-{% endif %} -{% if row_actions|length == 1 %} - {% include "horizon/common/_data_table_row_action_dropdown.html" with action=row_actions.0 %} -{% endif %} + {% endfor %} +
+ {% endif %} + {% endspaceless %} diff --git a/horizon/templates/horizon/common/_data_table_table_action.html b/horizon/templates/horizon/common/_data_table_table_action.html deleted file mode 100644 index b0e819e2e..000000000 --- a/horizon/templates/horizon/common/_data_table_table_action.html +++ /dev/null @@ -1,11 +0,0 @@ -{% if action.method != "GET" %} - -{% else %} - - {% if action.icon != None %} {% endif %} - {{ action.verbose_name }} - -{% endif %} diff --git a/horizon/templates/horizon/common/_data_table_table_actions.html b/horizon/templates/horizon/common/_data_table_table_actions.html index bd3f79d50..40cb31cdc 100644 --- a/horizon/templates/horizon/common/_data_table_table_actions.html +++ b/horizon/templates/horizon/common/_data_table_table_actions.html @@ -30,14 +30,23 @@ {% endfor %} - + {% endif %} {% endblock table_filter %} + {% block table_actions %} + + {% comment %} + For each single action in the Table Actions area + {% endcomment %} {% for action in table_actions_buttons %} - {% include "horizon/common/_data_table_table_action.html" %} + {% include "horizon/common/_data_table_action.html" with is_table_action=1 is_single=1 %} {% endfor %} + + {% comment %} + If additional actions are defined, scoop them into the actions dropdown menu + {% endcomment %} {% if table_actions_menu|length > 0 %}
@@ -51,11 +60,12 @@
{% endif %} {% endblock table_actions %} + diff --git a/horizon/templatetags/horizon.py b/horizon/templatetags/horizon.py index c8ef4f112..bca67bd45 100644 --- a/horizon/templatetags/horizon.py +++ b/horizon/templatetags/horizon.py @@ -19,6 +19,7 @@ from horizon.contrib import bootstrap_datepicker from django.conf import settings from django import template +from django.template import Node from django.utils.encoding import force_text from django.utils import translation from django.utils.translation import ugettext_lazy as _ @@ -26,10 +27,19 @@ from django.utils.translation import ugettext_lazy as _ from horizon.base import Horizon # noqa from horizon import conf - register = template.Library() +class MinifiedNode(Node): + def __init__(self, nodelist): + self.nodelist = nodelist + + def render(self, context): + return ' '.join( + force_text(self.nodelist.render(context).strip()).split() + ) + + @register.filter def has_permissions(user, component): """Checks if the given user meets the permissions requirements for @@ -198,3 +208,28 @@ def datepicker_locale(): locale_mapping = getattr(settings, 'DATEPICKER_LOCALES', bootstrap_datepicker.LOCALE_MAPPING) return locale_mapping.get(translation.get_language(), 'en') + + +@register.tag +def minifyspace(parser, token): + """Removes whitespace including tab and newline characters. Do not use this + if you are using a
 tag
+
+    Example usage::
+
+        {% minifyspace %}
+            

+ + Foo + +

+ {% endminifyspace %} + + This example would return this HTML:: + +

Foo

+ """ + nodelist = parser.parse(('endminifyspace',)) + parser.delete_first_token() + return MinifiedNode(nodelist) diff --git a/horizon/utils/html.py b/horizon/utils/html.py index fe5c3c60b..e936bb89c 100644 --- a/horizon/utils/html.py +++ b/horizon/utils/html.py @@ -33,13 +33,17 @@ class HTMLElement(object): """ return {} - def get_final_attrs(self): + def get_final_attrs(self, classes=True): """Returns a dict containing the final attributes of this element which will be rendered. """ final_attrs = copy.copy(self.get_default_attrs()) final_attrs.update(self.attrs) - final_attrs['class'] = self.get_final_css() + if classes: + final_attrs['class'] = self.get_final_css() + else: + final_attrs.pop('class', None) + return final_attrs def get_final_css(self): @@ -58,6 +62,13 @@ class HTMLElement(object): """ return flatatt(self.get_final_attrs()) + @property + def attr_string_nc(self): + """Returns a flattened string of HTML attributes based on the + ``attrs`` dict provided to the class. + """ + return flatatt(self.get_final_attrs(False)) + @property def class_string(self): """Returns a list of class name of HTML Element in string.""" diff --git a/openstack_dashboard/static/dashboard/scss/components/_table_actions.scss b/openstack_dashboard/static/dashboard/scss/components/_table_actions.scss index 5c262f88a..b17156173 100644 --- a/openstack_dashboard/static/dashboard/scss/components/_table_actions.scss +++ b/openstack_dashboard/static/dashboard/scss/components/_table_actions.scss @@ -1,3 +1,56 @@ +/* Table Dropdowns */ +/* Unfortunately, we want to style a button in a dropdown + the same way that we style an anchor. This isn't possible + in the current Bootstrap: + https://github.com/twbs/bootstrap/issues/10248 + Until it is, wrap all buttons with anchors ... + and we have this workaround. +*/ +/* Specificity required */ +.table_actions_menu .dropdown-menu > li > button, +.actions_column .dropdown-menu > li > button { + border: none; + margin: 0; // prevent the form-inline styles from messing with margin + padding: 3px 20px; // Hardcoded in Bootstrap also, see _dropdowns.scss + color: $dropdown-link-color; + white-space: nowrap; // prevent links from breaking onto new lines + min-width: 100%; + text-align: left; + background: transparent; + display: block; + clear: both; + font-weight: normal; + line-height: $line-height-base; + + &:hover, + &:focus { + text-decoration: none; + color: $dropdown-link-hover-color; + background-color: $dropdown-link-hover-bg; + } + + &.disabled, + &[disabled] { + cursor: not-allowed; + pointer-events: none; // Future-proof disabling of clicks + @include opacity(.65); + @include box-shadow(none); + } + + &.btn-primary { + color: $brand-primary; + } + &.btn-danger { + color: $brand-danger; + } + &.btn-warning { + color: $brand-warning; + } + &.btn-info { + color: $brand-info; + } +} + .table_actions { float: right; @extend .form-inline; diff --git a/openstack_dashboard/static/dashboard/scss/horizon.scss b/openstack_dashboard/static/dashboard/scss/horizon.scss index d69149ebb..9cda6b29e 100644 --- a/openstack_dashboard/static/dashboard/scss/horizon.scss +++ b/openstack_dashboard/static/dashboard/scss/horizon.scss @@ -26,6 +26,7 @@ @import "components/network_topology"; @import "components/context_selection"; @import "components/pie_charts"; +@import "components/table_actions"; @import "/framework/framework"; @import "components/tables"; @import "components/table_actions"; @@ -411,14 +412,13 @@ a.current_item:hover h4 { } } -/* Actions dropdown */ - -.actions_column { +/* Tables */ +/* This works around a known bug in Bootstrap, the + wrapping of button groups within the cell of a table: + https://github.com/twbs/bootstrap/issues/3130 +*/ +td .btn-group { white-space: nowrap; - padding: 10px; - position: relative; - width: 1em; - background-clip: padding-box; // We want the actions column to be a small button, but // we can't get to the class attribute yet to customize @@ -426,118 +426,11 @@ a.current_item:hover h4 { .btn { @extend .btn-sm; } -} -form.actions_column { - width: auto; - font-family: $font-family-base; -} - -// TODO(hurgleburgler): We need to fix this, we still support IE8+ -.actions_column .btn-group { - display: inline-flex; - display: -ms-inline-flexbox; - -ms-flex-direction: row; - display: -webkit-inline-flex; - display: -moz-inline-flex; -} - -.actions_column .row_actions a, -.actions_column .row_actions input, -.actions_column .row_actions button, -div.table_actions_menu .dropdown-menu a, -div.table_actions_menu .dropdown-menu input, -div.table_actions_menu .dropdown-menu button { - background: none; - float: none; - display: block; - padding: 5px 10px; - color: $text-color; - text-align: left; - border-radius: 0; - border: 0 none; - @include box-shadow(none); -} - -.actions_column .row_actions .hide { - display: none; -} - -.actions_column .btn-action-required { - font-weight: bold; -} - -.tab-content { - overflow: visible; -} - -/* Makes size consistent across browsers when mixing "btn-group" and "small" */ -.btn.hide, .btn-group .hide { - display: none; -} -.btn-group .dropdown-toggle:focus { - outline: none; -} -.dropdown-menu button { - line-height: 18px; /* Matches rule for ".dropdown-menu a" in bootstrap */ - width: 100%; -} -.btn-group .dropdown-menu .btn { - border-radius: 0; -} -.dropdown-menu .btn.btn-danger, -.dropdown-menu .btn.btn-danger:hover, -.dropdown-menu .btn.btn-success, -.dropdown-menu .btn.btn-success:hover, -.dropdown-menu .btn.btn-info, -.dropdown-menu .btn.btn-info:hover { - text-shadow: none; /* remove default bootstrap shadowing from button text. */ -} -.dropdown-menu li:hover { - background: none; -} - -.actions_column .dropdown-menu a:hover, -.actions_column .dropdown-menu button:hover, -div.table_actions_menu .dropdown-menu a:hover, -div.table_actions_menu .dropdown-menu button:hover { - background-color: $gray-lighter; -} -.dropdown-menu .btn.btn-danger { - color: $brand-danger; -} -.dropdown-menu .btn.btn-danger:hover { - background-color: $gray-lighter; -} - - -/* Overrides for single-action rows (no dropdown) */ - -tr .actions_column ul.row_actions.single, -tr:hover .actions_column ul.row_actions.single, -.actions_column ul.row_actions.single, -.actions_column ul.row_actions.single:hover { - border: none; -} - -.actions_column ul.row_actions.single li.action { - display: block; -} - -.actions_column ul.row_actions.single li.action:hover { - background-color: transparent; -} - -.actions_column ul.row_actions.single a, -.actions_column ul.row_actions.single input, -.actions_column ul.row_actions.single button { - color: $brand-info; -} - -.actions_column ul.row_actions.single a:hover, -.actions_column ul.row_actions.single input:hover, -.actions_column ul.row_actions.single button:hover { - color: $text-color; + & > .btn-group, + & > .btn { + float: none; + } } div.input input[type="checkbox"] {