---
title: "Context Menu"
enterprise: true
framework: javascript
version: "14.1.0"
---

# Context Menu

The Context Menu provides context-aware interactions with the chart elements.

#### Context Menu

```ts
import {
  AgCartesianChartOptions,
  AgCharts,
  AnimationModule,
  BarSeriesModule,
  CategoryAxisModule,
  ContextMenuModule,
  CrosshairModule,
  LegendModule,
  ModuleRegistry,
  NumberAxisModule,
} from "ag-charts-enterprise";

ModuleRegistry.registerModules([
  AnimationModule,
  BarSeriesModule,
  CategoryAxisModule,
  ContextMenuModule,
  CrosshairModule,
  LegendModule,
  NumberAxisModule,
]);

const options: AgCartesianChartOptions = {
  title: {
    text: "Sweaters made",
  },
  data: [
    {
      month: "Jun",
      sweaters: 50,
      hats: 40,
    },
    {
      month: "Jul",
      sweaters: 70,
      hats: 50,
    },
    {
      month: "Aug",
      sweaters: 60,
      hats: 30,
    },
  ],
  series: [
    {
      type: "bar",
      xKey: "month",
      yKey: "sweaters",
      yName: "Sweaters Made",
    },
    {
      type: "bar",
      xKey: "month",
      yKey: "hats",
      yName: "Hats Made",
    },
  ],
};

options.container = document.getElementById("myChart");

const chart = AgCharts.create(options);
```

