Skip to content

Commit

Permalink
[BUGFIX] Decouple id selectors from SvgTree and PageTree
Browse files Browse the repository at this point in the history
The PageTree component (added in TYPO3 v9) is based on SVG Tree,
which was added in a previous version (TYPO3 v8).

However, changes like https://review.typo3.org/c/Packages/TYPO3.CMS/+/55878/
unnecessarily coupled the SVG Tree to the PageTree.

In addition, some minor details to NOT use pagetree IDs
everywhere allows to use other SVG-tree based components
in the navigation container in the future.

id=typo3-pagetree-treeContainer is kept for
typo3/testing-framework compatibility, for now.

Resolves: #93389
Releases: master
Change-Id: Ieb51e46937cfb2f11efeb976da09bc600af8b5cf
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/67575
Tested-by: Benjamin Franzke <bfr@qbus.de>
Tested-by: TYPO3com <noreply@typo3.com>
Tested-by: Benni Mack <benni@typo3.org>
Reviewed-by: Benjamin Franzke <bfr@qbus.de>
Reviewed-by: Benni Mack <benni@typo3.org>
  • Loading branch information
bmack committed Feb 1, 2021
1 parent 9f7ed14 commit 6532b6e
Show file tree
Hide file tree
Showing 10 changed files with 49 additions and 56 deletions.
22 changes: 16 additions & 6 deletions Build/Sources/Sass/typo3/_element_tree.scss
Expand Up @@ -319,17 +319,27 @@ span.dragIcon {
// Tree
//
[id=ext-backend-Modules-FileSystemNavigationFrame-index-php],
[id=ext-backend-Modules-FileSystemNavigationFrame-index-php] .module-body,
[id=typo3-pagetree-treeContainer] {
[id=ext-backend-Modules-FileSystemNavigationFrame-index-php] .module-body {
background-color: $navigation-bg;
height: 100%;
}

[id=typo3-pagetree-tree] {
height: 100%;
.scaffold-content-navigation-component {
&,
.svg-tree,
.svg-tree > div,
.navigation-tree-container {
flex: 1 0 0;
display: flex;
flex-direction: column;
}

.svg-tree-wrapper {
flex: 1 0 0;
}

.x-panel-body {
height: 100% !important;
.navigation-tree-container {
background-color: $navigation-bg;
}
}

Expand Down
Expand Up @@ -31,12 +31,12 @@ export class PageTreeElement {
// let SvgTree know it shall be visible
// @todo Replace jQuery event with CustomEvent
if (targetEl && targetEl.childNodes.length > 0) {
$('#typo3-pagetree').trigger('isVisible');
$('.svg-tree', targetEl).trigger('isVisible');
return;
}

render(PageTreeElement.renderTemplate(), targetEl);
const treeEl = targetEl.querySelector('#typo3-pagetree-tree');
const treeEl = targetEl.querySelector('.svg-tree-wrapper');

// Ensure tooltips don't stay when scrolling the pagetree
$(treeEl).on('scroll', () => {
Expand All @@ -58,10 +58,10 @@ export class PageTreeElement {
tree.initialize(treeEl, configuration);
viewPort.NavigationContainer.setComponentInstance(tree);
// the toolbar relies on settings retrieved in this step
const toolbar = document.getElementById('svg-toolbar');
const toolbar = <HTMLElement>targetEl.querySelector('.svg-toolbar');
if (!toolbar.dataset.treeShowToolbar) {
const pageTreeToolbar = new PageTreeToolbar();
pageTreeToolbar.initialize('#typo3-pagetree-tree');
pageTreeToolbar.initialize('#typo3-pagetree-tree', toolbar);
toolbar.dataset.treeShowToolbar = 'true';
}
});
Expand All @@ -71,9 +71,9 @@ export class PageTreeElement {
return html`
<div id="typo3-pagetree" class="svg-tree">
<div>
<div id="svg-toolbar" class="svg-toolbar"></div>
<div id="typo3-pagetree-treeContainer">
<div id="typo3-pagetree-tree" class="svg-tree-wrapper" style="height:1000px;">
<div id="typo3-pagetree-toolbar" class="svg-toolbar"></div>
<div id="typo3-pagetree-treeContainer" class="navigation-tree-container">
<div id="typo3-pagetree-tree" class="svg-tree-wrapper">
<div class="node-loader">
${icon('spinner-circle-light', 'small')}
</div>
Expand Down
Expand Up @@ -28,7 +28,6 @@ export class PageTreeToolbar
private settings = {
toolbarSelector: 'tree-toolbar',
searchInput: '.search-input',
target: '.svg-toolbar',
filterTimeout: 450
};

Expand All @@ -43,8 +42,9 @@ export class PageTreeToolbar
this.dragDrop = pageTreeDragDrop;
}

public initialize(treeSelector: string, settings: any = {}): void {
public initialize(treeSelector: string, toolbar: HTMLElement, settings: any = {}): void {
this.$treeWrapper = $(treeSelector);
this.targetEl = toolbar;

if (!this.$treeWrapper.data('svgtree-initialized')
|| typeof this.$treeWrapper.data('svgtree') !== 'object'
Expand Down Expand Up @@ -149,9 +149,7 @@ export class PageTreeToolbar
// @todo Better use initialize() settings, drop this assignment here
Object.assign(this.settings, this.tree.settings);

this.targetEl = document.querySelector(this.settings.target);
render(this.renderTemplate(), this.targetEl);
this.tree.setWrapperHeight();

const d3Toolbar = d3.select('.svg-toolbar');
$.each(this.tree.settings.doktypes, (id: number, item: any) => {
Expand All @@ -167,7 +165,7 @@ export class PageTreeToolbar
new DebounceEvent('input', (evt: InputEvent) => {
this.search(evt.target as HTMLInputElement);
}, this.settings.filterTimeout)
.bindTo(document.querySelector(this.settings.searchInput));
.bindTo(this.targetEl.querySelector(this.settings.searchInput));

$(this.targetEl).find('[data-bs-toggle="tooltip"]').tooltip();

Expand Down

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion typo3/sysext/backend/Resources/Public/Css/backend.css

Large diffs are not rendered by default.

Expand Up @@ -10,12 +10,12 @@
*
* The TYPO3 project - inspiring people to share!
*/
var __importDefault=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};define(["require","exports","jquery","lit-html","lit-element","TYPO3/CMS/Core/lit-helper","TYPO3/CMS/Backend/PageTree/PageTree","../Viewport","./PageTreeToolbar","TYPO3/CMS/Core/Ajax/AjaxRequest"],(function(e,t,r,i,a,o,l,s,n,d){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PageTreeElement=void 0,r=__importDefault(r),s=__importDefault(s),d=__importDefault(d);class g{static initialize(e){const t=document.querySelector(e);if(t&&t.childNodes.length>0)return void r.default("#typo3-pagetree").trigger("isVisible");i.render(g.renderTemplate(),t);const a=t.querySelector("#typo3-pagetree-tree");r.default(a).on("scroll",()=>{r.default(a).find("[data-bs-toggle=tooltip]").tooltip("hide")});const o=new l,p=top.TYPO3.settings.ajaxUrls.page_tree_configuration;new d.default(p).get().then(async e=>{const t=await e.resolve("json"),r=top.TYPO3.settings.ajaxUrls.page_tree_data,i=top.TYPO3.settings.ajaxUrls.page_tree_filter;Object.assign(t,{dataUrl:r,filterUrl:i,showIcons:!0}),o.initialize(a,t),s.default.NavigationContainer.setComponentInstance(o);const l=document.getElementById("svg-toolbar");if(!l.dataset.treeShowToolbar){(new n.PageTreeToolbar).initialize("#typo3-pagetree-tree"),l.dataset.treeShowToolbar="true"}})}static renderTemplate(){return a.html`
var __importDefault=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};define(["require","exports","jquery","lit-html","lit-element","TYPO3/CMS/Core/lit-helper","TYPO3/CMS/Backend/PageTree/PageTree","../Viewport","./PageTreeToolbar","TYPO3/CMS/Core/Ajax/AjaxRequest"],(function(e,t,r,a,i,o,l,s,n,d){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PageTreeElement=void 0,r=__importDefault(r),s=__importDefault(s),d=__importDefault(d);class c{static initialize(e){const t=document.querySelector(e);if(t&&t.childNodes.length>0)return void r.default(".svg-tree",t).trigger("isVisible");a.render(c.renderTemplate(),t);const i=t.querySelector(".svg-tree-wrapper");r.default(i).on("scroll",()=>{r.default(i).find("[data-bs-toggle=tooltip]").tooltip("hide")});const o=new l,g=top.TYPO3.settings.ajaxUrls.page_tree_configuration;new d.default(g).get().then(async e=>{const r=await e.resolve("json"),a=top.TYPO3.settings.ajaxUrls.page_tree_data,l=top.TYPO3.settings.ajaxUrls.page_tree_filter;Object.assign(r,{dataUrl:a,filterUrl:l,showIcons:!0}),o.initialize(i,r),s.default.NavigationContainer.setComponentInstance(o);const d=t.querySelector(".svg-toolbar");if(!d.dataset.treeShowToolbar){(new n.PageTreeToolbar).initialize("#typo3-pagetree-tree",d),d.dataset.treeShowToolbar="true"}})}static renderTemplate(){return i.html`
<div id="typo3-pagetree" class="svg-tree">
<div>
<div id="svg-toolbar" class="svg-toolbar"></div>
<div id="typo3-pagetree-treeContainer">
<div id="typo3-pagetree-tree" class="svg-tree-wrapper" style="height:1000px;">
<div id="typo3-pagetree-toolbar" class="svg-toolbar"></div>
<div id="typo3-pagetree-treeContainer" class="navigation-tree-container">
<div id="typo3-pagetree-tree" class="svg-tree-wrapper">
<div class="node-loader">
${o.icon("spinner-circle-light","small")}
</div>
Expand All @@ -26,4 +26,4 @@ var __importDefault=this&&this.__importDefault||function(e){return e&&e.__esModu
${o.icon("spinner-circle-light","large")}
</div>
</div>
`}}t.PageTreeElement=g}));
`}}t.PageTreeElement=c}));
Expand Up @@ -10,7 +10,7 @@
*
* The TYPO3 project - inspiring people to share!
*/
var __importDefault=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};define(["require","exports","d3","jquery","lit-html","lit-element","TYPO3/CMS/Core/lit-helper","TYPO3/CMS/Backend/PageTree/PageTreeDragDrop","TYPO3/CMS/Core/Event/DebounceEvent"],(function(e,t,s,r,i,a,n,o,l){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PageTreeToolbar=void 0,r=__importDefault(r),l=__importDefault(l);t.PageTreeToolbar=class{constructor(){this.settings={toolbarSelector:"tree-toolbar",searchInput:".search-input",target:".svg-toolbar",filterTimeout:450},this.hideUncheckedState=!1,this.dragDrop=o}initialize(e,t={}){this.$treeWrapper=r.default(e),this.$treeWrapper.data("svgtree-initialized")&&"object"==typeof this.$treeWrapper.data("svgtree")?(Object.assign(this.settings,t),this.render()):this.$treeWrapper.on("svgTree.initialized",()=>this.render())}refreshTree(){this.tree.refreshOrFilterTree()}search(e){this.tree.searchQuery=e.value.trim(),this.tree.refreshOrFilterTree(),this.tree.prepareDataForVisibleNodes(),this.tree.update()}toggleHideUnchecked(e){this.hideUncheckedState=!this.hideUncheckedState,this.hideUncheckedState?this.tree.nodes.forEach(e=>{e.checked?(this.showParents(e),e.expanded=!0,e.hidden=!1):(e.hidden=!0,e.expanded=!1)}):this.tree.nodes.forEach(e=>{e.hidden=!1}),this.tree.prepareDataForVisibleNodes(),this.tree.update()}showParents(e){if(0===e.parents.length)return;const t=this.tree.nodes[e.parents[0]];t.hidden=!1,t.expanded=!0,this.showParents(t)}showSubmenu(e){this.targetEl.querySelectorAll("[data-tree-show-submenu]").forEach(t=>{t.dataset.treeShowSubmenu===e?t.classList.add("active"):t.classList.remove("active")}),this.targetEl.querySelectorAll("[data-tree-submenu]").forEach(t=>{t.dataset.treeSubmenu===e?t.classList.add("active"):t.classList.remove("active")});const t=this.targetEl.querySelector('[data-tree-submenu="'+e+'"]').querySelector("input");t&&(t.focus(),t.clearable({onClear:()=>{this.tree.resetFilter(),this.tree.prepareDataForVisibleNodes(),this.tree.update()}}))}render(){this.tree=this.$treeWrapper.data("svgtree"),Object.assign(this.settings,this.tree.settings),this.targetEl=document.querySelector(this.settings.target),i.render(this.renderTemplate(),this.targetEl),this.tree.setWrapperHeight();const e=s.select(".svg-toolbar");r.default.each(this.tree.settings.doktypes,(t,s)=>{s.icon?e.selectAll("[data-tree-icon="+s.icon+"]").call(this.dragDrop.dragToolbar()):console.warn("Missing icon definition for doktype: "+s.nodeType)}),new l.default("input",e=>{this.search(e.target)},this.settings.filterTimeout).bindTo(document.querySelector(this.settings.searchInput)),r.default(this.targetEl).find('[data-bs-toggle="tooltip"]').tooltip();const t=this.targetEl.querySelector('[data-tree-show-submenu="page-new"]'),a=this.targetEl.querySelector(".svg-toolbar__menu :first-child:not(.js-svg-refresh)");(t||a).click()}renderTemplate(){return a.html`
var __importDefault=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};define(["require","exports","d3","jquery","lit-html","lit-element","TYPO3/CMS/Core/lit-helper","TYPO3/CMS/Backend/PageTree/PageTreeDragDrop","TYPO3/CMS/Core/Event/DebounceEvent"],(function(e,t,s,r,i,a,n,o,l){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PageTreeToolbar=void 0,r=__importDefault(r),l=__importDefault(l);t.PageTreeToolbar=class{constructor(){this.settings={toolbarSelector:"tree-toolbar",searchInput:".search-input",filterTimeout:450},this.hideUncheckedState=!1,this.dragDrop=o}initialize(e,t,s={}){this.$treeWrapper=r.default(e),this.targetEl=t,this.$treeWrapper.data("svgtree-initialized")&&"object"==typeof this.$treeWrapper.data("svgtree")?(Object.assign(this.settings,s),this.render()):this.$treeWrapper.on("svgTree.initialized",()=>this.render())}refreshTree(){this.tree.refreshOrFilterTree()}search(e){this.tree.searchQuery=e.value.trim(),this.tree.refreshOrFilterTree(),this.tree.prepareDataForVisibleNodes(),this.tree.update()}toggleHideUnchecked(e){this.hideUncheckedState=!this.hideUncheckedState,this.hideUncheckedState?this.tree.nodes.forEach(e=>{e.checked?(this.showParents(e),e.expanded=!0,e.hidden=!1):(e.hidden=!0,e.expanded=!1)}):this.tree.nodes.forEach(e=>{e.hidden=!1}),this.tree.prepareDataForVisibleNodes(),this.tree.update()}showParents(e){if(0===e.parents.length)return;const t=this.tree.nodes[e.parents[0]];t.hidden=!1,t.expanded=!0,this.showParents(t)}showSubmenu(e){this.targetEl.querySelectorAll("[data-tree-show-submenu]").forEach(t=>{t.dataset.treeShowSubmenu===e?t.classList.add("active"):t.classList.remove("active")}),this.targetEl.querySelectorAll("[data-tree-submenu]").forEach(t=>{t.dataset.treeSubmenu===e?t.classList.add("active"):t.classList.remove("active")});const t=this.targetEl.querySelector('[data-tree-submenu="'+e+'"]').querySelector("input");t&&(t.focus(),t.clearable({onClear:()=>{this.tree.resetFilter(),this.tree.prepareDataForVisibleNodes(),this.tree.update()}}))}render(){this.tree=this.$treeWrapper.data("svgtree"),Object.assign(this.settings,this.tree.settings),i.render(this.renderTemplate(),this.targetEl);const e=s.select(".svg-toolbar");r.default.each(this.tree.settings.doktypes,(t,s)=>{s.icon?e.selectAll("[data-tree-icon="+s.icon+"]").call(this.dragDrop.dragToolbar()):console.warn("Missing icon definition for doktype: "+s.nodeType)}),new l.default("input",e=>{this.search(e.target)},this.settings.filterTimeout).bindTo(this.targetEl.querySelector(this.settings.searchInput)),r.default(this.targetEl).find('[data-bs-toggle="tooltip"]').tooltip();const t=this.targetEl.querySelector('[data-tree-show-submenu="page-new"]'),a=this.targetEl.querySelector(".svg-toolbar__menu :first-child:not(.js-svg-refresh)");(t||a).click()}renderTemplate(){return a.html`
<div class="${this.settings.toolbarSelector}">
<div class="svg-toolbar__menu">
<div class="btn-group">
Expand Down
33 changes: 9 additions & 24 deletions typo3/sysext/backend/Resources/Public/JavaScript/SvgTree.js
Expand Up @@ -167,7 +167,6 @@ define(
$.extend(this.settings, settings);
var _this = this;
this.wrapper = $wrapper;
this.setWrapperHeight();
this.dispatch = d3.dispatch(
'updateNodes',
'updateSvg',
Expand Down Expand Up @@ -226,8 +225,9 @@ define(
_this.update();
});

$('#typo3-pagetree').on('isVisible', function() {
_this.updateWrapperHeight();
this.wrapper.on('isVisible', function() {
_this.updateScrollPosition();
_this.update();
});

this.wrapper.data('svgtree', this);
Expand Down Expand Up @@ -350,8 +350,9 @@ define(
resize: function() {
var _this = this;
$(window).on('resize', function() {
if ($('#typo3-pagetree').is(':visible')) {
_this.updateWrapperHeight();
if ($(_this.wrapper).is(':visible')) {
_this.updateScrollPosition();
_this.update();
}
});
},
Expand Down Expand Up @@ -382,25 +383,6 @@ define(
this.switchFocus(nodeElement);
},

/**
* Update svg wrapper height
*/
updateWrapperHeight: function() {
var _this = this;

_this.setWrapperHeight();
_this.updateScrollPosition();
_this.update();
},

/**
* Set svg wrapper height
*/
setWrapperHeight: function() {
var treeWrapperHeight = ($('body').height() - $('#svg-toolbar').outerHeight() - $('.scaffold-topbar').height());
$('#typo3-pagetree-tree').height(treeWrapperHeight);
},

/**
* Updates variables used for visible nodes calculation
*/
Expand All @@ -426,6 +408,9 @@ define(
var nodes = Array.isArray(json) ? json : [];
_this.replaceData(nodes);
_this.nodesRemovePlaceholder();
// @todo: needed?
_this.updateScrollPosition();
_this.update();
})
.catch(function(error) {
var title = TYPO3.lang.pagetree_networkErrorTitle;
Expand Down
Expand Up @@ -24,11 +24,11 @@

class PageTreeFilterCest
{
protected $filterInputFieldClearButton = '#typo3-pagetree #svg-toolbar span[data-identifier=actions-close]';
protected $filterButton = '#typo3-pagetree #svg-toolbar button[data-tree-icon=actions-filter]';
protected $filterInputField = '#typo3-pagetree #svg-toolbar .search-input';
protected $pageTreeReloadButton = '#typo3-pagetree #svg-toolbar button[data-tree-icon=actions-refresh]';
protected $inPageTree = '#typo3-pagetree-treeContainer .nodes';
protected $filterInputFieldClearButton = '#typo3-pagetree #typo3-pagetree-toolbar span[data-identifier=actions-close]';
protected $filterButton = '#typo3-pagetree #typo3-pagetree-toolbar button[data-tree-icon=actions-filter]';
protected $filterInputField = '#typo3-pagetree #typo3-pagetree-toolbar .search-input';
protected $pageTreeReloadButton = '#typo3-pagetree #typo3-pagetree-toolbar button[data-tree-icon=actions-refresh]';
protected $inPageTree = '#typo3-pagetree-tree .nodes';

public function _before(BackendTester $I, PageTree $pageTree, SiteConfiguration $siteConfiguration)
{
Expand Down
Expand Up @@ -28,7 +28,7 @@
class PageCreationWithDragAndDropCest
{
protected static string $treeNode = '#typo3-pagetree-tree .nodes .node';
protected static string $dragNode = '#svg-toolbar .svg-toolbar__drag-node';
protected static string $dragNode = '#typo3-pagetree-toolbar .svg-toolbar__drag-node';
protected static string $nodeEditInput = '.node-edit';

/**
Expand Down

0 comments on commit 6532b6e

Please sign in to comment.