external/ag-shared/prompts/skills/example/ag-charts/examples-guide.md
This guide covers working with examples in the AG Charts codebase, including guidelines, validation, and path mappings.
Example paths are mapped from repo paths:
packages/ag-charts-website/src/content/gallery/_examples/${exampleName}/index.html => /charts/gallery/examples/${exampleName}packages/ag-charts-website/src/content/docs/${pageName}/_examples/${exampleName}/index.html => /charts/vanilla/${pageName}/examples/${exampleName}_examples/ folder). Load the /spruce-docs skill for how to structure documentation pages.-test page examples are for internal testing and don't typically need much documentation.index.mdoc file which should be a sibling of the enclosing parent folder _examples.index.html which is just a HTML snippet, not a full HTML document.
main.ts is automatically included at runtime.<div id="myChart"></div>
<div class="example-controls">
<div class="controls-row">
<button id="toggleBtn" onclick="toggleUpdates()">Start Updates</button>
<select id="methodSelect" onchange="updateMethod(event.target.value)">
<option value="updateDelta">updateDelta()</option>
<option value="applyTransaction">applyTransaction()</option>
</select>
<span id="cpuUsage" style="margin-left: 10px">CPU: 0%</span>
</div>
</div>
<div id="myChart"></div>
class="example-controls" with class="controls-row" for each row of controls.<button> elements rather than a <select> dropdown.styles.css file which will automatically be included at runtime.
external/ag-website-shared/src/components/example-runner/styles/example-controls.css are applied automatically, and should be favoured for presenting controls in examples.title on the chart options. Do not disable tooltips (tooltip: { enabled: false }) or add explicit axis/option configurations that aren't needed — let defaults work and only configure what the example is demonstrating.data.ts with a getData() function (for single data-set examples) which includes the dataset used by the example.data.ts should also declare this.FormConfig pattern to persist selections to URL hash params (loadConfig()/saveConfig()/hashchange listener). When adding new configurable state to such an example, wire it into this infrastructure so settings round-trip correctly.Functions called from HTML event handlers (onclick, onchange, etc.) should be declared as top-level functions in main.ts. The framework generator automatically handles making these functions available to the HTML.
Correct Pattern:
const chart = AgCharts.create(options);
function toggleFeature() {
options.someOption = !options.someOption;
chart.update(options);
}
<button onclick="toggleFeature()">Toggle</button>
Anti-Pattern - Do NOT use:
// WRONG: Do not assign functions to window
(window as any).toggleFeature = toggleFeature;
// WRONG: Do not use window assignments
window.toggleFeature = toggleFeature;
The framework generator handles function exposure automatically. Using window assignments breaks framework transformation and is unnecessary.
Examples must explicitly register the modules they use with ModuleRegistry before creating charts. Key rules:
ag-charts-enterprise (re-exports all community modules; never mix packages)ag-charts-communitymain.ts, before chart creation; list modules alphabeticallySee .rulesync/skills/example/ag-charts/chart-construction.md for full import patterns (enterprise, community, financial) and common module listings.
Use the object-based axes syntax (axes: { x: { type: 'time' }, y: { type: 'number' } }), NOT the legacy array syntax. For multiple axes, use yKeyAxis/xKeyAxis on the series — there is no axes object on the series.
See .rulesync/skills/example/ag-charts/chart-construction.md for full axes syntax, multiple axes patterns, and migration details.
Examples written in vanilla TypeScript are automatically transformed into React, Angular, and Vue variants. Understanding how this transformation works is essential for creating examples that work across all frameworks.
The example generator parses your vanilla TypeScript code and transforms it into framework-specific implementations. The following patterns are supported and transform cleanly:
Supported Patterns:
Container Setup: Chart container must use document.getElementById():
const options: AgChartOptions = {
container: document.getElementById('myChart'),
// ... other options
};
Chart Instance Storage: Store chart instance in a top-level variable:
const chart = AgCharts.create(options);
Options Object: Declare options as a top-level variable (not const options = {...} inline in create call):
const options: AgChartOptions = {
/* ... */
};
const chart = AgCharts.create(options);
Event Handlers in HTML: Use inline event handlers that call top-level functions:
<button onclick="updateData()">Update</button>
<select onchange="changeTheme(event.target.value)">
...
</select>
Important: Always use event.target.value instead of this.value for select/input handlers. The this.value pattern doesn't work in React/Angular/Vue because this in the generated arrow functions refers to the component context, not the DOM element.
Top-Level Functions: Functions that update the chart should be top-level (not nested):
function updateData() {
options.data = getNewData();
chart.update(options);
}
Chart Updates: Use either chart.update(options) or chart.updateDelta(partial):
// Full update
function changeData() {
options.data = newData;
chart.update(options);
}
// Delta update
function changeTitle() {
chart.updateDelta({ title: { text: 'New Title' } });
}
Using Chart API: Access chart methods directly:
function downloadChart() {
chart.download({ fileName: 'my-chart.png' });
}
Scoping Utility Functions: Functions that use chart or state but aren't called from DOM need /** inScope */:
const options: AgChartOptions = {
container: document.getElementById('myChart'),
// ... options
};
const chart = AgCharts.create(options);
let isRunning = false;
// Called from DOM - automatically hoisted, no comment needed
function toggleUpdates() {
if (isRunning) {
stopUpdates();
} else {
startUpdates();
}
}
// Uses chart/state but NOT called from DOM - needs /** inScope */
/** inScope */
function startUpdates() {
isRunning = true;
// ... use chart
}
/** inScope */
function stopUpdates() {
isRunning = false;
// ... use chart
}
Important: Chart must be initialized with const chart = AgCharts.create(options) immediately after options, not deferred with let chart and later assignment.
**When to use /** inScope \*/:**
chart reference or module-level state variables**When NOT to use /** inScope \*/:**
The following patterns do not transform cleanly to frameworks and should be avoided or require @ag-skip-fws:
Unsupported Patterns:
window object (except for event handlers which are handled automatically)/** inScope \*/**: Utility functions that use chart/state but aren't called from DOM must have this commentExamples Requiring @ag-skip-fws:
// External library integration
import * as d3 from 'd3';
import { getData } from './data';
const scale = d3.scaleLinear();
// Complex DOM manipulation
function updateUI() {
document.querySelector('.status').innerHTML = '<div>Updated</div>';
document.getElementById('myChart').style.height = '500px';
}
// Advanced async patterns
async function fetchAndUpdate() {
const data = await complexAsyncOperation();
await processData(data);
chart.update(options);
}
Follow these guidelines to ensure your examples transform cleanly across all frameworks:
Checklist for Framework-Compatible Examples:
Structure your code consistently:
// 1. Imports
import { AgChartOptions, AgCharts } from 'ag-charts-community';
import { getData } from './data';
// 2. Options object (top-level)
const options: AgChartOptions = {
container: document.getElementById('myChart'),
data: getData(),
// ... rest of options
};
// 3. Chart creation (top-level)
const chart = AgCharts.create(options);
// 4. Top-level functions for interactions
function updateChart() {
options.data = getNewData();
chart.update(options);
}
Keep event handlers simple:
<!-- Good: calls named function -->
<button onclick="toggleSeries()">Toggle</button>
<select onchange="updateInterval(event.target.value)">
...
</select>
<!-- Bad: inline logic -->
<button onclick="chart.update({ title: { text: 'New' } })">Update</button>
Use appropriate update methods:
// Good: full options update
function changeData() {
options.data = newData;
chart.update(options);
}
// Good: delta update for small changes
function changeTitle() {
chart.updateDelta({ title: { text: 'New Title' } });
}
// Avoid: mixing both patterns inconsistently
Manage state in options object:
// Good: state in options
const options: AgChartOptions = {
series: [{ type: 'line', xKey: 'x', yKey: 'y' }],
};
function toggleMarkers() {
const series = options.series![0];
series.marker = { enabled: !series.marker?.enabled };
chart.update(options);
}
// Avoid: separate state variables
let markersEnabled = true;
function toggleMarkers() {
markersEnabled = !markersEnabled;
// ... this pattern doesn't transform well
}
Test across frameworks:
# Generate all framework variants
nx run ag-charts-website-${pageName}_${exampleName}_main.ts:generate
# Typecheck to verify transformations
nx validate-examples
# Visually test in dev server (switch frameworks in UI)
nx dev
Common Pitfalls:
| Issue | Problem | Solution |
|---|---|---|
| Chart not updating | Not calling chart.update() | Always call update() after changing options |
| "No data to display" after partial update | Using chart.update() with subset of options | chart.update() is a full replacement — omitted options (axes, series, etc.) are wiped. Use chart.updateDelta() for partial updates |
| TypeScript errors in React | Using chart in wrong scope | Ensure chart stored in top-level variable |
| Event handlers not working | Complex inline handlers | Use simple function calls |
| Options not persisting | Creating new options each time | Mutate the options object, then update |
| Multiple charts coordination | Complex state synchronization | Simplify to single chart or separate examples |
| Type errors in generated code | Missing type imports | Import all needed types in main.ts |
CRITICAL: All public documentation examples MUST work across all frameworks. The @ag-skip-fws directive is ONLY for internal use.
Public vs Internal Examples:
benchmarks, *-test pages): Can use @ag-skip-fws if neededFor Public Documentation Examples:
If your example requires patterns that don't transform to frameworks (complex DOM manipulation, external libraries, etc.), you MUST redesign the example to be framework-compatible. Do NOT use @ag-skip-fws.
Example Redesign Strategies:
For Internal Test/Benchmark Examples Only:
Use the // @ag-skip-fws directive in main.ts for internal testing or benchmarking:
// @ag-skip-fws
import { AgChartOptions, AgCharts } from 'ag-charts-community';
// ... rest of example
Valid uses of @ag-skip-fws (internal only):
-test page examples for specific edge casesDecision Tree:
Is this a public documentation example?
|-- YES -> MUST be framework-compatible
| |-- Simple controls? -> Implement as shown in patterns
| |-- Complex patterns needed? -> Redesign to be simpler and framework-compatible
| +-- Cannot simplify? -> Reconsider if example belongs in public docs
|
+-- NO (benchmark or *-test page) -> Can use @ag-skip-fws if genuinely needed
|-- Performance testing -> Use @ag-skip-fws
|-- Browser API testing -> Use @ag-skip-fws
+-- Could be made compatible? -> Prefer framework-compatible even for tests
Note on Provided Examples: A mechanism exists at provided/modules/{framework}/ to manually override generated framework files, but this is strongly discouraged as it requires manual maintenance for each framework update. Do not use this mechanism.
yarn nx run ag-charts-website-gallery_${exampleName}_main.ts:generate + :typecheckyarn nx run ag-charts-website-${pageName}_${exampleName}_main.ts:generate + :typecheckyarn nx validate-examples (batch typecheck; much faster than individual targets)yarn nx generate-examples ag-charts-websiteyarn nx generate-thumbnails ag-charts-websiteSee .rulesync/skills/example/ag-charts/validation.md for full validation workflow, decision tree, and common failure fixes.
When implementing features or updating gallery examples based on external code examples (Plnkr, CodePen, etc.), follow this approach to extract code:
Note: When working with Plunker URLs (creating, modifying, or reading plunks), use the plunker skill (/plunker or Skill tool with skill="plunker") instead of manual browser automation or API calls. The skill provides guided workflows for common Plunker operations.
The most efficient way to extract Plnkr code is through their JSON API:
Extract the Plunk ID from the URL:
https://plnkr.co/edit/95LNJoaB0eYqh6DU?open=main.js -> ID is 95LNJoaB0eYqh6DUhttps://embed.plnkr.co/plunk/mWWciY -> ID is mWWciYFetch the plunk data via API:
https://api.plnkr.co/plunks/{plunkId}
This returns JSON with all file contents in the files object.
Extract file contents programmatically:
files object with keys like main.js, index.html, styles.css, data.tscontent property with the full source code//run.plnkr.co/plunks/{plunkId}/{filename}Example API response structure:
{
"id": "95LNJoaB0eYqh6DU",
"files": {
"main.js": {
"content": "const { AgCharts } = agCharts;\n...",
"filename": "main.js",
"raw_url": "//run.plnkr.co/plunks/95LNJoaB0eYqh6DU/main.js"
},
"index.html": { ... }
},
"description": "Example description",
"tags": ["ag-grid", "ag-charts", "example"]
}
WebFetch url="https://api.plnkr.co/plunks/95LNJoaB0eYqh6DU"
prompt="Extract the main.js and data.ts file contents from this plunk"
async function extractPlunkCode(plunkId: string) {
const response = await fetch(`https://api.plnkr.co/plunks/${plunkId}`);
const data = await response.json();
return {
mainJs: data.files['main.js']?.content,
dataTs: data.files['data.ts']?.content,
indexHtml: data.files['index.html']?.content,
styles: data.files['styles.css']?.content,
};
}
// For direct file access without API:
https://run.plnkr.co/plunks/95LNJoaB0eYqh6DU/main.js
https://run.plnkr.co/plunks/95LNJoaB0eYqh6DU/data.ts
If the API is unavailable or access fails, use this fallback approach:
mcp__claude-in-chrome__javascript_tool or mcp__claude-in-chrome__get_page_text) to extract text contentdocument.body.innerTextWhen copying label formatter patterns, ensure you preserve:
// Original Plnkr pattern:
calloutLabel: {
formatter: ({ datum }) => [
{ text: datum.value.toString(), fontSize: 20, color: 'purple', fontWeight: 'bold' },
{ text: '\n' + datum.label, fontSize: 10, color: 'grey' },
],
}
// In your example, maintain this structure:
// - Array of text segment objects (not a string)
// - Each segment has: text, fontSize, color, fontWeight properties
// - Use '\n' for line breaks between segments
When updating gallery examples from external references:
main.ts with the formatter patterndata.ts to match the reference exampleyarn nx validate-examples before committingyarn nx dev to verify visual appearanceindex.htmlhttps://plnkr.co/edit/{plunkId}https://embed.plnkr.co/plunk/{plunkId}https://embed.plnkr.co/{plunkId}/previewhttps://api.plnkr.co/plunks/{plunkId}https://run.plnkr.co/plunks/{plunkId}/{filename}index.html, main.ts, optional styles.css/data.ts)index.mdoc docs page (load /spruce-docs for documentation patterns)yarn nx validate-examplesWhen creating examples that use AG Grid's integrated charts feature (enableCharts: true with createRangeChart()):
Load AG Charts before AG Grid: Always include the AG Charts script tag BEFORE AG Grid Enterprise in the HTML. Without this, enableCharts will fail with error #200 "Unable to use enableCharts as either the ag-charts-community or ag-charts-enterprise script needs to be included alongside ag-grid-enterprise."
<!-- Correct order -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/ag-charts-enterprise.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/ag-grid-enterprise.js"></script>
Use chartDataType: 'time' for timestamps: When displaying time-series data in integrated charts, set chartDataType: 'time' on timestamp columns (not just 'category'). This ensures proper time-axis rendering.
columnDefs: [
{
field: 'timestamp',
chartDataType: 'time', // For time-series data
valueFormatter: (params) => new Date(params.value).toLocaleString(),
},
{ field: 'value', chartDataType: 'series' },
];
When working with AG Charts API features in examples:
initialState zoom structure: Use rangeX/rangeY (not x/y) with start/end properties (not min/max). For date values, use AgStateSerializableDate format:
// Correct structure
const initialState = {
zoom: {
rangeX: {
start: { __type: 'date', value: timestamp },
end: { __type: 'date', value: timestamp }
}
}
};
// Wrong - incorrect property names
const initialState = {
zoom: {
x: { min: timestamp, max: timestamp } // Wrong
}
};
AgStateSerializableDate format: When using dates in initialState, wrap timestamps in the proper format with __type: 'date' and value property as required by the AgStateSerializableDate type.
/spruce-docs skill - How to write documentation pages that reference examples.rulesync/skills/spruce-docs/checklist.md - Validation checklist for documentation pages