Skip to content

Commit 57e491d

Browse files
authored
Merge pull request #729 from NHSDigital/dtoss-11446-support-exclusive-choices
Add param to mark options exclusive
2 parents 6c1c4e0 + 0c524e6 commit 57e491d

File tree

3 files changed

+63
-0
lines changed

3 files changed

+63
-0
lines changed

manage_breast_screening/nhsuk_forms/fields/choice_fields.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from django import forms
22
from django.forms import widgets
33

4+
from manage_breast_screening.nhsuk_forms.validators import ExcludesOtherOptionsValidator
5+
46

57
class RadioSelectWithoutFieldset(widgets.RadioSelect):
68
use_fieldset = False
@@ -23,6 +25,7 @@ def __init__(self, form: forms.Form, field: "ChoiceField", name: str):
2325

2426
self._conditional_html = {}
2527
self.dividers = {}
28+
self._exclusive_options = set()
2629

2730
def add_conditional_html(self, value, html):
2831
if isinstance(self.field.widget, widgets.Select):
@@ -106,6 +109,7 @@ def __init__(
106109
label_classes="nhsuk-fieldset__legend--m",
107110
visually_hidden_label_prefix=None,
108111
visually_hidden_label_suffix=None,
112+
exclusive_choices=(),
109113
classes=None,
110114
**kwargs,
111115
):
@@ -116,5 +120,21 @@ def __init__(
116120
self.label_classes = label_classes
117121
self.visually_hidden_label_prefix = visually_hidden_label_prefix
118122
self.visually_hidden_label_suffix = visually_hidden_label_suffix
123+
self.exclusive_choices = exclusive_choices
119124

120125
super().__init__(*args, **kwargs)
126+
127+
choice_labels = {choice: label for choice, label in self.choices}
128+
129+
for exclusive_choice in self.exclusive_choices:
130+
try:
131+
label = choice_labels[exclusive_choice]
132+
except KeyError:
133+
raise ValueError(f"{exclusive_choice} is not in choices")
134+
135+
self.validators.append(
136+
ExcludesOtherOptionsValidator(exclusive_choice, label)
137+
)
138+
139+
def is_exclusive(self, value):
140+
return value in self.exclusive_choices

manage_breast_screening/nhsuk_forms/jinja2/forms/checkboxes.jinja

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"value": value,
1313
"text": text,
1414
"checked": value in (field.value() or []),
15+
"exclusive": unbound_field.is_exclusive(value),
1516
"conditional": {
1617
"html": conditional_html
1718
} if conditional_html else undefined

manage_breast_screening/nhsuk_forms/tests/fields/test_choice_fields.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import pytest
22
from django.forms import Form
33
from django.forms.widgets import CheckboxSelectMultiple, Select
4+
from django.http import QueryDict
45
from pytest_django.asserts import assertHTMLEqual
56

67
from manage_breast_screening.nhsuk_forms.fields.choice_fields import (
@@ -197,6 +198,9 @@ class TestForm(Form):
197198
hint="Pick any number",
198199
widget=CheckboxSelectMultiple,
199200
)
201+
checkbox_field_with_exclusive_option = MultipleChoiceField(
202+
label="Abc", choices=(("a", "A"), ("b", "B")), exclusive_choices={"b"}
203+
)
200204
details = CharField(label="Abc", initial="")
201205

202206
return TestForm
@@ -261,6 +265,44 @@ def test_renders_nhs_checkboxes_with_conditional_html(self, form_class):
261265
""",
262266
)
263267

268+
def test_renders_exclusive_options(self, form_class):
269+
form = form_class()
270+
271+
assertHTMLEqual(
272+
form["checkbox_field_with_exclusive_option"].as_field_group(),
273+
"""
274+
<div class="nhsuk-form-group">
275+
<fieldset class="nhsuk-fieldset">
276+
<legend class="nhsuk-fieldset__legend nhsuk-fieldset__legend--m">
277+
Abc
278+
</legend>
279+
<div class="nhsuk-checkboxes" data-module="nhsuk-checkboxes">
280+
<div class="nhsuk-checkboxes__item">
281+
<input class="nhsuk-checkboxes__input" id="id_checkbox_field_with_exclusive_option" name="checkbox_field_with_exclusive_option" type="checkbox" value="a">
282+
<label class="nhsuk-label nhsuk-checkboxes__label" for="id_checkbox_field_with_exclusive_option">A</label>
283+
</div>
284+
<div class="nhsuk-checkboxes__item">
285+
<input class="nhsuk-checkboxes__input" id="id_checkbox_field_with_exclusive_option-2" name="checkbox_field_with_exclusive_option" type="checkbox" value="b" data-checkbox-exclusive>
286+
<label class="nhsuk-label nhsuk-checkboxes__label" for="id_checkbox_field_with_exclusive_option-2">B</label>
287+
</div>
288+
</div>
289+
</fieldset>
290+
</div>
291+
""",
292+
)
293+
294+
def test_exclusive_options_are_validated(self, form_class):
295+
form = form_class(
296+
QueryDict(
297+
"checkbox_field=a&details=abc&checkbox_field_with_exclusive_option=a&checkbox_field_with_exclusive_option=b"
298+
)
299+
)
300+
assert form.errors == {
301+
"checkbox_field_with_exclusive_option": [
302+
'Unselect "B" in order to select other options'
303+
]
304+
}
305+
264306
def test_renders_without_fieldset(self, form_class):
265307
class TestForm(Form):
266308
checkbox_field = MultipleChoiceField(

0 commit comments

Comments
 (0)