[Live example: Context Menu](https://www.ag-grid.com/charts/typescript/context-menu/examples/context-menu)

In this example:

- Right clicking anywhere on the chart shows the Context Menu with the option to download the chart.
- Right clicking on a legend item will show the Context Menu with additional options to toggle series visibility.

The Context Menu is enabled by default, to disable set `contextMenu.enabled` to `false`.

```js
{
    contextMenu: {
        enabled: false,
    },
}
```

## Built-in Items

The Context Menu displays default items based on the right-clicked element.

The `contextMenu.items` array accepts special string values to conveniently reconfigure these defaults.

#### Context Menu Built-in Items

```ts
import {
  AgCartesianChartOptions,
  AgCharts,
  AgContextMenuItemLiteral,
  AnimationModule,
  CategoryAxisModule,
  ContextMenuModule,
  CrosshairModule,
  LegendModule,
  LineSeriesModule,
  ModuleRegistry,
  NumberAxisModule,
  ZoomModule,
} from "ag-charts-enterprise";
import { generateCurrencyData } from "./data";

const CUSTOM_ORDER: AgContextMenuItemLiteral[] = [
  "toggle-series-visibility",
  "toggle-other-series",
  "separator",
  "zoom-to-cursor",
  "pan-to-cursor",
  "reset-zoom",
  "separator",
  "download",
];
ModuleRegistry.registerModules([
  AnimationModule,
  CategoryAxisModule,
  ContextMenuModule,
  CrosshairModule,
  LegendModule,
  LineSeriesModule,
  NumberAxisModule,
  ZoomModule,
]);

const options: AgCartesianChartOptions = {
  title: { text: "Inflation-Adjusted Currency Values (1800–2025)" },
  zoom: { enabled: true },
  contextMenu: {
    items: CUSTOM_ORDER,
  },
  data: generateCurrencyData(),
  series: [
    { type: "line", xKey: "year", yKey: "USD" },
    { type: "line", xKey: "year", yKey: "GBP" },
    { type: "line", xKey: "year", yKey: "JPY" },
  ],
  axes: {
    x: { type: "category", label: { autoRotate: false } },
  },
};

options.container = document.getElementById("myChart");

const chart = AgCharts.create(options);

function onCustomOrder() {
  options.contextMenu!.items = CUSTOM_ORDER;

  chart.update(options);
}

function onDefaultOrder() {
  options.contextMenu!.items = ["defaults"];

  chart.update(options);
}

if (typeof window !== "undefined") {
  // Attach external event handlers to window so they can be called from index.html
  (<any>window).onCustomOrder = onCustomOrder;
  (<any>window).onDefaultOrder = onDefaultOrder;
}
```

[Live example: Context Menu Built-in Items](https://www.ag-grid.com/charts/typescript/context-menu/examples/context-menu-builtins)

```js
{
    contextMenu: {
        items: [
            'defaults', //all the default menu-items in the pre-determined order.
            'separator', // a non-interactive horizontal line.
            'toggle-series-visibility', // used for legend items.
            'toggle-other-series', // used for legend items in multi-series charts.
            'zoom-to-cursor', // used with zoom.
            'pan-to-cursor', // used with zoom.
            'reset-zoom', // used with zoom.
            'download',
        ],
    },
}
```

In this example:

- The "Custom Order" button reorders the default built-in Menu Items and adds separators.
- The "Default Order" button resets the Menu Items to the default `['defaults']` value.

These string values can be used in combination with [Custom Actions](#custom-actions).

## Custom Actions

The Context Menu's custom actions can be used to run arbitrary functions based on the element that is right-clicked.

#### Context Menu Custom Actions

```ts
import {
  AgAxisContextMenuActionEvent,
  AgCartesianChartOptions,
  AgCharts,
  AnimationModule,
  BarSeriesModule,
  CategoryAxisModule,
  ContextMenuModule,
  CrossLinesModule,
  CrosshairModule,
  LegendModule,
  ModuleRegistry,
  NumberAxisModule,
} from "ag-charts-enterprise";
import { DataType, getData } from "./data";

ModuleRegistry.registerModules([
  AnimationModule,
  BarSeriesModule,
  CategoryAxisModule,
  ContextMenuModule,
  CrossLinesModule,
  CrosshairModule,
  LegendModule,
  NumberAxisModule,
]);

const options: AgCartesianChartOptions<DataType> = {
  title: {
    text: "Q3 Production",
  },
  subtitle: {
    text: "Total clothing manufactured from July to September",
  },
  footnote: {
    text: "Note: This data excludes products made with manufacturing defects.",
  },
  contextMenu: {
    items: [
      "defaults",
      "separator",
      {
        showOn: "always",
        label: "Say hello",
        action: () => {
          console.log("Hello world!");
        },
      },
      "separator",
      {
        showOn: "axis",
        label: "Say hello to an axis",
        action: (ev: AgAxisContextMenuActionEvent) => {
          console.log(`Hello in axis "${ev.axisId}":"`, ev);
        },
      },
      "separator",
      {
        showOn: "caption",
        label: "Say hello in a caption",
        action: ({ captionType, text }) => {
          console.log(`Hello in a ${captionType} caption: "${text}"`);
        },
      },
      "separator",
      {
        showOn: "series-area",
        label: "Say hello in the series area",
        action: () => {
          console.log("Hello in the series area!");
        },
      },
      "separator",
      {
        showOn: "series-node",
        label: "Say hello to a node",
        action: ({ datum, yKey }) => {
          console.log(`Hello ${yKey} in ${datum.month}!`);
        },
      },
      "separator",
      {
        showOn: "legend-item",
        label: "Say hello to a legend item",
        action: ({ itemId }) => {
          console.log(`Hello ${itemId}!`);
        },
      },
      {
        showOn: "cross-line",
        label: "Say hello to a cross line",
        action: ({ value, direction, crossLineType }) => {
          console.log(`Hello ${direction}-${crossLineType} ${value}!`);
        },
      },
    ],
  },
  data: getData(),
  series: [
    {
      type: "bar",
      xKey: "month",
      yKey: "sweaters",
      yName: "Sweaters Made",
    },
    {
      type: "bar",
      xKey: "month",
      yKey: "hats",
      yName: "Hats Made",
    },
  ],
  axes: {
    y: {
      type: "number",
      crossLines: [
        {
          type: "line",
          value: 53,
          label: {
            text: "Target",
            position: "top-right",
            fontStyle: "italic",
          },
          lineDash: [2, 4],
        },
      ],
    },
  },
};

options.container = document.getElementById("myChart");

