A column definition should read like a description
Open the column definitions of a grid that has been in production for two years
and you will find something nobody would have written on purpose. Forty
properties per column, flat, in the order they were added, with
headerClass next to valueFormatter next to suppressSizeToFit. It is not
badly written. It grew, and flat lists always grow that way, because there is
nowhere for a new property to belong except the end.
The grid above is a monthly service bill: thirteen columns, six of them money, three of them latency. Its column list is shorter than you would expect, and the reason is two ideas rather than one.
Settings group by what they are about
A column in this grid has its settings in named blocks. layout is where it
sits and how wide it is: width, min, max, flex, pin, hidden. cell is what the
cell looks like: alignment, a decoration, a variant, a tooltip. filter is what
the filter menu offers. format is how the value reads. total is what goes in
the footer. sort, group, pivot, edit and value are each their own
block for the same reason.
So the Runtime column above reads like a sentence about the Runtime column:
{
field: 'runtime', title: 'Runtime',
filter: { type: 'set' },
layout: { width: 130 },
cell: {
decoration: 'pill',
variant: { map: { node20: 'success', 'go1.23': 'info', jvm21: 'warning' } },
},
}
You can read that without knowing the API. Where it sits, what it shows, what the filter offers. The blocks also mean a property has an obvious home when it is added later, which is the actual reason flat lists rot: not that they are long, but that nothing tells you where the next thing goes.
Say it once for every column, and once per kind of column
Two keys do the rest.
columnDefaults applies to every column. The grid above sets
{ filter: true, sort: true } there, and no column below repeats it.
columnPresets is the interesting one. It is a named bundle of settings that a
column opts into with preset: 'money'. The grid above declares two:
columnPresets: {
money: {
type: 'number',
format: { style: 'currency', currency: 'USD', decimals: 2 },
total: 'sum',
layout: { width: 150 },
filter: { type: 'number' },
},
latency: {
type: 'number',
format: { decimals: 0, suffix: ' ms' },
total: 'avg',
layout: { width: 110 },
filter: { type: 'number' },
},
}
Six columns then say preset: 'money' and three say preset: 'latency'. Nine
columns, two declarations. Compute, Storage, Network, Licences, Support and
Total are all currency to two places, all summed in the footer, all the same
width, all filterable as numbers, and all of that is written down once.
We checked what the columns come out as rather than trusting the merge: read back through the headless grid, Compute arrives as a number column, right aligned, one hundred and fifty pixels wide, summed, with a numeric filter, none of which appears on its own line.
The Total column shows the other half of the idea. It takes the preset and overrides the one thing that differs:
{ field: 'total', title: 'Total', preset: 'money', layout: { width: 170, pin: 'end' } }
A preset is a starting point rather than a lock, so the column that needs to be wider and pinned to the right says so, in one line, and still inherits the currency, the sum and the filter.
Why this is a buying argument and not a taste argument
Config that reads well is not an aesthetic preference. The moment somebody changes the reporting currency, a flat list means finding six places and missing one, and the one you miss is the column with the smallest numbers, so nobody notices for a quarter. With a preset it is one line, and there is no version of the change that half applies.
It also decides whether a column list can be generated. When the settings for a kind of column live in one named object, a screen that builds its columns from metadata picks a preset by name rather than assembling forty properties by hand. That is the difference between a grid you configure and a grid your application configures.
Group the demo by Squad, sum the money columns, and then look at how little configuration it took.