From 3990f21a74a90f00837f38238ae893b5bfc9d8c2 Mon Sep 17 00:00:00 2001 From: Dave Gouge Date: Mon, 19 Oct 2026 13:58:22 +0100 Subject: [PATCH] #641 Moved options rendering on check you data to before the data tabs. --- .../Views/CheckYourPupilData/Index.cshtml | 159 +++++++++--------- .../Web/CheckYourPupilDataViewRenderTests.cs | 17 +- 2 files changed, 96 insertions(+), 80 deletions(-) diff --git a/src/DfE.CheckPerformanceData.Web/Views/CheckYourPupilData/Index.cshtml b/src/DfE.CheckPerformanceData.Web/Views/CheckYourPupilData/Index.cshtml index 253a9394..ee66b099 100644 --- a/src/DfE.CheckPerformanceData.Web/Views/CheckYourPupilData/Index.cshtml +++ b/src/DfE.CheckPerformanceData.Web/Views/CheckYourPupilData/Index.cshtml @@ -72,6 +72,86 @@ CSV files as a ZIP file

+@* #317: the form offers whatever the open exercises offer. Nothing open means no form at all — + the tables, the search and the downloads are untouched, because a closed exercise removes + actions, never content. #641: the options sit above the tables, so a school sees what it can do + without scrolling past them. AB#298317: when results enquiry is the only thing left, the page asks + the designed Yes/No question instead of showing a one-item form. *@ +@if (Model.OffersEnquiryOnly) +{ +
+ @Html.AntiForgeryToken() + + + @* Not is-page-heading: the EditableTitle above is the page's only h1. *@ + + Would you like to report an issue with an exam result? + + + + Select one option + + + Yes + No, I'd like to sign out of this service + + + + Continue +
+} +else if (Model.AvailableNextSteps.Count == 1) +{ + @* A radio group with a single choice is a poor pattern and contradicts the "select one option" + hint, so the one surviving option becomes the button itself. The value still posts through a + hidden field, so NextStep re-derives and re-checks it exactly as it does for the radios. + Unreachable today (pupil data always offers two options and results enquiry is handled + above); kept for a future exercise type that offers exactly one. *@ + var only = Model.AvailableNextSteps[0]; + +
+ @Html.AntiForgeryToken() + + @NextStepLabels.For(only, Model.LearnerNoun) +
+} +else if (Model.AvailableNextSteps.Count > 1) +{ +
+ @Html.AntiForgeryToken() + + + @* Not is-page-heading: that renders the legend as an

, and this page already + has one (the check-pupil-data-title EditableTitle above). Two

s leave the + document with no single unambiguous title. The legend keeps the --l size so it + looks unchanged. *@ + + What would you like to do? + + + + Select one option. + + + @* "Confirm ... data is correct" is the alternative to doing anything, so it reads + last, after the "or" divider — the GDS pattern, and the order this page has always + rendered. Every other option keeps the service's exercise-sort order. *@ + @foreach (var step in Model.AvailableNextSteps.Where(s => s != NextSteps.Confirm)) + { + @NextStepLabels.For(step, Model.LearnerNoun) + } + @if (Model.AvailableNextSteps.Contains(NextSteps.Confirm)) + { + or + @NextStepLabels.For(NextSteps.Confirm, Model.LearnerNoun) + } + + + + Continue + +} + @{ // Every section on the page, so a query string rebuilt from any one tab (search, paging) // carries the others' page and search — the Results tab included, when there is one. @@ -160,82 +240,3 @@ else } } - -@* #317: the form offers whatever the open exercises offer. Nothing open means no form at all — - the tables, the search and the downloads above are untouched, because a closed exercise removes - actions, never content. AB#298317: when results enquiry is the only thing left, the page asks - the designed Yes/No question instead of showing a one-item form. *@ -@if (Model.OffersEnquiryOnly) -{ -
- @Html.AntiForgeryToken() - - - @* Not is-page-heading: the EditableTitle above is the page's only h1. *@ - - Would you like to report an issue with an exam result? - - - - Select one option - - - Yes - No, I'd like to sign out of this service - - - - Continue -
-} -else if (Model.AvailableNextSteps.Count == 1) -{ - @* A radio group with a single choice is a poor pattern and contradicts the "select one option" - hint, so the one surviving option becomes the button itself. The value still posts through a - hidden field, so NextStep re-derives and re-checks it exactly as it does for the radios. - Unreachable today (pupil data always offers two options and results enquiry is handled - above); kept for a future exercise type that offers exactly one. *@ - var only = Model.AvailableNextSteps[0]; - -
- @Html.AntiForgeryToken() - - @NextStepLabels.For(only, Model.LearnerNoun) -
-} -else if (Model.AvailableNextSteps.Count > 1) -{ -
- @Html.AntiForgeryToken() - - - @* Not is-page-heading: that renders the legend as an

, and this page already - has one (the check-pupil-data-title EditableTitle above). Two

s leave the - document with no single unambiguous title. The legend keeps the --l size so it - looks unchanged. *@ - - What would you like to do? - - - - Select one option. - - - @* "Confirm ... data is correct" is the alternative to doing anything, so it reads - last, after the "or" divider — the GDS pattern, and the order this page has always - rendered. Every other option keeps the service's exercise-sort order. *@ - @foreach (var step in Model.AvailableNextSteps.Where(s => s != NextSteps.Confirm)) - { - @NextStepLabels.For(step, Model.LearnerNoun) - } - @if (Model.AvailableNextSteps.Contains(NextSteps.Confirm)) - { - or - @NextStepLabels.For(NextSteps.Confirm, Model.LearnerNoun) - } - - - - Continue - -} diff --git a/tests/DfE.CheckPerformanceData.UnitTests/Web/CheckYourPupilDataViewRenderTests.cs b/tests/DfE.CheckPerformanceData.UnitTests/Web/CheckYourPupilDataViewRenderTests.cs index d49a0308..52327672 100644 --- a/tests/DfE.CheckPerformanceData.UnitTests/Web/CheckYourPupilDataViewRenderTests.cs +++ b/tests/DfE.CheckPerformanceData.UnitTests/Web/CheckYourPupilDataViewRenderTests.cs @@ -35,7 +35,7 @@ public void The_form_is_driven_by_the_available_options_not_by_the_window_being_ public void No_available_option_renders_no_form_at_all() { // AB#298317: the closed statement moved up into the intro paragraph (see - // The_closed_window_paragraph_…), so nothing renders below the tables when nothing is open. + // The_closed_window_paragraph_…), so no form renders when nothing is open. var view = ReadView(); Assert.DoesNotContain("closed for changes", view); @@ -45,6 +45,21 @@ public void No_available_option_renders_no_form_at_all() Assert.True(firstBranch >= 0 && firstForm > firstBranch, "no form may render before the option branches"); } + [Fact] + public void The_options_render_after_the_download_all_link_and_before_the_tabs() + { + // #641: title, intro, Download all, then the call to action, then the tables. + var view = ReadView(); + + var downloadAll = view.IndexOf("asp-action=\"DownloadAll\"", StringComparison.Ordinal); + var firstBranch = view.IndexOf("@if (Model.OffersEnquiryOnly)", StringComparison.Ordinal); + var lastForm = view.LastIndexOf("", StringComparison.Ordinal); + var firstTabs = view.IndexOf("", StringComparison.Ordinal); + + Assert.True(downloadAll >= 0 && firstBranch > downloadAll, "the options render after the Download all link"); + Assert.True(firstTabs >= 0 && lastForm < firstTabs, "every option form renders before the tabs"); + } + [Fact] public void A_single_available_option_renders_a_button_not_a_one_item_radio_group() {