Skip to content

Field Metadata

Field metadata is the information Pivot uses to describe fields before they become row groups, column groups, filters, or measures. Use it to give users clearer labels, keep internal fields out of normal field lists, and make generated totals easier to read.

Source code
TypeScript ts
import { defineCustomElements } from '@revolist/revogrid/loader';
defineCustomElements();

import NumberColumnType from '@revolist/revogrid-column-numeral';
import {
  PivotPlugin,
  filterPivotDimensions,
  type PivotConfig,
  type PivotConfigDimension,
} from '@revolist/revogrid-enterprise';
import { AdvanceFilterPlugin, RowOddPlugin } from '@revolist/revogrid-pro';
import { currentTheme } from '../composables/useRandomData';

const PIVOT_FIELD_METADATA_ROWS = [
  { region: 'North', channel: 'Retail', product: 'Desk', quarter: 'Q1', internalSegment: 'Expansion', revenue: 18400, units: 31 },
  { region: 'North', channel: 'Retail', product: '', quarter: 'Q2', internalSegment: 'Expansion', revenue: 22600, units: 38 },
  { region: null, channel: 'Partner', product: 'Desk', quarter: 'Q1', internalSegment: 'Internal', revenue: 9600, units: 15 },
  { region: 'South', channel: '', product: 'Chair', quarter: 'Q1', internalSegment: 'Strategic', revenue: 14200, units: 24 },
  { region: 'South', channel: 'Direct', product: null, quarter: 'Q2', internalSegment: 'Strategic', revenue: 17300, units: 29 },
  { region: 'West', channel: 'Partner', product: 'Desk', quarter: 'Q1', internalSegment: 'Internal', revenue: 11800, units: 19 },
  { region: 'West', channel: 'Direct', product: 'Chair', quarter: 'Q2', internalSegment: 'Expansion', revenue: 20500, units: 34 },
];

const PIVOT_FIELD_METADATA_DIMENSIONS: PivotConfigDimension[] = [
  { prop: 'region', name: 'Region', description: 'Sales territory with null labels enabled.' },
  { prop: 'channel', name: 'Channel', description: 'Go-to-market channel with empty labels enabled.' },
  { prop: 'product', name: 'Product', description: 'Product family with empty and null members.' },
  { prop: 'quarter', name: 'Quarter', description: 'Column quarter.' },
  { prop: 'internalSegment', name: 'Internal Segment', description: 'Hidden field revealed by show hidden.', hidden: true },
  { prop: 'revenue', name: 'Revenue', description: 'Currency measure.' },
  { prop: 'units', name: 'Units', description: 'Unit measure.' },
];

function createPivotConfig(): PivotConfig {
  return {
    dimensions: PIVOT_FIELD_METADATA_DIMENSIONS,
    rows: ['region', 'channel', 'product'],
    columns: ['quarter', 'internalSegment'],
    filters: ['internalSegment'],
    values: [
      { prop: 'revenue', aggregator: 'sum', label: 'Revenue $' },
      { prop: 'units', aggregator: 'sum', name: 'Units Sold' },
    ],
    hasConfigurator: true,
    fieldPanel: {
      visible: true,
      allowFieldDragging: true,
      allowFieldRemoving: true,
      showDataFields: true,
      showRowFields: true,
      showColumnFields: true,
      showFilterFields: true,
    },
    groupLabels: {
      empty: '(Empty)',
      null: '(No value)',
    },
    totals: {
      grandTotal: true,
      subtotals: true,
      disabledSubtotals: {
        rows: { fields: ['channel'] },
        columns: { levels: [0] },
      },
      subtotalLabel: 'Subtotal',
      grandTotalLabel: 'Grand Total',
    },
  };
}

function renderFieldList(target: HTMLElement, search: string, showHidden: boolean) {
  const dimensions = filterPivotDimensions(PIVOT_FIELD_METADATA_DIMENSIONS, search, { showHidden });
  target.replaceChildren(
    ...dimensions.map((dimension) => {
      const item = document.createElement('li');
      const label = document.createElement('span');
      item.className = 'flex items-center justify-between gap-2 border-b border-slate-200 py-1 text-xs last:border-b-0';
      label.textContent = dimension.name ?? String(dimension.prop);
      item.append(label);

      if (dimension.hidden) {
        const hiddenBadge = document.createElement('span');
        hiddenBadge.className = 'rounded bg-slate-200 px-1.5 py-0.5 text-[10px] uppercase';
        hiddenBadge.textContent = 'hidden';
        item.append(hiddenBadge);
      }

      return item;
    }),
  );
}

