36733 unify folder p tree into a shared dotcmsui component - #36848
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. |
- 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 13m 22s —— View job Code ReviewReviewed the diff against New Issues
Existing (follow-ups explicitly deferred, noting for completeness)
Resolved
Nothing here is data-corrupting or a security issue. The High item is error-path-only but leaves the UI unrecoverable, so worth fixing before merge. · |
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';
|
Reviewed the refactor end to end. The core mechanic is correct and I verified it rather than assuming: PrimeNG resolves the load-more template via Nothing below blocks. A few small cleanups worth doing in this PR, then some follow-up material. Worth fixing here (all deletions / one-liners)Dead symbols introduced by this PR:
Misleading comments in new test helpers ( Plus the two one-liners the bots already raised: the dead Follow-up, not this PR
type TreeNodeData =
| { type: 'site' | 'folder'; path: string; hostname: string; id: string }
| { type: 'load-more'; id: string; nextPage?: number; remaining?: number };That change also covers the related smell that Two smaller follow-ups in the same bucket: the shared component defaults Review written by Claude (Claude Code), posted from @zJaaal's account. |
|
Addressed the in-PR cleanup items from the reviews:
Leaving the follow-ups (discriminated |
- Introduced a new `base.fixture.ts` to centralize shared Playwright fixtures for admin pages and API helpers. - Updated `content-drive.fixture.ts`, `host-folder.fixture.ts`, and `relationship.fixture.ts` to extend from the new base fixture, simplifying their structure and improving maintainability. - Modified the `createFolders` function to return void, aligning with the updated API design. - Enhanced the `getDefaultSite` function to throw an error if no default site is found, improving error handling. - Updated various components and tests to utilize the new `dotFolderName` pipe for consistent folder name formatting. - Removed deprecated `dot-truncate-path` pipe references across the codebase. - Improved the handling of load-more nodes in the folder tree structure, ensuring consistent behavior across components.
- Introduced a new helper function `isKnownFilterKey` to improve readability and maintainability. - Updated the filter decoding process to utilize the typed known-key map, enhancing type safety and clarity in the assignment of decoded values. - Simplified the handling of unknown filter keys by retaining existing functionality while improving code structure.
…ents Co-authored-by: nicobytes <7611944+nicobytes@users.noreply.github.com>
- Updated the test for handling filters with null or undefined values to cast the filter object correctly, ensuring type safety. - Enhanced the path assertion in the folder nodes test to safely access the path property, preventing potential runtime errors.
- Added a guard clause in the test for `getFolderNodesByPath` to ensure `data` is present and valid before accessing its properties, preventing potential runtime errors. - Updated assertions to directly reference the `data` object for clarity and safety in the test outcomes.
- Added a new constant, FOLDER_TREE_HIERARCHY_PAGE_SIZE, to manage pagination for deep-link and initial hierarchy fetches, ensuring that folders beyond the interactive page of 40 are accessible. - Updated utility functions and tests to utilize the new page size, enhancing the folder hierarchy retrieval process and maintaining consistency with existing pagination behavior. - Adjusted related tests to reflect the new hierarchy page size and ensure proper functionality in folder searches.
- Reformatted conditional assignments in `dot-content-drive-dialog-content-type-selector`, `dot-content-drive-toolbar`, and `dot-content-drive-shell` components for better clarity and consistency. - Updated import statements in `browser.store.ts` to follow a cleaner multi-line format, enhancing maintainability.
…ent-type-selector tests - Modified the `selectedNode` mock to return an object with a `type` property set to 'folder', ensuring consistency with the expected data structure in tests. - Enhanced the `onRequestUpload` method in `dot-content-drive-shell` to handle cases where `targetFolder` may be null or undefined, improving robustness against potential runtime errors.
|
Tick the box to add this pull request to the merge queue (same as
|
…tent-drive components - Replaced folder icons from 'pi-folder-open' and 'pi-folder' to 'pi-globe' for consistency across various components and tests. - Removed the `showFolderIconOnFirstOnly` property from the `dot-tree-folder` component and its related tests, simplifying the component's API. - Adjusted related tests and mock data to reflect the new icon usage and ensure proper functionality.
- Introduced the `dot-users` portlet, including user creation and listing components. - Implemented routing for the users section in the application. - Added ESLint and Jest configurations specific to the new portlet. - Enhanced the user list with search and filter functionalities, utilizing PrimeNG components for UI consistency. - Created tests for the user list and filter components to ensure functionality and reliability.
…spec.ts - Updated the `ALL_FOLDER` mock to include a PrimeNG folder icon (`icon: 'pi pi-folder'`). - Added a new test case to verify the presence of the folder icon in the mock data.
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
This PR fixes: #36733