# Impact Nova — Real-World Patterns (from production apps)

Patterns observed in apps that use Impact Nova extensively (e.g. filter UIs, data tables, sheets, empty states).

---

## 1. Subpath imports (tree-shaking)

**Default for production apps:** use subpath imports. Production apps (e.g. mtp-mfe-itemsmart-v3) migrated to 100% subpath imports — zero barrel `from 'impact-nova'` in feature code. Barrel is OK for i18n bootstrapping only.

| Use case | Import |
|----------|--------|
| Layout (app shell) | `import { Layout } from 'impact-nova/layout';` |
| Sidebar | `import { Sidebar, SidebarProvider, SidebarContent } from 'impact-nova/sidebar';` |
| Header | `import { Header, HeaderLeft, HeaderRight, HeaderTitle } from 'impact-nova/header';` |
| Breadcrumb | `import { Breadcrumb, BreadcrumbList, BreadcrumbHeader } from 'impact-nova/breadcrumb';` |
| Button only | `import { Button } from 'impact-nova/button';` |
| Combobox | `import { Combobox } from 'impact-nova/combobox';` |
| Sheet (side panel) | `import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetBody, SheetFooter } from 'impact-nova/sheet';` |
| Filter panel | `import { FilterPanel, FilterPanelSidebar } from 'impact-nova/filter-panel';` and `FilterPanelBody`, `FilterPanelFooter` |
| Filter strip | `import { FilterStrip } from 'impact-nova/filter-strip';` |
| Empty state | `import { EmptyContainer, EmptyContainerImage, EmptyContainerTitle, EmptyContainerDescription, EmptyContainerAction } from 'impact-nova/empty-container';` |
| Accordion | `import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from 'impact-nova/accordion';` |
| Date pickers | `import { DateRangePicker } from 'impact-nova/date-picker';` or `WeekRangePicker`, `MonthRangePicker`. Week range selection language (Wk gutter join, rounded-lg start/end caps) is built into Calendar — do not restyle the grid. |
| Select | `import { Select } from 'impact-nova/select';` |
| Data table | `import { DataTable, DataTableContent, DataTableToolbar, useDataTable } from 'impact-nova/data-table';` |
| Progress bar | `import { ProgressBar } from 'impact-nova/progress-bar';` |
| Virtualized list hooks | `import { useVirtualizedList, useDebouncedValue } from 'impact-nova/virtualized';` |
| Expandable list row | `import { ExpandableListItem, type ExpandableListItemSection } from 'impact-nova/expandable-list-item';` |
| Row selection type | `import type { RowSelectionState } from 'impact-nova/virtualized';` |
| Alert | `import { Alert, AlertTitle, AlertDescription, AlertIcon } from 'impact-nova/alert';` |
| Tooltip | `import { Tooltip, TooltipTrigger, TooltipContent } from 'impact-nova/tooltip';` |
| Icons | `import { Pin, Unpin, Chart } from 'impact-nova-icons';` + `createIconResolver` for dynamic lookup |
| AG Grid cell renderers | `import { AG_GRID_CELL_COMPONENTS, LinkCellRenderer } from 'impact-nova/ag-grid-react/cell-renderers';` |
| Prompt | `import { Prompt } from 'impact-nova/prompt';` |
| Radio group | `import { RadioGroup, RadioGroupItem } from 'impact-nova/radio-group';` |
| Button group | `import { ButtonGroup } from 'impact-nova/button-group';` |
| Smart input | `import { SmartInput as InputWrapper } from 'impact-nova/smart-input';` |
| Chips | `import { Chip } from 'impact-nova/chips';` |
| Loader | `import { Loader } from 'impact-nova/loader';` |

---

## 2. Type-only imports

Use type-only imports for types to avoid pulling runtime code:

```ts
import type { Option, MultiValue, ActionMeta } from 'impact-nova/select';
import type { FilterSidebarItem } from 'impact-nova/filter-panel';
import type { FilterItem, SavedFilterItem } from 'impact-nova/filter-strip';
import type { NestedListItem } from 'impact-nova/nested-list';
import type { FileUploadProps } from 'impact-nova/file-upload';
import type { BackendColDef } from 'impact-nova';
```

---

## 3. Compound patterns

### Filter UI (panel + strip)

