Skip to content

Commit 6c27621

Browse files
authored
Merge pull request #920 from objectstack-ai/copilot/fix-row-click-issue
2 parents 56da7ef + 9d5a1f0 commit 6c27621

File tree

4 files changed

+52
-24
lines changed

4 files changed

+52
-24
lines changed

ROADMAP.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,6 +1191,14 @@ The `FlowDesigner` is a canvas-based flow editor that bridges the gap between th
11911191

11921192
**Tests:** All 32 ObjectView tests and 29 useNavigationOverlay tests pass.
11931193

1194+
### App.tsx handleRowClick Overriding All Navigation Modes (February 2026)
1195+
1196+
**Root Cause:** `App.tsx` defined a `handleRowClick` that hardcoded `setSearchParams({recordId})` (drawer-only behavior) and passed it unconditionally to `<ObjectView>` via `onRowClick={handleRowClick}`. This overrode the internal `navOverlay.handleClick` in Console's `ObjectView`, preventing page/modal/split/popover/new_window navigation modes from working.
1197+
1198+
**Fix:** Removed `handleRowClick` from `App.tsx` and the `onRowClick` prop from both `<ObjectView>` route elements. Updated Console `ObjectView` to always use `navOverlay.handleClick` (3 locations) instead of falling back to the external `onRowClick` prop. Removed the unused `onRowClick` prop from the `ObjectView` function signature.
1199+
1200+
**Tests:** All 42 ObjectView tests and 33 useNavigationOverlay tests pass (3 new tests added).
1201+
11941202
### ListView Multi-Navigation Mode Support — split/popover/page/new_window (February 2026)
11951203

11961204
**Root Cause:** While `drawer`/`modal` modes worked in the Console ObjectView, the remaining 4 navigation modes had gaps:

apps/console/src/App.tsx

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { BrowserRouter, Routes, Route, Navigate, useNavigate, useLocation, useSearchParams } from 'react-router-dom';
1+
import { BrowserRouter, Routes, Route, Navigate, useNavigate, useLocation } from 'react-router-dom';
22
import { useState, useEffect, useCallback, lazy, Suspense, useMemo, type ReactNode } from 'react';
33
import { ModalForm } from '@object-ui/plugin-form';
44
import { Empty, EmptyTitle, EmptyDescription, Button } from '@object-ui/components';
@@ -90,7 +90,6 @@ export function AppContent() {
9090
// App Selection
9191
const navigate = useNavigate();
9292
const location = useLocation();
93-
const [, setSearchParams] = useSearchParams();
9493
const { appName } = useParams();
9594
const { apps, objects: allObjects, loading: metadataLoading } = useMetadata();
9695

@@ -230,18 +229,6 @@ export function AppContent() {
230229
setIsDialogOpen(true);
231230
};
232231

233-
const handleRowClick = (record: any) => {
234-
const id = record._id || record.id;
235-
if (id) {
236-
// Open Drawer
237-
setSearchParams(prev => {
238-
const next = new URLSearchParams(prev);
239-
next.set('recordId', id);
240-
return next;
241-
});
242-
}
243-
};
244-
245232
const handleAppChange = (newAppName: string) => {
246233
navigate(`/apps/${newAppName}`);
247234
};
@@ -347,7 +334,6 @@ export function AppContent() {
347334
dataSource={dataSource}
348335
objects={allObjects}
349336
onEdit={handleEdit}
350-
onRowClick={handleRowClick}
351337
/>
352338
} />
353339

@@ -357,7 +343,6 @@ export function AppContent() {
357343
dataSource={dataSource}
358344
objects={allObjects}
359345
onEdit={handleEdit}
360-
onRowClick={handleRowClick}
361346
/>
362347
} />
363348

apps/console/src/__tests__/ObjectView.test.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -934,4 +934,39 @@ describe('ObjectView Component', () => {
934934
// ViewConfigPanel should be open in edit mode
935935
expect(screen.getByTestId('view-config-panel')).toBeInTheDocument();
936936
});
937+
938+
it('navigates to record detail in page mode even if an onRowClick prop is passed (prop is ignored)', () => {
939+
const externalRowClick = vi.fn();
940+
const objectsWithPage = [{
941+
...mockObjects[0],
942+
navigation: { mode: 'page' as const },
943+
}];
944+
mockUseParams.mockReturnValue({ objectName: 'opportunity' });
945+
render(<ObjectView dataSource={mockDataSource} objects={objectsWithPage} onEdit={vi.fn()} onRowClick={externalRowClick} />);
946+
fireEvent.click(screen.getByTestId('list-row-click'));
947+
expect(mockNavigate).toHaveBeenCalledWith('record/rec-1');
948+
expect(externalRowClick).not.toHaveBeenCalled();
949+
});
950+
951+
it('navigates to record detail when no navigation config and no onRowClick', () => {
952+
mockUseParams.mockReturnValue({ objectName: 'opportunity' });
953+
render(<ObjectView dataSource={mockDataSource} objects={mockObjects} onEdit={vi.fn()} />);
954+
fireEvent.click(screen.getByTestId('list-row-click'));
955+
expect(mockNavigate).toHaveBeenCalledWith('record/rec-1');
956+
});
957+
958+
it('opens drawer overlay instead of navigating for drawer navigation mode', () => {
959+
const objectsWithDrawer = [{
960+
...mockObjects[0],
961+
navigation: { mode: 'drawer' as const },
962+
}];
963+
mockUseParams.mockReturnValue({ objectName: 'opportunity' });
964+
const dataSourceWithFindOne = {
965+
...mockDataSource,
966+
findOne: vi.fn().mockResolvedValue({ _id: 'rec-1', id: 'rec-1', name: 'Test' }),
967+
};
968+
render(<ObjectView dataSource={dataSourceWithFindOne} objects={objectsWithDrawer} onEdit={vi.fn()} />);
969+
fireEvent.click(screen.getByTestId('list-row-click'));
970+
expect(mockNavigate).not.toHaveBeenCalledWith(expect.stringContaining('record/'));
971+
});
937972
});

