Projet

Général

Profil

0001-forms-add-dynamic-source-support-to-items-field-5376.patch

Frédéric Péters, 04 juillet 2022 13:40

Télécharger (11,3 ko)

Voir les différences:

Subject: [PATCH] forms: add dynamic source support to items field (#53763)

 tests/form_pages/test_live.py                 | 85 ++++++++++++++++++-
 wcs/fields.py                                 | 18 ++--
 wcs/formdef.py                                |  2 +-
 wcs/forms/common.py                           |  4 +-
 wcs/qommon/static/js/qommon.forms.js          | 32 ++++++-
 .../qommon/forms/widgets/checkboxes.html      |  2 +-
 6 files changed, 128 insertions(+), 15 deletions(-)
tests/form_pages/test_live.py
5 5
from unittest import mock
6 6

  
7 7
import pytest
8
from webtest import Hidden
8
from webtest import Checkbox, Hidden
9 9

  
10 10
from wcs import fields
11 11
from wcs.blocks import BlockDef
......
591 591
    assert len(live_resp.json['result']['3']['items']) == 1
592 592

  
593 593

  
594
def test_field_live_items_checkboxes(pub, http_requests):
595
    FormDef.wipe()
596
    formdef = FormDef()
597
    formdef.name = 'Foo'
598
    formdef.fields = [
599
        fields.StringField(type='string', id='2', label='Bar2', size='40', required=True, varname='bar2'),
600
        fields.ItemsField(
601
            type='items',
602
            id='3',
603
            label='Foo',
604
            data_source={
605
                'type': 'json',
606
                'value': '{% if form_var_bar2 %}http://remote.example.net/json-list?plop={{form_var_bar2}}{% endif %}',
607
            },
608
        ),
609
    ]
610
    formdef.store()
611
    formdef.data_class().wipe()
612

  
613
    app = get_app(pub)
614
    resp = app.get('/foo/')
615
    assert resp.html.find('div', {'data-field-id': '2'}).attrs['data-live-source'] == 'true'
616
    assert resp.html.find('div', {'data-field-id': '3'}).find('ul')
617
    assert not resp.html.find('div', {'data-field-id': '3'}).find('ul li')
618
    resp.form['f2'] = 'plop'
619
    live_resp = app.post('/foo/live?modified_field_id=2', params=resp.form.submit_fields())
620
    assert len(live_resp.json['result']['3']['items']) == 1
621
    # simulate js, add relevant checkboxes
622
    for option in live_resp.json['result']['3']['items']:
623
        checkbox_name = '%s$element%s' % (
624
            resp.html.find('div', {'data-field-id': '3'}).attrs['data-widget-name'],
625
            option['id'],
626
        )
627
        resp.form.fields[checkbox_name] = Checkbox(
628
            form=resp.form, name=checkbox_name, tag='input', value='yes', pos=10
629
        )
630
        resp.form.field_order.append((checkbox_name, resp.form.fields[checkbox_name]))
631
    resp.form.fields[checkbox_name].checked = True
632
    resp = resp.form.submit('submit')  # -> validation
633
    assert resp.pyquery('.CheckboxesWidget li label').text() == 'b'
634
    resp = resp.form.submit('submit')  # -> submitted
635
    assert formdef.data_class().select()[0].data['3'] == ['a']
636
    assert formdef.data_class().select()[0].data['3_display'] == 'b'
637

  
638

  
639
def test_field_live_items_select_multiple(pub, http_requests):
640
    FormDef.wipe()
641
    formdef = FormDef()
642
    formdef.name = 'Foo'
643
    formdef.fields = [
644
        fields.StringField(type='string', id='2', label='Bar2', size='40', required=True, varname='bar2'),
645
        fields.ItemsField(
646
            type='items',
647
            display_mode='autocomplete',
648
            id='3',
649
            label='Foo',
650
            data_source={
651
                'type': 'json',
652
                'value': '{% if form_var_bar2 %}http://remote.example.net/json-list?plop={{form_var_bar2}}{% endif %}',
653
            },
654
        ),
655
    ]
656
    formdef.store()
657
    formdef.data_class().wipe()
658

  
659
    app = get_app(pub)
660
    resp = app.get('/foo/')
661
    assert resp.html.find('div', {'data-field-id': '2'}).attrs['data-live-source'] == 'true'
662
    assert resp.html.find('div', {'data-field-id': '3'}).find('select')
663
    assert not resp.html.find('div', {'data-field-id': '3'}).find('select option')
664
    resp.form['f2'] = 'plop'
665
    live_resp = app.post('/foo/live?modified_field_id=2', params=resp.form.submit_fields())
666
    assert len(live_resp.json['result']['3']['items']) == 1
667
    # simulate js, add relevant options
668
    resp.form['f3[]'].options = [(x['id'], False, x['text']) for x in live_resp.json['result']['3']['items']]
669
    resp.form['f3[]'].select_multiple(['a'])
670
    resp = resp.form.submit('submit')  # -> validation
671
    assert resp.pyquery('select option[selected]').text() == 'b'
672
    resp = resp.form.submit('submit')  # -> submitted
673
    assert formdef.data_class().select()[0].data['3'] == ['a']
674
    assert formdef.data_class().select()[0].data['3_display'] == 'b'
675

  
676

  
594 677
def test_field_live_template_content(pub, http_requests):
595 678
    FormDef.wipe()
596 679
    formdef = FormDef()
wcs/fields.py
2066 2066
        except KeyError:
2067 2067
            return None
2068 2068

  
2069
    def get_extended_options(self):
2070
        if self.data_source:
2071
            return data_sources.get_structured_items(
2072
                self.data_source, mode='lazy', include_disabled=self.display_disabled_items
2073
            )
2074
        if self.items:
2075
            return [{'id': x, 'text': x} for x in self.items]
2076
        return []
2077

  
2069 2078

  
2070 2079
class ItemField(WidgetField, MapOptionsMixin, ItemFieldMixin):
2071 2080
    key = 'item'
......
2126 2135
            return data_sources.get_id_by_option_text(self.data_source, text_value)
2127 2136
        return text_value
2128 2137

  
2129
    def get_extended_options(self):
2130
        if self.data_source:
2131
            return data_sources.get_structured_items(
2132
                self.data_source, mode='lazy', include_disabled=self.display_disabled_items
2133
            )
2134
        if self.items:
2135
            return [{'id': x, 'text': x} for x in self.items]
2136
        return []
2137

  
2138 2138
    def get_display_mode(self, data_source=None):
2139 2139
        if not data_source:
2140 2140
            data_source = data_sources.get_object(self.data_source)
wcs/formdef.py
967 967
                    if varname not in live_condition_fields:
968 968
                        live_condition_fields[varname] = []
969 969
                    live_condition_fields[varname].append(field)
970
            if field.key == 'item' and field.data_source:
970
            if field.key in ('item', 'items') and field.data_source:
971 971
                data_source = data_sources.get_object(field.data_source)
972 972
                if data_source.type not in ('json', 'geojson') and not data_source.type.startswith(
973 973
                    'carddef:'
wcs/forms/common.py
785 785
                    break
786 786

  
787 787
        for field in displayed_fields:
788
            if field.key == 'item' and field.data_source:
788
            if field.key in ('item', 'items') and field.data_source:
789 789
                data_source = data_sources.get_object(field.data_source)
790 790
                if data_source.type not in ('json', 'geojson') and not data_source.type.startswith(
791 791
                    'carddef:'
......
793 793
                    continue
794 794
                varnames = data_source.get_referenced_varnames(field.formdef)
795 795
                if (not modified_field_varnames or modified_field_varnames.intersection(varnames)) and (
796
                    field.display_mode == 'autocomplete' and data_source.can_jsonp()
796
                    field.display_mode == 'autocomplete' and data_source.can_jsonp() and field.type != 'items'
797 797
                ):
798 798
                    # computed earlier, in perform_more_widget_changes, when the field
799 799
                    # was added to the form
wcs/qommon/static/js/qommon.forms.js
514 514
              $label.appendTo($content);
515 515
            }
516 516
            $hint.appendTo($content);
517
          } else if (value.items && $widget.is('.CheckboxesWidget')) {
518
            var widget_name = $widget.data('widget-name');
519
            var $ul = $widget.find('ul');
520
            var current_value = $ul.find('input[type=checkbox]'
521
                    ).filter(function() {return this.checked}
522
                    ).map(function() {return this.name;}
523
                    ).toArray();
524
            var base_for_name = $ul.data('base-for-name');
525
            var input_name = $widget.data('widget-name');
526
            $ul.empty();
527
            for (var i=0; i<value.items.length; i++) {
528
              var $li = $('<li>');
529
              var $label = $('<label>', {'for': base_for_name + i});
530
              var $input = $('<input>', {
531
                      type: 'checkbox', 'id': base_for_name + i,
532
                      value: 'yes', name: widget_name + '$element' + value.items[i].id});
533
              if (current_value.indexOf(widget_name + '$element' + value.items[i].id) != -1) {
534
                $input.attr('checked', 'checked');
535
              }
536
              if (value.items[i].disabled) {
537
                $input.prop('disabled', true);
538
                $li.addClass('disabled');
539
              }
540
              var $span = $('<span>', {text: value.items[i].text});
541
              $input.appendTo($label);
542
              $span.appendTo($label);
543
              $label.appendTo($li);
544
              $li.appendTo($ul);
545
            }
517 546
          } else if (value.items) {
518 547
            // replace <select> contents
519 548
            var $select = $widget.find('select');
......
527 556
            }
528 557
            for (var i=0; i<value.items.length; i++) {
529 558
              var $option = $('<option></option>', {value: value.items[i].id, text: value.items[i].text});
530
              if (value.items[i].id == current_value) {
559
              if ((Array.isArray(current_value) && current_value.indexOf(value.items[i].id.toString()) != -1) ||
560
                      value.items[i].id == current_value) {
531 561
                $option.attr('selected', 'selected');
532 562
                value.items[i].selected = true;
533 563
              }
wcs/qommon/templates/qommon/forms/widgets/checkboxes.html
1 1
{% extends "qommon/forms/widget.html" %}
2 2

  
3 3
{% block widget-control %}
4
<ul {% if widget.inline %}class="inline"{% endif %}>
4
<ul {% if widget.inline %}class="inline"{% endif %} data-base-for-name="{{ widget.get_name_for_id }}_op_">
5 5
  {% for option in widget.get_options %}
6 6
  <li {% if option.disabled %}class="disabled"{% endif %}><label for="{{ widget.get_name_for_id }}_op_{{ forloop.counter0 }}"><input
7 7
   id="{{ widget.get_name_for_id }}_op_{{ forloop.counter0 }}"
8
-