diff --git a/packages/mui-material/src/useMediaQuery/index.d.ts b/packages/mui-material/src/useMediaQuery/index.d.ts index 189cf6a41d036a..534d0663384e7f 100644 --- a/packages/mui-material/src/useMediaQuery/index.d.ts +++ b/packages/mui-material/src/useMediaQuery/index.d.ts @@ -1,2 +1,11 @@ -export { default } from '@mui/system/useMediaQuery'; +import { UseMediaQueryOptions } from '@mui/system/useMediaQuery'; +import { Theme } from '../styles/createTheme'; + export * from '@mui/system/useMediaQuery'; + +declare function useMediaQuery( + queryInput: string | ((theme: Theme) => string), + options?: UseMediaQueryOptions, +): boolean; + +export default useMediaQuery; diff --git a/packages/mui-material/src/useMediaQuery/index.js b/packages/mui-material/src/useMediaQuery/index.js index ebc567714f0478..3773d6445a45d3 100644 --- a/packages/mui-material/src/useMediaQuery/index.js +++ b/packages/mui-material/src/useMediaQuery/index.js @@ -1 +1,6 @@ -export { default } from '@mui/system/useMediaQuery'; +import { unstable_createUseMediaQuery } from '@mui/system/useMediaQuery'; +import THEME_ID from '../styles/identifier'; + +const useMediaQuery = unstable_createUseMediaQuery({ themeId: THEME_ID }); + +export default useMediaQuery; diff --git a/packages/mui-material/src/useMediaQuery/useMediaQuery.test.js b/packages/mui-material/src/useMediaQuery/useMediaQuery.test.js index d5dc1530998c74..2f1059ef72926c 100644 --- a/packages/mui-material/src/useMediaQuery/useMediaQuery.test.js +++ b/packages/mui-material/src/useMediaQuery/useMediaQuery.test.js @@ -11,7 +11,7 @@ import mediaQuery from 'css-mediaquery'; import { expect } from 'chai'; import { stub } from 'sinon'; import useMediaQuery from '@mui/material/useMediaQuery'; -import { ThemeProvider } from '@mui/material/styles'; +import { THEME_ID, ThemeProvider, createTheme } from '@mui/material/styles'; const usesUseSyncExternalStore = React.useSyncExternalStore !== undefined; const matchMediaInstances = new Map(); @@ -344,6 +344,40 @@ describe('useMediaQuery', () => { }); }); + describe('theme scoping', () => { + it('should work with theme scoping', () => { + function MyComponent() { + const matches = useMediaQuery((theme) => theme.breakpoints.up('xl')); + + return {`${matches}`}; + } + + function Test() { + const ssrMatchMedia = (query) => ({ + matches: mediaQuery.match(query, { + width: 3000, + }), + }); + + return ( + + + + ); + } + + const { container } = renderToString(); + + expect(container.firstChild).to.have.text('true'); + }); + }); + describe('warnings', () => { it('warns on invalid `query` argument', () => { function MyComponent() { diff --git a/packages/mui-system/src/useMediaQuery/useMediaQuery.ts b/packages/mui-system/src/useMediaQuery/useMediaQuery.ts index 0c4bf8c9e5e4e1..d3da7b61287469 100644 --- a/packages/mui-system/src/useMediaQuery/useMediaQuery.ts +++ b/packages/mui-system/src/useMediaQuery/useMediaQuery.ts @@ -117,53 +117,64 @@ function useMediaQueryNew( return match; } -export default function useMediaQuery( - queryInput: string | ((theme: Theme) => string), - options: UseMediaQueryOptions = {}, -): boolean { - const theme = useTheme(); - // Wait for jsdom to support the match media feature. - // All the browsers MUI support have this built-in. - // This defensive check is here for simplicity. - // Most of the time, the match media logic isn't central to people tests. - const supportMatchMedia = - typeof window !== 'undefined' && typeof window.matchMedia !== 'undefined'; - const { - defaultMatches = false, - matchMedia = supportMatchMedia ? window.matchMedia : null, - ssrMatchMedia = null, - noSsr = false, - } = getThemeProps({ name: 'MuiUseMediaQuery', props: options, theme }); - - if (process.env.NODE_ENV !== 'production') { - if (typeof queryInput === 'function' && theme === null) { - console.error( - [ - 'MUI: The `query` argument provided is invalid.', - 'You are providing a function without a theme in the context.', - 'One of the parent elements needs to use a ThemeProvider.', - ].join('\n'), - ); +// eslint-disable-next-line @typescript-eslint/naming-convention +export function unstable_createUseMediaQuery(params: { themeId?: string } = {}) { + const { themeId } = params; + return function useMediaQuery( + queryInput: string | ((theme: Theme) => string), + options: UseMediaQueryOptions = {}, + ): boolean { + let theme = useTheme(); + if (theme && themeId) { + theme = (theme as Record)[themeId] || theme; + } + // Wait for jsdom to support the match media feature. + // All the browsers MUI support have this built-in. + // This defensive check is here for simplicity. + // Most of the time, the match media logic isn't central to people tests. + const supportMatchMedia = + typeof window !== 'undefined' && typeof window.matchMedia !== 'undefined'; + const { + defaultMatches = false, + matchMedia = supportMatchMedia ? window.matchMedia : null, + ssrMatchMedia = null, + noSsr = false, + } = getThemeProps({ name: 'MuiUseMediaQuery', props: options, theme }); + + if (process.env.NODE_ENV !== 'production') { + if (typeof queryInput === 'function' && theme === null) { + console.error( + [ + 'MUI: The `query` argument provided is invalid.', + 'You are providing a function without a theme in the context.', + 'One of the parent elements needs to use a ThemeProvider.', + ].join('\n'), + ); + } } - } - - let query = typeof queryInput === 'function' ? queryInput(theme) : queryInput; - query = query.replace(/^@media( ?)/m, ''); - - const useMediaQueryImplementation = - maybeReactUseSyncExternalStore !== undefined ? useMediaQueryNew : useMediaQueryOld; - const match = useMediaQueryImplementation( - query, - defaultMatches, - matchMedia, - ssrMatchMedia, - noSsr, - ); - - if (process.env.NODE_ENV !== 'production') { - // eslint-disable-next-line react-hooks/rules-of-hooks - React.useDebugValue({ query, match }); - } - return match; + let query = typeof queryInput === 'function' ? queryInput(theme) : queryInput; + query = query.replace(/^@media( ?)/m, ''); + + const useMediaQueryImplementation = + maybeReactUseSyncExternalStore !== undefined ? useMediaQueryNew : useMediaQueryOld; + const match = useMediaQueryImplementation( + query, + defaultMatches, + matchMedia, + ssrMatchMedia, + noSsr, + ); + + if (process.env.NODE_ENV !== 'production') { + // eslint-disable-next-line react-hooks/rules-of-hooks + React.useDebugValue({ query, match }); + } + + return match; + }; } + +const useMediaQuery = unstable_createUseMediaQuery(); + +export default useMediaQuery;