diff --git a/ROADMAP.md b/ROADMAP.md index 5e84e5cbd4..294dd90df7 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -1050,6 +1050,21 @@ The `FlowDesigner` is a canvas-based flow editor that bridges the gap between th **Tests:** All 32 ObjectView tests and 29 useNavigationOverlay tests pass. +### ListView Multi-Navigation Mode Support — split/popover/page/new_window (February 2026) + +**Root Cause:** While `drawer`/`modal` modes worked in the Console ObjectView, the remaining 4 navigation modes had gaps: +1. Console's `onNavigate` callback relied on implicit fallthrough for `view` action (page mode) — not explicit. +2. `PluginObjectView`'s `formLayout` only mapped `drawer`/`modal` modes; `split`/`popover` fell through to the default layout (`drawer`), rendering the wrong overlay type. +3. `PluginObjectView` lacked `NavigationOverlay` integration for `split` (resizable side-by-side panels) and `popover` (compact dialog preview). + +**Fix:** +- Console `onNavigate` now explicitly checks for `action === 'view'` (page mode) alongside the existing `'new_window'` check. +- `PluginObjectView` `formLayout` now includes `split` and `popover` branches. +- `PluginObjectView` imports and renders `NavigationOverlay` from `@object-ui/components` for both `split` mode (with `mainContent` wrapping the grid) and `popover` mode (Dialog fallback when no `popoverTrigger`). +- Split mode close button properly resets form state via `handleFormCancel`. + +**Tests:** Updated split/popover tests to verify `NavigationOverlay` rendering (close panel button for split, dialog role for popover). Added split close-and-return test. All 29 PluginObjectView tests and 37 Console ObjectView tests pass. + ### ListView Grouping Mode Empty Rows (February 2026) **Root Cause:** When grouping is enabled in list view, `buildGroupTableSchema` in `ObjectGrid.tsx` sets `pagination: false` but inherits `pageSize: 10` from the parent schema. The `DataTableRenderer` filler row logic (`Array.from({ length: Math.max(0, pageSize - paginatedData.length) })`) pads each group table with empty rows up to `pageSize`, creating many blank lines. diff --git a/apps/console/src/components/ObjectView.tsx b/apps/console/src/components/ObjectView.tsx index 54767626f4..2fc12b92ed 100644 --- a/apps/console/src/components/ObjectView.tsx +++ b/apps/console/src/components/ObjectView.tsx @@ -433,10 +433,14 @@ export function ObjectView({ dataSource, objects, onEdit, onRowClick }: any) { return; } // page / view mode — navigate to record detail page - if (viewId) { - navigate(`../../record/${String(recordId)}`, { relative: 'path' }); - } else { - navigate(`record/${String(recordId)}`); + // Handles action === 'view' (from useNavigationOverlay page mode) and + // default fallthrough for any unrecognised action + if (action === 'view' || !action || action === 'page') { + if (viewId) { + navigate(`../../record/${String(recordId)}`, { relative: 'path' }); + } else { + navigate(`record/${String(recordId)}`); + } } }, }); diff --git a/packages/plugin-view/src/ObjectView.tsx b/packages/plugin-view/src/ObjectView.tsx index d0bdd101b5..4d4e86a068 100644 --- a/packages/plugin-view/src/ObjectView.tsx +++ b/packages/plugin-view/src/ObjectView.tsx @@ -49,6 +49,7 @@ import { DrawerHeader, DrawerTitle, DrawerDescription, + NavigationOverlay, Button, Tabs, TabsList, @@ -974,8 +975,57 @@ export const ObjectView: React.FC = ({ // Determine which form container to render const formLayout = navigationConfig?.mode === 'modal' ? 'modal' : navigationConfig?.mode === 'drawer' ? 'drawer' + : navigationConfig?.mode === 'split' ? 'split' + : navigationConfig?.mode === 'popover' ? 'popover' : layout; + // Build the record detail content for NavigationOverlay (split/popover modes) + const renderOverlayDetail = (record: Record) => ( +
+ +
+ ); + + // Shared handler for NavigationOverlay onOpenChange — close form when overlay is dismissed + const handleOverlayOpenChange = useCallback((open: boolean) => { + if (!open) handleFormCancel(); + }, [handleFormCancel]); + + // For split mode, wrap content inside NavigationOverlay with mainContent + if (formLayout === 'split') { + const objectLabel = (objectSchema?.label as string) || schema.objectName; + return ( +
+ {(schema.title || schema.description) && ( +
+ {schema.title &&

{schema.title}

} + {schema.description &&

{schema.description}

} +
+ )} +
{renderToolbar()}
+
+ {isFormOpen && selectedRecord ? ( + {renderContent()}
} + > + {renderOverlayDetail} + + ) : ( + renderContent() + )} +
+ + ); + } + return (
{/* Title and description */} @@ -1003,6 +1053,21 @@ export const ObjectView: React.FC = ({ {/* Form (drawer or modal) */} {formLayout === 'drawer' && renderDrawerForm()} {formLayout === 'modal' && renderModalForm()} + {/* Popover mode — uses NavigationOverlay Dialog fallback (no popoverTrigger) */} + {formLayout === 'popover' && isFormOpen && selectedRecord && ( + + {renderOverlayDetail} + + )}
); }; diff --git a/packages/plugin-view/src/__tests__/ObjectView.test.tsx b/packages/plugin-view/src/__tests__/ObjectView.test.tsx index 72b86ad885..f1b279e158 100644 --- a/packages/plugin-view/src/__tests__/ObjectView.test.tsx +++ b/packages/plugin-view/src/__tests__/ObjectView.test.tsx @@ -300,8 +300,9 @@ describe('ObjectView', () => { fireEvent.click(screen.getByTestId('grid-row')); - // Split mode should trigger form display in view mode + // Split mode renders NavigationOverlay with split panels including a close button expect(screen.getByTestId('object-form')).toBeDefined(); + expect(screen.getByLabelText('Close panel')).toBeDefined(); }); it('should open form in view mode when popover navigation mode is clicked', () => { @@ -315,8 +316,30 @@ describe('ObjectView', () => { fireEvent.click(screen.getByTestId('grid-row')); - // Popover mode should trigger form display in view mode + // Popover mode renders NavigationOverlay Dialog fallback (no popoverTrigger) expect(screen.getByTestId('object-form')).toBeDefined(); + expect(screen.getByRole('dialog')).toBeDefined(); + }); + + it('should close split panel and return to normal view', () => { + const schema: ObjectViewSchema = { + type: 'object-view', + objectName: 'contacts', + navigation: { mode: 'split' }, + }; + + render(); + + // Open split panel + fireEvent.click(screen.getByTestId('grid-row')); + expect(screen.getByLabelText('Close panel')).toBeDefined(); + + // Close split panel + fireEvent.click(screen.getByLabelText('Close panel')); + + // Form should be gone, grid should remain + expect(screen.queryByLabelText('Close panel')).toBeNull(); + expect(screen.getByTestId('object-grid')).toBeDefined(); }); });