Support unlimited-depth ViewManager groups with bulk + group editing in the Manage dialog - #4479
Open
cnrudd wants to merge 80 commits into
Open
Support unlimited-depth ViewManager groups with bulk + group editing in the Manage dialog#4479cnrudd wants to merge 80 commits into
cnrudd wants to merge 80 commits into
Conversation
* Forward slashes in group names are interpreted as sub-group delimiters, with new shared path/tree helpers in `cmp/viewmanager/GroupUtils.ts` and normalization on read in `ViewInfo`.
* ViewManager menu renders groups as recursively nested sub-menus, including within per-owner shared sections.
* Manage dialog grids converted from ag-Grid row grouping to Hoist tree grids with synthetic group rows (and owner rows on the shared tab).
* New `GroupEditor` popover input replaces the group select in the Manage dialog's edit panel, supporting both moving a single view between groups and renaming/re-parenting an entire group - renames are sent as `groupRename: {from, to}` on updateInfo, cascading server-side to all views under the renamed path (requires hoist-core v41+).
* Save As dialog group select now displays existing groups as an indented hierarchy.
Drag-and-drop between groups (issue item 3) deliberately excluded - to follow separately.
Per review feedback - the GroupEditor popover trigger now renders as an outlined button within the Group field's label row (mirroring the Unpin button style), with the popover anchored to the group path display. GroupEditorModel is exported so external triggers can toggle the popover via a component ref.
Group path select options now use the full delimited path as their label, so the select's value container and typed create-entries unambiguously read from the top level - previously the leaf-only label made "subGroup1/newGroup" look relative to the current parent when it is actually rooted. Dropdown menu items still render the indented leaf-name hierarchy.
The Move-mode toggle button and rename-pane info text now use the ViewManager's configured typeDisplayName (title-cased / pluralized as appropriate) instead of hardcoded "View"/"views".
groupPathOptionRenderer now passes react-select's dynamically-injected create option (__isNew__) through untouched, so the formatted create message displays instead of being re-rendered as a depth-0 folder row showing only the leaf of the typed path. Also sets a custom createMessageFn on the move-pane select: Create path "...".
* New ViewMultiPanelModel backs the multi-select detail panel with a visibility-only form, initialized to the selection's common value (or blank when mixed) and readonly unless all selected views are editable. * ViewMultiPanel now renders the visibility select with Save Changes/Revert buttons swapping in on dirty state, matching ViewPanel, alongside the existing bulk Pin/Unpin and Delete actions. * New updateViewsInfoAsync plumbing through ViewManagerModel/DataAccess posts to the new xhView/bulkUpdateInfo endpoint (requires hoist-core v41+).
…ty and group for selected views
* Selecting one or more group rows now shows the bulk-edit multi panel instead of the empty placeholder, targeting all views beneath the selected groups (to any nesting depth, respecting any active filter). * Group rows can be combined with individually selected views - the resulting set is deduped, so a group row plus one of its own children does not double-count. * A single group row containing exactly one view also routes to the multi panel, rather than a blank single-view panel.
…iewManager Manage dialog * Group rows in the owned and global grids now offer a right-click Edit Group action, opening a dialog to rename or re-parent the group, cascading to all views within it. On the global tab the action requires manageGlobal. * ViewPanel's group field is now the same plain move-only group select used by ViewMultiPanel - the popover-based GroupEditor with its Move View / Edit Group mode toggle has been removed, along with ViewPanelModel's pendingGroupRename staging. * Group renames anchor on one view under the group, setting its own rewritten path in the same updateInfo call since the server-side cascade excludes the source token.
* Replaced the dialog's raw model-bound inputs with a FormModel and form/formField components, gaining standard labels and inline validation display. * Group name is validated via the required constraint plus a rule rejecting the group delimiter, with a hint to re-parent via Nest Under instead.
…ager groups and Manage dialog enhancements
…ners on the shared tab Group row ids under each owner row are now owner-prefixed recursively at every depth, not just the top level - previously two owners sharing the same nested group path (e.g. Reports/Monthly) produced colliding record ids, throwing on load and breaking the entire shared tab.
…move alongside a visibility change
Group row record ids incorporate the group's path, so a renamed or re-parented group returns from the post-save refresh as a new record - collapsed and unselected. Now the grid re-expands and the renamed group row is reselected, surfacing its views in the multi panel as immediate confirmation.
Renaming or re-parenting a group onto a path that already exists (including implied ancestor paths) previously merged the two groups silently. The dialog now confirms first, noting the existing target path and the number of views that will be added to it.
* The multi panel's group select now always starts blank - a value entered always indicates a pending move, and clearing it reverts to no-change. Replaces the previous common-value/(Mixed) initialization. * The '(Top Level)' option carries a new TOP_LEVEL_VALUE sentinel (mapped to a null group on save) so an empty field neither displays as top-level nor conflates with it for dirty-checking. * Info text now notes that bulk moves discard existing nested groups, and the selection count pluralizes correctly for a single view.
… createMessageFn label does not echo the typed query The synthetic create option passes through the label-based filterOption like any other option, so a custom create message not containing the raw query verbatim caused the option to vanish, leaving 'No matches found'. The filter now always passes the create option - its visibility is governed by react-select itself.
The group selects seed their filter input with the selected option's label, so a new path typed while (Top Level) was selected arrived prefixed with that display label and was persisted literally as a group named '(Top Level)'. Group select values now resolve through a shared parseGroupSelectValue helper that strips the label prefix and handles the top-level sentinel, and create prompts display the effective parsed path.
…ctly one view selectedView now falls back to the selection's single resolved view, so a group row containing one view routes to the standard ViewPanel rather than the bulk multi panel. Simplifies detail-panel routing to a plain count check and drops the now-unneeded hasGroupRowsSelected computed.
* Confirm before any drop that moves a global view - such moves re-group the view in every user's menu. * Single-view drag ghost now shows the view's name (e.g. 'Layout "France"') rather than a count. * Move toasts now display top-center rather than the default corner.
* New context-menu item on group rows in the personal and global grids, above Expand All - hidden on view rows and when the group's views are not all editable by the current user. * Opens a focused rename dialog backed by the same form and save flow as the selection-driven GroupPanel, selecting the group row on open. * Renames of global groups now confirm before applying - they re-group global views in every user's menu. Applies to both the dialog and the GroupPanel save. * Cancelling the rename dialog resets the shared form, so the GroupPanel does not surface the discarded edit as dirty.
* A sole-selected group row now shows the ViewMultiPanel, bulk-editing the group's views - group renames are handled solely by the "Rename Group" context-menu dialog. * Deleted GroupPanel; converted GroupPanelModel into RenameGroupDialogModel, now dedicated to the rename dialog. * ViewMultiPanel passes the selected group's name through to formButtons, preserving the group-contextual delete confirm, and the group pin/unpin buttons now read with a view count (e.g. "Pin 3 layouts to your Menu").
…tree grids, removing the extra leaf indent that read as an additional nesting level
…move them to the top level * Symmetric with dropping into the empty space below the last row - exiting past the grid's top edge shows the top-level indicator and arms a one-shot drop that completes on release outside the grid, where ag-Grid fires no drop event of its own. * Re-entering the grid, dropping normally, cancelling, or closing the dialog disarms. Escape is handled directly while outside - ag-Grid does not observe it there.
…ds, where the pinned row captures hovers/drops not intended for it
* A group row can only move on its own - selections combining a group with any other rows (more groups or views) now disable dragging across the grid. * Disabled state grays the drag handles and blocks drag initiation, with the drag event handlers also guarding as a backstop.
* The "Configuring N layouts" header no longer fills the panel - a compact, non-expanding block showing the ViewManager's defaultViewIcon above the same text, with the edit fields directly beneath. * The resolved defaultViewIcon prop is passed through from the ViewManager component via the manage dialog, so app-customized icons carry into the panel.
* One card per selected view - an opaque, bordered chip bearing the ViewManager's defaultViewIcon - fanned via CSS-var driven rotation around a pivot below the hand. * Pure-CSS clamps keep large hands tidy: the per-card angle shrinks to cap the total spread, and the card overlap tightens to solve for the header width, so the hand never overflows its container.
…ple groups - no meaningful single value to display or edit
* Removed GroupField's unreachable mixed-sentinel handling - the multi panel omits the field entirely when the selection spans groups.
* Slimmed RenameGroupDialogModel of GroupPanel-era readonly plumbing - the context menu already gates the dialog to fully-editable groups.
* Renamed the newGroup field/state to subgroup ("Sub Group" in the UI) throughout the edit panels and SaveAs dialog.
* ViewPanelModel.saveAsync now skips the server call when a whitespace-only subgroup leaves no real updates.
cnrudd
marked this pull request as ready for review
July 10, 2026 22:20
# Conflicts: # CHANGELOG.md
* `ViewManagerModel.renameGroupAsync` and `DataAccess.renameGroupAsync` call hoist-core's new `xhView/renameGroup` endpoint, passing `isGlobal` to select which group namespace to rename within. * Replaces the anchor-view approach, where the Manage dialog rewrote one member view's group and passed a `groupRename` directive for the server to cascade from. That left the scope of the rename implicit in the anchor blob's ownership, and did nothing at all if no editable view was found beneath the group. `groupRename` is removed from `ViewUpdateSpec`. * A group rename is now a single atomic call covering every view beneath the path, so it can no longer half-apply.
…rement * The nested-groups and bulk-editing entry had drifted under the already-shipped 86.4.0 heading - moved to 87.0.0-SNAPSHOT. * Called out as a breaking change: v87 requires `hoist-core >= 40.4.0` for the `xhView/renameGroup` and `xhView/bulkUpdateInfo` endpoints, which do not exist on earlier servers. The hoist-core changes themselves are additive, so this is a minimum-version bump rather than a server-side break. * Corrected the `hoist-core v41` references in `ViewManagerModel` docs to v40.4 - hoist-core has no breaking changes pending, so its next release is expected to be 40.4.0.
* `renderNdjson()` ships in the same hoist-core release as the ViewManager group APIs, so this reference is v40.4 as well.
6 tasks
…ue-4469-group-rename-scope # Conflicts: # CHANGELOG.md
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.
Closes #4469.
Adds unlimited-depth nesting for
ViewManagergroups and reworks the Manage dialog around three dedicated edit panels with selection-based routing.Key changes
Reports/Sales/Monthly), rendered as nested sub-menus in the ViewManager menu and as expandable tree grids (with open/closed folder icons) in the Manage dialog.formButtonscomponent now backs all three panels.Hoist-React Select Bug Fix:
SelectwithenableCreateto always display its "Create..." option while typing, even whencreateMessageFndoes not echo the raw query.Notes
Requires hoist-core v41+ (
groupRenamecascade support and newxhView/bulkUpdateInfoendpoint) - pairs with Support nested view group renames + bulk view info updates for ViewManager hoist-core#570.See
CHANGELOG.mdfor the user-facing summary.Apps must register ag-Grid's
RowDragModuleto enable the new Manage dialog drag-and-drop - see companion Toolbox PR Register ag-Grid RowDragModule for ViewManager Manage dialog drag-and-drop toolbox#876.