Prefilter and the node context menu
Two Enterprise surfaces that both attach to what is already on screen: a predicate applied to rows before anything is aggregated, and a right-click menu on a header node.
The prefilter
It runs earlier than everything else — before each field's own filterValues,
before grouping, before aggregation. So it changes the totals, not just what
you can see.
import { EnterprisePivotGrid } from '@kanunilabs/pivotgrid-react-enterprise';
<EnterprisePivotGrid
licenseKey={key}
data={data}
initialFields={fields}
layout={layout}
onLayoutChange={setLayout}
/>
EnterprisePivotGrid supplies the builder; the footer's filter button opens
it. The applied predicate lives at layout.prefilter.
The AST
type PivotPrefilter = FilterGroup;
interface FilterGroup {
logicalOperator: 'and' | 'or';
conditions: (FilterCondition | FilterGroup)[];
}
interface FilterCondition {
fieldId: string; // PivotField.id — not dataField
operator: FilterOperator;
value: PivotValue;
}
Set one from code without the dialog:
setLayout((l) => ({
...l,
prefilter: {
logicalOperator: 'and',
conditions: [
{ fieldId: 'region', operator: '=', value: 'EMEA' },
{ fieldId: 'year', operator: '>=', value: 2024 },
],
},
}));
Do not pass an inline layout={{ … }} literal. A fresh object every render
overwrites whatever the dialog just applied. Hold it in state, as above.
Note this is not the DataGrid filter AST. PivotGrid uses
logicalOperator / conditions / fieldId; DataGrid uses logic / nodes /
columnId. They are not interchangeable.
The node context menu
Right-click a row or column header that has children:
| Item | What it does |
|---|---|
| Expand / Collapse | toggles that node |
| Expand All | expands one more level, across the whole tree |
| Collapse All | collapses one level, across the whole tree |
What this does not do
The prefilter is evaluated twice, by two different implementations
This is the one worth knowing before you rely on it. The predicate is interpreted by two separate switch statements:
engine/FlatPivotEngine.ts— drives everything the grid displays. On a string (dictionary-encoded) column it compares with string semantics:strVal > targetStr.engine/filter.ts— drives drill-down and export. It coerces first:Number(val) > targetNum.
So > on a column of numeric-looking strings can order values differently in
the grid than in the file you export from it. The two were written
independently and nothing keeps them in step.
Two more consequences of the same split:
isnullon a measure treats zero as blank. The numeric branch returnsisNaN(numVal) || numVal === 0, so a genuine0is reported as missing.- Text operators on a measure match everything. The numeric branch has no
case for
contains/startswith/endswithand falls through todefault: return true.
The rest
- A condition on a field that is in neither the Row nor the Column area is silently ignored — while the dialog's dropdown offers every field, with nothing to distinguish them.
- The dialog offers 12 of the 14 operators.
inandnotinare implemented in both evaluators and absent from the UI. Worse: loading an existing prefilter that uses one corrupts it, because theselecthas no matching option and the next Apply writes whatever the browser selected instead. - Every value is a string. One text input for all types — no date picker,
no numeric input, no
between. - An empty condition blanks the grid. Apply only checks whether the root
group is empty; a freshly added condition defaults to
{ operator: '=', value: '' }and matches nothing. - "Expand All" expands exactly one level, and it ignores the node you right-clicked. It walks the already-materialised tree, which only contains children of already-expanded nodes, and it is seeded from the root — so right-clicking EMEA also expands APAC and every other branch at that depth.
- No keyboard path to the menu at all. The grid's key handler covers
arrows, Home/End, PageUp/PageDown, Escape, Space and Enter — there is no
ContextMenu-key binding. The dialog has no Escape handler, no focus trap and
no
role="dialog". - Parts are hardcoded English despite the dictionary being one hook away:
all 12 operator labels, "Add Group", and the condition editor generally — that
component never calls
usePivotConfig, so it has no translator at all. The dialog's own title reads "Filter Area", which is the caption of a different feature. - You cannot add your own menu items. There is no
itemsprop, and the only escape hatch replaces the whole component. - Neither feature is licence-gated. Without a key they run watermarked.
If any of these is load-bearing for you, say so — several are small, and knowing which one blocks real work is how they get prioritised.