const chart = AgCharts.create(options);
```

[Live example: Context Menu Custom Actions](https://www.ag-grid.com/charts/typescript/context-menu/examples/context-menu-actions)

Use the `showOn` property to specify when a custom item should be shown:

- `always` - shown regardless of what was clicked.
- `axis` - shown when right-clicking an axis.
- `caption` - shown when right-clicking a caption (title, subtitle, footnote).
- `cross-line` - shown when right-clicking a [Cross Line's](https://www.ag-grid.com/charts/javascript/axes-cross-lines/) line or fill.
- `series-area` - shown when right-clicking anywhere within the series area bounds.
- `series-node` - shown when right-clicking a datum node.
- `legend-item` - shown when right-clicking a legend item.

If there are multiple elements that match the `showOn` value, all of them are shown in the Context Menu.

```js
{
    contextMenu: {
        items: [
            'defaults',
            'separator',
            {
                showOn: 'always',
                label: 'Say hello',
                action: () => console.log('Hello world!'),
            },
            'separator',
            {
                showOn: 'axis',
                label: 'Say hello to an axis',
                action: (ev) => console.log(`Hello in axis "${ev.axisId}":"`, ev),
            },
            {
                showOn: 'series-area',
                label: 'Say hello in the series area',
                action: () => console.log('Hello in the series area!'),
            },
            'separator',
            {
                showOn: 'series-node',
                label: 'Say hello to a node',
                action: ({ datum, yKey }) => console.log(`Hello ${yKey} in ${datum.month}!`),
            },
            //...more custom actions for legend items, captions, etc.
        ],
    },
}
```

In this configuration:

- A single custom action is added for the entire chart, the captions, the axes, the series area, the series node and the legend items.
- Multiple entries per `showOn` element can be specified within the array if required.
- Right clicking on one of these areas will show these additional actions in the Context Menu.
- Clicking these extra actions will display information in the console, demonstrating that the action is aware of details about the right-clicked item.

## Sub-Menus

To add Sub-Menus to the Context Menu, simply use the `items` property recursively.

#### Context Menu Sub-Menus

```ts
import {
  AgAxisContextMenuActionEvent,
  AgCartesianChartOptions,
  AgCharts,
  AnimationModule,
  CategoryAxisModule,
  ContextMenuModule,
  CrossLinesModule,
  CrosshairModule,
  LegendModule,
  LineSeriesModule,
  ModuleRegistry,
  NumberAxisModule,
  ZoomModule,
} from "ag-charts-enterprise";
import { DataType, getData } from "./data";

ModuleRegistry.registerModules([
  AnimationModule,
  CategoryAxisModule,
  ContextMenuModule,
  CrossLinesModule,
  CrosshairModule,
  LegendModule,
  LineSeriesModule,
  NumberAxisModule,
  ZoomModule,
]);

const options: AgCartesianChartOptions<DataType> = {
  title: { text: "GDP Growth (1995–2024)" },
  subtitle: { text: "Trillions USD" },
  footnote: {
    text: "Disclaimer: This data is for illustration purposes only.",
  },
  contextMenu: {
    items: [
      "download",
      {
        showOn: "series-area",
        label: "Zoom Controls",
        items: ["zoom-to-cursor", "pan-to-cursor", "reset-zoom"],
      },
      {
        showOn: "legend-item",
        label: "Legend Controls",
        items: ["toggle-series-visibility", "toggle-other-series"],
      },
      "separator",
      {
        label: "Debug Console",
        items: [
          {
            showOn: "always",
            label: `On 'always'`,
            action: () => console.log(`On 'always' clicked.`),
          },
          {
            showOn: "axis",
            label: `On 'axis'`,
            action: (ev: AgAxisContextMenuActionEvent) =>
              console.log(`On 'axis' clicked -`, ev.axisId, ev),
          },
          {
            showOn: "caption",
            label: `On 'caption'`,
            action: ({ captionType, text }) =>
              console.log(`On 'caption' clicked -`, captionType, text),
          },
          {
            showOn: "series-area",
            label: `On 'series-area'`,
            action: () => console.log(`On 'series-area' clicked.`),
          },
          {
            showOn: "series-node",
            label: `On 'series-node'`,
            action: ({ datum, xKey, yKey }) =>
              console.log(
                `On 'series-node' clicked -`,
                yKey,
                datum[xKey!],
                datum[yKey!],
              ),
          },
          {
            showOn: "legend-item",
            label: `On 'legend-item'`,
            action: ({ itemId }) =>
              console.log(`On 'legend-item' clicked -`, itemId),
          },
          {
            showOn: "cross-line",
            label: `On 'cross-line'`,
            action: ({ crossLineType, value }) =>
              console.log(`On 'cross-line' clicked -`, crossLineType, value),
          },
        ],
      },
    ],
  },
  data: getData(),
  legend: { position: "bottom" },
  zoom: { enabled: true },
  series: [
    {
      type: "line",
      marker: { size: 12 },
      tooltip: { range: "exact" },
      xKey: "year",
      yKey: "USA",
    },
    {
      type: "line",
      marker: { size: 12 },
      tooltip: { range: "exact" },
      xKey: "year",
      yKey: "EU",
    },
    {
      type: "line",
      marker: { size: 12 },
      tooltip: { range: "exact" },
      xKey: "year",
      yKey: "China",
    },
    {
      type: "line",
      marker: { size: 12 },
      tooltip: { range: "exact" },
      xKey: "year",
      yKey: "India",
    },
  ],
  axes: {
    y: { type: "number", title: { text: "GDP (Trillions USD)" } },
    x: {
      type: "category",
      label: { autoRotate: false },
      crossLines: [
        {
          type: "line",
          value: 2020,
          label: {
            text: "China overtakes EU",
            position: "top",
            fontStyle: "italic",
          },
          lineDash: [2, 4],
        },
      ],
    },
  },
};

