From 678621e164b6b3b47aa1f9a4138a856bd0c57246 Mon Sep 17 00:00:00 2001
From: Ayke Halder <mail@ayke-halder.de>
Date: Sun, 14 Apr 2024 13:04:54 +0200
Subject: [PATCH] [BUGFIX] Hide hidden content elements in page module

There is a checkbox in page module to show hidden content elements.
If this checkbox is not selected the hidden content elements must be
hidden.

Additionally hidden elements must not be accessible/focusable by
keyboard.

Resolves: #103626
Releases: main, 12.4
Change-Id: Id8f4b98938ae27e3bd3d3e7903c3f50588d554b8
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/83781
Tested-by: core-ci <typo3@b13.com>
Reviewed-by: Willi Wehmeier <wwwehmeier@gmail.com>
Tested-by: Oliver Bartsch <bo@cedev.de>
Reviewed-by: Oliver Bartsch <bo@cedev.de>
Tested-by: Willi Wehmeier <wwwehmeier@gmail.com>
---
 .../TypeScript/backend/page-actions.ts        | 31 +++++++++++++------
 .../Private/Partials/PageLayout/Record.html   |  2 +-
 .../Public/JavaScript/page-actions.js         |  2 +-
 3 files changed, 23 insertions(+), 12 deletions(-)

