diff --git a/app/main/forms.py b/app/main/forms.py index 6e5cdba88..cc5c5d436 100644 --- a/app/main/forms.py +++ b/app/main/forms.py @@ -688,33 +688,139 @@ class RegisterUserFromOrgInviteForm(StripWhitespaceForm): auth_type = HiddenField('auth_type', validators=[DataRequired()]) -class GovukCheckboxesMixin: +def extend_params(params, extensions): + items = None + param_items = len(params['items']) if 'items' in params else 0 - def extend_params(self, params, extensions): - items = None - param_items = len(params['items']) if 'items' in params else 0 + # split items off from params to make it a pure dict + if 'items' in extensions: + items = extensions['items'] + del extensions['items'] - # split items off from params to make it a pure dict - if 'items' in extensions: - items = extensions['items'] - del extensions['items'] + # merge dicts + merge_jsonlike(params, extensions) - # merge dicts - merge_jsonlike(params, extensions) - - # merge items - if items: - if 'items' not in params: - params['items'] = items - else: - for idx, _item in enumerate(items): - if idx >= param_items: - params['items'].append(items[idx]) - else: - params['items'][idx].update(items[idx]) + # merge items + if items: + if 'items' not in params: + params['items'] = items + else: + for idx, _item in enumerate(items): + if idx >= param_items: + params['items'].append(items[idx]) + else: + params['items'][idx].update(items[idx]) -class GovukCheckboxField(GovukCheckboxesMixin, BooleanField): +def govuk_checkbox_field_widget(self, field, param_extensions=None, **kwargs): + + # error messages + error_message = None + if field.errors: + error_message = { + "attributes": { + "data-module": "track-error", + "data-error-type": field.errors[0], + "data-error-label": field.name + }, + "text": " ".join(field.errors).strip() + } + + params = { + 'name': field.name, + 'errorMessage': error_message, + 'items': [ + { + "name": field.name, + "id": field.id, + "text": field.label.text, + "value": 'y', + "checked": field.data + } + ] + + } + + # extend default params with any sent in during instantiation + if self.param_extensions: + extend_params(params, self.param_extensions) + + # add any sent in though use in templates + if param_extensions: + extend_params(params, param_extensions) + + return Markup( + render_template('forms/fields/checkboxes/macro.njk', params=params)) + + +def govuk_checkboxes_field_widget(self, field, wrap_in_collapsible=False, param_extensions=None, **kwargs): + + def _wrap_in_collapsible(field_label, checkboxes_string): + # wrap the checkboxes HTML in the HTML needed by the collapisble JS + result = Markup( + f'
' + f' {checkboxes_string}' + f'
' + ) + + return result + + # error messages + error_message = None + if field.errors: + error_message = { + "attributes": { + "data-module": "track-error", + "data-error-type": field.errors[0], + "data-error-label": field.name + }, + "text": " ".join(field.errors).strip() + } + + # returns either a list or a hierarchy of lists + # depending on how get_items_from_options is implemented + items = self.get_items_from_options(field) + + params = { + 'name': field.name, + "fieldset": { + "attributes": {"id": field.name}, + "legend": { + "text": field.label.text, + "classes": "govuk-fieldset__legend--s" + } + }, + "asList": self.render_as_list, + 'errorMessage': error_message, + 'items': items + } + + # extend default params with any sent in during instantiation + if self.param_extensions: + extend_params(params, self.param_extensions) + + # add any sent in though use in templates + if param_extensions: + extend_params(params, param_extensions) + + if wrap_in_collapsible: + + # add a blank hint to act as an ARIA live-region + params.update( + {"hint": {"html": "
"}}) + + return _wrap_in_collapsible( + self.field_label, + Markup(render_template('forms/fields/checkboxes/macro.njk', params=params)) + ) + else: + return Markup( + render_template('forms/fields/checkboxes/macro.njk', params=params)) + + +class GovukCheckboxField(BooleanField): def __init__(self, label='', validators=None, param_extensions=None, **kwargs): super(GovukCheckboxField, self).__init__(label, validators, false_values=None, **kwargs) @@ -725,48 +831,11 @@ class GovukCheckboxField(GovukCheckboxesMixin, BooleanField): # 2. calls field.widget # this bypasses that by making self.widget a method with the same interface as widget.__call__ def widget(self, field, param_extensions=None, **kwargs): - - # error messages - error_message = None - if field.errors: - error_message = { - "attributes": { - "data-module": "track-error", - "data-error-type": field.errors[0], - "data-error-label": field.name - }, - "text": " ".join(field.errors).strip() - } - - params = { - 'name': field.name, - 'errorMessage': error_message, - 'items': [ - { - "name": field.name, - "id": field.id, - "text": field.label.text, - "value": 'y', - "checked": field.data - } - ] - - } - - # extend default params with any sent in during instantiation - if self.param_extensions: - self.extend_params(params, self.param_extensions) - - # add any sent in though use in templates - if param_extensions: - self.extend_params(params, param_extensions) - - return Markup( - render_template('forms/fields/checkboxes/macro.njk', params=params)) + return govuk_checkbox_field_widget(self, field, param_extensions=param_extensions, **kwargs) # based on work done by @richardjpope: https://github.com/richardjpope/recourse/blob/master/recourse/forms.py#L6 -class GovukCheckboxesField(GovukCheckboxesMixin, SelectMultipleField): +class GovukCheckboxesField(SelectMultipleField): render_as_list = False @@ -791,84 +860,24 @@ class GovukCheckboxesField(GovukCheckboxesMixin, SelectMultipleField): # 2. calls field.widget # this bypasses that by making self.widget a method with the same interface as widget.__call__ def widget(self, field, param_extensions=None, **kwargs): - - # error messages - error_message = None - if field.errors: - error_message = { - "attributes": { - "data-module": "track-error", - "data-error-type": field.errors[0], - "data-error-label": field.name - }, - "text": " ".join(field.errors).strip() - } - - # returns either a list or a hierarchy of lists - # depending on how get_items_from_options is implemented - items = self.get_items_from_options(field) - - params = { - 'name': field.name, - "fieldset": { - "attributes": {"id": field.name}, - "legend": { - "text": field.label.text, - "classes": "govuk-fieldset__legend--s" - } - }, - "asList": self.render_as_list, - 'errorMessage': error_message, - 'items': items - } - - # extend default params with any sent in during instantiation - if self.param_extensions: - self.extend_params(params, self.param_extensions) - - # add any sent in though use in templates - if param_extensions: - self.extend_params(params, param_extensions) - - return Markup( - render_template('forms/fields/checkboxes/macro.njk', params=params)) + return govuk_checkboxes_field_widget(self, field, param_extensions=param_extensions, **kwargs) -# Extends fields using the GovukCheckboxesField interface to wrap their render in HTML needed by the collapsible JS -class GovukCollapsibleCheckboxesMixin: +# Wraps checkboxes rendering in HTML needed by the collapsible JS +class GovukCollapsibleCheckboxesField(GovukCheckboxesField): def __init__(self, label='', validators=None, field_label='', param_extensions=None, **kwargs): - super(GovukCollapsibleCheckboxesMixin, self).__init__(label, validators, param_extensions, **kwargs) + super(GovukCollapsibleCheckboxesField, self).__init__(label, validators, param_extensions, **kwargs) self.field_label = field_label def widget(self, field, **kwargs): - - # add a blank hint to act as an ARIA live-region - if self.param_extensions is not None: - self.param_extensions.update( - {"hint": {"html": "
"}}) - else: - self.param_extensions = \ - {"hint": {"html": "
"}} - - # wrap the checkboxes HTML in the HTML needed by the collapisble JS - return Markup( - f'
' - f' {super(GovukCollapsibleCheckboxesMixin, self).widget(field, **kwargs)}' - f'
' - ) + return govuk_checkboxes_field_widget(self, field, wrap_in_collapsible=True, param_extensions=None, **kwargs) -class GovukCollapsibleCheckboxesField(GovukCollapsibleCheckboxesMixin, GovukCheckboxesField): - pass - - -# GovukCollapsibleCheckboxesMixin adds an ARIA live-region to the hint and wraps the render in HTML needed by the +# GovukCollapsibleCheckboxesField adds an ARIA live-region to the hint and wraps the render in HTML needed by the # collapsible JS # NestedFieldMixin puts the items into a tree hierarchy, pre-rendering the sub-trees of the top-level items -class GovukCollapsibleNestedCheckboxesField(GovukCollapsibleCheckboxesMixin, NestedFieldMixin, GovukCheckboxesField): +class GovukCollapsibleNestedCheckboxesField(NestedFieldMixin, GovukCollapsibleCheckboxesField): NONE_OPTION_VALUE = None render_as_list = True