diff --git a/.eslintrc.js b/.eslintrc.js index fac05b6d0e35..c9bfa88a5df2 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -292,6 +292,7 @@ module.exports = { '!@mui/internal-*/**', // Exceptions (QUESTION: Keep or remove?) + '!@mui/x-data-grid/internals/demo', '!@mui/x-date-pickers/internals/demo', '!@mui/x-tree-view/hooks/useTreeViewApiRef', // TODO: export this from /ButtonBase in core. This will break after we move to package exports diff --git a/docs/data/data-grid/components/columns-panel/GridColumnsPanelTrigger.js b/docs/data/data-grid/components/columns-panel/GridColumnsPanelTrigger.js new file mode 100644 index 000000000000..2327648fab76 --- /dev/null +++ b/docs/data/data-grid/components/columns-panel/GridColumnsPanelTrigger.js @@ -0,0 +1,29 @@ +import * as React from 'react'; +import { DataGrid, Grid } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import ViewColumnIcon from '@mui/icons-material/ViewColumn'; + +function Toolbar() { + return ( + + }> + + Columns + + + ); +} + +export default function GridColumnsPanelTrigger() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 10, + maxColumns: 10, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/components/columns-panel/GridColumnsPanelTrigger.tsx b/docs/data/data-grid/components/columns-panel/GridColumnsPanelTrigger.tsx new file mode 100644 index 000000000000..2327648fab76 --- /dev/null +++ b/docs/data/data-grid/components/columns-panel/GridColumnsPanelTrigger.tsx @@ -0,0 +1,29 @@ +import * as React from 'react'; +import { DataGrid, Grid } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import ViewColumnIcon from '@mui/icons-material/ViewColumn'; + +function Toolbar() { + return ( + + }> + + Columns + + + ); +} + +export default function GridColumnsPanelTrigger() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 10, + maxColumns: 10, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/components/columns-panel/GridColumnsPanelTrigger.tsx.preview b/docs/data/data-grid/components/columns-panel/GridColumnsPanelTrigger.tsx.preview new file mode 100644 index 000000000000..6e44e64d1dbc --- /dev/null +++ b/docs/data/data-grid/components/columns-panel/GridColumnsPanelTrigger.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/components/columns-panel/columns-panel.md b/docs/data/data-grid/components/columns-panel/columns-panel.md new file mode 100644 index 000000000000..ec5217e72eff --- /dev/null +++ b/docs/data/data-grid/components/columns-panel/columns-panel.md @@ -0,0 +1,39 @@ +# Data Grid - Columns Panel component 🚧 + +

Customize the columns panel UI.

+ +:::warning +This component is incomplete. + +Currently, the only feature available for the Columns Panel component is the Trigger. In the future, this component will allow you to extend the data grid's columns panel. +::: + +## Anatomy + +The `Grid.ColumnsPanel` component is comprised of the following parts. + +```tsx + +``` + +### Trigger + +A button that opens the columns panel. + +Renders a [Button](/material-ui/react-button/) component or a custom button provided to `slots.baseButton`. + +## Examples + +Below are some ways the Columns Panel component can be used. + +### Toolbar columns panel trigger + +Toggle the visibility of the columns panel. + +{{"demo": "GridColumnsPanelTrigger.js", "bg": "inline"}} + +## API + +- [Grid](/x/api/data-grid/data-grid/) +- [GridColumnsPanel](/x/api/data-grid/data-grid/) +- [GridColumnsPanelTrigger](/x/api/data-grid/data-grid/) diff --git a/docs/data/data-grid/components/export/GridExportMenu.js b/docs/data/data-grid/components/export/GridExportMenu.js new file mode 100644 index 000000000000..a6520c24036c --- /dev/null +++ b/docs/data/data-grid/components/export/GridExportMenu.js @@ -0,0 +1,67 @@ +import * as React from 'react'; +import { DataGrid, Grid } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import Menu from '@mui/material/Menu'; +import MenuItem from '@mui/material/MenuItem'; +import FileDownloadIcon from '@mui/icons-material/FileDownload'; +import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; + +function ExportMenu() { + const [open, setOpen] = React.useState(false); + const triggerRef = React.useRef(null); + + return ( + + setOpen(true)} + > + + Export + + + setOpen(false)} + MenuListProps={{ + 'aria-labelledby': 'export-menu-trigger', + }} + > + }> + Download as CSV + + }> + Print + + + + ); +} + +function Toolbar() { + return ( + + + + ); +} + +export default function GridExportMenu() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 10, + maxColumns: 10, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/components/export/GridExportMenu.tsx b/docs/data/data-grid/components/export/GridExportMenu.tsx new file mode 100644 index 000000000000..f3deba6ea47c --- /dev/null +++ b/docs/data/data-grid/components/export/GridExportMenu.tsx @@ -0,0 +1,67 @@ +import * as React from 'react'; +import { DataGrid, Grid } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import Menu from '@mui/material/Menu'; +import MenuItem from '@mui/material/MenuItem'; +import FileDownloadIcon from '@mui/icons-material/FileDownload'; +import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; + +function ExportMenu() { + const [open, setOpen] = React.useState(false); + const triggerRef = React.useRef(null); + + return ( + + setOpen(true)} + > + + Export + + + setOpen(false)} + MenuListProps={{ + 'aria-labelledby': 'export-menu-trigger', + }} + > + }> + Download as CSV + + }> + Print + + + + ); +} + +function Toolbar() { + return ( + + + + ); +} + +export default function GridExportMenu() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 10, + maxColumns: 10, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/components/export/GridExportMenu.tsx.preview b/docs/data/data-grid/components/export/GridExportMenu.tsx.preview new file mode 100644 index 000000000000..6e44e64d1dbc --- /dev/null +++ b/docs/data/data-grid/components/export/GridExportMenu.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/components/export/GridExportTrigger.js b/docs/data/data-grid/components/export/GridExportTrigger.js new file mode 100644 index 000000000000..91d62b0ddeeb --- /dev/null +++ b/docs/data/data-grid/components/export/GridExportTrigger.js @@ -0,0 +1,34 @@ +import * as React from 'react'; +import { DataGrid, Grid } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import FileDownloadIcon from '@mui/icons-material/FileDownload'; +import PrintIcon from '@mui/icons-material/Print'; + +function Toolbar() { + return ( + + }> + + CSV + + }> + + Print + + + ); +} + +export default function GridExportTrigger() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 10, + maxColumns: 10, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/components/export/GridExportTrigger.tsx b/docs/data/data-grid/components/export/GridExportTrigger.tsx new file mode 100644 index 000000000000..91d62b0ddeeb --- /dev/null +++ b/docs/data/data-grid/components/export/GridExportTrigger.tsx @@ -0,0 +1,34 @@ +import * as React from 'react'; +import { DataGrid, Grid } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import FileDownloadIcon from '@mui/icons-material/FileDownload'; +import PrintIcon from '@mui/icons-material/Print'; + +function Toolbar() { + return ( + + }> + + CSV + + }> + + Print + + + ); +} + +export default function GridExportTrigger() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 10, + maxColumns: 10, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/components/export/GridExportTrigger.tsx.preview b/docs/data/data-grid/components/export/GridExportTrigger.tsx.preview new file mode 100644 index 000000000000..6e44e64d1dbc --- /dev/null +++ b/docs/data/data-grid/components/export/GridExportTrigger.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/components/export/export.md b/docs/data/data-grid/components/export/export.md new file mode 100644 index 000000000000..6a1470713aef --- /dev/null +++ b/docs/data/data-grid/components/export/export.md @@ -0,0 +1,39 @@ +# Data Grid - Export component + +

Components to trigger exports of the Data Grid.

+ +## Anatomy + +The `Grid.Export` component is comprised of the following parts. + +```tsx + +``` + +### Trigger + +A button that triggers an export. + +The `exportType` prop can be set to `print`, `csv` or `excel`[](/x/introduction/licensing/#premium-plan 'Premium plan'). + +## Examples + +Below are some ways the Export component can be used. + +### Toolbar export buttons + +Display export options as buttons on the toolbar. + +{{"demo": "GridExportTrigger.js", "bg": "inline"}} + +### Toolbar export menu + +Display export options within a menu on the toolbar. + +{{"demo": "GridExportMenu.js", "bg": "inline"}} + +## API + +- [Grid](/x/api/data-grid/data-grid/) +- [GridExport](/x/api/data-grid/data-grid/) +- [GridExportTrigger](/x/api/data-grid/data-grid/) diff --git a/docs/data/data-grid/components/filter-panel/GridFilterPanelTrigger.js b/docs/data/data-grid/components/filter-panel/GridFilterPanelTrigger.js new file mode 100644 index 000000000000..116f3e09b8a1 --- /dev/null +++ b/docs/data/data-grid/components/filter-panel/GridFilterPanelTrigger.js @@ -0,0 +1,29 @@ +import * as React from 'react'; +import { DataGrid, Grid } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import FilterListIcon from '@mui/icons-material/FilterList'; + +function Toolbar() { + return ( + + }> + + Filters + + + ); +} + +export default function GridFilterPanelTrigger() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 10, + maxColumns: 10, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/components/filter-panel/GridFilterPanelTrigger.tsx b/docs/data/data-grid/components/filter-panel/GridFilterPanelTrigger.tsx new file mode 100644 index 000000000000..116f3e09b8a1 --- /dev/null +++ b/docs/data/data-grid/components/filter-panel/GridFilterPanelTrigger.tsx @@ -0,0 +1,29 @@ +import * as React from 'react'; +import { DataGrid, Grid } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import FilterListIcon from '@mui/icons-material/FilterList'; + +function Toolbar() { + return ( + + }> + + Filters + + + ); +} + +export default function GridFilterPanelTrigger() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 10, + maxColumns: 10, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/components/filter-panel/GridFilterPanelTrigger.tsx.preview b/docs/data/data-grid/components/filter-panel/GridFilterPanelTrigger.tsx.preview new file mode 100644 index 000000000000..6e44e64d1dbc --- /dev/null +++ b/docs/data/data-grid/components/filter-panel/GridFilterPanelTrigger.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/components/filter-panel/filter-panel.md b/docs/data/data-grid/components/filter-panel/filter-panel.md new file mode 100644 index 000000000000..38b45f3aafd2 --- /dev/null +++ b/docs/data/data-grid/components/filter-panel/filter-panel.md @@ -0,0 +1,39 @@ +# Data Grid - Filter Panel component 🚧 + +

Customize the filter panel UI.

+ +:::warning +This component is incomplete. + +Currently, the only feature available for the Filter Panel component is the Trigger. In the future, this component will allow you to extend the data grid's filter panel. +::: + +## Anatomy + +The `Grid.FilterPanel` component is comprised of the following parts. + +```tsx + +``` + +### Trigger + +A button that opens the filter panel. + +Renders a [Button](/material-ui/react-button/) component or a custom button provided to `slots.baseButton`. + +## Examples + +Below are some ways the Filter Panel component can be used. + +### Toolbar filter panel trigger + +Toggle the visibility of the filter panel. + +{{"demo": "GridFilterPanelTrigger.js", "bg": "inline"}} + +## API + +- [Grid](/x/api/data-grid/data-grid/) +- [GridFilterPanel](/x/api/data-grid/data-grid/) +- [GridFilterPanelTrigger](/x/api/data-grid/data-grid/) diff --git a/docs/data/data-grid/components/overview.md b/docs/data/data-grid/components/overview.md new file mode 100644 index 000000000000..f3a35a98be80 --- /dev/null +++ b/docs/data/data-grid/components/overview.md @@ -0,0 +1,80 @@ +# Data Grid - Components + +

Data Grid components provide a way to extend the default UI via composable parts.

+ +## Introduction + +The `Grid` component is available to import from the same package as the `DataGrid` component. It consists of many parts that can be used in combination with [slots](/x/react-data-grid/components/) to extend the Data Grid. + +```tsx +import { DataGrid, Grid } from '@mui/x-data-grid'; + +function Toolbar() { + return ( + + }> + Filters + + + ); +} + +function App() { + return ; +} +``` + +## Customization + +The grid components are highly customizable, built to integrate with components from any design system, and any styling method. + +### className + +The `className` prop can be used to apply styles to grid components: + +```tsx + +``` + +Some grid components also provide internal state that can be used to conditionally apply classes: + +```tsx + (state.open ? 'text-blue-600' : 'text-gray-900')} +/> +``` + +### render + +The `render` prop can be used to override the element rendered by each grid component: + +```tsx +} /> +``` + +A function can also be passed to the `render` prop, giving you control over the props that are forwarded to the custom element: + +```tsx + } +/> +``` + +Some grid components also provide internal state that can be used to control what is returned by the `render` function: + +```tsx + ( + + {state.open ? 'Close filter panel' : 'Open filter panel'} + + )} +/> +``` + +## Components + +- [Toolbar](/x/react-data-grid/components/toolbar/) +- [Columns Panel](/x/react-data-grid/components/columns-panel/) +- [Filter Panel](/x/react-data-grid/components/filter-panel/) +- [Export](/x/react-data-grid/components/export/) diff --git a/docs/data/data-grid/components/toolbar/GridToolbar.js b/docs/data/data-grid/components/toolbar/GridToolbar.js new file mode 100644 index 000000000000..651b6066cc72 --- /dev/null +++ b/docs/data/data-grid/components/toolbar/GridToolbar.js @@ -0,0 +1,60 @@ +import * as React from 'react'; +import { DemoContainer } from '@mui/x-data-grid/internals/demo'; +import { Grid, GridToolbarQuickFilter } from '@mui/x-data-grid'; +import ViewColumnIcon from '@mui/icons-material/ViewColumn'; +import FilterListIcon from '@mui/icons-material/FilterList'; +import PrintIcon from '@mui/icons-material/Print'; +import FileDownloadIcon from '@mui/icons-material/FileDownload'; +import GridViewIcon from '@mui/icons-material/ViewModule'; +import ListViewIcon from '@mui/icons-material/ViewList'; + +export default function GridToolbar() { + const [view, setView] = React.useState('grid'); + return ( + + + + + + + + + + + + + + + + + + + + + + setView('grid')} + > + + + setView('list')} + > + + + + + + ); +} + +const demoStyles = { + border: '1px solid', + borderColor: 'divider', + borderRadius: 1, + width: '100%', +}; diff --git a/docs/data/data-grid/components/toolbar/GridToolbar.tsx b/docs/data/data-grid/components/toolbar/GridToolbar.tsx new file mode 100644 index 000000000000..20f738afba86 --- /dev/null +++ b/docs/data/data-grid/components/toolbar/GridToolbar.tsx @@ -0,0 +1,62 @@ +import * as React from 'react'; +import { DemoContainer } from '@mui/x-data-grid/internals/demo'; +import { Grid, GridToolbarQuickFilter } from '@mui/x-data-grid'; +import ViewColumnIcon from '@mui/icons-material/ViewColumn'; +import FilterListIcon from '@mui/icons-material/FilterList'; +import PrintIcon from '@mui/icons-material/Print'; +import FileDownloadIcon from '@mui/icons-material/FileDownload'; +import GridViewIcon from '@mui/icons-material/ViewModule'; +import ListViewIcon from '@mui/icons-material/ViewList'; + +export default function GridToolbar() { + const [view, setView] = React.useState<'grid' | 'list'>('grid'); + return ( + + + + + + + + + + + + + + + + + + + + + + + + setView('grid')} + > + + + setView('list')} + > + + + + + + ); +} + +const demoStyles = { + border: '1px solid', + borderColor: 'divider', + borderRadius: 1, + width: '100%', +}; diff --git a/docs/data/data-grid/components/toolbar/GridToolbarButton.js b/docs/data/data-grid/components/toolbar/GridToolbarButton.js new file mode 100644 index 000000000000..f803dcdc658a --- /dev/null +++ b/docs/data/data-grid/components/toolbar/GridToolbarButton.js @@ -0,0 +1,14 @@ +import * as React from 'react'; +import { DemoContainer } from '@mui/x-data-grid/internals/demo'; +import { Grid } from '@mui/x-data-grid'; +import PrintIcon from '@mui/icons-material/Print'; + +export default function GridToolbarButton() { + return ( + + + Print + + + ); +} diff --git a/docs/data/data-grid/components/toolbar/GridToolbarButton.tsx b/docs/data/data-grid/components/toolbar/GridToolbarButton.tsx new file mode 100644 index 000000000000..f803dcdc658a --- /dev/null +++ b/docs/data/data-grid/components/toolbar/GridToolbarButton.tsx @@ -0,0 +1,14 @@ +import * as React from 'react'; +import { DemoContainer } from '@mui/x-data-grid/internals/demo'; +import { Grid } from '@mui/x-data-grid'; +import PrintIcon from '@mui/icons-material/Print'; + +export default function GridToolbarButton() { + return ( + + + Print + + + ); +} diff --git a/docs/data/data-grid/components/toolbar/GridToolbarButton.tsx.preview b/docs/data/data-grid/components/toolbar/GridToolbarButton.tsx.preview new file mode 100644 index 000000000000..10c601462b52 --- /dev/null +++ b/docs/data/data-grid/components/toolbar/GridToolbarButton.tsx.preview @@ -0,0 +1,3 @@ + + Print + \ No newline at end of file diff --git a/docs/data/data-grid/components/toolbar/GridToolbarDefault.js b/docs/data/data-grid/components/toolbar/GridToolbarDefault.js new file mode 100644 index 000000000000..95e9590ee839 --- /dev/null +++ b/docs/data/data-grid/components/toolbar/GridToolbarDefault.js @@ -0,0 +1,97 @@ +import * as React from 'react'; +import { DataGrid, Grid, GridToolbarQuickFilter } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import Tooltip from '@mui/material/Tooltip'; +import Menu from '@mui/material/Menu'; +import Badge from '@mui/material/Badge'; +import ViewColumnIcon from '@mui/icons-material/ViewColumn'; +import FilterListIcon from '@mui/icons-material/FilterList'; +import PrintIcon from '@mui/icons-material/Print'; +import FileDownloadIcon from '@mui/icons-material/FileDownload'; +import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; +import MenuItem from '@mui/material/MenuItem'; + +function Toolbar() { + const [downloadMenuOpen, setDownloadMenuOpen] = React.useState(false); + const downloadMenuTriggerRef = React.useRef(null); + + return ( + + + }> + + + + + + ( + + + + + + )} + /> + + + + + }> + + + + + + setDownloadMenuOpen(true)} + > + + + + + + setDownloadMenuOpen(false)} + MenuListProps={{ + 'aria-labelledby': 'export-menu-trigger', + }} + > + }> + Download as CSV + + {/* Available to MUI X Premium users */} + {/* }> + Download as Excel + */} + + + + + ); +} + +export default function GridToolbarDefault() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 10, + maxColumns: 10, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/components/toolbar/GridToolbarDefault.tsx b/docs/data/data-grid/components/toolbar/GridToolbarDefault.tsx new file mode 100644 index 000000000000..462867a810c2 --- /dev/null +++ b/docs/data/data-grid/components/toolbar/GridToolbarDefault.tsx @@ -0,0 +1,103 @@ +import * as React from 'react'; +import { + DataGrid, + Grid, + GridToolbarButtonProps, + GridToolbarQuickFilter, +} from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import Tooltip from '@mui/material/Tooltip'; +import Menu from '@mui/material/Menu'; +import Badge from '@mui/material/Badge'; +import ViewColumnIcon from '@mui/icons-material/ViewColumn'; +import FilterListIcon from '@mui/icons-material/FilterList'; +import PrintIcon from '@mui/icons-material/Print'; +import FileDownloadIcon from '@mui/icons-material/FileDownload'; +import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; +import MenuItem from '@mui/material/MenuItem'; + +function Toolbar() { + const [downloadMenuOpen, setDownloadMenuOpen] = React.useState(false); + const downloadMenuTriggerRef = React.useRef(null); + + return ( + + + }> + + + + + + ( + + + + + + )} + /> + + + + + + }> + + + + + + setDownloadMenuOpen(true)} + > + + + + + + setDownloadMenuOpen(false)} + MenuListProps={{ + 'aria-labelledby': 'export-menu-trigger', + }} + > + }> + Download as CSV + + {/* Available to MUI X Premium users */} + {/* }> + Download as Excel + */} + + + + + ); +} + +export default function GridToolbarDefault() { + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 10, + maxColumns: 10, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/components/toolbar/GridToolbarDefault.tsx.preview b/docs/data/data-grid/components/toolbar/GridToolbarDefault.tsx.preview new file mode 100644 index 000000000000..6e44e64d1dbc --- /dev/null +++ b/docs/data/data-grid/components/toolbar/GridToolbarDefault.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/components/toolbar/GridToolbarFilterBar.js b/docs/data/data-grid/components/toolbar/GridToolbarFilterBar.js new file mode 100644 index 000000000000..45609faec87a --- /dev/null +++ b/docs/data/data-grid/components/toolbar/GridToolbarFilterBar.js @@ -0,0 +1,94 @@ +import * as React from 'react'; +import { + DataGridPro, + gridColumnLookupSelector, + gridFilterActiveItemsSelector, + Grid, + useGridApiContext, + useGridSelector, + GridFilterListIcon, +} from '@mui/x-data-grid-pro'; +import Chip from '@mui/material/Chip'; +import { unstable_capitalize as capitalize } from '@mui/utils'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +function Toolbar({ onRemoveFilter, ...other }) { + const apiRef = useGridApiContext(); + const activeFilters = useGridSelector(apiRef, gridFilterActiveItemsSelector); + const columns = useGridSelector(apiRef, gridColumnLookupSelector); + + return ( + + }> + + + + {activeFilters.map((filter) => { + const column = columns[filter.field]; + const field = column?.headerName ?? filter.field; + const operator = apiRef.current.getLocaleText( + `filterOperator${capitalize(filter.operator)}`, + ); + const isDate = column?.type === 'date'; + const value = isDate + ? new Date(filter.value).toLocaleDateString() + : (filter.value ?? ''); + + return ( + onRemoveFilter(filter.id)} + sx={{ mx: 0.25 }} + /> + ); + })} + + ); +} + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'position']; + +export default function GridToolbarFilterBar() { + const { data, loading } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + const [filterModel, setFilterModel] = React.useState({ + items: [ + { + id: 'rating', + field: 'rating', + operator: '>', + value: '2.5', + }, + { + id: 'dateCreated', + field: 'dateCreated', + operator: 'before', + value: '2024-01-01', + }, + ], + }); + + const onRemoveFilter = (filterId) => { + setFilterModel({ + items: filterModel.items.filter((item) => item.id !== filterId), + }); + }; + + return ( +
+ setFilterModel(newFilterModel)} + slots={{ toolbar: Toolbar }} + slotProps={{ toolbar: { onRemoveFilter } }} + /> +
+ ); +} diff --git a/docs/data/data-grid/components/toolbar/GridToolbarFilterBar.tsx b/docs/data/data-grid/components/toolbar/GridToolbarFilterBar.tsx new file mode 100644 index 000000000000..c1e3afab9a2d --- /dev/null +++ b/docs/data/data-grid/components/toolbar/GridToolbarFilterBar.tsx @@ -0,0 +1,107 @@ +import * as React from 'react'; +import { + DataGridPro, + gridColumnLookupSelector, + gridFilterActiveItemsSelector, + GridFilterItem, + GridFilterModel, + Grid, + useGridApiContext, + useGridSelector, + GridFilterListIcon, + GridToolbarProps, +} from '@mui/x-data-grid-pro'; +import Chip from '@mui/material/Chip'; +import { unstable_capitalize as capitalize } from '@mui/utils'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +declare module '@mui/x-data-grid' { + interface ToolbarPropsOverrides { + onRemoveFilter: (filterId: GridFilterItem['id']) => void; + } +} + +type ToolbarProps = GridToolbarProps & { + onRemoveFilter: (filterId: GridFilterItem['id']) => void; +}; + +function Toolbar({ onRemoveFilter, ...other }: ToolbarProps) { + const apiRef = useGridApiContext(); + const activeFilters = useGridSelector(apiRef, gridFilterActiveItemsSelector); + const columns = useGridSelector(apiRef, gridColumnLookupSelector); + + return ( + + }> + + + + {activeFilters.map((filter) => { + const column = columns[filter.field]; + const field = column?.headerName ?? filter.field; + const operator = apiRef.current.getLocaleText( + `filterOperator${capitalize(filter.operator)}` as 'filterOperatorContains', + ); + const isDate = column?.type === 'date'; + const value = isDate + ? new Date(filter.value).toLocaleDateString() + : (filter.value ?? ''); + + return ( + onRemoveFilter(filter.id)} + sx={{ mx: 0.25 }} + /> + ); + })} + + ); +} + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'position']; + +export default function GridToolbarFilterBar() { + const { data, loading } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + const [filterModel, setFilterModel] = React.useState({ + items: [ + { + id: 'rating', + field: 'rating', + operator: '>', + value: '2.5', + }, + { + id: 'dateCreated', + field: 'dateCreated', + operator: 'before', + value: '2024-01-01', + }, + ], + }); + + const onRemoveFilter = (filterId: GridFilterItem['id']) => { + setFilterModel({ + items: filterModel.items.filter((item) => item.id !== filterId), + }); + }; + + return ( +
+ setFilterModel(newFilterModel)} + slots={{ toolbar: Toolbar }} + slotProps={{ toolbar: { onRemoveFilter } }} + /> +
+ ); +} diff --git a/docs/data/data-grid/components/toolbar/GridToolbarFilterBar.tsx.preview b/docs/data/data-grid/components/toolbar/GridToolbarFilterBar.tsx.preview new file mode 100644 index 000000000000..3746335e5f4a --- /dev/null +++ b/docs/data/data-grid/components/toolbar/GridToolbarFilterBar.tsx.preview @@ -0,0 +1,8 @@ + setFilterModel(newFilterModel)} + slots={{ toolbar: Toolbar }} + slotProps={{ toolbar: { onRemoveFilter } }} +/> \ No newline at end of file diff --git a/docs/data/data-grid/components/toolbar/GridToolbarToggleButton.js b/docs/data/data-grid/components/toolbar/GridToolbarToggleButton.js new file mode 100644 index 000000000000..d260c2014f36 --- /dev/null +++ b/docs/data/data-grid/components/toolbar/GridToolbarToggleButton.js @@ -0,0 +1,21 @@ +import * as React from 'react'; +import { DemoContainer } from '@mui/x-data-grid/internals/demo'; +import { Grid } from '@mui/x-data-grid'; +import PivotTableChartIcon from '@mui/icons-material/PivotTableChart'; + +export default function GridToolbarToggleButton() { + const [pivotEnabled, setPivotEnabled] = React.useState(false); + + return ( + + setPivotEnabled(!pivotEnabled)} + > + Pivot + + + ); +} diff --git a/docs/data/data-grid/components/toolbar/GridToolbarToggleButton.tsx b/docs/data/data-grid/components/toolbar/GridToolbarToggleButton.tsx new file mode 100644 index 000000000000..d260c2014f36 --- /dev/null +++ b/docs/data/data-grid/components/toolbar/GridToolbarToggleButton.tsx @@ -0,0 +1,21 @@ +import * as React from 'react'; +import { DemoContainer } from '@mui/x-data-grid/internals/demo'; +import { Grid } from '@mui/x-data-grid'; +import PivotTableChartIcon from '@mui/icons-material/PivotTableChart'; + +export default function GridToolbarToggleButton() { + const [pivotEnabled, setPivotEnabled] = React.useState(false); + + return ( + + setPivotEnabled(!pivotEnabled)} + > + Pivot + + + ); +} diff --git a/docs/data/data-grid/components/toolbar/GridToolbarToggleButton.tsx.preview b/docs/data/data-grid/components/toolbar/GridToolbarToggleButton.tsx.preview new file mode 100644 index 000000000000..07f3051c65c9 --- /dev/null +++ b/docs/data/data-grid/components/toolbar/GridToolbarToggleButton.tsx.preview @@ -0,0 +1,8 @@ + setPivotEnabled(!pivotEnabled)} +> + Pivot + \ No newline at end of file diff --git a/docs/data/data-grid/components/toolbar/GridToolbarToggleButtonGroup.js b/docs/data/data-grid/components/toolbar/GridToolbarToggleButtonGroup.js new file mode 100644 index 000000000000..ffc6edd5f03a --- /dev/null +++ b/docs/data/data-grid/components/toolbar/GridToolbarToggleButtonGroup.js @@ -0,0 +1,30 @@ +import * as React from 'react'; +import { DemoContainer } from '@mui/x-data-grid/internals/demo'; +import { Grid } from '@mui/x-data-grid'; +import GridViewIcon from '@mui/icons-material/ViewModule'; +import ListViewIcon from '@mui/icons-material/ViewList'; + +export default function GridToolbarToggleButtonGroup() { + const [view, setView] = React.useState('grid'); + + return ( + + + setView('grid')} + > + Grid view + + setView('list')} + > + List view + + + + ); +} diff --git a/docs/data/data-grid/components/toolbar/GridToolbarToggleButtonGroup.tsx b/docs/data/data-grid/components/toolbar/GridToolbarToggleButtonGroup.tsx new file mode 100644 index 000000000000..ffc6edd5f03a --- /dev/null +++ b/docs/data/data-grid/components/toolbar/GridToolbarToggleButtonGroup.tsx @@ -0,0 +1,30 @@ +import * as React from 'react'; +import { DemoContainer } from '@mui/x-data-grid/internals/demo'; +import { Grid } from '@mui/x-data-grid'; +import GridViewIcon from '@mui/icons-material/ViewModule'; +import ListViewIcon from '@mui/icons-material/ViewList'; + +export default function GridToolbarToggleButtonGroup() { + const [view, setView] = React.useState('grid'); + + return ( + + + setView('grid')} + > + Grid view + + setView('list')} + > + List view + + + + ); +} diff --git a/docs/data/data-grid/components/toolbar/GridToolbarToggleButtonGroup.tsx.preview b/docs/data/data-grid/components/toolbar/GridToolbarToggleButtonGroup.tsx.preview new file mode 100644 index 000000000000..f378c8ee193c --- /dev/null +++ b/docs/data/data-grid/components/toolbar/GridToolbarToggleButtonGroup.tsx.preview @@ -0,0 +1,16 @@ + + setView('grid')} + > + Grid view + + setView('list')} + > + List view + + \ No newline at end of file diff --git a/docs/data/data-grid/components/toolbar/toolbar.md b/docs/data/data-grid/components/toolbar/toolbar.md new file mode 100644 index 000000000000..be6d335dad7d --- /dev/null +++ b/docs/data/data-grid/components/toolbar/toolbar.md @@ -0,0 +1,114 @@ +# Data Grid - Toolbar component + +

