Implement required field indicator

Change Horizon's use of forms to set 'required' css class for required fields.
Change template _form_fields.html to include Django field classes in order
to honor above setting.
Also changed horizon workflow to set "required" class on tabs of steps
that contain required fields.

Horizon CSS appends an asterisk after the field label
and tab text based on "required" classname.
The current indicator is based on review with horizon UX
people and can be customized easily.

Change-Id: I03261328d70abcefd13f609088ccb4d58ef30067
Implements: blueprint required-fields
This commit is contained in:
Rob Raymond 2013-08-28 09:05:40 -06:00
parent 8411e406e6
commit dbe4a6792b
6 changed files with 25 additions and 7 deletions

View File

@ -36,6 +36,8 @@ class SelfHandlingForm(SelfHandlingMixin, forms.Form):
A base :class:`Form <django:django.forms.Form>` class which includes
processing logic in its subclasses.
"""
required_css_class = 'required'
def api_error(self, message):
"""
Adds an error to the form's error dictionary after validation

View File

@ -7,7 +7,7 @@
</div>
{% endif %}
{% for field in form.visible_fields %}
<div class="control-group form-field clearfix{% if field.errors %} error{% endif %}">
<div class="control-group form-field clearfix{% if field.errors %} error{% endif %} {{ field.css_classes }}">
{{ field.label_tag }}
{% if field.errors %}
{% for error in field.errors %}

View File

@ -13,7 +13,7 @@
{% block modal-body %}
<ul class="nav nav-tabs">
{% for step in workflow.steps %}
<li class="{% if entry_point == step.slug %}active{% endif %}{% if step.has_errors %} error{% endif %}">
<li class="{% if entry_point == step.slug %}active{% endif %}{% if step.has_errors %} error{% endif %}{% if step.has_required_fields %} required{% endif %}">
<a href="#{{ step.get_id }}" data-toggle="tab" data-target="#{{ step.get_id }}">{{ step }}</a>
</li>
{% endfor %}

View File

@ -139,6 +139,7 @@ class Action(forms.Form):
% self.__class__.__name__)
self.request = request
self._populate_choices(request, context)
self.required_css_class = 'required'
def __unicode__(self):
return force_unicode(self.name)
@ -445,6 +446,16 @@ class Step(object):
"""
self.action.add_error(message)
def has_required_fields(self):
"""
Returns True if action contains any required fields
"""
for key in self.contributes:
field = self.action.fields.get(key, None)
if (field and field.required):
return True
return False
class WorkflowMetaclass(type):
def __new__(mcs, name, bases, attrs):

View File

@ -36,8 +36,6 @@ LOG = logging.getLogger(__name__)
class CreateNetworkInfoAction(workflows.Action):
net_name = forms.CharField(max_length=255,
label=_("Network Name"),
help_text=_("Network Name. This field is "
"optional."),
required=False)
admin_state = forms.BooleanField(label=_("Admin State"),
initial=True, required=False)
@ -59,8 +57,6 @@ class CreateSubnetInfoAction(workflows.Action):
initial=True, required=False)
subnet_name = forms.CharField(max_length=255,
label=_("Subnet Name"),
help_text=_("Subnet Name. This field is "
"optional."),
required=False)
cidr = fields.IPField(label=_("Network Address"),
required=False,
@ -72,7 +68,7 @@ class CreateSubnetInfoAction(workflows.Action):
ip_version = forms.ChoiceField(choices=[(4, 'IPv4'), (6, 'IPv6')],
label=_("IP Version"))
gateway_ip = fields.IPField(
label=_("Gateway IP (optional)"),
label=_("Gateway IP"),
required=False,
initial="",
help_text=_("IP address of Gateway (e.g. 192.168.0.254) "

View File

@ -1144,6 +1144,15 @@ form div.clearfix.error {
content: "*";
}
.nav-tabs li.required a:after,
form .form-field.required > label:after {
content: "*";
font-weight: bold;
line-height: 0;
padding-left: 4px;
color: #3290c0;
}
/* Region selector in header */
#region_selector {