- **FilterPanel** + **FilterPanelSidebar** for the sliding panel; **FilterPanelBody** and **FilterPanelFooter** for content and Apply/Cancel.
- **FilterStrip** to show applied filters (chips/tags). Types: `FilterItem`, `SavedFilterItem` from `impact-nova/filter-strip`.
- Config drives **FilterSidebarItem** (from `impact-nova/filter-panel`); form state often with react-hook-form.

### Data table + AG Grid

**Mandatory (no deviation):** For DataTable or any data table using AG Grid, refer **only** to AG Grid's official documentation, follow **only** AG Grid recommended patterns, and use **only** the AG Grid API for grid operations. See resource `impact-nova://ag-grid-rules` for full rules. If the user has **ag-mcp** installed, use it for AG Grid API/docs and collaborate with this MCP for Impact Nova integration.

- **DataTable**, **DataTableContent**, **DataTableSheet**, **DataTableSheetContent**, **DataTableSheetHeader**, **DataTableSheetBody**, **DataTableSheetSection**, **DataTableSheetTabs**, **DataTableSheetTabsList**, **DataTableSheetTabPanel**, **DataTableColumnList**, **DataTableFormatOptions** from `impact-nova/data-table`.
- **Settings sheet layout:** Use compound sheet layout components — not raw `Tabs` + manual `overflow-y-auto`. `DataTableSheetTabPanel layout="list"` for column/KPI lists; `layout="scroll"` for format/filter forms. Resource: `impact-nova://data-table-sheet-layout`.
- **AG Grid v36 peers:** `ag-grid-community@36.0.1`, `ag-grid-react@36.0.1`, `ag-grid-enterprise@36.0.1` — exact pin, dedupe in Vite. No legacy `ag-theme-*` CSS.
- **Column settings:** `DataTableColumnList` reads pin/sort/filter from the live `GridApi` (frozen vs scrollable sections). Storybook: `Data/DataTable/Column settings scenarios`.
- Use **processBackendColumnDefs** from `impact-nova` when column defs come from the backend; type **BackendColDef**.
- Cell renderers: **AG_GRID_CELL_COMPONENTS** or **LinkCellRenderer** from `impact-nova/ag-grid-react/cell-renderers`; extend/wrap as needed (e.g. PlanningLinkCellRenderer).

### Sheet (side panel with form)

- **Sheet**, **SheetContent**, **SheetHeader**, **SheetTitle**, **SheetBody**, **SheetFooter** from `impact-nova/sheet`.
- Use for “Match With”, “Bulk Edit”, “View Management” panels. Pair with react-hook-form for form content.

### Empty state

- **EmptyContainer** wrapping **EmptyContainerImage**, **EmptyContainerTitle**, **EmptyContainerDescription**, **EmptyContainerAction** (e.g. a Button to “Select filter” or “Add item”).
- **Lazy illustrations:** When no custom `children` are passed to `EmptyContainerImage`, one random illustration loads via dynamic `import()` — only ~1 webp chunk per empty state, not all 6 upfront. Pass custom children to skip illustration loading entirely.

### Tabs + NestedList + Accordion (view management)

- **Tabs**, **TabsList**, **TabsTrigger**, **TabsContent** for switching sections.
- **NestedList** with **SelectionMode**; data shape **NestedListItem** from `impact-nova/nested-list`.
- **Accordion**, **AccordionItem**, **AccordionTrigger**, **AccordionContent** for expandable blocks (e.g. Versions & Variance).

### Chart (Highcharts)

