Fix initial maxlength attribute for translation widgets (#20081)
- When rendering a translation widget for a field with no initial value the widgets are created at init, so we need to re-set their attributes for the maxlength one to be set. - When rendering the special sub-widget to be cloned in JavaScript for other languages, we need to render it with the attributes of the parent widget for the maxlength to be set too when we switch languages.
This commit is contained in:
Родитель
295a414300
Коммит
978b1718b6
|
@ -1285,6 +1285,11 @@ class BaseModelSerializerAndFormMixin:
|
|||
# - Update the widget attributes again, for form fields
|
||||
if hasattr(field, 'widget'):
|
||||
field.widget.attrs.update(field.widget_attrs(field.widget))
|
||||
# - Update the sub-widgets too if there are any (our
|
||||
# translations widgets work like that)
|
||||
if hasattr(field.widget, 'widgets'):
|
||||
for widget in field.widget.widgets:
|
||||
widget.attrs.update(field.widget_attrs(widget))
|
||||
# - Convert that max_length into a validator ourselves if
|
||||
# the field requires it. Unfortunately some fields
|
||||
# (FileField) do not work like that and instead deal with
|
||||
|
|
|
@ -628,6 +628,30 @@ class TestDescribeForm(TestCase):
|
|||
'Ensure this value has at most 100 characters (it has 101).',
|
||||
]
|
||||
|
||||
def test_render_maxlength(self):
|
||||
def _check_output(form):
|
||||
output = str(form.as_p())
|
||||
doc = pq(output)
|
||||
assert doc('input#id_name_0').attr('maxlength') == '50'
|
||||
assert doc('input#id_name.trans-init').attr('maxlength') == '50'
|
||||
assert doc('input#id_support_email_0').attr('maxlength') == '100'
|
||||
assert doc('input#id_support_email.trans-init').attr('maxlength') == '100'
|
||||
assert doc('input#id_support_url_0').attr('maxlength') == '255'
|
||||
assert doc('input#id_support_url.trans-init').attr('maxlength') == '255'
|
||||
|
||||
form = forms.DescribeForm(
|
||||
request=self.request,
|
||||
instance=Addon.objects.get(),
|
||||
)
|
||||
_check_output(form)
|
||||
|
||||
# Check again with an empty instance (no existing translations).
|
||||
form = forms.DescribeForm(
|
||||
request=self.request,
|
||||
instance=Addon(),
|
||||
)
|
||||
_check_output(form)
|
||||
|
||||
def test_description_optional(self):
|
||||
delicious = Addon.objects.get()
|
||||
assert delicious.type == amo.ADDON_EXTENSION
|
||||
|
|
|
@ -90,17 +90,32 @@ class TestWidget(TestCase):
|
|||
id=666, locale='en-us', localized_string='test value en'
|
||||
)
|
||||
widget = widgets.TransTextarea(attrs={'rows': 5, 'cols': 20})
|
||||
widget.attrs.update({'maxlength': 333})
|
||||
|
||||
doc = pq(widget.render('foo', 666))
|
||||
assert doc('textarea')[0].get('rows') == '5'
|
||||
assert doc('textarea')[0].get('cols') == '20'
|
||||
assert doc('textarea')[0].get('name') == 'foo_en-us'
|
||||
|
||||
assert doc('textarea')[1].get('rows') == '5'
|
||||
assert doc('textarea')[1].get('cols') == '20'
|
||||
assert doc('textarea')[1].get('maxlength') == '333'
|
||||
assert doc('textarea')[1].get('name') == 'foo_init'
|
||||
assert doc('textarea')[1].get('class') == 'trans-init hidden'
|
||||
|
||||
def test_transinput_renders_attrs(self):
|
||||
models.Translation.objects.create(
|
||||
id=666, locale='en-us', localized_string='test value en'
|
||||
)
|
||||
widget = widgets.TransInput(attrs={'rows': 5, 'cols': 20})
|
||||
widget = widgets.TransInput(attrs={'something': 'wicked'})
|
||||
widget.attrs.update({'maxlength': 333})
|
||||
|
||||
doc = pq(widget.render('foo', 666))
|
||||
assert doc('input')[0].get('rows') == '5'
|
||||
assert doc('input')[0].get('cols') == '20'
|
||||
assert doc('input')[0].get('something') == 'wicked'
|
||||
assert doc('input')[0].get('maxlength') == '333'
|
||||
assert doc('input')[0].get('name') == 'foo_en-us'
|
||||
|
||||
assert doc('input')[1].get('something') == 'wicked'
|
||||
assert doc('input')[1].get('maxlength') == '333'
|
||||
assert doc('input')[1].get('name') == 'foo_init'
|
||||
assert doc('input')[1].get('class') == 'trans-init hidden'
|
||||
|
|
|
@ -95,6 +95,18 @@ class TransMulti(forms.widgets.MultiWidget):
|
|||
final_attrs = dict(final_attrs, id=f'{id_}_{i}')
|
||||
output.append(widget.render(name + '_%s' % i, widget_value, final_attrs))
|
||||
|
||||
# Add a widget that'll be cloned for when we want to add a new
|
||||
# translation. Hide it by default, it's only used in devhub, not the
|
||||
# admin (which doesn't need to add new translations).
|
||||
final_attrs = self.build_attrs(attrs)
|
||||
final_attrs['class'] = 'trans-init hidden'
|
||||
output.append(
|
||||
self.widget(attrs=self.attrs).render(
|
||||
self.name + '_',
|
||||
Translation(locale='init', localized_string=''),
|
||||
final_attrs,
|
||||
)
|
||||
)
|
||||
return mark_safe(self.format_output(output))
|
||||
|
||||
def decompress(self, value):
|
||||
|
@ -132,23 +144,13 @@ class TransMulti(forms.widgets.MultiWidget):
|
|||
rv[locale(key)] = data[key]
|
||||
return rv
|
||||
|
||||
def format_output(self, widgets):
|
||||
def format_output(self, rendered_widgets):
|
||||
# Gather output for all widgets as normal...
|
||||
formatted = ''.join(widgets)
|
||||
# ...But also add a widget that'll be cloned for when we want to add
|
||||
# a new translation. Hide it by default, it's only used in devhub, not
|
||||
# the admin (which doesn't need to add new translations).
|
||||
init_widget = self.widget().render(
|
||||
self.name + '_',
|
||||
Translation(locale='init', localized_string=''),
|
||||
{'class': 'trans-init hidden'},
|
||||
)
|
||||
# Wrap it all inside a div that the javascript will look for.
|
||||
return '<div id="trans-{}" class="trans" data-name="{}">{}{}</div>'.format(
|
||||
self.name,
|
||||
self.name,
|
||||
formatted,
|
||||
init_widget,
|
||||
formatted = ''.join(rendered_widgets)
|
||||
# But wrap it all inside a div that the javascript will look for.
|
||||
return (
|
||||
f'<div id="trans-{self.name}" class="trans" data-name="{self.name}">'
|
||||
f'{formatted}</div>'
|
||||
)
|
||||
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче