Using Context Menus

The easiest way to configure a context menu is to provide the getCellContextMenuItems callback function and use it to return the menu items you want to show in the context menu.

const getCellContextMenuItems = ({ column, value }) => {
  if (column.id === 'currency') {
    return [
      {
        label: `Convert ${value}`,
        key: 'currency-convert',
      },
    ];
  }

  if (column.id === 'age') {
    return null;
  }

  return [
    {
      label: `Welcome ${value}`,
      key: 'hi',
    },
  ];
};

<DataSource<Developer> data={data} primaryKey="id">
  <InfiniteTable<Developer>
    getCellContextMenuItems={getCellContextMenuItems}
    columns={columns}
  />
</DataSource>

Using context menus

Right-click any cell in the table to see the custom context menu.

View Mode
Fork
import {
  InfiniteTable,
  DataSource,
  InfiniteTablePropColumns,
} from '@infinite-table/infinite-react';

import * as React from 'react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};

const dataSource = () => {
  return fetch('https://infinite-table.com/.netlify/functions/json-server' + '/developers100')
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const columns: InfiniteTablePropColumns<Developer> = {
  stack: {
    field: 'stack',

    header: 'Stack',
  },
  firstName: {
    field: 'firstName',
    header: 'Name',
  },
  age: {
    field: 'age',
    header: 'Age',
  },
  hobby: {
    field: 'hobby',
    header: 'Hobby',
  },
  preferredLanguage: {
    header: 'Language',
    field: 'preferredLanguage',
  },
};

export default function App() {
  return (
    <>
      <DataSource<Developer> primaryKey="id" data={dataSource}>
        <InfiniteTable<Developer>
          columns={columns}
          getCellContextMenuItems={({ data, column }) => {
            return [
              {
                key: 'hello',
                label: `Hello, ${data?.lastName} ${data?.firstName}`,
                onClick: () => {
                  alert(`Hello, ${data?.lastName} ${data?.firstName}`);
                },
              },
              {
                key: 'col',
                label: `Current clicked column: ${column.header}`,
              },
              {
                key: 'learn',
                label: `Learn`,
                menu: {
                  items: [
                    {
                      key: 'backend',
                      label: 'Backend',
                      onClick: () => {
                        alert(
                          `Learn Backend, ${data?.lastName} ${data?.firstName}`,
                        );
                      },
                    },
                    {
                      key: 'frontend',
                      label: 'Frontend',
                      onClick: () => {
                        alert(
                          `Learn Frontend, ${data?.lastName} ${data?.firstName}`,
                        );
                      },
                    },
                  ],
                },
              },
            ];
          }}
        />
      </DataSource>
    </>

Note

The getCellContextMenuItems function can return one of the following:

  • null - no custom context menu will be displayed, the default context menu will be shown (default event behavior not prevented)
  • [] - an empty array - no custom context menu will be displayed, but the default context menu is not shown - the default event behavior is prevented
  • Array<MenuItem> - an array of menu items to be displayed in the context menu - each MenuItem should have:
    • a unique key property,
    • a label property with the value to display in the menu cell - it's called label because this is the name of the default column in the context menu
    • an optional onClick callback function to handle the click event on the menu item.

In addition, if you need to configure the context menu to have other columns rather than the default column (named label), you can do so by returning an object with columns and items:

const getCellContextMenuItems = () => {
  return {
    columns: [{ name: 'label' }, { name: 'lcon' }],
    items: [
      {
        label: 'Welcome',
        icon: '👋',
        key: 'hi',
        onAction: () => {
          // do something
        },
        hideMenuOnAction: true,
      },
      {
        label: 'Convert',
        icon: '🔁',
        key: 'convert',
      },
    ],
  };
};
Customising columns in the context menu

Right-click any cell in the table to see a context menu with multiple columns (icon, label and description).

View Mode
Fork
import {
  InfiniteTable,
  DataSource,
  InfiniteTablePropColumns,
} from '@infinite-table/infinite-react';

import * as React from 'react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};

const dataSource = () => {
  return fetch('https://infinite-table.com/.netlify/functions/json-server' + '/developers100')
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const columns: InfiniteTablePropColumns<Developer> = {
  stack: {
    field: 'stack',
    header: 'Stack',
  },
  firstName: {
    field: 'firstName',
    header: 'Name',
  },
  age: {
    field: 'age',
    header: 'Age',
  },
  hobby: {
    field: 'hobby',
    header: 'Hobby',
  },
  preferredLanguage: {
    header: 'Language',
    field: 'preferredLanguage',
  },
};

export default function App() {
  return (
    <>
      <DataSource<Developer> primaryKey="id" data={dataSource}>
        <InfiniteTable<Developer>
          columns={columns}
          columnDefaultEditable
          getCellContextMenuItems={({ data, column }) => {
            const columns = [
              { name: 'icon' },
              { name: 'label' },
              { name: 'description' },
            ];
            return {
              columns,
              items: [
                {
                  key: 'hello',
                  icon: '👋',
                  label: `Hello, ${data?.lastName} ${data?.firstName}`,
                  description: `This is a description for ${data?.lastName}`,
                  onClick: () => {
                    alert(`Hello, ${data?.lastName} ${data?.firstName}`);
                  },
                },
                {
                  key: 'col',
                  icon: '🙌',
                  label: `Column: ${column.header}`,
                  description: `Current clicked column: ${column.header}`,
                },
                {
                  key: 'learn',
                  icon: '📚',
                  label: `Learn`,
                  description: `Learn more about ${data?.preferredLanguage}`,
                  menu: {
                    columns,
                    items: [
                      {
                        key: 'backend',
                        label: 'Backend',
                        icon: '👨‍💻',
                        description: 'In the Backend',
                        onClick: () => {
                          alert(
                            `Learn Backend, ${data?.lastName} ${data?.firstName}`,
                          );
                        },
                      },
                      {
                        key: 'frontend',
                        label: 'Frontend',
                        icon: '👨‍💻',
                        description: 'In the Frontend',
                        onClick: () => {
                          alert(
                            `Learn Frontend, ${data?.lastName} ${data?.firstName}`,
                          );
                        },
                      },
                    ],
                  },
                },
              ],
            };
          }}
        />
      </DataSource>
    </>

Context Menus for the Table Body

You might want to show a context menu for the table body, when the user right-clicks outside of any existing cell.

For this, you can use the getContextMenuItems prop.

This function has almost the same signature as getCellContextMenuItems, with the following differences in the object passed as first parameter:

  • all cell-related properties (column, data, value, etc) can be undefined
  • it contains an event property with the original event object for the right-click event
Context menu for outside cells

Right-click outside cells in the table to see a context menu for the table body.

View Mode
Fork
import {
  InfiniteTable,
  DataSource,
  InfiniteTablePropColumns,
} from '@infinite-table/infinite-react';

import * as React from 'react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};

const dataSource = () => {
  return fetch('https://infinite-table.com/.netlify/functions/json-server' + '/developers100')
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

const columns: InfiniteTablePropColumns<Developer> = {
  firstName: {
    field: 'firstName',
    header: 'Name',
    defaultWidth: 120,
  },
  age: {
    field: 'age',
    header: 'Age',
    defaultWidth: 100,
  },
  preferredLanguage: {
    header: 'Language',
    defaultWidth: 120,
    field: 'preferredLanguage',
  },
};

export default function App() {
  return (
    <>
      <DataSource<Developer> primaryKey="id" data={dataSource}>
        <InfiniteTable<Developer>
          columns={columns}
          getContextMenuItems={({ data, column }) => {
            if (!data)
              return [
                {
                  key: 'add',
                  label: 'Add Item',
                  onClick: () => {
                    alert('Add Item');
                  },
                },
              ];

            return [
              {
                key: 'hello',
                label: `Hello, ${data?.lastName} ${data?.firstName}`,
                onClick: () => {
                  alert(`Hello, ${data?.lastName} ${data?.firstName}`);
                },
              },
              {
                key: 'col',
                label: `Current clicked column: ${column?.header}`,
              },
              {
                key: 'learn',
                label: `Learn`,
                menu: {
                  items: [
                    {
                      key: 'backend',
                      label: 'Backend',
                      onClick: () => {
                        alert(
                          `Learn Backend, ${data?.lastName} ${data?.firstName}`,
                        );
                      },
                    },
                    {
                      key: 'frontend',
                      label: 'Frontend',
                      onClick: () => {
                        alert(
                          `Learn Frontend, ${data?.lastName} ${data?.firstName}`,
                        );
                      },
                    },
                  ],
                },
              },
            ];
          }}
        />
      </DataSource>
    </>

Hiding the Context Menu

To hide the context menu when you click a menu item, you can use the hideMenuOnAction property on the menu item.

Alternatively, you can use the object passed in as a parameter to the item.onAction callback function to hide the menu:

const getCellContextMenuItems = () => {
  return {
    items: [
      {
        label: 'Hello',
        key: 'hi',
        onAction: ({ key, hideMenu }) => {
          // do something
          console.log('Hello');

          // hide the menu
          hideMenu();
        },
      },
    ],
  };
};

The third option is to use the hideContextMenu function in the API.