feat(categories): migrate Categories portlet from Dojo to Angular#35146
Merged
feat(categories): migrate Categories portlet from Dojo to Angular#35146
Conversation
Replace the legacy categories portlet in dotcms-ui with a new standalone portlet library following the dot-tags canonical pattern. Includes API service, SignalStore with breadcrumb navigation, CRUD dialog, and full test coverage (52 tests). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Show only 4 fields: name (required), variable (readonly, auto-generated from name in camelCase), key, and keywords (textarea) - Variable field is always visible and auto-generates on create mode - Remove sortOrder, active, description fields from the dialog Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use Angular Router query params (?inode=xxx) as source of truth for category navigation. Breadcrumbs are rebuilt from the API parentList field on each load. Row click now navigates to children while a dedicated edit icon button opens the edit dialog. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…te form Remove childrenCount guard so users can navigate into any category to create children. Add parentName to store state and URL query params so the create dialog shows an info hint indicating the parent category. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When the children API returns an empty list, keep the existing breadcrumbs from the store instead of wiping them out. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The API parentList may not include the current parent when navigating deeper. Append the current parent from query params when the last parentList item does not match the current parentInode. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Drop Router/ActivatedRoute query param sync and parentList API param. Breadcrumbs are now built purely from navigation steps in the store: navigateToChildren pushes, navigateToBreadcrumb truncates. This avoids issues with the API parentList not matching the full navigation depth. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a row-level popup menu with ellipsis icon containing edit, permissions, and delete actions. Edit opens the existing edit dialog, delete confirms and removes a single category, permissions opens an empty placeholder dialog. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace per-row p-menu with a single shared menu appended to body. Menu items are set imperatively on button click before toggling, avoiding the change detection issue where template-bound items were recreated on every cycle. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use group/group-hover Tailwind pattern to show the ellipsis button only when hovering the row, matching the content-drive portlet. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add CSS grid layout to the list component host so toolbar, breadcrumb, and table fill the available height. Enable scrollable flex mode on the table so it scrolls within its container. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Passing null to DialogService.open causes NG0919. Created a minimal DotCategoriesPermissionsComponent as a placeholder. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The separate file import caused a runtime circular dependency error. Define the placeholder component in the same file as the list component to avoid the import resolution issue. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add CSV export and import capabilities to the categories service, store, and list component. Export triggers a blob download; import uses a dialog with file upload and merge/replace mode selection. The Add button is now a PrimeNG SplitButton with Import as a dropdown option. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Hide FileUpload header via PT, use large download icon in bg-primary-50 circle, bold drag-drop text, and info-circle icon on CSV subtitle. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… order editing - Add 32 i18n keys for all category UI labels (toolbar, table, empty state, dialogs, toasts) - Replace right-click p-menu with p-contextMenu so it opens at cursor position - Add row hover state and pointer cursor matching other portlets (content-drive pattern) - Add breadcrumb cursor-pointer via PrimeNG passthrough API - Add empty state matching tags portlet (full-height, centered icon + message) - Add p-inputNumber per row for inline sort order editing (save on blur/Enter) - Optimistic sort order update avoids table reload/flickering; reverts on error - Show success toast after sort order save via MessageService at shell level - Fix toast: move MessageService provider + p-toast to shell component so it's visible - Fix children count lost after sort update: use optimistic patch instead of _sort response - Add updateSortOrder() to DotCategoriesService (PUT /api/v1/categories/_sort) - Center-align Children, Sort Order and Actions columns Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ve filter Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add MessageService and DotMessageService providers to store spec (required after adding them to the store's withMethods injections) - Add updateSortOrder mock to DotCategoriesService in store and component specs - Add 4 new component tests for onSortOrderInput/Blur handlers - Add 4 new store tests for updateSortOrder: no-reload (optimistic), correct params, success toast, and error revert Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…egacy to Category Legacy #34731 - Switch portlet.xml 'categories' entry from StrutsPortlet to PortletController (/categories) - Add 'categories-legacy' portlet entry preserving the old Struts/Dojo view - Add CATEGORIES_LEGACY enum to PortletID - Add Language.properties label for categories-legacy - Add MenuGuard and reuseRoute:false to the categories Angular route - Update SerializationHelperTest for new portlet count (51) and updated assertions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contributor
Rollback Safety Analysis Complete (Re-analysis)Analyzed all 13 rollback-unsafe categories against the incremental diff (5e1128c...6e9deac). Verdict: ✅ Safe To Rollback All changes are additive frontend/configuration changes — no database migrations, no ES mapping changes, no data model changes. Label applied: AI: Safe To Rollback |
Contributor
There was a problem hiding this comment.
Pull request overview
Migrates the Categories portlet from the legacy Dojo/Struts implementation to a new Angular/PrimeNG portlet, while preserving the old portlet as Categories Legacy and introducing a reusable UI dialog for rendering legacy permissions pages in an iframe.
Changes:
- Added a new Angular Categories portlet library (
core-web/libs/portlets/dot-categories/) powered by NgRx Signals, with CRUD, sorting, import/export, breadcrumbs, and bulk actions. - Introduced a generic
DotPermissionsIframeDialogComponent(and refactored edit-content sidebar permissions to reuse it) to display legacy permissions JSPs in an iframe. - Updated backend portlet registration (
portlet.xml,PortletID,Language.properties) and adjusted the serialization test for the new portlet count.
Reviewed changes
Copilot reviewed 57 out of 58 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| dotCMS/src/test/java/com/dotmarketing/business/portal/SerializationHelperTest.java | Updates portlet count assertion and verifies both categories and categories-legacy portlets deserialize with expected configuration. |
| dotCMS/src/main/webapp/WEB-INF/portlet.xml | Switches existing categories to categories-legacy and adds a new Spring PortletController entry for Angular /categories. |
| dotCMS/src/main/webapp/WEB-INF/messages/Language.properties | Adds portlet title for categories-legacy, adds categories i18n keys, and adds a new dialog empty-state message key. |
| dotCMS/src/main/webapp/html/portlet/ext/categories/permissions.jsp | Adds a thin legacy JSP wrapper to load a category and render the permissions tab include. |
| dotCMS/src/main/java/com/dotmarketing/util/PortletID.java | Adds CATEGORIES_LEGACY("categories-legacy") enum value for the preserved legacy portlet. |
| core-web/tsconfig.base.json | Adds TS path alias for the new dot-categories portlet package entry point. |
| core-web/libs/ui/src/lib/components/dot-permissions-iframe-dialog/dot-permissions-iframe-dialog.component.ts | Introduces a standalone dialog component to render a permissions page URL inside an iframe. |
| core-web/libs/ui/src/lib/components/dot-permissions-iframe-dialog/dot-permissions-iframe-dialog.component.spec.ts | Adds unit tests for iframe rendering, minHeight handling, and empty-state behavior. |
| core-web/libs/ui/src/lib/components/add-to-bundle/dot-add-to-bundle.component.html | Disables dragging for the Add-to-Bundle dialog. |
| core-web/libs/ui/src/index.ts | Exports the new permissions iframe dialog component from @dotcms/ui. |
| core-web/libs/portlets/dot-categories/tsconfig.spec.json | Adds Jest tsconfig for the new portlet library. |
| core-web/libs/portlets/dot-categories/tsconfig.lib.json | Adds build tsconfig for the new portlet library. |
| core-web/libs/portlets/dot-categories/tsconfig.json | Adds base TS/Angular compiler configuration for the new portlet library. |
| core-web/libs/portlets/dot-categories/src/test-setup.ts | Adds jest-preset-angular Zone test environment setup for the new portlet library. |
| core-web/libs/portlets/dot-categories/src/lib/lib.routes.ts | Defines route(s) for the new Categories portlet shell. |
| core-web/libs/portlets/dot-categories/src/lib/dot-categories-shell/dot-categories-shell.component.ts | Adds a shell component providing toast/message service scope for the portlet. |
| core-web/libs/portlets/dot-categories/src/lib/dot-categories-list/store/dot-categories-list.store.ts | Implements the NgRx Signals store (load, filter, pagination/sort, breadcrumbs, CRUD, import/export, optimistic sort-order updates). |
| core-web/libs/portlets/dot-categories/src/lib/dot-categories-list/store/dot-categories-list.store.spec.ts | Adds tests for store behaviors including optimistic sort-order updates and error handling. |
| core-web/libs/portlets/dot-categories/src/lib/dot-categories-list/dot-categories-list.component.ts | Adds the main Categories list component wiring UI actions to the signals store and dialogs. |
| core-web/libs/portlets/dot-categories/src/lib/dot-categories-list/dot-categories-list.component.spec.ts | Adds component tests for search debounce, lazy-load paging/sort, dialogs, and sort-order input handlers. |
| core-web/libs/portlets/dot-categories/src/lib/dot-categories-list/dot-categories-list.component.html | Adds the PrimeNG table + toolbar UI for listing, filtering, selecting, and editing categories. |
| core-web/libs/portlets/dot-categories/src/lib/dot-categories-import/dot-categories-import.component.ts | Adds a dialog component for CSV import (merge/replace), drag/drop upload, and inline error display. |
| core-web/libs/portlets/dot-categories/src/lib/dot-categories-import/dot-categories-import.component.spec.ts | Adds unit tests covering import params, success close behavior, and error message rendering. |
| core-web/libs/portlets/dot-categories/src/lib/dot-categories-import/dot-categories-import.component.html | Adds the import dialog UI (file selection, tooltip, merge/replace options, actions). |
| core-web/libs/portlets/dot-categories/src/lib/dot-categories-create/dot-categories-create.component.ts | Adds create/edit dialog component with reactive form and auto-generated camelCase variable name in create mode. |
| core-web/libs/portlets/dot-categories/src/lib/dot-categories-create/dot-categories-create.component.spec.ts | Adds tests for create mode, create-with-parent hint, and edit mode behavior. |
| core-web/libs/portlets/dot-categories/src/lib/dot-categories-create/dot-categories-create.component.html | Adds the form UI for create/edit category dialog. |
| core-web/libs/portlets/dot-categories/src/index.ts | Exports the new portlet routes from the library entry point. |
| core-web/libs/portlets/dot-categories/project.json | Registers the Nx library project with lint and Jest targets. |
| core-web/libs/portlets/dot-categories/jest.config.ts | Adds Jest config for the new portlet library. |
| core-web/libs/portlets/dot-categories/.eslintrc.json | Adds ESLint config for the new portlet library. |
| core-web/libs/edit-content/src/lib/components/dot-edit-content-sidebar/dot-edit-content-sidebar.component.spec.ts | Updates dialog ref mocking to use a Subject for onClose. |
| core-web/libs/edit-content/src/lib/components/dot-edit-content-sidebar/components/dot-edit-content-sidebar-permissions/dot-edit-content-sidebar-permissions.component.ts | Refactors permissions dialog to reuse the new generic iframe dialog; updates lifecycle cleanup logic. |
| core-web/libs/edit-content/src/lib/components/dot-edit-content-sidebar/components/dot-edit-content-sidebar-permissions/dot-edit-content-sidebar-permissions.component.spec.ts | Updates tests to assert the new dialog component and URL building behavior. |
| core-web/libs/edit-content/src/lib/components/dot-edit-content-sidebar/components/dot-edit-content-sidebar-permissions/components/permissions-dialog/permissions-dialog.component.ts | Removes the now-replaced dedicated permissions dialog component. |
| core-web/libs/edit-content/src/lib/components/dot-edit-content-sidebar/components/dot-edit-content-sidebar-permissions/components/permissions-dialog/permissions-dialog.component.spec.ts | Removes tests for the deleted dedicated permissions dialog component. |
| core-web/libs/edit-content/src/lib/components/dot-edit-content-sidebar/components/dot-edit-content-sidebar-permissions/components/permissions-dialog/permissions-dialog.component.html | Removes template for the deleted dedicated permissions dialog component. |
| core-web/libs/data-access/src/lib/dot-categories/dot-categories.service.ts | Adds a shared data-access service for categories API calls (pagination, CRUD, import/export, sort order). |
| core-web/libs/data-access/src/lib/dot-categories/dot-categories.service.spec.ts | Adds HTTP unit tests for the new categories service, including export/import behaviors. |
| core-web/libs/data-access/src/index.ts | Exports the new DotCategoriesService from the shared data-access barrel. |
| core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories.routes.ts | Removes old app-local categories routing in favor of the new library portlet. |
| core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-permissions/dot-categories-permissions.component.ts | Removes the old categories permissions component (replaced by generic iframe dialog). |
| core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-permissions/dot-categories-permissions.component.spec.ts | Removes tests for the deleted old categories permissions component. |
| core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-permissions/dot-categories-permissions.component.html | Removes template for the deleted old categories permissions component. |
| core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/store/dot-categories-list-store.ts | Removes the old ComponentStore-based categories store. |
| core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/dot-categories-list.component.ts | Removes the old categories list component implementation. |
| core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/dot-categories-list.component.spec.ts | Removes old (disabled) test suite for the previous categories list component. |
| core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/dot-categories-list.component.scss | Removes SCSS for the deleted old categories list component. |
| core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-list/dot-categories-list.component.html | Removes template for the deleted old categories list component. |
| core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/store/dot-categories-create-edit.store.ts | Removes old create/edit state store (no longer needed with new dialog-based UX). |
| core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/dot-categories-create-edit.component.ts | Removes old create/edit component wrapper UI (tabs-based). |
| core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/dot-categories-create-edit.component.spec.ts | Removes tests for the deleted old create/edit component. |
| core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/dot-categories-create-edit.component.scss | Removes styles for the deleted old create/edit component. |
| core-web/apps/dotcms-ui/src/app/portlets/dot-categories/dot-categories-create-edit/dot-categories-create-edit.component.html | Removes template for the deleted old create/edit component. |
| core-web/apps/dotcms-ui/src/app/app.routes.ts | Updates Categories route to use MenuGuard + reuseRoute: false and lazy-load the new portlet library routes. |
| core-web/apps/dotcms-ui/src/app/api/services/dot-categories/dot-categories.service.ts | Removes old app-local PaginatorService-based categories service in favor of shared data-access service. |
| core-web/apps/dotcms-ui/src/app/api/services/dot-categories/dot-categories.service.spec.ts | Removes tests for the deleted old app-local categories service. |
oidacra
reviewed
Apr 8, 2026
oidacra
reviewed
Apr 8, 2026
oidacra
reviewed
Apr 8, 2026
oidacra
reviewed
Apr 8, 2026
oidacra
reviewed
Apr 8, 2026
oidacra
reviewed
Apr 8, 2026
oidacra
reviewed
Apr 8, 2026
oidacra
reviewed
Apr 8, 2026
oidacra
reviewed
Apr 8, 2026
oidacra
reviewed
Apr 8, 2026
oidacra
reviewed
Apr 8, 2026
- remove redundant standalone:true from all 5 new components (Angular 19+) - add $ prefix to all signals (selectedFile, importing, errorMessage, rowMenu, toolbarMenu, ptConfig, iframeSrc, minHeight) - convert private keyword to # ECMAScript private fields (except viewChild which requires public access) - use FileUpload type from primeng/fileupload instead of structural type - fix unsafe spread + cast in openEditDialog: use typed variable instead of 'as' - add URL whitelist to DotPermissionsIframeDialogComponent: reject absolute, protocol-relative, and javascript: URLs - fix null assertion on response.body in exportCategories - replace inline style= attributes with Tailwind utility classes - remove $any() cast in category row actions button - add generic <T> to handleCategoryAction in store - add i18n key categories.more.actions to Language.properties - add error path tests for all major service operations
oidacra
approved these changes
Apr 9, 2026
Open
8 tasks
This was referenced Apr 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR migrates the Categories portlet from the legacy Dojo/Java-server implementation to a modern Angular portlet using PrimeNG and NgRx Signals. It is extracted from the broader PrimeNG migration branch (`dot-categories`) and rebased on top of `main`.
Closes #34731
What changed
Frontend — New portlet
Features implemented
Backend wiring
Tests
Checklist
This PR fixes: #34731