Extending a shipped grid¶
Any module can ship view/adminhtml/grid/<id>.json with the same id as a grid Nebula ships. The engine merges the files in module load order, so your file changes the shipped grid without copying it. The rules are on The definition engine; this page is the recipes.
Every example below is a complete file. None of them repeats the base definition.
The overlay only merges if the file name matches, and a grid's id is always the filename stem of the shipped definition. Run ls vendor/nebula/module-core-*/view/adminhtml/grid/ for every grid id on your install. bin/magento nebula:doctor counts the definitions of each type but does not name them.
Add a column¶
The passkey module adds two columns to the customer grid this way:
{
"columns": {
"nebula_passkey_count": {
"label": "Passkeys",
"type": "text",
"filter": "select",
"filterOptions": { "1": "Has passkeys", "0": "No passkeys" },
"sortable": false,
"hiddenByDefault": true,
"position": 200
},
"nebula_two_step": {
"label": "2-Step",
"type": "badge",
"options": { "0": { "label": "Off", "class": "neutral" }, "1": { "label": "On", "class": "success" } },
"filter": "select",
"filterOptions": { "1": "On", "0": "Off" },
"sortable": false,
"hiddenByDefault": true,
"position": 210
}
}
}
position places the column among the shipped ones. hiddenByDefault keeps it out of the way until an administrator turns it on.
The column also needs data. The customer grid reads the customer grid collection, so the passkey module adds the two values to that collection with CustomerGridExtensionInterface, a seam the customer provider offers for exactly this. For a grid on grid.collection, add the column to the collection's select with a plugin, or with a method in the data source config. See Data providers.
Change a label, a filter, or the default sort¶
{
"settings": { "pageSize": 50, "defaultSort": { "field": "update_time", "direction": "desc" } },
"columns": {
"title": { "label": "Page title" },
"identifier": { "filter": "text", "searchable": true }
}
}
Only the keys you write change. Every other key on title and identifier stays as shipped.
Point the add button and the row actions elsewhere¶
The email template editor takes over the shipped email template grid without touching its columns:
{
"settings": { "addButton": { "url": "nebulaemail/editor" } },
"columns": {
"actions": {
"actions": [ { "id": "edit", "label": "Edit", "url": "nebulaemail/editor/index/id/{{template_id}}" } ]
}
}
}
Row actions are a list. A list whose items carry id is merged by id, so the shipped edit action is updated and any other shipped action is kept. A list without ids is replaced wholesale.
Remove a column, an action or a mass action¶
{
"columns": {
"billing_postcode": { "$remove": true },
"actions": { "actions": [ { "id": "delete", "$remove": true } ] }
},
"settings": {
"massActions": [ { "id": "delete", "$remove": true } ]
}
}
$remove on a map entry removes the entry. $remove on a list item with an id removes that item. The merger honours it, but the grid schema has no $remove property, so bin/magento nebula:lint reports these files. --no-strict does not change this: the flag is reserved and skips nothing today, so an overlay that removes something never lints. Keep such files out of the lint step, or remove the entry from the base definition instead.
Add a mass action¶
{
"settings": {
"massActions": [
{ "id": "export_crm", "label": "Send to CRM", "url": "vendor/crm/export", "confirm": "Send the selected customers to the CRM?" }
]
}
}
Appended after the shipped ones. The selected rows are posted as <field>[], where field is the mass action's own key and defaults to ids. Set "field": "reviews" when the controller reads another name.
What each checkbox submits as its value is a different key. settings.massActionIdField names the row field the checkbox takes its value from, and defaults to id. The shipped review grid sets both: "massActionIdField": "review_id" on settings, and "field": "reviews" on each action. Changing massActionIdField never renames the POST array.
Turn on whole-row navigation or a quick-edit drawer¶
{
"editable": {
"identifierField": "block_id",
"acl": "Magento_Cms::save",
"repository": "Magento\\Cms\\Api\\BlockRepositoryInterface"
},
"columns": { "title": { "editable": true }, "is_active": { "editable": true } }
}
Set one. With both, the drawer wins: a grid that can quick-edit defaults to rowClick: "quickEdit" and the row URL is never used. The editable block is documented in the Grid reference.
Rules to follow¶
Sequence after the module you overlay. In module.xml, add <module name="Nebula_Theme"/> (or whichever module ships the base grid) to your <sequence>. Without it your file can load first and become the base.
Never write an empty object. "columns": {} replaces the columns map with nothing. Omit a key to leave it alone.
Validate. bin/magento nebula:lint checks your overlay against the grid schema. The schema is strict; a misspelt key fails.
Clear the cache. bin/magento cache:clean nebula_definitions in default mode, bin/magento nebula:compile in production mode. Developer mode reads the file on every request.
Check the page. Open the grid, filter on your column, sort on it, export, and run a mass action. A column with no data renders empty and does not error.