From 8a79e635f364d1795c47e5d5d1117845b2f10bbd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Mon, 10 Jan 2022 14:25:16 +0100
Subject: [PATCH 5/5] wcs: configure card field display in case of empty value
 (#58802)

---
 combo/apps/wcs/models.py                      |  17 ++-
 .../combo/wcs/card-field-as-text.html         |   6 +
 .../combo/wcs/card-field-as-title.html        |  13 ++
 combo/apps/wcs/templates/combo/wcs/card.html  |  23 ++-
 .../wcs/manager/card-infos-cell-form.html     |  16 +++
 combo/manager/static/js/combo.manager.js      |  32 ++++-
 tests/test_wcs.py                             | 135 +++++++++++++++++-
 7 files changed, 212 insertions(+), 30 deletions(-)
 create mode 100644 combo/apps/wcs/templates/combo/wcs/card-field-as-text.html
 create mode 100644 combo/apps/wcs/templates/combo/wcs/card-field-as-title.html

diff --git a/combo/apps/wcs/models.py b/combo/apps/wcs/models.py
index ba9a5252..1dc308f6 100644
--- a/combo/apps/wcs/models.py
+++ b/combo/apps/wcs/models.py
@@ -1087,13 +1087,16 @@ class WcsCardInfosCell(CardMixin, CellBase):
             if cell.get('varname') == '@custom@':
                 if cell.get('display_mode') == 'value':
                     cell['display_mode'] = 'text'
-            elif not cell.get('field_content'):
-                if cell.get('display_mode') == 'title':
-                    cell['field_content'] = 'value'
-                else:
-                    cell['field_content'] = cell.get('display_mode') or 'value'
-                if cell.get('display_mode') in ['label', 'value', 'label-and-value']:
-                    cell['display_mode'] = 'text'
+            else:
+                if not cell.get('field_content'):
+                    if cell.get('display_mode') == 'title':
+                        cell['field_content'] = 'value'
+                    else:
+                        cell['field_content'] = cell.get('display_mode') or 'value'
+                    if cell.get('display_mode') in ['label', 'value', 'label-and-value']:
+                        cell['display_mode'] = 'text'
+                if not cell.get('empty_value'):
+                    cell['empty_value'] = '@empty@'
 
         return custom_schema
 
diff --git a/combo/apps/wcs/templates/combo/wcs/card-field-as-text.html b/combo/apps/wcs/templates/combo/wcs/card-field-as-text.html
new file mode 100644
index 00000000..6d68a1a2
--- /dev/null
+++ b/combo/apps/wcs/templates/combo/wcs/card-field-as-text.html
@@ -0,0 +1,6 @@
+{% if item.field_content == "label" or item.field_content == "label-and-value" %}
+  <p class="label">{{ field.label }}</p>
+{% endif %}
+{% if item.field_content == "value" or item.field_content == "label-and-value" %}
+  {% include "combo/wcs/card-field-value.html" %}
+{% endif %}
diff --git a/combo/apps/wcs/templates/combo/wcs/card-field-as-title.html b/combo/apps/wcs/templates/combo/wcs/card-field-as-title.html
new file mode 100644
index 00000000..9a562200
--- /dev/null
+++ b/combo/apps/wcs/templates/combo/wcs/card-field-as-title.html
@@ -0,0 +1,13 @@
+{% if item.field_content == "label" %}
+  {% if value or item.empty_value != '@skip@' %}
+    <{{ title_tag }}>{{ field.label }}</{{ title_tag }}>
+  {% endif %}
+{% elif item.field_content == "value" %}
+  {% if value or item.empty_value == '@empty@' %}
+    <{{ title_tag }}>{% include "combo/wcs/card-field-value.html" with mode="inline" %}</{{ title_tag }}>
+  {% elif item.empty_value != '@skip@' and item.empty_value != '@empty@' %}
+    {% with item.empty_value as value %}
+      <{{ title_tag }}>{% include "combo/wcs/card-field-value.html" with mode="inline" %}</{{ title_tag }}>
+    {% endwith %}
+  {% endif %}
+{% endif %}
diff --git a/combo/apps/wcs/templates/combo/wcs/card.html b/combo/apps/wcs/templates/combo/wcs/card.html
index f3b7bd32..a7a88350 100644
--- a/combo/apps/wcs/templates/combo/wcs/card.html
+++ b/combo/apps/wcs/templates/combo/wcs/card.html
@@ -32,23 +32,16 @@
           {% with fields_by_varnames|get:item.varname as field %}
           {% with card.fields|get:item.varname as value %}
             {% if item.display_mode == "title" %}
-              {% if item.field_content == "label" %}
-              <h3>{{ field.label }}</h3>
-              {% elif item.field_content == "value" %}
-              <h3>{% include "combo/wcs/card-field-value.html" with mode="inline" %}</h3>
-              {% endif %}
+              {% include "combo/wcs/card-field-as-title.html" with title_tag="h3" %}
             {% elif item.display_mode == "subtitle" %}
-              {% if item.field_content == "label" %}
-              <h4>{{ field.label }}</h4>
-              {% elif item.field_content == "value" %}
-              <h4>{% include "combo/wcs/card-field-value.html" with mode="inline" %}</h4>
-              {% endif %}
+              {% include "combo/wcs/card-field-as-title.html" with title_tag="h4" %}
             {% elif item.display_mode == "text" %}
-              {% if item.field_content == "label" or item.field_content == "label-and-value" %}
-                <p class="label">{{ field.label }}</p>
-              {% endif %}
-              {% if item.field_content == "value" or item.field_content == "label-and-value" %}
-                {% include "combo/wcs/card-field-value.html" %}
+              {% if value or item.empty_value == '@empty@' %}
+                {% include "combo/wcs/card-field-as-text.html" %}
+              {% elif item.empty_value != '@skip@' and item.empty_value != '@empty@' %}
+                {% with item.empty_value as value %}
+                  {% include "combo/wcs/card-field-as-text.html" %}
+                {% endwith %}
               {% endif %}
             {% endif %}
           {% endwith %}
diff --git a/combo/apps/wcs/templates/combo/wcs/manager/card-infos-cell-form.html b/combo/apps/wcs/templates/combo/wcs/manager/card-infos-cell-form.html
index f954dd19..0abdf8dc 100644
--- a/combo/apps/wcs/templates/combo/wcs/manager/card-infos-cell-form.html
+++ b/combo/apps/wcs/templates/combo/wcs/manager/card-infos-cell-form.html
@@ -73,6 +73,22 @@
         </select>
       </label>
     </p>
+    <p>
+      <label>
+        {% trans "Empty value display mode" %}
+        <select name="field_empty_display_mode" data-dynamic-display-child-of="entry_type" data-dynamic-display-value="@field@">
+          <option value="@empty@">{% trans "Display as empty" %}</option>
+          <option value="@skip@">{% trans "Hide" %}</option>
+          <option value="@custom@">{% trans "Display a custom text" %}</option>
+        </select>
+      </label>
+    </p>
+    <p>
+      <label>
+        {% trans "Empty value custom text" %}
+        <input name="field_empty_text" data-dynamic-display-child-of="entry_type" data-dynamic-display-value="@field@" />
+      </label>
+    </p>
     <p>
       <label>
         {% trans "Value template" %}
diff --git a/combo/manager/static/js/combo.manager.js b/combo/manager/static/js/combo.manager.js
index 9d6b4b91..3893e8db 100644
--- a/combo/manager/static/js/combo.manager.js
+++ b/combo/manager/static/js/combo.manager.js
@@ -493,9 +493,11 @@ Card_cell_custom.prototype = {
     this.grid_cell_form.field_varname= this.grid_cell_form[1];
     this.grid_cell_form.field_content = this.grid_cell_form[2];
     this.grid_cell_form.field_display_mode = this.grid_cell_form[3];
-    this.grid_cell_form.custom_template = this.grid_cell_form[4];
-    this.grid_cell_form.custom_display_mode = this.grid_cell_form[5];
-    this.grid_cell_form.size = this.grid_cell_form[6];
+    this.grid_cell_form.field_empty_display_mode = this.grid_cell_form[4];
+    this.grid_cell_form.field_empty_text = this.grid_cell_form[5];
+    this.grid_cell_form.custom_template = this.grid_cell_form[6];
+    this.grid_cell_form.custom_display_mode = this.grid_cell_form[7];
+    this.grid_cell_form.size = this.grid_cell_form[8];
     const varname_select = this.grid_cell_form.field_varname;
     this.cardSchema.fields.forEach(function(el, id) {
       if (el.varname) {
@@ -514,6 +516,14 @@ Card_cell_custom.prototype = {
         $(_self.grid_cell_form.field_display_mode).addClass('field-hidden').parent().parent().show();
       }
     });
+    const field_empty_display_mode_select = this.grid_cell_form.field_empty_display_mode;
+    $(field_empty_display_mode_select).on('change', function() {
+      if ($(this).hasClass('field-hidden') || $(this).val() != '@custom@') {
+        $(_self.grid_cell_form.field_empty_text).removeClass('field-hidden').parent().parent().hide();
+      } else {
+        $(_self.grid_cell_form.field_empty_text).addClass('field-hidden').parent().parent().show();
+      }
+    });
   },
   grid_cell__form_dialog: function(button_label, callback){
     const _self = this;
@@ -523,6 +533,7 @@ Card_cell_custom.prototype = {
       open: function( event, ui ) {
         $(document).trigger('combo:dialog-loaded');
         $(_self.grid_cell_form.field_content).change();
+        $(_self.grid_cell_form.field_empty_display_mode).change();
       },
       buttons: [
         {
@@ -613,6 +624,8 @@ Card_cell_custom.prototype = {
       this.grid_cell_form.field_varname.value = '';
       this.grid_cell_form.field_content.value = '';
       this.grid_cell_form.field_display_mode.value = '';
+      this.grid_cell_form.field_empty_display_mode.value = '';
+      this.grid_cell_form.field_empty_display_mode_select.value = '';
       this.grid_cell_form.custom_template.value = grid_cell.dataset.template || '';
       this.grid_cell_form.custom_display_mode.value = grid_cell.dataset.display_mode;
     } else {
@@ -624,6 +637,13 @@ Card_cell_custom.prototype = {
       } else {
         this.grid_cell_form.field_display_mode.value = grid_cell.dataset.display_mode;
       }
+      if (grid_cell.dataset.empty_value != '@skip@' && grid_cell.dataset.empty_value != '@empty@') {
+        this.grid_cell_form.field_empty_display_mode.value = '@custom@';
+        this.grid_cell_form.field_empty_text.value = grid_cell.dataset.empty_value || '';
+      } else {
+        this.grid_cell_form.field_empty_display_mode.value = grid_cell.dataset.empty_value;
+        this.grid_cell_form.field_empty_text.value = '';
+      }
       this.grid_cell_form.custom_template.value = '';
       this.grid_cell_form.custom_display_mode.value = '';
     }
@@ -640,12 +660,18 @@ Card_cell_custom.prototype = {
     if (form_datas.entry_type == '@custom@') {
       schema_cell.varname = '@custom@';
       delete schema_cell.field_content;
+      delete schema_cell.empty_value;
       schema_cell.display_mode = form_datas.custom_display_mode;
       schema_cell.template = form_datas.custom_template;
     } else {
       schema_cell.varname = form_datas.field_varname;
       schema_cell.field_content = form_datas.field_content;
       schema_cell.display_mode = form_datas.field_display_mode;
+      if (form_datas.field_empty_display_mode == '@custom@') {
+        schema_cell.empty_value = form_datas.field_empty_text;
+      } else {
+        schema_cell.empty_value = form_datas.field_empty_display_mode;
+      }
       delete schema_cell.template;
     }
     schema_cell.cell_size = form_datas.cell_size;
diff --git a/tests/test_wcs.py b/tests/test_wcs.py
index 79b46384..fd661ec9 100644
--- a/tests/test_wcs.py
+++ b/tests/test_wcs.py
@@ -193,6 +193,7 @@ WCS_CARDDEF_SCHEMAS = {
             {'label': 'Field F', 'varname': 'fieldf', 'type': 'text', 'pre': True},
             {'label': 'Field G', 'varname': 'fieldg', 'type': 'email'},
             {'label': 'Field H', 'varname': 'fieldh', 'type': 'string'},
+            {'label': 'Empty', 'varname': 'empty', 'type': 'string'},
             {'label': 'Related', 'varname': 'related', 'type': 'item'},
             {'label': 'Page', 'type': 'page'},
             {'label': 'Comment', 'type': 'comment'},
@@ -1712,7 +1713,9 @@ def test_card_cell_setup(mock_send, app, admin_user):
     assert 'custom_schema' in form.fields
     assert form.initial['customize_display'] is True
     assert form.initial['custom_schema'] == {
-        'cells': [{'varname': 'foo', 'field_content': 'value', 'display_mode': 'text'}]
+        'cells': [
+            {'varname': 'foo', 'field_content': 'value', 'display_mode': 'text', 'empty_value': '@empty@'}
+        ]
     }
 
     app = login(app)
@@ -1732,20 +1735,47 @@ def test_card_cell_custom_schema_migration():
     }
     assert cell.get_custom_schema() == {
         'cells': [
-            {'varname': 'some-field', 'field_content': 'label', 'display_mode': 'text', 'cell_size': 'foobar'}
+            {
+                'varname': 'some-field',
+                'field_content': 'label',
+                'display_mode': 'text',
+                'empty_value': '@empty@',
+                'cell_size': 'foobar',
+            }
         ]
     }
     cell.custom_schema = {'cells': [{'varname': 'some-field', 'display_mode': 'value'}]}
     assert cell.get_custom_schema() == {
-        'cells': [{'varname': 'some-field', 'field_content': 'value', 'display_mode': 'text'}]
+        'cells': [
+            {
+                'varname': 'some-field',
+                'field_content': 'value',
+                'display_mode': 'text',
+                'empty_value': '@empty@',
+            }
+        ]
     }
     cell.custom_schema = {'cells': [{'varname': 'some-field', 'display_mode': 'label-and-value'}]}
     assert cell.get_custom_schema() == {
-        'cells': [{'varname': 'some-field', 'field_content': 'label-and-value', 'display_mode': 'text'}]
+        'cells': [
+            {
+                'varname': 'some-field',
+                'field_content': 'label-and-value',
+                'display_mode': 'text',
+                'empty_value': '@empty@',
+            }
+        ]
     }
     cell.custom_schema = {'cells': [{'varname': 'some-field', 'display_mode': 'title'}]}
     assert cell.get_custom_schema() == {
-        'cells': [{'varname': 'some-field', 'field_content': 'value', 'display_mode': 'title'}]
+        'cells': [
+            {
+                'varname': 'some-field',
+                'field_content': 'value',
+                'display_mode': 'title',
+                'empty_value': '@empty@',
+            }
+        ]
     }
 
     cell.custom_schema = {
@@ -2217,6 +2247,101 @@ def test_card_cell_render_custom_schema_card_field(mock_send, context):
     assert PyQuery(result).find('span.value a').attr['href'] == 'https://www.example.net/'
 
 
+@mock.patch('combo.apps.wcs.utils.requests.send', side_effect=mocked_requests_send)
+def test_card_cell_render_custom_schema_card_empty_field(mock_send, context):
+    page = Page.objects.create(title='xxx', template_name='standard')
+    cell = WcsCardInfosCell.objects.create(
+        page=page,
+        placeholder='content',
+        order=0,
+        carddef_reference='default:card_model_1',
+        custom_schema={
+            'cells': [
+                {
+                    'varname': 'empty',
+                    'field_content': 'label-and-value',
+                    'display_mode': 'text',
+                    'empty_value': '@skip@',
+                }
+            ]
+        },
+    )
+
+    context['card_model_1_id'] = 11
+    request = RequestFactory().get('/')
+    cell.modify_global_context(context, request)
+    cell.repeat_index = 0
+    context['synchronous'] = True  # to get fresh content
+
+    result = cell.render(context)
+    assert PyQuery(result).find('p.label') == []
+    assert PyQuery(result).find('span.value') == []
+
+    cell.custom_schema['cells'][0] = {
+        'varname': 'empty',
+        'field_content': 'label-and-value',
+        'display_mode': 'text',
+        'empty_value': '@empty@',
+    }
+    cell.save()
+    result = cell.render(context)
+    assert PyQuery(result).find('p.label').text() == 'Empty'
+    assert PyQuery(result).find('span.value').text() == ''
+
+    cell.custom_schema['cells'][0] = {
+        'varname': 'empty',
+        'field_content': 'label-and-value',
+        'display_mode': 'text',
+        'empty_value': 'Custom text',
+    }
+    cell.save()
+    result = cell.render(context)
+    assert PyQuery(result).find('p.label').text() == 'Empty'
+    assert PyQuery(result).find('span.value').text() == 'Custom text'
+
+    for field_content in ['label', 'value']:
+        for display_mode in ['text', 'title', 'subtitle']:
+            if display_mode == 'title':
+                html_tag = 'h3'
+            elif display_mode == 'subtitle':
+                html_tag = 'h4'
+            elif display_mode == 'text' and field_content == 'label':
+                html_tag = 'p.label'
+            elif display_mode == 'text' and field_content == 'value':
+                html_tag = 'span.value'
+            cell.custom_schema['cells'][0] = {
+                'varname': 'empty',
+                'field_content': field_content,
+                'display_mode': display_mode,
+                'empty_value': '@skip@',
+            }
+            cell.save()
+            result = cell.render(context)
+            assert PyQuery(result).find(html_tag) == []
+
+            cell.custom_schema['cells'][0] = {
+                'varname': 'empty',
+                'field_content': field_content,
+                'display_mode': display_mode,
+                'empty_value': '@empty@',
+            }
+            cell.save()
+            result = cell.render(context)
+            assert PyQuery(result).find(html_tag).text() == ('Empty' if field_content == 'label' else '')
+
+            cell.custom_schema['cells'][0] = {
+                'varname': 'empty',
+                'field_content': field_content,
+                'display_mode': display_mode,
+                'empty_value': 'Custom text',
+            }
+            cell.save()
+            result = cell.render(context)
+            assert PyQuery(result).find(html_tag).text() == (
+                'Empty' if field_content == 'label' else 'Custom text'
+            )
+
+
 @mock.patch('combo.apps.wcs.utils.requests.send', side_effect=mocked_requests_send)
 def test_card_cell_render_custom_schema_custom_entry(mock_send, context, app):
     page = Page.objects.create(title='xxx', template_name='standard')
-- 
2.34.1

