Add a Feature Across Framework Adapters
When adding a new grid feature that needs framework adapter support (Angular, React, Vue), follow this guide to ensure consistency and feature parity.
Architecture Overview
| Library | Path | Pattern |
|---|---|---|
@toolbox-web/grid-angular | libs/grid-angular/ | Angular directives + AngularGridAdapter |
@toolbox-web/grid-react | libs/grid-react/ | React components + ReactGridAdapter |
@toolbox-web/grid-vue | libs/grid-vue/ | Vue composables + VueGridAdapter |
All adapters register a framework-specific GridAdapter that handles rendering cells/editors/panels using the framework's template system.
Step 1: Understand the Core Feature
Before touching adapters, understand how the feature works in the vanilla grid:
- •Read the relevant core code in
libs/grid/src/lib/ - •Check if the feature uses:
- •Custom renderers (cell, header, filter) → Adapters need to wrap framework components
- •Events → Adapters need to expose framework-idiomatic event binding
- •Configuration → Adapters may extend config types
- •DOM manipulation → Adapters might need lifecycle management
Step 2: Angular Adapter (libs/grid-angular/)
Exported API
- •
Griddirective — Auto-registersAngularGridAdapteron<tbw-grid>elements - •
TbwRenderer— Structural directive (*tbwRenderer) for cell renderer templates - •
TbwEditor— Structural directive (*tbwEditor) for cell editor templates with auto-wired commit/cancel - •
GridColumnView/GridColumnEditor— Alternative nested element syntax with<ng-template>
Usage Example
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { Grid, TbwRenderer, TbwEditor } from '@toolbox-web/grid-angular';
@Component({
imports: [Grid, TbwRenderer, TbwEditor],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `
<tbw-grid [rows]="data" [gridConfig]="config">
<tbw-grid-column field="status">
<app-status-badge *tbwRenderer="let value; row as row" [value]="value" [row]="row" />
<app-status-select *tbwEditor="let value" [value]="value" />
</tbw-grid-column>
</tbw-grid>
`
})
export class GridComponent { ... }
Key Files
- •
src/lib/grid.directive.ts— MainGriddirective (auto-registers adapter) - •
src/lib/angular-grid-adapter.ts— Adapter that renders Angular templates - •
src/lib/directives/— Structural directives (TbwRenderer,TbwEditor, etc.) - •
src/index.ts— Public exports
Pattern
- •Use structural directives (
*tbwXxx) for template-driven features - •Use
@Input()/@Output()for configuration - •Register new template types in the adapter's
renderCell/renderEditormethods - •Export from
src/index.ts
Testing
- •Co-located
*.spec.tsfiles - •Use
Object.create(Class.prototype)for testing class methods without DI - •Run:
bun nx test grid-angular
Step 3: React Adapter (libs/grid-react/)
Exported API
Components: DataGrid, GridColumn, GridDetailPanel, GridToolPanel, GridToolButtons
Hooks: useGrid (programmatic access), useGridEvent (type-safe event subscription)
Types: ReactGridConfig (extends GridConfig with React renderers), ReactColumnConfig
Usage Example
import { DataGrid, type ReactGridConfig } from '@toolbox-web/grid-react';
import { SelectionPlugin } from '@toolbox-web/grid/all';
const config: ReactGridConfig<Employee> = {
columns: [
{ field: 'name', header: 'Name' },
{
field: 'status',
renderer: (ctx) => <StatusBadge value={ctx.value} />,
editor: (ctx) => <StatusSelect value={ctx.value} onCommit={ctx.commit} />,
},
],
plugins: [new SelectionPlugin({ mode: 'row' })],
};
function App() {
return <DataGrid rows={employees} gridConfig={config} />;
}
Key Files
- •
src/lib/DataGrid.tsx— MainDataGridcomponent - •
src/lib/react-grid-adapter.ts— Adapter that renders React elements - •
src/lib/GridColumn.tsx— Declarative column with render props - •
src/lib/hooks/—useGrid,useGridEvent - •
src/index.ts— Public exports
Pattern
- •Use render props or React component types in config
- •Extend
ReactGridConfig/ReactColumnConfigfor new renderable slots - •Add hooks for new event subscriptions if needed
- •Export from
src/index.ts
Testing
- •Use
@testing-library/reactwithrender() - •Mock grid element for hook tests
- •Run:
bun nx test grid-react
Step 4: Vue Adapter (libs/grid-vue/)
Exported API
Components: DataGrid, GridColumn, GridDetailPanel, GridToolPanel, GridToolButtons
Composables: useGrid (programmatic access), useGridEvent (event subscription)
Types: VueGridConfig (extends GridConfig with Vue slot renderers), VueColumnConfig
Usage Example
<script setup lang="ts">
import { DataGrid, GridColumn } from '@toolbox-web/grid-vue';
import { SelectionPlugin } from '@toolbox-web/grid/all';
import StatusBadge from './StatusBadge.vue';
const config = {
plugins: [new SelectionPlugin({ mode: 'row' })],
};
</script>
<template>
<DataGrid :rows="employees" :gridConfig="config">
<GridColumn field="status" header="Status">
<template #renderer="{ value, row }">
<StatusBadge :value="value" :row="row" />
</template>
</GridColumn>
</DataGrid>
</template>
Key Files
- •
src/lib/DataGrid.vue— MainDataGridcomponent - •
src/lib/vue-grid-adapter.ts— Adapter that renders Vue slots - •
src/lib/composables.ts—useGrid,useGridEvent - •
src/lib/registry files— WeakMap-based registries for panels, cards, etc. - •
src/index.ts— Public exports
Pattern
- •Use slots and registry pattern (WeakMap keyed on grid element) for renderables
- •Add composables for new grid interactions
- •Extend
VueGridConfig/VueColumnConfigfor new features - •Export from
src/index.ts
Testing
- •Use
@vue/test-utilswithmount() - •Test composables via
defineComponentwrappers - •Run:
bun nx test grid-vue
Step 5: Verify Feature Parity
After implementing across all adapters:
- •Types align: Config extensions should have equivalent properties
- •Behavior matches: Same user interactions produce same results
- •Tests cover: Each adapter has tests for the new feature
- •Docs updated: README files and any MDX docs reflect new feature
- •Exports added: New public types/components exported from barrel files
Step 6: Run All Tests
bun run test
Ensure all 4 projects pass (grid, grid-angular, grid-react, grid-vue).
Checklist
- • Core grid feature working in vanilla mode
- • Angular adapter updated with directive/template support
- • React adapter updated with component/hook support
- • Vue adapter updated with composable/slot support
- • Tests added for each adapter
- • Public exports updated in each
index.ts - • TypeScript types consistent across adapters
- • All tests passing