Grid Integration Analysis
Goal: Refactor the callil item-grid organism to use the jmpicnic entity-data-grid (preferred) or entity-data-grid-shim as its base, promoting general-purpose capabilities into entity-data-grid where justified.
Source branches:
jmpicnic/list-view-management-620—entity-data-grid,entity-data-grid-shim, cell atoms,DataGridmoleculejmpicnic/callil-consolidation—item-gridorganism, typeahead/select editors, drag-to-scroll, auto-publish editing
Component Overview
Section titled “Component Overview”entity-data-grid (jmpicnic)
Section titled “entity-data-grid (jmpicnic)”Factory function createEntityDataGrid<T>(config) returning { Component }. Built on top of the DataGrid molecule.
Factory config: displayName, persistenceKeyPrefix, columnDefs, defaultColDef, getEntityId, optional enhanceEditableColumnDefs callback.
Runtime props: data, columnVisibility, columnOrder, loading, enableCellEditing, activeTab, onRowClick, onSelectionChange, paginationData, onNextPage, onPreviousPage, onFirstPage, emptyStateComponent, onEntityUpdated, onUnsavedChangesChange.
Ref API: saveAllDrafts(), getHasUnsavedChanges(), discardAllDrafts().
State management: useDirtyTracking hook tracks edited entity IDs, captures original snapshots, supports discard-all by restoring from snapshots.
Tier 3a reserved props (accepted, no-ops): enableMultiSort, onSortChanged, enableFiltering, onFilterChanged, onCellEditingStarted, onCellEditingStopped, onCellFocused, getRowClass.
entity-data-grid-shim (jmpicnic) — DEPRECATED
Section titled “entity-data-grid-shim (jmpicnic) — DEPRECATED”Decision (2026-03-19): The shim is deprecated but kept in place. Its original intent was to facilitate migration from the current
ArdaGrid-baseddev-witnesspages. With entity-data-grid gaining all needed capabilities directly (Run 5), new code should use entity-data-grid, not the shim. The shim remains available in case it is needed during migration.
Wraps createEntityDataGrid with Tier 3b features. Returns { Component }.
Additional props: enableRowActions, rowActions: RowAction<T>[], onRowDoubleClicked, hasActiveSearch, initialState.
RowAction interface: { label, icon?, onClick }. Rendered via ActionCellRenderer in a pinned-right column (48px).
Extended ref stubs: refreshData(), getSelectedRows(), selectAll(), deselectAll() — all no-ops currently.
item-grid (callil)
Section titled “item-grid (callil)”Direct component (not a factory). Built on top of the same DataGrid molecule.
Static config: height, autoHeight, enableRowSelection, editable, lookups: ItemGridLookups, pageSize, emptyMessage, emptyContent, actionsColumn, toolbar: ReactNode, className.
Runtime config: items, loading, onItemClick, onSelectionChange, onPublishRow, onDirtyChange, onNotesClick, editingRef: Ref<ItemGridEditingHandle>, gridRef.
Editing handle: saveAll(), discardAll(), getDirtyRowIds().
Curated columns (10): Image, Name, SKU, GL Code, Classification (typeahead), Supplier (typeahead), Order Method (select), Unit Cost, Order Cost, Taxable, Notes.
Already Covered by entity-data-grid
Section titled “Already Covered by entity-data-grid”These capabilities exist in entity-data-grid and require no promotion work. item-grid would consume them via factory config or runtime props.
| Capability | entity-data-grid | item-grid equivalent |
|---|---|---|
| Factory pattern + generics | createEntityDataGrid<T> | Direct component |
| Column defs via config | columnDefs in factory config | Curated columns |
| Dirty tracking | useDirtyTracking hook + ref API | useItemGridEditing hook |
| Cell editing toggle | enableCellEditing + enhanceEditableColumnDefs | editable prop |
| Row click | onRowClick | onItemClick |
| Selection change | onSelectionChange | onSelectionChange |
| Loading overlay | loading prop | loading prop |
| Empty state | emptyStateComponent prop | emptyContent/emptyMessage |
| Column visibility/order | columnVisibility + columnOrder props | Via AG Grid API directly |
| Column persistence | DataGrid molecule useColumnPersistence | Same molecule |
| Pagination | PaginationData + nav callbacks | AG Grid built-in pageSize |
Partially Covered by entity-data-grid-shim
Section titled “Partially Covered by entity-data-grid-shim”| Capability | Shim implementation | item-grid implementation | Gap |
|---|---|---|---|
| Actions column | RowAction<T>[] + pinned right (48px fixed) | actionsColumn ColDef + auto-width from actionCount | Shim is simpler; item-grid allows arbitrary ColDef with custom width calc |
| Search empty state | hasActiveSearch flag toggles “No items found” | Client-side search with filtered count display | Shim only toggles message; item-grid has full search UX |
| Double-click | onRowDoubleClicked | Not used | Covered |
General-Purpose Capabilities Requiring Promotion
Section titled “General-Purpose Capabilities Requiring Promotion”These capabilities exist in item-grid but not in entity-data-grid. Each is general-purpose and would benefit any entity grid.
1. Toolbar Slot (trivial)
Section titled “1. Toolbar Slot (trivial)”item-grid: toolbar?: ReactNode rendered above the grid alongside the search input.
Promotion cost: Add a toolbar prop to entity-data-grid view props. Render a container div above the grid. Minimal implementation.
Recommendation: Promote into entity-data-grid.
2. Auto-Height Mode (trivial)
Section titled “2. Auto-Height Mode (trivial)”item-grid: autoHeight?: boolean — disables vertical scroll, grid grows to fit content. Passes domLayout: 'autoHeight' to AG Grid.
Promotion cost: One boolean prop, one AG Grid option pass-through.
Recommendation: Promote into entity-data-grid.
3. Client-Side Search/Filtering (medium)
Section titled “3. Client-Side Search/Filtering (medium)”item-grid: Search input with 150ms debounce. Filters on name and internalSKU fields (domain-specific). Displays filtered count (“3 of 12 items”) or selection count (“3 of 12 selected”).
The field-specific filter predicate is domain-specific, but the search UI, debounce, quick-filter mechanism, and count display are general-purpose.
Promotion cost: Need to design a general API. Options:
quickFilterTextpass-through to AG Grid’s built-in quick filtersearchConfig: { fields: string[], placeholder?: string }for field-specific filteringonSearchChangecallback + external filter management
Recommendation: Promote the search bar UI and count display into entity-data-grid. The filter predicate can be domain-specific via config.
4. Drag-to-Scroll (medium)
Section titled “4. Drag-to-Scroll (medium)”item-grid: Mouse-drag horizontal scroll on grid body. 5px threshold before activating. Cursor changes to “grabbing”. Cell focus cleared on drag start. Click event suppressed after drag ends (100ms timeout). Ignores drags on header, popups, inputs, buttons.
Promotion cost: ~80 lines of pointer-event logic. Could be extracted as a reusable hook (useDragToScroll). Useful for any wide grid.
Recommendation: Promote into entity-data-grid (or DataGrid molecule) as an opt-in behavior.
5. Row State Visual Feedback (medium)
Section titled “5. Row State Visual Feedback (medium)”item-grid: Rows get .ag-row-saving (light primary background) and .ag-row-error (light destructive background) CSS classes during the publish lifecycle. Managed via RowEditState type: 'idle' | 'saving' | 'error'.
entity-data-grid: Has dirty tracking but no per-row visual state during save operations.
Promotion cost: Need a rowStateMap: Record<string, RowState> or similar mechanism, plus CSS class injection via AG Grid’s getRowClass callback. The Tier 3a reserved prop getRowClass already exists as a no-op — this would implement it.
Recommendation: Promote. Activate the reserved getRowClass Tier 3a prop and add built-in saving/error states.
6. Auto-Publish on Row Blur (hard — opinionated lifecycle)
Section titled “6. Auto-Publish on Row Blur (hard — opinionated lifecycle)”item-grid: When cell editing stops and no more cells are editing in the row, auto-publish after 50ms delay. Row gets saving state visual feedback. Calls onPublishRow(rowId, changes, item?) which is async. On success, row returns to idle. On failure, row gets error state.
entity-data-grid: Fires onEntityUpdated(entity) per individual cell change. No row-level batching. No auto-publish. No async lifecycle.
This is the biggest semantic gap. entity-data-grid treats editing as cell-granular (fire-and-forget per cell). item-grid treats editing as row-granular with async auto-save and visual lifecycle feedback.
Promotion cost: Significant API design work. Options:
- Add
editingMode: 'cell' | 'row-auto-publish'to entity-data-grid - When
row-auto-publish: batch cell changes per row, auto-publish on row blur, provide asynconRowPublishcallback, manage row visual state - When
cell(current behavior): unchanged
Decision (2026-03-19): Promote into entity-data-grid. Row-auto-publish is the only editing mode — no need to support the cell-granular onEntityUpdated mode. The existing cell-granular callback is replaced. Feasibility to be confirmed during implementation design.
7. Typeahead Cell Editor (hard — complex component)
Section titled “7. Typeahead Cell Editor (hard — complex component)”item-grid: Full async typeahead cell editor with:
- Async lookup function with 150ms debounce
- Keyboard navigation (Arrow Up/Down, Enter, Escape)
- Optional “Create [value]” option when value not in results
- Loading spinner, error state (“Search failed”)
- Max 8 results, scroll highlighted into view
- Abort previous search on new input
entity-data-grid: Has no typeahead editor. Cell editor atoms are: text, number, date, enum, boolean, color, memo.
Promotion cost: ~150 lines. This is a molecule-level component. Should become a new canary atom or molecule (typeahead-cell-editor), not embedded in entity-data-grid itself. entity-data-grid would gain it as an available cell editor type.
Recommendation: Extract as a standalone canary molecule. item-grid references it via column definitions. entity-data-grid doesn’t need to know about it — it’s a cell editor registered in ColDef, which AG Grid handles natively.
8. Select Cell Editor (medium — compare with existing enum editor)
Section titled “8. Select Cell Editor (medium — compare with existing enum editor)”item-grid: Fixed-list dropdown with keyboard navigation, checkmark for selected option, max height 240px with scroll.
entity-data-grid: The enum atom has EnumCellEditor which serves a similar purpose.
Requires deeper comparison: The two implementations may be functionally equivalent or have meaningful UX differences. If the enum editor covers the select use case, no promotion needed — item-grid would use the enum editor with its OrderMethod options.
Decision (2026-03-19): Callil’s SelectCellEditor becomes the canonical enum/select editor, promoted to a canary atom at atoms/grid/select/. The jmpicnic EnumCellEditor (native <select> wrapper) is replaced. The options format is generalized to accept either SelectOption[] or Record<string, string>. Follows the existing canary cell editor factory convention: exports both SelectCellEditor (direct component) and createSelectCellEditor (factory function with static config baked in), consistent with createTextCellEditor, createEnumCellEditor, etc.
Purely Domain-Specific (Stays in item-grid Config)
Section titled “Purely Domain-Specific (Stays in item-grid Config)”These capabilities are inherently tied to the item/inventory domain and should remain as item-grid configuration passed to entity-data-grid:
- Curated column definitions: Name, SKU, GL Code, Classification, Supplier, Order Method, Unit Cost, Order Cost, Taxable, Notes
- Nested field value getters/setters:
primarySupply.supplier,primarySupply.unitCost.value, etc. - Item-specific lookups: Supplier and Classification typeahead functions via
ItemGridLookups - OrderMethod renderer: Badge-style label with formatted text for 9 fixed enum values
- Notes icon renderer: Icon button, filled if notes present, calls
onNotesClick - Image renderer: Thumbnail with initials fallback circle
- Taxable checkbox: Interactive boolean toggle (click to change)
- Search fields: Filters on
name+internalSKUspecifically
Key Tradeoff: Factory Config vs Wrapper
Section titled “Key Tradeoff: Factory Config vs Wrapper”The ideal outcome is item-grid as a pure createEntityDataGrid(itemGridConfig) call. The pragmatic fallback is a wrapper component.
Path to Pure Factory Config
Section titled “Path to Pure Factory Config”Requires promoting into entity-data-grid:
- Toolbar slot (trivial)
- Auto-height (trivial)
- Search/filter UI (medium)
- Drag-to-scroll (medium)
- Row state visual feedback (medium)
- Auto-publish editing lifecycle (hard)
- Typeahead cell editor as standalone molecule (hard, but decoupled)
If all seven are done, item-grid becomes:
const { Component: ItemGrid } = createEntityDataGrid<Item>({ displayName: 'ItemGrid', persistenceKeyPrefix: 'item-grid', columnDefs: itemGridColumnDefs, defaultColDef: itemGridDefaultColDef, getEntityId: (item) => item.entityId, enhanceEditableColumnDefs: itemGridEditableEnhancements, editingMode: 'row-auto-publish', searchConfig: { fields: ['name', 'internalSKU'] }, enableDragToScroll: true,});Path to Wrapper Component
Section titled “Path to Wrapper Component”If auto-publish (#6) stays out of entity-data-grid:
// ItemGrid wraps EntityDataGrid to manage row-level editing lifecyclefunction ItemGrid(props: ItemGridProps) { const editing = useItemGridEditing(/* ... */); const { Component: BaseGrid } = createEntityDataGrid<Item>(itemGridConfig);
return ( <div> <SearchBar /> <Toolbar /> <BaseGrid data={props.items} onEntityUpdated={editing.handleCellChange} /* ... */ /> </div> );}This is still a significant improvement over the current item-grid which builds directly on the DataGrid molecule, bypassing entity-data-grid entirely.
Open Questions
Section titled “Open Questions”Auto-publish lifecycle: Decided (2026-03-19) — Promote into entity-data-grid aseditingMode: 'row-auto-publish'. Feasibility confirmed during implementation design.Select vs Enum editor: Decided (2026-03-19) — Callil’s SelectCellEditor replaces jmpicnic’s EnumCellEditor. Promoted to canary atom with generalized options format.Pagination approach: Decided (2026-03-19) — Support both modes via a StaticConfig (design-time) property on the factory config. Server-driven (PaginationData+ callbacks) and client-side (AG Grid built-inpageSize) are both valid; the choice is made at factory creation time, not switchable at runtime.Actions column: Decided (2026-03-19) — Use item-grid’sactionsColumnColDef pattern (full column definition with optionalactionCountfor width calc). Configured as an InitConfig (mount-time) property, since contents may be determined by user/tenant settings in the future. The shim’s simplerRowAction[]pattern is dropped.AG Grid theme: Decided (2026-03-19) — Item-grid’sthemeQuartzwith design-system token mapping is canonical. The DataGrid molecule’s'legacy'theme string is retrofitted to usethemeQuartzwith the same custom params (row height, header height, font, cell padding, color tokens, borders).
Copyright: © Arda Systems 2025-2026, All rights reserved