options.container = document.getElementById("myChart");

const chart = AgCharts.create(options);
```

[Live example: Context Menu Sub-Menus](https://www.ag-grid.com/charts/typescript/context-menu/examples/context-menu-submenus)

```js
{
    contextMenu: {
        items: [
            'download',
            {
                showOn: 'series-area',
                label: 'Zoom Controls',
                items: ['zoom-to-cursor', 'pan-to-cursor', 'reset-zoom'],
            },
            {
                showOn: 'legend-item',
                label: 'Legend Controls',
                items: ['toggle-series-visibility', 'toggle-other-series'],
            },
            'separator',
            {
                label: 'Debug Console',
                items: [
                    {
                        showOn: 'always',
                        label: `On 'always'`,
                        action: () => console.log(`On 'always' clicked.`),
                    },
                    {
                        showOn: 'axis',
                        label: `On 'axis'`,
                        action: (ev) => console.log(`On 'axis' clicked -`, ev.axisId, ev),
                    },
                    {
                        showOn: 'caption',
                        label: `On 'caption'`,
                        action: ({ captionType, text }) =>
                            console.log(`On 'caption' clicked -`, captionType, text),
                    },
                    {
                        showOn: 'series-area',
                        label: `On 'series-area'`,
                        action: () => console.log(`On 'series-area' clicked.`),
                    },
                    {
                        showOn: 'series-node',
                        label: `On 'series-node'`,
                        action: ({ datum, xKey, yKey }) =>
                            console.log(`On 'series-node' clicked -`, yKey, datum[xKey], datum[yKey]),
                    },
                    {
                        showOn: 'legend-item',
                        label: `On 'legend-item'`,
                        action: ({ itemId }) => console.log(`On 'legend-item' clicked -`, itemId),
                    },
                ],
            },
        ],
    },
}
```

In this configuration:

- The built-in Zoom and Legend menu items are placed into Sub-Menus.
- An additional "Debug Console" Sub-Menu is added with custom actions to log events to the console.

## Dynamic Items

The `getItems` callback can be used to implement Dynamic Context Menus. This is typically used to write Menu Items that depend on underlying state or data in some way.

#### Dynamic Context Menu

```ts
import {
  AgCharts,
  BarSeriesModule,
  CategoryAxisModule,
  ContextMenuModule,
  LegendModule,
  ModuleRegistry,
  NumberAxisModule,
} from "ag-charts-enterprise";
import { AgCartesianChartOptions, AgContextMenuItem } from "ag-charts-types";

type DatumType = {
  sector: string;
  nyse: number;
  lse: number;
  tyo: number;
};

function updateVisibility(seriesId: string, visible: boolean) {
  for (const series of options.series!) {
    if (series.id === seriesId) {
      series.visible = visible;
    }
  }
  chart.update(options);
}
ModuleRegistry.registerModules([
  BarSeriesModule,
  NumberAxisModule,
  CategoryAxisModule,
  LegendModule,
  ContextMenuModule,
]);