export function load(parentSelector: string, rows: any[] = PIVOT_FIELD_METADATA_ROWS) {
  const { isDark } = currentTheme();
  const parent = document.querySelector(parentSelector);
  const data = Array.isArray(rows) && rows.length > 0 ? rows : PIVOT_FIELD_METADATA_ROWS;
  const wrapper = document.createElement('div');
  wrapper.className = 'flex h-full min-h-[620px] w-full gap-3';

  const metadataPanel = document.createElement('aside');
  metadataPanel.className = 'flex w-64 shrink-0 flex-col gap-2 rounded border border-slate-200 p-3 text-sm';
  metadataPanel.innerHTML = `
    <label class="text-xs font-semibold uppercase">Field metadata</label>
    <input class="pivot-field-search rounded border border-slate-300 px-2 py-1 text-sm" type="search" placeholder="Search fields" />
    <label class="flex items-center gap-2 text-xs">
      <input type="checkbox" />
      Show hidden fields
    </label>
    <ul class="m-0 list-none p-0"></ul>
  `;

  const searchInput = metadataPanel.querySelector('input[type="search"]') as HTMLInputElement;
  const showHiddenInput = metadataPanel.querySelector('input[type="checkbox"]') as HTMLInputElement;
  const fieldList = metadataPanel.querySelector('ul') as HTMLUListElement;
  const refreshList = () => renderFieldList(fieldList, searchInput.value, showHiddenInput.checked);
  searchInput.addEventListener('input', refreshList);
  showHiddenInput.addEventListener('change', refreshList);
  refreshList();

  const grid = document.createElement('revo-grid') as any;
  grid.className = 'grow h-full w-full cell-border';
  grid.range = true;
  grid.resize = true;
  grid.filter = true;
  grid.colSize = 170;
  grid.readonly = true;
  grid.hideAttribution = true;
  grid.theme = isDark() ? 'darkCompact' : 'compact';
  grid.columnTypes = {
    currency: new NumberColumnType('$0,0.00'),
    integer: new NumberColumnType('0,0'),
  };
  grid.plugins = [PivotPlugin, AdvanceFilterPlugin, RowOddPlugin];
  grid.columns = [];

  Object.assign(grid, {
    pivot: createPivotConfig(),
  });

  wrapper.append(metadataPanel, grid);
  parent?.appendChild(wrapper);
  grid.source = data;

  return () => {
    wrapper.remove();
  };
}
Vue vue
<template>
  <div class="flex h-full min-h-[620px] w-full gap-3">
    <aside class="flex w-64 shrink-0 flex-col gap-2 rounded border border-slate-200 p-3 text-sm">
      <label class="text-xs font-semibold uppercase">Field metadata</label>
      <input
        v-model="search"
        class="pivot-field-search rounded border border-slate-300 px-2 py-1 text-sm"
        type="search"
        placeholder="Search fields"
      />
      <label class="flex items-center gap-2 text-xs">
        <input v-model="showHidden" type="checkbox" />
        Show hidden fields
      </label>
      <ul class="m-0 list-none p-0">
        <li
          v-for="dimension in filteredDimensions"
          :key="String(dimension.prop)"
          class="flex items-center justify-between gap-2 border-b border-slate-200 py-1 text-xs last:border-b-0"
        >
          <span>{{ dimension.name ?? dimension.prop }}</span>
          <span v-if="dimension.hidden" class="rounded bg-slate-200 px-1.5 py-0.5 text-[10px] uppercase">hidden</span>
        </li>
      </ul>
    </aside>
    <RevoGrid
      class="grow h-full cell-border"
      hide-attribution
      range
      resize
      filter
      :colSize="170"
      :source="gridRows"
      :pivot.prop="pivot"
      :theme="isDark ? 'darkCompact' : 'compact'"
      :plugins="plugins"
      :column-types="columnTypes"
      readonly
    />
  </div>