diff --git a/Build/Sources/TypeScript/backend/page-actions.ts b/Build/Sources/TypeScript/backend/page-actions.ts
index d2556a7f6d3f..552f1447e1ec 100644
--- a/Build/Sources/TypeScript/backend/page-actions.ts
+++ b/Build/Sources/TypeScript/backend/page-actions.ts
@@ -42,21 +42,27 @@ class PageActions {
     me.disabled = true;
 
     for (const hiddenElement of hiddenElements) {
-      hiddenElement.style.display = 'block';
+      hiddenElement.style.display = 'flow-root';
       const scrollHeight = hiddenElement.scrollHeight;
-      hiddenElement.style.display = '';
+
+      // Always set `overflow: clip` after storing scrollHeight
+      // * For hidden state `height: 0px` is already set.
+      // * For visible state setting `overflow: clip` is fine anyway.
+      hiddenElement.style.overflow = 'clip';
 
       if (!me.checked) {
-        // Spacing between content elements is kept uniform by collapsed margins,
-        // hidden elements have a height of 0 and the margins of the surrounding elements
-        // cannot collapse, causing a visual gap. We therefore remove the element
-        // from the flow to prevent this.
+        // * Invisible elements must not be accessible/focusable by keyboard.
+        // * Spacing between content elements is kept uniform by collapsed margins,
+        //   hidden elements have a height of 0 and the margins of the surrounding elements
+        //   cannot collapse, causing a visual gap.
+        // Therefore do not display the element at all by setting `display: none`.
         hiddenElement.addEventListener('transitionend', (): void => {
-          hiddenElement.style.position = 'absolute';
+          hiddenElement.style.display = 'none';
+          hiddenElement.style.overflow = '';
         }, { once: true });
 
-        // We use requestAnimationFrame() as we have to set the container's height at first before resizing to 0px
-        // results in a smooth animation.
+        // We use requestAnimationFrame() as we have to set the container's height at first before resizing to
+        // collapsed-element-height. This results in a smooth animation.
         requestAnimationFrame(function() {
           hiddenElement.style.height = scrollHeight + 'px';
           requestAnimationFrame(function() {
@@ -64,7 +70,12 @@ class PageActions {
           });
         });
       } else {
-        hiddenElement.style.position = '';
+        hiddenElement.addEventListener('transitionend', (): void => {
+          hiddenElement.style.display = '';
+          hiddenElement.style.overflow = '';
+          hiddenElement.style.height = '';
+        }, { once: true });
+
         hiddenElement.style.height = scrollHeight + 'px';
       }
     }
diff --git a/typo3/sysext/backend/Resources/Private/Partials/PageLayout/Record.html b/typo3/sysext/backend/Resources/Private/Partials/PageLayout/Record.html
index 2d1304484f53..ea41eabc1961 100644
--- a/typo3/sysext/backend/Resources/Private/Partials/PageLayout/Record.html
+++ b/typo3/sysext/backend/Resources/Private/Partials/PageLayout/Record.html
@@ -1,4 +1,4 @@
-{f:if(condition: '{item.disabled} && {item.context.drawingConfiguration.showHidden} == 0', then: 'height: 0; position: absolute;') -> f:variable(name: 'style')}
+{f:if(condition: '{item.disabled} && {item.context.drawingConfiguration.showHidden} == 0', then: 'height: 0; display: none;') -> f:variable(name: 'style')}
 <div class="t3-page-ce {item.wrapperClassName} t3js-page-ce t3js-page-ce-sortable" id="element-tt_content-{item.record.uid}" data-table="tt_content" data-uid="{item.record.uid}" data-language-uid="{item.record.sys_language_uid}" style="{style}">
     <div class="t3-page-ce-element t3-page-ce-dragitem">
         <f:render partial="PageLayout/Record/{item.record.CType}/Header" arguments="{_all}" optional="1">
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/page-actions.js b/typo3/sysext/backend/Resources/Public/JavaScript/page-actions.js
index a16ba5b5c15c..bbc9b5358694 100644
--- a/typo3/sysext/backend/Resources/Public/JavaScript/page-actions.js
+++ b/typo3/sysext/backend/Resources/Public/JavaScript/page-actions.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-import DocumentService from"@typo3/core/document-service.js";import RegularEvent from"@typo3/core/event/regular-event.js";import PersistentStorage from"@typo3/backend/storage/persistent.js";var IdentifierEnum;!function(e){e.hiddenElements=".t3js-hidden-record"}(IdentifierEnum||(IdentifierEnum={}));class PageActions{constructor(){DocumentService.ready().then((()=>{const e=document.getElementById("checkShowHidden");null!==e&&new RegularEvent("change",this.toggleContentElementVisibility).bindTo(e)}))}toggleContentElementVisibility(e){const t=e.target,n=document.querySelectorAll(IdentifierEnum.hiddenElements);t.disabled=!0;for(const e of n){e.style.display="block";const n=e.scrollHeight;e.style.display="",t.checked?(e.style.position="",e.style.height=n+"px"):(e.addEventListener("transitionend",(()=>{e.style.position="absolute"}),{once:!0}),requestAnimationFrame((function(){e.style.height=n+"px",requestAnimationFrame((function(){e.style.height="0px"}))})))}PersistentStorage.set("moduleData.web_layout.showHidden",t.checked?"1":"0").then((()=>{t.disabled=!1}))}}export default new PageActions;
\ No newline at end of file
+import DocumentService from"@typo3/core/document-service.js";import RegularEvent from"@typo3/core/event/regular-event.js";import PersistentStorage from"@typo3/backend/storage/persistent.js";var IdentifierEnum;!function(e){e.hiddenElements=".t3js-hidden-record"}(IdentifierEnum||(IdentifierEnum={}));class PageActions{constructor(){DocumentService.ready().then((()=>{const e=document.getElementById("checkShowHidden");null!==e&&new RegularEvent("change",this.toggleContentElementVisibility).bindTo(e)}))}toggleContentElementVisibility(e){const t=e.target,n=document.querySelectorAll(IdentifierEnum.hiddenElements);t.disabled=!0;for(const e of n){e.style.display="flow-root";const n=e.scrollHeight;e.style.overflow="clip",t.checked?(e.addEventListener("transitionend",(()=>{e.style.display="",e.style.overflow="",e.style.height=""}),{once:!0}),e.style.height=n+"px"):(e.addEventListener("transitionend",(()=>{e.style.display="none",e.style.overflow=""}),{once:!0}),requestAnimationFrame((function(){e.style.height=n+"px",requestAnimationFrame((function(){e.style.height="0px"}))})))}PersistentStorage.set("moduleData.web_layout.showHidden",t.checked?"1":"0").then((()=>{t.disabled=!1}))}}export default new PageActions;
\ No newline at end of file
-- 
GitLab