# AG Grid & Data Table — Mandatory Rules

**When working with DataTable, or any data table that uses AG Grid (including Impact Nova's grid components), the following rules are mandatory. Do not deviate.**

---

## 1. Use AG Grid documentation only

- **Refer only to AG Grid's official documentation** for grid behavior, APIs, column definitions, filtering, sorting, and all grid features.
- Do not rely on third-party tutorials or generic "data grid" patterns that contradict or bypass AG Grid's docs.
- Official docs: [ag-grid.com/documentation](https://www.ag-grid.com/documentation/) (React: [AG Grid React](https://www.ag-grid.com/react-data-grid/)).

---

## 2. Follow AG Grid recommended patterns

- **Always follow AG Grid's recommended patterns** for:
  - Column definitions (`ColDef`, `ColGroupDef`)
  - Cell renderers and editors
  - Filtering, sorting, and row selection
  - API ref access (`gridRef.current?.api`)
  - Theming and styling (AG Grid theme / CSS variables)
- Do not invent custom patterns that bypass or replace AG Grid's intended usage.

---

## 3. Use the AG Grid API only

- **Rely on the AG Grid API only** for all grid operations:
  - Getting/setting data, refreshing cells, updating columns
  - Export (CSV/Excel if using Enterprise)
  - Filter/sort state, row selection, pinned columns
  - Any behavior that AG Grid exposes via its API
- Do not manipulate the DOM or internal structure of the grid directly. Do not use non-API workarounds unless AG Grid docs explicitly suggest them.

---

## 4. Collaboration with ag-mcp (when installed)

- **If the user has installed the ag-mcp server** (AG Grid MCP), this MCP can collaborate with it:
  - Use **ag-mcp** for AG Grid–specific questions: API reference, column config, React integration, and official examples.
  - Use **impact-nova-mcp** for Impact Nova wrappers (`DataTable`, `DataTableContent`, `processBackendColumnDefs`, `BackendColDef`, cell renderers from `impact-nova/ag-grid-react/cell-renderers`).
  - When generating or editing grid code: prefer fetching AG Grid details from ag-mcp when available, and combine with Impact Nova's DataTable/column/cell-renderer patterns from this MCP.
- If ag-mcp is not installed, still follow rules 1–3 using AG Grid's official documentation only.

---

**Summary:** For any code or design involving AG Grid or Impact Nova's DataTable (which uses AG Grid), use **only** AG Grid documentation, **only** AG Grid recommended patterns, and **only** the AG Grid API—with no deviation. When ag-mcp is available, use it to get accurate AG Grid API and docs; use this MCP for Impact Nova–specific integration.

---

## 5. Mandatory Data Table component usage

**CRITICAL RULE:** If you see a table in a screenshot, or if the user asks for a table by default, you **MUST** use **Impact Nova DataTable** from `impact-nova/data-table`. **Do not build a raw AG Grid or HTML table.**

```tsx
import {
  DataTable,
  DataTableContent,
  DataTableToolbar,
  useDataTable,
} from 'impact-nova/data-table';
```

A typical Default Data Table in Impact Nova features this exact structure:

```tsx
const DataTableWithFilters = () => {
    const { tStory } = useStorybookStoryI18n();
    const [sheetOpen, setSheetOpen] = useState(false);
    const [activeTab, setActiveTab] = useState("columns");
    const [showFilterStrip, setShowFilterStrip] = useState(false);

    return (
    <div className="h-[800px] w-full p-8 bg-slate-50 flex flex-col items-center justify-center">
       <div className="w-full max-w-[1200px] h-[600px] bg-white rounded-lg overflow-hidden flex flex-col [box-shadow:0px_0px_4px_0px_rgba(0,0,0,0.12)]">
        <DataTable className="h-full">
            <DataTableToolbar className="border-b border-[#e6e8f0]">
                <div className="flex items-center gap-2">
                    <h2 className="font-bold text-sm text-slate-800">Sales Report</h2>
                </div>
                
                <div className="flex items-center gap-2">
                    <Button 
                        variant="secondary" 
                        size="icon" 
                        onClick={() => setShowFilterStrip(!showFilterStrip)}
                        aria-label={showFilterStrip ? tStory('filterStrip.hideFilters') : tStory('filterStrip.showFilters')}
                    >
                        {showFilterStrip ? <FunnelHide size="xs" /> : <FunnelShow size="xs" />}
                    </Button>

                    <div className="h-4 w-[1px] bg-[#e6e8f0] mx-1" />

                    {/* Scoped Sheet for Settings - Renders inside this relative container */}
                    <DataTableSheet open={sheetOpen} onOpenChange={setSheetOpen}>
                        <DropdownMenu>
                            <DataTableViewMenuTrigger />
                            <DataTableViewMenuContent>
                                <DataTableViewMenuSettingsItem />
                                <DataTableViewMenuDensity />
                            </DataTableViewMenuContent>
                        </DropdownMenu>
                        
                        <DataTableSheetContent>
                            <DataTableSheetHeader title="Table Settings" />
                            <Tabs value={activeTab} onValueChange={setActiveTab} variant="line" hideInactiveLabel tooltipClassName="z-[110]" className="flex-1 flex flex-col min-h-0 w-full px-4">
                                <TabsList>
                                    <TabsTrigger 
                                        value="columns" 
                                        icon={<Column size={16} />}
                                    >
                                        Columns
                                    </TabsTrigger>
                                    <TabsTrigger 
                                        value="format" 
                                        icon={<Font size={16} />}
                                    >
                                        Format
                                    </TabsTrigger>
                                    <TabsTrigger 
                                        value="custom-filters" 
                                        icon={<Filter size={16} />}
                                    >
                                        Filters
                                    </TabsTrigger>
                                </TabsList>
                                
                                <TabsContent value="columns" className="flex-1 min-h-0 relative p-0 data-[state=inactive]:hidden mt-0">
                                     <div className="flex-1 h-full p-2">
                                        <DataTableColumnList />
                                     </div>
                                </TabsContent>
                                <TabsContent value="format" className="flex-1 min-h-0 relative p-4 pt-1 data-[state=inactive]:hidden overflow-y-auto mt-0">
                                     <DataTableFormatOptions />
                                </TabsContent>
                                <TabsContent value="custom-filters" className="flex-1 min-h-0 relative p-4 pt-1 data-[state=inactive]:hidden overflow-y-auto mt-0">
                                     <div className="flex flex-col gap-4 text-sm text-[#60697d]">
                                         <p>Custom filter configuration would go here.</p>
                                         <Button variant="outline" className="w-full justify-start">
                                            + Add Condition
                                         </Button>
                                     </div>
                                </TabsContent>
                            </Tabs>
                        </DataTableSheetContent>
                    </DataTableSheet>
                </div>
            </DataTableToolbar>
            
            <DataTableContent
                rowData={rowData}
                columnDefs={columnDefs}
                // Standard AG Grid props work here
                pagination={true}
                paginationPageSize={20}
            />
        </DataTable>
      </div>
      <p className="mt-4 text-slate-400 text-sm">
        Note: The settings panel opens *inside* the table container, respecting its boundaries.
      </p>
    </div>
    );
};
```
Note: This component (`DataTable`) internally uses `ag-grid-react` as a customized version. Rely on it rather than naked AG Grid.

---

## 6. Built-in Clipboard Handlers for JSON Objects

**All DataTable and AG Grid instances in Impact Nova automatically support copying and pasting JSON objects.**

The `AgGridWrapper` component includes built-in clipboard handlers that:
- **When copying**: Automatically stringify object values to JSON format
- **When pasting**: Automatically parse JSON strings back to objects

### How it works

```tsx
// When you copy a cell with an object value like:
{ 
  wp: { value: 6479, _isDisabled: true, cellMetadata: {...} },
  iaf: { value: 6090, _isDisabled: true, cellMetadata: {...} }
}

// It's automatically converted and split into separate Excel columns:
// Column 1      Column 2
// WP: 6479      IAF: 6090

// Technical metadata (_isDisabled, cellMetadata) is hidden for clarity
// Only the meaningful "value" is shown - perfect for PMs and non-technical users
// Tab-separated format automatically expands into multiple columns in Excel
```

**Example with simple properties:**
```tsx
// Copy this object:
{ name: "John", age: 30, status: "active" }

// Expands into 3 Excel columns:
// Column 1      Column 2    Column 3
// name: John    age: 30     status: active
```

**When pasting:**
```tsx
// You can paste JSON strings and they'll be parsed back to objects:
'{"name":"Jane","age":25}' → { name: "Jane", age: 25 }
```

**Benefits:**
- ✅ Simple and readable for non-technical users (PMs, stakeholders)
- ✅ Extracts only meaningful values, hides technical metadata
- ✅ Automatically expands object properties into separate Excel columns
- ✅ Perfect for analysis - each property gets its own column
- ✅ Works seamlessly for both single-cell and multi-cell copy operations
- ✅ Still supports pasting JSON back for developers

### Usage

**No configuration needed!** This works automatically for all DataTable instances:

```tsx
<DataTable>
  <DataTableContent
    rowData={data}
    columnDefs={columns}
    // Clipboard handlers are already active ✅
  />
</DataTable>
```

### Override if needed

You can override the default behavior by passing your own handlers:

```tsx
<DataTableContent
  rowData={data}
  columnDefs={columns}
  processCellForClipboard={(params) => {
    // Custom copy logic
    return customFormat(params.value);
  }}
  processCellFromClipboard={(params) => {
    // Custom paste logic
    return customParse(params.value);
  }}
/>
```

**Key benefits:**
- ✅ Works automatically for all tables
- ✅ No code duplication needed
- ✅ Handles complex object structures
- ✅ Gracefully falls back to string if JSON parsing fails
- ✅ Can be overridden when custom behavior is needed

---

## 6. Column autosize on container resize

**AgGridWrapper** (from `impact-nova/ag-grid-react`) automatically re-runs `autoSizeStrategy` when the grid container width changes (accordion expand, viewport resize, responsive layout).

- Pass `autoSizeStrategy` to DataTable/AgGridWrapper props (e.g. `{ type: 'fitCellContents', scaleUpToFitGridWidth: true }`).
- **Do not** add app-level ResizeObserver hacks or deferred grid mount delays — the framework handles this.
- When applying saved column views, wait for `firstDataRendered` or `newColumnsLoaded` grid events before applying column state — not `setTimeout(0)`.
- Strip `width`/`flex` from saved column state when applying structural views so autosize can recalculate correctly.

---

## 6b. Truncated text in custom cell renderers

**Do not** use `OverflowTooltip` inside AG Grid cells. Use the grid-native tooltip API via **`useAgGridTruncationTooltip`** from `impact-nova/ag-grid-react`:

```tsx
import { useAgGridTruncationTooltip } from 'impact-nova/ag-grid-react';

const MyCellRenderer = (params: ICellRendererParams) => {
  const textRef = useRef<HTMLSpanElement>(null);
  const displayValue = String(params.value ?? '');

  useAgGridTruncationTooltip(
    params.setTooltip,
    displayValue,
    textRef,
    'ellipsis',
    'cell',
  );

  return (
    <span ref={textRef} className="truncate">
      {displayValue}
    </span>
  );
};
```

- Pass AG Grid's `setTooltip` callback (from `ICellRendererParams` / header APIs).
- Apply `truncate` (or `line-clamp`) on the measured element.
- `location` is `'cell' | 'leaf' | 'group'` — affects tooltip show mode wiring.
- For general (non-grid) truncated labels, use `OverflowTooltip` from `impact-nova/tooltip` instead.

---

## 7. AG Grid v36 version pin (mandatory)

Impact Nova **2.2.0+** targets **AG Grid v36**. Consumer apps must install all three packages at the **exact same version**:

```bash
npm install ag-grid-community@36.0.1 ag-grid-react@36.0.1 ag-grid-enterprise@36.0.1
```

**Rules:**

- Pin `36.0.1` in `package.json` — do not float `^36` across community/react/enterprise.
- Vite/webpack: `resolve.dedupe: ['ag-grid-community', 'ag-grid-enterprise', 'ag-grid-react']` (see `create-impact-nova` template).
- **Do not** import legacy AG Grid CSS theme files (`ag-grid.css`, `ag-theme-*`). Impact Nova `AgGridWrapper` applies the v36 Quartz theme (`themeQuartz.withPart(iconSetMaterial)`) via the `theme` prop.
- Column settings (`DataTableColumnList`) sync pin/sort/filter from the live `GridApi` — do not mirror column state in app-level React state.

**Column settings sheet pattern (scroll-safe — use compound layout components):**

```tsx
<DataTableSheet open={sheetOpen} onOpenChange={setSheetOpen}>
  <DataTableSheetContent>
    <DataTableSheetHeader title="Table Settings" />
    <DataTableSheetBody>
      <DataTableSheetSection>{savedViews}</DataTableSheetSection>
      <DataTableSheetTabs value={tab} onValueChange={setTab} variant="line" className="px-4">
        <DataTableSheetTabsList>
          <TabsTrigger value="columns">Columns</TabsTrigger>
          <TabsTrigger value="format">Format</TabsTrigger>
        </DataTableSheetTabsList>
        <DataTableSheetTabPanel layout="list" value="columns" className="p-0 pt-2">
          <DataTableColumnList />
        </DataTableSheetTabPanel>
        <DataTableSheetTabPanel layout="scroll" value="format" className="p-4 pt-1">
          <DataTableFormatOptions />
        </DataTableSheetTabPanel>
      </DataTableSheetTabs>
    </DataTableSheetBody>
  </DataTableSheetContent>
</DataTableSheet>
```

Do **not** use raw `Tabs` + manual `overflow-y-auto` in sheet bodies — see `impact-nova://data-table-sheet-layout`.

Storybook reference: `Data Display/DataTable/Sheet layout` (`CanonicalComposition`) and `Column settings scenarios` (`PinSortSearchAndReset`).

---
