Merge "Horizon selects are now themable: Launch Instance"
This commit is contained in:
commit
5c42ad657b
@ -235,6 +235,7 @@ class ThemableSelectWidget(SelectWidget):
|
||||
# or both.
|
||||
|
||||
new_choices = []
|
||||
initial_value = value
|
||||
for opt_value, opt_label in itertools.chain(self.choices, choices):
|
||||
other_html = self.transform_option_html_attrs(opt_label)
|
||||
|
||||
@ -244,12 +245,15 @@ class ThemableSelectWidget(SelectWidget):
|
||||
|
||||
opt_label = self.transform_option_label(opt_label)
|
||||
|
||||
# If value exists, save off its label for use
|
||||
if opt_value == value:
|
||||
initial_value = opt_label
|
||||
|
||||
if other_html:
|
||||
new_choices.append((opt_value, opt_label, other_html))
|
||||
else:
|
||||
new_choices.append((opt_value, opt_label))
|
||||
|
||||
initial_value = value
|
||||
if value is None and new_choices:
|
||||
initial_value = new_choices[0][1]
|
||||
|
||||
|
@ -38,7 +38,7 @@ class MinifiedNode(Node):
|
||||
def render(self, context):
|
||||
return ' '.join(
|
||||
force_text(self.nodelist.render(context).strip()).split()
|
||||
)
|
||||
).replace(' > ', '>').replace(' <', '<')
|
||||
|
||||
|
||||
@register.filter
|
||||
|
@ -113,17 +113,19 @@ class TestChoiceFieldForm(forms.SelfHandlingForm):
|
||||
name = forms.CharField(max_length=255,
|
||||
label="Test Name",
|
||||
help_text="Please enter a name")
|
||||
test_choices = forms.ChoiceField(label="Test Choices",
|
||||
required=False,
|
||||
help_text="Testing drop down choices",
|
||||
widget=forms.fields.SelectWidget(
|
||||
attrs={
|
||||
'class': 'switchable',
|
||||
'data-slug': 'source'},
|
||||
transform_html_attrs=title_dic.get))
|
||||
test_choices = forms.ChoiceField(
|
||||
label="Test Choices",
|
||||
required=False,
|
||||
help_text="Testing drop down choices",
|
||||
widget=forms.fields.SelectWidget(
|
||||
attrs={
|
||||
'class': 'switchable',
|
||||
'data-slug': 'source'},
|
||||
transform_html_attrs=title_dic.get))
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(TestChoiceFieldForm, self).__init__(request, *args, **kwargs)
|
||||
super(TestChoiceFieldForm, self).__init__(request, *args,
|
||||
**kwargs)
|
||||
choices = ([('choice1', 'label1'),
|
||||
('choice2', 'label2')])
|
||||
self.fields['test_choices'].choices = choices
|
||||
@ -144,7 +146,78 @@ class ChoiceFieldTests(test.TestCase):
|
||||
return shortcuts.render(self.request, self.template,
|
||||
{'form': self.form})
|
||||
|
||||
def test_choicefield_title(self):
|
||||
def test_legacychoicefield_title(self):
|
||||
resp = self._render_form()
|
||||
self.assertContains(
|
||||
resp,
|
||||
'<option value="choice1" title="This is choice 1">label1</option>',
|
||||
count=1, html=True)
|
||||
self.assertContains(
|
||||
resp,
|
||||
'<option value="choice2" title="This is choice 2">label2</option>',
|
||||
count=1, html=True)
|
||||
|
||||
|
||||
class TestThemableChoiceFieldForm(forms.SelfHandlingForm):
|
||||
# It's POSSIBLE to combine this with the test helper form above, but
|
||||
# I fear we'd run into collisions where one test's desired output is
|
||||
# actually within a separate widget's output.
|
||||
|
||||
title_dic = {"label1": {"title": "This is choice 1"},
|
||||
"label2": {"title": "This is choice 2"},
|
||||
"label3": {"title": "This is choice 3"}}
|
||||
name = forms.CharField(max_length=255,
|
||||
label="Test Name",
|
||||
help_text="Please enter a name")
|
||||
test_choices = forms.ThemableChoiceField(
|
||||
label="Test Choices",
|
||||
required=False,
|
||||
help_text="Testing drop down choices",
|
||||
widget=forms.fields.ThemableSelectWidget(
|
||||
attrs={
|
||||
'class': 'switchable',
|
||||
'data-slug': 'source'},
|
||||
transform_html_attrs=title_dic.get))
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(TestThemableChoiceFieldForm, self).__init__(request, *args,
|
||||
**kwargs)
|
||||
choices = ([('choice1', 'label1'),
|
||||
('choice2', 'label2')])
|
||||
self.fields['test_choices'].choices = choices
|
||||
|
||||
def handle(self, request, data):
|
||||
return True
|
||||
|
||||
|
||||
class ThemableChoiceFieldTests(test.TestCase):
|
||||
|
||||
template = 'horizon/common/_form_fields.html'
|
||||
|
||||
def setUp(self):
|
||||
super(ThemableChoiceFieldTests, self).setUp()
|
||||
self.form = TestThemableChoiceFieldForm(self.request)
|
||||
|
||||
def _render_form(self):
|
||||
return shortcuts.render(self.request, self.template,
|
||||
{'form': self.form})
|
||||
|
||||
def test_choicefield_labels_and_title_attr(self):
|
||||
resp = self._render_form()
|
||||
self.assertContains(
|
||||
resp,
|
||||
'<a data-select-value="choice1" title="This is choice 1">'
|
||||
'label1</a>',
|
||||
count=1,
|
||||
html=True)
|
||||
self.assertContains(
|
||||
resp,
|
||||
'<a data-select-value="choice2" title="This is choice 2">'
|
||||
'label2</a>',
|
||||
count=1,
|
||||
html=True)
|
||||
|
||||
def test_choicefield_title_select_compatible(self):
|
||||
resp = self._render_form()
|
||||
self.assertContains(
|
||||
resp,
|
||||
|
@ -1639,7 +1639,7 @@ class InstanceTests(helpers.TestCase):
|
||||
('eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee', 'm1.metadata'),
|
||||
)
|
||||
|
||||
select_options = '\n'.join([
|
||||
select_options = ''.join([
|
||||
'<option value="%s">%s</option>' % (f[0], f[1])
|
||||
for f in sorted_flavors
|
||||
])
|
||||
|
@ -52,8 +52,8 @@ LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SelectProjectUserAction(workflows.Action):
|
||||
project_id = forms.ChoiceField(label=_("Project"))
|
||||
user_id = forms.ChoiceField(label=_("User"))
|
||||
project_id = forms.ThemableChoiceField(label=_("Project"))
|
||||
user_id = forms.ThemableChoiceField(label=_("User"))
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(SelectProjectUserAction, self).__init__(request, *args, **kwargs)
|
||||
@ -79,35 +79,37 @@ class SelectProjectUser(workflows.Step):
|
||||
|
||||
|
||||
class SetInstanceDetailsAction(workflows.Action):
|
||||
availability_zone = forms.ChoiceField(label=_("Availability Zone"),
|
||||
required=False)
|
||||
availability_zone = forms.ThemableChoiceField(label=_("Availability Zone"),
|
||||
required=False)
|
||||
|
||||
name = forms.CharField(label=_("Instance Name"),
|
||||
max_length=255)
|
||||
|
||||
flavor = forms.ChoiceField(label=_("Flavor"),
|
||||
help_text=_("Size of image to launch."))
|
||||
flavor = forms.ThemableChoiceField(label=_("Flavor"),
|
||||
help_text=_("Size of image to launch."))
|
||||
|
||||
count = forms.IntegerField(label=_("Number of Instances"),
|
||||
min_value=1,
|
||||
initial=1)
|
||||
|
||||
source_type = forms.ChoiceField(label=_("Instance Boot Source"),
|
||||
help_text=_("Choose Your Boot Source "
|
||||
"Type."))
|
||||
source_type = forms.ThemableChoiceField(
|
||||
label=_("Instance Boot Source"),
|
||||
help_text=_("Choose Your Boot Source "
|
||||
"Type."))
|
||||
|
||||
instance_snapshot_id = forms.ChoiceField(label=_("Instance Snapshot"),
|
||||
required=False)
|
||||
instance_snapshot_id = forms.ThemableChoiceField(
|
||||
label=_("Instance Snapshot"),
|
||||
required=False)
|
||||
|
||||
volume_id = forms.ChoiceField(label=_("Volume"), required=False)
|
||||
volume_id = forms.ThemableChoiceField(label=_("Volume"), required=False)
|
||||
|
||||
volume_snapshot_id = forms.ChoiceField(label=_("Volume Snapshot"),
|
||||
required=False)
|
||||
volume_snapshot_id = forms.ThemableChoiceField(label=_("Volume Snapshot"),
|
||||
required=False)
|
||||
|
||||
image_id = forms.ChoiceField(
|
||||
label=_("Image Name"),
|
||||
required=False,
|
||||
widget=forms.SelectWidget(
|
||||
widget=forms.ThemableSelectWidget(
|
||||
data_attrs=('volume_size',),
|
||||
transform=lambda x: ("%s (%s)" % (x.name,
|
||||
filesizeformat(x.bytes)))))
|
||||
@ -536,10 +538,11 @@ KEYPAIR_IMPORT_URL = "horizon:project:access_and_security:keypairs:import"
|
||||
|
||||
|
||||
class SetAccessControlsAction(workflows.Action):
|
||||
keypair = forms.DynamicChoiceField(label=_("Key Pair"),
|
||||
help_text=_("Key pair to use for "
|
||||
"authentication."),
|
||||
add_item_link=KEYPAIR_IMPORT_URL)
|
||||
keypair = forms.ThemableDynamicChoiceField(
|
||||
label=_("Key Pair"),
|
||||
help_text=_("Key pair to use for "
|
||||
"authentication."),
|
||||
add_item_link=KEYPAIR_IMPORT_URL)
|
||||
admin_pass = forms.RegexField(
|
||||
label=_("Admin Password"),
|
||||
required=False,
|
||||
@ -627,10 +630,11 @@ class CustomizeAction(workflows.Action):
|
||||
('file', _('File'))]
|
||||
|
||||
attributes = {'class': 'switchable', 'data-slug': 'scriptsource'}
|
||||
script_source = forms.ChoiceField(label=_('Customization Script Source'),
|
||||
choices=source_choices,
|
||||
widget=forms.Select(attrs=attributes),
|
||||
required=False)
|
||||
script_source = forms.ChoiceField(
|
||||
label=_('Customization Script Source'),
|
||||
choices=source_choices,
|
||||
widget=forms.ThemableSelectWidget(attrs=attributes),
|
||||
required=False)
|
||||
|
||||
script_help = _("A script or set of commands to be executed after the "
|
||||
"instance has been built (max 16kb).")
|
||||
@ -812,7 +816,7 @@ class SetNetworkPorts(workflows.Step):
|
||||
|
||||
|
||||
class SetAdvancedAction(workflows.Action):
|
||||
disk_config = forms.ChoiceField(
|
||||
disk_config = forms.ThemableChoiceField(
|
||||
label=_("Disk Partition"), required=False,
|
||||
help_text=_("Automatic: The entire disk is a single partition and "
|
||||
"automatically resizes. Manual: Results in faster build "
|
||||
@ -822,7 +826,7 @@ class SetAdvancedAction(workflows.Action):
|
||||
required=False, help_text=_("Configure OpenStack to write metadata to "
|
||||
"a special configuration drive that "
|
||||
"attaches to the instance when it boots."))
|
||||
server_group = forms.ChoiceField(
|
||||
server_group = forms.ThemableChoiceField(
|
||||
label=_("Server Group"), required=False,
|
||||
help_text=_("Server group to associate with this instance."))
|
||||
|
||||
|
@ -53,6 +53,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Input groups need to look like button groups
|
||||
.input-group .themable-select .btn {
|
||||
border-bottom-right-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
// For vertical forms, we'll want the button to take on the entire width.
|
||||
// think old-school launch-instance.
|
||||
form:not(.form-inline) .form-group .themable-select {
|
||||
|
Loading…
Reference in New Issue
Block a user