Skip to content

AG Grid Issues Analysis

Consolidated issue list for the AG Grid implementation in arda-frontend-app, merging the deep-dive inventory and the compliance audit. Includes a phased implementation plan for restoring normal AG Grid behavior.

Date: 2026-02-23

H1 — Two Competing Column Persistence Systems

Section titled “H1 — Two Competing Column Persistence Systems”

Files: ArdaGrid.tsx lines 524–745, ItemTableAGGrid.tsx lines 1831–1994

Both ArdaGrid and ItemTableAGGrid write to localStorage key items-grid-${activeTab} at overlapping times, overwriting each other.

Impact: Column order resets after switching tabs. View dropdown visibility toggles sometimes revert.

Resolution: One owner for persistence. Either ArdaGrid owns it entirely (pass enableColumnStatePersistence={false} from ItemTableAGGrid), or ItemTableAGGrid owns it and ArdaGrid persistence is disabled.

H2 — ~20 setTimeout Calls Used for State Coordination

Section titled “H2 — ~20 setTimeout Calls Used for State Coordination”

Files: ArdaGrid.tsx, ItemTableAGGrid.tsx

Timing delays coordinate state between layers instead of proper AG Grid lifecycle events. Delays in use: 0, 50, 100, 150, 200, 300, 400, 500, 600, 1500, 2000ms.

Impact: On slow machines the 200ms assumption that columns are ready breaks. Any change to render timing breaks coordination.

Resolution: Use AG Grid lifecycle events: onGridReady for column state, onFirstDataRendered for selection restore.

H3 — Column Visibility Has Three Sources of Truth

Section titled “H3 — Column Visibility Has Three Sources of Truth”

Files: ArdaGrid.tsx, ItemTableAGGrid.tsx

Three places all fire around mount time and set visibility:

  1. React render — hide: !isVisible from columnVisibility prop on each column def.
  2. ArdaGrid persistence restore — api.applyColumnState() 200ms after mount.
  3. ItemTableAGGrid handleGridReadyapi.setColumnsVisible() for each column.

Impact: Columns flash visible then hide on first load. Tab switch causes visibility bleed.

Resolution: Make columnVisibility prop the single runtime source of truth. Only persist column order and width — not visibility — in localStorage.

H4 — Select Editors Missing destroy() — Memory Leak

Section titled “H4 — Select Editors Missing destroy() — Memory Leak”

File: ItemTableAGGrid.tsx — 5 class-based select editors

afterGuiAttached() adds event listeners that are never removed because destroy() is not implemented.

Resolution: Replace all 5 with AG Grid’s built-in agSelectCellEditor (see compliance-refactor.md H1 for the before/after pattern).

H5 — QuickActionsCell Fires N+1 API Requests on Mount

Section titled “H5 — QuickActionsCell Fires N+1 API Requests on Mount”

File: columnPresets.tsxQuickActionsCell renderer

50 visible rows = 50 extra API calls on every page load.

Resolution: Move card count fetching to the grid data source.

H6 — Select-All Fires N Selection-Changed Events

Section titled “H6 — Select-All Fires N Selection-Changed Events”

Files: columnPresets.tsx, DesktopScanView.tsx

node.setSelected() per node fires one event per row.

Resolution: Use api.setNodesSelected({ nodes, newValue }) or AG Grid’s built-in rowSelection.headerCheckbox.

H7 — exportDataAsExcel Calls Enterprise API

Section titled “H7 — exportDataAsExcel Calls Enterprise API”

File: ArdaGrid.tsx

Enterprise API called with only Community modules registered — throws at runtime.

Resolution: Remove from ArdaGrid API, or replace with exportDataAsCsv().

H8 — Full Data Refresh Triggered on Every Cell Edit

Section titled “H8 — Full Data Refresh Triggered on Every Cell Edit”

File: ItemTableAGGrid.tsx

Edit → publish → onRefreshRequested() → full data reload → selection lost → polling loop. Rapid edits create draft conflicts.

Resolution: Use gridApi.applyTransaction({ update: [updatedItem] }) for incremental updates. Debounce onRefreshRequested.