</template>

<script setup lang="ts">
import { computed, ref } from 'vue';
import NumberColumnType from '@revolist/revogrid-column-numeral';
import RevoGrid, { type GridPlugin } from '@revolist/vue3-datagrid';
import {
  PivotPlugin,
  filterPivotDimensions,
  type PivotConfig,
  type PivotConfigDimension,
} from '@revolist/revogrid-enterprise';
import { AdvanceFilterPlugin, RowOddPlugin } from '@revolist/revogrid-pro';
import { currentThemeVue } from '../composables/useRandomData';

const PIVOT_FIELD_METADATA_ROWS = [
  { region: 'North', channel: 'Retail', product: 'Desk', quarter: 'Q1', internalSegment: 'Expansion', revenue: 18400, units: 31 },
  { region: 'North', channel: 'Retail', product: '', quarter: 'Q2', internalSegment: 'Expansion', revenue: 22600, units: 38 },
  { region: null, channel: 'Partner', product: 'Desk', quarter: 'Q1', internalSegment: 'Internal', revenue: 9600, units: 15 },
  { region: 'South', channel: '', product: 'Chair', quarter: 'Q1', internalSegment: 'Strategic', revenue: 14200, units: 24 },
  { region: 'South', channel: 'Direct', product: null, quarter: 'Q2', internalSegment: 'Strategic', revenue: 17300, units: 29 },
  { region: 'West', channel: 'Partner', product: 'Desk', quarter: 'Q1', internalSegment: 'Internal', revenue: 11800, units: 19 },
  { region: 'West', channel: 'Direct', product: 'Chair', quarter: 'Q2', internalSegment: 'Expansion', revenue: 20500, units: 34 },
];

const PIVOT_FIELD_METADATA_DIMENSIONS: PivotConfigDimension[] = [
  { prop: 'region', name: 'Region', description: 'Sales territory with null labels enabled.' },
  { prop: 'channel', name: 'Channel', description: 'Go-to-market channel with empty labels enabled.' },
  { prop: 'product', name: 'Product', description: 'Product family with empty and null members.' },
  { prop: 'quarter', name: 'Quarter', description: 'Column quarter.' },
  { prop: 'internalSegment', name: 'Internal Segment', description: 'Hidden field revealed by show hidden.', hidden: true },
  { prop: 'revenue', name: 'Revenue', description: 'Currency measure.' },
  { prop: 'units', name: 'Units', description: 'Unit measure.' },
];

const props = defineProps<{
  rows?: any[];
}>();

const { isDark } = currentThemeVue();
const search = ref('');
const showHidden = ref(false);
const gridRows = computed(() => (Array.isArray(props.rows) && props.rows.length > 0 ? props.rows : PIVOT_FIELD_METADATA_ROWS));
const filteredDimensions = computed(() =>
  filterPivotDimensions(PIVOT_FIELD_METADATA_DIMENSIONS, search.value, { showHidden: showHidden.value }),
);

const columnTypes = ref({
  currency: new NumberColumnType('$0,0.00'),
  integer: new NumberColumnType('0,0'),
});

const plugins: GridPlugin[] = [PivotPlugin, AdvanceFilterPlugin, RowOddPlugin];
const pivot = computed((): PivotConfig => ({
  dimensions: PIVOT_FIELD_METADATA_DIMENSIONS,
  rows: ['region', 'channel', 'product'],
  columns: ['quarter', 'internalSegment'],
  filters: ['internalSegment'],
  values: [
    { prop: 'revenue', aggregator: 'sum', label: 'Revenue $' },
    { prop: 'units', aggregator: 'sum', name: 'Units Sold' },
  ],
  hasConfigurator: true,
  fieldPanel: {
    visible: true,
    allowFieldDragging: true,
    allowFieldRemoving: true,
    showDataFields: true,
    showRowFields: true,
    showColumnFields: true,
    showFilterFields: true,
  },
  groupLabels: {
    empty: '(Empty)',
    null: '(No value)',
  },
  totals: {
    grandTotal: true,
    subtotals: true,
    disabledSubtotals: {
      rows: { fields: ['channel'] },
      columns: { levels: [0] },
    },
    subtotalLabel: 'Subtotal',
    grandTotalLabel: 'Grand Total',
  },
}));
</script>
React tsx
import { useMemo, useState } from 'react';
import { RevoGrid } from '@revolist/react-datagrid';
import NumberColumnType from '@revolist/revogrid-column-numeral';
import {
  PivotPlugin,
  filterPivotDimensions,
  type PivotConfig,
  type PivotConfigDimension,
} from '@revolist/revogrid-enterprise';
import { AdvanceFilterPlugin, RowOddPlugin } from '@revolist/revogrid-pro';
import { currentTheme } from '../composables/useRandomData';

