36733 unify folder p tree into a shared dotcmsui component - #36848
36733 unify folder p tree into a shared dotcmsui component#36848nicobytes wants to merge 5 commits into
Conversation
- Extracts a reusable dot-folder-tree component so host-folder-field, dot-tree-folder, and dot-sidebar consume one shared implementation instead of duplicating p-tree config, load-more button, and toggler templates. - Adds a folder-name pipe as the successor to dot-truncate-path, kept as a thin deprecated alias for backward compatibility. - Simplifies selectedNode handling to a single TreeNode instead of an array-transform workaround for PrimeNG's selection API.
Introduces a presentational folder-tree component in @dotcms/ui to consolidate the p-tree chrome (togglers, load-more, node labels) shared across Content Drive, Browser Selector, and Host Folder Field, so each consumer can own its data/UX without duplicating tree markup.
There was a problem hiding this comment.
Pull request overview
Refactors the admin UI folder-tree implementations to use a shared, presentational DotFolderTreeComponent in @dotcms/ui, unifying selection handling and introducing a consistent “load-more” sentinel node model across consumers (Content Drive, Browser Selector sidebar, and Host Folder Field).
Changes:
- Added
DotFolderTreeComponent(standalone) that owns the PrimeNGp-treeshell, templates (default/load-more/toggler), selection normalization, and test IDs. - Migrated Browser Selector sidebar, Content Drive tree wrapper, and Host Folder Field overlay tree to consume the shared component and the unified node model (including
type: 'load-more'). - Consolidated path-label behavior by promoting
FolderNamePipeand deprecatingDotTruncatePathPipeas an alias, with corresponding test updates.
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| core-web/libs/ui/src/lib/pipes/dot-truncate-path/dot-truncate-path.spec.ts | Adds coverage for empty-path behavior in the deprecated alias pipe. |
| core-web/libs/ui/src/lib/pipes/dot-truncate-path/dot-truncate-path.pipe.ts | Deprecates the pipe in favor of FolderNamePipe while keeping compatibility for existing call sites. |
| core-web/libs/ui/src/lib/pipes/dot-folder-name/dot-folder-name.pipe.ts | Clarifies pipe intent/docs for extracting the final path segment. |
| core-web/libs/ui/src/lib/components/dot-folder-tree/dot-folder-tree.component.ts | Introduces the shared folder-tree shell component and its unified API (folders/selection/load-more/toggler behavior). |
| core-web/libs/ui/src/lib/components/dot-folder-tree/dot-folder-tree.component.html | Implements shared templates for default nodes, load-more button, and custom toggler icons. |
| core-web/libs/ui/src/lib/components/dot-folder-tree/dot-folder-tree.component.scss | Centralizes tree toggler mode styling (chevron vs first-only folder icon). |
| core-web/libs/ui/src/lib/components/dot-folder-tree/dot-folder-tree.component.spec.ts | Adds Jest/Spectator coverage for selection normalization, outputs, load-more behavior, and default labeling. |
| core-web/libs/ui/src/lib/components/dot-browser-selector/components/dot-sidebar/dot-sidebar.component.ts | Switches Browser Selector sidebar to the shared tree and simplifies selection state management. |
| core-web/libs/ui/src/lib/components/dot-browser-selector/components/dot-sidebar/dot-sidebar.component.html | Replaces inline p-tree usage with <dot-folder-tree> and wires events/inputs. |
| core-web/libs/ui/src/index.ts | Exports the new shared DotFolderTreeComponent from @dotcms/ui. |
| core-web/libs/portlets/dot-content-drive/ui/src/lib/dot-tree-folder/dot-tree-folder.component.ts | Refactors Content Drive tree wrapper to delegate the tree shell to DotFolderTreeComponent while keeping drag/drop and “All folders” behavior. |
| core-web/libs/portlets/dot-content-drive/ui/src/lib/dot-tree-folder/dot-tree-folder.component.html | Replaces Content Drive’s inline p-tree with <dot-folder-tree> and projects the label template for Content Drive–specific markup. |
| core-web/libs/portlets/dot-content-drive/ui/src/lib/dot-tree-folder/dot-tree-folder.component.scss | Removes tree-specific toggler styling now owned by the shared component. |
| core-web/libs/portlets/dot-content-drive/ui/src/lib/dot-tree-folder/dot-tree-folder.component.spec.ts | Updates tests for the wrapper’s new selected-node shape and wrapper-based rendering. |
| core-web/libs/portlets/dot-content-drive/portlet/src/lib/utils/functions.ts | Ensures load-more sentinel nodes also set the PrimeNG TreeNode.type for template matching. |
| core-web/libs/portlets/dot-content-drive/portlet/src/lib/utils/functions.spec.ts | Updates expectations to include the type: 'load-more' field for sentinel nodes. |
| core-web/libs/portlets/dot-content-drive/portlet/src/lib/components/dot-content-drive-sidebar/dot-content-drive-sidebar.component.spec.ts | Aligns sidebar tests with the new single-node selection input. |
| core-web/libs/edit-content/src/lib/fields/dot-edit-content-host-folder-field/store/host-folder-field.store.ts | Updates load-more node creation to include unified data payload for the new shared tree. |
| core-web/libs/edit-content/src/lib/fields/dot-edit-content-host-folder-field/components/host-folder-field/host-folder-field.component.ts | Migrates Host Folder Field overlay tree to the shared component and adjusts scroll-to-selection logic. |
| core-web/libs/edit-content/src/lib/fields/dot-edit-content-host-folder-field/components/host-folder-field/host-folder-field.component.html | Replaces inline p-tree with <dot-folder-tree>, projects label template, and wires load-more via the shared output. |
| core-web/libs/edit-content/src/lib/fields/dot-edit-content-host-folder-field/components/host-folder-field/host-folder-field.component.spec.ts | Updates stubs/expectations to match the wrapper component API and scroll helper access. |
| core-web/libs/dotcms-models/src/lib/dot-browser-selector.model.ts | Extends the shared tree node data model to include load-more with pagination metadata. |
| </button> | ||
| </ng-template> | ||
|
|
||
| <!-- LOADING WONT WORK WITH CUSTOM TEMPLATE: https://github.com/primefaces/primeng/blob/v17/src/app/components/tree/tree.ts#L113 --> |
| import { Pipe, PipeTransform } from '@angular/core'; | ||
|
|
||
| import { FolderNamePipe } from '../dot-folder-name/dot-folder-name.pipe'; | ||
|
|
||
| /** |
- Updated the `createFolders` function to return an array of `CreatedFolder` objects instead of void, enhancing the API's usability. - Modified the `createFolders` type definition in the fixture to reflect the new return type. - Added export for `ContentDrivePage` in the index file to improve component accessibility. - Introduced a new `ContentDrive` constant in the portlets utility for better navigation within the application.
|
Claude finished @nicobytes's task in 14m 52s —— View job Code Review — PR #36848Reviewed the shared New Issues
Notes (non-blocking)
Verify (low confidence)
Overall the refactor is clean and the consolidation into |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 30 out of 30 changed files in this pull request and generated no new comments.
Suppressed comments (8)
core-web/libs/ui/src/lib/components/dot-folder-tree/dot-folder-tree.component.ts:113
- PrimeNG's
(onNodeSelect)emits a select event, not an expand event. Typing this output asTreeNodeSelectEventavoids confusing API consumers and matches other tree consumers in the codebase.
onNodeSelect = output<TreeNodeExpandEvent>();
core-web/libs/ui/src/lib/components/dot-browser-selector/components/dot-sidebar/dot-sidebar.component.ts:63
onNodeSelectshould be typed asTreeNodeSelectEvent(selection), notTreeNodeExpandEvent(expansion). This helps downstream consumers and matches the JSDoc intent.
onNodeSelect = output<TreeNodeExpandEvent>();
core-web/libs/ui/src/lib/components/dot-browser-selector/components/dot-sidebar/dot-sidebar.component.ts:103
handleNodeSelectreceives the selection event fromdot-folder-tree/ PrimeNG(onNodeSelect), so its parameter type should beTreeNodeSelectEventrather thanTreeNodeExpandEvent.
handleNodeSelect(event: TreeNodeExpandEvent): void {
this.#userSelected.set(event.node);
this.onNodeSelect.emit(event);
}
core-web/libs/ui/src/lib/components/dot-folder-tree/dot-folder-tree.component.html:22
TreeNode.labelis optional in PrimeNG, andFolderNamePipecalls.split()on its input. If any node has an undefined/nulllabel, this will throw at runtime. Guard the pipe input in the template to ensure a string is always passed.
<span data-testid="tree-node-label" class="font-normal">
{{ node.label | folderName }}
</span>
core-web/libs/edit-content/src/lib/fields/dot-edit-content-host-folder-field/components/host-folder-field/host-folder-field.component.html:281
- This template now uses
folderNameonnode.label, but PrimeNGTreeNode.labelcan beundefined. Guarding withnode.label ?? ''prevents runtime errors from.split()in the pipe if a node ever lacks a label.
<span
class="block w-full min-w-0 truncate"
[class.font-semibold]="isSearching"
[class.leading-normal]="isSearching">
{{ node.label | folderName }}
</span>
core-web/libs/ui/src/lib/pipes/dot-truncate-path/dot-truncate-path.pipe.ts:22
- Even as a deprecated alias,
dotTruncatePathcan still be invoked from templates withnull/undefinedvalues. Guard the input so it never throws when delegating toFolderNamePipe.
private readonly folderNamePipe = new FolderNamePipe();
transform(value: string): string {
return this.folderNamePipe.transform(value);
}
core-web/libs/ui/src/lib/components/dot-folder-tree/dot-folder-tree.component.ts:17
DotFolderTreeComponent.onNodeSelectis currently typed asTreeNodeExpandEvent, but PrimeNG emitsTreeNodeSelectEventfor selection. Importing the correct event type makes the wrapper API clearer and aligns with existing patterns (e.g. Content Drive sidebar usesTreeNodeSelectEvent).
This issue also appears on line 113 of the same file.
import type { TreeNodeCollapseEvent, TreeNodeExpandEvent } from 'primeng/types/tree';
core-web/libs/ui/src/lib/components/dot-browser-selector/components/dot-sidebar/dot-sidebar.component.ts:13
- For consistency with other tree consumers (and to get access to
TreeNodeSelectEvent), import PrimeNG tree event types fromprimeng/types/treeinstead ofprimeng/tree.
This issue also appears in the following locations of the same file:
- line 63
- line 100
import type { TreeNodeExpandEvent } from 'primeng/tree';
This pull request refactors the folder tree display components in both the edit-content host folder field and the content drive sidebar to use the new
DotFolderTreeComponentwrapper instead of the raw PrimeNGp-tree. It unifies the tree node data model to support a new "load-more" sentinel node type, updates related tests, and simplifies the handling of selected nodes and labels.Component Refactoring and API Unification
p-treewith the newDotFolderTreeComponentinhost-folder-field.component.htmlanddot-tree-folder.component.html, updating inputs and outputs to match the new API. This includes switching from[value]/[selection]to[folders]/[selectedNode], and updating event handlers and templates accordingly. [1] [2] [3]DotFolderTreeComponentand new pipes, removing unused PrimeNG modules and pipes. [1] [2] [3] [4]Tree Node Data Model Enhancements
TreeNodeDatatype to include aload-morenode type, with optionalnextPageandremainingproperties for pagination support. Updated all relevant code to use this new type and ensure "load-more" nodes are correctly constructed. [1] [2] [3] [4]Selected Node Handling
Load More Node Handling
Test and Utility Updates
These changes modernize and standardize the folder tree UI components, improve maintainability, and add support for paginated folder loading.
This PR fixes: #36733