- Use **Chart** from `impact-nova/chart` as the wrapper.
- For **series, options, and configuration** refer to **Highcharts documentation** ([highcharts.com/docs](https://www.highcharts.com/docs/)); use the Highcharts API for chart behavior. Impact Nova provides the React integration and styling; options/series follow Highcharts.
- Install: `highcharts@^12`, `highcharts-react-official@^3`, optional `highcharts-border-radius@^0.0.4`.

### Toast + Alert

- **toast**, **Toaster**, **useToast** from `impact-nova`; wrap app with **Toaster**.
- **Alert**, **AlertTitle**, **AlertDescription**, **AlertIcon** from `impact-nova/alert` for inline or toast-style messages.

### Expandable list row vs virtualized list (independent, composable)

Both are **decoupled** subpaths. Pick the integration style that fits — preset, compound, or headless hooks.

#### ExpandableListItem (`impact-nova/expandable-list-item`)

| Style | When | Example |
|-------|------|---------|
| **Preset** | Data-driven sections from API/config | `<ExpandableListItem title="…" description="…" sections={sections} />` |
| **Compound** | Custom row layout / slots | `ExpandableListItemRoot` → `Row` → `Checkbox` + `Trigger` → `Content` |
| **Inside virtualized** | Large lists (optional) | Render preset or compound inside `VirtualizedListItem` |

- Props: **`title`**, **`description`** (not legacy `sku` / `subtitle`)
- Sections: `ExpandableListItemSection[]` — `metrics` | `attributes` | `custom`
- Storybook: **Expandable List Item → Composition**

#### Virtualized (`impact-nova/virtualized`)

| Style | When | Example |
|-------|------|---------|
| **Compound UI** | New list chrome (search, select-all, viewport) | `VirtualizedList` → `Viewport` → `Content` → `Item` |
| **Headless hooks** | Full DOM control / existing layout | `useVirtualizedList` + `getItemProps` / `resetRowSize` |
| **Custom rows** | Any row JSX | ExpandableListItem optional — not required |

- Expandable rows: `getItemProps(index, { measure: isExpanded })`; call `resetRowSize(index)` on collapse
- Types: `RowSelectionState`, `VirtualRowModel`, `UseVirtualizedListResult`
- Storybook: **Virtualized List → Composition** and **Custom row UI**
- Patterns: `query_patterns` → `virtualized-selectable-list`, `expandable-list-item-preset`, `expandable-list-item-compound`
- **Renamed:** `impact-nova/expandable-sku` → `impact-nova/expandable-list-item`. See `impact-nova://removed-imports`.

### Command Palette & keyboard shortcuts

Implementation is **more involved** than other components. Use the dedicated guide: resource **`impact-nova://command-palette`**.

- **Import:** `import { CommandPaletteProvider, CommandPalette, useShortcut, useGlobalShortcut, ShortcutSettings, ShortcutScopeProvider, Kbd, useCommandPalette, useBrowserShortcuts } from 'impact-nova/command-palette';`
- **Minimal setup:** Wrap app with `CommandPaletteProvider`; render `<CommandPalette />` once inside it (⌘K overlay). Register commands with `useShortcut` (scoped: page/module/modal) or `useGlobalShortcut` (app-wide). Use correct `scope` for priority (modal > page > module > global).
- **Multi-table:** When multiple DataTables share the same shortcut, wrap each in `ShortcutScopeProvider` with a unique `instance` so only the focused one responds.
- **ShortcutSettings:** AG Grid–powered panel for user customisation; put inside a Sheet (e.g. Keyboard Shortcuts settings). For AG Grid behaviour follow `impact-nova://ag-grid-rules`.
- **Kbd:** Use `<Kbd keybinding={{ key: 'k', meta: true }} />` to show shortcut in UI. **Button trigger:** `useCommandPalette().setOpen(true)`.

---

## 4. App setup

- **CSS once at root**: `import 'impact-nova/dist/impact-nova.css';` (e.g. in App.tsx or main.tsx).
- **AG Grid**: Set `LicenseManager.setLicenseKey(...)` if using enterprise. Install `ag-grid-community@36.0.1 ag-grid-react@36.0.1 ag-grid-enterprise@36.0.1`.
- **Vite**: Dedupe react/ag-grid when using impact-nova to avoid dual instances. Exclude `impact-nova-icons` from `optimizeDeps` to prevent SVG asset warnings in dev:

```js
optimizeDeps: {
  exclude: ['impact-nova-icons'],
},
resolve: {
  dedupe: ['react', 'react-dom', 'ag-grid-community', 'ag-grid-enterprise', 'ag-grid-react'],
},
```

---

## 5. Known workarounds

- **HorizontalScroller**: If the package entry is empty in your build, add a Vite alias to `.../horizontal-scroller/horizontal-scroller.js` and a small `declare module 'impact-nova/horizontal-scroller'` for TypeScript.
- **Chart**: Prefer `import { Chart } from 'impact-nova/chart'`. Pre-bundle Highcharts UMD modules in Vite `optimizeDeps.include` if you see interop errors in dev.

---

## 6. Forms and options

- **Select options**: Use type **Option** (or **SelectOption**) from `impact-nova/select` for `{ value, label }` (and optional fields). **MultiValue** for multi-select.
- **react-hook-form**: Use Impact Nova form controls (SmartInput, Select, DateRangePicker, RadioGroup, ButtonGroup, Chips, etc.) as controlled components; pass `value`/`onChange` from the form library.
- **Validation and errors**: Show validation errors with **Alert** or field-level messages; use the form library’s error state (e.g. `formState.errors`) and optional schema validation (e.g. Zod) with react-hook-form.

### ReactHooksForm (config-driven dynamic forms)

Use when the form layout and rules come from **JSON** (`IFormConfig`) — bulk edit sheets, settings, wizards, filters.

```tsx
import { ReactHooksForm, type IFormConfig, type FormContext } from 'impact-nova/form-react';

const formContext: FormContext = {
  transport: {
    fetchSelectOptions: async ({ fieldId, apiConfig }) => { /* your fetch */ },
    showFormErrorToast: ({ title, description }) => { /* your toast */ },
  },
  validateForm: ({ formValues }) => ({ isValid: true }), // optional cross-field rules
};

<ReactHooksForm formConfig={formConfig} formContext={formContext} onSubmit={handleSubmit} />
```

| Concept | What it does |
|---------|----------------|
| `isRequired` | Mandatory field — blocks submit when visible and empty |
| `disableDependency` + `__empty__` | Disable child until parent has value; **auto-clear** when parent empties |
| `disableDependency` + `null` | Disable when other field has any value (mutually exclusive) |
| `disableDependency` + `"true"` | Disable when switch/checkbox is on (grand total, lock) |
| `visibilityDependency` | Show/hide fields (month range vs multi-month; filter hierarchy) |
| `formContext.transport` | **Required** for API selects — Nova never calls HTTP |
| `formContext.validateForm` | Cross-field submit validation (e.g. “at least one edit”) |
| `doNotIncludeInSubmit` | UI-only toggles excluded from payload |
| `includeOnlyVisibleFieldsInSubmit` | Only active picker variant submitted |

**MCP resource (full catalog):** `impact-nova://dynamic-form-framework`

**Storybook (12 ItemSmart patterns):** `Dynamic Form / ReactHooksForm` + `Dynamic Form / Guide`

| Story | URL slug |
|-------|----------|
| Required fields | `dynamic-form-reacthooksform--required-fields` |
| Disable dependency | `dynamic-form-reacthooksform--disable-dependency-period-gates-value` |
| validateForm gate | `dynamic-form-reacthooksform--item-details-bulk-edit-validate-form` |
| Scenario simulate | `dynamic-form-reacthooksform--scenario-simulate` |
| Filter disable chain | `dynamic-form-reacthooksform--filter-disable-chain` |
| Filter visibility | `dynamic-form-reacthooksform--filter-visibility-hierarchy` |

**`query_patterns` ids:** `config-driven-dynamic-form`, `form-required-fields`, `form-visibility-dependency`, `time-phased-metric-bulk-edit`, `form-validate-form-submit-gate`, `form-mutually-exclusive-fields`, `form-switch-disable-bypass`, `form-approval-hub-lock`, `filter-form-dependencies`, `form-filter-visibility-hierarchy`

**Fixtures:** `impact-nova/src/form-react/stories/fixtures/itemSmartProductionFormConfigs.ts`

**ItemSmart reference:** `mtp-mfe-itemsmart-v3/docs/react-hooks-form-production-patterns.md`

**Packages:** `impact-nova/form-engine` · `impact-nova/form-react` · `impact-nova/filter-shell`

---

## 7. Breadcrumb and toolbar

- **Breadcrumb** with **BreadcrumbList**, **BreadcrumbItem**, **BreadcrumbLink**, **BreadcrumbSeparator**, **BreadcrumbPage** for table/section context.
- **DataTableToolbar** for table toolbars (view mode, view management, filters, Update/Cancel when editing).

Use these patterns so the MCP and generated code stay close to how Impact Nova is used in real apps.

---

## 8. Wizard (multi-step flow)

Use **Wizard** for any multi-step creation or onboarding flow. Radix-style controlled state (`value` / `onValueChange`), optional split-panel layout, and `useWizard()` for flexible footers.

### Import

```ts
import {
  WizardRoot,
  WizardLayout,
  WizardSidebar,
  WizardTitle,
  WizardDescription,
  WizardMedia,
  WizardBody,
  WizardProgress,
  WizardHeader,
  WizardContent,
  WizardStep,
  WizardFooter,
  useWizard,
} from 'impact-nova/wizard';
```

### Minimal multi-step example

```tsx
import { useState } from 'react';
import {
  WizardRoot, WizardLayout,
  WizardSidebar, WizardTitle, WizardDescription, WizardMedia,
  WizardBody, WizardProgress,
  WizardContent, WizardStep, WizardHeader,
  WizardFooter,
} from 'impact-nova/wizard';
import { Button } from 'impact-nova/button';

export function CreateAlertFlow() {
  const [step, setStep] = useState(1);

  return (
    <WizardRoot value={step} totalSteps={3} onValueChange={setStep}>
      <WizardLayout>
        <WizardSidebar>
          <WizardTitle>Create Alert</WizardTitle>
          <WizardDescription>Configure your alert step by step.</WizardDescription>
          <WizardMedia />
        </WizardSidebar>

        <WizardBody>
          <WizardProgress />
          <WizardContent>
            <WizardStep value={1}>
              <WizardHeader>Alert Details</WizardHeader>
            </WizardStep>
            <WizardStep value={2}>
              <WizardHeader>Configuration</WizardHeader>
            </WizardStep>
            <WizardStep value={3}>
              <WizardHeader>Review</WizardHeader>
            </WizardStep>
          </WizardContent>
          <WizardFooter>
            <Button variant="secondary">Cancel</Button>
            <Button variant="default">Submit</Button>
          </WizardFooter>
        </WizardBody>
      </WizardLayout>
    </WizardRoot>
  );
}
```

### Key rules

- **WizardRoot** is the context provider — every other sub-component must be a descendant.
- Use **useWizard()** for `goNext`, `goBack`, `isFirstStep`, `isLastStep` without prop drilling.
- **WizardStep** renders nothing when `value` doesn't match the current step — use one per step.
- **WizardMedia** renders a default SVG when neither `src` nor `children` are provided.
- The sidebar is hidden below the `lg` breakpoint.
- All sub-components except **WizardRoot** are optional — compose only what you need.
---

## 9. App layout (app shell)

**Use `Layout`** for the app chrome — not `DynamicLayout` (that is a grid/flex/stack utility only).

Full beginner scaffold: resource **`impact-nova://layout`**.

### Structure

```tsx
import { Layout } from 'impact-nova/layout';
import { Sidebar, SidebarProvider } from 'impact-nova/sidebar';
import { Header, HeaderLeft, HeaderTitle } from 'impact-nova/header';
import { BreadcrumbHeader, Breadcrumb, BreadcrumbList } from 'impact-nova/breadcrumb';
import { FilterStrip } from 'impact-nova/filter-strip';

<SidebarProvider defaultOpen={false} id="app-sidebar" className="flex h-svh min-h-0 w-full min-w-0 overflow-hidden">
  <Layout
    sidebar={<Sidebar collapsible="offcanvas">...</Sidebar>}
    header={<Header>...</Header>}
  >
    {/* Page content — Item Smart parity */}
    <PageShell>
      <PageStickyHeader pageTitle="..." filterStrip={filtersApplied ? <FilterStrip ... /> : undefined} />
      {filtersApplied ? <WorkspaceBody /> : <EmptyStateView title="..." primaryActionLabel="Select filter" />}
    </PageShell>
  </Layout>
</SidebarProvider>
```

### Key rules

- **`SidebarProvider` at app root** — wraps `Layout`, not inside the sidebar slot.
- **Page background** — `PageShell` → `bg-brand-subtle` (not `bg-canvas-wash` on module pages)
- **Filter strip** — white sticky bar via `PageStickyHeader` `filterStrip` slot (`page-filter-strip` class)
- **Tables** — `module-table-container mx-6 flex min-h-[480px] flex-1` → white `DataTable` card with shadow
- **Subpath imports** — `impact-nova/layout`, `impact-nova/sidebar`, `impact-nova/header`, `impact-nova/breadcrumb`, `impact-nova/filter-strip`.
- **With React Router** — put `<Outlet />` inside `Layout` children; route pages render breadcrumb + body.
- **With Command Palette** — `CommandPaletteProvider` at root; `<CommandPalette />` as sibling inside `SidebarProvider`. See `impact-nova://command-palette`.

### Premium primitives (shipped in create-impact-nova template)

| Component | Role |
|-----------|------|
| `PageShell` | `bg-brand-subtle` + flex fill chain |
| `PageStickyHeader` | Breadcrumb `z-30` + white filter strip slot |
| `EmptyStateView` | Illustration + primary/secondary CTAs |
| `SummaryMetricCard` | Dashboard KPI tiles (`border-[#C3C8D4]`, hover shadow) |
| `module-table-container` | Table card shadow + `min-h-[480px] flex-1` fill |