const PIVOT_FIELD_METADATA_ROWS = [
  { region: 'North', channel: 'Retail', product: 'Desk', quarter: 'Q1', internalSegment: 'Expansion', revenue: 18400, units: 31 },
  { region: 'North', channel: 'Retail', product: '', quarter: 'Q2', internalSegment: 'Expansion', revenue: 22600, units: 38 },
  { region: null, channel: 'Partner', product: 'Desk', quarter: 'Q1', internalSegment: 'Internal', revenue: 9600, units: 15 },
  { region: 'South', channel: '', product: 'Chair', quarter: 'Q1', internalSegment: 'Strategic', revenue: 14200, units: 24 },
  { region: 'South', channel: 'Direct', product: null, quarter: 'Q2', internalSegment: 'Strategic', revenue: 17300, units: 29 },
  { region: 'West', channel: 'Partner', product: 'Desk', quarter: 'Q1', internalSegment: 'Internal', revenue: 11800, units: 19 },
  { region: 'West', channel: 'Direct', product: 'Chair', quarter: 'Q2', internalSegment: 'Expansion', revenue: 20500, units: 34 },
];

const PIVOT_FIELD_METADATA_DIMENSIONS: PivotConfigDimension[] = [
  { prop: 'region', name: 'Region', description: 'Sales territory with null labels enabled.' },
  { prop: 'channel', name: 'Channel', description: 'Go-to-market channel with empty labels enabled.' },
  { prop: 'product', name: 'Product', description: 'Product family with empty and null members.' },
  { prop: 'quarter', name: 'Quarter', description: 'Column quarter.' },
  { prop: 'internalSegment', name: 'Internal Segment', description: 'Hidden field revealed by show hidden.', hidden: true },
  { prop: 'revenue', name: 'Revenue', description: 'Currency measure.' },
  { prop: 'units', name: 'Units', description: 'Unit measure.' },
];

interface PivotFieldMetadataProps {
  rows?: any[];
}

function createPivotConfig(): PivotConfig {
  return {
    dimensions: PIVOT_FIELD_METADATA_DIMENSIONS,
    rows: ['region', 'channel', 'product'],
    columns: ['quarter', 'internalSegment'],
    filters: ['internalSegment'],
    values: [
      { prop: 'revenue', aggregator: 'sum', label: 'Revenue $' },
      { prop: 'units', aggregator: 'sum', name: 'Units Sold' },
    ],
    hasConfigurator: true,
    fieldPanel: {
      visible: true,
      allowFieldDragging: true,
      allowFieldRemoving: true,
      showDataFields: true,
      showRowFields: true,
      showColumnFields: true,
      showFilterFields: true,
    },
    groupLabels: {
      empty: '(Empty)',
      null: '(No value)',
    },
    totals: {
      grandTotal: true,
      subtotals: true,
      disabledSubtotals: {
        rows: { fields: ['channel'] },
        columns: { levels: [0] },
      },
      subtotalLabel: 'Subtotal',
      grandTotalLabel: 'Grand Total',
    },
  };
}