The Toolbar component provides a way to add custom controls and functionality to the Data Grid.

+ +{{"demo": "GridToolbar.js"}} + +## Usage + +Import the `Grid` component. See [Grid components](/x/react-data-grid/components/overview/) for details. + +```tsx +import { Grid } from '@mui/x-data-grid'; +``` + +Create a toolbar using the `Grid.Toolbar` component. + +```tsx +function Toolbar() { + return ( + + }> + Filters + + + ); +} +``` + +Pass the component to the `toolbar` slot. + +```tsx + +``` + +## Anatomy + +The `Grid.Toolbar` component is comprised of the following parts. + +```tsx + + + + + + + +``` + +### Root + +The top level component that positions items in a row. + +Renders a `div` with `role="toolbar"`. + +### Button + +A button item that can be used to perform actions from the toolbar. + +See [ButtonBase API](/material-ui/api/button-base/) for available props. + +{{"demo": "GridToolbarButton.js"}} + +### ToggleButtonGroup + +A toggle button group that can be used to switch between multiple states. + +See [ToggleButtonGroup API](/material-ui/api/toggle-button/) for available props. + +{{"demo": "GridToolbarToggleButtonGroup.js"}} + +### ToggleButton + +A toggle button item that can be used to switch between two states. + +See [ToggleButton API](/material-ui/api/toggle-button-group/) for available props. + +{{"demo": "GridToolbarToggleButton.js"}} + +### Separator + +Separate items and groups in the toolbar. + +Renders a `div` with `role="separator"`. + +```tsx + +``` + +## Examples + +Below are some ways the Toolbar component can be used. + +### Default toolbar + +The demo below shows the default and recommended toolbar configuration. + +This example can be used as a starting point for customizing the toolbar by adding or removing features based on specific needs. + +{{"demo": "GridToolbarDefault.js", "bg": "inline"}} + +### Filter bar + +Show active filter chips in the toolbar. + +{{"demo": "GridToolbarFilterBar.js", "bg": "inline"}} + +## API + +- [GridToolbar](/x/api/data-grid/data-grid/) +- [GridToolbarRoot](/x/api/data-grid/data-grid/) +- [GridToolbarButton](/x/api/data-grid/data-grid/) +- [GridToolbarToggleButtonGroup](/x/api/data-grid/data-grid/) +- [GridToolbarToggleButton](/x/api/data-grid/data-grid/) +- [GridToolbarSeparator](/x/api/data-grid/data-grid/) diff --git a/docs/data/pages.ts b/docs/data/pages.ts index ffd848c5fa46..13e08f194494 100644 --- a/docs/data/pages.ts +++ b/docs/data/pages.ts @@ -178,6 +178,21 @@ const pages: MuiPage[] = [ { pathname: '/x/react-data-grid/style-recipes', title: 'Styling recipes' }, { pathname: '/x/react-data-grid/overlays', title: 'Overlays' }, { pathname: '/x/react-data-grid/components', title: 'Custom subcomponents' }, + { + pathname: '/x/react-data-grid/components/', + title: 'Components', + unstable: true, + children: [ + { pathname: '/x/react-data-grid/components/overview', title: 'Overview' }, + { pathname: '/x/react-data-grid/components/toolbar', title: 'Toolbar' }, + { + pathname: '/x/react-data-grid/components/columns-panel', + title: 'Columns Panel', + }, + { pathname: '/x/react-data-grid/components/filter-panel', title: 'Filter Panel' }, + { pathname: '/x/react-data-grid/components/export', title: 'Export' }, + ], + }, ], }, { diff --git a/docs/pages/x/api/data-grid/data-grid-premium.json b/docs/pages/x/api/data-grid/data-grid-premium.json index c19a664541cd..576236225400 100644 --- a/docs/pages/x/api/data-grid/data-grid-premium.json +++ b/docs/pages/x/api/data-grid/data-grid-premium.json @@ -883,6 +883,18 @@ "default": "MenuItem", "class": null }, + { + "name": "baseToggleButton", + "description": "The custom ToggleButton component used in the grid.", + "default": "ToggleButton", + "class": null + }, + { + "name": "baseToggleButtonGroup", + "description": "The custom ToggleButtonGroup component used in the grid.", + "default": "ToggleButtonGroup", + "class": null + }, { "name": "booleanCellTrueIcon", "description": "Icon displayed on the boolean cell to represent the true value.", @@ -964,7 +976,7 @@ { "name": "exportIcon", "description": "Icon displayed on the open export button present in the toolbar by default.", - "default": "GridSaveAltIcon", + "default": "GridFileDownloadIcon", "class": null }, { @@ -1893,6 +1905,12 @@ "description": "Styles applied to the sort icon element.", "isGlobal": false }, + { + "key": "toolbarButton", + "className": "MuiDataGridPremium-toolbarButton", + "description": "Styles applied to the toolbar button element.", + "isGlobal": false + }, { "key": "toolbarContainer", "className": "MuiDataGridPremium-toolbarContainer", @@ -1935,6 +1953,30 @@ "description": "Styles applied to the toolbar prompt control send button element.", "isGlobal": false }, + { + "key": "toolbarRoot", + "className": "MuiDataGridPremium-toolbarRoot", + "description": "Styles applied to the toolbar root element.", + "isGlobal": false + }, + { + "key": "toolbarSeparator", + "className": "MuiDataGridPremium-toolbarSeparator", + "description": "Styles applied to the toolbar separator element.", + "isGlobal": false + }, + { + "key": "toolbarToggleButton", + "className": "MuiDataGridPremium-toolbarToggleButton", + "description": "Styles applied to the toolbar toggle button element.", + "isGlobal": false + }, + { + "key": "toolbarToggleButtonGroup", + "className": "MuiDataGridPremium-toolbarToggleButtonGroup", + "description": "Styles applied to the toolbar toggle button group element.", + "isGlobal": false + }, { "key": "treeDataGroupingCell", "className": "MuiDataGridPremium-treeDataGroupingCell", diff --git a/docs/pages/x/api/data-grid/data-grid-pro.json b/docs/pages/x/api/data-grid/data-grid-pro.json index 677e8939b095..e607f6eabb08 100644 --- a/docs/pages/x/api/data-grid/data-grid-pro.json +++ b/docs/pages/x/api/data-grid/data-grid-pro.json @@ -818,6 +818,18 @@ "default": "MenuItem", "class": null }, + { + "name": "baseToggleButton", + "description": "The custom ToggleButton component used in the grid.", + "default": "ToggleButton", + "class": null + }, + { + "name": "baseToggleButtonGroup", + "description": "The custom ToggleButtonGroup component used in the grid.", + "default": "ToggleButtonGroup", + "class": null + }, { "name": "booleanCellTrueIcon", "description": "Icon displayed on the boolean cell to represent the true value.", @@ -899,7 +911,7 @@ { "name": "exportIcon", "description": "Icon displayed on the open export button present in the toolbar by default.", - "default": "GridSaveAltIcon", + "default": "GridFileDownloadIcon", "class": null }, { @@ -1798,6 +1810,12 @@ "description": "Styles applied to the sort icon element.", "isGlobal": false }, + { + "key": "toolbarButton", + "className": "MuiDataGridPro-toolbarButton", + "description": "Styles applied to the toolbar button element.", + "isGlobal": false + }, { "key": "toolbarContainer", "className": "MuiDataGridPro-toolbarContainer", @@ -1840,6 +1858,30 @@ "description": "Styles applied to the toolbar prompt control send button element.", "isGlobal": false }, + { + "key": "toolbarRoot", + "className": "MuiDataGridPro-toolbarRoot", + "description": "Styles applied to the toolbar root element.", + "isGlobal": false + }, + { + "key": "toolbarSeparator", + "className": "MuiDataGridPro-toolbarSeparator", + "description": "Styles applied to the toolbar separator element.", + "isGlobal": false + }, + { + "key": "toolbarToggleButton", + "className": "MuiDataGridPro-toolbarToggleButton", + "description": "Styles applied to the toolbar toggle button element.", + "isGlobal": false + }, + { + "key": "toolbarToggleButtonGroup", + "className": "MuiDataGridPro-toolbarToggleButtonGroup", + "description": "Styles applied to the toolbar toggle button group element.", + "isGlobal": false + }, { "key": "treeDataGroupingCell", "className": "MuiDataGridPro-treeDataGroupingCell", diff --git a/docs/pages/x/api/data-grid/data-grid.json b/docs/pages/x/api/data-grid/data-grid.json index 3e0425377810..2a3669cbee4e 100644 --- a/docs/pages/x/api/data-grid/data-grid.json +++ b/docs/pages/x/api/data-grid/data-grid.json @@ -702,6 +702,18 @@ "default": "MenuItem", "class": null }, + { + "name": "baseToggleButton", + "description": "The custom ToggleButton component used in the grid.", + "default": "ToggleButton", + "class": null + }, + { + "name": "baseToggleButtonGroup", + "description": "The custom ToggleButtonGroup component used in the grid.", + "default": "ToggleButtonGroup", + "class": null + }, { "name": "booleanCellTrueIcon", "description": "Icon displayed on the boolean cell to represent the true value.", @@ -783,7 +795,7 @@ { "name": "exportIcon", "description": "Icon displayed on the open export button present in the toolbar by default.", - "default": "GridSaveAltIcon", + "default": "GridFileDownloadIcon", "class": null }, { @@ -1670,6 +1682,12 @@ "description": "Styles applied to the sort icon element.", "isGlobal": false }, + { + "key": "toolbarButton", + "className": "MuiDataGrid-toolbarButton", + "description": "Styles applied to the toolbar button element.", + "isGlobal": false + }, { "key": "toolbarContainer", "className": "MuiDataGrid-toolbarContainer", @@ -1712,6 +1730,30 @@ "description": "Styles applied to the toolbar prompt control send button element.", "isGlobal": false }, + { + "key": "toolbarRoot", + "className": "MuiDataGrid-toolbarRoot", + "description": "Styles applied to the toolbar root element.", + "isGlobal": false + }, + { + "key": "toolbarSeparator", + "className": "MuiDataGrid-toolbarSeparator", + "description": "Styles applied to the toolbar separator element.", + "isGlobal": false + }, + { + "key": "toolbarToggleButton", + "className": "MuiDataGrid-toolbarToggleButton", + "description": "Styles applied to the toolbar toggle button element.", + "isGlobal": false + }, + { + "key": "toolbarToggleButtonGroup", + "className": "MuiDataGrid-toolbarToggleButtonGroup", + "description": "Styles applied to the toolbar toggle button group element.", + "isGlobal": false + }, { "key": "treeDataGroupingCell", "className": "MuiDataGrid-treeDataGroupingCell", diff --git a/docs/pages/x/react-data-grid/components/columns-panel.js b/docs/pages/x/react-data-grid/components/columns-panel.js new file mode 100644 index 000000000000..3914954ad653 --- /dev/null +++ b/docs/pages/x/react-data-grid/components/columns-panel.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import * as pageProps from 'docsx/data/data-grid/components/columns-panel/columns-panel.md?muiMarkdown'; + +export default function Page() { + return ; +} diff --git a/docs/pages/x/react-data-grid/components/export.js b/docs/pages/x/react-data-grid/components/export.js new file mode 100644 index 000000000000..94181aa839d6 --- /dev/null +++ b/docs/pages/x/react-data-grid/components/export.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import * as pageProps from 'docsx/data/data-grid/components/export/export.md?muiMarkdown'; + +export default function Page() { + return ; +} diff --git a/docs/pages/x/react-data-grid/components/filter-panel.js b/docs/pages/x/react-data-grid/components/filter-panel.js new file mode 100644 index 000000000000..e1f857fb5ad8 --- /dev/null +++ b/docs/pages/x/react-data-grid/components/filter-panel.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import * as pageProps from 'docsx/data/data-grid/components/filter-panel/filter-panel.md?muiMarkdown'; + +export default function Page() { + return ; +} diff --git a/docs/pages/x/react-data-grid/components.js b/docs/pages/x/react-data-grid/components/index.js similarity index 100% rename from docs/pages/x/react-data-grid/components.js rename to docs/pages/x/react-data-grid/components/index.js diff --git a/docs/pages/x/react-data-grid/components/overview.js b/docs/pages/x/react-data-grid/components/overview.js new file mode 100644 index 000000000000..dfb6df552d71 --- /dev/null +++ b/docs/pages/x/react-data-grid/components/overview.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import * as pageProps from 'docsx/data/data-grid/components/overview.md?muiMarkdown'; + +export default function Page() { + return ; +} diff --git a/docs/pages/x/react-data-grid/components/toolbar.js b/docs/pages/x/react-data-grid/components/toolbar.js new file mode 100644 index 000000000000..57ff36c41b70 --- /dev/null +++ b/docs/pages/x/react-data-grid/components/toolbar.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import * as pageProps from 'docsx/data/data-grid/components/toolbar/toolbar.md?muiMarkdown'; + +export default function Page() { + return ; +} diff --git a/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json b/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json index 65776cfbe2bb..1b30c0643cee 100644 --- a/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json +++ b/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json @@ -1197,6 +1197,10 @@ "description": "Styles applied to {{nodeName}}.", "nodeName": "the sort icon element" }, + "toolbarButton": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the toolbar button element" + }, "toolbarContainer": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the toolbar container element" @@ -1226,6 +1230,22 @@ "description": "Styles applied to {{nodeName}}.", "nodeName": "the toolbar prompt control send button element" }, + "toolbarRoot": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the toolbar root element" + }, + "toolbarSeparator": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the toolbar separator element" + }, + "toolbarToggleButton": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the toolbar toggle button element" + }, + "toolbarToggleButtonGroup": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the toolbar toggle button group element" + }, "treeDataGroupingCell": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the root of the grouping column of the tree data" @@ -1276,6 +1296,8 @@ "baseSelect": "The custom Select component used in the grid.", "baseSelectOption": "The custom SelectOption component used in the grid.", "baseTextField": "The custom TextField component used in the grid.", + "baseToggleButton": "The custom ToggleButton component used in the grid.", + "baseToggleButtonGroup": "The custom ToggleButtonGroup component used in the grid.", "baseTooltip": "The custom Tooltip component used in the grid.", "booleanCellFalseIcon": "Icon displayed on the boolean cell to represent the false value.", "booleanCellTrueIcon": "Icon displayed on the boolean cell to represent the true value.", diff --git a/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json b/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json index ee17a6dd230c..7def79aea860 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json @@ -1135,6 +1135,10 @@ "description": "Styles applied to {{nodeName}}.", "nodeName": "the sort icon element" }, + "toolbarButton": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the toolbar button element" + }, "toolbarContainer": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the toolbar container element" @@ -1164,6 +1168,22 @@ "description": "Styles applied to {{nodeName}}.", "nodeName": "the toolbar prompt control send button element" }, + "toolbarRoot": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the toolbar root element" + }, + "toolbarSeparator": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the toolbar separator element" + }, + "toolbarToggleButton": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the toolbar toggle button element" + }, + "toolbarToggleButtonGroup": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the toolbar toggle button group element" + }, "treeDataGroupingCell": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the root of the grouping column of the tree data" @@ -1214,6 +1234,8 @@ "baseSelect": "The custom Select component used in the grid.", "baseSelectOption": "The custom SelectOption component used in the grid.", "baseTextField": "The custom TextField component used in the grid.", + "baseToggleButton": "The custom ToggleButton component used in the grid.", + "baseToggleButtonGroup": "The custom ToggleButtonGroup component used in the grid.", "baseTooltip": "The custom Tooltip component used in the grid.", "booleanCellFalseIcon": "Icon displayed on the boolean cell to represent the false value.", "booleanCellTrueIcon": "Icon displayed on the boolean cell to represent the true value.", diff --git a/docs/translations/api-docs/data-grid/data-grid/data-grid.json b/docs/translations/api-docs/data-grid/data-grid/data-grid.json index 9aa616d6f50d..99c4d630d60c 100644 --- a/docs/translations/api-docs/data-grid/data-grid/data-grid.json +++ b/docs/translations/api-docs/data-grid/data-grid/data-grid.json @@ -1009,6 +1009,10 @@ "description": "Styles applied to {{nodeName}}.", "nodeName": "the sort icon element" }, + "toolbarButton": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the toolbar button element" + }, "toolbarContainer": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the toolbar container element" @@ -1038,6 +1042,22 @@ "description": "Styles applied to {{nodeName}}.", "nodeName": "the toolbar prompt control send button element" }, + "toolbarRoot": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the toolbar root element" + }, + "toolbarSeparator": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the toolbar separator element" + }, + "toolbarToggleButton": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the toolbar toggle button element" + }, + "toolbarToggleButtonGroup": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the toolbar toggle button group element" + }, "treeDataGroupingCell": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the root of the grouping column of the tree data" @@ -1088,6 +1108,8 @@ "baseSelect": "The custom Select component used in the grid.", "baseSelectOption": "The custom SelectOption component used in the grid.", "baseTextField": "The custom TextField component used in the grid.", + "baseToggleButton": "The custom ToggleButton component used in the grid.", + "baseToggleButtonGroup": "The custom ToggleButtonGroup component used in the grid.", "baseTooltip": "The custom Tooltip component used in the grid.", "booleanCellFalseIcon": "Icon displayed on the boolean cell to represent the false value.", "booleanCellTrueIcon": "Icon displayed on the boolean cell to represent the true value.", diff --git a/packages/x-data-grid/src/components/grid/columnsPanel/GridColumnsPanelTrigger.tsx b/packages/x-data-grid/src/components/grid/columnsPanel/GridColumnsPanelTrigger.tsx new file mode 100644 index 000000000000..4ff0b3cc832f --- /dev/null +++ b/packages/x-data-grid/src/components/grid/columnsPanel/GridColumnsPanelTrigger.tsx @@ -0,0 +1,75 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import useId from '@mui/utils/useId'; +import { ButtonProps } from '@mui/material/Button'; +import { useGridApiContext } from '../../../hooks/utils/useGridApiContext'; +import { + gridPreferencePanelStateSelector, + GridPreferencePanelsValue, + useGridSelector, +} from '../../../hooks'; +import { useGridRootProps } from '../../../hooks/utils/useGridRootProps'; +import { + useGridComponentRenderer, + RenderProp, +} from '../../../hooks/utils/useGridComponentRenderer'; + +export interface GridColumnsPanelTriggerState { + open: boolean; +} + +export interface GridColumnsPanelTriggerProps extends ButtonProps { + // eslint-disable-next-line react/no-unused-prop-types + render?: RenderProp; +} + +const GridColumnsPanelTrigger = React.forwardRef( + function GridColumnsPanelTrigger(props, ref) { + const { render, ...other } = props; + const rootProps = useGridRootProps(); + const buttonId = useId(); + const panelId = useId(); + const apiRef = useGridApiContext(); + const panelState = useGridSelector(apiRef, gridPreferencePanelStateSelector); + const open = + panelState.open && panelState.openedPanelValue === GridPreferencePanelsValue.columns; + + const toggleColumnsPanel = () => { + if (open) { + apiRef.current.hidePreferences(); + } else { + apiRef.current.showPreferences(GridPreferencePanelsValue.columns, panelId, buttonId); + } + }; + + const { renderElement } = useGridComponentRenderer({ + render, + defaultElement: rootProps.slots.baseButton, + props: { + ref, + id: buttonId, + 'aria-haspopup': 'true', + 'aria-expanded': open ? 'true' : undefined, + 'aria-controls': open ? panelId : undefined, + onClick: toggleColumnsPanel, + ...rootProps.slotProps?.baseButton, + ...other, + }, + state: { + open, + }, + }); + + return renderElement(); + }, +); + +GridColumnsPanelTrigger.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the TypeScript types and run "pnpm proptypes" | + // ---------------------------------------------------------------------- + render: PropTypes.oneOfType([PropTypes.element, PropTypes.func]), +} as any; + +export { GridColumnsPanelTrigger }; diff --git a/packages/x-data-grid/src/components/grid/columnsPanel/index.parts.ts b/packages/x-data-grid/src/components/grid/columnsPanel/index.parts.ts new file mode 100644 index 000000000000..bc186c21beec --- /dev/null +++ b/packages/x-data-grid/src/components/grid/columnsPanel/index.parts.ts @@ -0,0 +1 @@ +export { GridColumnsPanelTrigger as Trigger } from './GridColumnsPanelTrigger'; diff --git a/packages/x-data-grid/src/components/grid/columnsPanel/index.ts b/packages/x-data-grid/src/components/grid/columnsPanel/index.ts new file mode 100644 index 000000000000..e333d675352a --- /dev/null +++ b/packages/x-data-grid/src/components/grid/columnsPanel/index.ts @@ -0,0 +1 @@ +export * from './GridColumnsPanelTrigger'; diff --git a/packages/x-data-grid/src/components/grid/export/GridExportTrigger.tsx b/packages/x-data-grid/src/components/grid/export/GridExportTrigger.tsx new file mode 100644 index 000000000000..43a022f094e2 --- /dev/null +++ b/packages/x-data-grid/src/components/grid/export/GridExportTrigger.tsx @@ -0,0 +1,99 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { ButtonProps } from '@mui/material/Button'; +import { useGridApiContext } from '../../../hooks/utils/useGridApiContext'; +import { useGridRootProps } from '../../../hooks/utils/useGridRootProps'; +import { + useGridComponentRenderer, + RenderProp, +} from '../../../hooks/utils/useGridComponentRenderer'; +import { GridCsvExportOptions, GridPrintExportOptions } from '../../../models/gridExport'; + +export interface GridExportTriggerState {} + +export type GridExportTriggerProps = ButtonProps & { + // eslint-disable-next-line react/no-unused-prop-types + render?: RenderProp; +} & ( + | { + exportType: 'csv'; + exportOptions?: GridCsvExportOptions; + } + | { + exportType: 'print'; + exportOptions?: GridPrintExportOptions; + } + ); + +const GridExportTrigger = React.forwardRef( + function GridExportTrigger(props, ref) { + const { render, exportType, exportOptions, onClick, ...other } = props; + const rootProps = useGridRootProps(); + const apiRef = useGridApiContext(); + + const handleClick = (event: React.MouseEvent) => { + switch (exportType) { + case 'csv': + apiRef.current.exportDataAsCsv(exportOptions); + break; + case 'print': + apiRef.current.exportDataAsPrint(exportOptions); + break; + default: + break; + } + + onClick?.(event); + }; + + const { renderElement } = useGridComponentRenderer({ + render, + defaultElement: rootProps.slots.baseButton, + props: { + ref, + onClick: handleClick, + ...rootProps.slotProps?.baseButton, + ...other, + }, + }); + + return renderElement(); + }, +); + +GridExportTrigger.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the TypeScript types and run "pnpm proptypes" | + // ---------------------------------------------------------------------- + exportOptions: PropTypes.oneOfType([ + PropTypes.shape({ + allColumns: PropTypes.bool, + delimiter: PropTypes.string, + escapeFormulas: PropTypes.bool, + fields: PropTypes.arrayOf(PropTypes.string), + fileName: PropTypes.string, + getRowsToExport: PropTypes.func, + includeColumnGroupsHeaders: PropTypes.bool, + includeHeaders: PropTypes.bool, + shouldAppendQuotes: PropTypes.bool, + utf8WithBom: PropTypes.bool, + }), + PropTypes.shape({ + allColumns: PropTypes.bool, + bodyClassName: PropTypes.string, + copyStyles: PropTypes.bool, + fields: PropTypes.arrayOf(PropTypes.string), + fileName: PropTypes.string, + getRowsToExport: PropTypes.func, + hideFooter: PropTypes.bool, + hideToolbar: PropTypes.bool, + includeCheckboxes: PropTypes.bool, + pageStyle: PropTypes.oneOfType([PropTypes.func, PropTypes.string]), + }), + ]), + exportType: PropTypes.oneOf(['csv', 'print']).isRequired, + render: PropTypes.oneOfType([PropTypes.element, PropTypes.func]), +} as any; + +export { GridExportTrigger }; diff --git a/packages/x-data-grid/src/components/grid/export/index.parts.ts b/packages/x-data-grid/src/components/grid/export/index.parts.ts new file mode 100644 index 000000000000..4c127d0970da --- /dev/null +++ b/packages/x-data-grid/src/components/grid/export/index.parts.ts @@ -0,0 +1 @@ +export { GridExportTrigger as Trigger } from './GridExportTrigger'; diff --git a/packages/x-data-grid/src/components/grid/export/index.ts b/packages/x-data-grid/src/components/grid/export/index.ts new file mode 100644 index 000000000000..ee8950a789fb --- /dev/null +++ b/packages/x-data-grid/src/components/grid/export/index.ts @@ -0,0 +1 @@ +export * from './GridExportTrigger'; diff --git a/packages/x-data-grid/src/components/grid/filterPanel/GridFilterPanelTrigger.tsx b/packages/x-data-grid/src/components/grid/filterPanel/GridFilterPanelTrigger.tsx new file mode 100644 index 000000000000..e542c569ec16 --- /dev/null +++ b/packages/x-data-grid/src/components/grid/filterPanel/GridFilterPanelTrigger.tsx @@ -0,0 +1,79 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import useId from '@mui/utils/useId'; +import { ButtonProps } from '@mui/material/Button'; +import { useGridApiContext } from '../../../hooks/utils/useGridApiContext'; +import { + gridFilterActiveItemsSelector, + gridPreferencePanelStateSelector, + GridPreferencePanelsValue, + useGridSelector, +} from '../../../hooks'; +import { useGridRootProps } from '../../../hooks/utils/useGridRootProps'; +import { + useGridComponentRenderer, + RenderProp, +} from '../../../hooks/utils/useGridComponentRenderer'; + +export interface GridFilterPanelTriggerState { + open: boolean; + filterCount: number; +} + +export interface GridFilterPanelTriggerProps extends ButtonProps { + render?: RenderProp; +} + +const GridFilterPanelTrigger = React.forwardRef( + function GridFilterPanelTrigger(props, ref) { + const { render, ...other } = props; + const rootProps = useGridRootProps(); + const buttonId = useId(); + const panelId = useId(); + const apiRef = useGridApiContext(); + const panelState = useGridSelector(apiRef, gridPreferencePanelStateSelector); + const open = + panelState.open && panelState.openedPanelValue === GridPreferencePanelsValue.filters; + const activeFilters = useGridSelector(apiRef, gridFilterActiveItemsSelector); + const filterCount = activeFilters.length; + + const toggleFilterPanel = () => { + if (open) { + apiRef.current.hidePreferences(); + } else { + apiRef.current.showPreferences(GridPreferencePanelsValue.filters, panelId, buttonId); + } + }; + + const { renderElement } = useGridComponentRenderer({ + render, + defaultElement: rootProps.slots.baseButton, + props: { + ref, + id: buttonId, + 'aria-haspopup': 'true', + 'aria-expanded': open ? 'true' : undefined, + 'aria-controls': open ? panelId : undefined, + onClick: toggleFilterPanel, + ...rootProps.slotProps?.baseButton, + ...other, + }, + state: { + open, + filterCount, + }, + }); + + return renderElement(); + }, +); + +GridFilterPanelTrigger.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the TypeScript types and run "pnpm proptypes" | + // ---------------------------------------------------------------------- + render: PropTypes.oneOfType([PropTypes.element, PropTypes.func]), +} as any; + +export { GridFilterPanelTrigger }; diff --git a/packages/x-data-grid/src/components/grid/filterPanel/index.parts.ts b/packages/x-data-grid/src/components/grid/filterPanel/index.parts.ts new file mode 100644 index 000000000000..9dee45abc9bb --- /dev/null +++ b/packages/x-data-grid/src/components/grid/filterPanel/index.parts.ts @@ -0,0 +1 @@ +export { GridFilterPanelTrigger as Trigger } from './GridFilterPanelTrigger'; diff --git a/packages/x-data-grid/src/components/grid/filterPanel/index.ts b/packages/x-data-grid/src/components/grid/filterPanel/index.ts new file mode 100644 index 000000000000..f77de815994c --- /dev/null +++ b/packages/x-data-grid/src/components/grid/filterPanel/index.ts @@ -0,0 +1 @@ +export * from './GridFilterPanelTrigger'; diff --git a/packages/x-data-grid/src/components/grid/index.parts.ts b/packages/x-data-grid/src/components/grid/index.parts.ts new file mode 100644 index 000000000000..375a00124f3c --- /dev/null +++ b/packages/x-data-grid/src/components/grid/index.parts.ts @@ -0,0 +1,4 @@ +export * as Toolbar from './toolbar/index.parts'; +export * as FilterPanel from './filterPanel/index.parts'; +export * as ColumnsPanel from './columnsPanel/index.parts'; +export * as Export from './export/index.parts'; diff --git a/packages/x-data-grid/src/components/grid/index.ts b/packages/x-data-grid/src/components/grid/index.ts new file mode 100644 index 000000000000..a5c9c08b73f2 --- /dev/null +++ b/packages/x-data-grid/src/components/grid/index.ts @@ -0,0 +1,4 @@ +export * from './columnsPanel'; +export * from './export'; +export * from './filterPanel'; +export * from './toolbar'; diff --git a/packages/x-data-grid/src/components/grid/toolbar/GridToolbarButton.tsx b/packages/x-data-grid/src/components/grid/toolbar/GridToolbarButton.tsx new file mode 100644 index 000000000000..ab3ea11d892d --- /dev/null +++ b/packages/x-data-grid/src/components/grid/toolbar/GridToolbarButton.tsx @@ -0,0 +1,133 @@ +import * as React from 'react'; +import { alpha, styled } from '@mui/material/styles'; +import composeClasses from '@mui/utils/composeClasses'; +import clsx from 'clsx'; +import ButtonBase, { ButtonBaseProps } from '@mui/material/ButtonBase'; +import { useGridRootProps } from '../../../hooks/utils/useGridRootProps'; +import { getDataGridUtilityClass } from '../../../constants/gridClasses'; +import type { DataGridProcessedProps } from '../../../models/props/DataGridProps'; + +export type GridToolbarButtonProps = ButtonBaseProps & { + color?: 'standard' | 'primary'; +}; + +type OwnerState = DataGridProcessedProps; + +const useUtilityClasses = (ownerState: OwnerState) => { + const { classes } = ownerState; + + const slots = { + root: ['toolbarButton'], + }; + + return composeClasses(slots, getDataGridUtilityClass, classes); +}; + +// Based on @mui/material/ToggleButton styles +const StyledGridToolbarButton = styled(ButtonBase, { + name: 'MuiDataGrid', + slot: 'ToolbarButton', + overridesResolver: (_, styles) => styles.toolbarButton, +})<{ ownerState: OwnerState; selected?: boolean }>(({ theme }) => ({ + ...theme.typography.button, + display: 'flex', + alignItems: 'center', + gap: theme.spacing(1), + borderRadius: (theme.vars || theme).shape.borderRadius, + padding: 7, + minHeight: 34, + fontSize: theme.typography.pxToRem(13), + lineHeight: 'normal', + color: (theme.vars || theme).palette.action.active, + '&:disabled': { + color: (theme.vars || theme).palette.action.disabled, + border: `1px solid ${(theme.vars || theme).palette.action.disabledBackground}`, + }, + '&:hover': { + textDecoration: 'none', + // Reset on mouse devices + backgroundColor: theme.vars + ? `rgba(${theme.vars.palette.text.primaryChannel} / ${theme.vars.palette.action.hoverOpacity})` + : alpha(theme.palette.text.primary, theme.palette.action.hoverOpacity), + '@media (hover: none)': { + backgroundColor: 'transparent', + }, + }, + variants: [ + { + props: { selected: true, color: 'standard' }, + style: { + color: (theme.vars || theme).palette.text.primary, + backgroundColor: theme.vars + ? `rgba(${theme.vars.palette.text.primaryChannel} / ${theme.vars.palette.action.selectedOpacity})` + : alpha(theme.palette.text.primary, theme.palette.action.selectedOpacity), + '&:hover': { + backgroundColor: theme.vars + ? `rgba(${theme.vars.palette.text.primaryChannel} / calc(${theme.vars.palette.action.selectedOpacity} + ${theme.vars.palette.action.hoverOpacity}))` + : alpha( + theme.palette.text.primary, + theme.palette.action.selectedOpacity + theme.palette.action.hoverOpacity, + ), + // Reset on touch devices, it doesn't add specificity + '@media (hover: none)': { + backgroundColor: theme.vars + ? `rgba(${theme.vars.palette.text.primaryChannel} / ${theme.vars.palette.action.selectedOpacity})` + : alpha(theme.palette.text.primary, theme.palette.action.selectedOpacity), + }, + }, + }, + }, + { + props: { color: 'primary' }, + style: { + color: (theme.vars || theme).palette.primary.main, + }, + }, + { + props: { selected: true, color: 'primary' }, + style: { + backgroundColor: theme.vars + ? `rgba(${theme.vars.palette.primary.mainChannel} / ${theme.vars.palette.action.selectedOpacity})` + : alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity), + '&:hover': { + backgroundColor: theme.vars + ? `rgba(${theme.vars.palette.primary.mainChannel} / calc(${theme.vars.palette.action.selectedOpacity} + ${theme.vars.palette.action.hoverOpacity}))` + : alpha( + theme.palette.primary.main, + theme.palette.action.selectedOpacity + theme.palette.action.hoverOpacity, + ), + // Reset on touch devices, it doesn't add specificity + '@media (hover: none)': { + backgroundColor: theme.vars + ? `rgba(${theme.vars.palette.primary.mainChannel} / ${theme.vars.palette.action.selectedOpacity})` + : alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity), + }, + }, + }, + }, + ], +})); + +const GridToolbarButton = React.forwardRef( + function GridToolbarButton(props, ref) { + const { children, className, color = 'standard', ...other } = props; + const rootProps = useGridRootProps(); + const classes = useUtilityClasses(rootProps); + + return ( + + {children} + + ); + }, +); + +export { GridToolbarButton }; diff --git a/packages/x-data-grid/src/components/grid/toolbar/GridToolbarRoot.tsx b/packages/x-data-grid/src/components/grid/toolbar/GridToolbarRoot.tsx new file mode 100644 index 000000000000..91a0d327c586 --- /dev/null +++ b/packages/x-data-grid/src/components/grid/toolbar/GridToolbarRoot.tsx @@ -0,0 +1,74 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import clsx from 'clsx'; +import { styled, SxProps, Theme } from '@mui/material/styles'; +import composeClasses from '@mui/utils/composeClasses'; +import { getDataGridUtilityClass } from '../../../constants/gridClasses'; +import type { DataGridProcessedProps } from '../../../models/props/DataGridProps'; +import { useGridRootProps } from '../../../hooks/utils/useGridRootProps'; + +export type GridToolbarRootProps = React.HTMLAttributes & { + sx?: SxProps; +}; + +type OwnerState = DataGridProcessedProps; + +const useUtilityClasses = (ownerState: OwnerState) => { + const { classes } = ownerState; + + const slots = { + root: ['toolbarRoot'], + }; + + return composeClasses(slots, getDataGridUtilityClass, classes); +}; + +const StyledGridToolbarRoot = styled('div', { + name: 'MuiDataGrid', + slot: 'ToolbarRoot', + overridesResolver: (_, styles) => styles.toolbarRoot, +})<{ ownerState: OwnerState }>(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + gap: theme.spacing(0.25), + padding: theme.spacing(0.5), + minHeight: 45, + borderBottom: `1px solid ${theme.palette.divider}`, +})); + +const GridToolbarRoot = React.forwardRef( + function GridToolbarRoot(props, ref) { + const { className, children, ...other } = props; + const rootProps = useGridRootProps(); + const classes = useUtilityClasses(rootProps); + if (!children) { + return null; + } + + return ( + + {children} + + ); + }, +); + +GridToolbarRoot.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the TypeScript types and run "pnpm proptypes" | + // ---------------------------------------------------------------------- + sx: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), + PropTypes.func, + PropTypes.object, + ]), +} as any; + +export { GridToolbarRoot }; diff --git a/packages/x-data-grid/src/components/grid/toolbar/GridToolbarSeparator.tsx b/packages/x-data-grid/src/components/grid/toolbar/GridToolbarSeparator.tsx new file mode 100644 index 000000000000..62c3aa1794d5 --- /dev/null +++ b/packages/x-data-grid/src/components/grid/toolbar/GridToolbarSeparator.tsx @@ -0,0 +1,65 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { styled, SxProps, Theme } from '@mui/material/styles'; +import composeClasses from '@mui/utils/composeClasses'; +import clsx from 'clsx'; +import { getDataGridUtilityClass } from '../../../constants/gridClasses'; +import type { DataGridProcessedProps } from '../../../models/props/DataGridProps'; +import { useGridRootProps } from '../../../hooks/utils/useGridRootProps'; + +export interface GridToolbarSeparatorProps extends React.HTMLAttributes { + sx?: SxProps; +} + +type OwnerState = DataGridProcessedProps; + +const useUtilityClasses = (ownerState: OwnerState) => { + const { classes } = ownerState; + + const slots = { + root: ['toolbarSeparator'], + }; + + return composeClasses(slots, getDataGridUtilityClass, classes); +}; + +const Separator = styled('div', { + name: 'MuiDataGrid', + slot: 'Separator', + overridesResolver: (_, styles) => styles.separator, +})<{ ownerState: OwnerState }>(({ theme }) => ({ + height: 24, + width: 1, + margin: theme.spacing(0.25), + backgroundColor: theme.palette.divider, +})); + +function GridToolbarSeparator(props: GridToolbarSeparatorProps) { + const rootProps = useGridRootProps(); + const classes = useUtilityClasses(rootProps); + const { className, ...other } = props; + + return ( + + ); +} + +GridToolbarSeparator.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the TypeScript types and run "pnpm proptypes" | + // ---------------------------------------------------------------------- + sx: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), + PropTypes.func, + PropTypes.object, + ]), +} as any; + +export { GridToolbarSeparator }; diff --git a/packages/x-data-grid/src/components/grid/toolbar/GridToolbarToggleButton.tsx b/packages/x-data-grid/src/components/grid/toolbar/GridToolbarToggleButton.tsx new file mode 100644 index 000000000000..f37ec5cdc1be --- /dev/null +++ b/packages/x-data-grid/src/components/grid/toolbar/GridToolbarToggleButton.tsx @@ -0,0 +1,56 @@ +import * as React from 'react'; +import ToggleButton, { ToggleButtonProps } from '@mui/material/ToggleButton'; +import { styled } from '@mui/system'; +import composeClasses from '@mui/utils/composeClasses'; +import clsx from 'clsx'; +import { useGridRootProps } from '../../../hooks/utils/useGridRootProps'; +import { getDataGridUtilityClass } from '../../../constants/gridClasses'; +import type { DataGridProcessedProps } from '../../../models/props/DataGridProps'; + +export type GridToolbarToggleButtonProps = ToggleButtonProps; + +type OwnerState = DataGridProcessedProps; + +const useUtilityClasses = (ownerState: OwnerState) => { + const { classes } = ownerState; + + const slots = { + root: ['toolbarToggleButton'], + }; + + return composeClasses(slots, getDataGridUtilityClass, classes); +}; + +const StyledGridToolbarToggleButton = styled(ToggleButton, { + name: 'MuiDataGrid', + slot: 'ToolbarToggleButton', + overridesResolver: (_, styles) => styles.toolbarToggleButton, +})<{ ownerState: OwnerState }>(({ theme }) => ({ + gap: theme.spacing(0.5), + minHeight: 34, + lineHeight: 'normal', +})); + +const GridToolbarToggleButton = React.forwardRef( + function GridToolbarToggleButton(props, ref) { + const { children, className, size = 'small', ...other } = props; + const rootProps = useGridRootProps(); + const classes = useUtilityClasses(rootProps); + + return ( + + {children} + + ); + }, +); + +export { GridToolbarToggleButton }; diff --git a/packages/x-data-grid/src/components/grid/toolbar/GridToolbarToggleButtonGroup.tsx b/packages/x-data-grid/src/components/grid/toolbar/GridToolbarToggleButtonGroup.tsx new file mode 100644 index 000000000000..e8c25e13dd33 --- /dev/null +++ b/packages/x-data-grid/src/components/grid/toolbar/GridToolbarToggleButtonGroup.tsx @@ -0,0 +1,40 @@ +import * as React from 'react'; +import clsx from 'clsx'; +import { ToggleButtonGroupProps } from '@mui/material/ToggleButtonGroup'; +import composeClasses from '@mui/utils/composeClasses'; +import { useGridRootProps } from '../../../hooks/utils/useGridRootProps'; +import { DataGridProcessedProps } from '../../../models/props/DataGridProps'; +import { getDataGridUtilityClass } from '../../../constants/gridClasses'; + +export type GridToolbarToggleButtonGroupProps = ToggleButtonGroupProps; + +type OwnerState = DataGridProcessedProps; + +const useUtilityClasses = (ownerState: OwnerState) => { + const { classes } = ownerState; + + const slots = { + root: ['toolbarToggleButtonGroup'], + }; + + return composeClasses(slots, getDataGridUtilityClass, classes); +}; + +function GridToolbarToggleButtonGroup(props: GridToolbarToggleButtonGroupProps) { + const rootProps = useGridRootProps(); + const classes = useUtilityClasses(rootProps); + const { children, className, ...other } = props; + + return ( + + {children} + + ); +} + +export { GridToolbarToggleButtonGroup }; diff --git a/packages/x-data-grid/src/components/grid/toolbar/index.parts.ts b/packages/x-data-grid/src/components/grid/toolbar/index.parts.ts new file mode 100644 index 000000000000..5019e808c303 --- /dev/null +++ b/packages/x-data-grid/src/components/grid/toolbar/index.parts.ts @@ -0,0 +1,5 @@ +export { GridToolbarRoot as Root } from './GridToolbarRoot'; +export { GridToolbarButton as Button } from './GridToolbarButton'; +export { GridToolbarToggleButton as ToggleButton } from './GridToolbarToggleButton'; +export { GridToolbarToggleButtonGroup as ToggleButtonGroup } from './GridToolbarToggleButtonGroup'; +export { GridToolbarSeparator as Separator } from './GridToolbarSeparator'; diff --git a/packages/x-data-grid/src/components/grid/toolbar/index.ts b/packages/x-data-grid/src/components/grid/toolbar/index.ts new file mode 100644 index 000000000000..9c113a2dfc28 --- /dev/null +++ b/packages/x-data-grid/src/components/grid/toolbar/index.ts @@ -0,0 +1,5 @@ +export * from './GridToolbarRoot'; +export * from './GridToolbarSeparator'; +export * from './GridToolbarButton'; +export * from './GridToolbarToggleButtonGroup'; +export * from './GridToolbarToggleButton'; diff --git a/packages/x-data-grid/src/components/index.ts b/packages/x-data-grid/src/components/index.ts index 2657dac0cd89..579b7fdcb718 100644 --- a/packages/x-data-grid/src/components/index.ts +++ b/packages/x-data-grid/src/components/index.ts @@ -18,3 +18,6 @@ export { GridPagination } from './GridPagination'; export * from './GridRowCount'; export * from './GridRow'; export * from './GridSelectedRowCount'; + +export * from './grid'; +export * as Grid from './grid/index.parts'; diff --git a/packages/x-data-grid/src/components/toolbar/GridToolbarFilterButton.tsx b/packages/x-data-grid/src/components/toolbar/GridToolbarFilterButton.tsx index 29a4a8c637f6..00fc6d21044e 100644 --- a/packages/x-data-grid/src/components/toolbar/GridToolbarFilterButton.tsx +++ b/packages/x-data-grid/src/components/toolbar/GridToolbarFilterButton.tsx @@ -155,7 +155,7 @@ const GridToolbarFilterButton = React.forwardRef - + } onClick={toggleFilter} diff --git a/packages/x-data-grid/src/components/toolbar/GridToolbarQuickFilter.tsx b/packages/x-data-grid/src/components/toolbar/GridToolbarQuickFilter.tsx index 231649853ab3..853775a48736 100644 --- a/packages/x-data-grid/src/components/toolbar/GridToolbarQuickFilter.tsx +++ b/packages/x-data-grid/src/components/toolbar/GridToolbarQuickFilter.tsx @@ -2,9 +2,11 @@ import * as React from 'react'; import clsx from 'clsx'; import PropTypes from 'prop-types'; import TextField, { TextFieldProps } from '@mui/material/TextField'; +import InputAdornment from '@mui/material/InputAdornment'; import { styled } from '@mui/material/styles'; import { unstable_debounce as debounce } from '@mui/utils'; import composeClasses from '@mui/utils/composeClasses'; +import { outlinedInputClasses } from '@mui/material/OutlinedInput'; import { getDataGridUtilityClass } from '../../constants'; import { useGridApiContext } from '../../hooks/utils/useGridApiContext'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; @@ -31,13 +33,11 @@ const GridToolbarQuickFilterRoot = styled(TextField, { slot: 'ToolbarQuickFilter', overridesResolver: (props, styles) => styles.toolbarQuickFilter, })<{ ownerState: OwnerState }>(({ theme }) => ({ - width: 'auto', - paddingBottom: theme.spacing(0.5), - '& input': { - marginLeft: theme.spacing(0.5), + [`.${outlinedInputClasses.root}`]: { + fontSize: theme.typography.body2.fontSize, }, - '& .MuiInput-underline:before': { - borderBottom: `1px solid ${(theme.vars || theme).palette.divider}`, + [`.${outlinedInputClasses.notchedOutline}`]: { + borderColor: theme.palette.divider, }, [`& input[type="search"]::-webkit-search-decoration, & input[type="search"]::-webkit-search-cancel-button, @@ -144,7 +144,8 @@ function GridToolbarQuickFilter(props: GridToolbarQuickFilterProps) { , + startAdornment: ( + + + + ), endAdornment: ( - - - + + + + + ), ...other.InputProps, }} diff --git a/packages/x-data-grid/src/constants/gridClasses.ts b/packages/x-data-grid/src/constants/gridClasses.ts index 30e9df86d3d0..99bdf2405097 100644 --- a/packages/x-data-grid/src/constants/gridClasses.ts +++ b/packages/x-data-grid/src/constants/gridClasses.ts @@ -592,6 +592,26 @@ export interface GridClasses { * Styles applied to the toolbar filter list element. */ toolbarFilterList: string; + /** + * Styles applied to the toolbar root element. + */ + toolbarRoot: string; + /** + * Styles applied to the toolbar button element. + */ + toolbarButton: string; + /** + * Styles applied to the toolbar separator element. + */ + toolbarSeparator: string; + /** + * Styles applied to the toolbar toggle button element. + */ + toolbarToggleButton: string; + /** + * Styles applied to the toolbar toggle button group element. + */ + toolbarToggleButtonGroup: string; /** * Styles applied to the toolbar prompt control element. */ @@ -823,6 +843,9 @@ export const gridClasses = generateUtilityClasses('MuiDataGrid', [ 'sortIcon', 'toolbarContainer', 'toolbarFilterList', + 'toolbarSeparator', + 'toolbarToggleButton', + 'toolbarToggleButtonGroup', 'toolbarPromptControl', 'toolbarPromptControl--recording', 'toolbarPromptControlRecordingIndicator', diff --git a/packages/x-data-grid/src/hooks/utils/index.ts b/packages/x-data-grid/src/hooks/utils/index.ts index 642c9db07366..2cdb466cd76e 100644 --- a/packages/x-data-grid/src/hooks/utils/index.ts +++ b/packages/x-data-grid/src/hooks/utils/index.ts @@ -6,3 +6,4 @@ export * from './useGridNativeEventListener'; export * from './useFirstRender'; export * from './useOnMount'; export * from './useRunOnce'; +export * from './useGridComponentRenderer'; diff --git a/packages/x-data-grid/src/hooks/utils/useGridComponentRenderer.test.tsx b/packages/x-data-grid/src/hooks/utils/useGridComponentRenderer.test.tsx new file mode 100644 index 000000000000..8134a80e3ebc --- /dev/null +++ b/packages/x-data-grid/src/hooks/utils/useGridComponentRenderer.test.tsx @@ -0,0 +1,133 @@ +import * as React from 'react'; +import { expect } from 'chai'; +import { createRenderer, screen } from '@mui/internal-test-utils'; +import Box, { BoxProps } from '@mui/material/Box'; +import { RenderProp, useGridComponentRenderer } from './useGridComponentRenderer'; + +const isJSDOM = /jsdom/.test(window.navigator.userAgent); + +describe('useGridComponentRenderer', () => { + const { render } = createRenderer(); + + function TestComponent( + props: React.ComponentPropsWithoutRef<'button'> & { + render?: RenderProp<{ someState: string }>; + }, + ) { + const { render: renderProp, ...other } = props; + const { renderElement } = useGridComponentRenderer({ + defaultElement: 'button', + props: other, + render: renderProp, + state: { someState: 'state value' }, + }); + return renderElement(); + } + + it('should render intrinsic element type as default element', () => { + render(children); + expect(screen.getByTestId('rendered-element')).to.be.instanceOf(window.HTMLButtonElement); + expect(screen.getByTestId('rendered-element')).to.have.text('children'); + }); + + it('should render component type as default element', () => { + function CustomButton(props: React.ComponentPropsWithoutRef<'button'>) { + return