From f71e85e1ea81cc842b2edcfbfb1e7bb104d4d9bd Mon Sep 17 00:00:00 2001 From: Auldrin Possa Date: Fri, 9 May 2025 15:27:32 +0100 Subject: [PATCH] TD-5453-Code added/updated to remove Wave, axe DevTools and Lighthouse issues --- .../Scripts/frameworks/htmleditor.ts | 74 +++++++- .../Styles/jodit.scss | 3 + .../Frameworks/Developer/Competency.cshtml | 174 +++++++++--------- .../Developer/Import/ImportCompleted.cshtml | 8 +- 4 files changed, 168 insertions(+), 91 deletions(-) diff --git a/DigitalLearningSolutions.Web/Scripts/frameworks/htmleditor.ts b/DigitalLearningSolutions.Web/Scripts/frameworks/htmleditor.ts index 129697c0af..3952d26df3 100644 --- a/DigitalLearningSolutions.Web/Scripts/frameworks/htmleditor.ts +++ b/DigitalLearningSolutions.Web/Scripts/frameworks/htmleditor.ts @@ -70,14 +70,19 @@ if (jodited === false) { const clean = DOMPurify.sanitize(editor.editor.innerHTML); editor.editor.innerHTML = clean; }); + + document.addEventListener('DOMContentLoaded', () => { + removeWaveErrors(); + removeDevToolsIssues(); + }); + + // ** Start* for jodit editor error (display red outline, focus on summary error text click) **** const textarea = document.querySelector('.nhsuk-textarea.html-editor.nhsuk-input--error') as HTMLTextAreaElement | null; if (textarea) { const editorDiv = document.querySelector('.jodit-container.jodit.jodit_theme_default.jodit-wysiwyg_mode') as HTMLDivElement | null; editorDiv?.classList.add('jodit-container', 'jodit', 'jodit_theme_default', 'jodit-wysiwyg_mode', 'jodit-error'); } - const summary = document.querySelector('.nhsuk-list.nhsuk-error-summary__list') as HTMLDivElement | null; - if (summary) { summary.addEventListener('click', (e: Event) => { if (textarea) { @@ -96,5 +101,70 @@ if (jodited === false) { } }); } + // ** End* for jodit editor error (display red outline, focus on summary error text click) **** + } +} + +function removeWaveErrors() { + const input = Array.from(document.querySelectorAll('input[tab-index="-1"]')) + .find((el) => el.style.width === '0px' && el.style.height === '0px' + && el.style.position === 'absolute' && el.style.visibility === 'hidden'); + + if (input) { + input.setAttribute('aria-label', 'Hidden input for accessibility'); + input.setAttribute('title', 'HiddenInput'); + } + + const observer = new MutationObserver((mutations, obs) => { + const textarea = document.querySelector('.ace_text-input') as HTMLTextAreaElement | null; + if (textarea) { + textarea.setAttribute('aria-label', 'ace_text-input'); + obs.disconnect(); + } + }); + observer.observe(document.body, { + childList: true, + subtree: true, + }); +} +function removeDevToolsIssues() { + // set role = 'list' to toolbar + const toolbarbox = document.querySelector('.jodit-toolbar__box') as HTMLElement | null; + if (toolbarbox) { + toolbarbox.setAttribute('role', 'list'); + } + // set role = 'list' to statusbar + const statusbar = document.querySelector('.jodit-xpath') as HTMLElement | null; + if (statusbar) { + statusbar.setAttribute('role', 'list'); } + document.querySelectorAll('.jodit-toolbar-button__trigger').forEach((el) => { + el.removeAttribute('role'); + }); + // observer to detect role='trigger' and remove role + const observer = new MutationObserver(() => { + document.querySelectorAll('.jodit-toolbar-button__trigger').forEach((el) => { + el.removeAttribute('role'); + }); + }); + const target = document.querySelector('.jodit-toolbar__box'); + if (target) { + observer.observe(target, { subtree: true, childList: true }); + } + + // observer to detect iframe and set title + const observer2 = new MutationObserver(() => { + const hiddenIframe = Array.from(document.querySelectorAll('iframe')).find((iframe) => { + const rect = iframe.getBoundingClientRect(); + return rect.width === 0 && rect.height === 0 && (iframe.src === 'about:blank' || iframe.getAttribute('src') === 'about:blank'); + }); + if (hiddenIframe) { + hiddenIframe.setAttribute('title', 'Hidden iframe'); + observer2.disconnect(); // Stop observing once found + } + }); + observer2.observe(document.body, { + childList: true, + subtree: true, + }); } diff --git a/DigitalLearningSolutions.Web/Styles/jodit.scss b/DigitalLearningSolutions.Web/Styles/jodit.scss index 076a7f0053..416099e8f7 100644 --- a/DigitalLearningSolutions.Web/Styles/jodit.scss +++ b/DigitalLearningSolutions.Web/Styles/jodit.scss @@ -4,3 +4,6 @@ border: 2px solid red !important; border-radius: 4px; } +.jodit-placeholder { + display: none !important; +} diff --git a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Competency.cshtml b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Competency.cshtml index 059514dbe2..88a4ce9339 100644 --- a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Competency.cshtml +++ b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Competency.cshtml @@ -1,106 +1,106 @@ @using DigitalLearningSolutions.Web.ViewModels.Frameworks; @model FrameworkCompetencyViewModel; @{ - ViewData["Title"] = !string.IsNullOrWhiteSpace(Model.VocabSingular()) ? Model.VocabSingular(): "Competency"; - ViewData["Application"] = "Framework Service"; - ViewData["HeaderPathName"] = "Framework Service"; + ViewData["Title"] = !string.IsNullOrWhiteSpace(Model.VocabSingular()) ? Model.VocabSingular() : "Competency"; + ViewData["Application"] = "Framework Service"; + ViewData["HeaderPathName"] = "Framework Service"; } @section NavMenuItems { - + } - @section NavBreadcrumbs { - } -
+
- @if (Model.FrameworkCompetency.Id > 0) - { -

- Edit @Model.VocabSingular().ToLower() -

- } - else - { -

- Add new @Model.VocabSingular().ToLower() -

- } - @if (Model.DetailFramework.PublishStatusID == 3) - { - - } -
- @if (!ViewData.ModelState.IsValid) - { - - } - - - - - -
- -
- @if (Model.CompetencyFlags?.Count() > 0) - { -
- - @foreach (var flag in Model.CompetencyFlags) - { -
- - + @if (Model.FrameworkCompetency.Id > 0) + { +

+ Edit @Model.VocabSingular().ToLower() +

+ } + else + { +

+ Add new @Model.VocabSingular().ToLower() +

+ } + @if (Model.DetailFramework.PublishStatusID == 3) + { + + } + + @if (!ViewData.ModelState.IsValid) + { + + } + + + + + +
+
- } -
- } + @if (Model.CompetencyFlags?.Count() > 0) + { +
+ + @foreach (var flag in Model.CompetencyFlags) + { +
+ + +
+ } +
+ } - - - + + + - - - -
+ + + +
@section scripts { - + } diff --git a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ImportCompleted.cshtml b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ImportCompleted.cshtml index 3a5cd7aa8d..9a992aab19 100644 --- a/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ImportCompleted.cshtml +++ b/DigitalLearningSolutions.Web/Views/Frameworks/Developer/Import/ImportCompleted.cshtml @@ -47,7 +47,7 @@ {
  • - Some existing @Model.FrameworkVocabularySingular.ToLower() @(Model.CompetenciesToReorderCount == 1 ? "record" : "records") have changed sequence in your uploaded sheet. You can choose whether to fix them in the new order next. + Some existing @Model.FrameworkVocabularySingular.ToLower() @(Model.CompetenciesToReorderCount == 1 ? "record" : "records") have changed sequence in your uploaded sheet. You can choose whether to fix them in the new order next.
  • }
  • No errors
  • @@ -55,7 +55,11 @@ @if (Model.ErrorCount == 0) { - Continue + + Continue + to apply sequence changes + } else {