function PivotFieldMetadata({ rows }: PivotFieldMetadataProps) {
  const { isDark } = currentTheme();
  const [search, setSearch] = useState('');
  const [showHidden, setShowHidden] = useState(false);

  const data = useMemo(() => (Array.isArray(rows) && rows.length > 0 ? rows : PIVOT_FIELD_METADATA_ROWS), [rows]);
  const filteredDimensions = useMemo(
    () => filterPivotDimensions(PIVOT_FIELD_METADATA_DIMENSIONS, search, { showHidden }),
    [search, showHidden],
  );
  const columnTypes = useMemo(
    () => ({
      currency: new NumberColumnType('$0,0.00'),
      integer: new NumberColumnType('0,0'),
    }),
    [],
  );
  const plugins = useMemo(() => [PivotPlugin, AdvanceFilterPlugin, RowOddPlugin], []);
  const pivot = useMemo(() => createPivotConfig(), []);

  return (
    <div className="flex h-full min-h-[620px] w-full gap-3">
      <aside className="flex w-64 shrink-0 flex-col gap-2 rounded border border-slate-200 p-3 text-sm">
        <label className="text-xs font-semibold uppercase">Field metadata</label>
        <input
          className="pivot-field-search rounded border border-slate-300 px-2 py-1 text-sm"
          type="search"
          placeholder="Search fields"
          value={search}
          onChange={(event) => setSearch(event.currentTarget.value)}
        />
        <label className="flex items-center gap-2 text-xs">
          <input
            type="checkbox"
            checked={showHidden}
            onChange={(event) => setShowHidden(event.currentTarget.checked)}
          />
          Show hidden fields
        </label>
        <ul className="m-0 list-none p-0">
          {filteredDimensions.map((dimension) => (
            <li
              className="flex items-center justify-between gap-2 border-b border-slate-200 py-1 text-xs last:border-b-0"
              key={String(dimension.prop)}
            >
              <span>{dimension.name ?? String(dimension.prop)}</span>
              {dimension.hidden && <span className="rounded bg-slate-200 px-1.5 py-0.5 text-[10px] uppercase">hidden</span>}
            </li>
          ))}
        </ul>
      </aside>
      <RevoGrid
        className="grow h-full cell-border"
        hideAttribution
        range
        resize
        filter
        colSize={170}
        source={data}
        columns={[]}
        pivot={pivot}
        theme={isDark() ? 'darkCompact' : 'compact'}
        plugins={plugins}
        columnTypes={columnTypes}
        readonly
      />
    </div>
  );
}

export default PivotFieldMetadata;
Angular ts
import { CommonModule } from '@angular/common';
import { Component, Input, NO_ERRORS_SCHEMA, ViewEncapsulation } from '@angular/core';
import { RevoGrid } from '@revolist/angular-datagrid';
import NumberColumnType from '@revolist/revogrid-column-numeral';
import {
  PivotPlugin,
  filterPivotDimensions,
  type PivotConfig,
  type PivotConfigDimension,
} from '@revolist/revogrid-enterprise';
import { AdvanceFilterPlugin, RowOddPlugin } from '@revolist/revogrid-pro';
import { currentTheme } from '../composables/useRandomData';

const PIVOT_FIELD_METADATA_ROWS = [
  { region: 'North', channel: 'Retail', product: 'Desk', quarter: 'Q1', internalSegment: 'Expansion', revenue: 18400, units: 31 },
  { region: 'North', channel: 'Retail', product: '', quarter: 'Q2', internalSegment: 'Expansion', revenue: 22600, units: 38 },
  { region: null, channel: 'Partner', product: 'Desk', quarter: 'Q1', internalSegment: 'Internal', revenue: 9600, units: 15 },
  { region: 'South', channel: '', product: 'Chair', quarter: 'Q1', internalSegment: 'Strategic', revenue: 14200, units: 24 },
  { region: 'South', channel: 'Direct', product: null, quarter: 'Q2', internalSegment: 'Strategic', revenue: 17300, units: 29 },
  { region: 'West', channel: 'Partner', product: 'Desk', quarter: 'Q1', internalSegment: 'Internal', revenue: 11800, units: 19 },
  { region: 'West', channel: 'Direct', product: 'Chair', quarter: 'Q2', internalSegment: 'Expansion', revenue: 20500, units: 34 },
];

