Skip to content

Commit b5caa34

Browse files
author
Asif Ansari
committed
demo: add locale-text, cell-error-boundary demos; update tree-data to use params.rowMeta
- LocaleTextDemo (/locale-text, Customization): locale switcher (English / French / Custom) with pageSize=3 so pagination labels are always visible; Clear rows button shows noRowsLabel - CellErrorBoundaryDemo (/cell-error-boundary, Main features): rows 3 and 6 throw in renderCell; "Make all rows throw / Restore" toggle demonstrates per-cell boundary + resetKey recovery - TreeData: custom renderCell on Name column reads params.rowMeta.treeDepth / hasChildren / isExpanded / descendantCount without touching row underscore fields (v1.1.0 API)
1 parent aa09f7b commit b5caa34

6 files changed

Lines changed: 271 additions & 2 deletions

File tree

‎demo/App.tsx‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ const FilterPanelDemo = lazy(() => import('./examples/FilterPanelDemo/FilterPane
3939
const SlotsDemo = lazy(() => import('./examples/SlotsDemo/SlotsDemo'));
4040
const CRUDTutorial = lazy(() => import('./examples/CRUDTutorial/CRUDTutorial'));
4141
const ScrollToIndexesDemo = lazy(() => import('./examples/ScrollToIndexesDemo/ScrollToIndexesDemo'));
42+
const LocaleTextDemo = lazy(() => import('./examples/LocaleTextDemo/LocaleTextDemo'));
43+
const CellErrorBoundaryDemo = lazy(() => import('./examples/CellErrorBoundaryDemo/CellErrorBoundaryDemo'));
4244

4345
const examplesConfig = [
4446
// Resources
@@ -52,6 +54,7 @@ const examplesConfig = [
5254
// Main Features
5355
{ path: '/basic', name: 'Basic Usage', component: BasicExample, category: 'Main features' },
5456
{ path: '/loading', name: 'Loading States', component: LoadingStatesDemo, category: 'Main features' },
57+
{ path: '/cell-error-boundary', name: 'Cell Error Boundary', component: CellErrorBoundaryDemo, category: 'Main features' },
5558
{ path: '/export', name: 'Export Data', component: ExportDemo, category: 'Main features' },
5659
{ path: '/clipboard', name: 'Clipboard Copy', component: ClipboardDemo, category: 'Main features' },
5760
{ path: '/filtering', name: 'Advanced Filtering', component: AdvancedFilteringDemo, category: 'Main features' },
@@ -83,6 +86,7 @@ const examplesConfig = [
8386
{ path: '/filter-panel', name: 'Filter Panel', component: FilterPanelDemo, category: 'Components' },
8487

8588
// Customization
89+
{ path: '/locale-text', name: 'Locale Text (i18n)', component: LocaleTextDemo, category: 'Customization' },
8690
{ path: '/theming', name: 'Theming', component: ThemingDemo, category: 'Customization' },
8791
{ path: '/list-view', name: 'List View', component: ListViewDemo, category: 'Customization' },
8892
{ path: '/slots', name: 'Slots & Renderers', component: SlotsDemo, category: 'Customization' },
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/* CellErrorBoundaryDemo styles */
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
2+
import { useState, useMemo } from 'react';
3+
import { DataGrid, GridColDef } from '@opencorestack/opengridx';
4+
import { DocsLayout } from '../../components/DocsLayout';
5+
import sourceCode from './CellErrorBoundaryDemo.tsx?raw';
6+
import './CellErrorBoundaryDemo.css';
7+
8+
interface Product {
9+
id: number;
10+
name: string;
11+
price: number;
12+
rating: number;
13+
}
14+
15+
const baseRows: Product[] = [
16+
{ id: 1, name: 'Widget Alpha', price: 29.99, rating: 4 },
17+
{ id: 2, name: 'Gadget Beta', price: 49.99, rating: 3 },
18+
{ id: 3, name: 'Device Gamma', price: 99.99, rating: 5 },
19+
{ id: 4, name: 'Tool Delta', price: 19.99, rating: 2 },
20+
{ id: 5, name: 'Unit Epsilon', price: 74.99, rating: 4 },
21+
{ id: 6, name: 'Module Zeta', price: 39.99, rating: 5 },
22+
{ id: 7, name: 'Part Eta', price: 14.99, rating: 3 },
23+
{ id: 8, name: 'Block Theta', price: 59.99, rating: 1 },
24+
];
25+
26+
const staticColumns: GridColDef<Product>[] = [
27+
{ field: 'id', headerName: 'ID', width: 70, type: 'number' },
28+
{ field: 'name', headerName: 'Name', width: 200 },
29+
{
30+
field: 'price',
31+
headerName: 'Price',
32+
width: 110,
33+
type: 'number',
34+
align: 'right',
35+
headerAlign: 'right',
36+
valueFormatter: (params) => `$${(params.value as number).toFixed(2)}`,
37+
},
38+
];
39+
40+
export default function CellErrorBoundaryDemo() {
41+
const [forceThrow, setForceThrow] = useState(false);
42+
// Changing rows reference resets the per-cell error boundaries (resetKey).
43+
const [rows, setRows] = useState<Product[]>(baseRows);
44+
45+
const columns = useMemo<GridColDef<Product>[]>(() => [
46+
...staticColumns,
47+
{
48+
field: 'rating',
49+
headerName: 'Rating',
50+
width: 160,
51+
renderCell: (params) => {
52+
if (forceThrow || params.row.id % 3 === 0) {
53+
throw new Error(`Cannot render rating for row ${params.row.id}`);
54+
}
55+
const rating = params.value as number;
56+
return (
57+
<span>
58+
{'★'.repeat(rating)}{'☆'.repeat(5 - rating)}
59+
</span>
60+
);
61+
},
62+
},
63+
], [forceThrow]);
64+
65+
const handleToggle = () => {
66+
const next = !forceThrow;
67+
setForceThrow(next);
68+
// Produce a new rows array so the grid sees fresh data and resets boundaries.
69+
setRows(baseRows.map((r) => ({ ...r })));
70+
};
71+
72+
return (
73+
<DocsLayout
74+
title="Cell Error Boundary"
75+
description="A renderCell that throws is caught per-cell by CellErrorBoundary (v1.1.0) — the rest of the grid continues rendering normally. When data changes the boundary auto-recovers."
76+
sourceCode={sourceCode}
77+
>
78+
<p style={{ marginBottom: 12, color: '#475569', fontSize: '0.875rem' }}>
79+
Rows 3 and 6 have a <code>renderCell</code> that throws. The{' '}
80+
<strong>CellErrorBoundary</strong> (v1.1.0) catches the error and shows ⚠ in that cell
81+
only — the rest of the grid keeps rendering. Click &quot;Make all rows throw&quot; to see
82+
every rating cell fail, then &quot;Restore&quot; to watch the auto-recovery via the
83+
internal <code>resetKey</code>.
84+
</p>
85+
86+
<div style={{ marginBottom: 16 }}>
87+
<button
88+
onClick={handleToggle}
89+
style={{
90+
padding: '6px 16px',
91+
borderRadius: 6,
92+
border: '1px solid #e2e8f0',
93+
background: forceThrow ? '#ef4444' : '#fff',
94+
color: forceThrow ? '#fff' : '#374151',
95+
cursor: 'pointer',
96+
fontSize: '0.875rem',
97+
fontWeight: forceThrow ? 600 : 400,
98+
}}
99+
>
100+
{forceThrow ? 'Restore' : 'Make all rows throw'}
101+
</button>
102+
</div>
103+
104+
<DataGrid
105+
rows={rows}
106+
columns={columns}
107+
height={400}
108+
pagination
109+
pageSizeOptions={[8, 25]}
110+
initialState={{ pagination: { paginationModel: { pageSize: 8, page: 0 } } }}
111+
/>
112+
</DocsLayout>
113+
);
114+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/* LocaleTextDemo styles */
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
2+
import { useState } from 'react';
3+
import { DataGrid, GridColDef, GridLocaleText } from '@opencorestack/opengridx';
4+
import { DocsLayout } from '../../components/DocsLayout';
5+
import sourceCode from './LocaleTextDemo.tsx?raw';
6+
import './LocaleTextDemo.css';
7+
8+
interface Employee {
9+
id: number;
10+
name: string;
11+
department: string;
12+
salary: number;
13+
}
14+
15+
const allRows: Employee[] = [
16+
{ id: 1, name: 'Alice Johnson', department: 'Engineering', salary: 95000 },
17+
{ id: 2, name: 'Bob Martinez', department: 'Marketing', salary: 72000 },
18+
{ id: 3, name: 'Carol White', department: 'Engineering', salary: 110000 },
19+
{ id: 4, name: 'David Lee', department: 'HR', salary: 68000 },
20+
{ id: 5, name: 'Eva Chen', department: 'Engineering', salary: 125000 },
21+
{ id: 6, name: 'Frank Garcia', department: 'Marketing', salary: 79000 },
22+
{ id: 7, name: 'Grace Kim', department: 'Finance', salary: 88000 },
23+
{ id: 8, name: 'Henry Brown', department: 'Engineering', salary: 102000 },
24+
{ id: 9, name: 'Iris Wilson', department: 'Finance', salary: 91000 },
25+
{ id: 10, name: 'James Taylor', department: 'HR', salary: 65000 },
26+
];
27+
28+
const columns: GridColDef<Employee>[] = [
29+
{ field: 'id', headerName: 'ID', width: 70, type: 'number' },
30+
{ field: 'name', headerName: 'Name', width: 180 },
31+
{ field: 'department', headerName: 'Department', width: 150 },
32+
{
33+
field: 'salary',
34+
headerName: 'Salary',
35+
width: 130,
36+
type: 'number',
37+
align: 'right',
38+
headerAlign: 'right',
39+
valueFormatter: (params) => `$${(params.value as number).toLocaleString()}`,
40+
},
41+
];
42+
43+
type LocaleKey = 'English' | 'French' | 'Custom';
44+
45+
const localeMap: Record<LocaleKey, GridLocaleText | undefined> = {
46+
English: undefined,
47+
French: {
48+
paginationRowsPerPage: 'Lignes par page :',
49+
paginationOf: (f, t, c) => `${f}–${t} sur ${c}`,
50+
paginationPage: (p, pc) => `Page ${p} sur ${pc}`,
51+
noRowsLabel: 'Aucune donnée',
52+
},
53+
Custom: {
54+
paginationRowsPerPage: 'Show:',
55+
paginationOf: (f, t, c) => `Records ${f}–${t} of ${c}`,
56+
paginationPage: (p, pc) => `${p}/${pc}`,
57+
noRowsLabel: 'Nothing to display',
58+
},
59+
};
60+
61+
const BTN_BASE: React.CSSProperties = {
62+
padding: '6px 16px',
63+
borderRadius: 6,
64+
border: '1px solid #e2e8f0',
65+
cursor: 'pointer',
66+
fontSize: '0.875rem',
67+
};
68+
69+
export default function LocaleTextDemo() {
70+
const [locale, setLocale] = useState<LocaleKey>('English');
71+
const [rows, setRows] = useState<Employee[]>(allRows);
72+
const localeText = localeMap[locale];
73+
74+
return (
75+
<DocsLayout
76+
title="Locale Text (i18n)"
77+
description="The localeText prop overrides all user-visible pagination strings. Switch locale below — labels update instantly without remounting the grid. Use 'Clear rows' to see the noRowsLabel override."
78+
sourceCode={sourceCode}
79+
>
80+
<div style={{ display: 'flex', gap: 8, marginBottom: 16, flexWrap: 'wrap', alignItems: 'center' }}>
81+
{(['English', 'French', 'Custom'] as LocaleKey[]).map((key) => (
82+
<button
83+
key={key}
84+
onClick={() => setLocale(key)}
85+
style={{
86+
...BTN_BASE,
87+
background: locale === key ? '#6366f1' : '#fff',
88+
color: locale === key ? '#fff' : '#374151',
89+
fontWeight: locale === key ? 600 : 400,
90+
}}
91+
>
92+
{key}
93+
</button>
94+
))}
95+
<span style={{ marginLeft: 8, color: '#6b7280', fontSize: '0.875rem' }}>
96+
Active: <strong>{locale}</strong>
97+
</span>
98+
<button
99+
onClick={() => setRows(rows.length === 0 ? allRows : [])}
100+
style={{ ...BTN_BASE, marginLeft: 'auto', background: '#fff', color: '#374151' }}
101+
>
102+
{rows.length === 0 ? 'Restore rows' : 'Clear rows'}
103+
</button>
104+
</div>
105+
106+
<DataGrid
107+
rows={rows}
108+
columns={columns}
109+
pagination
110+
pageSizeOptions={[3, 5, 10]}
111+
initialState={{ pagination: { paginationModel: { pageSize: 3, page: 0 } } }}
112+
height={400}
113+
localeText={localeText}
114+
/>
115+
</DocsLayout>
116+
);
117+
}

‎demo/examples/TreeData/TreeData.tsx‎

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,39 @@
11

22
import { useState } from 'react';
3-
import { DataGrid, GridPaginationModel, GridRowId } from '@opencorestack/opengridx';
3+
import { DataGrid, GridPaginationModel, GridRowId, GridColDef, GridRenderCellParams } from '@opencorestack/opengridx';
44
import { generateEmployees, Employee } from '../../data/mockData';
55
import { allColumns } from '../../data/columns';
66
import './TreeData.css';
77
import { DocsLayout } from '../../components/DocsLayout';
88
import sourceCode from './TreeData.tsx?raw';
99

10+
const treeColumns: GridColDef<Employee>[] = allColumns.map((col) => {
11+
if (col.field !== 'name') return col;
12+
return {
13+
...col,
14+
renderCell: (params: GridRenderCellParams<Employee>) => {
15+
const depth = params.rowMeta?.treeDepth ?? 0;
16+
const hasChildren = params.rowMeta?.hasChildren ?? false;
17+
const isExpanded = params.rowMeta?.isExpanded ?? false;
18+
return (
19+
<div style={{ display: 'flex', alignItems: 'center', gap: 4, paddingLeft: depth * 16 }}>
20+
{hasChildren && (
21+
<span style={{ fontSize: '0.7rem', color: '#6366f1' }}>
22+
{isExpanded ? '▼' : '▶'}
23+
</span>
24+
)}
25+
<span>{params.value as string}</span>
26+
{params.rowMeta && hasChildren && (
27+
<span style={{ fontSize: '0.65rem', color: '#94a3b8', marginLeft: 4 }}>
28+
({params.rowMeta.descendantCount ?? 0} items)
29+
</span>
30+
)}
31+
</div>
32+
);
33+
},
34+
};
35+
});
36+
1037
export default function TreeDataExample() {
1138

1239
const [rows] = useState(() => generateEmployees(100));
@@ -22,9 +49,14 @@ export default function TreeDataExample() {
2249
description="Hierarchical data display with expand/collapse controls. Define parent-child relationships and OpenGridX renders the tree structure automatically."
2350
sourceCode={sourceCode}
2451
>
52+
<p style={{ marginBottom: 12, color: '#475569', fontSize: '0.875rem' }}>
53+
The <strong>Name</strong> column uses <code>params.rowMeta</code> (v1.1.0) to read{' '}
54+
<code>treeDepth</code>, <code>hasChildren</code>, and <code>isExpanded</code> — rendered
55+
without touching the row object&apos;s underscore fields.
56+
</p>
2557
<DataGrid
2658
rows={rows}
27-
columns={allColumns}
59+
columns={treeColumns}
2860
autoHeight
2961
treeData
3062
getTreeDataPath={(row: Employee) => row.path}

0 commit comments

Comments
 (0)