diff --git a/.circleci/config.yml b/.circleci/config.yml index 7db19933a6e7a..86a47dd04d852 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: @@ -338,50 +345,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 6c6057abfc00b..004f99b50e1c5 100644 --- a/docs/data/charts/getting-started/getting-started.md +++ b/docs/data/charts/getting-started/getting-started.md @@ -41,8 +41,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/data-grid/custom-columns/cell-renderers/rating.tsx b/docs/data/data-grid/custom-columns/cell-renderers/rating.tsx index 9eb9c68ce767d..56160cf6caf68 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 2b77ca3b6ee80..a7e2494e3083a 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 e57e07a9de800..c3a703efd7b44 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 6acc9e5382c17..bc146ab1f18f3 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 f65d2f65836b9..600694c6c2bab 100644 --- a/packages/x-charts-pro/README.md +++ b/packages/x-charts-pro/README.md @@ -15,9 +15,9 @@ This component has the following peer dependencies that you will need to install ```json "peerDependencies": { - "@mui/material": "^5.15.14", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "@mui/material": "^5.15.14 || ^6.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 73c250e6f1a39..94d14d1ab15e4 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 babe6333320c4..7660f5479a2af 100644 --- a/packages/x-charts/README.md +++ b/packages/x-charts/README.md @@ -15,9 +15,9 @@ This component has the following peer dependencies that you will need to install ```json "peerDependencies": { - "@mui/material": "^5.15.14", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "@mui/material": "^5.15.14 || ^6.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 f801917b2cd61..27eb6a52ce144 100644 --- a/packages/x-charts/package.json +++ b/packages/x-charts/package.json @@ -53,8 +53,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 997cdbd30429b..bb0f82706104f 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 29337e8d7191e..fb17106413fd3 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 4bbe16ff5cb63..8bc52cb6c2e30 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 ed345538cc148..ac447050ea370 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 { DrawingProvider } from '../context/DrawingProvider'; @@ -21,17 +21,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 722753c858338..56c2ec168ca94 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 4caf314b9a853..fcd8aa855c501 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 c8527da8ec8b6..a578b5c362e42 100644 --- a/packages/x-data-grid-premium/README.md +++ b/packages/x-data-grid-premium/README.md @@ -15,9 +15,9 @@ This component has the following peer dependencies that you will need to install ```json "peerDependencies": { - "@mui/material": "^5.15.14", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "@mui/material": "^5.15.14 || ^6.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 4934928cfc916..cbea611b95b5b 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 9f60eb3694cf7..f956007843f70 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 25a3704effd43..77a9ce4dd6299 100644 --- a/packages/x-data-grid-pro/README.md +++ b/packages/x-data-grid-pro/README.md @@ -15,9 +15,9 @@ This component has the following peer dependencies that you will need to install ```json "peerDependencies": { - "@mui/material": "^5.15.14", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "@mui/material": "^5.15.14 || ^6.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 0ecf26914d482..d43020a31a593 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 b8e38ff9df4a6..115a7b69feeb5 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 350aa89568c4f..5f8b9422490b9 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 61a722c634f8e..8ba96004f2253 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 25de6af92b191..62b13cce4e92b 100644 --- a/packages/x-data-grid/README.md +++ b/packages/x-data-grid/README.md @@ -15,9 +15,9 @@ This component has the following peer dependencies that you will need to install ```json "peerDependencies": { - "@mui/material": "^5.15.14", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "@mui/material": "^5.15.14 || ^6.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 d7b56140fc12e..a7e24276e9d0f 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 1ee0bd2c105f0..35df8e23d954f 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 a5645d892066b..a4baffb0dabfa 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 f92b82e0dc2f0..cd4d6d324bb53 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 94421d70f768b..002a0d18ccad8 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 44d608d5602fd..8f1153174c38a 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 0952929261989..2c859d30fccbf 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'; @@ -252,8 +253,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 0aa832db0d5f5..055f24b470593 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 8a5f497bc1aaa..22274f718010a 100644 --- a/packages/x-date-pickers-pro/README.md +++ b/packages/x-date-pickers-pro/README.md @@ -34,9 +34,9 @@ This component has the following peer dependencies that you will need to install ```json "peerDependencies": { - "@mui/material": "^5.15.14", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "@mui/material": "^5.15.14 || ^6.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 ecd6412064f69..d0e3a5f14b5bf 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 3410aec62f209..a8b9fc59c4c98 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 9c0bde6d9d5bb..aa1862e0f49a5 100644 --- a/packages/x-date-pickers-pro/src/internals/hooks/useEnrichedRangePickerFieldProps.ts +++ b/packages/x-date-pickers-pro/src/internals/hooks/useEnrichedRangePickerFieldProps.ts @@ -189,7 +189,7 @@ const useMultiInputFieldSlotProps = < const previousRangePosition = React.useRef(rangePosition); React.useEffect(() => { - if (!open) { + if (!open || wrapperVariant === 'mobile') { return; } @@ -207,7 +207,7 @@ const useMultiInputFieldSlotProps = < previousRangePosition.current === rangePosition ? currentView : 0, ); previousRangePosition.current = rangePosition; - }, [rangePosition, open, currentView, startFieldRef, endFieldRef]); + }, [rangePosition, open, currentView, startFieldRef, endFieldRef, wrapperVariant]); const openRangeStartSelection: React.UIEventHandler = (event) => { event.stopPropagation(); @@ -364,7 +364,7 @@ const useSingleInputFieldSlotProps = < const handleFieldRef = useForkRef(fieldProps.unstableFieldRef, startFieldRef, endFieldRef); React.useEffect(() => { - if (!open || !startFieldRef.current) { + if (!open || !startFieldRef.current || wrapperVariant === 'mobile') { return; } @@ -381,7 +381,7 @@ const useSingleInputFieldSlotProps = < : sections.lastIndexOf(currentView); startFieldRef.current?.focusField(newSelectedSection); } - }, [rangePosition, open, currentView, startFieldRef]); + }, [rangePosition, open, currentView, startFieldRef, wrapperVariant]); const updateRangePosition = () => { if (!startFieldRef.current?.isFieldFocused()) { diff --git a/packages/x-date-pickers/README.md b/packages/x-date-pickers/README.md index aa2376eac454f..a094a44ef58f9 100644 --- a/packages/x-date-pickers/README.md +++ b/packages/x-date-pickers/README.md @@ -34,9 +34,9 @@ This component has the following peer dependencies that you will need to install ```json "peerDependencies": { - "@mui/material": "^5.15.14", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "@mui/material": "^5.15.14 || ^6.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 7ffe54b91874c..1e6f78678547e 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 148ed86ce6e9d..1569494e29ff5 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.17", diff --git a/packages/x-license/package.json b/packages/x-license/package.json index 8dc6070e560a5..89d0e70b61379 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.17", diff --git a/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx b/packages/x-license/src/useLicenseVerifier/useLicenseVerifier.test.tsx index 7605292c6acd8..1cfd9ab49dd68 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 9d9bfe64f2757..a324f7ac02845 100644 --- a/packages/x-tree-view-pro/README.md +++ b/packages/x-tree-view-pro/README.md @@ -15,9 +15,9 @@ This component has the following peer dependencies that you will need to install ```json "peerDependencies": { - "@mui/material": "^5.15.14", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "@mui/material": "^5.15.14 || ^6.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 05656fb77def2..e2eb33a8aee81 100644 --- a/packages/x-tree-view-pro/package.json +++ b/packages/x-tree-view-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-tree-view/README.md b/packages/x-tree-view/README.md index 567adb406dfb2..ff4816b596bbc 100644 --- a/packages/x-tree-view/README.md +++ b/packages/x-tree-view/README.md @@ -15,9 +15,9 @@ This component has the following peer dependencies that you will need to install ```json "peerDependencies": { - "@mui/material": "^5.15.14", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "@mui/material": "^5.15.14 || ^6.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 2f479f2652a76..f6ce9f354a094 100644 --- a/packages/x-tree-view/package.json +++ b/packages/x-tree-view/package.json @@ -56,8 +56,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/useTreeViewItems/useTreeViewItems.test.tsx b/packages/x-tree-view/src/internals/plugins/useTreeViewItems/useTreeViewItems.test.tsx index 1fda8500521e8..7dc729dad6d45 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, @@ -22,16 +22,28 @@ describeTreeView< this.skip(); } - expect(() => - render({ items: [{ id: '1' }, { id: '1' }], withErrorBoundary: true }), - ).toErrorDev([ - ...(treeViewComponentName === 'SimpleTreeView' - ? ['Encountered two children with the same key'] - : []), - '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`, - ]); + if (treeViewComponentName === 'SimpleTreeView') { + expect(() => + render({ items: [{ id: '1' }, { id: '1' }], withErrorBoundary: true }), + ).toErrorDev([ + 'Encountered two children with the same key, `1`', + 'MUI X: The Tree View component requires all items to have a unique `id` property.', + 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`, + ]); + } 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.', + 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`, + ]); + } }); it('should be able to use a custom id attribute', function test() { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 095dcbb3da838..5fe897bf428ff 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -761,10 +761,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/internal-test-utils': @@ -832,10 +832,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': @@ -1002,10 +1002,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 @@ -1058,7 +1058,7 @@ importers: specifier: ^11.0.1 version: 11.0.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/icons-material': @@ -1114,10 +1114,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 @@ -1176,10 +1176,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 @@ -1229,10 +1229,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 @@ -1324,10 +1324,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 @@ -1377,7 +1377,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': @@ -1397,7 +1397,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': @@ -1435,10 +1435,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 @@ -1494,10 +1494,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 07f2aed4e3aba..abc2fe55845b4 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 5bb982bb61ca6..594badd07c282 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 de549e5eef462..a7db4008e51ee 100644 Binary files a/test/circleci-workflow.png and b/test/circleci-workflow.png differ