From 93b86651805cc7974a0f425a967d563b0e17f045 Mon Sep 17 00:00:00 2001 From: killij <51908793+killij@users.noreply.github.com> Date: Fri, 16 Feb 2024 08:32:40 +0000 Subject: [PATCH] feat: Add character count to comment input of the feedback component (#423) * Add character count to feedback control * Refactor javascript * Update feedback.js * Update _BetaBanner.cshtml * Update _Feedback.cshtml --- .../Controllers/FeedbackController.cs | 2 +- .../Views/Shared/_BetaBanner.cshtml | 2 +- .../Views/Shared/_Feedback.cshtml | 56 ++++---- .../wwwroot/javascript/components/feedback.js | 121 ++++++++++++++++++ .../wwwroot/javascript/feedback.js | 72 ----------- 5 files changed, 155 insertions(+), 98 deletions(-) create mode 100644 Childrens-Social-Care-CPD/wwwroot/javascript/components/feedback.js delete mode 100644 Childrens-Social-Care-CPD/wwwroot/javascript/feedback.js diff --git a/Childrens-Social-Care-CPD/Controllers/FeedbackController.cs b/Childrens-Social-Care-CPD/Controllers/FeedbackController.cs index 25a5c271..e3416831 100644 --- a/Childrens-Social-Care-CPD/Controllers/FeedbackController.cs +++ b/Childrens-Social-Care-CPD/Controllers/FeedbackController.cs @@ -45,7 +45,7 @@ private static bool IsModelValid(FeedbackModel model, out string pageId) pageId = pageId.Trim('/'); if (pageId.Length > 512 - || model.Comments?.Length > 500 + || model.Comments?.Length > 400 || !Regex.IsMatch(pageId, @"^[0-9a-z](\/?[0-9a-z\-])*\/?$", RegexOptions.Compiled, TimeSpan.FromSeconds(1))) { return false; diff --git a/Childrens-Social-Care-CPD/Views/Shared/_BetaBanner.cshtml b/Childrens-Social-Care-CPD/Views/Shared/_BetaBanner.cshtml index df7449e6..606a7747 100644 --- a/Childrens-Social-Care-CPD/Views/Shared/_BetaBanner.cshtml +++ b/Childrens-Social-Care-CPD/Views/Shared/_BetaBanner.cshtml @@ -7,7 +7,7 @@ BETA - This is a new service – your feedback will help us to improve it + This is a new service – your feedback will help us to improve it.

diff --git a/Childrens-Social-Care-CPD/Views/Shared/_Feedback.cshtml b/Childrens-Social-Care-CPD/Views/Shared/_Feedback.cshtml index 2e2e3871..69304ac8 100644 --- a/Childrens-Social-Care-CPD/Views/Shared/_Feedback.cshtml +++ b/Childrens-Social-Care-CPD/Views/Shared/_Feedback.cshtml @@ -6,9 +6,10 @@ @{ var contextModel = (ContextModel)ViewBag.ContextModel; + var commentsId = $"comments-{Guid.NewGuid()}"; } -
+
@if (contextModel.FeedbackSubmitted) @@ -27,37 +28,36 @@ return; } -
+
Give feedback about this page - @using (@Html.BeginForm("feedback", "Feedback", null, FormMethod.Post, true, new { id = "feedbackForm" })) - { - -
+
+ +
Use this form to provide feedback about this page. If you have more general feedback about this website, please use this feedback form.
-
+
Did you find this page useful? -
- +
- + @@ -66,31 +66,39 @@
-
-
@{ - Html.RequireScriptUrl("~/javascript/feedback.js"); + Html.RequireScriptUrl("~/javascript/components/feedback.js"); } \ No newline at end of file diff --git a/Childrens-Social-Care-CPD/wwwroot/javascript/components/feedback.js b/Childrens-Social-Care-CPD/wwwroot/javascript/components/feedback.js new file mode 100644 index 00000000..6b607f6d --- /dev/null +++ b/Childrens-Social-Care-CPD/wwwroot/javascript/components/feedback.js @@ -0,0 +1,121 @@ +class FeedbackControl { + #root + #isUsefulYesRadioButton + #isUsefulNoRadioButton + #pageInput + #feedbackForm + #cancelLink + #isUsefulQuestionGroup + #commentsInput + #commentsFormGroup + + constructor(root) { + this.#root = root + } + + init = () => { + this.#feedbackForm = this.#root.querySelector("[data-module-id=feedbackForm]") + this.#cancelLink = this.#root.querySelector("[data-module-id=cancelLink]") + this.#isUsefulYesRadioButton = this.#root.querySelector("[data-module-id=isUsefulYes]") + this.#isUsefulNoRadioButton = this.#root.querySelector("[data-module-id=isUsefulNo]") + this.#isUsefulQuestionGroup = this.#root.querySelector("[data-module-id=isUsefulQuestionGroup]") + this.#pageInput = this.#root.querySelector("[data-module-id=page]") + this.#commentsInput = this.#root.querySelector("[data-module-id=comments]") + this.#commentsFormGroup = this.#root.querySelector("[data-module-id=commentsFormGroup]") + + // Initialise the event handlers + this.#feedbackForm.addEventListener("submit", this.#handleFormSubmit) + this.#feedbackForm.addEventListener("reset", this.#handleFormReset) + this.#cancelLink.addEventListener("click", this.#resetForm) + + this.#show(this.#cancelLink) + } + + #show = element => element.style.display = "block" + #hide = element => element.style.display = "none" + + #handleFormSubmit = (event) => { + event.preventDefault() + + if (this.#validateForm()) { + const data = { + Page: this.#pageInput.value, + IsUseful: this.#isUsefulYesRadioButton.checked, + Comments: this.#commentsInput.value, + } + this.#submitFeedback(data) + this.#root.querySelector("[data-module-id=submitButton]").disabled = true + this.#hide(this.#root.querySelector("[data-module-id=controlsContainer]")) + this.#show(this.#root.querySelector("[data-module-id=thankYouMessage]")) + } + } + + #handleFormReset = () => { + // Close the detail + this.#root.querySelector("[data-module-id=feedbackDetail]").removeAttribute("open") + + // Hide the error messages + this.#isUsefulQuestionGroup.classList.remove("govuk-form-group--error") + this.#commentsFormGroup.classList.remove("govuk-form-group--error") + this.#hide(this.#root.querySelector("[data-module-id=isUsefulErrorMessage]")) + this.#hide(this.#root.querySelector("[data-module-id=commentsErrorMessage]")) + + // Executes after the form has been reset - resets the character count component + setTimeout((() => this.#commentsInput.dispatchEvent(new KeyboardEvent("keyup"))), 1); + } + + #resetForm = (event) => { + event.preventDefault() + this.#feedbackForm.reset() + } + + #submitFeedback = async (data) => { + try { + await fetch("/api/feedback", { + method: "POST", + mode: "cors", + cache: "no-cache", + credentials: "same-origin", + headers: { + "Content-Type": "application/json", + RequestVerificationToken: this.#root.querySelector("[name=__RequestVerificationToken]").value + }, + redirect: "error", + referrerPolicy: "same-origin", + body: JSON.stringify(data), + }); + } catch (e) { + console.error(e) + } + } + + #validateForm = () => { + let isValid = true + if (this.#isUsefulYesRadioButton.checked === false && this.#isUsefulNoRadioButton.checked === false) { + isValid = false + this.#isUsefulQuestionGroup.classList.add("govuk-form-group--error") + this.#show(this.#root.querySelector("[data-module-id=isUsefulErrorMessage]")) + } else { + this.#isUsefulQuestionGroup.classList.remove("govuk-form-group--error") + this.#hide(this.#root.querySelector("[data-module-id=isUsefulErrorMessage]")) + } + + if (this.#commentsInput.value.length > 400) { + isValid = false + this.#commentsFormGroup.classList.add("govuk-form-group--error") + this.#show(this.#root.querySelector("[data-module-id=commentsErrorMessage]")) + } else { + this.#commentsFormGroup.classList.remove("govuk-form-group--error") + this.#hide(this.#root.querySelector("[data-module-id=commentsErrorMessage]")) + } + + return isValid + } +} + +document.addEventListener("DOMContentLoaded", () => { + const controls = document.querySelectorAll("[data-module=feedback-module]") + controls.forEach(control => { + new FeedbackControl(control).init() + }) +}) \ No newline at end of file diff --git a/Childrens-Social-Care-CPD/wwwroot/javascript/feedback.js b/Childrens-Social-Care-CPD/wwwroot/javascript/feedback.js deleted file mode 100644 index 3c5287fa..00000000 --- a/Childrens-Social-Care-CPD/wwwroot/javascript/feedback.js +++ /dev/null @@ -1,72 +0,0 @@ -let submitButton - -const show = (element) => element.style.display = "block" -const hide = (element) => element.style.display = "none" - -addEventListener("DOMContentLoaded", () => { - submitButton = document.getElementById("submitButton") - document.getElementById("isUsefulYes").removeAttribute("required") - document.getElementById("feedbackForm").addEventListener("submit", handleFormSubmit) - show(document.getElementById("cancelButton")) -}) - -function handleFormSubmit(event) { - const isValid = validateForm() - try { - if (isValid) { - document.getElementById("isUsefulQuestionGroup").classList.remove("govuk-form-group--error") - hide(document.getElementById("was-useful-error-message")) - submitFeedback() - } else { - document.getElementById("isUsefulQuestionGroup").classList.add("govuk-form-group--error") - show(document.getElementById("was-useful-error-message")) - } - } - finally { - event.preventDefault() - } -} - -async function submitFeedback() { - const data = { - Page: document.getElementById("page").value, - IsUseful: document.getElementById("isUsefulYes").checked, - Comments: document.getElementById("feebackText").value, - } - - submitButton.disabled = true - hide(document.getElementById("controlsContainer")) - show(document.getElementById("thankYouMessage")) - - try { - await fetch("/api/feedback", { - method: "POST", - mode: "cors", - cache: "no-cache", - credentials: "same-origin", - headers: { - "Content-Type": "application/json", - RequestVerificationToken: document.getElementsByName("__RequestVerificationToken")[0].value - }, - redirect: "error", - referrerPolicy: "same-origin", - body: JSON.stringify(data), - }); - } catch { } - - return false -} - -function resetForm() { - document.getElementById("feedback-control").removeAttribute("open") - document.getElementById("isUsefulYes").checked = false - document.getElementById("isUsefulNo").checked = false - document.getElementById("feebackText").value = "" - document.getElementById("isUsefulQuestionGroup").classList.remove("govuk-form-group--error") - hide(document.getElementById("was-useful-error-message")) - return false -} - -function validateForm() { - return document.getElementById("isUsefulYes").checked === true || document.getElementById("isUsefulNo").checked === true -} \ No newline at end of file