-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #668 from cabinetoffice/feature/single-choice-fiel…
…d-type DP-544 - Add single choice question type
- Loading branch information
Showing
5 changed files
with
215 additions
and
0 deletions.
There are no files selected for viewing
92 changes: 92 additions & 0 deletions
92
Frontend/CO.CDP.OrganisationApp.Tests/Pages/Forms/FormElementSingleChoiceModelTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
using CO.CDP.OrganisationApp.Models; | ||
using CO.CDP.OrganisationApp.Pages.Forms; | ||
using FluentAssertions; | ||
using System.ComponentModel.DataAnnotations; | ||
|
||
namespace CO.CDP.OrganisationApp.Tests.Pages.Forms; | ||
|
||
public class FormElementSingleChoiceModelTest | ||
{ | ||
private readonly FormElementSingleChoiceModel _model; | ||
|
||
public FormElementSingleChoiceModelTest() | ||
{ | ||
_model = new FormElementSingleChoiceModel(); | ||
_model.Options = new FormQuestionOptions() { Choices = ["Option 1", "Option 2", "Option 3"] } ; | ||
} | ||
|
||
[Theory] | ||
[InlineData(null, null)] | ||
[InlineData(" ", null)] | ||
[InlineData("yes", null)] | ||
[InlineData("no", null)] | ||
[InlineData("Option 1", "Option 1")] | ||
[InlineData("Option 2", "Option 2")] | ||
[InlineData("Option 3", "Option 3")] | ||
public void GetAnswer_GetsExpectedFormAnswer(string? input, string? expected) | ||
{ | ||
_model.SelectedOption = input; | ||
|
||
var answer = _model.GetAnswer(); | ||
|
||
if (expected == null) | ||
{ | ||
answer.Should().BeNull(); | ||
} | ||
else | ||
{ | ||
answer.Should().NotBeNull(); | ||
answer!.OptionValue.Should().Be(expected); | ||
} | ||
} | ||
|
||
[Theory] | ||
[InlineData(null, null)] | ||
[InlineData(" ", null)] | ||
[InlineData("yes", null)] | ||
[InlineData("no", null)] | ||
[InlineData("Option 1", "Option 1")] | ||
[InlineData("Option 2", "Option 2")] | ||
[InlineData("Option 3", "Option 3")] | ||
public void SetAnswer_SetsExpectedOption(string? selectedOption, string? expected) | ||
{ | ||
var answer = new FormAnswer { OptionValue = selectedOption }; | ||
|
||
_model.SetAnswer(answer); | ||
|
||
if (expected == null) | ||
{ | ||
_model.SelectedOption.Should().BeNull(); | ||
} | ||
else | ||
{ | ||
_model.SelectedOption.Should().NotBeNull(); | ||
_model.SelectedOption.Should().Be(expected); | ||
} | ||
} | ||
|
||
[Theory] | ||
[InlineData(null, "Select an option")] | ||
[InlineData(" ", "Select an option")] | ||
[InlineData("yes", "Invalid option selected")] | ||
[InlineData("no", "Invalid option selected")] | ||
[InlineData("Option 1", null)] | ||
[InlineData("Option 2", null)] | ||
[InlineData("Option 3", null)] | ||
public void Validate_ReturnsExpectedResults(string? selectedOption, string? expectedErrorMessage) | ||
{ | ||
_model.SelectedOption = selectedOption; | ||
|
||
var validationResults = _model.Validate(new ValidationContext(_model)).ToList(); | ||
|
||
if (expectedErrorMessage != null) | ||
{ | ||
validationResults.Should().ContainSingle(); | ||
validationResults.First().ErrorMessage.Should().Be(expectedErrorMessage); | ||
} | ||
else | ||
{ | ||
validationResults.Should().BeEmpty(); | ||
} | ||
} | ||
} |
50 changes: 50 additions & 0 deletions
50
Frontend/CO.CDP.OrganisationApp/Pages/Forms/FormElementSingleChoiceModel.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
using CO.CDP.OrganisationApp.Models; | ||
using Microsoft.AspNetCore.Mvc; | ||
using System.ComponentModel.DataAnnotations; | ||
using System.Text.RegularExpressions; | ||
|
||
namespace CO.CDP.OrganisationApp.Pages.Forms; | ||
|
||
public class FormElementSingleChoiceModel : FormElementModel, IValidatableObject | ||
{ | ||
[BindProperty] | ||
public string? SelectedOption { get; set; } | ||
|
||
public override FormAnswer? GetAnswer() | ||
{ | ||
if( | ||
SelectedOption != null | ||
&& Options?.Choices != null | ||
&& Options.Choices.Contains(SelectedOption) | ||
) | ||
{ | ||
return new FormAnswer { OptionValue = SelectedOption }; | ||
} | ||
|
||
return null; | ||
} | ||
|
||
public override void SetAnswer(FormAnswer? answer) | ||
{ | ||
if (answer?.OptionValue != null && Options?.Choices != null && Options.Choices.Contains(answer.OptionValue)) | ||
{ | ||
SelectedOption = answer.OptionValue; | ||
} | ||
} | ||
|
||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) | ||
{ | ||
|
||
if (string.IsNullOrWhiteSpace(SelectedOption)) | ||
{ | ||
yield return new ValidationResult("Select an option", new[] { nameof(SelectedOption) }); | ||
yield break; | ||
} | ||
|
||
if(Options?.Choices == null || (SelectedOption != null && !Options.Choices.Contains(SelectedOption))) | ||
{ | ||
yield return new ValidationResult("Invalid option selected", new[] { nameof(SelectedOption) }); | ||
yield break; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
67 changes: 67 additions & 0 deletions
67
Frontend/CO.CDP.OrganisationApp/Pages/Forms/_FormElementSingleChoice.cshtml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
@model FormElementSingleChoiceModel | ||
|
||
@{ | ||
var hasError = ((TagBuilder)Html.ValidationMessageFor(m => m.SelectedOption)).HasInnerHtml; | ||
string ariaDescribedby = ""; | ||
|
||
if (!string.IsNullOrWhiteSpace(Model.Description)) | ||
{ | ||
ariaDescribedby += "SelectedOption-description"; | ||
} | ||
|
||
if (hasError) | ||
{ | ||
ariaDescribedby += " SelectedOption-error"; | ||
} | ||
} | ||
|
||
<div class="govuk-form-group"> | ||
<fieldset class="govuk-fieldset" aria-describedby="@ariaDescribedby"> | ||
@if (!string.IsNullOrWhiteSpace(Model.Heading)) | ||
{ | ||
<legend class="govuk-fieldset__legend govuk-fieldset__legend--l"> | ||
<h1 class="govuk-fieldset__heading"> | ||
@Model.Heading | ||
@if (!string.IsNullOrWhiteSpace(Model.Caption)) | ||
{ | ||
<span class="govuk-caption-l govuk-!-margin-bottom-3">@Model.Caption</span> | ||
} | ||
</h1> | ||
</legend> | ||
} | ||
|
||
@if (!string.IsNullOrWhiteSpace(Model.Description)) | ||
{ | ||
<div id="SelectedOption-description"> | ||
@Html.Raw(Model.Description) | ||
</div> | ||
} | ||
|
||
@if (hasError) | ||
{ | ||
<p class="govuk-error-message" id="SelectedOption-error"> | ||
<span class="govuk-visually-hidden">Error:</span> | ||
@Html.ValidationMessageFor(m => m.SelectedOption) | ||
</p> | ||
} | ||
|
||
<div class="govuk-radios" data-module="govuk-radios"> | ||
@{ | ||
var index = 0; | ||
if (Model.Options?.Choices != null ) { | ||
foreach(var value in Model.Options.Choices) | ||
{ | ||
var id = index == 0 ? "SelectedOption" : $"SelectedOption_{index}"; | ||
|
||
<div class="govuk-radios__item"> | ||
<input class="govuk-radios__input" id="@id" name="SelectedOption" type="radio" value="@value" @(Model.SelectedOption == value ? "checked": "")> | ||
<label class="govuk-label govuk-radios__label" for="@id">@value</label> | ||
</div> | ||
|
||
index++; | ||
} | ||
} | ||
} | ||
</div> | ||
</fieldset> | ||
</div> |