From d114e96c4c724e3f8234a171f6a10686a68fdd91 Mon Sep 17 00:00:00 2001 From: Armin Mehinovic <4390250+arminmeh@users.noreply.github.com> Date: Fri, 11 Oct 2024 11:22:20 +0200 Subject: [PATCH 01/44] v7.20.0 (#14910) Signed-off-by: Armin Mehinovic <4390250+arminmeh@users.noreply.github.com> Co-authored-by: Flavien DELANGLE Co-authored-by: Lukas Tyla --- CHANGELOG.md | 92 +++++++++++++++++++++ package.json | 2 +- packages/x-charts-pro/package.json | 2 +- packages/x-charts-vendor/package.json | 2 +- packages/x-charts/package.json | 2 +- packages/x-codemod/package.json | 2 +- packages/x-data-grid-generator/package.json | 2 +- packages/x-data-grid-premium/package.json | 2 +- packages/x-data-grid-pro/package.json | 2 +- packages/x-data-grid/package.json | 2 +- packages/x-date-pickers-pro/package.json | 2 +- packages/x-date-pickers/package.json | 2 +- packages/x-internals/package.json | 2 +- packages/x-license/package.json | 2 +- packages/x-tree-view-pro/package.json | 2 +- packages/x-tree-view/package.json | 2 +- scripts/README.md | 1 + scripts/releaseChangelog.mjs | 28 ++++--- 18 files changed, 123 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd938614ee1b..cad4db17e886 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,96 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 7.20.0 + +_Oct 11, 2024_ + +We'd like to offer a big thanks to the 14 contributors who made this release possible. Here are some highlights ✨: + +- 📚 Updated ["What's new"](https://mui.com/x/whats-new/) page giving more detailed overview of the latest new features and other highlights +- 📚 New [collapsible column groups demo](https://mui.com/x/react-data-grid/column-groups/#collapsible-column-groups) for the Data Grid component +- 📚 New [Tree Item Customization](https://mui.com/x/react-tree-view/tree-item-customization/) documentation to learn how to use the new APIs to create custom Tree Items. The old APIs (`props.ContentComponent` and `props.ContentProps`) have been deprecated and will be removed in the new major version of the Tree View component. +- 🌍 Improve Japanese (ja-JP) locale on the Data Grid component +- 🐞 Bugfixes +- 📚 Other documentation improvements + +Special thanks go out to the community contributors who have helped make this release possible: +@k-rajat19, @kalyan90, @uma-neko, @vfbiby. +Following are all team members who have contributed to this release: @alelthomas, @arminmeh, @arthurbalduini, +@cherniavskii, @flaviendelangle, @JCQuintas, @MBilalShafi, @noraleonte, @oliviertassinari, @samuelsycamore, @siriwatknp. + + + +### Data Grid + +#### `@mui/x-data-grid@7.20.0` + +- [DataGrid] Add `onColumnHeaderContextMenu` event (#14734) @vfbiby +- [DataGrid] Avoid row spanning computation of outdated rows (#14902) @MBilalShafi +- [DataGrid] Fix scrollbar position not being updated after `scrollToIndexes` (#14888) @arminmeh +- [DataGrid] Pass `rowId` param to `processRowUpdate` (#14821) @k-rajat19 +- [l10n] Improve Japanese (ja-JP) locale (#14870) @uma-neko + +#### `@mui/x-data-grid-pro@7.20.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan') + +Same changes as in `@mui/x-data-grid@7.20.0`, plus: + +- [DataGridPro] Fix wording on the `rowSelectionPropagation` JSDoc and doc section (#14907) @flaviendelangle + +#### `@mui/x-data-grid-premium@7.20.0` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link 'Premium plan') + +Same changes as in `@mui/x-data-grid-pro@7.20.0`. + +### Date and Time Pickers + +#### `@mui/x-date-pickers@7.20.0` + +- [pickers] Add `PageUp` and `PageDown` support for time components (#14812) @arthurbalduini +- [pickers] Fix regression on `PickerValidDate` (#14896) @flaviendelangle +- [pickers] Move the `DateFieldInPickerProps` interface to the `DatePicker` folder and rename it `DatePickerFieldProps` (same for time and date time) (#14828) @flaviendelangle + +#### `@mui/x-date-pickers-pro@7.20.0` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan') + +Same changes as in `@mui/x-date-pickers@7.20.0`. + +### Charts + +#### `@mui/x-charts@7.20.0` + +No changes since `@mui/x-charts@v7.19.0`. + +#### `@mui/x-charts-pro@7.0.0-beta.4` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan') + +Same changes as in `@mui/x-charts@7.20.0`. + +### Tree View + +#### `@mui/x-tree-view@7.20.0` + +- [TreeItem] Deprecate the `ContentComponent` and `ContentProps` props (#14908) @flaviendelangle +- [TreeView] Rework how items are being rendered in Rich Tree View components (#14749) @flaviendelangle + +### Docs + +- [docs] Update "What's new" page (#14858) @cherniavskii +- [docs] Add collapsible column groups demo (#14818) @cherniavskii +- [docs] Add custom columns panel demo (#14825) @cherniavskii +- [docs] Capitalize all instances of "Data Grid" (#14884) @samuelsycamore +- [docs] Divide charts `tooltip` and `highlighting` pages (#14824) @JCQuintas +- [docs] Document the `TreeItem2` component and the `useTreeItem2` hook (#14551) @noraleonte +- [docs] Fix column pinning for "Disable detail panel content scroll" section (#14854 and #14885) @kalyan90 +- [docs] Fix detail panel demo not working well with pinned columns (#14883) @cherniavskii +- [docs] New recipe of a read-only field (#14606) @flaviendelangle +- [docs] Change demo name example (#14822) @alelthomas + +### Core + +- [core] Support `@mui/utils` v6 (#14867) @siriwatknp +- [code-infra] Remove deprecated `data-mui-test` in favour of `data-testid` (#14882) @JCQuintas +- [code-infra] Update renovate config and add a `vitest` group (#14856) @JCQuintas +- [test] Replace `waitFor()` with `act()` (#14851) @oliviertassinari +- [test] Restore "pnpm tc" CLI (#14852) @oliviertassinari + ## 7.19.0 _Oct 4, 2024_ @@ -678,6 +768,8 @@ Same changes as in `@mui/x-charts@7.12.1`, plus: #### `@mui/x-tree-view@7.12.1` +No changes since `@mui/x-tree-view@7.12.0`. + ### Docs - [docs] Add a warning to promote the usage of `updateRows` (#14027) @MBilalShafi diff --git a/package.json b/package.json index 1f637d64683e..3a3919035b18 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "7.19.0", + "version": "7.20.0", "private": true, "scripts": { "preinstall": "npx only-allow pnpm", diff --git a/packages/x-charts-pro/package.json b/packages/x-charts-pro/package.json index c1cb4dcf4032..97b8cbc52a96 100644 --- a/packages/x-charts-pro/package.json +++ b/packages/x-charts-pro/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-charts-pro", - "version": "7.0.0-beta.3", + "version": "7.0.0-beta.4", "description": "The Pro plan edition of the Charts components (MUI X).", "author": "MUI Team", "main": "src/index.ts", diff --git a/packages/x-charts-vendor/package.json b/packages/x-charts-vendor/package.json index 32017c75598c..79dd902e3dd1 100644 --- a/packages/x-charts-vendor/package.json +++ b/packages/x-charts-vendor/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-charts-vendor", - "version": "7.19.0", + "version": "7.20.0", "description": "Vendored dependencies for MUI X Charts", "author": "MUI Team", "keywords": [ diff --git a/packages/x-charts/package.json b/packages/x-charts/package.json index 19bf9b128ca8..08869517b7ce 100644 --- a/packages/x-charts/package.json +++ b/packages/x-charts/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-charts", - "version": "7.19.0", + "version": "7.20.0", "description": "The community edition of the Charts components (MUI X).", "author": "MUI Team", "main": "src/index.js", diff --git a/packages/x-codemod/package.json b/packages/x-codemod/package.json index 97463fe9bde0..309dc08ecb8b 100644 --- a/packages/x-codemod/package.json +++ b/packages/x-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-codemod", - "version": "7.18.0", + "version": "7.20.0", "bin": "./codemod.js", "private": false, "author": "MUI Team", diff --git a/packages/x-data-grid-generator/package.json b/packages/x-data-grid-generator/package.json index 6c9e6a5d5a7b..cf21b769479f 100644 --- a/packages/x-data-grid-generator/package.json +++ b/packages/x-data-grid-generator/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid-generator", - "version": "7.19.0", + "version": "7.20.0", "description": "Generate fake data for demo purposes only.", "author": "MUI Team", "main": "src/index.ts", diff --git a/packages/x-data-grid-premium/package.json b/packages/x-data-grid-premium/package.json index 07f38acf80c6..2831040fbfb4 100644 --- a/packages/x-data-grid-premium/package.json +++ b/packages/x-data-grid-premium/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid-premium", - "version": "7.19.0", + "version": "7.20.0", "description": "The Premium plan edition of the Data Grid Components (MUI X).", "author": "MUI Team", "main": "src/index.ts", diff --git a/packages/x-data-grid-pro/package.json b/packages/x-data-grid-pro/package.json index 5504d4e612ec..b6833d5bf47d 100644 --- a/packages/x-data-grid-pro/package.json +++ b/packages/x-data-grid-pro/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid-pro", - "version": "7.19.0", + "version": "7.20.0", "description": "The Pro plan edition of the Data Grid components (MUI X).", "author": "MUI Team", "main": "src/index.ts", diff --git a/packages/x-data-grid/package.json b/packages/x-data-grid/package.json index ba5f1ef9eb65..61dbcb5a3321 100644 --- a/packages/x-data-grid/package.json +++ b/packages/x-data-grid/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid", - "version": "7.19.0", + "version": "7.20.0", "description": "The Community plan edition of the Data Grid components (MUI X).", "author": "MUI Team", "main": "src/index.ts", diff --git a/packages/x-date-pickers-pro/package.json b/packages/x-date-pickers-pro/package.json index e8936b60e3a8..968697a6c21c 100644 --- a/packages/x-date-pickers-pro/package.json +++ b/packages/x-date-pickers-pro/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-date-pickers-pro", - "version": "7.19.0", + "version": "7.20.0", "description": "The Pro plan edition of the Date and Time Picker components (MUI X).", "author": "MUI Team", "main": "src/index.ts", diff --git a/packages/x-date-pickers/package.json b/packages/x-date-pickers/package.json index 23c7765f14c0..3f4ace490c85 100644 --- a/packages/x-date-pickers/package.json +++ b/packages/x-date-pickers/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-date-pickers", - "version": "7.19.0", + "version": "7.20.0", "description": "The community edition of the Date and Time Picker components (MUI X).", "author": "MUI Team", "main": "src/index.ts", diff --git a/packages/x-internals/package.json b/packages/x-internals/package.json index 7918338925f4..94c1f8969389 100644 --- a/packages/x-internals/package.json +++ b/packages/x-internals/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-internals", - "version": "7.18.0", + "version": "7.20.0", "description": "Utility functions for the MUI X packages (internal use only).", "author": "MUI Team", "license": "MIT", diff --git a/packages/x-license/package.json b/packages/x-license/package.json index 423cf538f5fb..dca472c63710 100644 --- a/packages/x-license/package.json +++ b/packages/x-license/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-license", - "version": "7.18.0", + "version": "7.20.0", "description": "MUI X License verification", "author": "MUI Team", "main": "src/index.ts", diff --git a/packages/x-tree-view-pro/package.json b/packages/x-tree-view-pro/package.json index 668892b9dd45..9f7b6ba9177b 100644 --- a/packages/x-tree-view-pro/package.json +++ b/packages/x-tree-view-pro/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-tree-view-pro", - "version": "7.19.0", + "version": "7.20.0", "description": "The Pro plan edition of the Tree View components (MUI X).", "author": "MUI Team", "main": "src/index.ts", diff --git a/packages/x-tree-view/package.json b/packages/x-tree-view/package.json index dea0bc5e9a99..1614f7d4374d 100644 --- a/packages/x-tree-view/package.json +++ b/packages/x-tree-view/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-tree-view", - "version": "7.19.0", + "version": "7.20.0", "description": "The community edition of the Tree View components (MUI X).", "author": "MUI Team", "main": "src/index.ts", diff --git a/scripts/README.md b/scripts/README.md index c6e13c5b3fdb..aa96e3420e62 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -24,6 +24,7 @@ pnpm release:changelog --githubToken YOUR_GITHUB_TOKEN (needs "public_repo" permission) --lastRelease The release to compare against (default: the last one) --release The branch to release (default: master) + --nextVersion Expected version of the next release (if not provided, __VERSION__ placeholders must be updated manually) ``` > :warning: the script will add a separator string in form of a comment like this right after the highlights: diff --git a/scripts/releaseChangelog.mjs b/scripts/releaseChangelog.mjs index 7b565212ddb1..209304695b1c 100644 --- a/scripts/releaseChangelog.mjs +++ b/scripts/releaseChangelog.mjs @@ -61,7 +61,7 @@ function resolvePackagesByLabels(labels) { } async function main(argv) { - const { githubToken, lastRelease: lastReleaseInput, release } = argv; + const { githubToken, lastRelease: lastReleaseInput, release, nextVersion } = argv; if (!githubToken) { throw new TypeError( @@ -297,7 +297,7 @@ async function main(argv) { return ''; } - return `Special thanks go out to our community contributors who have helped make this release possible:\n${contributors.join(', ')}.`; + return `Special thanks go out to the community contributors who have helped make this release possible:\n${contributors.join(', ')}.`; }; const logTeamSection = () => { @@ -328,15 +328,14 @@ ${logTeamSection()} #### \`@mui/x-data-grid@__VERSION__\` -${logChangelogSection(dataGridCommits)} +${logChangelogSection(dataGridCommits) || `No changes since \`@mui/x-data-grid@${lastRelease}\`.`} #### \`@mui/x-data-grid-pro@__VERSION__\` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan') Same changes as in \`@mui/x-data-grid@__VERSION__\`${ dataGridProCommits.length > 0 ? ', plus:\n' : '.' } -${logChangelogSection(dataGridProCommits)} - +${logChangelogSection(dataGridProCommits)}${dataGridProCommits.length > 0 ? '\n' : ''} #### \`@mui/x-data-grid-premium@__VERSION__\` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link 'Premium plan') Same changes as in \`@mui/x-data-grid-pro@__VERSION__\`${ @@ -347,31 +346,29 @@ ${logChangelogSection(dataGridPremiumCommits)}${dataGridPremiumCommits.length > #### \`@mui/x-date-pickers@__VERSION__\` -${logChangelogSection(pickersCommits)} +${logChangelogSection(pickersCommits) || `No changes since \`@mui/x-date-pickers@${lastRelease}\`.`} #### \`@mui/x-date-pickers-pro@__VERSION__\` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan') Same changes as in \`@mui/x-date-pickers@__VERSION__\`${ pickersProCommits.length > 0 ? ', plus:\n' : '.' } -${logChangelogSection(pickersProCommits)} - +${logChangelogSection(pickersProCommits)}${pickersProCommits.length > 0 ? '\n' : ''} ### Charts #### \`@mui/x-charts@__VERSION__\` -${logChangelogSection(chartsCommits)} +${logChangelogSection(chartsCommits) || `No changes since \`@mui/x-charts@${lastRelease}\`.`} #### \`@mui/x-charts-pro@__VERSION-ALPHA__\` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan') Same changes as in \`@mui/x-charts@__VERSION__\`${chartsProCommits.length > 0 ? ', plus:\n' : '.'} -${logChangelogSection(chartsProCommits)} - +${logChangelogSection(chartsProCommits)}${chartsProCommits.length > 0 ? '\n' : ''} ### Tree View #### \`@mui/x-tree-view@__VERSION__\` -${logChangelogSection(treeViewCommits)} +${logChangelogSection(treeViewCommits) || `No changes since \`@mui/x-tree-view@${lastRelease}\`.`} ${logChangelogSection(codemodCommits, `### \`@mui/x-codemod@__VERSION__\``)} ${logChangelogSection(docsCommits, '### Docs')} ${logChangelogSection(coreCommits, '### Core')} @@ -380,7 +377,7 @@ ${logChangelogSection(otherCommits, '')} `; // eslint-disable-next-line no-console -- output of this script - console.log(changelog); + console.log(nextVersion ? changelog.replace(/__VERSION__/g, nextVersion) : changelog); } yargs(hideBin(process.argv)) @@ -405,6 +402,11 @@ yargs(hideBin(process.argv)) default: 'master', describe: 'Ref which we want to release', type: 'string', + }) + .option('nextVersion', { + describe: + 'The version expected to be released e.g. `5.2.0`. Replaces `_VERSION__` placeholder in the changelog.', + type: 'string', }); }, handler: main, From ad83347f10e9d9b33d57afc4299946090d593d4a Mon Sep 17 00:00:00 2001 From: Armin Mehinovic <4390250+arminmeh@users.noreply.github.com> Date: Fri, 11 Oct 2024 12:07:47 +0200 Subject: [PATCH 02/44] [core] Fix docs deploy command (#14920) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3a3919035b18..012fc1220f68 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "docs:populate:demos": "pnpm --filter docs populate:demos", "docs:importDocsStatic": "node scripts/importDocsStatic.mjs", "docs:size-why": "cross-env DOCS_STATS_ENABLED=true pnpm docs:build", - "docs:deploy": "pnpm --filter docs deploy", + "docs:deploy": "pnpm --filter docs run deploy", "deduplicate": "pnpm dedupe", "dataset:file-tree": "tsx ./scripts/treeDataFromFileTree.ts", "l10n": "tsx ./scripts/l10n.ts", From 185dd938565432eda64909dd15f5ba6a91c39d67 Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Fri, 11 Oct 2024 13:56:34 +0200 Subject: [PATCH 03/44] [codemod] Add a new utility to rename imports (#14919) Signed-off-by: Flavien DELANGLE Co-authored-by: Nora <72460825+noraleonte@users.noreply.github.com> --- packages/x-codemod/src/util/renameImports.ts | 176 ++++++++++++++++++ .../pickers/view-components-rename/index.ts | 156 +++++++--------- .../view-components-rename.test.ts | 2 +- .../actual-nested-imports.spec.tsx | 1 + .../expected-nested-imports.spec.tsx | 7 +- .../expected-root-imports.spec.tsx | 12 +- .../index.ts | 66 ++----- ...rename-tree-view-simple-tree-view.test.ts} | 0 .../tree-view/rename-use-tree-item/index.ts | 37 ++-- ...e-item.ts => rename-use-tree-item.test.ts} | 0 10 files changed, 292 insertions(+), 165 deletions(-) create mode 100644 packages/x-codemod/src/util/renameImports.ts rename packages/x-codemod/src/v7.0.0/tree-view/rename-tree-view-simple-tree-view/{rename-tree-view-simple-tree-view.ts => rename-tree-view-simple-tree-view.test.ts} (100%) rename packages/x-codemod/src/v7.0.0/tree-view/rename-use-tree-item/{rename-use-tree-item.ts => rename-use-tree-item.test.ts} (100%) diff --git a/packages/x-codemod/src/util/renameImports.ts b/packages/x-codemod/src/util/renameImports.ts new file mode 100644 index 000000000000..07821cf7c191 --- /dev/null +++ b/packages/x-codemod/src/util/renameImports.ts @@ -0,0 +1,176 @@ +import type { + ASTPath, + Collection, + ImportDeclaration, + ImportSpecifier, + JSCodeshift, +} from 'jscodeshift'; + +interface ImportConfig { + oldEndpoint?: string; + newEndpoint?: string; + skipRoot?: boolean; + importsMapping: Record; +} + +interface RenameImportsParameters { + j: JSCodeshift; + root: Collection; + packageNames: string[]; + imports: ImportConfig[]; +} + +const getPathStrFromPath = (path: ASTPath | ASTPath) => { + let cleanPath: ASTPath; + if (path.get('type').value === 'ImportDeclaration') { + cleanPath = path as ASTPath; + } else { + cleanPath = path.parentPath.parentPath as ASTPath; + } + + return cleanPath.node.source.value?.toString() ?? ''; +}; + +const getRelativeEndpointFromPathStr = (pathStr: string, packageNames: string[]) => { + return pathStr.replace(new RegExp(`^(${packageNames.join('|')})/`), ''); +}; + +const getMatchingNestedImport = ( + path: ASTPath | ASTPath, + parameters: RenameImportsParameters, +) => { + const pathStr = getPathStrFromPath(path); + const relativeEndpoint = getRelativeEndpointFromPathStr(pathStr, parameters.packageNames); + return parameters.imports.find((importConfig) => importConfig.oldEndpoint === relativeEndpoint); +}; + +const getMatchingRootImport = ( + path: ASTPath, + parameters: RenameImportsParameters, +) => { + return parameters.imports.find((importConfig) => { + return ( + !importConfig.skipRoot && importConfig.importsMapping.hasOwnProperty(path.node.imported.name) + ); + }); +}; + +export function renameImports(parameters: RenameImportsParameters) { + const { j, root } = parameters; + + const renamedIdentifiersMap: Record = {}; + + const importDeclarations = root + // Find all the import declarations (import { ... } from '...') + .find(j.ImportDeclaration); + + // Rename the nested imports specifiers + // - import { A } from '@mui/x-date-pickers/A' + // + import { B } from '@mui/x-date-pickers/A' + const nestedImportRegExp = new RegExp(`^(${parameters.packageNames.join('|')})/(.*)$`); + importDeclarations + // Filter out the declarations that are not nested endpoints of the matching packages or that don't have any update to apply + .filter((path) => { + const pathStr = getPathStrFromPath(path); + if (!pathStr.match(nestedImportRegExp)) { + return false; + } + + return !!getMatchingNestedImport(path, parameters); + }) + // Find all the import specifiers (extract A in import { A } from '...') + .find(j.ImportSpecifier) + // Filter out the specifiers that don't need to be updated + .filter((path) => { + return getMatchingNestedImport(path, parameters)!.importsMapping.hasOwnProperty( + path.node.imported.name, + ); + }) + // Rename the import specifiers + .replaceWith((path) => { + const newName = getMatchingNestedImport(path, parameters)!.importsMapping[ + path.node.imported.name + ]; + + // If the import is alias, we keep the alias and don't rename the variable usage + const hasAlias = path.node.local?.name !== path.node.imported.name; + if (hasAlias) { + return j.importSpecifier(j.identifier(newName), j.identifier(path.node.local!.name)); + } + + renamedIdentifiersMap[path.node.imported.name] = newName; + return j.importSpecifier(j.identifier(newName)); + }); + + // Rename the root imports specifiers + // - import { A } from '@mui/x-date-pickers' + // + import { B } from '@mui/x-date-pickers' + const rootImportRegExp = new RegExp(`^(${parameters.packageNames.join('|')})$`); + importDeclarations + // Filter out the declarations that are not root endpoint of the matching packages + .filter((path) => { + const pathStr = getPathStrFromPath(path); + return !!pathStr.match(rootImportRegExp); + }) + .find(j.ImportSpecifier) + .filter((path) => { + return !!getMatchingRootImport(path, parameters); + }) + // Rename the import specifiers + .replaceWith((path) => { + const newName = getMatchingRootImport(path, parameters)!.importsMapping[ + path.node.imported.name + ]; + + // If the import is alias, we keep the alias and don't rename the variable usage + const hasAlias = path.node.local?.name !== path.node.imported.name; + if (hasAlias) { + return j.importSpecifier(j.identifier(newName), j.identifier(path.node.local!.name)); + } + + renamedIdentifiersMap[path.node.imported.name] = newName; + return j.importSpecifier(j.identifier(newName)); + }); + + // Rename the nested import declarations + // - import { B } from '@mui/x-date-pickers/A' + // + import { B } from '@mui/x-date-pickers/B' + importDeclarations + // Filter out the declarations that are not nested endpoints of the matching packages or that don't have any update to apply + .filter((path) => { + const pathStr = getPathStrFromPath(path); + if (!pathStr.match(nestedImportRegExp)) { + return false; + } + + return !!getMatchingNestedImport(path, parameters)?.newEndpoint; + }) + .replaceWith((path) => { + const pathStr = getPathStrFromPath(path); + const oldEndpoint = getRelativeEndpointFromPathStr(pathStr, parameters.packageNames); + const newEndpoint = getMatchingNestedImport(path, parameters)!.newEndpoint; + const newPathStr = pathStr.replace(oldEndpoint, newEndpoint!); + + return j.importDeclaration( + // Copy over the existing import specifiers + path.node.specifiers, + // Replace the source with our new source + j.stringLiteral(newPathStr), + ); + }); + + // Rename the import usage + // - + // + + root + .find(j.Identifier) + .filter((path) => { + return renamedIdentifiersMap.hasOwnProperty(path.node.name); + }) + .replaceWith((path) => { + const newName = renamedIdentifiersMap[path.node.name]; + return j.identifier(newName); + }); + + return root; +} diff --git a/packages/x-codemod/src/v6.0.0/pickers/view-components-rename/index.ts b/packages/x-codemod/src/v6.0.0/pickers/view-components-rename/index.ts index b6c16cfd4ba3..194996e1a0f4 100644 --- a/packages/x-codemod/src/v6.0.0/pickers/view-components-rename/index.ts +++ b/packages/x-codemod/src/v6.0.0/pickers/view-components-rename/index.ts @@ -1,59 +1,6 @@ -import { ASTPath, ImportDeclaration } from 'jscodeshift'; import type { JsCodeShiftAPI, JsCodeShiftFileInfo } from '../../../types'; +import { renameImports } from '../../../util/renameImports'; -const SUB_PACKAGES = { - CalendarPicker: 'DateCalendar', - CalendarPickerSkeleton: 'DayCalendarSkeleton', - MonthPicker: 'MonthCalendar', - YearPicker: 'YearCalendar', - ClockPicker: 'TimeClock', -}; - -const VARIABLES = { - // Date Calendar - CalendarPicker: 'DateCalendar', - CalendarPickerProps: 'DateCalendarProps', - CalendarPickerSlotsComponent: 'DateCalendarSlotsComponent', - CalendarPickerSlotsComponentsProps: 'DateCalendarSlotsComponentsProps', - CalendarPickerClasses: 'DateCalendarClasses', - CalendarPickerClassKey: 'DateCalendarClassKey', - calendarPickerClasses: 'dateCalendarClasses', - getCalendarPickerUtilityClass: 'getDateCalendarUtilityClass', - - // Month Calendar - MonthPicker: 'MonthCalendar', - MonthPickerProps: 'MonthCalendarProps', - MonthPickerClasses: 'MonthCalendarClasses', - MonthPickerClassKey: 'MonthCalendarClassKey', - monthPickerClasses: 'monthCalendarClasses', - getMonthPickerUtilityClass: 'getMonthCalendarUtilityClass', - - YearPicker: 'YearCalendar', - YearPickerProps: 'YearCalendarProps', - YearPickerClasses: 'YearCalendarClasses', - YearPickerClassKey: 'YearCalendarClassKey', - yearPickerClasses: 'yearCalendarClasses', - getYearPickerUtilityClass: 'getYearCalendarUtilityClass', - - ClockPicker: 'TimeClock', - ClockPickerProps: 'TimeClockProps', - ClockPickerClasses: 'TimeClockClasses', - ClockPickerClassKey: 'TimeClockClassKey', - clockPickerClasses: 'timeClockClasses', - getClockPickerUtilityClass: 'getTimeClockUtilityClass', - - CalendarPickerSkeleton: 'DayCalendarSkeleton', - CalendarPickerSkeletonProps: 'DayCalendarSkeletonProps', - CalendarPickerSkeletonClasses: 'DayCalendarSkeletonClasses', - CalendarPickerSkeletonClassKey: 'DayCalendarSkeletonClassKey', - calendarPickerSkeletonClasses: 'dayCalendarSkeletonClasses', - getCalendarPickerSkeletonUtilityClass: 'getDayCalendarSkeletonUtilityClass', -}; - -const PACKAGE_REGEXP = /@mui\/x-date-pickers(-pro|)(\/(.*)|)/; - -const matchImport = (path: ASTPath) => - (path.node.source.value?.toString() ?? '').match(PACKAGE_REGEXP); export default function transformer(file: JsCodeShiftFileInfo, api: JsCodeShiftAPI, options: any) { const j = api.jscodeshift; const root = j(file.source); @@ -63,38 +10,75 @@ export default function transformer(file: JsCodeShiftFileInfo, api: JsCodeShiftA trailingComma: true, }; - const matchingImports = root.find(j.ImportDeclaration).filter((path) => !!matchImport(path)); - - // Rename the import specifiers - // - import { MonthPicker } from '@mui/x-date-pickers/MonthPicker' - // + import { MonthCalendar } from '@mui/x-date-pickers/MonthPicker' - matchingImports - .find(j.ImportSpecifier) - .filter((path) => VARIABLES.hasOwnProperty(path.node.imported.name)) - .replaceWith((path) => j.importSpecifier(j.identifier(VARIABLES[path.node.imported.name]))); - - // Rename the nested import declarations - // - import {} from '@mui/x-date-pickers/MonthPicker' - // + import {} from '@mui/x-date-pickers/MonthCalendar' - matchingImports - .filter((path) => SUB_PACKAGES.hasOwnProperty(matchImport(path)?.[3] ?? '')) - .replaceWith((path) => { - const subPackage = matchImport(path)![3]; - const importPath = path.node.source.value?.toString() ?? ''; - - return j.importDeclaration( - path.node.specifiers, // copy over the existing import specifiers - j.stringLiteral(importPath.replace(subPackage, SUB_PACKAGES[subPackage])), // Replace the source with our new source - ); - }); - - // Rename the import usage - // - - // + - root - .find(j.Identifier) - .filter((path) => VARIABLES.hasOwnProperty(path.node.name)) - .replaceWith((path) => j.identifier(VARIABLES[path.node.name])); + renameImports({ + j, + root, + packageNames: ['@mui/x-date-pickers', '@mui/x-date-pickers-pro'], + imports: [ + { + oldEndpoint: 'CalendarPicker', + newEndpoint: 'DateCalendar', + importsMapping: { + CalendarPicker: 'DateCalendar', + CalendarPickerProps: 'DateCalendarProps', + CalendarPickerSlotsComponent: 'DateCalendarSlotsComponent', + CalendarPickerSlotsComponentsProps: 'DateCalendarSlotsComponentsProps', + CalendarPickerClasses: 'DateCalendarClasses', + CalendarPickerClassKey: 'DateCalendarClassKey', + calendarPickerClasses: 'dateCalendarClasses', + getCalendarPickerUtilityClass: 'getDateCalendarUtilityClass', + }, + }, + { + oldEndpoint: 'MonthPicker', + newEndpoint: 'MonthCalendar', + importsMapping: { + MonthPicker: 'MonthCalendar', + MonthPickerProps: 'MonthCalendarProps', + MonthPickerClasses: 'MonthCalendarClasses', + MonthPickerClassKey: 'MonthCalendarClassKey', + monthPickerClasses: 'monthCalendarClasses', + getMonthPickerUtilityClass: 'getMonthCalendarUtilityClass', + }, + }, + { + oldEndpoint: 'YearPicker', + newEndpoint: 'YearCalendar', + importsMapping: { + YearPicker: 'YearCalendar', + YearPickerProps: 'YearCalendarProps', + YearPickerClasses: 'YearCalendarClasses', + YearPickerClassKey: 'YearCalendarClassKey', + yearPickerClasses: 'yearCalendarClasses', + getYearPickerUtilityClass: 'getYearCalendarUtilityClass', + }, + }, + { + oldEndpoint: 'ClockPicker', + newEndpoint: 'TimeClock', + importsMapping: { + ClockPicker: 'TimeClock', + ClockPickerProps: 'TimeClockProps', + ClockPickerClasses: 'TimeClockClasses', + ClockPickerClassKey: 'TimeClockClassKey', + clockPickerClasses: 'timeClockClasses', + getClockPickerUtilityClass: 'getTimeClockUtilityClass', + }, + }, + { + oldEndpoint: 'CalendarPickerSkeleton', + newEndpoint: 'DayCalendarSkeleton', + importsMapping: { + CalendarPickerSkeleton: 'DayCalendarSkeleton', + CalendarPickerSkeletonProps: 'DayCalendarSkeletonProps', + CalendarPickerSkeletonClasses: 'DayCalendarSkeletonClasses', + CalendarPickerSkeletonClassKey: 'DayCalendarSkeletonClassKey', + calendarPickerSkeletonClasses: 'dayCalendarSkeletonClasses', + getCalendarPickerSkeletonUtilityClass: 'getDayCalendarSkeletonUtilityClass', + }, + }, + ], + }); return root.toSource(printOptions); } diff --git a/packages/x-codemod/src/v6.0.0/pickers/view-components-rename/view-components-rename.test.ts b/packages/x-codemod/src/v6.0.0/pickers/view-components-rename/view-components-rename.test.ts index c45d637ea35a..6ad973056d03 100644 --- a/packages/x-codemod/src/v6.0.0/pickers/view-components-rename/view-components-rename.test.ts +++ b/packages/x-codemod/src/v6.0.0/pickers/view-components-rename/view-components-rename.test.ts @@ -16,7 +16,7 @@ describe('v6.0.0/pickers', () => { const actualPath = `./actual-${testFile}.spec.tsx`; const expectedPath = `./expected-${testFile}.spec.tsx`; - describe(`Community package (${testFile.replace(/-/g, ' ')})`, () => { + describe(`Package (${testFile.replace(/-/g, ' ')})`, () => { it('transforms imports as needed', () => { const actual = transform( { source: read(actualPath) }, diff --git a/packages/x-codemod/src/v7.0.0/tree-view/rename-tree-view-simple-tree-view/actual-nested-imports.spec.tsx b/packages/x-codemod/src/v7.0.0/tree-view/rename-tree-view-simple-tree-view/actual-nested-imports.spec.tsx index 45035f6c615b..b36d2c603cc2 100644 --- a/packages/x-codemod/src/v7.0.0/tree-view/rename-tree-view-simple-tree-view/actual-nested-imports.spec.tsx +++ b/packages/x-codemod/src/v7.0.0/tree-view/rename-tree-view-simple-tree-view/actual-nested-imports.spec.tsx @@ -13,6 +13,7 @@ import { TreeItem } from '@mui/x-tree-view/TreeItem'; function App() { getTreeViewUtilityClass('root'); + // prettier-ignore return ( diff --git a/packages/x-codemod/src/v7.0.0/tree-view/rename-tree-view-simple-tree-view/expected-nested-imports.spec.tsx b/packages/x-codemod/src/v7.0.0/tree-view/rename-tree-view-simple-tree-view/expected-nested-imports.spec.tsx index b2d2c5e39c90..5f709c3eba0a 100644 --- a/packages/x-codemod/src/v7.0.0/tree-view/rename-tree-view-simple-tree-view/expected-nested-imports.spec.tsx +++ b/packages/x-codemod/src/v7.0.0/tree-view/rename-tree-view-simple-tree-view/expected-nested-imports.spec.tsx @@ -7,15 +7,16 @@ import { SimpleTreeViewClassKey, simpleTreeViewClasses, getSimpleTreeViewUtilityClass, -} from '@mui/x-tree-view/TreeView'; +} from '@mui/x-tree-view/SimpleTreeView'; import { TreeItem } from '@mui/x-tree-view/TreeItem'; function App() { getSimpleTreeViewUtilityClass('root'); + // prettier-ignore return ( - + ( - + ) ); } diff --git a/packages/x-codemod/src/v7.0.0/tree-view/rename-tree-view-simple-tree-view/expected-root-imports.spec.tsx b/packages/x-codemod/src/v7.0.0/tree-view/rename-tree-view-simple-tree-view/expected-root-imports.spec.tsx index 4a6b80863b69..29aca88cef92 100644 --- a/packages/x-codemod/src/v7.0.0/tree-view/rename-tree-view-simple-tree-view/expected-root-imports.spec.tsx +++ b/packages/x-codemod/src/v7.0.0/tree-view/rename-tree-view-simple-tree-view/expected-root-imports.spec.tsx @@ -1,10 +1,10 @@ /* eslint-disable no-restricted-imports */ // @ts-nocheck import { - TreeView, - TreeViewProps, - TreeViewClasses, - TreeViewClassKey, - treeViewClasses, - getTreeViewUtilityClass, + SimpleTreeView, + SimpleTreeViewProps, + SimpleTreeViewClasses, + SimpleTreeViewClassKey, + simpleTreeViewClasses, + getSimpleTreeViewUtilityClass, } from '@mui/x-tree-view'; diff --git a/packages/x-codemod/src/v7.0.0/tree-view/rename-tree-view-simple-tree-view/index.ts b/packages/x-codemod/src/v7.0.0/tree-view/rename-tree-view-simple-tree-view/index.ts index d3fe7ea1347c..21f2db6adaf2 100644 --- a/packages/x-codemod/src/v7.0.0/tree-view/rename-tree-view-simple-tree-view/index.ts +++ b/packages/x-codemod/src/v7.0.0/tree-view/rename-tree-view-simple-tree-view/index.ts @@ -1,19 +1,5 @@ -import { ASTPath, ImportDeclaration } from 'jscodeshift'; import type { JsCodeShiftAPI, JsCodeShiftFileInfo } from '../../../types'; - -const VARIABLES = { - TreeView: 'SimpleTreeView', - TreeViewProps: 'SimpleTreeViewProps', - TreeViewClasses: 'SimpleTreeViewClasses', - TreeViewClassKey: 'SimpleTreeViewClassKey', - treeViewClasses: 'simpleTreeViewClasses', - getTreeViewUtilityClass: 'getSimpleTreeViewUtilityClass', -}; - -const PACKAGE_REGEXP = /@mui\/x-tree-view(\/(.*)|)/; - -const matchImport = (path: ASTPath) => - (path.node.source.value?.toString() ?? '').match(PACKAGE_REGEXP); +import { renameImports } from '../../../util/renameImports'; export default function transformer(file: JsCodeShiftFileInfo, api: JsCodeShiftAPI, options: any) { const j = api.jscodeshift; @@ -24,37 +10,25 @@ export default function transformer(file: JsCodeShiftFileInfo, api: JsCodeShiftA trailingComma: true, }; - const matchingImports = root.find(j.ImportDeclaration).filter((path) => !!matchImport(path)); - - // Rename the import specifiers - // - import { TreeView } from '@mui/x-tree-view/TreeView' - // + import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView' - matchingImports - .find(j.ImportSpecifier) - .filter((path) => VARIABLES.hasOwnProperty(path.node.imported.name)) - .replaceWith((path) => j.importSpecifier(j.identifier(VARIABLES[path.node.imported.name]))); - - // Rename the nested import declarations - // - import {} from '@mui/x-tree-view/TreeView' - // + import {} from '@mui/x-tree-view/SimpleTreeView' - matchingImports - .filter((path) => matchImport(path)?.[2] === 'TreeView') - .replaceWith((path) => { - const importPath = path.node.source.value?.toString() ?? ''; - - return j.importDeclaration( - path.node.specifiers, // copy over the existing import specifiers - j.stringLiteral(importPath.replace('TreeView', 'SimpleTreeView')), // Replace the source with our new source - ); - }); - - // Rename the import usage - // - - // + - root - .find(j.Identifier) - .filter((path) => VARIABLES.hasOwnProperty(path.node.name)) - .replaceWith((path) => j.identifier(VARIABLES[path.node.name])); + renameImports({ + j, + root, + packageNames: ['@mui/x-tree-view'], + imports: [ + { + oldEndpoint: 'TreeView', + newEndpoint: 'SimpleTreeView', + importsMapping: { + TreeView: 'SimpleTreeView', + TreeViewProps: 'SimpleTreeViewProps', + TreeViewClasses: 'SimpleTreeViewClasses', + TreeViewClassKey: 'SimpleTreeViewClassKey', + treeViewClasses: 'simpleTreeViewClasses', + getTreeViewUtilityClass: 'getSimpleTreeViewUtilityClass', + }, + }, + ], + }); return root.toSource(printOptions); } diff --git a/packages/x-codemod/src/v7.0.0/tree-view/rename-tree-view-simple-tree-view/rename-tree-view-simple-tree-view.ts b/packages/x-codemod/src/v7.0.0/tree-view/rename-tree-view-simple-tree-view/rename-tree-view-simple-tree-view.test.ts similarity index 100% rename from packages/x-codemod/src/v7.0.0/tree-view/rename-tree-view-simple-tree-view/rename-tree-view-simple-tree-view.ts rename to packages/x-codemod/src/v7.0.0/tree-view/rename-tree-view-simple-tree-view/rename-tree-view-simple-tree-view.test.ts diff --git a/packages/x-codemod/src/v7.0.0/tree-view/rename-use-tree-item/index.ts b/packages/x-codemod/src/v7.0.0/tree-view/rename-use-tree-item/index.ts index 8dac34f0a523..d3bee203ed83 100644 --- a/packages/x-codemod/src/v7.0.0/tree-view/rename-use-tree-item/index.ts +++ b/packages/x-codemod/src/v7.0.0/tree-view/rename-use-tree-item/index.ts @@ -1,10 +1,5 @@ -import { ASTPath, ImportDeclaration } from 'jscodeshift'; import type { JsCodeShiftAPI, JsCodeShiftFileInfo } from '../../../types'; - -const PACKAGE_REGEXP = /@mui\/x-tree-view(\/TreeItem|)/; - -const matchImport = (path: ASTPath) => - (path.node.source.value?.toString() ?? '').match(PACKAGE_REGEXP); +import { renameImports } from '../../../util/renameImports'; export default function transformer(file: JsCodeShiftFileInfo, api: JsCodeShiftAPI, options: any) { const j = api.jscodeshift; @@ -15,23 +10,19 @@ export default function transformer(file: JsCodeShiftFileInfo, api: JsCodeShiftA trailingComma: true, }; - const matchingImports = root.find(j.ImportDeclaration).filter((path) => !!matchImport(path)); - - // Rename the import specifiers - // - import { useTreeItem } from '@mui/x-tree-view/TreeItem' - // + import { useTreeItemState } from '@mui/x-tree-view/TreeItem' - matchingImports - .find(j.ImportSpecifier) - .filter((path) => path.node.imported.name === 'useTreeItem') - .replaceWith(j.importSpecifier(j.identifier('useTreeItemState'))); - - // Rename the import usage - // - useTreeItem(nodeId); - // + useTreeItemState(nodeId) - root - .find(j.Identifier) - .filter((path) => path.node.name === 'useTreeItem') - .replaceWith(j.identifier('useTreeItemState')); + renameImports({ + j, + root, + packageNames: ['@mui/x-tree-view'], + imports: [ + { + oldEndpoint: 'TreeItem', + importsMapping: { + useTreeItem: 'useTreeItemState', + }, + }, + ], + }); return root.toSource(printOptions); } diff --git a/packages/x-codemod/src/v7.0.0/tree-view/rename-use-tree-item/rename-use-tree-item.ts b/packages/x-codemod/src/v7.0.0/tree-view/rename-use-tree-item/rename-use-tree-item.test.ts similarity index 100% rename from packages/x-codemod/src/v7.0.0/tree-view/rename-use-tree-item/rename-use-tree-item.ts rename to packages/x-codemod/src/v7.0.0/tree-view/rename-use-tree-item/rename-use-tree-item.test.ts From ef0d5c70328fae64053af8051b46ea5862d55497 Mon Sep 17 00:00:00 2001 From: Bilal Shafi Date: Fri, 11 Oct 2024 18:27:42 +0500 Subject: [PATCH 04/44] [DataGrid] Fix `onRowSelectionModelChange` firing unnecessarily on initial render (#14909) --- .../src/tests/rowSelection.DataGridPro.test.tsx | 16 ++++++++++++++++ .../features/rowSelection/useGridRowSelection.ts | 10 ++++++++++ 2 files changed, 26 insertions(+) diff --git a/packages/x-data-grid-pro/src/tests/rowSelection.DataGridPro.test.tsx b/packages/x-data-grid-pro/src/tests/rowSelection.DataGridPro.test.tsx index 23994a6684d0..fdf3990d7767 100644 --- a/packages/x-data-grid-pro/src/tests/rowSelection.DataGridPro.test.tsx +++ b/packages/x-data-grid-pro/src/tests/rowSelection.DataGridPro.test.tsx @@ -201,6 +201,22 @@ describe(' - Row selection', () => { expect(getCell(1, 0).querySelector('input')!).to.have.attr('data-indeterminate', 'true'); }); + // Context: https://github.com/mui/mui-x/issues/14859 + it('should not throw when controlling a selection model', () => { + function TestDataGrid() { + const [rowSelectionModel, setRowSelectionModel] = React.useState([]); + return ( + + ); + } + expect(() => { + render(); + }).not.to.throw(); + }); + describe('prop: checkboxSelectionVisibleOnly = false', () => { it('should select all rows of all pages if no row is selected', () => { render( diff --git a/packages/x-data-grid/src/hooks/features/rowSelection/useGridRowSelection.ts b/packages/x-data-grid/src/hooks/features/rowSelection/useGridRowSelection.ts index d50883fa5c36..9d5b7f8e0aa3 100644 --- a/packages/x-data-grid/src/hooks/features/rowSelection/useGridRowSelection.ts +++ b/packages/x-data-grid/src/hooks/features/rowSelection/useGridRowSelection.ts @@ -442,8 +442,12 @@ export const useGridRowSelection = ( /* * EVENTS */ + const isFirstRender = React.useRef(true); const removeOutdatedSelection = React.useCallback( (sortModelUpdated = false) => { + if (isFirstRender.current) { + return; + } const currentSelection = gridRowSelectionStateSelector(apiRef.current.state); const filteredRowsLookup = gridFilteredRowsLookupSelector(apiRef); @@ -757,4 +761,10 @@ export const useGridRowSelection = ( React.useEffect(() => { runIfRowSelectionIsEnabled(removeOutdatedSelection); }, [removeOutdatedSelection, runIfRowSelectionIsEnabled]); + + React.useEffect(() => { + if (isFirstRender.current) { + isFirstRender.current = false; + } + }, []); }; From ca0a8478741e70e5ab86e8a3bc69c989cacdf6d4 Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Fri, 11 Oct 2024 16:42:26 +0200 Subject: [PATCH 05/44] [docs] Update v5 migration codesandbox --- docs/data/migration/migration-data-grid-v4/CoreV5WithCoreV4.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/data/migration/migration-data-grid-v4/CoreV5WithCoreV4.js b/docs/data/migration/migration-data-grid-v4/CoreV5WithCoreV4.js index 9c28eb6a9c90..8eefa3c26dd4 100644 --- a/docs/data/migration/migration-data-grid-v4/CoreV5WithCoreV4.js +++ b/docs/data/migration/migration-data-grid-v4/CoreV5WithCoreV4.js @@ -4,7 +4,7 @@ export default function CoreV5WithCoreV4() { return (