0001-blocks-min_items-configuration-58450.patch
tests/form_pages/test_block.py | ||
---|---|---|
895 | 895 |
assert '>bar2<' in resp |
896 | 896 | |
897 | 897 | |
898 |
def test_block_repeated_with_min(pub): |
|
899 |
FormDef.wipe() |
|
900 |
BlockDef.wipe() |
|
901 | ||
902 |
block = BlockDef() |
|
903 |
block.name = 'foobar' |
|
904 |
block.fields = [ |
|
905 |
fields.StringField(id='123', required=True, label='Test', type='string'), |
|
906 |
fields.StringField(id='234', required=True, label='Test2', type='string'), |
|
907 |
] |
|
908 |
block.store() |
|
909 | ||
910 |
formdef = FormDef() |
|
911 |
formdef.name = 'form title' |
|
912 |
formdef.fields = [ |
|
913 |
fields.PageField(id='0', label='1st page', type='page'), |
|
914 |
fields.BlockField( |
|
915 |
id='1', label='test', type='block:foobar', min_items=2, max_items=3, hint='hintblock' |
|
916 |
), |
|
917 |
fields.PageField(id='2', label='2nd page', type='page'), |
|
918 |
] |
|
919 |
formdef.store() |
|
920 | ||
921 |
app = get_app(pub) |
|
922 |
resp = app.get(formdef.get_url()) |
|
923 |
assert resp.text.count('>Test<') == 2 |
|
924 |
assert resp.text.count('>hintblock<') == 1 |
|
925 |
assert 'wcs-block-add-clicked' not in resp |
|
926 |
assert 'Add another' in resp |
|
927 |
assert resp.html.find('div', {'class': 'list-add'}) |
|
928 |
resp = resp.form.submit('f1$add_element') |
|
929 |
assert resp.text.count('>Test<') == 3 |
|
930 |
assert resp.text.count('>hintblock<') == 1 |
|
931 |
assert resp.pyquery('.list-add').attr['style'] == 'display: none' |
|
932 | ||
933 |
formdef.fields[1].min_items = 3 |
|
934 |
formdef.store() |
|
935 | ||
936 |
app = get_app(pub) |
|
937 |
resp = app.get(formdef.get_url()) |
|
938 |
assert resp.text.count('>Test<') == 3 |
|
939 |
assert resp.text.count('>hintblock<') == 1 |
|
940 |
assert resp.pyquery('.list-add').attr['style'] == 'display: none' |
|
941 | ||
942 |
formdef.fields[1].min_items = 4 |
|
943 |
formdef.store() |
|
944 |
app = get_app(pub) |
|
945 |
resp = app.get(formdef.get_url()) |
|
946 |
assert resp.text.count('>Test<') == 3 |
|
947 | ||
948 | ||
898 | 949 |
def test_block_repeated_over_limit(pub): |
899 | 950 |
FormDef.wipe() |
900 | 951 |
BlockDef.wipe() |
... | ... | |
943 | 994 |
assert 'Too many elements (maximum: 2)' in resp |
944 | 995 | |
945 | 996 | |
997 |
def test_block_repeated_under_limit(pub): |
|
998 |
FormDef.wipe() |
|
999 |
BlockDef.wipe() |
|
1000 | ||
1001 |
block = BlockDef() |
|
1002 |
block.name = 'foobar' |
|
1003 |
block.fields = [ |
|
1004 |
fields.StringField(id='123', required=True, label='Test', type='string'), |
|
1005 |
fields.StringField(id='234', required=True, label='Test2', type='string'), |
|
1006 |
] |
|
1007 |
block.store() |
|
1008 | ||
1009 |
formdef = FormDef() |
|
1010 |
formdef.name = 'form title' |
|
1011 |
formdef.fields = [ |
|
1012 |
fields.PageField(id='0', label='1st page', type='page'), |
|
1013 |
fields.BlockField(id='1', label='test', type='block:foobar', min_items=2, max_items=3), |
|
1014 |
fields.PageField(id='2', label='2nd page', type='page'), |
|
1015 |
] |
|
1016 |
formdef.store() |
|
1017 | ||
1018 |
app = get_app(pub) |
|
1019 |
resp = app.get(formdef.get_url()) |
|
1020 |
assert resp.text.count('>Test<') == 2 |
|
1021 | ||
1022 |
# fill items |
|
1023 |
resp.form['f1$element0$f123'] = 'foo' |
|
1024 |
resp.form['f1$element0$f234'] = 'bar' |
|
1025 | ||
1026 |
# submit form - it's ok to have empty block items if block fields are not required |
|
1027 |
resp = resp.form.submit('submit') |
|
1028 |
resp = resp.form.submit('submit') # -> validation page |
|
1029 |
resp = resp.form.submit('submit') # -> submit |
|
1030 |
resp = resp.follow() |
|
1031 |
assert '>foo<' in resp |
|
1032 |
assert '>bar<' in resp |
|
1033 |
formdata = formdef.data_class().select()[0] |
|
1034 |
assert len(formdata.data['1']['data']) == 1 |
|
1035 | ||
1036 | ||
946 | 1037 |
def test_block_repeated_files(pub): |
947 | 1038 |
FormDef.wipe() |
948 | 1039 |
BlockDef.wipe() |
wcs/blocks.py | ||
---|---|---|
245 | 245 |
always_include_add_button = True |
246 | 246 | |
247 | 247 |
def __init__( |
248 |
self, name, value=None, title=None, block=None, max_items=None, add_element_label=None, **kwargs |
|
248 |
self, |
|
249 |
name, |
|
250 |
value=None, |
|
251 |
title=None, |
|
252 |
block=None, |
|
253 |
min_items=None, |
|
254 |
max_items=None, |
|
255 |
add_element_label=None, |
|
256 |
**kwargs, |
|
249 | 257 |
): |
250 | 258 |
self.block = block |
251 | 259 |
self.readonly = kwargs.get('readonly') |
... | ... | |
254 | 262 |
element_values = None |
255 | 263 |
if value: |
256 | 264 |
element_values = value.get('data') |
257 |
if not max_items:
|
|
258 |
max_items = 1
|
|
265 |
max_items = max_items or 1
|
|
266 |
min_items = min(min_items or 1, max_items)
|
|
259 | 267 |
hint = kwargs.pop('hint', None) |
260 | 268 |
element_kwargs = {'block': self.block, 'render_br': False, 'remove_button': self.remove_button} |
261 | 269 |
element_kwargs.update(kwargs) |
... | ... | |
263 | 271 |
name, |
264 | 272 |
value=element_values, |
265 | 273 |
title=title, |
274 |
min_items=min_items, |
|
266 | 275 |
max_items=max_items, |
267 | 276 |
element_type=BlockSubWidget, |
268 | 277 |
element_kwargs=element_kwargs, |
wcs/fields.py | ||
---|---|---|
3323 | 3323 |
allow_complex = True |
3324 | 3324 | |
3325 | 3325 |
widget_class = BlockWidget |
3326 |
min_items = 1 |
|
3326 | 3327 |
max_items = 1 |
3327 |
extra_attributes = ['block', 'max_items', 'add_element_label', 'label_display', 'remove_button'] |
|
3328 |
extra_attributes = [ |
|
3329 |
'block', |
|
3330 |
'min_items', |
|
3331 |
'max_items', |
|
3332 |
'add_element_label', |
|
3333 |
'label_display', |
|
3334 |
'remove_button', |
|
3335 |
] |
|
3328 | 3336 |
add_element_label = '' |
3329 | 3337 |
label_display = 'normal' |
3330 | 3338 |
remove_button = False |
... | ... | |
3346 | 3354 |
super().fill_admin_form(form) |
3347 | 3355 |
if form.get_widget('prefill'): |
3348 | 3356 |
form.remove('prefill') |
3357 |
form.add(IntWidget, 'min_items', title=_('Minimum number of items'), value=self.min_items) |
|
3349 | 3358 |
form.add(IntWidget, 'max_items', title=_('Maximum number of items'), value=self.max_items) |
3350 | 3359 |
form.add( |
3351 | 3360 |
StringWidget, 'add_element_label', title=_('Label of "Add" button'), value=self.add_element_label |
... | ... | |
3366 | 3375 | |
3367 | 3376 |
def get_admin_attributes(self): |
3368 | 3377 |
return super().get_admin_attributes() + [ |
3378 |
'min_items', |
|
3369 | 3379 |
'max_items', |
3370 | 3380 |
'add_element_label', |
3371 | 3381 |
'label_display', |
wcs/qommon/form.py | ||
---|---|---|
1682 | 1682 |
element_type=StringWidget, |
1683 | 1683 |
element_kwargs=None, |
1684 | 1684 |
add_element_label="Add row", |
1685 |
min_items=None, |
|
1685 | 1686 |
max_items=None, |
1686 | 1687 |
**kwargs, |
1687 | 1688 |
): |
... | ... | |
1695 | 1696 |
self.element_type = element_type |
1696 | 1697 |
self.element_kwargs = element_kwargs or {} |
1697 | 1698 |
self.element_names = [] |
1699 |
self.min_items = min_items or 1 |
|
1698 | 1700 |
self.max_items = max_items |
1699 | 1701 | |
1700 | 1702 |
# Add element widgets for initial value |
1701 | 1703 |
if value is not None: |
1702 | 1704 |
for element_value in value: |
1703 | 1705 |
self.add_element(value=element_value) |
1704 |
if not self.element_names:
|
|
1705 |
# Add at least an element widget
|
|
1706 |
while len(self.element_names) < self.min_items:
|
|
1707 |
# Add elements until min_items
|
|
1706 | 1708 |
self.add_element() |
1707 | 1709 | |
1708 | 1710 |
if not kwargs.get('readonly'): |
wcs/qommon/static/js/qommon.forms.js | ||
---|---|---|
516 | 516 | |
517 | 517 |
function disable_single_block_remove_button() { |
518 | 518 |
$('.BlockSubWidget button.remove-button').each(function(i, elem) { |
519 |
if ($(this).parents('.BlockWidget').find('.BlockSubWidget').length == 1) {
|
|
519 |
if ($(this).parents('.BlockWidget').find('.BlockSubWidget').length == parseInt($(this).parents('.BlockWidget').data('min-items'), 10)) {
|
|
520 | 520 |
$(this).prop('disabled', true); |
521 | 521 |
} |
522 | 522 |
}); |
... | ... | |
525 | 525 |
if ($('.BlockWidget').length) { |
526 | 526 |
disable_single_block_remove_button(); |
527 | 527 |
$('form').on('click', '.BlockSubWidget button.remove-button', function() { |
528 |
if ($(this).parents('.BlockWidget').find('.BlockSubWidget').length > 1) {
|
|
528 |
if ($(this).parents('.BlockWidget').find('.BlockSubWidget').length > parseInt($(this).parents('.BlockWidget').data('min-items'), 10)) {
|
|
529 | 529 |
$(this).parents('.BlockWidget').find('.list-add').show(); |
530 | 530 |
$(this).parents('.BlockSubWidget').remove(); |
531 | 531 |
disable_single_block_remove_button(); |
wcs/qommon/templates/qommon/forms/widget.html | ||
---|---|---|
20 | 20 |
data-dynamic-display-value-in="{{widget.attrs|get:"data-dynamic-display-value-in"}}" |
21 | 21 |
{% endif %} |
22 | 22 |
{% if widget.live_condition_source %}data-live-source="true"{% endif %} |
23 |
{% block widget-extra-data %}{% endblock %} |
|
23 | 24 |
> |
24 | 25 |
{% block widget-title %} |
25 | 26 |
{{widget.rendered_title}} |
wcs/qommon/templates/qommon/forms/widgets/block.html | ||
---|---|---|
1 | 1 |
{% extends "qommon/forms/widget.html" %} |
2 | 2 | |
3 | 3 |
{% block widget-css-classes %}{{ block.super }} {% if widget.had_add_clicked %}wcs-block-add-clicked{% endif %} {% if widget.remove_button %}wcs-block-with-remove-button{% endif %}{% endblock %} |
4 |
{% block widget-extra-data %}data-min-items={{ widget.min_items }}{% endblock %} |
|
4 |
- |