diff --git a/.circleci/config.yml b/.circleci/config.yml index a5685e260757..dba93579ac67 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -13,6 +13,14 @@ parameters: description: The name of the workflow to run type: string default: pipeline + with-material-ui-6: + description: Use material-ui v6 for additional checks and tests + type: boolean + default: false + with-react-version: + description: The version of react to be used for the additional tests + type: string + default: stable e2e-base-url: description: The base url for running end-to-end test type: string @@ -90,11 +98,10 @@ commands: git --no-pager diff HEAD - when: - condition: - equal: [material-ui-v6, << pipeline.parameters.workflow >>] + condition: << pipeline.parameters.with-material-ui-6 >> steps: - run: - name: Install @mui/material@next + name: Install @mui/material v6 command: pnpm use-material-ui-v6 jobs: @@ -349,50 +356,38 @@ workflows: requires: - checkout - react-next: + additional-tests: when: - equal: [react-next, << pipeline.parameters.workflow >>] - # triggers: - # - schedule: - # cron: '0 0 * * *' - # filters: - # branches: - # only: - # - master + and: + - equal: [additional, << pipeline.parameters.workflow >>] + - or: + - equal: [true, << pipeline.parameters.with-material-ui-6 >>] + - not: + equal: ['stable', << pipeline.parameters.with-react-version >>] jobs: - test_unit: <<: *default-context - react-version: next - name: test_unit-react@next + name: test_unit_additional + react-version: << pipeline.parameters.with-react-version >> - test_browser: <<: *default-context - react-version: next - name: test_browser-react@next + name: test_browser_additional + react-version: << pipeline.parameters.with-react-version >> - test_regressions: <<: *default-context - react-version: next - name: test_regressions-react@next + name: test_regressions_additional + react-version: << pipeline.parameters.with-react-version >> - test_e2e: <<: *default-context - react-version: next - name: test_e2e-react@next + name: test_e2e_additional + react-version: << pipeline.parameters.with-react-version >> - material-ui-v6: + additional-checks: when: - equal: [material-ui-v6, << pipeline.parameters.workflow >>] + and: + - equal: [additional, << pipeline.parameters.workflow >>] + - equal: [true, << pipeline.parameters.with-material-ui-6 >>] jobs: - - test_unit: - <<: *default-context - name: test_unit-material@next - - test_browser: - <<: *default-context - name: test_browser-material@next - - test_regressions: - <<: *default-context - name: test_regressions-material@next - - test_e2e: - <<: *default-context - name: test_e2e-material@next - test_types: <<: *default-context - name: test_types-material@next + name: test_types_additional diff --git a/docs/data/charts/getting-started/getting-started.md b/docs/data/charts/getting-started/getting-started.md index bfac2ba84414..7efdf925eebf 100644 --- a/docs/data/charts/getting-started/getting-started.md +++ b/docs/data/charts/getting-started/getting-started.md @@ -41,8 +41,8 @@ yarn add @mui/material @emotion/react @emotion/styled ```json "peerDependencies": { - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, ``` diff --git a/docs/data/data-grid/custom-columns/cell-renderers/rating.tsx b/docs/data/data-grid/custom-columns/cell-renderers/rating.tsx index 9eb9c68ce767..56160cf6caf6 100644 --- a/docs/data/data-grid/custom-columns/cell-renderers/rating.tsx +++ b/docs/data/data-grid/custom-columns/cell-renderers/rating.tsx @@ -46,7 +46,7 @@ function EditRating(props: GridRenderEditCellParams) { changedThroughKeyboard.current = false; }; - const handleRef = (element: HTMLElement | undefined) => { + const handleRef = (element: HTMLElement | null) => { if (element) { if (value !== 0) { element.querySelector(`input[value="${value}"]`)!.focus(); diff --git a/docs/data/data-grid/getting-started/getting-started.md b/docs/data/data-grid/getting-started/getting-started.md index 2b77ca3b6ee8..a7e2494e3083 100644 --- a/docs/data/data-grid/getting-started/getting-started.md +++ b/docs/data/data-grid/getting-started/getting-started.md @@ -35,8 +35,8 @@ Please note that [react](https://www.npmjs.com/package/react) and [react-dom](ht ```json "peerDependencies": { - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, ``` diff --git a/docs/data/date-pickers/getting-started/getting-started.md b/docs/data/date-pickers/getting-started/getting-started.md index e57e07a9de80..c3a703efd7b4 100644 --- a/docs/data/date-pickers/getting-started/getting-started.md +++ b/docs/data/date-pickers/getting-started/getting-started.md @@ -58,8 +58,8 @@ yarn add @mui/material @emotion/react @emotion/styled ```json "peerDependencies": { - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, ``` diff --git a/docs/data/tree-view/getting-started/getting-started.md b/docs/data/tree-view/getting-started/getting-started.md index 6acc9e5382c1..bc146ab1f18f 100644 --- a/docs/data/tree-view/getting-started/getting-started.md +++ b/docs/data/tree-view/getting-started/getting-started.md @@ -44,8 +44,8 @@ Please note that [react](https://www.npmjs.com/package/react) and [react-dom](ht ```json "peerDependencies": { - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, ``` diff --git a/packages/x-charts-pro/README.md b/packages/x-charts-pro/README.md index 4e4c99c2e35a..05616ba80e2b 100644 --- a/packages/x-charts-pro/README.md +++ b/packages/x-charts-pro/README.md @@ -16,8 +16,8 @@ This component has the following peer dependencies that you need to install as w ```json "peerDependencies": { "@mui/material": "^5.15.14 || ^6.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, ``` diff --git a/packages/x-charts-pro/package.json b/packages/x-charts-pro/package.json index d3df0c0605b3..9960214e81ca 100644 --- a/packages/x-charts-pro/package.json +++ b/packages/x-charts-pro/package.json @@ -55,8 +55,8 @@ "@emotion/styled": "^11.8.1", "@mui/material": "^5.15.14 || ^6.0.0", "@mui/system": "^5.15.14 || ^6.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/react": { diff --git a/packages/x-charts/README.md b/packages/x-charts/README.md index cda7ac8e591e..7eafec7d9e44 100644 --- a/packages/x-charts/README.md +++ b/packages/x-charts/README.md @@ -16,8 +16,8 @@ This component has the following peer dependencies that you need to install as w ```json "peerDependencies": { "@mui/material": "^5.15.14 || ^6.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, ``` diff --git a/packages/x-charts/package.json b/packages/x-charts/package.json index 94f595857921..42cce10cd1d2 100644 --- a/packages/x-charts/package.json +++ b/packages/x-charts/package.json @@ -55,8 +55,8 @@ "@emotion/styled": "^11.8.1", "@mui/material": "^5.15.14 || ^6.0.0", "@mui/system": "^5.15.14 || ^6.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/react": { diff --git a/packages/x-charts/src/context/AnimationProvider/useSkipAnimation.test.tsx b/packages/x-charts/src/context/AnimationProvider/useSkipAnimation.test.tsx index 997cdbd30429..bb0f82706104 100644 --- a/packages/x-charts/src/context/AnimationProvider/useSkipAnimation.test.tsx +++ b/packages/x-charts/src/context/AnimationProvider/useSkipAnimation.test.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { expect } from 'chai'; -import { ErrorBoundary, createRenderer, screen } from '@mui/internal-test-utils'; +import { ErrorBoundary, createRenderer, screen, reactMajor } from '@mui/internal-test-utils'; import { useSkipAnimation } from './useSkipAnimation'; import { AnimationProvider } from './AnimationProvider'; @@ -37,17 +37,22 @@ describe('useSkipAnimation', () => { const errorRef = React.createRef(); + const errorMessage1 = 'MUI X: Could not find the animation ref context.'; + const errorMessage2 = + 'It looks like you rendered your component outside of a ChartsContainer parent component.'; + const errorMessage3 = 'The above error occurred in the component:'; + const expextedError = + reactMajor < 19 + ? [errorMessage1, errorMessage2, errorMessage3] + : `${errorMessage1}\n${errorMessage2}`; + expect(() => render( , ), - ).toErrorDev([ - 'MUI X: Could not find the animation ref context.', - 'It looks like you rendered your component outside of a ChartsContainer parent component.', - 'The above error occurred in the component:', - ]); + ).toErrorDev(expextedError); expect((errorRef.current as any).errors).to.have.length(1); expect((errorRef.current as any).errors[0].toString()).to.include( diff --git a/packages/x-charts/src/context/HighlightedProvider/useHighlighted.test.tsx b/packages/x-charts/src/context/HighlightedProvider/useHighlighted.test.tsx index 29337e8d7191..fb17106413fd 100644 --- a/packages/x-charts/src/context/HighlightedProvider/useHighlighted.test.tsx +++ b/packages/x-charts/src/context/HighlightedProvider/useHighlighted.test.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { expect } from 'chai'; -import { ErrorBoundary, createRenderer, screen } from '@mui/internal-test-utils'; +import { ErrorBoundary, createRenderer, screen, reactMajor } from '@mui/internal-test-utils'; import { useHighlighted } from './useHighlighted'; import { HighlightedProvider } from './HighlightedProvider'; import { SeriesProvider } from '../SeriesProvider'; @@ -23,17 +23,22 @@ describe('useHighlighted', () => { const errorRef = React.createRef(); + const errorMessage1 = 'MUI X: Could not find the highlighted ref context.'; + const errorMessage2 = + 'It looks like you rendered your component outside of a ChartsContainer parent component.'; + const errorMessage3 = 'The above error occurred in the component:'; + const expextedError = + reactMajor < 19 + ? [errorMessage1, errorMessage2, errorMessage3] + : `${errorMessage1}\n${errorMessage2}`; + expect(() => render( , ), - ).toErrorDev([ - 'MUI X: Could not find the highlighted ref context.', - 'It looks like you rendered your component outside of a ChartsContainer parent component.', - 'The above error occurred in the component:', - ]); + ).toErrorDev(expextedError); expect((errorRef.current as any).errors).to.have.length(1); expect((errorRef.current as any).errors[0].toString()).to.include( diff --git a/packages/x-charts/src/hooks/useSeries.test.tsx b/packages/x-charts/src/hooks/useSeries.test.tsx index 4bbe16ff5cb6..8bc52cb6c2e3 100644 --- a/packages/x-charts/src/hooks/useSeries.test.tsx +++ b/packages/x-charts/src/hooks/useSeries.test.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { expect } from 'chai'; -import { ErrorBoundary, createRenderer, screen } from '@mui/internal-test-utils'; +import { ErrorBoundary, createRenderer, reactMajor, screen } from '@mui/internal-test-utils'; import { useSeries } from './useSeries'; import { SeriesProvider } from '../context/SeriesProvider'; import { PluginProvider } from '../internals'; @@ -22,17 +22,22 @@ describe('useSeries', () => { const errorRef = React.createRef(); + const errorMessage1 = 'MUI X: Could not find the series ref context.'; + const errorMessage2 = + 'It looks like you rendered your component outside of a ChartsContainer parent component.'; + const errorMessage3 = 'The above error occurred in the component:'; + const expextedError = + reactMajor < 19 + ? [errorMessage1, errorMessage2, errorMessage3] + : `${errorMessage1}\n${errorMessage2}`; + expect(() => render( , ), - ).toErrorDev([ - 'MUI X: Could not find the series ref context.', - 'It looks like you rendered your component outside of a ChartsContainer parent component.', - 'The above error occurred in the component:', - ]); + ).toErrorDev(expextedError); expect((errorRef.current as any).errors).to.have.length(1); expect((errorRef.current as any).errors[0].toString()).to.include( diff --git a/packages/x-charts/src/hooks/useSvgRef.test.tsx b/packages/x-charts/src/hooks/useSvgRef.test.tsx index e6d16c2beab9..d03aca479c9d 100644 --- a/packages/x-charts/src/hooks/useSvgRef.test.tsx +++ b/packages/x-charts/src/hooks/useSvgRef.test.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { expect } from 'chai'; -import { ErrorBoundary, createRenderer, screen } from '@mui/internal-test-utils'; +import { ErrorBoundary, createRenderer, reactMajor, screen } from '@mui/internal-test-utils'; import { useSvgRef } from './useSvgRef'; import { SvgRefProvider, useSurfaceRef } from '../context/SvgRefProvider'; @@ -30,17 +30,22 @@ describe('useSvgRef', () => { const errorRef = React.createRef(); + const errorMessage1 = 'MUI X: Could not find the svg ref context.'; + const errorMessage2 = + 'It looks like you rendered your component outside of a ChartsContainer parent component.'; + const errorMessage3 = 'The above error occurred in the component:'; + const expextedError = + reactMajor < 19 + ? [errorMessage1, errorMessage2, errorMessage3] + : `${errorMessage1}\n${errorMessage2}`; + expect(() => render( , ), - ).toErrorDev([ - 'MUI X: Could not find the svg ref context.', - 'It looks like you rendered your component outside of a ChartsContainer parent component.', - 'The above error occurred in the component:', - ]); + ).toErrorDev(expextedError); expect((errorRef.current as any).errors).to.have.length(1); expect((errorRef.current as any).errors[0].toString()).to.include( diff --git a/packages/x-data-grid-generator/package.json b/packages/x-data-grid-generator/package.json index 54385f60af06..99216250cf76 100644 --- a/packages/x-data-grid-generator/package.json +++ b/packages/x-data-grid-generator/package.json @@ -50,7 +50,7 @@ "@emotion/styled": "^11.8.1", "@mui/icons-material": "^5.4.1 || ^6.0.0", "@mui/material": "^5.15.14 || ^6.0.0", - "react": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/react": { diff --git a/packages/x-data-grid-generator/src/renderer/renderEditRating.tsx b/packages/x-data-grid-generator/src/renderer/renderEditRating.tsx index 4caf314b9a85..fcd8aa855c50 100644 --- a/packages/x-data-grid-generator/src/renderer/renderEditRating.tsx +++ b/packages/x-data-grid-generator/src/renderer/renderEditRating.tsx @@ -18,7 +18,7 @@ function EditRating(props: GridRenderEditCellParams) { changedThroughKeyboard.current = false; }; - const handleRef = (element: HTMLElement | undefined) => { + const handleRef = (element: HTMLElement | null) => { if (element) { if (value !== 0) { element.querySelector(`input[value="${value}"]`)!.focus(); diff --git a/packages/x-data-grid-premium/README.md b/packages/x-data-grid-premium/README.md index cdb502174687..21ba5482e3c4 100644 --- a/packages/x-data-grid-premium/README.md +++ b/packages/x-data-grid-premium/README.md @@ -16,8 +16,8 @@ This component has the following peer dependencies that you need to install as w ```json "peerDependencies": { "@mui/material": "^5.15.14 || ^6.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, ``` diff --git a/packages/x-data-grid-premium/package.json b/packages/x-data-grid-premium/package.json index 2c0c7c86f30a..3c3a8d362010 100644 --- a/packages/x-data-grid-premium/package.json +++ b/packages/x-data-grid-premium/package.json @@ -60,8 +60,8 @@ "@emotion/styled": "^11.8.1", "@mui/material": "^5.15.14 || ^6.0.0", "@mui/system": "^5.15.14 || ^6.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/react": { diff --git a/packages/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx b/packages/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx index 9f60eb3694cf..f956007843f7 100644 --- a/packages/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx +++ b/packages/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx @@ -1,5 +1,12 @@ import * as React from 'react'; -import { createRenderer, fireEvent, screen, act, waitFor } from '@mui/internal-test-utils'; +import { + createRenderer, + fireEvent, + screen, + act, + waitFor, + reactMajor, +} from '@mui/internal-test-utils'; import { microtasks, getColumnHeaderCell, @@ -666,7 +673,7 @@ describe(' - Row grouping', () => { isGroupExpandedByDefault={isGroupExpandedByDefault} />, ); - expect(isGroupExpandedByDefault.callCount).to.equal(12); // Should not be called on leaves + expect(isGroupExpandedByDefault.callCount).to.equal(reactMajor >= 19 ? 6 : 12); // Should not be called on leaves const { childrenExpanded, ...node } = apiRef.current.state.rows.tree.A as GridGroupNode; const callForNodeA = isGroupExpandedByDefault .getCalls() diff --git a/packages/x-data-grid-pro/README.md b/packages/x-data-grid-pro/README.md index 8f230f5d7d25..4a63af61c3ae 100644 --- a/packages/x-data-grid-pro/README.md +++ b/packages/x-data-grid-pro/README.md @@ -16,8 +16,8 @@ This component has the following peer dependencies that you need to install as w ```json "peerDependencies": { "@mui/material": "^5.15.14 || ^6.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, ``` diff --git a/packages/x-data-grid-pro/package.json b/packages/x-data-grid-pro/package.json index eeb1118bb410..c4a94b2d5c3d 100644 --- a/packages/x-data-grid-pro/package.json +++ b/packages/x-data-grid-pro/package.json @@ -58,8 +58,8 @@ "@emotion/styled": "^11.8.1", "@mui/material": "^5.15.14 || ^6.0.0", "@mui/system": "^5.15.14 || ^6.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/react": { diff --git a/packages/x-data-grid-pro/src/tests/detailPanel.DataGridPro.test.tsx b/packages/x-data-grid-pro/src/tests/detailPanel.DataGridPro.test.tsx index b8e38ff9df4a..115a7b69feeb 100644 --- a/packages/x-data-grid-pro/src/tests/detailPanel.DataGridPro.test.tsx +++ b/packages/x-data-grid-pro/src/tests/detailPanel.DataGridPro.test.tsx @@ -11,7 +11,14 @@ import { GRID_DETAIL_PANEL_TOGGLE_FIELD, } from '@mui/x-data-grid-pro'; import { useBasicDemoData } from '@mui/x-data-grid-generator'; -import { createRenderer, fireEvent, screen, waitFor, act } from '@mui/internal-test-utils'; +import { + createRenderer, + fireEvent, + screen, + waitFor, + act, + reactMajor, +} from '@mui/internal-test-utils'; import { $, $$, grid, getRow, getCell, getColumnValues, microtasks } from 'test/utils/helperFn'; import { fireUserEvent } from 'test/utils/fireUserEvent'; @@ -311,12 +318,18 @@ describe(' - Detail panel', () => { // + 2x during state initialization (StrictMode) // + 2x when sortedRowsSet is fired // + 2x when sortedRowsSet is fired (StrictMode) = 8x - expect(getDetailPanelContent.callCount).to.equal(8); + // Because of https://react.dev/blog/2024/04/25/react-19-upgrade-guide#strict-mode-improvements + // from React 19 it is: + // 2x during state initialization + // + 2x when sortedRowsSet is fired + const expectedCallCount = reactMajor >= 19 ? 4 : 8; + + expect(getDetailPanelContent.callCount).to.equal(expectedCallCount); fireEvent.click(screen.getByRole('button', { name: 'Expand' })); - expect(getDetailPanelContent.callCount).to.equal(8); + expect(getDetailPanelContent.callCount).to.equal(expectedCallCount); fireEvent.click(screen.getByRole('button', { name: /next page/i })); - expect(getDetailPanelContent.callCount).to.equal(8); + expect(getDetailPanelContent.callCount).to.equal(expectedCallCount); const getDetailPanelContent2 = spy(() =>
Detail
); setProps({ getDetailPanelContent: getDetailPanelContent2 }); @@ -342,16 +355,23 @@ describe(' - Detail panel', () => { initialState={{ pagination: { paginationModel: { pageSize: 1 } } }} />, ); + // 2x during state initialization // + 2x during state initialization (StrictMode) // + 2x when sortedRowsSet is fired // + 2x when sortedRowsSet is fired (StrictMode) = 8x - expect(getDetailPanelHeight.callCount).to.equal(8); + // Because of https://react.dev/blog/2024/04/25/react-19-upgrade-guide#strict-mode-improvements + // from React 19 it is: + // 2x during state initialization + // + 2x when sortedRowsSet is fired + const expectedCallCount = reactMajor >= 19 ? 4 : 8; + + expect(getDetailPanelHeight.callCount).to.equal(expectedCallCount); fireEvent.click(screen.getByRole('button', { name: 'Expand' })); - expect(getDetailPanelHeight.callCount).to.equal(8); + expect(getDetailPanelHeight.callCount).to.equal(expectedCallCount); fireEvent.click(screen.getByRole('button', { name: /next page/i })); - expect(getDetailPanelHeight.callCount).to.equal(8); + expect(getDetailPanelHeight.callCount).to.equal(expectedCallCount); const getDetailPanelHeight2 = spy(() => 200); setProps({ getDetailPanelHeight: getDetailPanelHeight2 }); @@ -413,7 +433,13 @@ describe(' - Detail panel', () => { // + 1x during state initialization (StrictMode) // + 1x when sortedRowsSet is fired // + 1x when sortedRowsSet is fired (StrictMode) = 4x - expect(getDetailPanelHeight.callCount).to.equal(4); + // Because of https://react.dev/blog/2024/04/25/react-19-upgrade-guide#strict-mode-improvements + // from React 19 it is: + // 1x during state initialization + // + 1x when sortedRowsSet is fired + const expectedCallCount = reactMajor >= 19 ? 2 : 4; + + expect(getDetailPanelHeight.callCount).to.equal(expectedCallCount); expect(getDetailPanelHeight.lastCall.args[0].id).to.equal(0); }); @@ -478,8 +504,8 @@ describe(' - Detail panel', () => { it('should not reuse detail panel components', () => { let counter = 0; function DetailPanel() { - const [number] = React.useState((counter += 1)); - return
{number}
; + counter += 1; + return
{counter}
; } const { setProps } = render( } detailPanelExpandedRowIds={[0]} />, diff --git a/packages/x-data-grid-pro/src/tests/infiniteLoader.DataGridPro.test.tsx b/packages/x-data-grid-pro/src/tests/infiniteLoader.DataGridPro.test.tsx index 350aa89568c4..5f8b9422490b 100644 --- a/packages/x-data-grid-pro/src/tests/infiniteLoader.DataGridPro.test.tsx +++ b/packages/x-data-grid-pro/src/tests/infiniteLoader.DataGridPro.test.tsx @@ -108,7 +108,6 @@ describe(' - Infnite loader', () => { const [loading, setLoading] = React.useState(false); const handleRowsScrollEnd = React.useCallback(async () => { setLoading(true); - await sleep(50); setRows((prevRows) => { const lastRowId = prevRows[prevRows.length - 1].id; const nextRow = getRow(lastRowId + 1); @@ -137,45 +136,20 @@ describe(' - Infnite loader', () => { // 1 initial row // 5 rows loaded one by one through `onRowsScrollEnd` callback - expect(getColumnValues(0)).to.deep.equal(['0']); + const multiplier = 2; // `setRows` is called twice for each `handleRowsScrollEnd` call await waitFor(() => { - expect(getRow.callCount).to.equal(1); - }); - await waitFor(() => { - expect(getColumnValues(0)).to.deep.equal(['0', '1']); - }); - - await waitFor(() => { - expect(getRow.callCount).to.equal(2); - }); - await waitFor(() => { - expect(getColumnValues(0)).to.deep.equal(['0', '1', '2']); - }); - - await waitFor(() => { - expect(getRow.callCount).to.equal(3); - }); - await waitFor(() => { - expect(getColumnValues(0)).to.deep.equal(['0', '1', '2', '3']); + expect(getRow.callCount).to.equal(5 * multiplier); }); - await waitFor(() => { - expect(getRow.callCount).to.equal(4); - }); - await waitFor(() => { - expect(getColumnValues(0)).to.deep.equal(['0', '1', '2', '3', '4']); - }); + const getRowCalls = getRow.getCalls(); + for (let callIndex = 0; callIndex < getRowCalls.length; callIndex += multiplier) { + const call = getRowCalls[callIndex]; + expect(call.returnValue?.id).to.equal(callIndex / multiplier + 1); + } - await waitFor(() => { - expect(getRow.callCount).to.equal(5); - }); await waitFor(() => { expect(getColumnValues(0)).to.deep.equal(['0', '1', '2', '3', '4', '5']); }); - - await sleep(200); - // should not load more rows because the threshold is not reached - expect(getRow.callCount).to.equal(5); }); it('should not observe intersections with the rows pinned to the bottom', async function test() { diff --git a/packages/x-data-grid-pro/src/tests/treeData.DataGridPro.test.tsx b/packages/x-data-grid-pro/src/tests/treeData.DataGridPro.test.tsx index 61a722c634f8..8ba96004f225 100644 --- a/packages/x-data-grid-pro/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/x-data-grid-pro/src/tests/treeData.DataGridPro.test.tsx @@ -1,4 +1,4 @@ -import { createRenderer, fireEvent, screen, act } from '@mui/internal-test-utils'; +import { createRenderer, fireEvent, screen, act, reactMajor } from '@mui/internal-test-utils'; import { getCell, getColumnHeaderCell, @@ -286,7 +286,7 @@ describe(' - Tree data', () => { const isGroupExpandedByDefault = spy((node: GridGroupNode) => node.id === 'A'); render(); - expect(isGroupExpandedByDefault.callCount).to.equal(8); // Should not be called on leaves + expect(isGroupExpandedByDefault.callCount).to.equal(reactMajor >= 19 ? 4 : 8); // Should not be called on leaves const { childrenExpanded, children, childrenFromPath, ...node } = apiRef.current.state.rows .tree.A as GridGroupNode; const callForNodeA = isGroupExpandedByDefault diff --git a/packages/x-data-grid/README.md b/packages/x-data-grid/README.md index e8af2fbf99ab..fab185d4804f 100644 --- a/packages/x-data-grid/README.md +++ b/packages/x-data-grid/README.md @@ -16,8 +16,8 @@ This component has the following peer dependencies that you need to install as w ```json "peerDependencies": { "@mui/material": "^5.15.14 || ^6.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, ``` diff --git a/packages/x-data-grid/package.json b/packages/x-data-grid/package.json index a2bceebaa950..df8b3246b917 100644 --- a/packages/x-data-grid/package.json +++ b/packages/x-data-grid/package.json @@ -59,8 +59,8 @@ "@emotion/styled": "^11.8.1", "@mui/material": "^5.15.14 || ^6.0.0", "@mui/system": "^5.15.14 || ^6.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/react": { diff --git a/packages/x-data-grid/src/hooks/utils/useGridApiEventHandler.test.tsx b/packages/x-data-grid/src/hooks/utils/useGridApiEventHandler.test.tsx index 1ee0bd2c105f..35df8e23d954 100644 --- a/packages/x-data-grid/src/hooks/utils/useGridApiEventHandler.test.tsx +++ b/packages/x-data-grid/src/hooks/utils/useGridApiEventHandler.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { spy } from 'sinon'; import { expect } from 'chai'; -import { createRenderer } from '@mui/internal-test-utils'; +import { createRenderer, reactMajor } from '@mui/internal-test-utils'; import { sleep } from 'test/utils/helperFn'; import { createUseGridApiEventHandler } from './useGridApiEventHandler'; import { FinalizationRegistryBasedCleanupTracking } from '../../utils/cleanupTracking/FinalizationRegistryBasedCleanupTracking'; @@ -42,14 +42,17 @@ describe('useGridApiEventHandler', () => { // which makes 2 event listeners to be registered. Since the second render is never // committed (to simulate a trashed render in React 18), the effects also don't run, so we're // unable to unsubscribe the last listener using the cleanup function. - expect(apiRef.current.subscribeEvent.callCount).to.equal(3); + // Since React 19, StrictMode works differently + // https://react.dev/blog/2024/04/25/react-19-upgrade-guide#strict-mode-improvements + const expectedCallCount = reactMajor >= 19 ? 1 : 3; + expect(apiRef.current.subscribeEvent.callCount).to.equal(expectedCallCount); unmount(); global.gc(); // Triggers garbage collector await sleep(50); // Ensure that both event listeners were unsubscribed - expect(unsubscribe.callCount).to.equal(3); + expect(unsubscribe.callCount).to.equal(expectedCallCount); }); }); @@ -74,13 +77,16 @@ describe('useGridApiEventHandler', () => { // which makes 2 event listeners to be registered. Since the second render is never // committed (to simulate a trashed render in React 18), the effects also don't run, so we're // unable to unsubscribe the last listener using the cleanup function. - expect(apiRef.current.subscribeEvent.callCount).to.equal(3); + // Since React 19, StrictMode works differently + // https://react.dev/blog/2024/04/25/react-19-upgrade-guide#strict-mode-improvements + const expectedCallCount = reactMajor >= 19 ? 1 : 3; + expect(apiRef.current.subscribeEvent.callCount).to.equal(expectedCallCount); unmount(); await sleep(60); - // Ensure that both event listeners were unsubscribed - expect(unsubscribe.callCount).to.equal(3); + // Ensure that all event listeners were unsubscribed + expect(unsubscribe.callCount).to.equal(expectedCallCount); }); }); }); diff --git a/packages/x-data-grid/src/joy/joySlots.tsx b/packages/x-data-grid/src/joy/joySlots.tsx index a5645d892066..a4baffb0dabf 100644 --- a/packages/x-data-grid/src/joy/joySlots.tsx +++ b/packages/x-data-grid/src/joy/joySlots.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { SxProps } from '@mui/system'; import { ColorPaletteProp, Theme, VariantProp } from '@mui/joy/styles'; -import JoyBadge from '@mui/joy/Badge'; +import JoyBadge, { BadgeOrigin } from '@mui/joy/Badge'; import JoyCheckbox from '@mui/joy/Checkbox'; import JoyDivider from '@mui/joy/Divider'; import JoyInput from '@mui/joy/Input'; @@ -72,10 +72,15 @@ function convertVariant>( - ({ slotProps, variant, color, sx, ...props }, ref) => { + ({ slotProps, variant, color, sx, anchorOrigin, ...props }, ref) => { return ( } diff --git a/packages/x-data-grid/src/tests/layout.DataGrid.test.tsx b/packages/x-data-grid/src/tests/layout.DataGrid.test.tsx index f92b82e0dc2f..cd4d6d324bb5 100644 --- a/packages/x-data-grid/src/tests/layout.DataGrid.test.tsx +++ b/packages/x-data-grid/src/tests/layout.DataGrid.test.tsx @@ -1,5 +1,11 @@ import * as React from 'react'; -import { createRenderer, screen, ErrorBoundary, waitFor } from '@mui/internal-test-utils'; +import { + createRenderer, + screen, + ErrorBoundary, + waitFor, + reactMajor, +} from '@mui/internal-test-utils'; import { stub, spy } from 'sinon'; import { expect } from 'chai'; import { @@ -960,8 +966,9 @@ describe(' - Layout & warnings', () => { ); }).toErrorDev([ 'The Data Grid component requires all rows to have a unique `id` property', - 'The Data Grid component requires all rows to have a unique `id` property', - 'The above error occurred in the component', + reactMajor < 19 && + 'The Data Grid component requires all rows to have a unique `id` property', + reactMajor < 19 && 'The above error occurred in the component', ]); expect((errorRef.current as any).errors).to.have.length(1); expect((errorRef.current as any).errors[0].toString()).to.include( @@ -1283,8 +1290,8 @@ describe(' - Layout & warnings', () => { , ); }).toErrorDev([ - 'Warning: Encountered two children with the same key, `id`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.', - 'Warning: Encountered two children with the same key, `id`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.', + 'Encountered two children with the same key, `id`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.', + 'Encountered two children with the same key, `id`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.', ]); }); diff --git a/packages/x-data-grid/src/tests/pagination.DataGrid.test.tsx b/packages/x-data-grid/src/tests/pagination.DataGrid.test.tsx index 94421d70f768..002a0d18ccad 100644 --- a/packages/x-data-grid/src/tests/pagination.DataGrid.test.tsx +++ b/packages/x-data-grid/src/tests/pagination.DataGrid.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { spy, stub, SinonStub, SinonSpy } from 'sinon'; import { expect } from 'chai'; -import { createRenderer, fireEvent, screen, waitFor } from '@mui/internal-test-utils'; +import { createRenderer, fireEvent, reactMajor, screen, waitFor } from '@mui/internal-test-utils'; import { DataGrid, DataGridProps, @@ -324,7 +324,8 @@ describe(' - Pagination', () => { ); }).toWarnDev([ `MUI X: The page size \`${pageSize}\` is not present in the \`pageSizeOptions\``, - `MUI X: The page size \`${pageSize}\` is not present in the \`pageSizeOptions\``, + reactMajor < 19 && + `MUI X: The page size \`${pageSize}\` is not present in the \`pageSizeOptions\``, ]); }); @@ -352,7 +353,8 @@ describe(' - Pagination', () => { render(); }).toWarnDev([ `MUI X: The page size \`${pageSize}\` is not present in the \`pageSizeOptions\``, - `MUI X: The page size \`${pageSize}\` is not present in the \`pageSizeOptions\``, + reactMajor < 19 && + `MUI X: The page size \`${pageSize}\` is not present in the \`pageSizeOptions\``, ]); }); @@ -361,7 +363,7 @@ describe(' - Pagination', () => { render(); }).toWarnDev([ `MUI X: The page size \`100\` is not present in the \`pageSizeOptions\``, - `MUI X: The page size \`100\` is not present in the \`pageSizeOptions\``, + reactMajor < 19 && `MUI X: The page size \`100\` is not present in the \`pageSizeOptions\``, ]); }); diff --git a/packages/x-data-grid/src/tests/quickFiltering.DataGrid.test.tsx b/packages/x-data-grid/src/tests/quickFiltering.DataGrid.test.tsx index 44d608d5602f..8f1153174c38 100644 --- a/packages/x-data-grid/src/tests/quickFiltering.DataGrid.test.tsx +++ b/packages/x-data-grid/src/tests/quickFiltering.DataGrid.test.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { createRenderer, screen, fireEvent } from '@mui/internal-test-utils'; +import { createRenderer, screen, fireEvent, reactMajor } from '@mui/internal-test-utils'; import { expect } from 'chai'; import { spy } from 'sinon'; import { @@ -313,18 +313,21 @@ describe(' - Quick filter', () => { />, ); + // Because of https://react.dev/blog/2024/04/25/react-19-upgrade-guide#strict-mode-improvements + const initialCallCount = reactMajor >= 19 ? 1 : 2; + expect(getColumnValues(0)).to.deep.equal(['1']); - expect(getApplyQuickFilterFnSpy.callCount).to.equal(2); + expect(getApplyQuickFilterFnSpy.callCount).to.equal(initialCallCount); setProps({ columnVisibilityModel: { brand: false } }); clock.runToLast(); expect(getColumnValues(0)).to.deep.equal([]); - expect(getApplyQuickFilterFnSpy.callCount).to.equal(3); + expect(getApplyQuickFilterFnSpy.callCount).to.equal(initialCallCount + 1); setProps({ columnVisibilityModel: { brand: true } }); clock.runToLast(); expect(getColumnValues(0)).to.deep.equal(['1']); - expect(getApplyQuickFilterFnSpy.callCount).to.equal(4); + expect(getApplyQuickFilterFnSpy.callCount).to.equal(initialCallCount + 2); }); it('should not apply filters on column visibility change when quickFilterExcludeHiddenColumns=true but no quick filter values', () => { @@ -380,18 +383,21 @@ describe(' - Quick filter', () => { />, ); + // Because of https://react.dev/blog/2024/04/25/react-19-upgrade-guide#strict-mode-improvements + const initialCallCount = reactMajor >= 19 ? 1 : 2; + expect(getColumnValues(0)).to.deep.equal(['1']); - expect(getApplyQuickFilterFnSpy.callCount).to.equal(2); + expect(getApplyQuickFilterFnSpy.callCount).to.equal(initialCallCount); setProps({ columnVisibilityModel: { brand: false } }); clock.runToLast(); expect(getColumnValues(0)).to.deep.equal(['1']); - expect(getApplyQuickFilterFnSpy.callCount).to.equal(2); + expect(getApplyQuickFilterFnSpy.callCount).to.equal(initialCallCount); setProps({ columnVisibilityModel: { brand: true } }); clock.runToLast(); expect(getColumnValues(0)).to.deep.equal(['1']); - expect(getApplyQuickFilterFnSpy.callCount).to.equal(2); + expect(getApplyQuickFilterFnSpy.callCount).to.equal(initialCallCount); }); }); diff --git a/packages/x-data-grid/src/tests/rows.DataGrid.test.tsx b/packages/x-data-grid/src/tests/rows.DataGrid.test.tsx index 4d222603334b..771ee43a173d 100644 --- a/packages/x-data-grid/src/tests/rows.DataGrid.test.tsx +++ b/packages/x-data-grid/src/tests/rows.DataGrid.test.tsx @@ -6,6 +6,7 @@ import { act, ErrorBoundary, waitFor, + reactMajor, } from '@mui/internal-test-utils'; import clsx from 'clsx'; import { expect } from 'chai'; @@ -254,8 +255,8 @@ describe(' - Rows', () => { ); }).toErrorDev([ 'MUI X: Missing the `getActions` property in the `GridColDef`.', - 'MUI X: Missing the `getActions` property in the `GridColDef`.', - 'The above error occurred in the component', + reactMajor < 19 && 'MUI X: Missing the `getActions` property in the `GridColDef`.', + reactMajor < 19 && 'The above error occurred in the component', ]); }); diff --git a/packages/x-data-grid/src/tests/slots.DataGrid.test.tsx b/packages/x-data-grid/src/tests/slots.DataGrid.test.tsx index 0aa832db0d5f..055f24b47059 100644 --- a/packages/x-data-grid/src/tests/slots.DataGrid.test.tsx +++ b/packages/x-data-grid/src/tests/slots.DataGrid.test.tsx @@ -1,5 +1,11 @@ import * as React from 'react'; -import { createRenderer, ErrorBoundary, fireEvent, screen } from '@mui/internal-test-utils'; +import { + createRenderer, + ErrorBoundary, + fireEvent, + reactMajor, + screen, +} from '@mui/internal-test-utils'; import { expect } from 'chai'; import { spy } from 'sinon'; import { DataGrid, DataGridProps, GridOverlay } from '@mui/x-data-grid'; @@ -169,8 +175,9 @@ describe(' - Slots', () => { ); }).toErrorDev([ 'MUI X: useGridRootProps should only be used inside the DataGrid, DataGridPro or DataGridPremium component.', - 'MUI X: useGridRootProps should only be used inside the DataGrid, DataGridPro or DataGridPremium component.', - 'The above error occurred in the component', + reactMajor < 19 && + 'MUI X: useGridRootProps should only be used inside the DataGrid, DataGridPro or DataGridPremium component.', + reactMajor < 19 && 'The above error occurred in the component', ]); }); diff --git a/packages/x-date-pickers-pro/README.md b/packages/x-date-pickers-pro/README.md index 5e6e0bd885c1..7aa6eabaaba4 100644 --- a/packages/x-date-pickers-pro/README.md +++ b/packages/x-date-pickers-pro/README.md @@ -35,8 +35,8 @@ This component has the following peer dependencies that you need to install as w ```json "peerDependencies": { "@mui/material": "^5.15.14 || ^6.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, ``` diff --git a/packages/x-date-pickers-pro/package.json b/packages/x-date-pickers-pro/package.json index 61be276118ab..10257d3d2e7e 100644 --- a/packages/x-date-pickers-pro/package.json +++ b/packages/x-date-pickers-pro/package.json @@ -63,8 +63,8 @@ "moment": "^2.29.4", "moment-hijri": "^2.1.2 || ^3.0.0", "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/react": { diff --git a/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/describes.MobileDateRangePicker.test.tsx b/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/describes.MobileDateRangePicker.test.tsx index 981bd8819f28..3d5b9c2013bc 100644 --- a/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/describes.MobileDateRangePicker.test.tsx +++ b/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/describes.MobileDateRangePicker.test.tsx @@ -87,9 +87,9 @@ describe(' - Describes', () => { } fireEvent.click( - screen.getAllByRole('gridcell', { + screen.getByRole('gridcell', { name: adapterToUse.getDate(newValue[setEndDate ? 1 : 0]).toString(), - })[0], + }), ); // Close the picker diff --git a/packages/x-date-pickers-pro/src/internals/hooks/useEnrichedRangePickerFieldProps.ts b/packages/x-date-pickers-pro/src/internals/hooks/useEnrichedRangePickerFieldProps.ts index 311e018ce3c0..17ffbc770c21 100644 --- a/packages/x-date-pickers-pro/src/internals/hooks/useEnrichedRangePickerFieldProps.ts +++ b/packages/x-date-pickers-pro/src/internals/hooks/useEnrichedRangePickerFieldProps.ts @@ -173,7 +173,7 @@ const useMultiInputFieldSlotProps = < const previousRangePosition = React.useRef(rangePosition); React.useEffect(() => { - if (!open) { + if (!open || variant === 'mobile') { return; } @@ -191,7 +191,7 @@ const useMultiInputFieldSlotProps = < previousRangePosition.current === rangePosition ? currentView : 0, ); previousRangePosition.current = rangePosition; - }, [rangePosition, open, currentView, startFieldRef, endFieldRef]); + }, [rangePosition, open, currentView, startFieldRef, endFieldRef, variant]); const openRangeStartSelection: React.UIEventHandler = (event) => { event.stopPropagation(); @@ -345,7 +345,7 @@ const useSingleInputFieldSlotProps = < const handleFieldRef = useForkRef(fieldProps.unstableFieldRef, startFieldRef, endFieldRef); React.useEffect(() => { - if (!open || !startFieldRef.current) { + if (!open || !startFieldRef.current || variant === 'mobile') { return; } @@ -362,7 +362,7 @@ const useSingleInputFieldSlotProps = < : sections.lastIndexOf(currentView); startFieldRef.current?.focusField(newSelectedSection); } - }, [rangePosition, open, currentView, startFieldRef]); + }, [rangePosition, open, currentView, startFieldRef, variant]); const updateRangePosition = () => { if (!startFieldRef.current?.isFieldFocused()) { diff --git a/packages/x-date-pickers/README.md b/packages/x-date-pickers/README.md index b66cfe572b1a..c070227afa4a 100644 --- a/packages/x-date-pickers/README.md +++ b/packages/x-date-pickers/README.md @@ -35,8 +35,8 @@ This component has the following peer dependencies that you need to install as w ```json "peerDependencies": { "@mui/material": "^5.15.14 || ^6.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, ``` diff --git a/packages/x-date-pickers/package.json b/packages/x-date-pickers/package.json index 7fc7cbf92d8e..d01e895e3278 100644 --- a/packages/x-date-pickers/package.json +++ b/packages/x-date-pickers/package.json @@ -65,8 +65,8 @@ "moment": "^2.29.4", "moment-hijri": "^2.1.2 || ^3.0.0", "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/react": { diff --git a/packages/x-internals/package.json b/packages/x-internals/package.json index da0d9307e5ee..ee4e8dd06c23 100644 --- a/packages/x-internals/package.json +++ b/packages/x-internals/package.json @@ -45,7 +45,7 @@ "@mui/utils": "^5.16.6 || ^6.0.0" }, "peerDependencies": { - "react": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "devDependencies": { "@mui/internal-test-utils": "^1.0.21", diff --git a/packages/x-license/package.json b/packages/x-license/package.json index 8e7c67cef294..4a19c4a41f0b 100644 --- a/packages/x-license/package.json +++ b/packages/x-license/package.json @@ -38,7 +38,7 @@ "@mui/utils": "^5.16.6 || ^6.0.0" }, "peerDependencies": { - "react": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "devDependencies": { "@mui/internal-test-utils": "^1.0.21", diff --git a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx index 7605292c6acd..1cfd9ab49dd6 100644 --- a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx +++ b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { expect } from 'chai'; -import { createRenderer, screen } from '@mui/internal-test-utils'; +import { createRenderer, ErrorBoundary, reactMajor, screen } from '@mui/internal-test-utils'; import { useLicenseVerifier, LicenseInfo, @@ -94,19 +94,20 @@ describe('useLicenseVerifier', function test() { }); LicenseInfo.setLicenseKey(expiredLicenseKey); - let actualErrorMsg; + const errorRef = React.createRef(); + expect(() => { - try { - render(); - } catch (error: any) { - actualErrorMsg = error.message; - } + render( + + + , + ); }).to.toErrorDev([ 'MUI X: Expired license key', - 'MUI X: Expired license key', - 'The above error occurred in the component', + reactMajor < 19 && 'MUI X: Expired license key', + reactMajor < 19 && 'The above error occurred in the component', ]); - expect(actualErrorMsg).to.match(/MUI X: Expired license key/); + expect((errorRef.current as any).errors[0].toString()).to.match(/MUI X: Expired license key/); }); it('should throw if the license is not covering charts and tree-view', () => { diff --git a/packages/x-tree-view-pro/README.md b/packages/x-tree-view-pro/README.md index b9177bb79f5f..3a7069bf7545 100644 --- a/packages/x-tree-view-pro/README.md +++ b/packages/x-tree-view-pro/README.md @@ -16,8 +16,8 @@ This component has the following peer dependencies that you need to install as w ```json "peerDependencies": { "@mui/material": "^5.15.14 || ^6.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, ``` diff --git a/packages/x-tree-view-pro/package.json b/packages/x-tree-view-pro/package.json index f02df842b6f4..e17ed745a622 100644 --- a/packages/x-tree-view-pro/package.json +++ b/packages/x-tree-view-pro/package.json @@ -60,8 +60,8 @@ "@emotion/styled": "^11.8.1", "@mui/material": "^5.15.14 || ^6.0.0", "@mui/system": "^5.15.14 || ^6.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/react": { diff --git a/packages/x-tree-view/README.md b/packages/x-tree-view/README.md index 6b16d4b1273f..54023a0abf6e 100644 --- a/packages/x-tree-view/README.md +++ b/packages/x-tree-view/README.md @@ -16,8 +16,8 @@ This component has the following peer dependencies that you need to install as w ```json "peerDependencies": { "@mui/material": "^5.15.14 || ^6.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, ``` diff --git a/packages/x-tree-view/package.json b/packages/x-tree-view/package.json index 251a35830562..d4a1eb6c5de5 100644 --- a/packages/x-tree-view/package.json +++ b/packages/x-tree-view/package.json @@ -58,8 +58,8 @@ "@emotion/styled": "^11.8.1", "@mui/material": "^5.15.14 || ^6.0.0", "@mui/system": "^5.15.14 || ^6.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/react": { diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.ts index 7cbc8d042cd7..fc2c5a40eebf 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.ts @@ -1,6 +1,5 @@ import * as React from 'react'; import useEventCallback from '@mui/utils/useEventCallback'; -import useEnhancedEffect from '@mui/utils/useEnhancedEffect'; import { EventHandlers } from '@mui/utils'; import { TreeViewPlugin } from '../../models'; import { UseTreeViewFocusSignature } from './useTreeViewFocus.types'; @@ -24,7 +23,7 @@ export const useTreeViewFocus: TreeViewPlugin = ({ store, models, }) => { - useEnhancedEffect(() => { + React.useEffect(() => { let defaultFocusableItemId = convertSelectedItemsToArray(models.selectedItems.value).find( (itemId) => { if (!selectorCanItemBeFocused(store.value, itemId)) { diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewItems/useTreeViewItems.test.tsx b/packages/x-tree-view/src/internals/plugins/useTreeViewItems/useTreeViewItems.test.tsx index 1256f01c37e0..625a54a3a8c1 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewItems/useTreeViewItems.test.tsx +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewItems/useTreeViewItems.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; -import { fireEvent } from '@mui/internal-test-utils'; +import { fireEvent, reactMajor } from '@mui/internal-test-utils'; import { describeTreeView } from 'test/utils/tree-view/describeTreeView'; import { UseTreeViewExpansionSignature, @@ -29,17 +29,20 @@ describeTreeView< ).toErrorDev([ 'Encountered two children with the same key, `1`', 'MUI X: The Tree View component requires all items to have a unique `id` property.', - 'MUI X: The Tree View component requires all items to have a unique `id` property.', - `The above error occurred in the component`, - `The above error occurred in the component`, + reactMajor < 19 && + 'MUI X: The Tree View component requires all items to have a unique `id` property.', + reactMajor < 19 && `The above error occurred in the component`, + reactMajor < 19 && `The above error occurred in the component`, ]); } else { expect(() => render({ items: [{ id: '1' }, { id: '1' }], withErrorBoundary: true }), ).toErrorDev([ 'MUI X: The Tree View component requires all items to have a unique `id` property.', - 'MUI X: The Tree View component requires all items to have a unique `id` property.', - `The above error occurred in the component`, + reactMajor < 19 && + 'MUI X: The Tree View component requires all items to have a unique `id` property.', + reactMajor < 19 && + `The above error occurred in the component`, ]); } }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a4ef6cec0d1a..1f99814099c9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -764,10 +764,10 @@ importers: specifier: ^15.8.1 version: 15.8.1 react: - specifier: ^17.0.0 || ^18.0.0 + specifier: ^17.0.0 || ^18.0.0 || ^19.0.0 version: 18.3.1 react-dom: - specifier: ^17.0.0 || ^18.0.0 + specifier: ^17.0.0 || ^18.0.0 || ^19.0.0 version: 18.3.1(react@18.3.1) reselect: specifier: ^5.1.1 @@ -844,10 +844,10 @@ importers: specifier: ^15.8.1 version: 15.8.1 react: - specifier: ^17.0.0 || ^18.0.0 + specifier: ^17.0.0 || ^18.0.0 || ^19.0.0 version: 18.3.1 react-dom: - specifier: ^17.0.0 || ^18.0.0 + specifier: ^17.0.0 || ^18.0.0 || ^19.0.0 version: 18.3.1(react@18.3.1) devDependencies: '@mui/material': @@ -1017,10 +1017,10 @@ importers: specifier: ^15.8.1 version: 15.8.1 react: - specifier: ^17.0.0 || ^18.0.0 + specifier: ^17.0.0 || ^18.0.0 || ^19.0.0 version: 18.3.1 react-dom: - specifier: ^17.0.0 || ^18.0.0 + specifier: ^17.0.0 || ^18.0.0 || ^19.0.0 version: 18.3.1(react@18.3.1) reselect: specifier: ^5.1.1 @@ -1073,7 +1073,7 @@ importers: specifier: ^11.0.2 version: 11.0.2 react: - specifier: ^17.0.0 || ^18.0.0 + specifier: ^17.0.0 || ^18.0.0 || ^19.0.0 version: 18.3.1 devDependencies: '@mui/icons-material': @@ -1129,10 +1129,10 @@ importers: specifier: ^15.8.1 version: 15.8.1 react: - specifier: ^17.0.0 || ^18.0.0 + specifier: ^17.0.0 || ^18.0.0 || ^19.0.0 version: 18.3.1 react-dom: - specifier: ^17.0.0 || ^18.0.0 + specifier: ^17.0.0 || ^18.0.0 || ^19.0.0 version: 18.3.1(react@18.3.1) reselect: specifier: ^5.1.1 @@ -1191,10 +1191,10 @@ importers: specifier: ^15.8.1 version: 15.8.1 react: - specifier: ^17.0.0 || ^18.0.0 + specifier: ^17.0.0 || ^18.0.0 || ^19.0.0 version: 18.3.1 react-dom: - specifier: ^17.0.0 || ^18.0.0 + specifier: ^17.0.0 || ^18.0.0 || ^19.0.0 version: 18.3.1(react@18.3.1) reselect: specifier: ^5.1.1 @@ -1244,10 +1244,10 @@ importers: specifier: ^15.8.1 version: 15.8.1 react: - specifier: ^17.0.0 || ^18.0.0 + specifier: ^17.0.0 || ^18.0.0 || ^19.0.0 version: 18.3.1 react-dom: - specifier: ^17.0.0 || ^18.0.0 + specifier: ^17.0.0 || ^18.0.0 || ^19.0.0 version: 18.3.1(react@18.3.1) react-transition-group: specifier: ^4.4.5 @@ -1339,10 +1339,10 @@ importers: specifier: ^15.8.1 version: 15.8.1 react: - specifier: ^17.0.0 || ^18.0.0 + specifier: ^17.0.0 || ^18.0.0 || ^19.0.0 version: 18.3.1 react-dom: - specifier: ^17.0.0 || ^18.0.0 + specifier: ^17.0.0 || ^18.0.0 || ^19.0.0 version: 18.3.1(react@18.3.1) react-transition-group: specifier: ^4.4.5 @@ -1392,7 +1392,7 @@ importers: specifier: ^5.16.6 || ^6.0.0 version: 5.16.6(@types/react@18.3.12)(react@18.3.1) react: - specifier: ^17.0.0 || ^18.0.0 + specifier: ^17.0.0 || ^18.0.0 || ^19.0.0 version: 18.3.1 devDependencies: '@mui/internal-test-utils': @@ -1412,7 +1412,7 @@ importers: specifier: ^5.16.6 || ^6.0.0 version: 5.16.6(@types/react@18.3.12)(react@18.3.1) react: - specifier: ^17.0.0 || ^18.0.0 + specifier: ^17.0.0 || ^18.0.0 || ^19.0.0 version: 18.3.1 devDependencies: '@mui/internal-test-utils': @@ -1450,10 +1450,10 @@ importers: specifier: ^15.8.1 version: 15.8.1 react: - specifier: ^17.0.0 || ^18.0.0 + specifier: ^17.0.0 || ^18.0.0 || ^19.0.0 version: 18.3.1 react-dom: - specifier: ^17.0.0 || ^18.0.0 + specifier: ^17.0.0 || ^18.0.0 || ^19.0.0 version: 18.3.1(react@18.3.1) react-transition-group: specifier: ^4.4.5 @@ -1518,10 +1518,10 @@ importers: specifier: ^15.8.1 version: 15.8.1 react: - specifier: ^17.0.0 || ^18.0.0 + specifier: ^17.0.0 || ^18.0.0 || ^19.0.0 version: 18.3.1 react-dom: - specifier: ^17.0.0 || ^18.0.0 + specifier: ^17.0.0 || ^18.0.0 || ^19.0.0 version: 18.3.1(react@18.3.1) react-transition-group: specifier: ^4.4.5 diff --git a/scripts/useMaterialUIv6.mjs b/scripts/useMaterialUIv6.mjs index 07f2aed4e3ab..abc2fe55845b 100644 --- a/scripts/useMaterialUIv6.mjs +++ b/scripts/useMaterialUIv6.mjs @@ -9,6 +9,9 @@ const pnpmUpdate = childProcess.spawnSync( '@mui/system@6.x', '@mui/icons-material@6.x', '@mui/utils@6.x', + '@mui/material-nextjs@6.x', + '@mui/styles@6.x', + '@mui/lab@latest', ], { shell: true, diff --git a/test/README.md b/test/README.md index 5bb982bb61ca..594badd07c28 100644 --- a/test/README.md +++ b/test/README.md @@ -12,57 +12,43 @@ Possible values for `version`: - a tag on npm, for example `next`, `experimental` or `latest` - an older version, for example `^17.0.0` -### CI +## Testing multiple versions of Material UI + +Currently, we use `@mui/material` v5 in the MUI X repo and all tests are run against it. +But MUI X packages are compatible with v5 and v6. +You can run the tests against `@mui/material` v6 by running the following command: -#### `next` version +`pnpm use-material-ui-v6` -For `react@next` specifically, there's a `react-next` workflow in our CircleCI pipeline that you can trigger in CircleCI on the PR you want to test: +## CI -1. Go to https://app.circleci.com/pipelines/github/mui/mui-x?branch=pull/PR_NUMBER and replace `PR_NUMBER` with the PR number you want to test. -2. Click `Trigger Pipeline` button. -3. Expand `Add parameters (optional)` and add the following parameter: +To execute additional jobs for custom versions of React/Material UI, you can use an `additional` workflow. In combination with `with-react-version` and/or `with-material-ui-6` parameters, it executes those jobs with dependency versions different from the main `pipeline` workflow. - | Parameter type | Name | Value | - | :------------- | :--------- | :----------- | - | `string` | `workflow` | `react-next` | +| Parameter type | Name | Value | +| :------------- | :------------------- | :------------------- | +| `string` | `workflow` | `additional` | +| `string` | `with-react-version` | `` | +| `boolean` | `with-material-ui-6` | `true` or `false` | +1. Go to https://app.circleci.com/pipelines/github/mui/mui-x?branch=pull/PR_NUMBER and replace `PR_NUMBER` with the PR number you want to test. +2. Click `Trigger Pipeline` button. +3. Go to the `Parameters` section and update the `workflow` parameter in combination with the version parameter(s). + You can leave the rest of the parameters with their default values. 4. Click `Trigger Pipeline` button. -#### Other versions +![CircleCI workflow](./circleci-workflow.png) + +### API -You can pass the same `version` to our CircleCI pipeline as well: +You can pass the same to our CircleCI pipeline through API as well: -With the following API request we're triggering a run of the default workflow in -PR #24289 for `react@next` +With the following API request we're triggering a run of the `additional` workflow in +PR #24289 for `react@rc` and `@mui/material-ui@6` ```bash curl --request POST \ --url https://circleci.com/api/v2/project/gh/mui/mui-x/pipeline \ --header 'content-type: application/json' \ --header 'Circle-Token: $CIRCLE_TOKEN' \ - --data-raw '{"branch":"pull/24289/head","parameters":{"react-version":"next"}}' + --data-raw '{"branch":"pull/24289/head","parameters":{"workflow":"additional","with-react-version":"rc","with-material-ui-6":true}}' ``` - -## Testing multiple versions of Material UI - -Currently, we use `@mui/material` v5 in the MUI X repo and all tests are run against it. -But MUI X packages are compatible with v5 and v6. -You can run the tests against `@mui/material` v6 by running the following command: - -`pnpm use-material-ui-v6` - -### CI - -There's a `material-ui-v6` workflow in our CircleCI pipeline that you can trigger in CircleCI on the PR you want to test: - -1. Go to https://app.circleci.com/pipelines/github/mui/mui-x?branch=pull/PR_NUMBER and replace `PR_NUMBER` with the PR number you want to test. -2. Click `Trigger Pipeline` button. -3. Expand `Add parameters (optional)` and add the following parameter: - - | Parameter type | Name | Value | - | :------------- | :--------- | :--------------- | - | `string` | `workflow` | `material-ui-v6` | - -4. Click `Trigger Pipeline` button. - -![CircleCI workflow](./circleci-workflow.png) diff --git a/test/circleci-workflow.png b/test/circleci-workflow.png index de549e5eef46..a7db4008e51e 100644 Binary files a/test/circleci-workflow.png and b/test/circleci-workflow.png differ