From b9abb5c6ef5b090414524e2fff39d6b1e6453c0e Mon Sep 17 00:00:00 2001 From: Oliver Bartsch <bo@cedev.de> Date: Mon, 3 Jul 2023 17:21:13 +0200 Subject: [PATCH] [BUGFIX] Show validation errors for fields with legend in form engine Since #97330, a form engine elements label might not be represented by a <label> tag, but by using a <legend> tag. This patch now ensures that those elements are also correctly considered within form engine validation. Resolves: #101221 Related: #97330 Releases: main, 12.4 Change-Id: Iac4ef9b198bd7a7b8c75e53b83e45117be348c98 Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/79636 Reviewed-by: Oliver Bartsch <bo@cedev.de> Tested-by: core-ci <typo3@b13.com> Tested-by: Oliver Bartsch <bo@cedev.de> --- Build/Sources/Sass/typo3/_main_form.scss | 3 ++- Build/Sources/TypeScript/backend/form-engine-review.ts | 7 ++++++- .../backend/Classes/Form/Element/AbstractFormElement.php | 2 +- typo3/sysext/backend/Resources/Public/Css/backend.css | 2 +- .../Resources/Public/JavaScript/form-engine-review.js | 2 +- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Build/Sources/Sass/typo3/_main_form.scss b/Build/Sources/Sass/typo3/_main_form.scss index 91b8922560fc..1f925ed791b8 100644 --- a/Build/Sources/Sass/typo3/_main_form.scss +++ b/Build/Sources/Sass/typo3/_main_form.scss @@ -127,7 +127,8 @@ fieldset[disabled] .form-control { // Form group validation states // .form-group.has-error { - > .form-label:before { + > .form-label:before, + > fieldset > .form-legend:before { @include icon-background($icon-actions-exclamation-circle, $danger); border-radius: 50%; width: 14px; diff --git a/Build/Sources/TypeScript/backend/form-engine-review.ts b/Build/Sources/TypeScript/backend/form-engine-review.ts index f4c97af2af87..d4038629fc49 100644 --- a/Build/Sources/TypeScript/backend/form-engine-review.ts +++ b/Build/Sources/TypeScript/backend/form-engine-review.ts @@ -36,6 +36,11 @@ class FormEngineReview { */ private readonly labelSelector: string = '.t3js-formengine-label'; + /** + * Class of FormEngine legends + */ + private readonly legendSelector: string = '.t3js-formengine-legend'; + /** * The constructor, set the class properties default values */ @@ -108,7 +113,7 @@ class FormEngineReview { const link = document.createElement('a'); link.classList.add('list-group-item'); link.href = '#'; - link.textContent = $field.find(me.labelSelector).text(); + link.textContent = $field.find(me.labelSelector).text() || $field.find(me.legendSelector).text(); link.addEventListener('click', (e: Event) => me.switchToField(e, $input)); $list.append(link); diff --git a/typo3/sysext/backend/Classes/Form/Element/AbstractFormElement.php b/typo3/sysext/backend/Classes/Form/Element/AbstractFormElement.php index 992bd6f0bce6..ed9c97987ce0 100644 --- a/typo3/sysext/backend/Classes/Form/Element/AbstractFormElement.php +++ b/typo3/sysext/backend/Classes/Form/Element/AbstractFormElement.php @@ -150,7 +150,7 @@ abstract class AbstractFormElement extends AbstractNode } $html = []; $html[] = '<fieldset>'; - $html[] = '<legend class="form-legend">' . $legend . '</legend>'; + $html[] = '<legend class="form-legend t3js-formengine-legend">' . $legend . '</legend>'; $html[] = $innerHTML; $html[] = '</fieldset>'; return implode(LF, $html); diff --git a/typo3/sysext/backend/Resources/Public/Css/backend.css b/typo3/sysext/backend/Resources/Public/Css/backend.css index e42d18bdd972..916fbf18b131 100644 --- a/typo3/sysext/backend/Resources/Public/Css/backend.css +++ b/typo3/sysext/backend/Resources/Public/Css/backend.css @@ -4182,7 +4182,7 @@ label .icon img{pointer-events:none} .formengine-field-item>.t3js-charcounter-wrapper .t3js-charcounter-min{margin:0 2px} .form-group .panel,.form-group .panel-group{overflow:visible} .form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{color:#737373} -.form-group.has-error>.form-label:before{content:"";vertical-align:middle;-webkit-mask:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' xml:space='preserve' viewBox='0 0 16 16'%3e%3cg class='icon-color'%3e%3cpath d='M8 2c3.3 0 6 2.7 6 6s-2.7 6-6 6-6-2.7-6-6 2.7-6 6-6m0-1C4.1 1 1 4.1 1 8s3.1 7 7 7 7-3.1 7-7-3.1-7-7-7z'/%3e%3ccircle cx='8' cy='11' r='1'/%3e%3cpath d='M8.5 9h-1l-.445-4.45A.5.5 0 0 1 7.552 4h.896a.5.5 0 0 1 .497.55L8.5 9z'/%3e%3c/g%3e%3c/svg%3e");mask:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' xml:space='preserve' viewBox='0 0 16 16'%3e%3cg class='icon-color'%3e%3cpath d='M8 2c3.3 0 6 2.7 6 6s-2.7 6-6 6-6-2.7-6-6 2.7-6 6-6m0-1C4.1 1 1 4.1 1 8s3.1 7 7 7 7-3.1 7-7-3.1-7-7-7z'/%3e%3ccircle cx='8' cy='11' r='1'/%3e%3cpath d='M8.5 9h-1l-.445-4.45A.5.5 0 0 1 7.552 4h.896a.5.5 0 0 1 .497.55L8.5 9z'/%3e%3c/g%3e%3c/svg%3e");background-color:#c83c3c;background-size:contain;display:inline-block;border-radius:50%;width:14px;height:14px} +.form-group.has-error>.form-label:before,.form-group.has-error>fieldset>.form-legend:before{content:"";vertical-align:middle;-webkit-mask:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' xml:space='preserve' viewBox='0 0 16 16'%3e%3cg class='icon-color'%3e%3cpath d='M8 2c3.3 0 6 2.7 6 6s-2.7 6-6 6-6-2.7-6-6 2.7-6 6-6m0-1C4.1 1 1 4.1 1 8s3.1 7 7 7 7-3.1 7-7-3.1-7-7-7z'/%3e%3ccircle cx='8' cy='11' r='1'/%3e%3cpath d='M8.5 9h-1l-.445-4.45A.5.5 0 0 1 7.552 4h.896a.5.5 0 0 1 .497.55L8.5 9z'/%3e%3c/g%3e%3c/svg%3e");mask:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' xml:space='preserve' viewBox='0 0 16 16'%3e%3cg class='icon-color'%3e%3cpath d='M8 2c3.3 0 6 2.7 6 6s-2.7 6-6 6-6-2.7-6-6 2.7-6 6-6m0-1C4.1 1 1 4.1 1 8s3.1 7 7 7 7-3.1 7-7-3.1-7-7-7z'/%3e%3ccircle cx='8' cy='11' r='1'/%3e%3cpath d='M8.5 9h-1l-.445-4.45A.5.5 0 0 1 7.552 4h.896a.5.5 0 0 1 .497.55L8.5 9z'/%3e%3c/g%3e%3c/svg%3e");background-color:#c83c3c;background-size:contain;display:inline-block;border-radius:50%;width:14px;height:14px} .form-group.has-error .btn-toolbar label:before{font-family:inherit;font-size:inherit;margin-right:inherit;text-align:inherit;content:"";color:inherit;display:block} select.form-select[multiple],select.form-select[size]:not([size="1"]){min-height:156px} select.form-select>optgroup{margin-top:9px} diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-review.js b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-review.js index 26c7c29a1f9f..a41f338f10dc 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-review.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-review.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import"bootstrap";import DocumentService from"@typo3/core/document-service.js";import $ from"jquery";import FormEngine from"@typo3/backend/form-engine.js";import"@typo3/backend/element/icon-element.js";import Popover from"@typo3/backend/popover.js";class FormEngineReview{constructor(){this.toggleButtonClass="t3js-toggle-review-panel",this.labelSelector=".t3js-formengine-label",this.checkForReviewableField=()=>{const e=this,t=FormEngineReview.findInvalidField(),o=document.querySelector("."+this.toggleButtonClass);if(null!==o)if(t.length>0){const i=$("<div />",{class:"list-group"});t.each((function(){const t=$(this),o=t.find("[data-formengine-validation-rules]"),n=document.createElement("a");n.classList.add("list-group-item"),n.href="#",n.textContent=t.find(e.labelSelector).text(),n.addEventListener("click",(t=>e.switchToField(t,o))),i.append(n)})),o.classList.remove("hidden"),Popover.setOptions(o,{html:!0,content:i[0]})}else o.classList.add("hidden"),Popover.hide(o)},this.switchToField=(e,t)=>{e.preventDefault(),t.parents('[id][role="tabpanel"]').each((function(){$('[aria-controls="'+$(this).attr("id")+'"]').tab("show")})),t.focus()},this.initialize()}static findInvalidField(){return $(document).find(".tab-content ."+FormEngine.Validation.errorClass)}static attachButtonToModuleHeader(e){const t=document.querySelector(".t3js-module-docheader-bar-buttons").lastElementChild.querySelector('[role="toolbar"]'),o=document.createElement("typo3-backend-icon");o.setAttribute("identifier","actions-info"),o.setAttribute("size","small");const i=document.createElement("button");i.type="button",i.classList.add("btn","btn-danger","btn-sm","hidden",e.toggleButtonClass),i.title=TYPO3.lang["buttons.reviewFailedValidationFields"],i.appendChild(o),Popover.popover(i),t.prepend(i)}initialize(){const e=this,t=$(document);DocumentService.ready().then((()=>{FormEngineReview.attachButtonToModuleHeader(e)})),t.on("t3-formengine-postfieldvalidation",this.checkForReviewableField)}}export default new FormEngineReview; \ No newline at end of file +import"bootstrap";import DocumentService from"@typo3/core/document-service.js";import $ from"jquery";import FormEngine from"@typo3/backend/form-engine.js";import"@typo3/backend/element/icon-element.js";import Popover from"@typo3/backend/popover.js";class FormEngineReview{constructor(){this.toggleButtonClass="t3js-toggle-review-panel",this.labelSelector=".t3js-formengine-label",this.legendSelector=".t3js-formengine-legend",this.checkForReviewableField=()=>{const e=this,t=FormEngineReview.findInvalidField(),o=document.querySelector("."+this.toggleButtonClass);if(null!==o)if(t.length>0){const n=$("<div />",{class:"list-group"});t.each((function(){const t=$(this),o=t.find("[data-formengine-validation-rules]"),i=document.createElement("a");i.classList.add("list-group-item"),i.href="#",i.textContent=t.find(e.labelSelector).text()||t.find(e.legendSelector).text(),i.addEventListener("click",(t=>e.switchToField(t,o))),n.append(i)})),o.classList.remove("hidden"),Popover.setOptions(o,{html:!0,content:n[0]})}else o.classList.add("hidden"),Popover.hide(o)},this.switchToField=(e,t)=>{e.preventDefault(),t.parents('[id][role="tabpanel"]').each((function(){$('[aria-controls="'+$(this).attr("id")+'"]').tab("show")})),t.focus()},this.initialize()}static findInvalidField(){return $(document).find(".tab-content ."+FormEngine.Validation.errorClass)}static attachButtonToModuleHeader(e){const t=document.querySelector(".t3js-module-docheader-bar-buttons").lastElementChild.querySelector('[role="toolbar"]'),o=document.createElement("typo3-backend-icon");o.setAttribute("identifier","actions-info"),o.setAttribute("size","small");const n=document.createElement("button");n.type="button",n.classList.add("btn","btn-danger","btn-sm","hidden",e.toggleButtonClass),n.title=TYPO3.lang["buttons.reviewFailedValidationFields"],n.appendChild(o),Popover.popover(n),t.prepend(n)}initialize(){const e=this,t=$(document);DocumentService.ready().then((()=>{FormEngineReview.attachButtonToModuleHeader(e)})),t.on("t3-formengine-postfieldvalidation",this.checkForReviewableField)}}export default new FormEngineReview; \ No newline at end of file -- GitLab