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:
Mathieu Pillard 2022-12-12 12:12:31 +01:00 коммит произвёл GitHub
Родитель 295a414300
Коммит 978b1718b6
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
4 изменённых файлов: 65 добавлений и 19 удалений

Просмотреть файл

@ -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>'
)