const options: AgCartesianChartOptions<DatumType> = {
  title: {
    text: "Stock Investment Portfolio by Sector",
  },
  subtitle: {
    text: "Allocation (%) by Market",
  },
  data: [
    { sector: "Industrial", nyse: 28, lse: 22, tyo: 18 },
    { sector: "Financial", nyse: 24, lse: 30, tyo: 20 },
    { sector: "Energy", nyse: 20, lse: 18, tyo: 25 },
    { sector: "Technology", nyse: 15, lse: 10, tyo: 12 },
    { sector: "Healthcare", nyse: 8, lse: 13, tyo: 10 },
    { sector: "Consumer Staples", nyse: 5, lse: 7, tyo: 15 },
  ],
  series: [
    {
      id: "New York Stock Exchange",
      type: "bar",
      xKey: "sector",
      yKey: "nyse",
      yName: "NYSE",
    },
    {
      id: "London Stock Exchange",
      type: "bar",
      xKey: "sector",
      yKey: "lse",
      yName: "LSE",
    },
    {
      id: "Tokyo Stock Exchange",
      type: "bar",
      xKey: "sector",
      yKey: "tyo",
      yName: "TYO",
    },
  ],
  contextMenu: {
    getItems: (params): AgContextMenuItem[] | undefined => {
      if (params.showOn === "series-node") {
        const xName = params.datum[params.xKey!];
        return [
          "defaults",
          "separator",
          // Dynamic Context Menu Item
          {
            type: "action",
            showOn: "series-node",
            label: `Log Datum "${params.seriesId} - ${xName}"`,
            action: () => console.log(params.datum),
          },
        ];
      }
    },
  },
};

options.container = document.getElementById("myChart");

const chart = AgCharts.create(options);
```

[Live example: Dynamic Context Menu](https://www.ag-grid.com/charts/typescript/context-menu/examples/dynamic-context-menu)

```js
{
    contextMenu: {
        getItems: (params) => {
            if (params.showOn === 'series-node') {
                const xName = params.datum[params.xKey];
                return [
                    'defaults',
                    'separator',
                    // Dynamic Context Menu Item
                    {
                        type: 'action',
                        showOn: 'series-node',
                        label: `Log Datum "${params.seriesId} - ${xName}"`,
                        action: () => console.log(params.datum),
                    },
                ];
            }
        },
    },
}
```

In this example:

- The Context Menu on Bar Nodes includes a console `Log Datum` Menu Item, whose text is dynamically derived from the Context Menu Event parameters.
- The `getItems` params includes a `showOn` property which indicates which element type was right-clicked, alongside relevant information about the clicked element.
- If there are multiple elements at the same click point, the `allShowOnParams` array on the `getItems` params provides the parameters for every element, not just the winning one.

### Example: Interactive Customisation

The `getItems` callback can be used to change the state or appearance of a data point or series.

#### Dynamic Context Menu Data

```ts
import {
  AgCharts,
  CategoryAxisModule,
  ContextMenuModule,
  LegendModule,
  LineSeriesModule,
  ModuleRegistry,
  NumberAxisModule,
} from "ag-charts-enterprise";
import {
  AgCartesianChartOptions,
  AgContextMenuGetItemsParams,
  AgContextMenuItem,
  AgLineSeriesOptions,
} from "ag-charts-types";

type DatumType = {
  year: string;
  usa: number | null;
  china: number | null;
  india: number | null;
};

