{
  "tag": "ed-data-grid",
  "kind": "component",
  "summary": "Data grids display tabular data with sorting, filtering, grouping and selection.",
  "status": "experimental",
  "level": "organism",
  "since": "1.0",
  "docs": "https://test-apps.eurodata.de/a/eurodata-ui/docs/components/data-grid/",
  "usage": "https://test-apps.eurodata.de/a/eurodata-ui/docs/components/data-grid/usage/",
  "playwright": "getByTestId('…')",
  "dependencies": [
    "ed-badge",
    "ed-button",
    "ed-checkbox",
    "ed-date-picker",
    "ed-divider",
    "ed-empty-state",
    "ed-dropdown",
    "ed-dropdown-item",
    "ed-icon",
    "ed-input",
    "ed-number-input",
    "ed-pagination",
    "ed-popover",
    "ed-skeleton",
    "ed-toolbar"
  ],
  "use": [
    "Finding, sorting and filtering a long list of records — open invoices, employees, bookings — to locate the right ones",
    "Selecting several rows and acting on them together, such as approving, exporting or deleting",
    "Grouping rows by a field such as client or cost center to see them in blocks",
    "Opening nested child rows of a record inside the table, without leaving the list",
    "A table long enough to need virtualization",
    "A table view with more than 20 items"
  ],
  "notFor": [
    {
      "when": "A small, standing set of rows nobody sorts, filters or selects",
      "instead": "a plain `<table>`, styled by this library without any markup of its own"
    },
    {
      "when": "Browsing varied items by quick preview instead of comparing columns",
      "instead": "a [Card](https://test-apps.eurodata.de/a/eurodata-ui/docs/components/card)"
    },
    {
      "when": "A short list of entries with no columns to sort or compare",
      "instead": "a [List](https://test-apps.eurodata.de/a/eurodata-ui/docs/components/list)"
    }
  ],
  "rules": [
    {
      "id": "data-grid/grid-or-plain-table",
      "rule": "Use this grid for a table users search, filter and sort, or select rows in — that is its job, not the plain table's — and a plain `<table>` for a small, standing set of rows nobody sorts, filters or selects.",
      "why": "This is the heaviest table in the library — sorting, filtering, grouping, selection and virtualization, all built in. The library styles a bare `<table>` on its own (headers, rules, a footer row set apart) with no markup needed, and the `ed-zebra-rows` and `ed-hover-rows` classes add the striping and the row highlight as opt-ins."
    },
    {
      "id": "data-grid/search-filter-sort",
      "rule": "Give a grid that is a list view's main content — the screen users come to find a record on — search, filtering and sorting together: a search field in the toolbar above it, filters on its columns or in that toolbar, and sortable columns. A grid that picks from a set inside a dialog or previews rows before an import needs only what its task asks for.",
      "why": "A grid is chosen because users look for rows in it; with one of the three missing they scan by eye, and with none of them the set cannot be narrowed at all.",
      "check": "warning"
    },
    {
      "id": "data-grid/label",
      "rule": "Give every grid a `label` — it is the grid's accessible name.",
      "why": "The `role=\"grid\"` node lives in the shadow root, so an `aria-labelledby` written on the host cannot reach it.",
      "check": "error",
      "covered": true
    },
    {
      "id": "data-grid/bounded-height",
      "rule": "Bound the grid's height once there is more data than comfortably fits, and never leave it unbounded while `pagination-mode=\"infinite\"` is set.",
      "why": "The grid renders, and renders only the rows in view, within whatever height it is given: a bounded host scrolls its rows, an unbounded one grows to show everything at once. With an endless list, an unbounded grid puts every loaded row in the rendered window at once, which the grid always reads as being near the end of what it has loaded, and answers by asking for the next slice immediately, and the one after that.",
      "check": "error"
    },
    {
      "id": "data-grid/pager-or-endless",
      "rule": "Paginate by default. `pagination-mode=\"infinite\"` is allowed in server mode; a migration or a rebuild keeps an existing endless list instead of replacing it with pages.",
      "why": "Pages give a working list a position, a total and a way back. Replacing an endless list that users already know takes a learned way of working away from them, and a rebuild changes nothing that exists unasked.",
      "check": "error"
    },
    {
      "id": "data-grid/server-mode",
      "rule": "Set `dataSource` (or `server` plus a handler for `ed-data-grid-data-request`) once sorting, filtering, searching or paging a server's own rows is cheaper than shipping all of them to the browser — the grid then does none of that itself and asks for exactly the slice it needs. Never reach for `dataSource` for a dataset the browser can comfortably hold and sort on its own.",
      "why": "Server mode is for rows a server itself sorts, filters and pages, not a way to add a loading indicator in front of a small array."
    },
    {
      "id": "data-grid/view-menu-with-hiding",
      "rule": "Offer `with-view-menu` on a grid where users hide columns as part of their work, so a hidden column can be shown again.",
      "why": "Hiding one with no way to show it again strands the user; \"Reset columns\" in that menu puts every column back."
    },
    {
      "id": "data-grid/save-view",
      "rule": "Save the view with `getState()` where users switch the appearance or the density in that menu.",
      "why": "The grid reports the switch with `ed-data-grid-view-change` and keeps nothing itself, so a choice not saved is gone on the next visit."
    },
    {
      "id": "data-grid/hidden-column-filter",
      "rule": "If you let the user hide a filterable column, clear that column's entry from `filters` yourself when it is hidden, or show the active filters somewhere the user can still reach. Don't treat the filter that keeps filtering as a defect to work around in the grid.",
      "why": "A hidden column's filter keeps filtering, with no button left on screen to clear it."
    },
    {
      "id": "data-grid/select-all-scope",
      "rule": "Keep in mind that the header's select-all box, a keyboard range, and Ctrl/Cmd+A act on the rows the filters currently show — the current page of them where `paginate` is set — and a row the filters are holding back keeps whatever selection it already had, either way."
    },
    {
      "id": "data-grid/narrowing-selectable-rows",
      "rule": "When you narrow `selectableRows`, clear the rows it now refuses from `selectedKeys` yourself, in the same step — never expect `selectedKeys` to shrink itself.",
      "why": "Narrowing the predicate does not unselect a row that was already selected under the old one."
    },
    {
      "id": "data-grid/announce-rewritten-search",
      "rule": "Announce the count yourself where your `ed-data-grid-filter-change` handler rewrites what the user typed (trimming it, lowercasing it) before writing it back to `searchTerm`.",
      "why": "The grid notices its own count is no longer for what the user typed and says nothing, rather than announcing the wrong number."
    },
    {
      "id": "data-grid/column-identity",
      "rule": "Give a column that reads a computed value through `value` its own `id` or `label`.",
      "why": "Without either it is identified by its position and warns, and a saved view naming that position breaks the moment a column is added or removed.",
      "check": "error",
      "covered": true
    },
    {
      "id": "data-grid/cell-control-density",
      "rule": "Render a control inside a cell one `density` step more compact than the grid's own where a smaller step exists — `compact` in a default grid, `default` in a spacious one; in a compact grid the control keeps the grid's density.",
      "why": "A control and a cell of the same density are both 32px high, so the control's border overlaps the row.",
      "check": "error"
    },
    {
      "id": "data-grid/row-actions",
      "rule": "Label a row action with the verb alone — \"Approve\", not \"Approve payroll run\": the row names the object. An established action — edit, delete, open — may be icon-only, with an [`ed-tooltip`](https://test-apps.eurodata.de/a/eurodata-ui/docs/components/tooltip) and an accessible name; anything else carries a visible label. A destructive row action asks before it acts, and the confirming button names the object ([`alert-dialog/destructive-confirmation`](https://test-apps.eurodata.de/a/eurodata-ui/docs/components/alert-dialog/usage#rule-alert-dialog-destructive-confirmation)).",
      "check": "warning"
    },
    {
      "id": "data-grid/preload-cell-elements",
      "rule": "Preload every element a `formatter` or a `headerFormatter` returns with `data-ed-preload` on the grid, such as `data-ed-preload=\"ed-format-date\"`.",
      "why": "The autoloader finds the elements in the page, not the ones the grid renders inside itself; an element it has not loaded leaves its cell empty."
    },
    {
      "id": "data-grid/built-in-filtering",
      "rule": "Don't build filtering or search around the grid — `filterable`, `filterType` and `with-search` are the grid's own, wired to `filters` and `searchTerm`.",
      "check": "warning"
    },
    {
      "id": "data-grid/one-nesting",
      "rule": "Never set both `child-rows` and `group-by` expecting them to combine into two levels of nesting — a row nests one way at a time.",
      "why": "`group-by` is kept but ignored while `child-rows` is set, and applies again the moment it is cleared.",
      "check": "error"
    }
  ],
  "api": {
    "attributes": [
      {
        "name": "row-key",
        "property": "rowKey",
        "type": "string | { }",
        "default": "'id'",
        "description": "What identifies a row: a field name, a dot path, or a function of the row. The function is handed\nthe row's place among its siblings and, in a tree, the parent's record and the parent's key, for\nchildren whose own ids repeat under different parents.\n\nThe value is the row's identity everywhere — selection, expansion and a saved view all key on\nit — and it has to survive sorting, filtering and a replaced `data` array, so it belongs to the\nrecord rather than to its position. A row the key does not reach falls back to its position and\nis reported once."
      },
      {
        "name": "label",
        "property": "label",
        "type": "string",
        "default": "''",
        "description": "The grid's accessible name.\n\nIt is a property rather than an `aria-labelledby` on the host because the `role=\"grid\"` node is\nin the shadow root, and an IDREF does not cross that boundary."
      },
      {
        "name": "without-sort-removal",
        "property": "withoutSortRemoval",
        "type": "boolean",
        "default": "false",
        "description": "Whether a third activation of a header leaves the column sorted instead of clearing its order.\n\nThe cycle is ascending, descending, none. With this set it is ascending, descending, ascending,\nfor a grid whose rows are meaningless in their original order."
      },
      {
        "name": "sort-desc-first",
        "property": "sortDescFirst",
        "type": "boolean",
        "default": "false",
        "description": "Whether the first activation of a header sorts descending. Individual columns may say otherwise."
      },
      {
        "name": "max-multi-sort",
        "property": "maxMultiSort",
        "type": "number",
        "default": "0",
        "description": "How many columns may be sorted by at once. `0` is no limit, `1` allows a single column only."
      },
      {
        "name": "child-rows",
        "property": "childRows",
        "type": "null | string | { }",
        "default": "null",
        "description": "Where a row's children are: a field name, a dot path, or a function of the row.\n\nSetting it is what makes the grid a tree. Nothing else has to change — a row without children\nsimply has none, and the engine only asks."
      },
      {
        "name": "group-by",
        "property": "groupBy",
        "type": "null | string | string[]",
        "default": "null",
        "description": "Which columns the rows are grouped by, in the order they nest.\n\nEach value of the first column becomes a row of its own holding the ones that share it; a second\ncolumn groups inside that. As an attribute it takes the ids separated by spaces or commas, which\nis how the template spells a list that has to fit in markup.\n\nThe grouped columns move to the front, in this order, and a grouping row's label stands in the\ncolumn it groups by. Where anything is pinned at the start, they are held there too.\n\nOne hierarchy at a time: where `childRows` is set the rows already nest, and this is ignored —\nkept rather than cleared, so it applies again once they stop, and reported as ignored so the\ngrouping does not simply go missing. A name with nothing in it names no column and is dropped."
      },
      {
        "name": "resizable",
        "property": "resizable",
        "type": "boolean",
        "default": "false",
        "description": "Whether the user can drag a column's width.\n\nA column may say otherwise for itself. What a drag produces is a width the grid remembers, so\na resized column stops sharing the leftover space and is laid out at what it was given."
      },
      {
        "name": "reorderable",
        "property": "reorderable",
        "type": "boolean",
        "default": "false",
        "description": "Whether the user can move columns, by dragging a header or with Shift and an arrow on it. A column\nmay say otherwise for itself with `movable`; a pinned column stands at its edge and is not moved."
      },
      {
        "name": "pinnable",
        "property": "pinnable",
        "type": "boolean",
        "default": "false",
        "description": "Whether the user can hold a column against an edge.\n\nA column may say otherwise for itself. Separate from the column's own `pinned`, which says\nwhere it **starts**: a grid that is not pinnable still lays out what its columns declared, and\none that is offers the edges in each column's menu. A pinned column's menu offers to let it go\neither way."
      },
      {
        "name": "hide-column-menu",
        "property": "hideColumnMenu",
        "type": "boolean",
        "default": "false",
        "description": "Takes the menu out of every column header.\n\nThe menu is where a user sorts, pins, filters, hides and fits a column by name. Without it a\ncolumn's filter is reached only once a filter is in force — a consumer sets it, and the button\nthat then appears in the header opens its panel — so the user can no longer start one."
      },
      {
        "name": "with-view-menu",
        "property": "withViewMenu",
        "type": "boolean",
        "default": "false",
        "description": "Whether the toolbar above the grid carries a view menu: the columns the user may hide, each with a\ncheck that shows or hides it, the appearance and the density to switch between, and an entry that\nputs the columns back as declared. What the user picks there overrides `appearance` and `density`\nas the consumer set them, reported by `ed-data-grid-view-change` and kept by `getState()`.\n`hide-appearance-option` and `hide-density-option` leave either choice out."
      },
      {
        "name": "hide-appearance-option",
        "property": "hideAppearanceOption",
        "type": "boolean",
        "default": "false",
        "description": "Whether the view menu leaves out the appearance, so the user keeps the one the consumer set.\n`getState()` then leaves it out too."
      },
      {
        "name": "hide-density-option",
        "property": "hideDensityOption",
        "type": "boolean",
        "default": "false",
        "description": "Whether the view menu leaves out the density, so the user keeps the one the consumer set.\n`getState()` then leaves it out too."
      },
      {
        "name": "selectable",
        "property": "selectable",
        "type": "boolean",
        "default": "false",
        "description": "Whether rows can be selected: by a click on a row and by Space, and where `selection-mode` is\n`multiple` by a box per row as well. Off, the grid renders no box and reports nothing."
      },
      {
        "name": "selection-mode",
        "property": "selectionMode",
        "type": "'single' | 'multiple'",
        "default": "'single'",
        "description": "Whether one row can be selected at a time, or several.\n\nThe template spells both the switch and the mode in one property; this library's rule says a\ntoggle and its mode are two, so `selectable` turns it on and this says which kind.\n\nIn `multiple` a control column leads the grid with a box per row and one in the header that\ntakes every row, and the grid says it takes several. In `single` there is no box and no column\nfor one — a box offers a choice independent of the others, which one-at-a-time is not. A row is\nselected by a click on it or by Space, released by Space or by Ctrl/Cmd with the click, and says\nso with its paint and its `aria-selected`."
      },
      {
        "name": "sub-row-selection",
        "property": "subRowSelection",
        "type": "boolean",
        "default": "true",
        "description": "Whether selecting a row selects the rows beneath it.\n\nOn, a parent takes its children with it and shows a part-selected box while only some of them\nare chosen. Off, every row answers for itself and a parent is just another row."
      },
      {
        "name": "without-click-selection",
        "property": "withoutClickSelection",
        "type": "boolean",
        "default": "false",
        "description": "Leaves a click on a row's data alone, so only the box and Space select — for a grid whose cell\nclicks open something of their own. In `single`, which draws no box, Space alone selects then.\nMUI 5's `disableSelectionOnClick`."
      },
      {
        "name": "without-toolbar",
        "property": "withoutToolbar",
        "type": "boolean",
        "default": "false",
        "description": "Leaves the toolbar out, for a consumer who builds one of their own: no search, no view menu, no\ncount and no filter badges, whatever else asks for them. The pager counts the entries again. What\nsuch a toolbar needs is public: `searchTerm`, `filters`, `toggleColumn()`, `resetColumns()`,\n`selectedKeys` and the events that report them."
      },
      {
        "name": "server",
        "property": "server",
        "type": "boolean",
        "default": "false",
        "description": "Whether the rows come from a server: the grid sorts, filters and pages nothing itself, and asks\nfor rows through `ed-data-grid-data-request` — answered by setting `data`, `total` and `loading`.\nA `dataSource` switches it on as well. Without `paginate`, it asks for the first `pageSize` rows."
      },
      {
        "name": "total",
        "property": "total",
        "type": "number",
        "default": "-1",
        "description": "How many rows the server has in all, for the pager; below zero while unknown. Unknown, the pager\ngoes on past every full page, and an answer shorter than the page size asked for is the last."
      },
      {
        "name": "loading",
        "property": "loading",
        "type": "boolean",
        "default": "false",
        "description": "Whether rows are being loaded: skeleton rows stand in for them, and the grid is marked busy."
      },
      {
        "name": "filter-debounce",
        "property": "filterDebounce",
        "type": "number",
        "default": "250",
        "description": "How long the typing in the search or a filter rests before a server is asked, in milliseconds."
      },
      {
        "name": "paginate",
        "property": "paginate",
        "type": "boolean",
        "default": "false",
        "description": "Whether the rows are shown a page at a time, with a pager beneath the grid."
      },
      {
        "name": "appearance",
        "property": "appearance",
        "type": "'modern' | 'classic'",
        "default": "'classic'",
        "description": "How the grid is drawn: `classic`, its rows on the page with no outline, as a table stands, or\n`modern`, its rows on a card with rounded corners. The consumer's choice is where the grid starts; the\nuser may switch it in the view menu."
      },
      {
        "name": "striped",
        "property": "striped",
        "type": "boolean",
        "default": "false",
        "description": "Whether every other row is set on a stripe, which makes a wide grid easier to follow across."
      },
      {
        "name": "density",
        "property": "density",
        "type": "'compact' | 'default' | 'spacious'",
        "default": "'default'",
        "description": "How tall the rows are and how much room their cells leave around what they hold; the header stands\na step taller. The consumer's choice is where the grid starts; the user may switch it in the view\nmenu."
      },
      {
        "name": "pagination-mode",
        "property": "paginationMode",
        "type": "'controls' | 'infinite'",
        "default": "'controls'",
        "description": "How `paginate` moves through the rows: a pager beneath the grid, or endlessly — in server mode\nonly — each slice asked for as the rows are scrolled near their end and appended to them. `page`\nis then the last slice asked for. Without server mode, the pager stands. Answering the request\nevent by hand, hand in every row loaded so far with the new slice appended. The grid needs a\nheight: unbounded, every row is in view and the slices load one after another to the end."
      },
      {
        "name": "page",
        "property": "page",
        "type": "number",
        "default": "0",
        "description": "The page shown, counted from zero — the engine's page index. Setting it is silent; the pager and\nthe search and filters the user changes write it and report. A page past the last is brought back\nto the last once there are rows."
      },
      {
        "name": "page-size",
        "property": "pageSize",
        "type": "number",
        "default": "20",
        "description": "How many rows a page holds."
      },
      {
        "name": "filter-from-leaf-rows",
        "property": "filterFromLeafRows",
        "type": "boolean",
        "default": "false",
        "description": "Whether a row stays for a descendant that matches, though it does not match itself. Without it a\ntree is filtered from the top: a row that does not match takes its children with it."
      },
      {
        "name": "max-leaf-row-filter-depth",
        "property": "maxLeafRowFilterDepth",
        "type": "number",
        "default": "100",
        "description": "How many levels down the filters and the search reach into a tree. The children of a row at this\ndepth stay with it as they are, filtered or not; with `filter-from-leaf-rows`, that row is no\nlonger kept for a descendant that matches, only for itself."
      },
      {
        "name": "with-search",
        "property": "withSearch",
        "type": "boolean",
        "default": "false",
        "description": "Whether a search field stands in the toolbar. The search runs on `searchTerm` either way."
      },
      {
        "name": "dir",
        "property": "dir",
        "type": "string"
      },
      {
        "name": "lang",
        "property": "lang",
        "type": "string"
      }
    ],
    "properties": [
      {
        "name": "columns",
        "type": "EdDataGridColumn<TData>[]",
        "description": "The columns to render.\n\nAssigning the same array again counts as a change, so columns edited in place show once the\narray is handed back, as the template allows."
      },
      {
        "name": "data",
        "type": "TData[]",
        "description": "The rows to render.\n\nAssigning the same array again counts as a change, so rows edited in place show once the array\nis handed back, as the template allows."
      },
      {
        "name": "sort",
        "type": "EdDataGridSort",
        "description": "Which columns the rows are sorted by, in the order that decides ties.\n\nReadable and writable: a consumer can set the order the grid opens with, and reads it back here\nafter the user changes it — the same value `ed-data-grid-sort-change` carries."
      },
      {
        "name": "isMultiSortEvent",
        "type": "((event: unknown) => boolean) | null",
        "description": "What makes an activation add a column to the sort rather than replace it.\n\nUnset, the engine's own answer applies: the Shift key. A consumer whose product already spends\nShift on something else replaces the test rather than losing multi-column sorting."
      },
      {
        "name": "rowDetail",
        "type": "((row: TData) => unknown) | null",
        "description": "What a row shows in a panel beneath it once opened: a function of the row, whose result is\nrendered as a cell's content is. Setting it gives every row but a group row the control that\nopens it; a row with children opens both together, the panel first."
      },
      {
        "name": "expandedKeys",
        "type": "EdDataGridExpandedKeys",
        "description": "Which rows are open, by their key.\n\nReadable and writable, like `sort`: a consumer can open rows up front and reads back what the\nuser opened. Setting it does **not** emit — only a user's own toggle does.\n\nNothing prunes it. The engine would empty it on every new `data`, which costs a consumer the\nview they just restored, so that is switched off — and the price is that a key outlives the row\nit named: a later dataset in which the same key has children again shows that row open, and a\nlong session accumulates keys that name nothing. Both are the consumer's to clear, and pruning\nagainst the rendered rows is not the answer while a filtered-away row still has to keep its\nstate (block 8)."
      },
      {
        "name": "columnOrder",
        "type": "string[]",
        "description": "The order the columns are shown in, by id — empty for the order they were declared in. The\nuser's moves write it, hidden columns included, and setting it moves the columns without a\nreport. Pinned columns still stand at their edges; this orders the rest."
      },
      {
        "name": "selectedKeys",
        "type": "EdDataGridSelectedKeys",
        "description": "Which rows are selected, by their key.\n\nReadable and writable, like `sort` and `expandedKeys`, and setting it does **not** emit — only\na user's own doing does. Keys and not positions, so a selection survives sorting, filtering and\na new `data` array carrying the same rows.\n\nA key naming a loaded row that `selectableRows` refuses is dropped when it is set; a key naming\nno loaded row is kept."
      },
      {
        "name": "selectableRows",
        "type": "((row: TData) => boolean) | null",
        "description": "Which rows the user may select. Rows it turns down are not reached by a click, Space, a range or\na select-all. In `multiple` their box cannot be operated; in `single`, which draws no box, they\ncarry no `aria-selected`, which says a row cannot be selected."
      },
      {
        "name": "selectedRows",
        "type": "TData[]",
        "description": "The records behind the selected rows, at every depth.\n\nRead off the core model rather than the rendered one, so a selected child inside a closed\nparent is in here — it is selected whether or not anyone can currently see it.\n\nSettable, as the template's is: the records are matched against the loaded rows by identity\nand become `selectedKeys`. A record that is not loaded names no key and is left out."
      },
      {
        "name": "filters",
        "type": "EdDataGridFilter[]",
        "description": "The column filters, as `{ id, value }` — the template's shape, which the engine reads as it is. A\nfilter whose value is empty filters nothing and is dropped. Setting it is silent; the user's\nfiltering writes it and reports."
      },
      {
        "name": "dataSource",
        "type": "EdDataGridDataSource<TData> | null",
        "description": "Where a grid in server mode gets its rows from: handed the request, it answers with the rows and\nthe total. The grid shows it is loading meanwhile, drops an answer a newer request overtook, and\nreports a failure through `ed-data-grid-data-error`."
      },
      {
        "name": "rowClass",
        "type": "((row: TData) => string | string[] | null | undefined | false) | null",
        "description": "Names for a row — to flag an overdue or an archived one. They land on the row as classes, as the\ntemplate has it, and on the row and each of its cells as parts, so a page styles them with\n`ed-data-grid::part(overdue)`. Space-separated or a list; nothing for none. A name the grid uses\nfor a part or a class of its own is left out."
      },
      {
        "name": "error",
        "type": "unknown",
        "description": "A failed request, set by a consumer who answers `ed-data-grid-data-request` by hand: the grid then\nsays the rows could not be loaded, as it does where a `dataSource` fails, with a button that asks\nagain, which sets it back to null. Set it back to null with the next answer as well."
      },
      {
        "name": "pageSizeOptions",
        "type": "number[]",
        "description": "The page sizes the pager offers."
      },
      {
        "name": "pageCount",
        "type": "number",
        "description": "How many pages the rows make: one without `paginate`, and at least one with it."
      },
      {
        "name": "searchTerm",
        "type": "string",
        "description": "The search term: a row stays where any searchable column contains it, whatever the case — the\nvalue the column reads, not its formatted text. Setting it is silent; typing in the search field\nwrites it and reports."
      },
      {
        "name": "searchFn",
        "type": "EdDataGridSearchFn<TData> | null",
        "description": "The grid's own search, in place of the case-insensitive \"contains\". Asked once per searchable column."
      },
      {
        "name": "filteredCount",
        "type": "number",
        "description": "How many rows the filters and the search leave, before grouping — at the top level of a tree."
      }
    ],
    "methods": [
      {
        "name": "expandRow",
        "parameters": [
          {
            "name": "id",
            "type": "string | number"
          }
        ],
        "description": "Opens a row, by the key `rowKey` gives it.\n\nSilent, as the template has it: these report nothing, because the caller is the one who did it.\nThe key is kept even for a row that cannot open yet, so the row opens once it can — once its\nchildren or a detail arrive."
      },
      {
        "name": "collapseRow",
        "parameters": [
          {
            "name": "id",
            "type": "string | number"
          }
        ],
        "description": "Closes a row. Silent, for the same reason."
      },
      {
        "name": "expandAllRows",
        "description": "Opens every row that has something to open, at every level."
      },
      {
        "name": "collapseAllRows",
        "description": "Closes all of them."
      },
      {
        "name": "copySelectedRows",
        "parameters": [
          {
            "name": "options",
            "type": "EdDataGridCopyOptions"
          }
        ],
        "returns": "Promise<number>",
        "description": "Puts rows on the clipboard as text: the selected ones, or every row where none is selected, in\nthe order the grid shows them and with the children of closed rows included.\n\nThe template's method and options. Tab-separated by default, which is what a spreadsheet takes\nfrom a paste; `csv` quotes what needs quoting. A value a spreadsheet would run as a formula gets\na quote in front unless `escapeFormulas` is `false`. Each cell is its formatter's text where the\nformatter returns a string, and its value otherwise. Answers how many rows went."
      },
      {
        "name": "getDataAsCsv",
        "parameters": [
          {
            "name": "options",
            "type": "EdDataGridCsvOptions"
          }
        ],
        "returns": "string",
        "description": "The rows as CSV: every data row the sort, the filters and the search leave, across the pages and\nevery depth, in the columns shown and their order. The template's method; in server mode, only\nthe rows loaded. Lines end in CRLF, as RFC 4180 §2 has it."
      },
      {
        "name": "exportDataAsCsv",
        "parameters": [
          {
            "name": "options",
            "type": "EdDataGridExportOptions"
          }
        ],
        "returns": "void",
        "description": "Saves `getDataAsCsv()` as a file. With a byte order mark in front, which is how a spreadsheet\ntells UTF-8 from its own legacy encoding — without it, Excel shows every umlaut garbled."
      },
      {
        "name": "getVisibleRows",
        "returns": "TData[]",
        "description": "The data rows shown right now, in order: after the sort, the filters, the open rows and the page."
      },
      {
        "name": "getProcessedRows",
        "returns": "TData[]",
        "description": "Every data row the sort, the filters and the search leave, across the pages and every depth."
      },
      {
        "name": "getState",
        "returns": "EdDataGridState",
        "description": "A snapshot of what the user made of the view — the columns' order, widths, visibility and pins,\nthe sort, the filters, the search, the selection, the open rows, the page and, where the view menu\noffers them, the appearance and the density — to keep and hand back to `setState()`."
      },
      {
        "name": "setState",
        "parameters": [
          {
            "name": "state",
            "type": "Partial<EdDataGridState>"
          }
        ],
        "returns": "void",
        "description": "Puts back what a snapshot holds, and leaves what it does not. Silent, as setting the properties\nis: the snapshot is the caller's own."
      },
      {
        "name": "resetState",
        "returns": "void",
        "description": "Returns the view to the columns as declared — their order, widths, visibility and pins — with no\nsort, no filters, no search and no row open. The selection and the page stay, as the template has\nit. Silent."
      },
      {
        "name": "scrollToIndex",
        "parameters": [
          {
            "name": "index",
            "type": "number"
          },
          {
            "name": "options",
            "type": "{ align?: 'start' | 'center' | 'end' | 'auto'; behavior?: 'auto' | 'smooth' }",
            "optional": true
          }
        ],
        "description": "Brings a row into view by its position in the data.\n\nPublic, and the one method block 2 owes: a consumer cannot scroll to a row that is not rendered,\nand with a window of a dozen rows out of fifty thousand, most rows are not. `align: 'start'` puts\nit just below the header rather than underneath it, which is what the scroll margin is for."
      },
      {
        "name": "pinColumn",
        "parameters": [
          {
            "name": "columnId",
            "type": "string"
          },
          {
            "name": "side",
            "type": "'left' | 'right' | false"
          }
        ],
        "returns": "void",
        "description": "Holds a column against an edge, or lets it go with `false`.\n\nThe column moves to that edge, after the columns already held there, and goes back to its place\nin the list when let go. The edges are named the way the column declares them, and read as\nlogical: `left` is the start edge and follows the writing direction.\n\nSilent, the way `expandRow()` is and for the same reason: the caller already knows, and an\nelement reporting its own configuration back would feed a listener that writes it. The gesture\nthat moves a column is what reports.\n\nA column the rows are grouped by is left where the grouping puts it: first, and held at the\nstart exactly when another column is."
      },
      {
        "name": "toggleColumn",
        "parameters": [
          {
            "name": "columnId",
            "type": "string"
          },
          {
            "name": "visible",
            "type": "boolean",
            "optional": true
          }
        ],
        "returns": "void",
        "description": "Shows or hides a column by its id, or turns it the other way without a value.\n\nSilent, like `pinColumn()`: the caller already knows. The column keeps its width and its pin for\nwhen it comes back."
      },
      {
        "name": "resetColumns",
        "returns": "void",
        "description": "Puts the columns back the way they were declared: the widths the user dragged, the columns they\nshowed or hid, and the edges they pinned them to all go. Sort, selection and the open rows stay.\nSilent, as the template's is."
      },
      {
        "name": "reload",
        "returns": "void",
        "description": "Asks the server again for the rows shown, though nothing changed."
      },
      {
        "name": "getColumnFacets",
        "parameters": [
          {
            "name": "columnId",
            "type": "string"
          }
        ],
        "returns": "EdDataGridColumnFacets",
        "description": "What a column holds before its own filter applies: each value with how often it occurs, and the\nrange of its numbers — for building a filter of one's own. The template's method."
      },
      {
        "name": "autoSizeColumn",
        "parameters": [
          {
            "name": "columnId",
            "type": "string"
          }
        ],
        "returns": "void",
        "description": "Sets a column to the width of what is in it, which can be narrower than it was.\n\nThe body cells count by the text they render and the header by its label beside the controls\nit holds, which is how the template measures."
      },
      {
        "name": "autoSizeColumns",
        "returns": "void",
        "description": "Fits every shown column the user may resize to its content, as `autoSizeColumn()` does one."
      },
      {
        "name": "sizeColumnsToFit",
        "returns": "void",
        "description": "Shares the grid's width out among the shown columns by their `flex`, each held within its\n`minWidth` and `maxWidth`, while the control columns keep their room. Each width is reported,\nfinished, as a width chosen for the column. The template's method, and like it this neither hands\nwhat a limit cut off to the others nor skips a column that may not be resized."
      },
      {
        "name": "getColumnPin",
        "parameters": [
          {
            "name": "columnId",
            "type": "string"
          }
        ],
        "returns": "'left' | 'right' | false",
        "description": "The edge a column is held at — `'left'`, `'right'`, or `false` — whether by its declaration, the\nuser, or because the rows are grouped by it and something else is held at the start."
      },
      {
        "name": "getPropertyDescriptor",
        "static": true,
        "parameters": [
          {
            "name": "name",
            "type": "PropertyKey"
          },
          {
            "name": "key",
            "type": "string | symbol"
          },
          {
            "name": "options",
            "type": "PropertyDeclaration"
          }
        ],
        "returns": "PropertyDescriptor | undefined",
        "description": "Wraps the accessor of reflected properties so they always hold a valid value, as a native\nreflected IDL attribute does (`button.type = undefined` reads `submit`):\n\n- A property that declares `useDefault` takes the first value set on an element for its\n  default. That value is its field initializer's: the constructor has finished before markup,\n  a framework or any other code can set anything. Every later `null` or `undefined` means what\n  a dropped prop means on a native element — the attribute is removed: with the attribute\n  there, it is removed and Lit's `useDefault` restores the default without writing it; without\n  one, the property is set to the default. A value set and cleared again before the element\n  updates still writes the default once, from the pending reflection.\n- A Boolean is coerced with `Boolean()`, so `undefined` is `false`.\n\nLit's `useDefault` keeps the initial default out of the DOM. Setting the default explicitly\nwrites it, as `button.type = 'submit'` writes `type`."
      }
    ],
    "events": [
      {
        "name": "ed-data-grid-cell-click",
        "react": "onEdDataGridCellClick",
        "type": "EdDataGridCellClickEvent",
        "description": "Emitted when a cell is activated, by a click or by Enter. The detail carries the `column` the cell stands in, by its id, the `row` record behind it, the cell's `value`, the `rowIndex` among the rows shown, and the `originalEvent` that tells the gestures apart. A cell that holds its own control does not report one: the control owns the gesture, by pointer and by key alike. Nor does a grouping row, which holds no record of its own. There is no row event beside it — a cell activation already names its row."
      },
      {
        "name": "ed-data-grid-cell-contextmenu",
        "react": "onEdDataGridCellContextmenu",
        "type": "EdDataGridCellContextmenuEvent",
        "description": "Emitted when a cell is asked for its context menu. Same detail; `originalEvent` is the browser's `contextmenu` event, so a consumer opening their own menu calls `preventDefault()` on it."
      },
      {
        "name": "ed-data-grid-row-expand",
        "react": "onEdDataGridRowExpand",
        "type": "EdDataGridRowExpandEvent",
        "description": "Emitted when the user opens a row, whether it is a parent with children or a grouping row. The detail carries the `row` record. Opening a row through `expandRow()`, `expandAllRows()` or the `expandedKeys` property is silent."
      },
      {
        "name": "ed-data-grid-row-collapse",
        "react": "onEdDataGridRowCollapse",
        "type": "EdDataGridRowCollapseEvent",
        "description": "Emitted when the user closes a row. Same detail, same rule."
      },
      {
        "name": "ed-data-grid-column-resize",
        "react": "onEdDataGridColumnResize",
        "type": "EdDataGridColumnResizeEvent",
        "description": "Emitted while the user drags a column's width and once more when they let go, and once, finished, for each Alt+Arrow step, each fit from a double-click or the menu, and with `width: null` for each width the user chose that \"Reset columns\" drops. The detail carries the `columnId`, the `width` it has reached and `finished`, which tells the two apart — a listener saving the result checks it rather than debouncing. A press that does not move reports nothing, and a cancelled drag no finished width: the column goes back to where it was."
      },
      {
        "name": "ed-data-grid-row-select",
        "react": "onEdDataGridRowSelect",
        "type": "EdDataGridRowSelectEvent",
        "description": "Emitted when the user changes which rows are selected: by a click on a row, with or without Ctrl/Cmd or Shift, through a row's box or the one in the header, or by key — Space on a row, Shift with an arrow for a range, Ctrl/Cmd+A for every row. The detail carries the whole selection — `selectedKeys` and the `selectedRows` behind them, at every depth — because one box can move many rows at once. Setting `selectedKeys` yourself does not fire it."
      },
      {
        "name": "ed-data-grid-sort-change",
        "react": "onEdDataGridSortChange",
        "type": "EdDataGridSortChangeEvent",
        "description": "Emitted when the user changes the order, by clicking a header or pressing Enter on one. The detail carries the whole `sort` list, first entry first. Setting `sort` yourself does not fire it."
      },
      {
        "name": "ed-data-grid-column-visibility-change",
        "react": "onEdDataGridColumnVisibilityChange",
        "type": "EdDataGridColumnVisibilityChangeEvent",
        "description": "Emitted when the user shows or hides a column, from its menu or from the view menu — including \"Reset columns\", once per column it changes. The detail carries the `columnId` and whether it is now `visible`. `toggleColumn()` and `resetColumns()` are silent."
      },
      {
        "name": "ed-data-grid-column-move",
        "react": "onEdDataGridColumnMove",
        "type": "EdDataGridColumnMoveEvent",
        "description": "Emitted when the user moves a column, by dragging its header or with Shift and an arrow on it: during a drag each time the place it would land at changes, with `finished` false, and once more when the drop settles. The detail carries the `columnId`, its `toIndex`, the whole `columnOrder` with hidden columns included, and `finished`. \"Reset columns\" reports once, with `columnId` and `toIndex` `null` and an empty `columnOrder`. Setting `columnOrder` is silent."
      },
      {
        "name": "ed-data-grid-data-request",
        "react": "onEdDataGridDataRequest",
        "type": "EdDataGridDataRequestEvent",
        "description": "Emitted in server mode when the grid needs rows: first, on a new sort, filters, search, page or page size, and on `reload()`. The detail is the request — `sort`, `filters`, `search`, `page`, `pageSize` — with a `signal` that aborts once a newer request overtakes it. Without a `dataSource`, answer it by setting `data`, `total` and `loading`, and `error` where it failed."
      },
      {
        "name": "ed-data-grid-data-error",
        "react": "onEdDataGridDataError",
        "type": "EdDataGridDataErrorEvent",
        "description": "Emitted when the `dataSource` fails a request that is still current. The detail carries the `error` and the `request`; the rows the grid had stay."
      },
      {
        "name": "ed-data-grid-page-change",
        "react": "onEdDataGridPageChange",
        "type": "EdDataGridPageChangeEvent",
        "description": "Emitted when the user moves to another page or changes the page size from the pager, and when a search or a filter the user changed takes the grid back to its first page. The detail carries the `page`, counted from zero, and the `pageSize`. Setting `page` or `pageSize` is silent."
      },
      {
        "name": "ed-data-grid-filter-change",
        "react": "onEdDataGridFilterChange",
        "type": "EdDataGridFilterChangeEvent",
        "description": "Emitted when the user changes a column's filter from its panel, clearing it included, clears it with its badge in the toolbar or all of them with \"Reset all filters\", or types in the search field. The detail carries every filter in force as `filters`, each `{ id, value }` with empty ones and columns that cannot be filtered left out, and the `searchTerm`. Setting `filters` or `searchTerm` is silent."
      },
      {
        "name": "ed-data-grid-view-change",
        "react": "onEdDataGridViewChange",
        "type": "EdDataGridViewChangeEvent",
        "description": "Emitted when the user changes how the grid looks from its view menu: its `appearance` or its `density`. The detail carries both as they are now. Setting either property yourself does not fire it."
      },
      {
        "name": "ed-data-grid-column-pin",
        "react": "onEdDataGridColumnPin",
        "type": "EdDataGridColumnPinEvent",
        "description": "Emitted when the user holds a column against an edge or lets it go from its menu, and for each pin \"Reset columns\" changes. The detail carries the `columnId` and where it is now `pinned` — `'left'`, `'right'` or `false`. `pinColumn()` is silent, and so is choosing the edge the column is already at."
      }
    ],
    "slots": [
      {
        "name": "toolbar-actions",
        "description": "Actions in the toolbar above the grid, after the search: anything that acts on the rows — creating, exporting, deleting — or opens a surface to do so. Typically `<ed-button>` and `<ed-dropdown>` elements; what no longer fits moves into the toolbar's overflow menu."
      },
      {
        "name": "toolbar-view",
        "description": "Controls in the toolbar that decide what the grid shows — quick filters, the button that opens a filter panel — before the view menu; they never collapse into the overflow menu. Typically `<ed-select>`, `<ed-radio-group appearance=\"button\">` or an `<ed-button>` for the filter panel."
      },
      {
        "name": "loading",
        "description": "What stands in for the rows while they load. Skeleton rows by default."
      },
      {
        "name": "empty",
        "description": "What stands where there are no rows and nothing is looked for. An `ed-empty-state` by default."
      },
      {
        "name": "no-results",
        "description": "What stands where the filters or the search leave no row. An `ed-empty-state` by default."
      }
    ],
    "cssProperties": [
      {
        "name": "--ed-data-grid-cell-padding-inline",
        "description": "The inline padding of every cell."
      },
      {
        "name": "--ed-data-grid-cell-padding-block",
        "description": "The block padding of every cell. The density steps set it."
      },
      {
        "name": "--ed-data-grid-row-height",
        "description": "How tall a row of one line is. The density steps set it."
      },
      {
        "name": "--ed-data-grid-border-color",
        "description": "The outline, the rules between the rows, the header's rules and the lines at a group's end and a pinned band's edge."
      },
      {
        "name": "--ed-data-grid-border-radius",
        "description": "The corners of the outline."
      },
      {
        "name": "--ed-data-grid-accent-color",
        "description": "The boxes that select, the bar a selected row carries at its start edge, and a column edge under the pointer."
      },
      {
        "name": "--ed-data-grid-header-background",
        "description": "The header's background."
      },
      {
        "name": "--ed-data-grid-header-text-color",
        "description": "The header's text, the sort priority included."
      },
      {
        "name": "--ed-data-grid-header-icon-color",
        "description": "The header's icons: the sort arrow and the number beside it, the filter button and the menu button."
      },
      {
        "name": "--ed-data-grid-stripe-background",
        "description": "Every other row, with `striped`."
      },
      {
        "name": "--ed-data-grid-row-hover-background",
        "description": "The row under the pointer."
      },
      {
        "name": "--ed-data-grid-focus-ring",
        "description": "The ring around the cell that holds the focus."
      },
      {
        "name": "--ed-data-grid-indent-size",
        "description": "How far one level of a tree steps in."
      },
      {
        "name": "--ed-data-grid-selected-background",
        "description": "What a selected row is painted with. The library's own selected color by default, so a selected row reads as the same thing as a selected option."
      }
    ],
    "parts": [
      {
        "name": "base",
        "description": "The grid itself. It is the scroll container: give the host a height and the rows scroll here."
      },
      {
        "name": "header",
        "description": "The rowgroup holding the column headers."
      },
      {
        "name": "header-row",
        "description": "One row of column headers. It carries the role and no box of its own."
      },
      {
        "name": "header-cell",
        "description": "One column header."
      },
      {
        "name": "sort-indicator",
        "description": "The arrow on a header that can be sorted by. Present whether or not the column is sorted, so the header keeps its width."
      },
      {
        "name": "sort-priority",
        "description": "The number saying where a column stands in the order. Rendered only while more than one column is sorted."
      },
      {
        "name": "toolbar",
        "description": "The `ed-toolbar` above the grid, rendered once anything is in it: the search, the view menu, content in `toolbar-actions` or `toolbar-view`, a filterable column, or selection. Its lower row always counts the entries."
      },
      {
        "name": "toolbar-count",
        "description": "How many entries there are, of how many where a filter or the search narrows them, and how many are selected."
      },
      {
        "name": "filter-badge",
        "description": "One active filter in the toolbar, as its column and its value, with a button that clears it."
      },
      {
        "name": "filter-reset",
        "description": "The button after the last filter badge that clears every filter and the search."
      },
      {
        "name": "search",
        "description": "The search field at the start of the toolbar, rendered with `with-search`."
      },
      {
        "name": "drag-ghost",
        "description": "The label naming a column that follows the pointer while the column is dragged. It sits in the top layer."
      },
      {
        "name": "view-menu",
        "description": "The menu at the end of the toolbar that shows or hides the columns and switches the appearance and the density."
      },
      {
        "name": "column-menu",
        "description": "The menu in a column header, on every column that has something to offer unless `hide-column-menu` is set: sorting, pinning, the filter, and hiding and fitting the column. Its entries are rendered only while it is open."
      },
      {
        "name": "column-menu-button",
        "description": "The button that opens a column's menu. Out of sight until the header is pointed at, the keyboard is in it or the menu is open, though it keeps its room; always shown on a screen that cannot point."
      },
      {
        "name": "filter-button",
        "description": "The button in a filterable column's header that opens its filter panel, rendered while a filter is in force on the column or its panel is open. A filter is started from the column's menu."
      },
      {
        "name": "filter-panel",
        "description": "The popover holding a column's filter. It stands beside the grid, not in the header, and holds its controls only while open."
      },
      {
        "name": "filter-clear",
        "description": "The button in a filter panel that clears the column's filter."
      },
      {
        "name": "filter-close",
        "description": "The button at the top of a filter panel that closes it."
      },
      {
        "name": "body",
        "description": "The rowgroup holding the data rows."
      },
      {
        "name": "row",
        "description": "One data row."
      },
      {
        "name": "group-row",
        "description": "A row standing for one value of a grouped column. It carries the `row` part as well."
      },
      {
        "name": "control-header-cell",
        "description": "A control column's header. It heads no data; the one over the boxes carries the box that takes every row, the one over the toggles holds nothing."
      },
      {
        "name": "control-cell",
        "description": "A cell holding what belongs to a row rather than to a column. These are the leading columns of the grid, one per control, and they are held at the edge whenever a column is pinned there."
      },
      {
        "name": "select-cell",
        "description": "The control cell holding a row's box, rendered where `selection-mode` is `multiple`. Carries the `control-cell` part as well."
      },
      {
        "name": "expand-cell",
        "description": "The control cell holding the control that opens a row. Carries the `control-cell` part as well."
      },
      {
        "name": "select",
        "description": "The box that selects one row, rendered where `selection-mode` is `multiple`. On a grouping row it takes the rows that row holds, and reads its own state back off them."
      },
      {
        "name": "select-all",
        "description": "The box in the header that takes every row. Rendered only where more than one row can be selected."
      },
      {
        "name": "expand",
        "description": "The control that opens a row, inside its control cell and stepped in by the row's depth. Absent on a row with nothing to open."
      },
      {
        "name": "resize-handle",
        "description": "The strip between two column headers that the width is dragged on; a double-click fits the column to its content. A named separator that takes no focus — the keyboard resizes from the header cell with Alt and the arrows."
      },
      {
        "name": "indent",
        "description": "The space in front of a row's content that shows how deep it sits. On the first cell holding data, one step per level."
      },
      {
        "name": "no-results",
        "description": "The message where the filters or the search leave no row. It stands below the grid's header, outside the grid."
      },
      {
        "name": "footer",
        "description": "The row group holding the footer row, held against the bottom of the grid."
      },
      {
        "name": "footer-row",
        "description": "The footer row, rendered where a shown column has a `footer` and there are rows."
      },
      {
        "name": "footer-cell",
        "description": "One cell of the footer row, under a data column. Under a control column the cell carries the `control-cell` part, as it does in the body."
      },
      {
        "name": "row-detail",
        "description": "The row beneath an open row that holds its detail, rendered with `rowDetail`."
      },
      {
        "name": "row-detail-cell",
        "description": "The one cell of that row, across every column, holding what `rowDetail` returns."
      },
      {
        "name": "pagination",
        "description": "The strip beneath the grid holding the pager, rendered with `paginate` while there are rows."
      },
      {
        "name": "pagination-info",
        "description": "Which rows the page shows, of how many there are."
      },
      {
        "name": "loading-row",
        "description": "The row standing for the rows while they load, with one cell across every column holding the `loading` slot."
      },
      {
        "name": "retry-row",
        "description": "The row at the end of an endless grid's rows where its next slice failed, rendered with `pagination-mode=\"infinite\"`."
      },
      {
        "name": "retry-cell",
        "description": "The one cell of that row, across every column, saying the rows could not be loaded."
      },
      {
        "name": "retry-button",
        "description": "The button in that cell that asks for the slice again."
      },
      {
        "name": "skeleton-row",
        "description": "A row of skeletons standing in for the rows while they load. Hidden from the accessibility tree; the grid is marked busy."
      },
      {
        "name": "empty",
        "description": "What stands where there are no rows and nothing is looked for. It stands below the header, outside the grid."
      },
      {
        "name": "load-error",
        "description": "What stands in the empty state's place where a data source failed and there are no rows: saying so, with a button to ask again."
      },
      {
        "name": "load-error-notice",
        "description": "What stands above the grid where a later request failed and the rows it had stay: saying so, with a button to ask again."
      },
      {
        "name": "live-region",
        "description": "Where the grid says what just happened, for a reader who cannot see it. Visually hidden and outside the grid, since a grid's children are row groups and rows."
      },
      {
        "name": "group-label",
        "description": "The value a grouping row stands for, in the column it groups by."
      },
      {
        "name": "group-count",
        "description": "How many rows that grouping row holds."
      },
      {
        "name": "cell",
        "description": "One data cell."
      }
    ],
    "states": [
      {
        "name": "reordering",
        "description": "While a column is being dragged."
      },
      {
        "name": "loading",
        "description": "While `loading` is set."
      },
      {
        "name": "empty",
        "description": "While there is no row to show and none is loading."
      }
    ]
  },
  "import": "import '@eurodata/eurodata-ui/dist/components/data-grid/data-grid.js';",
  "react": {
    "component": "EdDataGrid",
    "import": "import EdDataGrid from '@eurodata/eurodata-ui/dist/react/data-grid/index.js';"
  }
}
