From 0e21c35777a789a5625f51cb07505b64c9a097c1 Mon Sep 17 00:00:00 2001 From: Sergei Maertens Date: Wed, 3 Apr 2024 10:49:53 +0200 Subject: [PATCH] :ambulance: [DH#667] Fix editgrid recursion iter_components The feature added in #3977 to support edit grids only supported direct children as editgrids (so that the children of an edit grid itself are not yielded as standalone components), but due to missing kwarg forwarding in the recursive calls, this did not hold up when the editgrid itself is contained in a layout component like a fieldset or column. Backport-of: #4100 --- src/openforms/formio/serializers.py | 4 +- .../formio/tests/validation/test_editgrid.py | 78 +++++++++++++++++++ src/openforms/formio/utils.py | 10 ++- 3 files changed, 88 insertions(+), 4 deletions(-) diff --git a/src/openforms/formio/serializers.py b/src/openforms/formio/serializers.py index dea899300c..68350ebc0d 100644 --- a/src/openforms/formio/serializers.py +++ b/src/openforms/formio/serializers.py @@ -41,13 +41,13 @@ def apply_hidden_state( Hidden fields in Formio don't run *any* validation, see the ``Component.shouldSkipValidation`` method for reference. """ - config_wrapper = FormioConfigurationWrapper(configuration) - # initial_data is only set if Serializer(data=...) was used, and we can't take # (dynamic) hidden state into account without initial data. if not hasattr(self, "initial_data"): return + config_wrapper = FormioConfigurationWrapper(configuration) + # can't use FormioData yet because of is_visible_in_frontend values: DataMapping = self.initial_data diff --git a/src/openforms/formio/tests/validation/test_editgrid.py b/src/openforms/formio/tests/validation/test_editgrid.py index ab4bbef10c..c9affb68ed 100644 --- a/src/openforms/formio/tests/validation/test_editgrid.py +++ b/src/openforms/formio/tests/validation/test_editgrid.py @@ -219,3 +219,81 @@ def test_required_but_empty_editgrid(self): ) self.assertTrue(is_valid) + + @tag("dh-667") + def test_regression_dh_ooievaarspas(self): + # Some fields inside the repeating group apparently get/got hoisted to the + # root serializer. + components = [ + { + "type": "content", + "key": "werkgeverInfo", + "label": "werkgeverInfo", + "html": "

Als u een werkgever heeft krijgt u loon. Dit noemen we ook wel inkomen. U bent dan in loondienst.

", + }, + { + "type": "radio", + "key": "heeftUEenWerkgever", + "label": "Heeft u een werkgever?", + "validate": {"required": True}, + "openForms": {"dataSrc": "manual"}, + "values": [ + {"label": "Ja", "value": "ja"}, + {"label": "Nee", "value": "nee"}, + ], + }, + { + "type": "fieldset", + "key": "loondienstWerkgevers", + "label": "Loondienst/werkgever(s)", + "conditional": {"eq": "ja", "show": True, "when": "heeftUEenWerkgever"}, + "components": [ + { + "type": "editgrid", + "key": "werkgevers", + "label": "Werkgever(s)", + "groupLabel": "Werkgever", + "validate": {"maxLength": 4}, + "components": [ + { + "type": "textfield", + "key": "naamWerkgever", + "label": "Naam werkgever", + "validate": {"required": True}, + }, + { + "type": "currency", + "key": "nettoLoon", + "label": "Hoeveel nettoloon krijgt u ?", + "currency": "EUR", + "validate": {"required": True}, + }, + { + "type": "radio", + "key": "periodeNettoLoon", + "label": "Over welke periode ontvangt u dit loon?", + "validate": {"required": True}, + "openForms": {"dataSrc": "manual"}, + "values": [ + {"label": "Per week", "value": "week"}, + {"label": "Per 4 weken", "value": "vierWeken"}, + {"label": "Per maand", "value": "maand"}, + ], + }, + ], + } + ], + }, + ] + data = { + "heeftUEenWerkgever": "ja", + "werkgevers": [ + {"naamWerkgever": "ABC", "nettoLoon": 5, "periodeNettoLoon": "maand"} + ], + } + context = {"submission": SubmissionFactory.build()} + serializer = build_serializer(components=components, data=data, context=context) + + is_valid = serializer.is_valid() + + self.assertTrue(is_valid) diff --git a/src/openforms/formio/utils.py b/src/openforms/formio/utils.py index fa0726523e..2f729a5b06 100644 --- a/src/openforms/formio/utils.py +++ b/src/openforms/formio/utils.py @@ -36,7 +36,10 @@ def iter_components( assert not components, "Both nested components and columns found" for column in configuration["columns"]: yield from iter_components( - configuration=column, recursive=recursive, _is_root=False + configuration=column, + recursive=recursive, + _is_root=False, + recurse_into_editgrid=recurse_into_editgrid, ) for component in components: @@ -52,7 +55,10 @@ def iter_components( if component.get("type") == "editgrid" and not recurse_into_editgrid: continue yield from iter_components( - configuration=component, recursive=recursive, _is_root=False + configuration=component, + recursive=recursive, + _is_root=False, + recurse_into_editgrid=recurse_into_editgrid, )