Data Grid component for CRUD ("Create Read Update Delete") applications.
+
+Toolpad Core extends the [X data grid](https://mui.com/x/react-data-grid/) with CRUD functionality. It abstracts the manipulations in a data provider object. The data provider object describes the shape of the data and the available manipulations. When you pass the data provider to a grid it is automatically configured as a CRUD grid. All properties of the X grid are also available and can be used to override the data provider behavior.
+
+Where Core and X components focus on the user interface, Toolpad Core components start from a definition of the data. It centralizes data loading, filtering, pagination, field formatting, mutations, access control and more.
+
+## Basic
+
+The simplest data provider exposes a `getMany` function and a `fields` definition. This is enough for a grid to render the rows.
+
+{{"demo": "BasicDataProvider.js"}}
+
+## Override columns
+
+The Toolpad Core grid adopts the fields that are defined in its data provider. This is handy because it allows for sharing formatting and validation options between data rendering components. However, it is still possible to override the defaults at the level of an individual grid. The grid adopts the columns you've defined in the `columns` property, and sets default values for the individual column options for each of them.
+
+{{"demo": "OverrideColumns.js"}}
+
+## Column inference
+
+To help you get started quickly, the grid is able to infer data provider fields if they are not defined. This allows you to quickly get started with a basic field definition based on the returned data. When a data provider is passed that doesn't have a field definiton, the grid infers field definitions and shows a warning. Click the question mark to show more information on how to solve the warning message. Try copying the snippet from the dialog and paste it in the data provider definition below:
+
+{{"demo": "FieldInference.js"}}
+
+## Server-side Pagination
+
+By default the grid paginates items client-side. If your backend supports server-side pagination, enable it with the `paginationMode` flag in the data grid. Now the `getMany` method receives a `pagination` parameter. This parameter is an object containing a `start` and `pageSize` property that denote the start index and offset for the requested page. You can optionally send a `rowCount` along with the `rows`.
+
+{{"demo": "ServerSidePagination.js"}}
+
+You can decide whether your data provider supports pagination exclusively or optionally by throwing an error when `pagination` is `null`.
+
+## Server-side Filtering
+
+By default, the grid filters rows client-side. If your backend supports filtering, you can enable it with the `filterMode` property in the data grid. To pass a `filter` to the data provider, set `filterMode` to `'server'`.
+
+```js
+async getMany({ filter }) {
+ const url = new URL('https://api.example.com/data');
+ for (const [field, ops = {}] of Object.entries(filter)) {
+ for (const [operator, value] of Object.entries(ops)) {
+ url.searchParams.append(field, `${operator}:${value}`);
+ }
+ }
+ const res = await fetch(url);
+ if (!res.ok) {
+ throw new Error(`HTTP ${res.status}: ${await res.text()}`);
+ }
+ return { rows: await res.json() };
+},
+```
+
+## CRUD
+
+The data provider supports all basic CRUD operations
+
+### Create a row
+
+When you add a `createOne` method to the data provider, the grid gets a "Add record" button in the Toolbar. When you click this button, a draft row shows wich you can fill with values for the created item. To commit the new row, click the save button. This calls the `createOne` function with the values that were filled. When the operation finishes, a notification shows.
+
+{{"demo": "CrudCreate.js"}}
+
+### Update a row
+
+When you add a `updateOne` method to the data provider, the grid gets edit buttons in its action column. When you click this button, the row goes in editing mode. When you click the save button, the `updateOne` method is called with the row id as first parameter and the changed values as the second parameter. When the operation finishes, a notification shows.
+
+{{"demo": "CrudUpdate.js"}}
+
+### Delete a row
+
+When you add a `deleteOne` method to your data provider, the grid gets delete buttons in its action column. When you click this button, the `deleteOne` method is called with the id of the relevant row. When the operation finishes, a notification shows.
+
+{{"demo": "CrudDelete.js"}}
+
+### 🚧 Delete multiple rows
+
+When the data provider contains a `deleteMany` method, the grid allows for multiple selection and delete.
+
+## 🚧 Input validation
+
+For create and update logic, the data provider supports validation
+
+### 🚧 Static
+
+In the field definitions, with for example a `required` property.
+
+```js
+fields: {
+ name: {
+ required: true,
+ validate: (value) => value.length < 10 ? null : 'Too long'
+ },
+}
+```
+
+### 🚧 Dynamic
+
+In the `updateOne`/`createOne` method, by throwing a specific error.
+
+```js
+throw new ValidationError({
+ name: 'Already exists',
+});
+```
+
+## 🚧 Premium/pro grid
+
+An X premium and pro version of the grid are exported from the `@toolpad/enterprise` package. An X license is required accordingly.
+
+## 🚧 Access control
+
+The data provider integrates with Toolpad access control to enable/disable CRUD features based on the current user roles.
diff --git a/docs/data/toolpad/core/components/index.md b/docs/data/toolpad/core/components/index.md
index 413dcb65b20..90b83666295 100644
--- a/docs/data/toolpad/core/components/index.md
+++ b/docs/data/toolpad/core/components/index.md
@@ -3,3 +3,5 @@
This page contains an index to the component pages that come with Toolpad Core.
Line Chart component for Toolpad Core applications.
+
+Toolpad Core extends X Charts with data provider support. Toolpad Core charts automatically load data and adopt defaults for labels and formatting.
+
+## Basic example
+
+Add a data provider to a chart and its data is automatically loaded in the chart.
+
+{{"demo": "BasicLineChart.js"}}
+
+Error and loading states are automatically handled. Errors from the data provider are shown in an overlay:
+
+{{"demo": "ErrorLineChart.js", "hideToolbar": true}}
+
+## Customization
+
+The chart automatically adopts configuration from the data provider. When you pick a `dataKey` for an axis or a series, the chart automatically infers default values for series and axis options. The Toolpad Core components accept all properties that the X components offer. SO to customize the chart further, you can override these defaults and add extra options as you need.
+
+{{"demo": "CustomizedLineChart.js"}}
diff --git a/docs/data/toolpad/core/features/data-providers.md b/docs/data/toolpad/core/features/data-providers.md
new file mode 100644
index 00000000000..2cd2d962991
--- /dev/null
+++ b/docs/data/toolpad/core/features/data-providers.md
@@ -0,0 +1,57 @@
+# Data Providers
+
+
Connect with multiple data providers fast - no boilerplate or lengthy integration efforts
+
+## Interface
+
+A data provider is a stateless interface representing a collection of remote data. It mainly contains methods to fetch and manipulate this data, along with certain additional methods for certain data providers.
+
+```tsx
+import { createDataProvider } from '@toolpad/data';
+
+const dataProvider = createDataProvider({
+ async getRecords({}) {
+ return fetch('/...');
+ },
+});
+```
+
+## Connecting to components
+
+To connect to components, the stateless data provider can be made stateful using a headless API.
+
+```tsx
+const gridProps = useDataGrid(dataProvider, {
+ // options
+});
+
+const chartProps = useChart(dataProvider, {
+ // options
+});
+
+const sharedDataSource = useSharedDataProvider(dataProvider, {
+ // options
+});
+```
+
+## Server-side data providers
+
+```tsx
+// ./pages/api/myDataProvider.ts
+import { serverDataProvider } from '@toopad/core';
+import db from '../postgres';
+
+export default servedataProvider({
+ async getRecords({}) {
+ return db.getRows();
+ },
+});
+```
+
+Then connect to it from the client with
+
+```tsx
+import { createRestProvider } from '@toopad/core';
+
+const dataProvider = createRestProvider('/api/myDataProvider');
+```
diff --git a/docs/data/toolpad/core/introduction/Tutorial1.js b/docs/data/toolpad/core/introduction/Tutorial1.js
new file mode 100644
index 00000000000..a84726e5795
--- /dev/null
+++ b/docs/data/toolpad/core/introduction/Tutorial1.js
@@ -0,0 +1,31 @@
+import * as React from 'react';
+import { createDataProvider } from '@toolpad/core/DataProvider';
+import { DataGrid } from '@toolpad/core/DataGrid';
+import Stack from '@mui/material/Stack';
+
+const npmData = createDataProvider({
+ async getMany({ filter }) {
+ const res = await fetch(
+ `https://api.npmjs.org/downloads/range/${encodeURIComponent(filter.range?.equals ?? 'last-month')}/react`,
+ );
+ if (!res.ok) {
+ const { error } = await res.json();
+ throw new Error(`HTTP ${res.status}: ${error}`);
+ }
+ const { downloads } = await res.json();
+ return { rows: downloads };
+ },
+ idField: 'day',
+ fields: {
+ day: { type: 'date' },
+ downloads: { type: 'number' },
+ },
+});
+
+export default function Tutorial1() {
+ return (
+
+
+
+ );
+}
diff --git a/docs/data/toolpad/core/introduction/Tutorial1.tsx b/docs/data/toolpad/core/introduction/Tutorial1.tsx
new file mode 100644
index 00000000000..a84726e5795
--- /dev/null
+++ b/docs/data/toolpad/core/introduction/Tutorial1.tsx
@@ -0,0 +1,31 @@
+import * as React from 'react';
+import { createDataProvider } from '@toolpad/core/DataProvider';
+import { DataGrid } from '@toolpad/core/DataGrid';
+import Stack from '@mui/material/Stack';
+
+const npmData = createDataProvider({
+ async getMany({ filter }) {
+ const res = await fetch(
+ `https://api.npmjs.org/downloads/range/${encodeURIComponent(filter.range?.equals ?? 'last-month')}/react`,
+ );
+ if (!res.ok) {
+ const { error } = await res.json();
+ throw new Error(`HTTP ${res.status}: ${error}`);
+ }
+ const { downloads } = await res.json();
+ return { rows: downloads };
+ },
+ idField: 'day',
+ fields: {
+ day: { type: 'date' },
+ downloads: { type: 'number' },
+ },
+});
+
+export default function Tutorial1() {
+ return (
+
+
+
+ );
+}
diff --git a/docs/data/toolpad/core/introduction/Tutorial1.tsx.preview b/docs/data/toolpad/core/introduction/Tutorial1.tsx.preview
new file mode 100644
index 00000000000..6d9f8e338b6
--- /dev/null
+++ b/docs/data/toolpad/core/introduction/Tutorial1.tsx.preview
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/data/toolpad/core/introduction/Tutorial2.js b/docs/data/toolpad/core/introduction/Tutorial2.js
new file mode 100644
index 00000000000..63ad2737a29
--- /dev/null
+++ b/docs/data/toolpad/core/introduction/Tutorial2.js
@@ -0,0 +1,38 @@
+import * as React from 'react';
+import { createDataProvider } from '@toolpad/core/DataProvider';
+import { DataGrid } from '@toolpad/core/DataGrid';
+import { LineChart } from '@toolpad/core/LineChart';
+import Stack from '@mui/material/Stack';
+
+const npmData = createDataProvider({
+ async getMany({ filter }) {
+ const res = await fetch(
+ `https://api.npmjs.org/downloads/range/${encodeURIComponent(filter.range?.equals ?? 'last-month')}/react`,
+ );
+ if (!res.ok) {
+ const { error } = await res.json();
+ throw new Error(`HTTP ${res.status}: ${error}`);
+ }
+ const { downloads } = await res.json();
+ return { rows: downloads };
+ },
+ idField: 'day',
+ fields: {
+ day: { type: 'date' },
+ downloads: { type: 'number', label: 'Npm Downloads' },
+ },
+});
+
+export default function Tutorial2() {
+ return (
+
+
+
+
+ );
+}
diff --git a/docs/data/toolpad/core/introduction/Tutorial2.tsx b/docs/data/toolpad/core/introduction/Tutorial2.tsx
new file mode 100644
index 00000000000..63ad2737a29
--- /dev/null
+++ b/docs/data/toolpad/core/introduction/Tutorial2.tsx
@@ -0,0 +1,38 @@
+import * as React from 'react';
+import { createDataProvider } from '@toolpad/core/DataProvider';
+import { DataGrid } from '@toolpad/core/DataGrid';
+import { LineChart } from '@toolpad/core/LineChart';
+import Stack from '@mui/material/Stack';
+
+const npmData = createDataProvider({
+ async getMany({ filter }) {
+ const res = await fetch(
+ `https://api.npmjs.org/downloads/range/${encodeURIComponent(filter.range?.equals ?? 'last-month')}/react`,
+ );
+ if (!res.ok) {
+ const { error } = await res.json();
+ throw new Error(`HTTP ${res.status}: ${error}`);
+ }
+ const { downloads } = await res.json();
+ return { rows: downloads };
+ },
+ idField: 'day',
+ fields: {
+ day: { type: 'date' },
+ downloads: { type: 'number', label: 'Npm Downloads' },
+ },
+});
+
+export default function Tutorial2() {
+ return (
+
+
+
+
+ );
+}
diff --git a/docs/data/toolpad/core/introduction/Tutorial2.tsx.preview b/docs/data/toolpad/core/introduction/Tutorial2.tsx.preview
new file mode 100644
index 00000000000..263a8cfda3a
--- /dev/null
+++ b/docs/data/toolpad/core/introduction/Tutorial2.tsx.preview
@@ -0,0 +1,7 @@
+
+
\ No newline at end of file
diff --git a/docs/data/toolpad/core/introduction/Tutorial3.js b/docs/data/toolpad/core/introduction/Tutorial3.js
new file mode 100644
index 00000000000..b9b89115743
--- /dev/null
+++ b/docs/data/toolpad/core/introduction/Tutorial3.js
@@ -0,0 +1,57 @@
+import * as React from 'react';
+import { createDataProvider, DataContext } from '@toolpad/core/DataProvider';
+import { DataGrid } from '@toolpad/core/DataGrid';
+import { LineChart } from '@toolpad/core/LineChart';
+import Stack from '@mui/material/Stack';
+import TextField from '@mui/material/TextField';
+import MenuItem from '@mui/material/MenuItem';
+import { Toolbar } from '@mui/material';
+
+const npmData = createDataProvider({
+ async getMany({ filter }) {
+ const res = await fetch(
+ `https://api.npmjs.org/downloads/range/${encodeURIComponent(filter.range?.equals ?? 'last-month')}/react`,
+ );
+ if (!res.ok) {
+ const { error } = await res.json();
+ throw new Error(`HTTP ${res.status}: ${error}`);
+ }
+ const { downloads } = await res.json();
+ return { rows: downloads };
+ },
+ idField: 'day',
+ fields: {
+ day: { type: 'date' },
+ downloads: { type: 'number', label: 'Npm Downloads' },
+ },
+});
+
+export default function Tutorial3() {
+ const [range, setRange] = React.useState('last-month');
+ const filter = React.useMemo(() => ({ range: { equals: range } }), [range]);
+
+ return (
+
+
+
+ setRange(e.target.value)}
+ >
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/docs/data/toolpad/core/introduction/Tutorial3.tsx b/docs/data/toolpad/core/introduction/Tutorial3.tsx
new file mode 100644
index 00000000000..b9b89115743
--- /dev/null
+++ b/docs/data/toolpad/core/introduction/Tutorial3.tsx
@@ -0,0 +1,57 @@
+import * as React from 'react';
+import { createDataProvider, DataContext } from '@toolpad/core/DataProvider';
+import { DataGrid } from '@toolpad/core/DataGrid';
+import { LineChart } from '@toolpad/core/LineChart';
+import Stack from '@mui/material/Stack';
+import TextField from '@mui/material/TextField';
+import MenuItem from '@mui/material/MenuItem';
+import { Toolbar } from '@mui/material';
+
+const npmData = createDataProvider({
+ async getMany({ filter }) {
+ const res = await fetch(
+ `https://api.npmjs.org/downloads/range/${encodeURIComponent(filter.range?.equals ?? 'last-month')}/react`,
+ );
+ if (!res.ok) {
+ const { error } = await res.json();
+ throw new Error(`HTTP ${res.status}: ${error}`);
+ }
+ const { downloads } = await res.json();
+ return { rows: downloads };
+ },
+ idField: 'day',
+ fields: {
+ day: { type: 'date' },
+ downloads: { type: 'number', label: 'Npm Downloads' },
+ },
+});
+
+export default function Tutorial3() {
+ const [range, setRange] = React.useState('last-month');
+ const filter = React.useMemo(() => ({ range: { equals: range } }), [range]);
+
+ return (
+
+
+
+ setRange(e.target.value)}
+ >
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/docs/data/toolpad/core/introduction/overview.md b/docs/data/toolpad/core/introduction/overview.md
index 0d36e60e06a..9e103476ec5 100644
--- a/docs/data/toolpad/core/introduction/overview.md
+++ b/docs/data/toolpad/core/introduction/overview.md
@@ -25,6 +25,8 @@ The framework follows the open-core model, with some features being available un
- Built with and exclusively for React ⚛️
- High performance 🚀
- [Layout and Navigation](/toolpad/core/react-dashboard-layout/)
+- [Data Providers](/toolpad/core/features/data-providers/)
+- [Data Grid](/toolpad/core/react-data-grid/)
## Upcoming Features 🚧
@@ -36,5 +38,3 @@ Visit the [roadmap](/toolpad/core/introduction/roadmap/) to see more details aro
- [Dialogs and Notifications](/)
- [Role-based Access Control](/)
- [Audit Logs](/)
-- [Data Providers](/)
-- [Data Grid](/)
diff --git a/docs/data/toolpad/core/introduction/tutorial.md b/docs/data/toolpad/core/introduction/tutorial.md
index e007a4697ef..765a37077e4 100644
--- a/docs/data/toolpad/core/introduction/tutorial.md
+++ b/docs/data/toolpad/core/introduction/tutorial.md
@@ -103,3 +103,131 @@ const NAVIGATION: Navigation = [
The newly created page can now be navigated to from the sidebar, like the following:
{{"demo": "TutorialPages.js", "iframe": true, "hideToolbar": true }}
+
+## Dashboard content
+
+Now that your project is set up, it's time to build your first dashboard. This part of the tutorial takes you through building a small dashboard that allows monitoring npm downloads.
+
+### Connecting to a data source
+
+Toolpad Core comes with the concept of data providers. At its core, you could look at a data provider as an abstraction over a remote collection. At the very least, a data provider implements the `getMany` method and defines the fields it returns. The `getMany` method must return an object with a `rows` property:
+
+```js
+import { createDataProvider } from '@toolpad/core/DataProvider';
+
+const npmData = createDataProvider({
+ async getMany({ filter }) {
+ const res = await fetch(
+ `https://api.npmjs.org/downloads/range/${encodeURIComponent(filter.range?.equals ?? 'last-month')}/react`,
+ );
+ if (!res.ok) {
+ const { error } = await res.json();
+ throw new Error(`HTTP ${res.status}: ${error}`);
+ }
+ const { downloads } = await res.json();
+ return { rows: downloads };
+ },
+ idField: 'day',
+ fields: {
+ day: { type: 'date' },
+ downloads: { type: 'number' },
+ },
+});
+```
+
+This data provider calls the npm API and returns the downloads collection. It defines the two fields available in this collection, `day`, which we mark as the unique id field with the `idField` property and `downloads`. You can then visualize this data by connecting it to a grid:
+
+```js
+import { DataGrid } from '@toolpad/core';
+import { Stack } from '@mui/material';
+
+// ...
+
+export default function App() {
+ return (
+
+
+
+ );
+}
+```
+
+This results in the following output
+
+{{"demo": "Tutorial1.js", "hideToolbar": true}}
+
+You don't need to configure any columns, the grid infers them from the data provider. Any default that you define in the fields is adopted by any data rendering component that uses this data provider.
+
+### Sharing data providers
+
+Interesting things happen when you share data providers between different components. For instance, you can add a chart that uses the same data. Similar to the grid, this chart displays the same data as the grid. Under the hood the data fetching happens only once.
+
+```js
+// ...
+import { DataGrid, LineChart } from '@toolpad/core';
+
+// ...
+
+export default function App() {
+ return (
+
+
+
+
+ );
+}
+```
+
+The Toolpad Core components automatically adopt default values. For instance, if you add a `label` to the field, both the grid uses it in the column header, and the chart uses it in the legend:
+
+```js
+ // ...
+ fields: {
+ day: { type: 'date' },
+ downloads: { type: 'number', label: 'Npm Downloads' },
+ },
+ // ...
+```
+
+The result is the following:
+
+{{"demo": "Tutorial2.js", "hideToolbar": true}}
+
+### Global Filtering
+
+Wrap the dashboard with a `DataContext` to apply global filtering:
+
+```js
+const [range, setRange] = React.useState('last-month');
+const filter = React.useMemo(() => ({ range: { equals: range } }), [range]);
+
+// ...
+
+return (
+
+
+
+ setRange(e.target.value)}
+ >
+
+
+
+
+ {/* ... */}
+
+
+);
+```
+
+Any data provider that is used under this context now by default applies this filter.
+
+{{"demo": "Tutorial3.js", "hideToolbar": true}}
diff --git a/docs/data/toolpad/core/pages.ts b/docs/data/toolpad/core/pages.ts
index 1c6b3d68394..a3ecaeda98a 100644
--- a/docs/data/toolpad/core/pages.ts
+++ b/docs/data/toolpad/core/pages.ts
@@ -36,6 +36,16 @@ const pages: MuiPage[] = [
pathname: '/toolpad/core/introduction/support',
title: 'Support',
},
+ {
+ pathname: '/toolpad/core/features-group',
+ subheader: 'Features',
+ children: [
+ {
+ pathname: '/toolpad/core/features/data-providers',
+ title: 'Data Providers',
+ },
+ ],
+ },
],
},
{
@@ -63,6 +73,20 @@ const pages: MuiPage[] = [
},
],
},
+ {
+ pathname: '/toolpad/core/components/data-group',
+ subheader: 'Data',
+ children: [
+ {
+ pathname: '/toolpad/core/react-data-grid',
+ title: 'Data Grid',
+ },
+ {
+ pathname: '/toolpad/core/react-line-chart',
+ title: 'Line Chart',
+ },
+ ],
+ },
{
pathname: '/toolpad/core/components/filter-group',
subheader: 'Utils',
diff --git a/docs/data/toolpad/core/pagesApi.js b/docs/data/toolpad/core/pagesApi.js
index 3b1a64b542a..360899799b6 100644
--- a/docs/data/toolpad/core/pagesApi.js
+++ b/docs/data/toolpad/core/pagesApi.js
@@ -1,6 +1,8 @@
module.exports = [
{ pathname: '/toolpad/core/api/app-provider' },
{ pathname: '/toolpad/core/api/dashboard-layout' },
+ { pathname: '/toolpad/core/api/data-grid' },
{ pathname: '/toolpad/core/api/dialogs-provider' },
+ { pathname: '/toolpad/core/api/line-chart' },
{ pathname: '/toolpad/core/api/notifications-provider' },
];
diff --git a/docs/pages/toolpad/core/api/data-grid.js b/docs/pages/toolpad/core/api/data-grid.js
new file mode 100644
index 00000000000..0e9f5785156
--- /dev/null
+++ b/docs/pages/toolpad/core/api/data-grid.js
@@ -0,0 +1,23 @@
+import * as React from 'react';
+import ApiPage from 'docs/src/modules/components/ApiPage';
+import mapApiPageTranslations from 'docs/src/modules/utils/mapApiPageTranslations';
+import jsonPageContent from './data-grid.json';
+
+export default function Page(props) {
+ const { descriptions, pageContent } = props;
+ return ;
+}
+
+Page.getInitialProps = () => {
+ const req = require.context(
+ 'docs-toolpad/translations/api-docs/data-grid',
+ false,
+ /\.\/data-grid.*.json$/,
+ );
+ const descriptions = mapApiPageTranslations(req);
+
+ return {
+ descriptions,
+ pageContent: jsonPageContent,
+ };
+};
diff --git a/docs/pages/toolpad/core/api/data-grid.json b/docs/pages/toolpad/core/api/data-grid.json
new file mode 100644
index 00000000000..cf92ca6e998
--- /dev/null
+++ b/docs/pages/toolpad/core/api/data-grid.json
@@ -0,0 +1,864 @@
+{
+ "props": {
+ "dataProvider": {
+ "type": {
+ "name": "shape",
+ "description": "{ createOne?: func, deleteOne?: func, fields?: object, getMany: func, getOne?: func, idField?: object, updateOne?: func }"
+ }
+ },
+ "height": { "type": { "name": "number" } }
+ },
+ "name": "DataGrid",
+ "imports": [
+ "import { DataGrid } from '@toolpad-core/DataGrid';",
+ "import { DataGrid } from '@toolpad-core';"
+ ],
+ "classes": [
+ {
+ "key": "actionsCell",
+ "className": "",
+ "description": "Styles applied to the root element of the cell with type=\"actions\".",
+ "isGlobal": false
+ },
+ {
+ "key": "aggregationColumnHeader",
+ "className": "",
+ "description": "Styles applied to the root element of the column header when aggregated.",
+ "isGlobal": false
+ },
+ {
+ "key": "aggregationColumnHeader--alignCenter",
+ "className": "",
+ "description": "Styles applied to the root element of the header when aggregation if `headerAlign=\"center\"`.",
+ "isGlobal": false
+ },
+ {
+ "key": "aggregationColumnHeader--alignLeft",
+ "className": "",
+ "description": "Styles applied to the root element of the header when aggregation if `headerAlign=\"left\"`.",
+ "isGlobal": false
+ },
+ {
+ "key": "aggregationColumnHeader--alignRight",
+ "className": "",
+ "description": "Styles applied to the root element of the header when aggregation if `headerAlign=\"right\"`.",
+ "isGlobal": false
+ },
+ {
+ "key": "aggregationColumnHeaderLabel",
+ "className": "",
+ "description": "Styles applied to the aggregation label in the column header when aggregated.",
+ "isGlobal": false
+ },
+ {
+ "key": "autoHeight",
+ "className": "",
+ "description": "Styles applied to the root element if `autoHeight={true}`.",
+ "isGlobal": false
+ },
+ {
+ "key": "autosizing",
+ "className": "",
+ "description": "Styles applied to the root element while it is being autosized.",
+ "isGlobal": false
+ },
+ {
+ "key": "booleanCell",
+ "className": "",
+ "description": "Styles applied to the icon of the boolean cell.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell",
+ "className": "",
+ "description": "Styles applied to the cell element.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--editable",
+ "className": "",
+ "description": "Styles applied to the cell element if the cell is editable.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--editing",
+ "className": "",
+ "description": "Styles applied to the cell element if the cell is in edit mode.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--flex",
+ "className": "",
+ "description": "Styles applied to the cell element in flex display mode.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--pinnedLeft",
+ "className": "",
+ "description": "Styles applied to the cell element if it is pinned to the left.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--pinnedRight",
+ "className": "",
+ "description": "Styles applied to the cell element if it is pinned to the right.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--rangeBottom",
+ "className": "",
+ "description": "Styles applied to the cell element if it is at the bottom edge of a cell selection range.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--rangeLeft",
+ "className": "",
+ "description": "Styles applied to the cell element if it is at the left edge of a cell selection range.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--rangeRight",
+ "className": "",
+ "description": "Styles applied to the cell element if it is at the right edge of a cell selection range.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--rangeTop",
+ "className": "",
+ "description": "Styles applied to the cell element if it is at the top edge of a cell selection range.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--selectionMode",
+ "className": "",
+ "description": "Styles applied to the cell element if it is in a cell selection range.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--textCenter",
+ "className": "",
+ "description": "Styles applied to the cell element if `align=\"center\"`.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--textLeft",
+ "className": "",
+ "description": "Styles applied to the cell element if `align=\"left\"`.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--textRight",
+ "className": "",
+ "description": "Styles applied to the cell element if `align=\"right\"`.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--withLeftBorder",
+ "className": "",
+ "description": "Styles applied the cell if `showColumnVerticalBorder={true}`.",
+ "isGlobal": false
+ },
+ {
+ "key": "cell--withRightBorder",
+ "className": "",
+ "description": "Styles applied the cell if `showColumnVerticalBorder={true}`.",
+ "isGlobal": false
+ },
+ {
+ "key": "cellCheckbox",
+ "className": "",
+ "description": "Styles applied to the cell checkbox element.",
+ "isGlobal": false
+ },
+ {
+ "key": "cellEmpty",
+ "className": "",
+ "description": "Styles applied to the empty cell element.",
+ "isGlobal": false
+ },
+ {
+ "key": "cellSkeleton",
+ "className": "",
+ "description": "Styles applied to the skeleton cell element.",
+ "isGlobal": false
+ },
+ {
+ "key": "checkboxInput",
+ "className": "",
+ "description": "Styles applied to the selection checkbox element.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader",
+ "className": "",
+ "description": "Styles applied to the column header element.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader--alignCenter",
+ "className": "",
+ "description": "Styles applied to the column header if `headerAlign=\"center\"`.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader--alignLeft",
+ "className": "",
+ "description": "Styles applied to the column header if `headerAlign=\"left\"`.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader--alignRight",
+ "className": "",
+ "description": "Styles applied to the column header if `headerAlign=\"right\"`.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader--dragging",
+ "className": "",
+ "description": "Styles applied to the floating column header element when it is dragged.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader--emptyGroup",
+ "className": "",
+ "description": "Styles applied to the empty column group header cell.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader--filledGroup",
+ "className": "",
+ "description": "Styles applied to the column group header cell if not empty.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader--filtered",
+ "className": "",
+ "description": "Styles applied to the column header if the column has a filter applied to it.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader--last",
+ "className": "",
+ "description": "Styles applied to the last column header element.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader--moving",
+ "className": "",
+ "description": "Styles applied to the column header if it is being dragged.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader--numeric",
+ "className": "",
+ "description": "Styles applied to the column header if the type of the column is `number`.",
+ "isGlobal": false
+ },
+ { "key": "columnHeader--pinnedLeft", "className": "", "description": "", "isGlobal": false },
+ { "key": "columnHeader--pinnedRight", "className": "", "description": "", "isGlobal": false },
+ {
+ "key": "columnHeader--sortable",
+ "className": "",
+ "description": "Styles applied to the column header if the column is sortable.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader--sorted",
+ "className": "",
+ "description": "Styles applied to the column header if the column is sorted.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader--withLeftBorder",
+ "className": "",
+ "description": "",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeader--withRightBorder",
+ "className": "",
+ "description": "Styles applied the column header if `showColumnVerticalBorder={true}`.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeaderCheckbox",
+ "className": "",
+ "description": "Styles applied to the header checkbox cell element.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeaderDraggableContainer",
+ "className": "",
+ "description": "Styles applied to the column header's draggable container element.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeaders",
+ "className": "",
+ "description": "Styles applied to the column headers.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeaderTitle",
+ "className": "",
+ "description": "Styles applied to the column header's title element;",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeaderTitleContainer",
+ "className": "",
+ "description": "Styles applied to the column header's title container element.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnHeaderTitleContainerContent",
+ "className": "",
+ "description": "Styles applied to the column header's title excepted buttons.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnSeparator",
+ "className": "",
+ "description": "Styles applied to the column header separator element.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnSeparator--resizable",
+ "className": "",
+ "description": "Styles applied to the column header separator if the column is resizable.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnSeparator--resizing",
+ "className": "",
+ "description": "Styles applied to the column header separator if the column is being resized.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnSeparator--sideLeft",
+ "className": "",
+ "description": "Styles applied to the column header separator if the side is \"left\".",
+ "isGlobal": false
+ },
+ {
+ "key": "columnSeparator--sideRight",
+ "className": "",
+ "description": "Styles applied to the column header separator if the side is \"right\".",
+ "isGlobal": false
+ },
+ {
+ "key": "columnsManagement",
+ "className": "",
+ "description": "Styles applied to the columns management body.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnsManagementFooter",
+ "className": "",
+ "description": "Styles applied to the columns management footer element.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnsManagementHeader",
+ "className": "",
+ "description": "Styles applied to the columns management header element.",
+ "isGlobal": false
+ },
+ {
+ "key": "columnsManagementRow",
+ "className": "",
+ "description": "Styles applied to the columns management row element.",
+ "isGlobal": false
+ },
+ {
+ "key": "container--bottom",
+ "className": "",
+ "description": "Styles applied to the bottom container.",
+ "isGlobal": false
+ },
+ {
+ "key": "container--top",
+ "className": "",
+ "description": "Styles applied to the top container.",
+ "isGlobal": false
+ },
+ {
+ "key": "detailPanel",
+ "className": "",
+ "description": "Styles applied to the detail panel element.",
+ "isGlobal": false
+ },
+ {
+ "key": "detailPanels",
+ "className": "",
+ "description": "Styles applied to the detail panels wrapper element.",
+ "isGlobal": false
+ },
+ {
+ "key": "detailPanelToggleCell",
+ "className": "",
+ "description": "Styles applied to the detail panel toggle cell element.",
+ "isGlobal": false
+ },
+ {
+ "key": "detailPanelToggleCell--expanded",
+ "className": "",
+ "description": "Styles applied to the detail panel toggle cell element if expanded.",
+ "isGlobal": false
+ },
+ {
+ "key": "editBooleanCell",
+ "className": "",
+ "description": "Styles applied to root of the boolean edit component.",
+ "isGlobal": false
+ },
+ {
+ "key": "editInputCell",
+ "className": "",
+ "description": "Styles applied to the root of the input component.",
+ "isGlobal": false
+ },
+ {
+ "key": "filterForm",
+ "className": "",
+ "description": "Styles applied to the root of the filter form component.",
+ "isGlobal": false
+ },
+ {
+ "key": "filterFormColumnInput",
+ "className": "",
+ "description": "Styles applied to the column input of the filter form component.",
+ "isGlobal": false
+ },
+ {
+ "key": "filterFormDeleteIcon",
+ "className": "",
+ "description": "Styles applied to the delete icon of the filter form component.",
+ "isGlobal": false
+ },
+ {
+ "key": "filterFormLogicOperatorInput",
+ "className": "",
+ "description": "Styles applied to the link operator input of the filter form component.",
+ "isGlobal": false
+ },
+ {
+ "key": "filterFormOperatorInput",
+ "className": "",
+ "description": "Styles applied to the operator input of the filter form component.",
+ "isGlobal": false
+ },
+ {
+ "key": "filterFormValueInput",
+ "className": "",
+ "description": "Styles applied to the value input of the filter form component.",
+ "isGlobal": false
+ },
+ {
+ "key": "filterIcon",
+ "className": "",
+ "description": "Styles applied to the filter icon element.",
+ "isGlobal": false
+ },
+ {
+ "key": "footerCell",
+ "className": "",
+ "description": "Styles applied to the root element of the cell inside a footer row.",
+ "isGlobal": false
+ },
+ {
+ "key": "footerContainer",
+ "className": "",
+ "description": "Styles applied to the footer container element.",
+ "isGlobal": false
+ },
+ {
+ "key": "groupingCriteriaCell",
+ "className": "",
+ "description": "Styles applied to the root element of the grouping criteria cell",
+ "isGlobal": false
+ },
+ {
+ "key": "groupingCriteriaCellToggle",
+ "className": "",
+ "description": "Styles applied to the toggle of the grouping criteria cell",
+ "isGlobal": false
+ },
+ {
+ "key": "headerFilterRow",
+ "className": "",
+ "description": "Styles applied to the column header filter row.",
+ "isGlobal": false
+ },
+ {
+ "key": "iconButtonContainer",
+ "className": "",
+ "description": "Styles applied to the column header icon's container.",
+ "isGlobal": false
+ },
+ {
+ "key": "iconSeparator",
+ "className": "",
+ "description": "Styles applied to the column header separator icon element.",
+ "isGlobal": false
+ },
+ {
+ "key": "main",
+ "className": "",
+ "description": "Styles applied to the main container element.",
+ "isGlobal": false
+ },
+ {
+ "key": "main--hasPinnedRight",
+ "className": "",
+ "description": "Styles applied to the main container element when it has right pinned columns.",
+ "isGlobal": false
+ },
+ {
+ "key": "menu",
+ "className": "",
+ "description": "Styles applied to the menu element.",
+ "isGlobal": false
+ },
+ {
+ "key": "menuIcon",
+ "className": "",
+ "description": "Styles applied to the menu icon element.",
+ "isGlobal": false
+ },
+ {
+ "key": "menuIconButton",
+ "className": "",
+ "description": "Styles applied to the menu icon button element.",
+ "isGlobal": false
+ },
+ {
+ "key": "menuList",
+ "className": "",
+ "description": "Styles applied to the menu list element.",
+ "isGlobal": false
+ },
+ {
+ "key": "menuOpen",
+ "className": "",
+ "description": "Styles applied to the menu icon element if the menu is open.",
+ "isGlobal": false
+ },
+ {
+ "key": "overlay",
+ "className": "",
+ "description": "Styles applied to the overlay element.",
+ "isGlobal": false
+ },
+ {
+ "key": "overlayWrapper",
+ "className": "",
+ "description": "Styles applied to the overlay wrapper element.",
+ "isGlobal": false
+ },
+ {
+ "key": "overlayWrapperInner",
+ "className": "",
+ "description": "Styles applied to the overlay wrapper inner element.",
+ "isGlobal": false
+ },
+ {
+ "key": "panel",
+ "className": "",
+ "description": "Styles applied to the panel element.",
+ "isGlobal": false
+ },
+ {
+ "key": "panelContent",
+ "className": "",
+ "description": "Styles applied to the panel content element.",
+ "isGlobal": false
+ },
+ {
+ "key": "panelFooter",
+ "className": "",
+ "description": "Styles applied to the panel footer element.",
+ "isGlobal": false
+ },
+ {
+ "key": "panelHeader",
+ "className": "",
+ "description": "Styles applied to the panel header element.",
+ "isGlobal": false
+ },
+ {
+ "key": "panelWrapper",
+ "className": "",
+ "description": "Styles applied to the panel wrapper element.",
+ "isGlobal": false
+ },
+ {
+ "key": "paper",
+ "className": "",
+ "description": "Styles applied to the paper element.",
+ "isGlobal": false
+ },
+ {
+ "key": "pinnedColumns",
+ "className": "",
+ "description": "Styles applied to the pinned columns.",
+ "isGlobal": false
+ },
+ {
+ "key": "pinnedRows",
+ "className": "",
+ "description": "Styles applied to the pinned rows container.",
+ "isGlobal": false
+ },
+ {
+ "key": "pinnedRows--bottom",
+ "className": "",
+ "description": "Styles applied to the bottom pinned rows container.",
+ "isGlobal": false
+ },
+ {
+ "key": "pinnedRows--top",
+ "className": "",
+ "description": "Styles applied to the top pinned rows container.",
+ "isGlobal": false
+ },
+ {
+ "key": "pinnedRowsRenderZone",
+ "className": "",
+ "description": "Styles applied to pinned rows render zones.",
+ "isGlobal": false
+ },
+ {
+ "key": "root",
+ "className": "",
+ "description": "Styles applied to the root element.",
+ "isGlobal": false
+ },
+ {
+ "key": "root--densityComfortable",
+ "className": "",
+ "description": "Styles applied to the root element if density is \"comfortable\".",
+ "isGlobal": false
+ },
+ {
+ "key": "root--densityCompact",
+ "className": "",
+ "description": "Styles applied to the root element if density is \"compact\".",
+ "isGlobal": false
+ },
+ {
+ "key": "root--densityStandard",
+ "className": "",
+ "description": "Styles applied to the root element if density is \"standard\" (default).",
+ "isGlobal": false
+ },
+ {
+ "key": "root--disableUserSelection",
+ "className": "",
+ "description": "Styles applied to the root element when user selection is disabled.",
+ "isGlobal": false
+ },
+ {
+ "key": "row",
+ "className": "",
+ "description": "Styles applied to the row element.",
+ "isGlobal": false
+ },
+ {
+ "key": "row--detailPanelExpanded",
+ "className": "",
+ "description": "Styles applied to the row if its detail panel is open.",
+ "isGlobal": false
+ },
+ {
+ "key": "row--dragging",
+ "className": "",
+ "description": "Styles applied to the floating special row reorder cell element when it is dragged.",
+ "isGlobal": false
+ },
+ {
+ "key": "row--dynamicHeight",
+ "className": "",
+ "description": "Styles applied to the row if it has dynamic row height.",
+ "isGlobal": false
+ },
+ {
+ "key": "row--editable",
+ "className": "",
+ "description": "Styles applied to the row element if the row is editable.",
+ "isGlobal": false
+ },
+ {
+ "key": "row--editing",
+ "className": "",
+ "description": "Styles applied to the row element if the row is in edit mode.",
+ "isGlobal": false
+ },
+ {
+ "key": "row--firstVisible",
+ "className": "",
+ "description": "Styles applied to the first visible row element on every page of the grid.",
+ "isGlobal": false
+ },
+ {
+ "key": "row--lastVisible",
+ "className": "",
+ "description": "Styles applied to the last visible row element on every page of the grid.",
+ "isGlobal": false
+ },
+ {
+ "key": "rowCount",
+ "className": "",
+ "description": "Styles applied to the footer row count element to show the total number of rows.\nOnly works when pagination is disabled.",
+ "isGlobal": false
+ },
+ {
+ "key": "rowReorderCell",
+ "className": "",
+ "description": "Styles applied to the root element of the row reorder cell",
+ "isGlobal": false
+ },
+ {
+ "key": "rowReorderCell--draggable",
+ "className": "",
+ "description": "Styles applied to the root element of the row reorder cell when dragging is allowed",
+ "isGlobal": false
+ },
+ {
+ "key": "rowReorderCellContainer",
+ "className": "",
+ "description": "Styles applied to the row reorder cell container element.",
+ "isGlobal": false
+ },
+ {
+ "key": "rowReorderCellPlaceholder",
+ "className": "",
+ "description": "Styles applied to the row's draggable placeholder element inside the special row reorder cell.",
+ "isGlobal": false
+ },
+ {
+ "key": "rowSkeleton",
+ "className": "",
+ "description": "Styles applied to the skeleton row element.",
+ "isGlobal": false
+ },
+ {
+ "key": "scrollArea",
+ "className": "",
+ "description": "Styles applied to both scroll area elements.",
+ "isGlobal": false
+ },
+ {
+ "key": "scrollArea--left",
+ "className": "",
+ "description": "Styles applied to the left scroll area element.",
+ "isGlobal": false
+ },
+ {
+ "key": "scrollArea--right",
+ "className": "",
+ "description": "Styles applied to the right scroll area element.",
+ "isGlobal": false
+ },
+ {
+ "key": "scrollbar",
+ "className": "",
+ "description": "Styles applied to the scrollbars.",
+ "isGlobal": false
+ },
+ {
+ "key": "scrollbar--horizontal",
+ "className": "",
+ "description": "Styles applied to the horizontal scrollbar.",
+ "isGlobal": false
+ },
+ {
+ "key": "scrollbar--vertical",
+ "className": "",
+ "description": "Styles applied to the horizontal scrollbar.",
+ "isGlobal": false
+ },
+ {
+ "key": "selectedRowCount",
+ "className": "",
+ "description": "Styles applied to the footer selected row count element.",
+ "isGlobal": false
+ },
+ {
+ "key": "sortIcon",
+ "className": "",
+ "description": "Styles applied to the sort icon element.",
+ "isGlobal": false
+ },
+ {
+ "key": "toolbarContainer",
+ "className": "",
+ "description": "Styles applied to the toolbar container element.",
+ "isGlobal": false
+ },
+ {
+ "key": "toolbarFilterList",
+ "className": "",
+ "description": "Styles applied to the toolbar filter list element.",
+ "isGlobal": false
+ },
+ {
+ "key": "treeDataGroupingCell",
+ "className": "",
+ "description": "Styles applied to the root of the grouping column of the tree data.",
+ "isGlobal": false
+ },
+ {
+ "key": "treeDataGroupingCellToggle",
+ "className": "",
+ "description": "Styles applied to the toggle of the grouping cell of the tree data.",
+ "isGlobal": false
+ },
+ {
+ "key": "virtualScroller",
+ "className": "",
+ "description": "Styles applied to the virtualization container.",
+ "isGlobal": false
+ },
+ {
+ "key": "virtualScrollerContent",
+ "className": "",
+ "description": "Styles applied to the virtualization content.",
+ "isGlobal": false
+ },
+ {
+ "key": "virtualScrollerContent--overflowed",
+ "className": "",
+ "description": "Styles applied to the virtualization content when its height is bigger than the virtualization container.",
+ "isGlobal": false
+ },
+ {
+ "key": "virtualScrollerRenderZone",
+ "className": "",
+ "description": "Styles applied to the virtualization render zone.",
+ "isGlobal": false
+ },
+ {
+ "key": "withBorderColor",
+ "className": "",
+ "description": "Styles applied to cells, column header and other elements that have border.\nSets border color only.",
+ "isGlobal": false
+ },
+ {
+ "key": "withVerticalBorder",
+ "className": "",
+ "description": "Styles applied the grid if `showColumnVerticalBorder={true}`.",
+ "isGlobal": false
+ }
+ ],
+ "spread": true,
+ "themeDefaultProps": false,
+ "muiName": "DataGrid",
+ "filename": "/packages/toolpad-core/src/DataGrid/DataGrid.tsx",
+ "inheritance": {
+ "component": "X DataGrid",
+ "pathname": "https://mui.com/x/api/data-grid/data-grid/"
+ },
+ "demos": "