dash-aggrid-js 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dash_aggrid_js-0.2.1/LICENSE +0 -0
- dash_aggrid_js-0.2.1/MANIFEST.in +12 -0
- dash_aggrid_js-0.2.1/PKG-INFO +619 -0
- dash_aggrid_js-0.2.1/README.md +593 -0
- dash_aggrid_js-0.2.1/dash_aggrid/__init__.py +26 -0
- dash_aggrid_js-0.2.1/dash_aggrid_js/AgChartsJS.py +72 -0
- dash_aggrid_js-0.2.1/dash_aggrid_js/AgGridJS.py +132 -0
- dash_aggrid_js-0.2.1/dash_aggrid_js/__init__.py +118 -0
- dash_aggrid_js-0.2.1/dash_aggrid_js/_imports_.py +7 -0
- dash_aggrid_js-0.2.1/dash_aggrid_js/dash_aggrid.min.js +3 -0
- dash_aggrid_js-0.2.1/dash_aggrid_js/dash_aggrid.min.js.map +1 -0
- dash_aggrid_js-0.2.1/dash_aggrid_js/metadata.json +1 -0
- dash_aggrid_js-0.2.1/dash_aggrid_js/package-info.json +62 -0
- dash_aggrid_js-0.2.1/dash_aggrid_js/ssrm.py +646 -0
- dash_aggrid_js-0.2.1/dash_aggrid_js.egg-info/PKG-INFO +619 -0
- dash_aggrid_js-0.2.1/dash_aggrid_js.egg-info/SOURCES.txt +22 -0
- dash_aggrid_js-0.2.1/dash_aggrid_js.egg-info/dependency_links.txt +1 -0
- dash_aggrid_js-0.2.1/dash_aggrid_js.egg-info/requires.txt +1 -0
- dash_aggrid_js-0.2.1/dash_aggrid_js.egg-info/top_level.txt +2 -0
- dash_aggrid_js-0.2.1/package.json +62 -0
- dash_aggrid_js-0.2.1/pyproject.toml +3 -0
- dash_aggrid_js-0.2.1/setup.cfg +4 -0
- dash_aggrid_js-0.2.1/setup.py +34 -0
- dash_aggrid_js-0.2.1/tests/test_usage.py +33 -0
|
File without changes
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
include dash_aggrid_js/dash_aggrid.min.js
|
|
2
|
+
include dash_aggrid_js/dash_aggrid.min.js.map
|
|
3
|
+
include dash_aggrid_js/async-*.js
|
|
4
|
+
include dash_aggrid_js/async-*.js.map
|
|
5
|
+
include dash_aggrid_js/*-shared.js
|
|
6
|
+
include dash_aggrid_js/*-shared.js.map
|
|
7
|
+
include dash_aggrid_js/metadata.json
|
|
8
|
+
include dash_aggrid_js/package-info.json
|
|
9
|
+
include dash_aggrid/__init__.py
|
|
10
|
+
include README.md
|
|
11
|
+
include LICENSE
|
|
12
|
+
include package.json
|
|
@@ -0,0 +1,619 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dash-aggrid-js
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Tiny Dash wrapper that mounts AgGridReact using a JS config registry
|
|
5
|
+
Home-page: https://github.com/ScottTpirate/dash-aggrid
|
|
6
|
+
Author: Gulf of Analytics <dev@goa.local>
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Source, https://github.com/ScottTpirate/dash-aggrid
|
|
9
|
+
Project-URL: Tracker, https://github.com/ScottTpirate/dash-aggrid/issues
|
|
10
|
+
Classifier: Framework :: Dash
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: dash>=2.0.0
|
|
15
|
+
Dynamic: author
|
|
16
|
+
Dynamic: classifier
|
|
17
|
+
Dynamic: description
|
|
18
|
+
Dynamic: description-content-type
|
|
19
|
+
Dynamic: home-page
|
|
20
|
+
Dynamic: license
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
Dynamic: project-url
|
|
23
|
+
Dynamic: requires-dist
|
|
24
|
+
Dynamic: requires-python
|
|
25
|
+
Dynamic: summary
|
|
26
|
+
|
|
27
|
+
# dash-aggrid-js
|
|
28
|
+
|
|
29
|
+
dash-aggrid-js (Python import: `dash_aggrid_js`) is a deliberately thin Dash wrapper around **AgGridReact**. It mounts the AG Grid React component directly, so you can copy examples from the AG Grid docs, drop them into a browser-side config registry, and the grid just works inside Dash.
|
|
30
|
+
|
|
31
|
+
> ℹ️ The legacy `dash_aggrid` import still works and simply re-exports `dash_aggrid_js`, so existing apps keep running while you upgrade.
|
|
32
|
+
|
|
33
|
+
> ⚠️ **Pick one wrapper per app.** AgGridJS is not meant to run alongside `dash-ag-grid`; loading both introduces duplicate CSS, overlapping themes, and conflicting event glue. Choose one approach per Dash project.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Table of contents
|
|
38
|
+
|
|
39
|
+
- [Why this wrapper?](#why-this-wrapper)
|
|
40
|
+
- [Quick start](#quick-start)
|
|
41
|
+
- [Installation](#installation)
|
|
42
|
+
- [Creating the config registry](#creating-the-config-registry)
|
|
43
|
+
- [Using `AgGridJS` in Dash](#using-aggridjs-in-dash)
|
|
44
|
+
- [Dash props & event bridge](#dash-props--event-bridge)
|
|
45
|
+
- [Passing arguments (`configArgs`)](#passing-arguments-configargs)
|
|
46
|
+
- [Styling & theming](#styling--theming)
|
|
47
|
+
- [Enterprise support](#enterprise-support)
|
|
48
|
+
- [Advanced patterns](#advanced-patterns)
|
|
49
|
+
- [Server-side row model (SSRM)](#server-side-row-model-ssrm)
|
|
50
|
+
- [Managing asset size](#managing-asset-size)
|
|
51
|
+
- [Developing the component](#developing-the-component)
|
|
52
|
+
- [Testing](#testing)
|
|
53
|
+
- [Known quirks](#known-quirks)
|
|
54
|
+
- [Migration checklist (dash-ag-grid → AgGridJS)](#migration-checklist-dash-ag-grid--aggridjs)
|
|
55
|
+
- [AI assistant playbook](#ai-assistant-playbook)
|
|
56
|
+
- [Packaging & distribution](#packaging--distribution)
|
|
57
|
+
- [Future enhancements](#future-enhancements)
|
|
58
|
+
- [FAQ](#faq)
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Why this wrapper?
|
|
63
|
+
|
|
64
|
+
Dash ships with Python-to-JS property mappings for many components. AG Grid already provides a rich React interface; we wanted a wrapper that stays out of the way:
|
|
65
|
+
|
|
66
|
+
- **No prop translation.** The exact object you pass to `AgGridReact` in the docs is what the wrapper forwards.
|
|
67
|
+
- **Pure JavaScript configs.** Keep complex row models, value formatters, renderers, and callbacks in JavaScript without serialising through Python.
|
|
68
|
+
- **Minimal Dash glue.** Only a handful of grid events (selection, filters, sorting, edits) are mirrored back to Dash for callbacks.
|
|
69
|
+
- **AG Grid v34.x** compatible (Community + optional Enterprise).
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Quick start
|
|
74
|
+
|
|
75
|
+
Clone the repo and run the sample app:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
git clone https://github.com/ScottTpirate/dash-aggrid.git
|
|
79
|
+
cd dash-aggrid
|
|
80
|
+
npm install
|
|
81
|
+
npm run build
|
|
82
|
+
python -m pip install -e .
|
|
83
|
+
python app.py
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Visit http://127.0.0.1:8050 for the sample app. `demo_app.py` renders three grids (including an integrated chart) and a standalone AG Charts example powered by `AgChartsJS`. The configs live in `assets/aggrid-configs.js` and `assets/agcharts-configs.js`.
|
|
87
|
+
|
|
88
|
+
> Reusing in another project? Install the package into that Dash app, copy the asset, and you’re ready to go.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Installation
|
|
93
|
+
|
|
94
|
+
### In an existing Dash project
|
|
95
|
+
|
|
96
|
+
1. Install the Python package (local path shown; swap in the PyPI name `dash-aggrid-js` when using the published wheel):
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
python -m pip install /path/to/dash_aggrid_js
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
2. If working from source, build the JS bundle once:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
cd /path/to/dash-aggrid
|
|
106
|
+
npm install
|
|
107
|
+
npm run build
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
3. Copy `assets/aggrid-configs.js` into your Dash app’s `assets/` folder (or create your own registry script).
|
|
111
|
+
|
|
112
|
+
4. Import and use the component:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from dash_aggrid_js import AgGridJS
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Dash will serve the JS bundle automatically when the component is requested.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Creating the config registry
|
|
123
|
+
|
|
124
|
+
`AgGridJS` looks up options from `window.AGGRID_CONFIGS`. Keep `assets/aggrid-configs.js` as your registry file: each key represents a grid and returns either a plain object or a factory function that the wrapper will call.
|
|
125
|
+
|
|
126
|
+
```javascript
|
|
127
|
+
// assets/aggrid-configs.js
|
|
128
|
+
(function registerAgGridConfigs() {
|
|
129
|
+
const { themeQuartz } = window.AgGridJsThemes || {};
|
|
130
|
+
|
|
131
|
+
const configs = {
|
|
132
|
+
// Static config
|
|
133
|
+
'feature-grid': {
|
|
134
|
+
columnDefs: [
|
|
135
|
+
{ field: 'make' },
|
|
136
|
+
{ field: 'model' },
|
|
137
|
+
{ field: 'price', valueFormatter: (p) => Intl.NumberFormat().format(p.value) },
|
|
138
|
+
],
|
|
139
|
+
defaultColDef: { sortable: true, filter: true, resizable: true },
|
|
140
|
+
rowData: window.FEATURE_GRID_ROWS || [],
|
|
141
|
+
theme: themeQuartz?.withParams?.({ borderRadius: 12 }),
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
// Factory config – reads configArgs/dashProps
|
|
145
|
+
'sales-grid': ({ configArgs }) => ({
|
|
146
|
+
columnDefs: getSalesColumns(configArgs),
|
|
147
|
+
defaultColDef: { sortable: true, filter: true, resizable: true },
|
|
148
|
+
rowData: configArgs?.rowData ?? [],
|
|
149
|
+
}),
|
|
150
|
+
|
|
151
|
+
// SSRM grid – demonstrates server-side datasource wiring
|
|
152
|
+
'ssrm-grid': function ssrmGrid(context) {
|
|
153
|
+
const gridId = context?.id || 'ssrm-grid';
|
|
154
|
+
const ssrmArgs = context?.configArgs?.ssrm || {};
|
|
155
|
+
const baseEndpoint = String(ssrmArgs.endpoint || '/_aggrid/ssrm').replace(/\/$/, '');
|
|
156
|
+
|
|
157
|
+
const datasource = {
|
|
158
|
+
getRows(params) {
|
|
159
|
+
fetch(`${baseEndpoint}/${encodeURIComponent(gridId)}`, {
|
|
160
|
+
method: 'POST',
|
|
161
|
+
headers: { 'Content-Type': 'application/json' },
|
|
162
|
+
body: JSON.stringify(params.request || {}),
|
|
163
|
+
})
|
|
164
|
+
.then(async (response) => {
|
|
165
|
+
const payload = await response.json().catch(() => null);
|
|
166
|
+
if (!response.ok || !payload || typeof payload !== 'object') {
|
|
167
|
+
throw new Error(payload?.error || `HTTP ${response.status}`);
|
|
168
|
+
}
|
|
169
|
+
const rows = Array.isArray(payload.rows) ? payload.rows : [];
|
|
170
|
+
const rowCount = typeof payload.rowCount === 'number' ? payload.rowCount : undefined;
|
|
171
|
+
params.success({ rowData: rows, rowCount });
|
|
172
|
+
})
|
|
173
|
+
.catch((err) => {
|
|
174
|
+
console.error('AgGridJS SSRM request failed', err);
|
|
175
|
+
params.fail();
|
|
176
|
+
});
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
columnDefs: buildSsrMColumns(),
|
|
182
|
+
defaultColDef: baseColumnDefaults(),
|
|
183
|
+
autoGroupColumnDef: { minWidth: 220 },
|
|
184
|
+
rowModelType: 'serverSide',
|
|
185
|
+
cacheBlockSize: 100,
|
|
186
|
+
sideBar: ['columns', 'filters'],
|
|
187
|
+
rowGroupPanelShow: 'always',
|
|
188
|
+
serverSideDatasource: datasource,
|
|
189
|
+
theme: themeQuartz,
|
|
190
|
+
};
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
window.AGGRID_CONFIGS = { ...(window.AGGRID_CONFIGS || {}), ...configs };
|
|
195
|
+
}());
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Registry entries can be either:
|
|
199
|
+
|
|
200
|
+
- **Static** – an object literal (useful for simple read-only grids).
|
|
201
|
+
- **Factory** – a function receiving `{ id, configKey, configArgs, dashProps }`. Use factories when you need runtime arguments (locale toggles, SSRM metadata, chart options) or when wiring server-side datasources.
|
|
202
|
+
|
|
203
|
+
The demo app follows this structure for every grid (`feature-grid`, `sales-grid`, `analytics-grid`, `ssrm-grid`) and does the same for charts via `assets/agcharts-configs.js`. Keeping everything in these registries keeps the asset declarative and makes it simple to share helpers across grids.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Using `AgGridJS` in Dash
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
from dash import Dash, html, Output, Input
|
|
211
|
+
from dash_aggrid_js import AgGridJS
|
|
212
|
+
|
|
213
|
+
app = Dash(__name__) # serves ./assets/aggrid-configs.js automatically
|
|
214
|
+
|
|
215
|
+
app.layout = html.Div(
|
|
216
|
+
[
|
|
217
|
+
AgGridJS(id="sales", configKey="sales-grid", style={"height": 420}),
|
|
218
|
+
html.Pre(id="debug", style={"marginTop": "1rem"}),
|
|
219
|
+
]
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
@app.callback(Output("debug", "children"), Input("sales", "selectedRows"))
|
|
224
|
+
def display_selected(rows):
|
|
225
|
+
if not rows:
|
|
226
|
+
return "No selection"
|
|
227
|
+
return f"Selected rows ({len(rows)}):\n{rows}"
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
if __name__ == "__main__":
|
|
231
|
+
app.run(debug=True)
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
`configKey` is the only required prop. The wrapper will fetch the config, mount `AgGridReact`, and mirror key events back to Dash.
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## Dash props & event bridge
|
|
239
|
+
|
|
240
|
+
| Prop | Direction | Description |
|
|
241
|
+
|-----------------|-----------|-------------|
|
|
242
|
+
| `configKey` | Dash → JS | Required. Looks up `window.AGGRID_CONFIGS[configKey]`. |
|
|
243
|
+
| `configArgs` | Dash → JS | Optional JSON-serialisable payload passed to config factory functions. |
|
|
244
|
+
| `className` | Dash → JS | Container class (defaults to `ag-theme-quartz`). |
|
|
245
|
+
| `style` | Dash → JS | Inline styles for sizing/positioning. |
|
|
246
|
+
| `rowData` | Dash → JS | Optional row data array supplied directly from Dash; overrides `rowData` defined in the JS config. |
|
|
247
|
+
| `selectedRows` | JS → Dash | Array of selected rows (`api.getSelectedRows()`). |
|
|
248
|
+
| `filterModel` | JS → Dash | Current filter model (`api.getFilterModel()`). |
|
|
249
|
+
| `sortModel` | JS → Dash | Simplified array of columns with active sorting (`colId`, `sort`, `sortIndex`). |
|
|
250
|
+
| `editedCells` | JS → Dash | Single-element array describing the most recent cell edit (`rowId`, `colId`, `oldValue`, `newValue`). |
|
|
251
|
+
|
|
252
|
+
The wrapper:
|
|
253
|
+
|
|
254
|
+
- Runs user-defined AG Grid handlers first, then calls `setProps`.
|
|
255
|
+
- Stores `gridApi` on `onGridReady` so other events can read selection/filter/sort state.
|
|
256
|
+
- Emits initial `selectedRows`, `filterModel`, and `sortModel` after the grid becomes ready.
|
|
257
|
+
|
|
258
|
+
Need more events? Add them to your config and call `setProps` manually.
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## Passing arguments (`configArgs`)
|
|
263
|
+
|
|
264
|
+
Use `configArgs` when Dash needs to parameterise the grid:
|
|
265
|
+
|
|
266
|
+
```python
|
|
267
|
+
@app.callback(Output("sales", "configArgs"), Input("region-dropdown", "value"))
|
|
268
|
+
def choose_region(region):
|
|
269
|
+
return {"region": region}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
In JS:
|
|
273
|
+
|
|
274
|
+
```javascript
|
|
275
|
+
window.AGGRID_CONFIGS['sales-grid'] = ({ configArgs }) => ({
|
|
276
|
+
columnDefs: getColumnsFor(configArgs?.region),
|
|
277
|
+
rowData: [],
|
|
278
|
+
});
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
`configArgs` must be JSON-serialisable. For functions or class instances, keep them inside the registry instead.
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## Styling & theming
|
|
286
|
+
|
|
287
|
+
AG Grid v34 defaults to the **Theming API**. The wrapper exposes common themes on `window.AgGridJsThemes` so your asset can pick them up on a per-grid basis:
|
|
288
|
+
|
|
289
|
+
```javascript
|
|
290
|
+
const { themeQuartz, themeAlpine } = window.AgGridJsThemes || {};
|
|
291
|
+
|
|
292
|
+
window.AGGRID_CONFIGS['sales-grid'] = {
|
|
293
|
+
rowData: [...],
|
|
294
|
+
columnDefs: [...],
|
|
295
|
+
theme: themeQuartz.withParams({
|
|
296
|
+
accentColor: '#2563eb',
|
|
297
|
+
headerBackgroundColor: '#0f172a',
|
|
298
|
+
headerTextColor: '#e2e8f0'
|
|
299
|
+
})
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
window.AGGRID_CONFIGS['inventory-grid'] = ({ configArgs }) => ({
|
|
303
|
+
rowData: [...],
|
|
304
|
+
columnDefs: [...],
|
|
305
|
+
theme: themeAlpine.withParams({
|
|
306
|
+
accentColor: configArgs?.locale === 'ja-JP' ? '#f97316' : '#14b8a6',
|
|
307
|
+
borderRadius: 10
|
|
308
|
+
})
|
|
309
|
+
});
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
- `style` still controls the container height/width. Set the height explicitly so the grid has room to render.
|
|
313
|
+
- `className` is optional and can be used for extra layout styling. Theme classes are no longer required when using the theming API.
|
|
314
|
+
- If you want to keep the legacy CSS themes instead, set `theme: 'legacy'` in your registry entry and include the old CSS manually.
|
|
315
|
+
- Integrated charts rely on AG Grid Enterprise. If the module is not present, the grid will skip chart creation automatically.
|
|
316
|
+
|
|
317
|
+
## Standalone AG Charts
|
|
318
|
+
|
|
319
|
+
`AgChartsJS` wraps the charting engine from `ag-charts-community` without the React helper. Define chart options in `window.AGCHART_CONFIGS` and point the component at a key:
|
|
320
|
+
|
|
321
|
+
```javascript
|
|
322
|
+
// assets/agcharts-configs.js
|
|
323
|
+
window.AGCHART_CONFIGS = window.AGCHART_CONFIGS || {};
|
|
324
|
+
|
|
325
|
+
window.AGCHART_CONFIGS['revenue-chart'] = ({ configArgs }) => ({
|
|
326
|
+
data: [...],
|
|
327
|
+
title: { text: 'Quarterly Revenue' },
|
|
328
|
+
series: [
|
|
329
|
+
{ type: 'bar', direction: 'vertical', xKey: 'quarter', yKey: 'north' },
|
|
330
|
+
{ type: 'bar', direction: 'vertical', xKey: 'quarter', yKey: 'emea' },
|
|
331
|
+
],
|
|
332
|
+
theme: {
|
|
333
|
+
baseTheme: 'ag-default',
|
|
334
|
+
palette: {
|
|
335
|
+
fills: [configArgs?.accentColor || '#2563eb', '#f97316'],
|
|
336
|
+
strokes: ['#1e3a8a', '#9a3412'],
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
In Dash:
|
|
343
|
+
|
|
344
|
+
```python
|
|
345
|
+
from dash_aggrid_js import AgChartsJS
|
|
346
|
+
|
|
347
|
+
AgChartsJS(id="revenue", optionsKey="revenue-chart", style={"height": 360})
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
The wrapper takes either `options` (inline chart definition) or an `optionsKey` that resolves to `window.AGCHART_CONFIGS[key]`. Options factories receive `{ optionsKey, configArgs, dashProps }` just like grid configs.
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
## Enterprise support
|
|
355
|
+
|
|
356
|
+
Enterprise features are fully supported. To enable:
|
|
357
|
+
|
|
358
|
+
1. Ensure `ag-grid-enterprise` is installed (this package already includes it).
|
|
359
|
+
2. Expose the license key on the window before configs load:
|
|
360
|
+
|
|
361
|
+
```javascript
|
|
362
|
+
window.AGGRID_LICENSE_KEY = "<YOUR KEY>";
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
The sample asset calls `LicenseManager.setLicenseKey` if the key exists. Remember: the key is visible client-side. Inject it dynamically if you prefer not to store it in source control.
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## Advanced patterns
|
|
370
|
+
|
|
371
|
+
- **Server-side row model:** See the [dedicated SSRM walkthrough](#server-side-row-model-ssrm) for the SQL helper, distinct endpoints, and asset patch.
|
|
372
|
+
- **Multiple variants:** Store each variant under its own key (`window.AGGRID_CONFIGS['sales/daily']`, `['sales/monthly']`), or return different objects inside a single factory.
|
|
373
|
+
- **Custom Dash outputs:** Pass `context` or other callbacks through the config and call `setProps` yourself for bespoke events (row drag, clipboard, etc.).
|
|
374
|
+
- **Sharing state between grids:** Keep shared data on `window` or a dedicated JS module; use Dash callbacks on `selectedRows`/`filterModel` to sync across components.
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
## Server-side row model (SSRM)
|
|
379
|
+
|
|
380
|
+
AgGridJS pairs a DuckDB-aware SQL builder with Dash hooks so server-side row-model grids work without manual Flask routes. Any grid that declares `configArgs={"ssrm": ...}` automatically registers JSON endpoints for data blocks and Set Filter values.
|
|
381
|
+
|
|
382
|
+
### 1. Configure the grid in Dash
|
|
383
|
+
|
|
384
|
+
```python
|
|
385
|
+
from pathlib import Path
|
|
386
|
+
|
|
387
|
+
from dash_aggrid_js import AgGridJS
|
|
388
|
+
|
|
389
|
+
AgGridJS(
|
|
390
|
+
id="orders-grid",
|
|
391
|
+
configKey="orders-ssrm",
|
|
392
|
+
style={"height": 420},
|
|
393
|
+
configArgs={
|
|
394
|
+
"ssrm": {
|
|
395
|
+
"duckdb_path": str(Path("data/orders.duckdb")),
|
|
396
|
+
"table": "public.orders", # or provide `builder` for custom SQL
|
|
397
|
+
# "builder": lambda req: sql_for(req, "(SELECT * FROM ... ) AS src"),
|
|
398
|
+
# "endpoint": "/_aggrid/ssrm" # optional route prefix override
|
|
399
|
+
}
|
|
400
|
+
},
|
|
401
|
+
)
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
- `duckdb_path` is required and is opened read-only for each request.
|
|
405
|
+
- Supply either a `table`/sub-query string or a custom `builder(request) -> SQL` callable (advanced scenarios).
|
|
406
|
+
- Optional `endpoint` lets you change the HTTP prefix (defaults to `/_aggrid/ssrm`).
|
|
407
|
+
|
|
408
|
+
### 2. Wire the asset config
|
|
409
|
+
|
|
410
|
+
The JS registry just needs to opt the grid into SSRM so the datasource can post to the generated endpoint; the component takes care of Set Filter values automatically.
|
|
411
|
+
|
|
412
|
+
```javascript
|
|
413
|
+
// assets/aggrid-configs.js
|
|
414
|
+
(function () {
|
|
415
|
+
const { themeQuartz } = window.AgGridJsThemes || {};
|
|
416
|
+
|
|
417
|
+
window.AGGRID_CONFIGS['orders-ssrm'] = (context) => {
|
|
418
|
+
const gridId = context?.id || 'orders-grid';
|
|
419
|
+
const ssrmArgs = context?.configArgs?.ssrm || {};
|
|
420
|
+
const baseEndpoint = (ssrmArgs.endpoint || '/_aggrid/ssrm').replace(/\/$/, '');
|
|
421
|
+
|
|
422
|
+
return {
|
|
423
|
+
columnDefs: [
|
|
424
|
+
{ field: 'order_id', headerName: 'Order ID', maxWidth: 130 },
|
|
425
|
+
{ field: 'region', filter: 'agSetColumnFilter', rowGroup: true },
|
|
426
|
+
{ field: 'product', minWidth: 160, filter: 'agSetColumnFilter' },
|
|
427
|
+
{ field: 'category', minWidth: 150, filter: 'agSetColumnFilter' },
|
|
428
|
+
{ field: 'quarter', maxWidth: 140, filter: 'agSetColumnFilter' },
|
|
429
|
+
{ field: 'units', type: 'numericColumn', aggFunc: 'sum' },
|
|
430
|
+
{
|
|
431
|
+
field: 'revenue',
|
|
432
|
+
type: 'numericColumn',
|
|
433
|
+
aggFunc: 'sum',
|
|
434
|
+
valueFormatter: (params) => Intl.NumberFormat('en-US', {
|
|
435
|
+
style: 'currency',
|
|
436
|
+
currency: 'USD',
|
|
437
|
+
maximumFractionDigits: 0,
|
|
438
|
+
}).format(params.value || 0),
|
|
439
|
+
},
|
|
440
|
+
],
|
|
441
|
+
defaultColDef: {
|
|
442
|
+
flex: 1,
|
|
443
|
+
sortable: true,
|
|
444
|
+
filter: true,
|
|
445
|
+
resizable: true,
|
|
446
|
+
enableRowGroup: true,
|
|
447
|
+
enablePivot: true,
|
|
448
|
+
enableValue: true,
|
|
449
|
+
},
|
|
450
|
+
autoGroupColumnDef: { minWidth: 220 },
|
|
451
|
+
rowModelType: 'serverSide',
|
|
452
|
+
cacheBlockSize: 100,
|
|
453
|
+
sideBar: ['columns', 'filters'],
|
|
454
|
+
rowGroupPanelShow: 'always',
|
|
455
|
+
serverSideDatasource: {
|
|
456
|
+
getRows(params) {
|
|
457
|
+
fetch(`${baseEndpoint}/${encodeURIComponent(gridId)}`, {
|
|
458
|
+
method: 'POST',
|
|
459
|
+
headers: { 'Content-Type': 'application/json' },
|
|
460
|
+
body: JSON.stringify(params.request || {}),
|
|
461
|
+
})
|
|
462
|
+
.then(async (response) => {
|
|
463
|
+
const payload = await response.json().catch(() => null);
|
|
464
|
+
if (!response.ok || !payload || typeof payload !== 'object') {
|
|
465
|
+
throw new Error(payload?.error || `HTTP ${response.status}`);
|
|
466
|
+
}
|
|
467
|
+
const rows = Array.isArray(payload.rows) ? payload.rows : [];
|
|
468
|
+
const rowCount = typeof payload.rowCount === 'number' ? payload.rowCount : undefined;
|
|
469
|
+
params.success({ rowData: rows, rowCount });
|
|
470
|
+
})
|
|
471
|
+
.catch((err) => {
|
|
472
|
+
console.error('AgGridJS SSRM request failed', err);
|
|
473
|
+
params.fail();
|
|
474
|
+
});
|
|
475
|
+
},
|
|
476
|
+
},
|
|
477
|
+
theme: themeQuartz,
|
|
478
|
+
};
|
|
479
|
+
};
|
|
480
|
+
}());
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### 3. Notes
|
|
484
|
+
|
|
485
|
+
- Multiple SSRM grids can coexist; give each a unique `id` and `configKey`. Routes are registered automatically per grid/table pair via Dash hooks—no manual Flask code required.
|
|
486
|
+
- Set Filters pick up values via `/distinct` only for columns using `agSetColumnFilter` (or a Multi Filter that includes it). Add that filter type to any columns where you want distinct lookups.
|
|
487
|
+
- Pivot mode currently falls back to basic SQL; if you need pivoted results you’ll need to extend `sql_for` (grouping and aggregation are supported today).
|
|
488
|
+
- Custom builders receive the raw AG Grid request payload. Use `sql_for` inside the builder if you need to compose additional joins or filters.
|
|
489
|
+
- You can still call `sql_for`/`distinct_sql` manually (for exports, reports, etc.) — they mirror the autogenerated queries.
|
|
490
|
+
|
|
491
|
+
---
|
|
492
|
+
## Managing asset size
|
|
493
|
+
|
|
494
|
+
`assets/aggrid-configs.js` ships as a single registry for ease of onboarding. Browser caching and HTTP/2 keep it efficient, but for very large apps you can:
|
|
495
|
+
|
|
496
|
+
- Split configs across multiple asset files (one per page/feature) and guard them with early returns (`if (window.location.pathname !== '/reports') return;`).
|
|
497
|
+
- Lazily attach large `rowData` arrays by fetching from the server (`fetch('/api/data').then(...)`) instead of embedding huge literals.
|
|
498
|
+
- Minify/treeshake custom helpers via your build pipeline if you generate assets programmatically.
|
|
499
|
+
|
|
500
|
+
The sample file stays small—only configuration objects and lightweight factories—so no additional bundling is required by default.
|
|
501
|
+
|
|
502
|
+
---
|
|
503
|
+
|
|
504
|
+
## Developing the component
|
|
505
|
+
|
|
506
|
+
```bash
|
|
507
|
+
npm install # install JS dependencies (React, AG Grid, tooling)
|
|
508
|
+
npm run build # build the production bundle + regenerate Dash bindings
|
|
509
|
+
python -m pip install -e . # expose the component as an editable install
|
|
510
|
+
python app.py # run the demo Dash app
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
- Component source lives in `src/lib/components/AgGridJS.jsx`.
|
|
514
|
+
- Webpack output goes to `dash_aggrid_js/dash_aggrid.min.js`.
|
|
515
|
+
- Python/R/Julia wrappers are regenerated via `dash-generate-components` during `npm run build`.
|
|
516
|
+
|
|
517
|
+
Need to tweak bundling? Edit `webpack.config.js`.
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
## Testing
|
|
522
|
+
|
|
523
|
+
Current repo ships with a simple smoke test:
|
|
524
|
+
|
|
525
|
+
```bash
|
|
526
|
+
python -m pytest tests/test_usage.py
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
For end-to-end tests, reintroduce a `dash_duo` Selenium test—just make sure Chrome/Chromedriver (or another WebDriver) is available.
|
|
530
|
+
|
|
531
|
+
---
|
|
532
|
+
|
|
533
|
+
## Known quirks
|
|
534
|
+
|
|
535
|
+
- Chrome will log "Added non-passive event listener" when AG Charts attaches wheel handlers. The library has not yet marked them as passive; there is no functional impact.
|
|
536
|
+
- React 18 warns about deprecated `componentWillMount`/`componentWillReceiveProps` lifecycles inside AG Grid Enterprise. They are harmless and scheduled for removal upstream.
|
|
537
|
+
|
|
538
|
+
---
|
|
539
|
+
|
|
540
|
+
## Migration checklist (dash-ag-grid → AgGridJS)
|
|
541
|
+
|
|
542
|
+
- Install the new wrapper (`pip install dash-aggrid-js`) and rebuild once (`npm install && npm run build`) so Dash can serve the bundled assets.
|
|
543
|
+
- Move grid definitions out of Dash props and into `assets/aggrid-configs.js`. Copy each grid’s `columnDefs`, default column settings, and event handlers into a registry entry (object or factory) so the asset controls configuration, not Python.
|
|
544
|
+
- Mirror the same approach for charts (`assets/agcharts-configs.js`) so chart options live alongside grid configs and can react to `configArgs`.
|
|
545
|
+
- Swap Python `gridOptions`/`columnState` assignments for `configKey`/`configArgs`. Any runtime values (locale, SSRM metadata, user selections) travel through `configArgs` and the JS factory can honour them.
|
|
546
|
+
- Use the component’s `rowData` prop when you truly need to push data from Dash; otherwise keep data fetching in JS (e.g., SSRM datasource, fetches).
|
|
547
|
+
- Port Python-defined renderers/formatters/value parsers to JavaScript functions—AgGridJS does not serialise functions across the bridge.
|
|
548
|
+
- Re-implement dash-ag-grid conveniences (persistence, context menus, quick filters) using native AG Grid APIs in the asset, calling `setProps` only when you need to notify Dash.
|
|
549
|
+
- Replace theme classes with the theming API: grab `themeQuartz`/`themeAlpine` from `window.AgGridJsThemes` and call `.withParams(...)` where needed.
|
|
550
|
+
- Continue handling Enterprise licensing by setting `window.AGGRID_LICENSE_KEY` before configs execute; the sample helper still applies it automatically.
|
|
551
|
+
|
|
552
|
+
---
|
|
553
|
+
|
|
554
|
+
## AI assistant playbook
|
|
555
|
+
|
|
556
|
+
- **Understand the registry**: Every grid/chart lives in `assets/aggrid-configs.js` (and `agcharts-configs.js`). Each exported key should be a self-contained config or factory.
|
|
557
|
+
- **When adding a grid**: Duplicate a pattern, update the key, tweak `columnDefs`, defaults, and any custom logic. Keep everything serialisable and let the registry own datasource setup (e.g., SSRM).
|
|
558
|
+
- **Migrations**: Move Python `gridOptions`/callbacks into JS; pass runtime values via `configArgs`. Use the component’s `rowData` only when Dash truly needs to push data.
|
|
559
|
+
- **SSRM**: Provide `configArgs={ ssrm: { duckdb_path, table | builder, endpoint? } }`. The helper auto-registers routes and set-filter values.
|
|
560
|
+
- **Charts**: Follow the same registry pattern in `agcharts-configs.js`; use factories when you need options derived from `configArgs`.
|
|
561
|
+
- **Bundle hygiene**: Run `npm run build` after editing component source or assets so Dash serves the latest bundle.
|
|
562
|
+
|
|
563
|
+
---
|
|
564
|
+
|
|
565
|
+
## Packaging & distribution
|
|
566
|
+
|
|
567
|
+
1. Run `npm run build` to guarantee the JS bundle matches the source.
|
|
568
|
+
2. Build Python artefacts (requires network access to fetch build-system deps):
|
|
569
|
+
|
|
570
|
+
```bash
|
|
571
|
+
python -m build # creates dist/*.tar.gz and dist/*.whl
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
3. Publish:
|
|
575
|
+
- PyPI: `twine upload dist/*`
|
|
576
|
+
- npm (optional): `npm publish`
|
|
577
|
+
|
|
578
|
+
`MANIFEST.in` already lists the built bundles so Dash can serve them when installed from a wheel/sdist.
|
|
579
|
+
|
|
580
|
+
For large apps you can drop page-specific config scripts into `assets/` and guard them by pathname (e.g., `if (window.location.pathname === '/reports') { ... }`). Each script is still global, but the early-return keeps unrelated pages lightweight.
|
|
581
|
+
|
|
582
|
+
---
|
|
583
|
+
|
|
584
|
+
## Future enhancements
|
|
585
|
+
|
|
586
|
+
- Add pytest coverage for `sql_for` and `distinct_sql` across filter combinations, grouping hierarchies, and pagination paths.
|
|
587
|
+
- Provide extension hooks so callers can override identifier quoting or literal casting without monkeypatching the helper.
|
|
588
|
+
- Document or automate SSRM export row-count strategies once production usage surfaces common patterns.
|
|
589
|
+
- Extend `sql_for` to generate pivot SQL so the SSRM demo supports AG Grid pivot mode end-to-end.
|
|
590
|
+
|
|
591
|
+
---
|
|
592
|
+
|
|
593
|
+
## FAQ
|
|
594
|
+
|
|
595
|
+
**How do I load configs from TypeScript?**
|
|
596
|
+
Compile them to JS (e.g., via `tsc`) and expose the resulting objects/functions on `window.AGGRID_CONFIGS`.
|
|
597
|
+
|
|
598
|
+
**Can I update data from Dash callbacks?**
|
|
599
|
+
Yes. Return new data through `configArgs` or store it in a registry the config reads from. You can also hit custom HTTP endpoints from within the config (server-side row model).
|
|
600
|
+
|
|
601
|
+
**What if I need more events back in Dash?**
|
|
602
|
+
Add the AG Grid handler in the registry and call `setProps` manually. Example:
|
|
603
|
+
|
|
604
|
+
```javascript
|
|
605
|
+
window.AGGRID_CONFIGS['editable-grid'] = {
|
|
606
|
+
onRowDragEnd: (params) => {
|
|
607
|
+
params.api.refreshCells();
|
|
608
|
+
params.context?.setProps?.({ lastDragged: params.node.data });
|
|
609
|
+
},
|
|
610
|
+
context: { setProps: window.dash_clientside.set_props }, // optional helper
|
|
611
|
+
};
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
**Does this conflict with dash-ag-grid?**
|
|
615
|
+
No; they serve different audiences. This package is intentionally thin and expects you to manage grid options in JS. `dash-ag-grid` provides Python prop mapping and many Dash-first conveniences.
|
|
616
|
+
|
|
617
|
+
---
|
|
618
|
+
|
|
619
|
+
Questions or ideas? Open an issue or PR—this wrapper aims to stay lightweight while keeping the AG Grid React API fully accessible in Dash.
|