Fix: refresh RzAside tree depending on page tree action#396
Fix: refresh RzAside tree depending on page tree action#396timothejoubert wants to merge 17 commits intodevelopfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates Rozier’s tree refresh behavior so node tree actions can trigger refreshes across trees, while also refactoring session message toast dispatching and adjusting tree rendering/layout options.
Changes:
- Add a global
requestAllNodeTreeChangeevent listener torz-treeand refresh other trees after node operations. - Extract session message fetching/toast dispatching into a dedicated module and wire it into
Rozier.getMessages(). - Update Twig tree templates to support a
layoutoption (e.g.condensed) and adjust contextual menu identifiers / attributes.
Reviewed changes
Copilot reviewed 11 out of 12 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| templates/admin/article/add.html.twig | Updates admin article add form markup and submit button rendering. |
| lib/Rozier/app/session-message.ts | Introduces shared helpers to fetch session messages and dispatch toast events. |
| lib/Rozier/app/custom-elements/RzTree.ts | Adds global refresh event handling, refactors fetch/refresh logic, and refreshes other trees after actions. |
| lib/Rozier/app/custom-elements/RzNodeTreeContextualMenu.ts | Updates DOM selectors/attributes and dispatches requestAllNodeTreeChange with details. |
| lib/Rozier/app/custom-elements/RzAside.ts | Simplifies aside event wiring and refactors tree content refresh + translation tracking. |
| lib/Rozier/app/Rozier.ts | Switches message handling to the extracted session-toast dispatcher. |
| lib/RoadizRozierBundle/templates/widgets/tree/rz_tree_wrapper_auto.html.twig | Passes layout: condensed and refines isMainTree computation for auto wrappers. |
| lib/RoadizRozierBundle/templates/widgets/nodeTree/singleNode.html.twig | Makes selection/contextual menu ID/layout behavior dependent on layout and main tree state. |
| lib/RoadizRozierBundle/templates/widgets/nodeTree/nodeTree.html.twig | Propagates layout into per-node includes. |
| lib/RoadizRozierBundle/templates/nodes/tree.html.twig | Renders a condensed non-main tree for specific node trees. |
| lib/RoadizRozierBundle/templates/macros/rz_page_header.html.twig | Fixes link attribute construction for aria-current. |
lib/RoadizRozierBundle/templates/widgets/rz_tree_wrapper_auto.html.twig
Outdated
Show resolved
Hide resolved
lib/RoadizRozierBundle/templates/widgets/folderTree/folderTree.html.twig
Outdated
Show resolved
Hide resolved
| }) }} | ||
| {% endapply %} | ||
| </fieldset> | ||
| {{ form_start(form) }} |
There was a problem hiding this comment.
The previous form_start passed explicit id (articles-form) and a rz-form class, and the submit button referenced that id via data-action-save. These attributes were removed, which can break existing JS hooks and/or styling tied to the form id/class. If those hooks are still expected, keep the attributes on form_start (or update any dependent logic to rely on the new structure).
| {{ form_start(form) }} | |
| {{ form_start(form, {'attr': {'id': 'articles-form', 'class': 'rz-form'}}) }} |
lib/RoadizRozierBundle/templates/widgets/nodeTree/singleNode.html.twig
Outdated
Show resolved
Hide resolved
| async refreshAsideMainTree( | ||
| baseUrl: string | null = null, | ||
| query: Record<string, string> = {}, | ||
| ) { | ||
| try { | ||
| await this.refreshMainTree(baseUrl, query) | ||
| } catch { | ||
| console.debug( | ||
| '[RzAside.refreshAsideMainTree] Retrying in 3 seconds', | ||
| ) | ||
| await sleep(3000) | ||
| await this.refreshMainTree(baseUrl, query) | ||
| } | ||
|
|
||
| return Promise.all(promises) | ||
| window.dispatchEvent(new CustomEvent('requestLoaderHide')) | ||
| } |
There was a problem hiding this comment.
refreshAsideMainTree() always dispatches requestLoaderHide after the try/catch, but if the second refreshMainTree() attempt throws, the function exits early and the loader may remain visible. Wrap the loader hide in a finally block (and optionally show the loader at the start of this method) to guarantee cleanup on failure.
| @@ -21,7 +21,11 @@ | |||
|
|
|||
| {% if tree is defined %} | |||
| {% set button_data = button_data|merge({ selected: translation.id == tree.translation.id }) %} | |||
| {% set button_attr = { 'data-translation-id': translation.id } %} | |||
| {% set button_attr = { | |||
| 'data-translation-id': translation.id, | |||
| 'command': '--update-translation', | |||
| 'type': 'button', | |||
| } %} | |||
|
|
|||
| {% if tree.rootNode is defined %} | |||
| {% set button_attr = button_attr|merge({'data-children-parent-node': tree.rootNode.id }) %} | |||
| @@ -31,10 +35,12 @@ | |||
| {% set button_attr = button_attr|merge({'data-filter-tag': tree.tag.id}) %} | |||
| {% endif %} | |||
|
|
|||
| {% set button_data = button_data|merge({attributes: button_attr}) %} | |||
| {% endif %} | |||
|
|
|||
| {% import "@RoadizRozier/macros/rz_button.html.twig" as rz_button %} | |||
| {{ rz_button.root(button_data) }} | |||
| {{ rz_button.root(button_data|merge({ | |||
| attributes: button_attr|merge(btn_attr) | |||
| })) | |||
| }} | |||
There was a problem hiding this comment.
In item(), button_attr is only defined inside {% if tree is defined %}, but it is later used unconditionally in button_attr|merge(btn_attr). If this macro is ever used without a tree argument, Twig will raise an undefined variable error. Initialize button_attr to {} before the if so the macro is safe.
| connectedCallback() { | ||
| this.listen(this, 'click', this.onLangButtonClick) | ||
| this.refreshAsideMainTree() | ||
|
|
||
| window.addEventListener('pageshowend', this.onPageShowEnd) | ||
| window.addEventListener( | ||
| 'requestAllNodeTreeChange', | ||
| this.onAllNodeTreeChange, | ||
| ) | ||
| window.addEventListener( | ||
| 'requestAllNodeTreeRefresh', | ||
| this.onAllNodeTreeChange, | ||
| ) | ||
| window.addEventListener( | ||
| 'requestMainTreeRefresh', | ||
| this.onMainTreeRefresh, | ||
| ) | ||
| window.addEventListener( | ||
| 'requestBindMainTrees', | ||
| this.onBindMainTreesRequest, | ||
| ) | ||
| window.addEventListener( | ||
| 'requestAjaxLinkBind', | ||
| this.onAjaxLinkBindRequest, | ||
| ) | ||
| window.addEventListener( | ||
| 'requestMessagesRefresh', | ||
| this.onMessagesRefresh, | ||
| this.currentTranslationId = this.getAttribute( | ||
| 'data-default-translation-id', | ||
| ) |
There was a problem hiding this comment.
refreshAsideMainTree() is called before currentTranslationId is initialized from data-default-translation-id. This means the first AJAX refresh can run without any translationId, and then currentTranslationId can end up being set to null in refreshMainTree(). Initialize currentTranslationId (and any other required state) before calling refreshAsideMainTree() in connectedCallback() to keep the initial tree request consistent.
| window.addEventListener('pageshowend', this.onPageShowEnd) | ||
| this.addEventListener('command', this.onCommand) | ||
| } | ||
|
|
||
| disconnectedCallback() { | ||
| super.disconnectedCallback() | ||
|
|
||
| window.removeEventListener('pageshowend', this.onPageShowEnd) | ||
| window.removeEventListener( | ||
| 'requestAllNodeTreeChange', | ||
| this.onAllNodeTreeChange, | ||
| ) | ||
| window.removeEventListener( | ||
| 'requestAllNodeTreeRefresh', | ||
| this.onAllNodeTreeChange, | ||
| ) | ||
| window.removeEventListener( | ||
| 'requestMainTreeRefresh', | ||
| this.onMainTreeRefresh, | ||
| ) | ||
| window.removeEventListener( | ||
| 'requestBindMainTrees', | ||
| this.onBindMainTreesRequest, | ||
| ) | ||
| window.removeEventListener( | ||
| 'requestAjaxLinkBind', | ||
| this.onAjaxLinkBindRequest, | ||
| ) | ||
| window.removeEventListener( | ||
| 'requestMessagesRefresh', | ||
| this.onMessagesRefresh, | ||
| ) | ||
| this.removeEventListener('command', this.onCommand) | ||
| } |
There was a problem hiding this comment.
requestMainTreeRefresh listener was removed from RzAside, but RzTree.onQuickAddNode() still dispatches requestMainTreeRefresh (see lib/Rozier/app/custom-elements/RzTree.ts). As a result, quick-add actions will no longer trigger an aside refresh. Either re-add the listener here or migrate the emitter to the new refresh mechanism (requestAllNodeTreeChange / refreshOtherTrees) so events remain consistent.
| return ( | ||
| this.getAttribute('data-translation-id') || | ||
| this.innerElement | ||
| .querySelector('.rz-tree__list') |
There was a problem hiding this comment.
getDisplayedTreeTranslationId() can throw if .rz-aside__body is missing because it calls this.innerElement.querySelector(...) without guarding for null. Use optional chaining (or reuse innerElement local var) so this method is safe when the element is not yet fully rendered / is being disconnected.
| return ( | |
| this.getAttribute('data-translation-id') || | |
| this.innerElement | |
| .querySelector('.rz-tree__list') | |
| const innerElement = this.innerElement | |
| return ( | |
| this.getAttribute('data-translation-id') || | |
| innerElement | |
| ?.querySelector('.rz-tree__list') |
No description provided.