const baseData: DatumType[] = [
  { year: "2015", usa: 2.9, china: 7.0, india: 8.0 },
  { year: "2016", usa: 1.8, china: 6.9, india: 8.3 },
  { year: "2017", usa: 2.0, china: 5.9, india: 6.0 },
  { year: "2018", usa: 3.0, china: 5.7, india: 5.6 },
  { year: "2019", usa: 3.2, china: 6.0, india: 3.4 },
  { year: "2020", usa: -2.2, china: 2.2, india: -5.8 },
  { year: "2021", usa: 5.8, china: 8.4, india: 9.7 },
  { year: "2022", usa: 1.9, china: 3.0, india: 7.0 },
  { year: "2023", usa: 2.5, china: 5.2, india: 8.2 },
  { year: "2024", usa: 2.8, china: 5.0, india: 6.5 },
];
// Default theme palette fills, in palette order, so the initial colours match the default theme.
const seriesMeta: {
  id: string;
  yKey: keyof DatumType;
  yName: string;
  color: string;
}[] = [
  {
    id: "United States",
    yKey: "usa",
    yName: "United States",
    color: "#5090dc",
  },
  { id: "China", yKey: "china", yName: "China", color: "#ffa03a" },
  { id: "India", yKey: "india", yName: "India", color: "#459d55" },
];
const seriesColors: {
  label: string;
  value: string;
}[] = [
  { label: "Blue", value: "#5090dc" },
  { label: "Green", value: "#459d55" },
  { label: "Orange", value: "#ffa03a" },
  { label: "Purple", value: "#9669cb" },
  { label: "Red", value: "#ef5452" },
];
const emphasisedPoints = new Set<string>();
function pointKey(seriesId: string, year: string) {
  return `${seriesId}::${year}`;
}
function colorSwatch(color: string) {
  const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><circle cx="12" cy="12" r="7" fill="${color}"/></svg>`;
  return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
}
function createSeries(): AgLineSeriesOptions<DatumType>[] {
  return seriesMeta.map(
    ({ id, yKey, yName, color }): AgLineSeriesOptions<DatumType> => ({
      id,
      type: "line",
      xKey: "year",
      yKey,
      yName,
      // Assign an explicit colour per series so the palette isn't reassigned by position when a series is removed.
      stroke: color,
      marker: {
        enabled: true,
        fill: color,
        stroke: color,
        itemStyler: ({ seriesId, datum }) =>
          emphasisedPoints.has(pointKey(seriesId, datum.year))
            ? { size: 14 }
            : {},
      },
      label: {
        enabled: true,
        color: { ref: "textColor", mix: 0.6, ontoColor: color },
        fill: { ref: "chartBackgroundColor", mix: 0.8, ontoColor: color },
        border: {
          enabled: true,
          stroke: color,
        },
        padding: 4,
        placement: ["top", "bottom", "left", "right"],
        collision: {
          alwaysShow: true,
        },
        itemStyler: ({ seriesId, datum }) => ({
          enabled: emphasisedPoints.has(pointKey(seriesId, datum.year)),
        }),
      },
    }),
  );
}
let data: DatumType[] = baseData.map((datum) => ({ ...datum }));
let series: AgLineSeriesOptions<DatumType>[] = createSeries();
ModuleRegistry.registerModules([
  LineSeriesModule,
  NumberAxisModule,
  CategoryAxisModule,
  LegendModule,
  ContextMenuModule,
]);

const options: AgCartesianChartOptions<DatumType> = {
  title: {
    text: "Annual GDP Growth by Country",
  },
  subtitle: {
    text: "Right-click a point or legend item to change the point or its series",
  },
  theme: {
    overrides: {
      line: {
        series: {
          highlight: {
            highlightedItem: {
              strokeWidth: 2,
            },
            unhighlightedSeries: {
              opacity: 1,
            },
          },
        },
      },
    },
  },
  data,
  series,
  contextMenu: { getItems: (params) => getItems(params) },
};

options.container = document.getElementById("myChart");

const chart = AgCharts.create(options);

function getItems(
  params: AgContextMenuGetItemsParams<DatumType>,
): AgContextMenuItem<DatumType>[] | undefined {
  if (params.showOn === "legend-item") {
    const { seriesId } = params;
    return [
      "toggle-series-visibility",
      {
        type: "action",
        showOn: "legend-item",
        label: `Remove "${seriesId}"`,
        action: () => removeSeries(seriesId),
      },
      {
        showOn: "legend-item",
        label: `Colour "${seriesId}"`,
        items: seriesColors.map((color) => ({
          type: "action",
          showOn: "legend-item",
          label: color.label,
          iconUrl: colorSwatch(color.value),
          action: () => colorSeries(seriesId, color.value),
        })),
      },
    ];
  }
  const result: AgContextMenuItem<DatumType>[] = [];
  for (const paramsEntry of params.allShowOnParams) {
    if (paramsEntry.showOn === "series-node") {
      const { yKey, xKey, seriesId } = paramsEntry;
      const year = String(paramsEntry.datum[xKey!]);
      const yKeyFormatting: Record<keyof DatumType, string | undefined> = {
        china: "China",
        india: "India",
        usa: "USA",
        year: undefined,
      };
      const formattedDatum = `${yKeyFormatting[yKey!]} ${year}`;
      const isEmphasised = emphasisedPoints.has(pointKey(seriesId, year));
      result.push(
        {
          type: "action",
          showOn: "series-node",
          label: `${isEmphasised ? "Remove Emphasis from" : "Emphasise"} "${formattedDatum}" Point`,
          action: () => toggleEmphasis(seriesId, year),
        },
        {
          type: "action",
          showOn: "series-node",
          label: `Remove "${formattedDatum}" Point`,
          action: () => removeDataPoint(year, yKey!),
        },
      );
    }
  }
  return result;
}

function syncOptions() {
  // Sync both data and series so every update reflects the full current state.
  options.data = data;
  options.series = series;

  chart.update(options);
}

function toggleEmphasis(seriesId: string, year: string) {
  const key = pointKey(seriesId, year);
  if (emphasisedPoints.has(key)) {
    emphasisedPoints.delete(key);
  } else {
    emphasisedPoints.add(key);
  }
  // Reassign the data so the styler and label re-run with the updated emphasis set.
  data = data.map((datum) => ({ ...datum }));
  syncOptions();
}

function removeDataPoint(year: string, yKey: keyof DatumType) {
  data = data.map((datum) =>
    datum.year === year ? { ...datum, [yKey]: null } : datum,
  );
  syncOptions();
}

function colorSeries(seriesId: string, color: string) {
  series = series.map((s) =>
    s.id === seriesId
      ? {
          ...s,
          stroke: color,
          marker: { ...s.marker, fill: color, stroke: color },
        }
      : s,
  );
  syncOptions();
}

function removeSeries(seriesId: string) {
  series = series.filter((s) => s.id !== seriesId);
  syncOptions();
}

function reset() {
  emphasisedPoints.clear();
  data = baseData.map((datum) => ({ ...datum }));
  series = createSeries();
  syncOptions();
}

if (typeof window !== "undefined") {
  // Attach external event handlers to window so they can be called from index.html
  (<any>window).reset = reset;
}
```

[Live example: Dynamic Context Menu Data](https://www.ag-grid.com/charts/typescript/context-menu/examples/dynamic-context-menu-data)

```js
{
    contextMenu: {
        getItems: (params) => {
            if (params.showOn === 'series-node') {
                const { seriesId } = params;
                const year = String(params.datum[params.xKey]);
                const yKey = params.yKey;
                const isEmphasised = emphasisedPoints.has(pointKey(seriesId, year));
                return [
                    {
                        type: 'action',
                        showOn: 'series-node',
                        label: `${isEmphasised ? 'Remove Emphasis from' : 'Emphasise'} "${year}" Point`,
                        action: () => toggleEmphasis(seriesId, year),
                    },
                    {
                        type: 'action',
                        showOn: 'series-node',
                        label: `Remove "${year}" Point`,
                        action: () => removeDataPoint(year, yKey),
                    },
                ];
            }
            if (params.showOn === 'legend-item') {
                const { seriesId } = params;
                return [
                    'toggle-series-visibility',
                    {
                        type: 'action',
                        showOn: 'legend-item',
                        label: `Remove "${seriesId}"`,
                        action: () => removeSeries(seriesId),
                    },
                    // A submenu of colour options
                    {
                        showOn: 'legend-item',
                        label: `Colour "${seriesId}"`,
                        items: seriesColors.map((color) => ({
                            type: 'action',
                            showOn: 'legend-item',
                            label: color.label,
                            action: () => colorSeries(seriesId, color.value),
                        })),
                    },
                ];
            }
        },
    },
}
```

In this example, right-clicking a data point shows custom actions for that point:

- "Emphasise" enlarges the marker and displays the value; triggering it again removes the emphasis.
- "Remove Point" sets the point's value to `null`, leaving a gap in the line.

Right-clicking a legend item shows actions for the whole series:

- The built-in `toggle-series-visibility` item hides or shows the series.
- "Remove Series" drops the series.
- "Colour" is a submenu of preset colours; selecting one recolours the series' line and markers.

## API Reference

#### Context Menu

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| enabled | boolean | true | Whether to show the context menu. |
| items | AgContextMenuItem[] | ['defaults'] | List of menu items (and submenus) for the context menu. |
| getItems | AgContextMenuGetItemsCallback | undefined | Callback to list the menu items (and submenus) for the context menu. Overrides `items` if return-value is defined, otherwise `items` is used as a fallback. |

#### Always

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| type | 'action' \| 'separator' | 'action' | The type of UI element that this item represents. |
| showOn | 'always' | 'always' | Which clicked element this menu item should be shown for. `'always'` menu items are always shown. |
| label (required) | string |  | The text label of this menu item. This property is required for Accessibility compliance. |
| action | Function |  | Function called when clicking on this menu item. |
| enabled | boolean | true | State of this menu-item. |
| items | AgContextMenuItem[] |  | The submenu items. If undefined or empty, then this item will just be treat like a regular menu item. Otherwise, this menu item will have a submenu popup attached to it. |

#### Axis

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| type | 'action' \| 'separator' | 'action' | The type of UI element that this item represents. |
| showOn (required) | 'axis' | 'axis' | Which clicked element this menu item should be shown for. `'axis'` menu items are when clicking any part of an axis. |
| label (required) | string |  | The text label of this menu item. This property is required for Accessibility compliance. |
| action | Function |  | Function called when clicking on this menu item. |
| enabled | boolean | true | State of this menu-item. |
| items | AgContextMenuItem[] |  | The submenu items. If undefined or empty, then this item will just be treat like a regular menu item. Otherwise, this menu item will have a submenu popup attached to it. |

#### Caption

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| type | 'action' \| 'separator' | 'action' | The type of UI element that this item represents. |
| showOn (required) | 'caption' | 'caption' | Which clicked element this menu item should be shown for. `'caption'` menu items are when clicking on a caption (title, subtitle, footnote). |
| label (required) | string |  | The text label of this menu item. This property is required for Accessibility compliance. |
| action | Function |  | Function called when clicking on this menu item. |
| enabled | boolean | true | State of this menu-item. |
| items | AgContextMenuItem[] |  | The submenu items. If undefined or empty, then this item will just be treat like a regular menu item. Otherwise, this menu item will have a submenu popup attached to it. |

#### Cross-Line

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| type | 'action' \| 'separator' | 'action' | The type of UI element that this item represents. |
| showOn (required) | 'cross-line' |  | Which clicked element this menu item should be shown for. `'cross-line'` menu items are shown when right-clicking a cross line's line or fill. |
| label (required) | string |  | The text label of this menu item. This property is required for Accessibility compliance. |
| action | Function |  | Function called when clicking on this menu item. |
| enabled | boolean | true | State of this menu-item. |
| items | AgContextMenuItem[] |  | The submenu items. If undefined or empty, then this item will just be treat like a regular menu item. Otherwise, this menu item will have a submenu popup attached to it. |

#### Series-Area

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| type | 'action' \| 'separator' | 'action' | The type of UI element that this item represents. |
| showOn (required) | 'series-area' |  | Which clicked element this menu item should be shown for. `'series-area'` menu items are shown when clicking anywhere within the series area bounds. |
| label (required) | string |  | The text label of this menu item. This property is required for Accessibility compliance. |
| action | Function |  | Function called when clicking on this menu item. |
| enabled | boolean | true | State of this menu-item. |
| items | AgContextMenuItem[] |  | The submenu items. If undefined or empty, then this item will just be treat like a regular menu item. Otherwise, this menu item will have a submenu popup attached to it. |

#### Series-Node

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| type | 'action' \| 'separator' | 'action' | The type of UI element that this item represents. |
| showOn (required) | 'series-node' |  | Which clicked element this menu item should be shown for. `'series-node'` menu items are shown when clicking when clicking on a datum node. |
| label (required) | string |  | The text label of this menu item. This property is required for Accessibility compliance. |
| action | Function |  | Function called when clicking on this menu item. |
| enabled | boolean | true | State of this menu-item. |
| items | AgContextMenuItem[] |  | The submenu items. If undefined or empty, then this item will just be treat like a regular menu item. Otherwise, this menu item will have a submenu popup attached to it. |

#### Legend

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| type | 'action' \| 'separator' | 'action' | The type of UI element that this item represents. |
| showOn (required) | 'legend-item' |  | Which clicked element this menu item should be shown for. `'legend-item'` menu items are shown when clicking on a legend item. |
| label (required) | string |  | The text label of this menu item. This property is required for Accessibility compliance. |
| action | Function |  | Function called when clicking on this menu item. |
| enabled | boolean | true | State of this menu-item. |
| items | AgContextMenuItem[] |  | The submenu items. If undefined or empty, then this item will just be treat like a regular menu item. Otherwise, this menu item will have a submenu popup attached to it. |
