Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 38 additions & 43 deletions packages/plugin-grid/src/ObjectGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@ import { getCellRenderer, formatCurrency, formatCompactCurrency, formatDate, for
import {
Badge, Button, NavigationOverlay,
Popover, PopoverContent, PopoverTrigger,
DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger,
} from '@object-ui/components';
import { usePullToRefresh } from '@object-ui/mobile';
import { Edit, Trash2, MoreVertical, ChevronRight, ChevronDown, Download, Rows2, Rows3, Rows4, AlignJustify, Type, Hash, Calendar, CheckSquare, User, Tag, Clock } from 'lucide-react';
import { ChevronRight, ChevronDown, Download, Rows2, Rows3, Rows4, AlignJustify, Type, Hash, Calendar, CheckSquare, User, Tag, Clock } from 'lucide-react';
import { useRowColor } from './useRowColor';
import { useGroupedData } from './useGroupedData';
import { GroupRow } from './GroupRow';
import { useColumnSummary } from './useColumnSummary';
import { RowActionMenu, formatActionLabel } from './components/RowActionMenu';
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

formatActionLabel is imported from ./components/RowActionMenu but not used in this file anymore. This will typically fail lint/typecheck with an unused import. Remove the unused import (or use it if intended).

Suggested change
import { RowActionMenu, formatActionLabel } from './components/RowActionMenu';
import { RowActionMenu } from './components/RowActionMenu';

Copilot uses AI. Check for mistakes.
import { BulkActionBar } from './components/BulkActionBar';

export interface ObjectGridProps {
schema: ObjectGridSchema;
Expand All @@ -52,14 +53,6 @@ export interface ObjectGridProps {
onAddRecord?: () => void;
}

/**
* Format an action identifier string into a human-readable label.
* e.g., 'send_email' → 'Send Email'
*/
function formatActionLabel(action: string): string {
return action.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
}

/**
* Helper to get data configuration from schema
* Handles both new ViewData format and legacy inline data
Expand Down Expand Up @@ -137,6 +130,7 @@ export const ObjectGrid: React.FC<ObjectGridProps> = ({
const [refreshKey, setRefreshKey] = useState(0);
const [showExport, setShowExport] = useState(false);
const [rowHeightMode, setRowHeightMode] = useState<'compact' | 'short' | 'medium' | 'tall' | 'extra_tall'>(schema.rowHeight ?? 'medium');
const [selectedRows, setSelectedRows] = useState<any[]>([]);

// Column state persistence (order and widths)
const columnStorageKey = React.useMemo(() => {
Expand Down Expand Up @@ -756,37 +750,15 @@ export const ObjectGrid: React.FC<ObjectGridProps> = ({
header: 'Actions',
accessorKey: '_actions',
cell: (_value: any, row: any) => (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon" className="h-8 w-8 min-h-[44px] min-w-[44px] sm:min-h-0 sm:min-w-0" data-testid="row-action-trigger">
<MoreVertical className="h-4 w-4" />
<span className="sr-only">Open menu</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
{operations?.update && onEdit && (
<DropdownMenuItem onClick={() => onEdit(row)}>
<Edit className="mr-2 h-4 w-4" />
Edit
</DropdownMenuItem>
)}
{operations?.delete && onDelete && (
<DropdownMenuItem onClick={() => onDelete(row)}>
<Trash2 className="mr-2 h-4 w-4" />
Delete
</DropdownMenuItem>
)}
{schema.rowActions?.map(action => (
<DropdownMenuItem
key={action}
onClick={() => executeAction({ type: action, params: { record: row } })}
data-testid={`row-action-${action}`}
>
{formatActionLabel(action)}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
<RowActionMenu
row={row}
rowActions={schema.rowActions}
canEdit={!!(operations?.update && onEdit)}
canDelete={!!(operations?.delete && onDelete)}
onEdit={onEdit}
onDelete={onDelete}
onAction={(action, r) => executeAction({ type: action, params: { record: r } })}
/>
),
sortable: false,
},
Expand Down Expand Up @@ -869,7 +841,10 @@ export const ObjectGrid: React.FC<ObjectGridProps> = ({
onAddRecord: onAddRecord,
rowClassName: schema.rowColor ? (row: any, _idx: number) => getRowClassName(row) : undefined,
frozenColumns: effectiveFrozenColumns,
onSelectionChange: onRowSelect,
onSelectionChange: (rows: any[]) => {
setSelectedRows(rows);
onRowSelect?.(rows);
},
onRowClick: navigation.handleClick,
onCellChange: onCellChange,
onRowSave: onRowSave,
Expand Down Expand Up @@ -1250,6 +1225,9 @@ export const ObjectGrid: React.FC<ObjectGridProps> = ({
</div>
) : null;

// Bulk actions — support both batchActions (ObjectUI) and bulkActions (spec) names
const effectiveBulkActions = schema.batchActions ?? (schema as any).bulkActions;

// Render grid content: grouped (multiple tables with headers) or flat (single table)
const gridContent = isGrouped ? (
<div className="space-y-2">
Expand Down Expand Up @@ -1280,7 +1258,18 @@ export const ObjectGrid: React.FC<ObjectGridProps> = ({
<NavigationOverlay
{...navigation}
title={detailTitle}
mainContent={<>{gridToolbar}{gridContent}</>}
mainContent={
<>
{gridToolbar}
{gridContent}
<BulkActionBar
selectedRows={selectedRows}
actions={effectiveBulkActions ?? []}
onAction={(action, rows) => executeAction({ type: action, params: { records: rows } })}
onClearSelection={() => setSelectedRows([])}
/>
Comment on lines +1265 to +1270
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

onClearSelection={() => setSelectedRows([])} only clears ObjectGrid's local selectedRows state, but it does not clear the underlying DataTable's internal selection state (checkboxes will remain checked). Consider wiring a real selection reset (e.g., add a clear-selection signal/controlled selection to the DataTable schema, or force a remount/reset of the DataTable selection state) and also notify onRowSelect with an empty array when cleared.

Copilot uses AI. Check for mistakes.
</>
}
>
{(record) => renderRecordDetail(record)}
</NavigationOverlay>
Expand All @@ -1299,6 +1288,12 @@ export const ObjectGrid: React.FC<ObjectGridProps> = ({
)}
{gridToolbar}
{gridContent}
<BulkActionBar
selectedRows={selectedRows}
actions={effectiveBulkActions ?? []}
onAction={(action, rows) => executeAction({ type: action, params: { records: rows } })}
onClearSelection={() => setSelectedRows([])}
/>
Comment on lines +1291 to +1296
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as the split-pane path: onClearSelection={() => setSelectedRows([])} hides the BulkActionBar but does not clear the DataTable's actual row selection state. Clearing should reset the table selection itself (and ideally propagate onRowSelect([])), otherwise the UI ends up with checked rows but no bulk bar.

Copilot uses AI. Check for mistakes.
{navigation.isOverlay && (
<NavigationOverlay
{...navigation}
Expand Down
Loading