Custom DataGridControls#

SummaryGrid and PalmyraGrid accept a DataGridControls prop — a React component that renders the toolbar above the grid. Build your own to add quick search, filter toggles, export buttons, and navigation controls, then reuse it across every grid page.

The contract#

interface DataGridPluginOptions {
  queryRef:    RefObject<IPageQueryable>;
  columns:     ColumnDefinition[];
  // plus anything your getPluginOptions() returns
}

The grid passes queryRef so your toolbar can call refresh(), setFilter(...), setQuickSearch(...), and export(...).

A reusable toolbar#

// src/common/components/gridControl/AppDataGridControls.tsx
import { Button, Group, TextInput } from '@mantine/core';
import { IconPlus, IconFilter, IconDownload, IconArrowLeft } from '@tabler/icons-react';
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { FilterForm } from '@palmyralabs/rt-forms-mantine';

export default function AppDataGridControls(o: any) {
  const navigate = useNavigate();
  const [search, setSearch]       = useState('');
  const [filterOpen, setFilterOpen] = useState(false);

  const opts = o.getPluginOptions?.() ?? {};

  const handleSearch = (value: string) => {
    setSearch(value);
    o.queryRef?.current?.setQuickSearch(value);
  };

  const handleExport = () => {
    o.queryRef?.current?.export({ format: 'csv' });
  };

  return (
    <>
      <Group justify="space-between" mb="sm">
        <Group>
          {opts.backOption !== false && (
            <Button variant="subtle" leftSection={<IconArrowLeft size={16} />}
                    onClick={() => navigate(-1)}>Back</Button>
          )}
          <TextInput placeholder="Quick search" size="sm"
                     value={search} onChange={e => handleSearch(e.currentTarget.value)} />
        </Group>
        <Group>
          <Button variant="light" leftSection={<IconFilter size={16} />}
                  onClick={() => setFilterOpen(!filterOpen)}>Filter</Button>
          {opts.export?.visible !== false && (
            <Button variant="light" leftSection={<IconDownload size={16} />}
                    onClick={handleExport}>Export CSV</Button>
          )}
          {opts.onNewClick && (
            <Button leftSection={<IconPlus size={16} />}
                    onClick={opts.onNewClick}>{opts.addText ?? 'Add'}</Button>
          )}
        </Group>
      </Group>
      {filterOpen && (
        <FilterForm
          queryRef={o.queryRef}
          columns={o.columns}
          column={2}
          onClose={() => setFilterOpen(false)}
        />
      )}
    </>
  );
}

Using it on a grid page#

<SummaryGrid
  title="Projects"
  columns={projectColumns}
  options={{ endPoint: '/project' }}
  pageSize={[15, 30, 45]}
  gridRef={gridRef}
  DataGridControls={AppDataGridControls}
  getPluginOptions={() => ({
    addText:    'New Project',
    onNewClick: open,
    export:     { visible: true },
    backOption: false,
  })}
/>

Variant: popup grid controls#

For embedded grids (inside a view page, alongside other content), a slimmer toolbar without back/add:

export default function PopupGridControls(o: any) {
  return (
    <Group justify="flex-end" mb="xs">
      <TextInput size="xs" placeholder="Search"
                 onChange={e => o.queryRef?.current?.setQuickSearch(e.currentTarget.value)} />
    </Group>
  );
}

Use it on a sibling grid:

<PalmyraGrid
  columns={relatedColumns}
  endPoint={`/project/${id}/invoices`}
  DataGridControls={PopupGridControls}
  pageSize={[10, 20]}
/>

Key patterns#

  • One toolbar component, many pages. getPluginOptions() per page controls which buttons appear — the toolbar reads the options, not the other way around.
  • queryRef is the escape hatch. Anything the grid can do, the toolbar can trigger: refresh(), setFilter(...), setQuickSearch(...), export(...).
  • FilterForm composition. Drop it inline when the filter toggle is on; it auto-generates fields from the grid’s columns.

See also: FilterForm plugin, SummaryGrid.