const PIVOT_FIELD_METADATA_DIMENSIONS: PivotConfigDimension[] = [
  { prop: 'region', name: 'Region', description: 'Sales territory with null labels enabled.' },
  { prop: 'channel', name: 'Channel', description: 'Go-to-market channel with empty labels enabled.' },
  { prop: 'product', name: 'Product', description: 'Product family with empty and null members.' },
  { prop: 'quarter', name: 'Quarter', description: 'Column quarter.' },
  { prop: 'internalSegment', name: 'Internal Segment', description: 'Hidden field revealed by show hidden.', hidden: true },
  { prop: 'revenue', name: 'Revenue', description: 'Currency measure.' },
  { prop: 'units', name: 'Units', description: 'Unit measure.' },
];

@Component({
  selector: 'pivot-field-metadata-grid',
  standalone: true,
  imports: [CommonModule, RevoGrid],
  schemas: [NO_ERRORS_SCHEMA],
  template: `
    <div class="flex h-full min-h-[620px] w-full gap-3">
      <aside class="flex w-64 shrink-0 flex-col gap-2 rounded border border-slate-200 p-3 text-sm">
        <label class="text-xs font-semibold uppercase">Field metadata</label>
        <input
          class="pivot-field-search rounded border border-slate-300 px-2 py-1 text-sm"
          type="search"
          placeholder="Search fields"
          [value]="search"
          (input)="setSearch($event)"
        />
        <label class="flex items-center gap-2 text-xs">
          <input
            type="checkbox"
            [checked]="showHidden"
            (change)="setShowHidden($event)"
          />
          Show hidden fields
        </label>
        <ul class="m-0 list-none p-0">
          <li
            *ngFor="let dimension of filteredDimensions"
            class="flex items-center justify-between gap-2 border-b border-slate-200 py-1 text-xs last:border-b-0"
          >
            <span>{{ dimension.name || dimension.prop }}</span>
            <span
              *ngIf="dimension.hidden"
              class="rounded bg-slate-200 px-1.5 py-0.5 text-[10px] uppercase"
            >hidden</span>
          </li>
        </ul>
      </aside>
      <revo-grid
        class="grow h-full cell-border"
        [hideAttribution]="true"
        [range]="true"
        [resize]="true"
        [filter]="true"
        [colSize]="170"
        [source]="gridRows"
        [pivot]="pivot"
        [theme]="theme"
        [plugins]="plugins"
        [columnTypes]="columnTypes"
        [readonly]="true"
      ></revo-grid>
    </div>
  `,
  encapsulation: ViewEncapsulation.None,
})
export class PivotFieldMetadataGridComponent {
  @Input() rows?: any[];

  search = '';
  showHidden = false;
  theme = currentTheme().isDark() ? 'darkCompact' : 'compact';

  columnTypes = {
    currency: new NumberColumnType('$0,0.00'),
    integer: new NumberColumnType('0,0'),
  };

  plugins = [PivotPlugin, AdvanceFilterPlugin, RowOddPlugin];

  pivot: PivotConfig = {
    dimensions: PIVOT_FIELD_METADATA_DIMENSIONS,
    rows: ['region', 'channel', 'product'],
    columns: ['quarter', 'internalSegment'],
    filters: ['internalSegment'],
    values: [
      { prop: 'revenue', aggregator: 'sum', label: 'Revenue $' },
      { prop: 'units', aggregator: 'sum', name: 'Units Sold' },
    ],
    hasConfigurator: true,
    fieldPanel: {
      visible: true,
      allowFieldDragging: true,
      allowFieldRemoving: true,
      showDataFields: true,
      showRowFields: true,
      showColumnFields: true,
      showFilterFields: true,
    },
    groupLabels: {
      empty: '(Empty)',
      null: '(No value)',
    },
    totals: {
      grandTotal: true,
      subtotals: true,
      disabledSubtotals: {
        rows: { fields: ['channel'] },
        columns: { levels: [0] },
      },
      subtotalLabel: 'Subtotal',
      grandTotalLabel: 'Grand Total',
    },
  };

  get gridRows() {
    return Array.isArray(this.rows) && this.rows.length > 0 ? this.rows : PIVOT_FIELD_METADATA_ROWS;
  }

  get filteredDimensions() {
    return filterPivotDimensions(PIVOT_FIELD_METADATA_DIMENSIONS, this.search, { showHidden: this.showHidden });
  }

  setSearch(event: Event) {
    this.search = (event.target as HTMLInputElement).value;
  }