IDIssueLocationResolution
M1getOrderedColumnDefs reads localStorage on every render — not in useMemoItemTableAGGrid.tsxWrap in useMemo([activeTab, columnVisibility])
M2Module-level lastSelectedRowIndex shared across all grid instancescolumnPresets.tsx, DesktopScanView.tsxMove to useRef per instance; eliminated by H6 fix
M3headerStyle silently ignored — not a valid ColDef propertycolumnPresets.tsxReplace with headerClass
M4Inline cellRenderer arrow functions recreate component on every rendercolumnPresets.tsxUse valueFormatter for text-only columns
M5Direct mutation of rowData objects bypasses React immutabilityItemTableAGGrid.tsxUse applyTransaction
M6document.querySelector('.ag-theme-arda') matches wrong grid when scan modal is openItemTableAGGrid.tsxUse a React ref to the grid container
M7afterGuiAttached focus-click loop in select editorsItemTableAGGrid.tsxEliminated by H4 fix
M8QuickActions buttons overflow at 480px breakpointArdaGrid.cssIncrease min-row-height or clip overflow
IDIssueLocationFix
L1suppressMultiRangeSelection removed in v33+ArdaGrid.tsxRemove
L2suppressSizeToFit deprecated in v31columnPresets.tsxReplace with suppressAutoSize
L3RowNode import should be IRowNode in v34columnPresets.tsxUpdate import
L4rowSelection: 'single' deprecated syntaxArdaGrid.tsxUpdate to { mode: 'singleRow' }
L5SelectAllHeaderComponent typed as anycolumnPresets.tsxAdd proper typing
L6Supplier URL link missing onMouseDown stopPropagationcolumnPresets.tsxAdd handler
L7300ms single-click delay feels sluggishItemTableAGGrid.tsxReduce to 200ms
L8Field-to-ViewKey mapping duplicated 3 times in same fileItemTableAGGrid.tsxExtract single FIELD_VIEW_MAP constant
  1. Typeahead editors implement full destroy() lifecycle with setTimeout(() => root.unmount(), 0) — correct for React 19.
  2. createPortal for modals and typeahead dropdowns escapes overflow: hidden cells.
  3. stopPropagation on interactive elements prevents unwanted row selection.
  4. isCancelAfterEnd() + wasCancelled flag correctly handles ESC.
  5. Draft deduplication via draftPromisesMapRef prevents concurrent draft creation.
  6. publishingRowsRef prevents concurrent publishes of the same row.

Fix bugs causing crashes and memory leaks with no visible UI change.

  1. Step 1.1 — Remove exportDataAsExcel from ArdaGrid interface and useImperativeHandle (H7).
  2. Step 1.2 — Add destroy() to all 5 select editors, storing the focus handler as a named reference so it can be removed (H4).
  3. Step 1.3 — Fix deprecated API warnings: update rowSelection to new object syntax, remove suppressMultiRangeSelection (L1, L4).

Eliminate competing writes to localStorage and the three-source visibility problem.

  1. Step 2.1 — Pass enableColumnStatePersistence={false} to ArdaGrid from ItemTableAGGrid (H1).
  2. Step 2.2 — Remove hide: !isVisible from baseColumnDefs; let handleGridReady be the sole visibility setter (H3).
  3. Step 2.3 — Remove double-save setTimeout(saveGridStateOnReady, 400/600) calls (H2 partial).
  4. Step 2.4 — Wrap getOrderedColumnDefs() in useMemo([activeTab]) (M1).

Phase 3 — Fix Selection and Rendering Performance

Section titled “Phase 3 — Fix Selection and Rendering Performance”
  1. Step 3.1 — Replace node.setSelected() loop with api.setNodesSelected({ nodes, newValue }) (H6).
  2. Step 3.2 — Move lastSelectedRowIndex to useRef per grid instance (M2).
  3. Step 3.3 — Switch text-only columns from cellRenderer to valueFormatter (M4).

Phase 4 — Fix the Edit → Refresh → Flicker Loop

Section titled “Phase 4 — Fix the Edit → Refresh → Flicker Loop”
  1. Step 4.1 — Use applyTransaction for notes mutations instead of direct mutation (M5, H8 partial).
  2. Step 4.2 — Debounce onRefreshRequested to batch rapid edits into one reload (H8).
  3. Step 4.3 — Replace selection polling loop with onFirstDataRendered event (H2 partial).
  1. Step 5.1 — Extract single FIELD_VIEW_MAP constant from 3 duplicated mappings (L8).
  2. Step 5.2 — Replace headerStyle with headerClass (M3).
  3. Step 5.3 — Replace document.querySelector with a React ref (M6).

Phases 1 and 2 are the minimum needed to stop the most disruptive bugs. Phases 3–5 improve performance and reduce maintenance burden. Phase 4 Step 2 (H5 QuickActionsCell refactor) is the most complex and may be deferred to a separate sprint.

ChangeLines RemovedLines Added
H4: Replace 5 select editors~500~15
H6: Replace custom select-all~189~5
Typeahead editor factory~1,170~30
M4: valueFormatter for text columns~50~50
M5: Immutable rowData updates~5~5
H5: Batch card data fetching~30~50
H7: Fix or remove Excel export~5~3
H1+H3: Single persistence owner~200~20
H2: Remove setTimeout coordination~60~0
L1–L8, M3, M6, M8 cleanup~30~20
Total~2,239~198

Net reduction: approximately 2,040 lines of custom code replaced by AG Grid built-ins and cleaner patterns.