+
\ 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
index 8f74f6f97e139..7c11cc8e781f8 100644
--- a/docs/data/data-grid/components/toolbar/toolbar.md
+++ b/docs/data/data-grid/components/toolbar/toolbar.md
@@ -39,6 +39,22 @@ It renders a styled `
` element.
`` is a button for performing actions from the toolbar.
It renders the `baseIconButton` slot.
+## Reusing the toolbar styles
+
+Use the `` component to apply the toolbar styles to elements outside of the toolbar, so they stay in sync with the Data Grid.
+
+```tsx
+import { ToolbarRoot } from '@mui/x-data-grid';
+
+Header;
+```
+
+It can be rendered anywhere, including outside of a Data Grid, and picks up the theme values the Data Grid uses.
+It only applies the styles.
+The toolbar subcomponents such as [Toolbar Button](#toolbar-button) render Data Grid slots and register with the toolbar context, so they need a [Toolbar](#toolbar) inside a Data Grid.
+
+{{"demo": "GridToolbarStyleReuse.js", "bg": "inline", "defaultCodeOpen": false}}
+
## Recipes
Below are some ways the Toolbar component can be used.
diff --git a/packages/x-data-grid/src/components/toolbarV8/Toolbar.tsx b/packages/x-data-grid/src/components/toolbarV8/Toolbar.tsx
index 0a2caddfb40b6..3092ad4c2ecea 100644
--- a/packages/x-data-grid/src/components/toolbarV8/Toolbar.tsx
+++ b/packages/x-data-grid/src/components/toolbarV8/Toolbar.tsx
@@ -9,6 +9,12 @@ import { useComponentRenderer } from '@mui/x-internals/useComponentRenderer';
import type { RenderProp } from '@mui/x-internals/useComponentRenderer';
import { ToolbarContextProvider } from '@mui/x-internals/ToolbarContext';
import { vars } from '../../constants/cssVariables';
+import {
+ getBackgroundBase,
+ getBorderColor,
+ getFontBody,
+ getSpacingUnit,
+} from '../../material/variables';
import { getDataGridUtilityClass } from '../../constants/gridClasses';
import { useGridRootProps } from '../../hooks/utils/useGridRootProps';
import type { DataGridProcessedProps } from '../../models/props/DataGridProps';
@@ -32,19 +38,38 @@ const useUtilityClasses = (ownerState: OwnerState) => {
return composeClasses(slots, getDataGridUtilityClass, classes);
};
+/**
+ * The styled `` element rendered by the `` component.
+ * Use it to apply the toolbar styles to other elements, inside or outside of a Data Grid.
+ */
const ToolbarRoot = styled('div', {
name: 'MuiDataGrid',
slot: 'Toolbar',
-})({
- flex: '0 1 1px',
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'end',
- gap: vars.spacing(0.25),
- padding: vars.spacing(0.75),
- minHeight: 52,
- boxSizing: 'border-box',
- borderBottom: `1px solid ${vars.colors.border.base}`,
+})(({ theme }) => {
+ // The Data Grid CSS variables are only defined on the Data Grid root element, so they are
+ // declared with a fallback to keep the styles working when rendered outside of a Data Grid.
+ const spacingUnit = `var(${vars.keys.spacingUnit}, ${getSpacingUnit(theme)})`;
+ const borderColor = `var(${vars.keys.colors.border.base}, ${getBorderColor(theme)})`;
+ const background = `var(${vars.keys.colors.background.base}, ${getBackgroundBase(theme)})`;
+ const foreground = `var(${vars.keys.colors.foreground.base}, ${(theme.vars || theme).palette.text.primary})`;
+ const fontBody = getFontBody(theme);
+
+ return {
+ flex: '0 1 1px',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'end',
+ gap: `calc(${spacingUnit} * 0.25)`,
+ padding: `calc(${spacingUnit} * 0.75)`,
+ minHeight: 52,
+ boxSizing: 'border-box',
+ borderBottom: `1px solid ${borderColor}`,
+ // Inherited from the Data Grid root inside a grid, declared here so the component keeps
+ // matching it when rendered on its own.
+ backgroundColor: background,
+ color: foreground,
+ font: fontBody ? `var(${vars.keys.typography.font.body}, ${fontBody})` : undefined,
+ };
});
/**
@@ -87,4 +112,4 @@ Toolbar.propTypes /* remove-proptypes */ = {
render: PropTypes.oneOfType([PropTypes.element, PropTypes.func]),
} as any;
-export { Toolbar };
+export { Toolbar, ToolbarRoot };
diff --git a/packages/x-data-grid/src/material/variables.ts b/packages/x-data-grid/src/material/variables.ts
index e66fec1ecda89..719f4e32be464 100644
--- a/packages/x-data-grid/src/material/variables.ts
+++ b/packages/x-data-grid/src/material/variables.ts
@@ -21,11 +21,7 @@ function transformTheme(t: Theme): GridCSSVariablesInterface {
const paperColor = (t.vars || t).palette.background.paper;
- const backgroundBase =
- dataGridPalette?.bg ??
- (t.palette.mode === 'dark'
- ? colorMixIfSupported(`color-mix(in srgb, ${paperColor} 95%, #fff)`, paperColor)
- : paperColor);
+ const backgroundBase = getBackgroundBase(t);
const backgroundHeader = dataGridPalette?.headerBg ?? backgroundBase;
const backgroundPinned = dataGridPalette?.pinnedBg ?? backgroundBase;
const backgroundBackdrop = t.alpha(
@@ -41,13 +37,13 @@ function transformTheme(t: Theme): GridCSSVariablesInterface {
const radius = getRadius(t);
- const fontBody = (t.vars as any)?.font?.body2 ?? formatFont(t.typography.body2);
+ const fontBody = getFontBody(t);
const fontSmall = (t.vars as any)?.font?.caption ?? formatFont(t.typography.caption);
const fontLarge = (t.vars as any)?.font?.body1 ?? formatFont(t.typography.body1);
const k = vars.keys;
return {
- [k.spacingUnit]: t.vars ? ((t.vars as any).spacing ?? t.spacing(1)) : t.spacing(1),
+ [k.spacingUnit]: getSpacingUnit(t),
[k.colors.border.base]: borderColor,
[k.colors.background.base]: backgroundBase,
@@ -111,7 +107,25 @@ function getRadius(theme: Theme) {
: theme.shape.borderRadius;
}
-function getBorderColor(theme: Theme) {
+export function getBackgroundBase(theme: Theme) {
+ const paperColor = (theme.vars || theme).palette.background.paper;
+ return (
+ (theme.vars || theme).palette.DataGrid?.bg ??
+ (theme.palette.mode === 'dark'
+ ? colorMixIfSupported(`color-mix(in srgb, ${paperColor} 95%, #fff)`, paperColor)
+ : paperColor)
+ );
+}
+
+export function getFontBody(theme: Theme) {
+ return (theme.vars as any)?.font?.body2 ?? formatFont(theme.typography.body2);
+}
+
+export function getSpacingUnit(theme: Theme) {
+ return theme.vars ? ((theme.vars as any).spacing ?? theme.spacing(1)) : theme.spacing(1);
+}
+
+export function getBorderColor(theme: Theme) {
if (theme.vars) {
return theme.vars.palette.TableCell.border;
}
diff --git a/packages/x-data-grid/src/tests/toolbar.DataGrid.test.tsx b/packages/x-data-grid/src/tests/toolbar.DataGrid.test.tsx
index c230d6b8ba94d..e729baba9a114 100644
--- a/packages/x-data-grid/src/tests/toolbar.DataGrid.test.tsx
+++ b/packages/x-data-grid/src/tests/toolbar.DataGrid.test.tsx
@@ -1,6 +1,7 @@
+import * as React from 'react';
import { createRenderer, screen, act } from '@mui/internal-test-utils';
import { getColumnHeadersTextContent } from 'test/utils/helperFn';
-import { DataGrid, Toolbar, ToolbarButton } from '@mui/x-data-grid';
+import { DataGrid, Toolbar, ToolbarButton, ToolbarRoot } from '@mui/x-data-grid';
import type { GridColumnsManagementProps } from '@mui/x-data-grid';
import { isJSDOM } from 'test/utils/skipIf';
import { describe, it, expect } from 'vitest';
@@ -413,6 +414,70 @@ describe(' - Toolbar', () => {
});
});
+ describe('ToolbarRoot', () => {
+ it('should render a div with the toolbar styles outside of the Data Grid', () => {
+ render(Header);
+
+ const element = screen.getByTestId('custom-header');
+ expect(element.tagName).to.equal('DIV');
+ expect(element).toHaveComputedStyle({
+ display: 'flex',
+ alignItems: 'center',
+ minHeight: '52px',
+ });
+ });
+
+ it('should apply the same styles as the element rendered by Toolbar', () => {
+ render(
+
+
+
+ ,
+ );
+
+ const toolbarClasses = new Set(screen.getByRole('toolbar').classList);
+ const rootClasses = Array.from(screen.getByTestId('custom-header').classList);
+ // The styled component class is shared between both elements.
+ expect(rootClasses.some((className) => toolbarClasses.has(className))).to.equal(true);
+ });
+
+ // The CSS variable fallbacks are resolved by the browser, jsdom keeps them unresolved.
+ it.skipIf(isJSDOM)(
+ 'should fall back to the theme values for the variables the Data Grid defines',
+ () => {
+ render(
+
+
+
+ ,
+ );
+
+ const toolbarStyle = getComputedStyle(screen.getByRole('toolbar'));
+ const rootStyle = getComputedStyle(screen.getByTestId('custom-header'));
+
+ expect(rootStyle.padding).not.to.equal('0px');
+ expect(rootStyle.padding).to.equal(toolbarStyle.padding);
+ expect(rootStyle.borderBottomColor).to.equal(toolbarStyle.borderBottomColor);
+ expect(rootStyle.borderBottomWidth).to.equal(toolbarStyle.borderBottomWidth);
+ expect(rootStyle.backgroundColor).to.equal(toolbarStyle.backgroundColor);
+ expect(rootStyle.color).to.equal(toolbarStyle.color);
+ expect(rootStyle.font).to.equal(toolbarStyle.font);
+ },
+ );
+
+ // The CSS variable fallbacks are resolved by the browser, jsdom keeps them unresolved.
+ it.skipIf(isJSDOM)('should prefer the Data Grid variables over the fallbacks', () => {
+ render(
+