  setShowHidden(event: Event) {
    this.showHidden = (event.target as HTMLInputElement).checked;
  }
}

Mark a dimension as hidden when it should still be available to the Pivot config but should not appear in normal field lists.

const dimensions = [
{ prop: 'region', name: 'Region' },
{ prop: 'quarter', name: 'Quarter' },
{
prop: 'internalSegment',
name: 'Internal Segment',
description: 'Internal reporting segment.',
hidden: true,
},
];
const pivot = {
dimensions,
rows: ['region'],
columns: ['quarter', 'internalSegment'],
filters: ['internalSegment'],
values: [{ prop: 'revenue', aggregator: 'sum' }],
};

Hidden does not remove the field from the Pivot engine. In this example, internalSegment can still be used in columns and filters; it is only hidden from helper-driven field lists unless you explicitly show hidden fields.

Use filterPivotDimensions for searchable field lists. Hidden dimensions are omitted by default. Pass { showHidden: true } when you want an admin or advanced-user mode that reveals them.

import { filterPivotDimensions } from '@revolist/revogrid-enterprise';
const visibleFields = filterPivotDimensions(dimensions, searchText);
const allFields = filterPivotDimensions(dimensions, searchText, {
showHidden: true,
});

The helper searches prop, name, and description, so descriptions are useful for discovery even when the visible label is short.

Real data often contains empty strings, null, or undefined. Pivot can replace those values with user-facing labels in row and column groups.

const pivot = {
rows: ['region', 'channel'],
columns: ['quarter'],
values: [{ prop: 'revenue', aggregator: 'sum' }],
groupLabels: {
empty: '(Empty)',
null: '(No value)',
},
};

groupLabels.empty is used for empty strings (''). groupLabels.null is used for null and undefined. Without these options, the current implementation displays empty labels for those group members.

Each value can have its own display label. Use label for the preferred generated measure name. Use name as a fallback when label is not set.

const pivot = {
values: [
{ prop: 'revenue', aggregator: 'sum', label: 'Revenue $' },
{ prop: 'units', aggregator: 'sum', name: 'Units Sold' },
],
};

Aliases are useful when the source field name is technical, abbreviated, or shared by multiple reports.

Global subtotals can stay enabled while specific subtotal rows or subtotal columns are skipped. Target subtotals by axis field or by zero-based axis level.

const pivot = {
rows: ['region', 'channel', 'product'],
columns: ['quarter', 'internalSegment'],
values: [{ prop: 'revenue', aggregator: 'sum' }],
totals: {
grandTotal: true,
subtotals: true,
disabledSubtotals: {
rows: {
fields: ['channel'],
},
columns: {
levels: [0],
},
},
},
};

In this example, Pivot keeps subtotal generation enabled overall, skips row subtotals for the channel field, and skips column subtotals at level 0.

const pivot = {
dimensions: [
{ prop: 'region', name: 'Region', description: 'Sales territory.' },
{ prop: 'channel', name: 'Channel', description: 'Go-to-market channel.' },
{ prop: 'product', name: 'Product' },
{ prop: 'quarter', name: 'Quarter' },
{ prop: 'internalSegment', name: 'Internal Segment', hidden: true },
{ prop: 'revenue', name: 'Revenue' },
{ prop: 'units', name: 'Units' },
],
rows: ['region', 'channel', 'product'],
columns: ['quarter', 'internalSegment'],
filters: ['internalSegment'],
values: [
{ prop: 'revenue', aggregator: 'sum', label: 'Revenue $' },
{ prop: 'units', aggregator: 'sum', name: 'Units Sold' },
],
groupLabels: {
empty: '(Empty)',
null: '(No value)',
},
totals: {
grandTotal: true,
subtotals: true,
disabledSubtotals: {
rows: { fields: ['channel'] },
columns: { levels: [0] },
},
},
};
  • Expecting hidden: true to prevent a field from being used in rows, columns, filters, or values. It only affects field-list helpers.
  • Passing { showHidden: true } to the Pivot config. It belongs to filterPivotDimensions, not pivot.
  • Using only groupLabels.empty when the source data contains null or undefined.
  • Disabling totals.subtotals and then expecting disabledSubtotals to do anything. Selective disabling only matters when subtotals: true.