apps/console/src/components/ObjectView.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ function DrawerDetailContent({ objectDef, recordId, dataSource, onEdit }: {
245245
);
246246
}
247247

248-
export function ObjectView({ dataSource, objects, onEdit, onRowClick }: any) {
248+
export function ObjectView({ dataSource, objects, onEdit }: any) {
249249
const navigate = useNavigate();
250250
const { objectName, viewId } = useParams();
251251
const [searchParams, setSearchParams] = useSearchParams();
@@ -484,7 +484,7 @@ export function ObjectView({ dataSource, objects, onEdit, onRowClick }: any) {
484484
}, [drawerRecordId]);
485485

486486
// Render multi-view content via ListView plugin (for kanban, calendar, etc.)
487-
const renderListView = useCallback(({ schema: listSchema, dataSource: ds, onEdit: editHandler, onRowClick: rowClickHandler, className }: any) => {
487+
const renderListView = useCallback(({ schema: listSchema, dataSource: ds, onEdit: editHandler, className }: any) => {
488488
const key = `${objectName}-${activeView.id}-${refreshKey}`;
489489
const viewDef = activeView;
490490

@@ -638,9 +638,9 @@ export function ObjectView({ dataSource, objects, onEdit, onRowClick }: any) {
638638
schema={fullSchema}
639639
className={className}
640640
onEdit={editHandler}
641-
onRowClick={rowClickHandler || ((record: any) => {
641+
onRowClick={(record: any) => {
642642
navOverlay.handleClick(record);
643-
})}
643+
}}
644644
dataSource={ds}
645645
/>
646646
);
@@ -832,9 +832,9 @@ export function ObjectView({ dataSource, objects, onEdit, onRowClick }: any) {
832832
activeViewId={activeViewId}
833833
onViewChange={handleViewChange}
834834
onEdit={(record: any) => onEdit?.(record)}
835-
onRowClick={onRowClick || ((record: any) => {
835+
onRowClick={(record: any) => {
836836
navOverlay.handleClick(record);
837-
})}
837+
}}
838838
renderListView={renderListView}
839839
onCreateView={handleCreateView}
840840
onViewAction={handleViewAction}
@@ -870,9 +870,9 @@ export function ObjectView({ dataSource, objects, onEdit, onRowClick }: any) {
870870
activeViewId={activeViewId}
871871
onViewChange={handleViewChange}
872872
onEdit={(record: any) => onEdit?.(record)}
873-
onRowClick={onRowClick || ((record: any) => {
873+
onRowClick={(record: any) => {
874874
navOverlay.handleClick(record);
875-
})}
875+
}}
876876
renderListView={renderListView}
877877
onCreateView={handleCreateView}
878878
onViewAction={handleViewAction}

0 commit comments

Comments
 (0)