diff --git a/.circleci/config.yml b/.circleci/config.yml index 1307411dff8739..3f573f62f82b06 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,6 +1,6 @@ version: 2.1 orbs: - aws-cli: circleci/aws-cli@4.2.3 + aws-cli: circleci/aws-cli@5.1.0 aws-s3: circleci/aws-s3@4.0 parameters: @@ -42,6 +42,7 @@ default-job: &default-job TEST_GATE: << parameters.test-gate >> AWS_REGION_ARTIFACTS: eu-central-1 COREPACK_ENABLE_DOWNLOAD_PROMPT: '0' + DANGER_DISABLE_TRANSPILATION: 'true' working_directory: /tmp/material-ui docker: - image: cimg/node:20.17 diff --git a/.codesandbox/ci.json b/.codesandbox/ci.json index 437b8c3d5eb5ef..2881171258056b 100644 --- a/.codesandbox/ci.json +++ b/.codesandbox/ci.json @@ -4,7 +4,6 @@ "node": "20", "packages": [ "packages/markdown", - "packages/mui-babel-macros", "packages/mui-base", "packages/mui-codemod", "packages/mui-core-downloads-tracker", @@ -33,7 +32,6 @@ "@mui/docs": "packages/mui-docs/build", "@mui/icons-material": "packages/mui-icons-material/build", "@mui/internal-test-utils": "packages-internal/test-utils", - "@mui/internal-babel-macros": "packages/mui-babel-macros", "@mui/internal-docs-utils": "packages-internal/docs-utils", "@mui/internal-markdown": "packages/markdown", "@mui/internal-scripts": "packages-internal/scripts", diff --git a/.eslintignore b/.eslintignore index 8100e5ec04840b..ec34e0c6403844 100644 --- a/.eslintignore +++ b/.eslintignore @@ -16,7 +16,6 @@ /packages/mui-icons-material/material-icons/ /packages/mui-icons-material/src/*.js /packages/mui-icons-material/templateSvgIcon.js -/packages/mui-utils/macros/__fixtures__/ # Ignore fixtures /packages-internal/scripts/typescript-to-proptypes/test/*/* /test/bundling/fixtures/**/*.fixture.js diff --git a/.eslintrc.js b/.eslintrc.js index 038be57d26624c..e0f4b9c8d01018 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,3 +1,9 @@ +// @ts-check + +/** + * @typedef {import('eslint').Linter.Config} Config + */ + const path = require('path'); const OneLevelImportMessage = [ @@ -39,7 +45,7 @@ const NO_RESTRICTED_IMPORTS_PATTERNS_DEEPLY_NESTED = [ }, ]; -module.exports = { +module.exports = /** @type {Config} */ ({ root: true, // So parent files don't get applied env: { es6: true, @@ -228,6 +234,10 @@ module.exports = { "The 'use client' pragma can't be used with export * in the same module. This is not supported by Next.js.", selector: 'ExpressionStatement[expression.value="use client"] ~ ExportAllDeclaration', }, + { + message: 'Do not call `Error(...)` without `new`. Use `new Error(...)` instead.', + selector: "CallExpression[callee.name='Error']", + }, ], // We re-export default in many places, remove when https://github.com/airbnb/javascript/issues/2500 gets resolved @@ -526,4 +536,4 @@ module.exports = { }, }, ], -}; +}); diff --git a/.github/workflows/cherry-pick-next-to-master.yml b/.github/workflows/cherry-pick-next-to-master.yml index 0b1810db65240e..614c58330d3a32 100644 --- a/.github/workflows/cherry-pick-next-to-master.yml +++ b/.github/workflows/cherry-pick-next-to-master.yml @@ -18,7 +18,7 @@ jobs: if: ${{ contains(github.event.pull_request.labels.*.name, 'needs cherry-pick') && github.event.pull_request.merged == true }} steps: - name: Checkout - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: fetch-depth: 0 - name: Cherry pick and create the new PR diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ee1e1ddca88f5..9c4e71a38269c2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,14 +24,14 @@ jobs: os: [macos-latest, windows-latest, ubuntu-latest] steps: - run: echo "${{ github.actor }}" - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: # fetch all tags which are required for `pnpm release:changelog` fetch-depth: 0 - name: Set up pnpm uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 - name: Use Node.js 20.x - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 with: node-version: 20 cache: 'pnpm' # https://github.com/actions/setup-node/blob/main/docs/advanced-usage.md#caching-packages-dependencies diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 16b15ebac1a654..689d7a1dc54947 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -16,10 +16,10 @@ jobs: security-events: write steps: - name: Checkout repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@8214744c546c1e5c8f03dde8fab3a7353211988d # v3.26.7 + uses: github/codeql-action/init@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 with: languages: typescript config-file: ./.github/codeql/codeql-config.yml @@ -30,4 +30,4 @@ jobs: # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # queries: security-extended,security-and-quality - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@8214744c546c1e5c8f03dde8fab3a7353211988d # v3.26.7 + uses: github/codeql-action/analyze@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 diff --git a/.github/workflows/publish-canaries.yml b/.github/workflows/publish-canaries.yml index b524ad7c36c267..a9b055aea6bc45 100644 --- a/.github/workflows/publish-canaries.yml +++ b/.github/workflows/publish-canaries.yml @@ -9,13 +9,13 @@ jobs: publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: fetch-depth: 0 - name: Set up pnpm uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0 - name: Use Node.js 20.x - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4 with: node-version: 20 cache: 'pnpm' # https://github.com/actions/setup-node/blob/main/docs/advanced-usage.md#caching-packages-dependencies diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 74e518c671012b..f5375b988a167b 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -22,7 +22,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: persist-credentials: false @@ -43,6 +43,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: Upload to code-scanning - uses: github/codeql-action/upload-sarif@8214744c546c1e5c8f03dde8fab3a7353211988d # v3.26.7 + uses: github/codeql-action/upload-sarif@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 with: sarif_file: results.sarif diff --git a/.github/workflows/support-stackoverflow.yml b/.github/workflows/support-stackoverflow.yml index 5ba93685846e1b..934ffc8fc8757c 100644 --- a/.github/workflows/support-stackoverflow.yml +++ b/.github/workflows/support-stackoverflow.yml @@ -24,8 +24,7 @@ jobs: issue-comment: | 👋 Thanks for using this project! - We use GitHub issues exclusively as a bug and feature requests tracker, however, - this issue appears to be a support request. + We use GitHub issues exclusively as a bug and feature requests tracker, however, this issue appears to be a support request. For support with Material UI please check out https://mui.com/material-ui/getting-started/support/. Thanks! diff --git a/.github/workflows/vale-action.yml b/.github/workflows/vale-action.yml index b2bb951d043c2e..7787a70601cc33 100644 --- a/.github/workflows/vale-action.yml +++ b/.github/workflows/vale-action.yml @@ -12,7 +12,7 @@ jobs: contents: read pull-requests: write steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - uses: errata-ai/vale-action@38bf078c328061f59879b347ca344a718a736018 # v2.1.0 continue-on-error: true # GitHub Action flag needed until https://github.com/errata-ai/vale-action/issues/89 is fixed with: diff --git a/CHANGELOG.md b/CHANGELOG.md index d1be1e2c8e4c62..94b64c52446626 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,64 @@ # [Versions](https://mui.com/versions/) +## v6.1.2 + + + +_Oct 2, 2024_ + +A big thanks to the 13 contributors who made this release possible. + +### `@mui/material@6.1.2` + +- [Autocomplete] Fix listbox opens and closes on click when used with `limitTags` (#42494) @appleSimple +- [Button] Ignore `dark` and `contrastText` if not provided in the theme (#43861) @siriwatknp +- [Button] Fix regression for color `inherit` (#43862) @siriwatknp +- [LinearProgress] Fix background color (#43949) @sai6855 +- Support CSS variables with shadow DOM (#43948) @siriwatknp +- [Rating] Use Rating `name` as prefix of input element ids (#43829) @yash49 +- [Drawer] Fix issue with main window being used instead of iframe's window (#43818) @albarv340 +- [ThemeProvider] Support setting default mode (#43951) @siriwatknp + +### Docs + +- Update theme toggle demo (#43956) @Janpot +- Add note about minimum required webpack version (#43864) @Janpot +- Format Pigment CSS docs (#43812) @oliviertassinari +- Fix visual bug on dashboard template (#43836) @oliviertassinari +- Fix pigment-css.md syntax error (#43837) @kdichev +- Fix Sign-in template form experience (#43838) @oliviertassinari +- Remove "To be continued" section from v0 –> v1 migration guide (#43832) @samuelsycamore +- Fix 301 to chromium (#43809) @oliviertassinari +- [joy-ui] Add missing ComponentLinkHeader components (#43865) @samuelsycamore +- [Modal] Remove unnecessary type assertion (#43825) @ZeeshanTamboli +- [Table] Stabilize random series in virtualized table demo (#43744) @Janpot +- [system] Add migration guide link to `@mui/styles` pages (#43833) @samuelsycamore + +### Core + +- [code-infra] Fix flaky dashboard screenshot - take 2 (#43937) @Janpot +- [code-infra] Replace all instances of `e` with `event` and add eslint rule (#43866) @samuelsycamore +- [code-infra] Fix and update bundling fixtures (#43709) @Janpot +- [code-infra] Update transitive dependencies with vulnerabilties (#43895) @Janpot +- [code-infra] Optimize regression tests (#43889) @Janpot +- [code-infra] Remove custom playwright installation steps (#43881) @Janpot +- [code-infra] Fix flaky dashboard screenshot (#43890) @Janpot +- [code-infra] Add new instanceof proptypes for toolpad (#43814) @Janpot +- Fix eslint-plugin-react-compiler issues in usePagination tests (#43946) @wilhelmlofsten +- Uniformity in version range @oliviertassinari +- Replace `toBeAriaHidden` matcher with `toBeInaccessible` in tests (#43870) @ZeeshanTamboli +- [docs-infra] Strengthen CSP (#43711) @oliviertassinari +- [docs-infra] Open Codesandbox demo with fontsize=12 (#43860) @siriwatknp +- [icons] Reduce Material Icon page size (#43911) @oliviertassinari +- [test] Point Istanbul to correct URL (#43935) @sai6855 +- [test] Sync React.version parse logic with codebase (#43820) @oliviertassinari +- Improve getReactElementRef() utils (#43022) @sai6855 +- [Drawer] Refactor getScrollbarSize usages (#43828) @BrianWoolfolk +- [Modal] Replace `show` parameter name with `hide` in modal manager (#43868) @ZeeshanTamboli +- [Modal] Remove unnecessary `manager` prop handling (#43867) @ZeeshanTamboli + +All contributors of this release in alphabetical order: @albarv340, @appleSimple, @BrianWoolfolk, @DanailH, @Janpot, @kdichev, @oliviertassinari, @sai6855, @samuelsycamore, @siriwatknp, @wilhelmlofsten, @yash49, @ZeeshanTamboli + ## v6.1.1 diff --git a/README.md b/README.md index f8f532c74b6553..f15e6e91bd3ce3 100644 --- a/README.md +++ b/README.md @@ -58,9 +58,9 @@ View the [Joy UI documentation](https://mui.com/joy-ui/getting-started/). ### Diamond 💎

- octopus - doit -marblism + octopus + doit + marblism

Diamond sponsors are those who have pledged \$1,500/month or more to MUI. @@ -71,20 +71,32 @@ via [Open Collective](https://opencollective.com/mui-org) or via [Patreon](http

tidelift.com - Spotify - Icons8 +   + Spotify +   + Icons8 +   RxDB - text-em-all.com - megafamous.com - dialmycalls.com - goread.io - copycopter.ai - Route4Me +   + text-em-all.com +   + megafamous.com +   + dialmycalls.com +   + goread.io +   + copycopter.ai +   + Route4Me +  

- buzzoid.comBuzzoid - twicsy.comTwicsy + buzzoid.comBuzzoid +   + twicsy.comTwicsy +  

Gold sponsors are those who have pledged \$500/month or more to MUI. diff --git a/apps/pigment-css-next-app/package.json b/apps/pigment-css-next-app/package.json index 89f9be47870607..2bc5ecedc194e8 100644 --- a/apps/pigment-css-next-app/package.json +++ b/apps/pigment-css-next-app/package.json @@ -23,9 +23,9 @@ "react-dom": "^18.3.1" }, "devDependencies": { - "@pigment-css/nextjs-plugin": "0.0.23", - "@types/node": "^20.16.5", - "@types/react": "^18.3.4", + "@pigment-css/nextjs-plugin": "0.0.24", + "@types/node": "^20.16.10", + "@types/react": "^18.3.11", "@types/react-dom": "^18.3.0", "eslint": "^8.57.1", "typescript": "^5.6.2" diff --git a/apps/pigment-css-vite-app/README.md b/apps/pigment-css-vite-app/README.md index a34e37f0611e53..aceccd0b184ba3 100644 --- a/apps/pigment-css-vite-app/README.md +++ b/apps/pigment-css-vite-app/README.md @@ -1,6 +1,6 @@ # Pigment CSS with Vite - Demo app -This is a Pigment CSS with [Vite](https://vitejs.dev/) project. +This is a Pigment CSS with [Vite](https://vite.dev/) project. This project is not part of the workspace yet. ## How to run diff --git a/apps/pigment-css-vite-app/package.json b/apps/pigment-css-vite-app/package.json index 77b2e844a535ea..73eb4f6d9a84bb 100644 --- a/apps/pigment-css-vite-app/package.json +++ b/apps/pigment-css-vite-app/package.json @@ -27,11 +27,11 @@ "devDependencies": { "@babel/preset-react": "^7.24.7", "@babel/preset-typescript": "^7.24.7", - "@pigment-css/vite-plugin": "0.0.23", - "@types/react": "^18.3.4", + "@pigment-css/vite-plugin": "0.0.24", + "@types/react": "^18.3.11", "@types/react-dom": "^18.3.0", "@types/webfontloader": "^1.6.38", - "@vitejs/plugin-react": "^4.3.1", + "@vitejs/plugin-react": "^4.3.2", "postcss": "^8.4.47", "postcss-combine-media-query": "^1.0.1", "vite": "5.4.8", diff --git a/apps/pigment-css-vite-app/src/pages/material-ui/react-progress.tsx b/apps/pigment-css-vite-app/src/pages/material-ui/react-progress.tsx index b1ecf1ebee01ba..badcc6a712d49c 100644 --- a/apps/pigment-css-vite-app/src/pages/material-ui/react-progress.tsx +++ b/apps/pigment-css-vite-app/src/pages/material-ui/react-progress.tsx @@ -8,7 +8,6 @@ import CircularUnderLoad from '../../../../../docs/data/material/components/prog import CircularWithValueLabel from '../../../../../docs/data/material/components/progress/CircularWithValueLabel.tsx'; import CustomizedProgressBars from '../../../../../docs/data/material/components/progress/CustomizedProgressBars.tsx'; import DelayingAppearance from '../../../../../docs/data/material/components/progress/DelayingAppearance.tsx'; -import LinearBuffer from '../../../../../docs/data/material/components/progress/LinearBuffer.tsx'; import LinearColor from '../../../../../docs/data/material/components/progress/LinearColor.tsx'; import LinearDeterminate from '../../../../../docs/data/material/components/progress/LinearDeterminate.tsx'; import LinearIndeterminate from '../../../../../docs/data/material/components/progress/LinearIndeterminate.tsx'; @@ -66,12 +65,6 @@ export default function Progress() { -
-

Linear Buffer

-
- -
-

Linear Color

diff --git a/babel.config.js b/babel.config.js index cbadb0c6a75a97..de9a6cdea126ae 100644 --- a/babel.config.js +++ b/babel.config.js @@ -78,15 +78,6 @@ module.exports = function getBabelConfig(api) { /** @type {babel.PluginItem[]} */ const plugins = [ - [ - 'babel-plugin-macros', - { - muiError: { - errorCodesPath, - missingError, - }, - }, - ], 'babel-plugin-optimize-clsx', [ '@babel/plugin-transform-runtime', @@ -114,6 +105,13 @@ module.exports = function getBabelConfig(api) { ], }, ], + [ + '@mui/internal-babel-plugin-minify-errors', + { + missingError, + errorCodesPath, + }, + ], ...(useESModules ? [ [ diff --git a/benchmark/package.json b/benchmark/package.json index eecc158fb2069d..5aa67a2f3f0eb8 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -31,7 +31,7 @@ "react-dom": "^18.3.1", "react-is": "^18.3.1", "react-jss": "^10.10.0", - "react-redux": "^8.1.3", + "react-redux": "^9.1.2", "redux": "^4.2.1", "serve-handler": "^6.1.5", "styled-components": "^6.1.13", diff --git a/dangerfile.ts b/dangerFileContent.ts similarity index 98% rename from dangerfile.ts rename to dangerFileContent.ts index 3b17317e6ba195..ee34fafdc9d622 100644 --- a/dangerfile.ts +++ b/dangerFileContent.ts @@ -1,9 +1,11 @@ -// danger has to be the first thing required! -import { danger, markdown } from 'danger'; import { exec } from 'child_process'; +import type * as dangerModule from 'danger'; import { loadComparison } from './scripts/sizeSnapshot'; import replaceUrl from './packages/api-docs-builder/utils/replaceUrl'; +declare const danger: (typeof dangerModule)['danger']; +declare const markdown: (typeof dangerModule)['markdown']; + const circleCIBuildNumber = process.env.CIRCLE_BUILD_NUM; const circleCIBuildUrl = `https://app.circleci.com/pipelines/github/mui/material-ui/jobs/${circleCIBuildNumber}`; const dangerCommand = process.env.DANGER_COMMAND; diff --git a/dangerfile.js b/dangerfile.js new file mode 100644 index 00000000000000..5862e174a8ab7a --- /dev/null +++ b/dangerfile.js @@ -0,0 +1,7 @@ +// danger uses babelify under the hood. The implementation is buggy and fails on our +// custom plugins in our codebase. We'll just disable it and do our own compilation. +// Danger must always be run with envirnonent variable `DANGER_DISABLE_TRANSPILATION="true"`. +require('@babel/register')({ + extensions: ['.js', '.ts', '.tsx'], +}); +require('./dangerFileContent'); diff --git a/docs/data/base/components/modal/UseModal.js b/docs/data/base/components/modal/UseModal.js index b35a6642a33844..02be482df395da 100644 --- a/docs/data/base/components/modal/UseModal.js +++ b/docs/data/base/components/modal/UseModal.js @@ -118,12 +118,6 @@ const Modal = React.forwardRef(function Modal(props, forwardedRef) { return ( - {/* - * Marking an element with the role presentation indicates to assistive technology - * that this element should be ignored; it exists to support the web application and - * is not meant for humans to interact with directly. - * https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/no-static-element-interactions.md - */} {!hideBackdrop ? : null} - {/* - * Marking an element with the role presentation indicates to assistive technology - * that this element should be ignored; it exists to support the web application and - * is not meant for humans to interact with directly. - * https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/no-static-element-interactions.md - */} {!hideBackdrop ? : null} Window; - children?: React.ReactElement; + children?: React.ReactElement; } function ScrollTop(props: Props) { diff --git a/docs/data/material/components/app-bar/ElevateAppBar.tsx b/docs/data/material/components/app-bar/ElevateAppBar.tsx index df548678f153fd..41f9f304c89c37 100644 --- a/docs/data/material/components/app-bar/ElevateAppBar.tsx +++ b/docs/data/material/components/app-bar/ElevateAppBar.tsx @@ -13,7 +13,7 @@ interface Props { * You won't need it on your project. */ window?: () => Window; - children?: React.ReactElement; + children?: React.ReactElement<{ elevation?: number }>; } function ElevationScroll(props: Props) { diff --git a/docs/data/material/components/app-bar/HideAppBar.tsx b/docs/data/material/components/app-bar/HideAppBar.tsx index 925410ed692eb9..9c4edd8a1af106 100644 --- a/docs/data/material/components/app-bar/HideAppBar.tsx +++ b/docs/data/material/components/app-bar/HideAppBar.tsx @@ -14,7 +14,7 @@ interface Props { * You won't need it on your project. */ window?: () => Window; - children?: React.ReactElement; + children?: React.ReactElement; } function HideOnScroll(props: Props) { diff --git a/docs/data/material/components/autocomplete/Virtualize.tsx b/docs/data/material/components/autocomplete/Virtualize.tsx index cfa2ccd1a91e93..11440cf0be8ec3 100644 --- a/docs/data/material/components/autocomplete/Virtualize.tsx +++ b/docs/data/material/components/autocomplete/Virtualize.tsx @@ -58,9 +58,13 @@ const ListboxComponent = React.forwardRef< React.HTMLAttributes >(function ListboxComponent(props, ref) { const { children, ...other } = props; - const itemData: React.ReactElement[] = []; - (children as React.ReactElement[]).forEach( - (item: React.ReactElement & { children?: React.ReactElement[] }) => { + const itemData: React.ReactElement[] = []; + (children as React.ReactElement[]).forEach( + ( + item: React.ReactElement & { + children?: React.ReactElement[]; + }, + ) => { itemData.push(item); itemData.push(...(item.children || [])); }, @@ -73,7 +77,7 @@ const ListboxComponent = React.forwardRef< const itemCount = itemData.length; const itemSize = smUp ? 36 : 48; - const getChildSize = (child: React.ReactElement) => { + const getChildSize = (child: React.ReactElement) => { if (child.hasOwnProperty('group')) { return 48; } diff --git a/docs/data/material/components/checkboxes/checkboxes.md b/docs/data/material/components/checkboxes/checkboxes.md index d0b1839573805b..770a0b95287c8e 100644 --- a/docs/data/material/components/checkboxes/checkboxes.md +++ b/docs/data/material/components/checkboxes/checkboxes.md @@ -56,6 +56,8 @@ A checkbox input can only have two states in a form: checked or unchecked. It either submits its value or doesn't. Visually, there are **three** states a checkbox can be in: checked, unchecked, or indeterminate. +You can change the indeterminate icon using the `indeterminateIcon` prop. + {{"demo": "IndeterminateCheckbox.js"}} :::warning diff --git a/docs/data/material/components/dialogs/FullScreenDialog.tsx b/docs/data/material/components/dialogs/FullScreenDialog.tsx index 5b0dc07b9cd042..2215e96f891b12 100644 --- a/docs/data/material/components/dialogs/FullScreenDialog.tsx +++ b/docs/data/material/components/dialogs/FullScreenDialog.tsx @@ -15,7 +15,7 @@ import { TransitionProps } from '@mui/material/transitions'; const Transition = React.forwardRef(function Transition( props: TransitionProps & { - children: React.ReactElement; + children: React.ReactElement; }, ref: React.Ref, ) { diff --git a/docs/data/material/components/grid2/grid2.md b/docs/data/material/components/grid2/grid2.md index 0dfd5b6d8e1222..983caa32c1398a 100644 --- a/docs/data/material/components/grid2/grid2.md +++ b/docs/data/material/components/grid2/grid2.md @@ -25,7 +25,7 @@ The grid system is implemented with the `Grid` component: - Item widths are set in percentages, so they're always fluid and sized relative to their parent element. - There are five default grid breakpoints: xs, sm, md, lg, and xl. If you need custom breakpoints, check out [custom breakpoints grid](#custom-breakpoints). - You can give integer values for each breakpoint, to indicate how many of the 12 available columns are occupied by the component when the viewport width satisfies the [breakpoint constraints](/material-ui/customization/breakpoints/#default-breakpoints). -- It uses negative margins and padding to create gaps between children, which behave similarly to [the `gap` CSS property](https://developer.mozilla.org/en-US/docs/Web/CSS/gap). +- It uses [the `gap` CSS property](https://developer.mozilla.org/en-US/docs/Web/CSS/gap) to add spacing between items. - It does _not_ support row spanning. Children elements cannot span multiple rows. We recommend using [CSS Grid](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout) if you need this functionality. - It does _not_ automatically place children. It will try to fit the children one by one, and if there is not enough space, the rest of the children will start on the next line, and so on. If you need auto-placement, we recommend using [CSS Grid](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout/Auto-placement_in_grid_layout) instead. diff --git a/docs/data/material/components/lists/InteractiveList.tsx b/docs/data/material/components/lists/InteractiveList.tsx index 661995d60917d4..e667a977cda23a 100644 --- a/docs/data/material/components/lists/InteractiveList.tsx +++ b/docs/data/material/components/lists/InteractiveList.tsx @@ -16,7 +16,7 @@ import Typography from '@mui/material/Typography'; import FolderIcon from '@mui/icons-material/Folder'; import DeleteIcon from '@mui/icons-material/Delete'; -function generate(element: React.ReactElement) { +function generate(element: React.ReactElement) { return [0, 1, 2].map((value) => React.cloneElement(element, { key: value, diff --git a/docs/data/material/components/material-icons/SearchIcons.js b/docs/data/material/components/material-icons/SearchIcons.js index f9322220054ef5..4e252ebcca769c 100644 --- a/docs/data/material/components/material-icons/SearchIcons.js +++ b/docs/data/material/components/material-icons/SearchIcons.js @@ -25,6 +25,7 @@ import * as mui from '@mui/icons-material'; import { Link } from '@mui/docs/Link'; import { useTranslate } from '@mui/docs/i18n'; import useQueryParameterState from 'docs/src/modules/utils/useQueryParameterState'; + // For Debugging // import Menu from '@mui/icons-material/Menu'; // import MenuOutlined from '@mui/icons-material/MenuOutlined'; @@ -95,6 +96,8 @@ function selectNode(node) { const iconWidth = 35; +const SVG_ICON_CLASS = 'svg-icon'; + const StyledIcon = styled('span')(({ theme }) => ({ display: 'inline-flex', flexDirection: 'column', @@ -108,23 +111,24 @@ const StyledIcon = styled('span')(({ theme }) => ({ textAlign: 'center', width: `calc(${iconWidth}px + ${theme.spacing(2)} * 2 + 2px)`, }, -})); - -const StyledSvgIcon = styled(SvgIcon)(({ theme }) => ({ - boxSizing: 'content-box', - cursor: 'pointer', - color: theme.palette.text.primary, - border: '1px solid transparent', - fontSize: iconWidth, - borderRadius: '12px', - transition: theme.transitions.create(['background-color', 'box-shadow'], { - duration: theme.transitions.duration.shortest, - }), - padding: theme.spacing(2), - margin: theme.spacing(0.5, 0), - '&:hover': { - backgroundColor: theme.palette.background.default, - borderColor: theme.palette.primary.light, + [`& .${SVG_ICON_CLASS}`]: { + width: iconWidth, + height: iconWidth, + boxSizing: 'content-box', + cursor: 'pointer', + color: theme.palette.text.primary, + border: '1px solid transparent', + fontSize: iconWidth, + borderRadius: '12px', + transition: theme.transitions.create(['background-color', 'box-shadow'], { + duration: theme.transitions.duration.shortest, + }), + padding: theme.spacing(2), + margin: theme.spacing(0.5, 0), + '&:hover': { + backgroundColor: theme.palette.background.default, + borderColor: theme.palette.primary.light, + }, }, })); @@ -143,20 +147,58 @@ function handleLabelClick(event) { selectNode(event.currentTarget); } +function isElmVisible(elm, margin = 0) { + const rect = elm.getBoundingClientRect(); + return rect.bottom >= -margin && rect.top <= window.innerHeight + margin; +} + function Icon(props) { - const { icon, onOpenClick } = props; + const { icon, onOpenClick, initiallyVisible = false } = props; + + const rootRef = React.useRef(null); + const [isVisible, setIsVisible] = React.useState(initiallyVisible); + + // Virtualize the icons to reduce page size and React rendering time. + // Only render the icons after they become visible in the viewport. + React.useEffect(() => { + const margin = 200; + const root = /** @type {SVGElement} */ (rootRef.current); + if (initiallyVisible || isElmVisible(root, margin)) { + setIsVisible(true); + return () => {}; + } + const observer = new IntersectionObserver( + (entries) => { + if (isElmVisible(entries[0].target, margin)) { + setIsVisible(true); + } + }, + { rootMargin: `${margin}px 0px` }, + ); + observer.observe(root); + return () => { + observer.disconnect(); + }; + }, [initiallyVisible]); + /* eslint-disable jsx-a11y/click-events-have-key-events */ return ( - + {isVisible ? ( + + ) : ( +
+ )} {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions -- TODO: a11y */}
{icon.importName}
{/* eslint-enable jsx-a11y/click-events-have-key-events */} @@ -169,8 +211,14 @@ const Icons = React.memo(function Icons(props) { return (
- {icons.map((icon) => ( - + {icons.map((icon, i) => ( + ))}
); @@ -474,25 +522,21 @@ const allIconsMap = {}; const allIcons = Object.keys(mui) .sort() .map((importName) => { - let theme; - if (importName.includes('Outlined')) { - theme = 'Outlined'; - } else if (importName.includes('TwoTone')) { - theme = 'Two tone'; - } else if (importName.includes('Rounded')) { - theme = 'Rounded'; - } else if (importName.includes('Sharp')) { - theme = 'Sharp'; - } else { - theme = 'Filled'; + let theme = 'Filled'; + let name = importName; + + for (const currentTheme of ['Outlined', 'Rounded', 'TwoTone', 'Sharp']) { + if (importName.endsWith(currentTheme)) { + theme = currentTheme === 'TwoTone' ? 'Two tone' : currentTheme; + name = importName.slice(0, -currentTheme.length); + break; + } } - - const name = importName.replace(/(Outlined|TwoTone|Rounded|Sharp)$/, ''); let searchable = name; if (synonyms[searchable]) { searchable += ` ${synonyms[searchable]}`; } - searchIndex.addAsync(importName, searchable); + searchIndex.add(importName, searchable); const icon = { importName, diff --git a/docs/data/material/components/material-icons/material-icons.md b/docs/data/material/components/material-icons/material-icons.md index 86c3b6f8c1c8c2..bfb72aa1158a3e 100644 --- a/docs/data/material/components/material-icons/material-icons.md +++ b/docs/data/material/components/material-icons/material-icons.md @@ -39,7 +39,7 @@ yarn add @mui/icons-material @mui/material @emotion/styled @emotion/react See the [Installation](/material-ui/getting-started/installation/) page for additional docs about how to make sure everything is set up correctly. :::info -Google also offers [Material Symbols](https://fonts.google.com/icons?icon.set=Material+Symbols) as an alternative to Material Icons. `@mui/icons-material` only covers Icons, and there are no plans to support Symbols at this time. +Google also offers [Material Symbols](https://fonts.google.com/icons?icon.set=Material+Symbols) as the successor of Material Icons. `@mui/icons-material` only covers Icons at this time, there are no support for Symbols yet. :::
diff --git a/docs/data/material/components/popper/SpringPopper.tsx b/docs/data/material/components/popper/SpringPopper.tsx index 5526317503ff16..636bedcfc229c1 100644 --- a/docs/data/material/components/popper/SpringPopper.tsx +++ b/docs/data/material/components/popper/SpringPopper.tsx @@ -4,7 +4,7 @@ import Popper from '@mui/material/Popper'; import { useSpring, animated } from '@react-spring/web'; interface FadeProps { - children?: React.ReactElement; + children?: React.ReactElement; in?: boolean; onEnter?: () => void; onExited?: () => void; diff --git a/docs/data/material/components/rating/RadioGroupRating.tsx b/docs/data/material/components/rating/RadioGroupRating.tsx index f42709d21c1359..89a509e2d0effe 100644 --- a/docs/data/material/components/rating/RadioGroupRating.tsx +++ b/docs/data/material/components/rating/RadioGroupRating.tsx @@ -15,7 +15,7 @@ const StyledRating = styled(Rating)(({ theme }) => ({ const customIcons: { [index: string]: { - icon: React.ReactElement; + icon: React.ReactElement; label: string; }; } = { diff --git a/docs/data/material/components/steppers/CustomizedSteppers.tsx b/docs/data/material/components/steppers/CustomizedSteppers.tsx index 64d482102e4d60..3c21df5ca89135 100644 --- a/docs/data/material/components/steppers/CustomizedSteppers.tsx +++ b/docs/data/material/components/steppers/CustomizedSteppers.tsx @@ -146,7 +146,7 @@ const ColorlibStepIconRoot = styled('div')<{ function ColorlibStepIcon(props: StepIconProps) { const { active, completed, className } = props; - const icons: { [index: string]: React.ReactElement } = { + const icons: { [index: string]: React.ReactElement } = { 1: , 2: , 3: , diff --git a/docs/data/material/components/toggle-button/StandaloneToggleButton.js b/docs/data/material/components/toggle-button/StandaloneToggleButton.js index 274f8aacaa7fb1..ab6f99ffd379ef 100644 --- a/docs/data/material/components/toggle-button/StandaloneToggleButton.js +++ b/docs/data/material/components/toggle-button/StandaloneToggleButton.js @@ -9,9 +9,7 @@ export default function StandaloneToggleButton() { { - setSelected(!selected); - }} + onChange={() => setSelected((prevSelected) => !prevSelected)} > diff --git a/docs/data/material/components/toggle-button/StandaloneToggleButton.tsx b/docs/data/material/components/toggle-button/StandaloneToggleButton.tsx index 274f8aacaa7fb1..ab6f99ffd379ef 100644 --- a/docs/data/material/components/toggle-button/StandaloneToggleButton.tsx +++ b/docs/data/material/components/toggle-button/StandaloneToggleButton.tsx @@ -9,9 +9,7 @@ export default function StandaloneToggleButton() { { - setSelected(!selected); - }} + onChange={() => setSelected((prevSelected) => !prevSelected)} > diff --git a/docs/data/material/components/toggle-button/StandaloneToggleButton.tsx.preview b/docs/data/material/components/toggle-button/StandaloneToggleButton.tsx.preview index 05eb67193ca26d..b59402da9c7a3d 100644 --- a/docs/data/material/components/toggle-button/StandaloneToggleButton.tsx.preview +++ b/docs/data/material/components/toggle-button/StandaloneToggleButton.tsx.preview @@ -1,9 +1,7 @@ { - setSelected(!selected); - }} + onChange={() => setSelected((prevSelected) => !prevSelected)} > \ No newline at end of file diff --git a/docs/data/material/customization/palette/palette.md b/docs/data/material/customization/palette/palette.md index 36da0166b90e14..0f70f3c7134d00 100644 --- a/docs/data/material/customization/palette/palette.md +++ b/docs/data/material/customization/palette/palette.md @@ -303,7 +303,7 @@ const theme = createTheme({ :::warning The `contrastThreshold` parameter can produce counterproductive results.\ -Please verify that the [APCA](https://contrast.tools/?tab=apca) color contrast is improved (WCAG 3 [will use](https://typefully.com/DanHollick/sle13GMW2Brp) this new algorithm). +Please verify that the [APCA](https://contrast.tools/?tab=apca) color contrast is improved (WCAG 3 [will use](https://typefully.com/DanHollick/wcag-3-and-apca-sle13GMW2Brp) this new algorithm). ::: ## Picking colors diff --git a/docs/data/material/discover-more/backers/backers.md b/docs/data/material/discover-more/backers/backers.md index b86de6bd821196..a674feb932bed9 100644 --- a/docs/data/material/discover-more/backers/backers.md +++ b/docs/data/material/discover-more/backers/backers.md @@ -8,8 +8,8 @@ Sponsorship increases the rate of bug fixes, documentation improvements, and fea ## Diamond sponsors

- octopus - doit + octopus + doit marblism

@@ -21,15 +21,15 @@ via [Open Collective](https://opencollective.com/mui-org) or via [the for-profi

tidelift.com - Spotify - Icons8 + Spotify + Icons8 RxDB - text-em-all.com - megafamous.com - dialmycalls.com - goread.io - copycopter.ai - route4me.com + text-em-all.com + megafamous.com + dialmycalls.com + goread.io + copycopter.ai + route4me.com

- buzzoid.comBuzzoid - twicsy.comTwicsy + buzzoid.comBuzzoid + twicsy.comTwicsy

Gold sponsors are those who've pledged \$500/month or more to the MUI organization. [Tier benefits](#gold). diff --git a/docs/data/material/integrations/routing/ListRouter.tsx b/docs/data/material/integrations/routing/ListRouter.tsx index 103e54c278419c..669d548181ffca 100644 --- a/docs/data/material/integrations/routing/ListRouter.tsx +++ b/docs/data/material/integrations/routing/ListRouter.tsx @@ -27,7 +27,7 @@ function Router(props: { children?: React.ReactNode }) { } interface ListItemLinkProps { - icon?: React.ReactElement; + icon?: React.ReactElement; primary: string; to: string; } diff --git a/docs/data/material/migration/migrating-from-deprecated-apis/migrating-from-deprecated-apis.md b/docs/data/material/migration/migrating-from-deprecated-apis/migrating-from-deprecated-apis.md index 94e8102d1b7c1c..ee033608634f79 100644 --- a/docs/data/material/migration/migrating-from-deprecated-apis/migrating-from-deprecated-apis.md +++ b/docs/data/material/migration/migrating-from-deprecated-apis/migrating-from-deprecated-apis.md @@ -1539,13 +1539,13 @@ All of the TextField's slot props (`*Props`) props were deprecated in favor of e - inputProps={CustomHtmlInputProps} - SelectProps={CustomSelectProps} - InputLabelProps={CustomInputLabelProps} -- FormHelperTextProps={CustomFormHelperProps} +- FormHelperTextProps={CustomFormHelperTextProps} + slotProps={{ + input: CustomInputProps + htmlInput: CustomHtmlInputProps + select: CustomSelectProps + inputLabel: CustomInputLabelProps -+ formHelper: CustomFormHelperProps ++ formHelperText: CustomFormHelperTextProps + }} /> ``` diff --git a/docs/data/material/migration/upgrade-to-v6/migrating-to-pigment-css.md b/docs/data/material/migration/upgrade-to-v6/migrating-to-pigment-css.md index e930a44fc56b89..8138a8117e1354 100644 --- a/docs/data/material/migration/upgrade-to-v6/migrating-to-pigment-css.md +++ b/docs/data/material/migration/upgrade-to-v6/migrating-to-pigment-css.md @@ -22,7 +22,7 @@ It can work alongside Emotion to ease the migration process, but it is recommend Pigment CSS can be used with one of the following frameworks: - [Next.js App Router](https://nextjs.org/docs/app) with Webpack v5 (Turbopack is not supported yet) -- [Vite](https://vitejs.dev/) +- [Vite](https://vite.dev/) ## Installation @@ -191,7 +191,10 @@ Add the following code to your [Next.js](#nextjs) or [Vite](#vite) config file: const pigmentConfig = { transformLibraries: ['@mui/material'], -+ theme: createTheme(/* parameters if any */), ++ theme: createTheme({ ++ cssVariables: true, ++ /* other parameters, if any */ ++ }), }; ``` diff --git a/docs/next.config.mjs b/docs/next.config.mjs index 78b8bbe89ce1f0..aaf5919d1c1f26 100644 --- a/docs/next.config.mjs +++ b/docs/next.config.mjs @@ -56,7 +56,8 @@ export default withDocsInfra({ // We only care about Node runtime at this point. (options.nextRuntime === undefined || options.nextRuntime === 'nodejs') ) { - const [nextExternals, ...externals] = config.externals; + const externals = config.externals.slice(0, -1); + const nextExternals = config.externals.at(-1); config.externals = [ // @ts-ignore diff --git a/docs/package.json b/docs/package.json index 29b849428de49f..1b72d6a31532ba 100644 --- a/docs/package.json +++ b/docs/package.json @@ -43,15 +43,15 @@ "@mui/system": "workspace:^", "@mui/types": "workspace:^", "@mui/utils": "workspace:^", - "@mui/x-charts": "7.18.0", - "@mui/x-data-grid": "7.18.0", - "@mui/x-data-grid-generator": "7.18.0", - "@mui/x-data-grid-premium": "7.18.0", - "@mui/x-data-grid-pro": "7.18.0", - "@mui/x-date-pickers": "7.18.0", - "@mui/x-date-pickers-pro": "7.18.0", + "@mui/x-charts": "7.19.0", + "@mui/x-data-grid": "7.19.0", + "@mui/x-data-grid-generator": "7.19.0", + "@mui/x-data-grid-premium": "7.19.0", + "@mui/x-data-grid-pro": "7.19.0", + "@mui/x-date-pickers": "7.19.0", + "@mui/x-date-pickers-pro": "7.19.0", "@mui/x-license": "7.18.0", - "@mui/x-tree-view": "7.18.0", + "@mui/x-tree-view": "7.19.0", "@popperjs/core": "^2.11.8", "@react-spring/web": "^9.7.4", "@toolpad/core": "^0.7.0", @@ -82,7 +82,7 @@ "lz-string": "^1.5.0", "markdown-to-jsx": "^7.5.0", "material-ui-popup-state": "^5.3.1", - "next": "^14.2.13", + "next": "^14.2.14", "notistack": "3.0.1", "nprogress": "^0.2.0", "postcss": "^8.4.47", @@ -124,9 +124,9 @@ "@types/css-mediaquery": "^0.1.4", "@types/gtag.js": "^0.0.20", "@types/json2mq": "^0.2.2", - "@types/node": "^20.16.5", + "@types/node": "^20.16.10", "@types/prop-types": "^15.7.13", - "@types/react": "^18.3.4", + "@types/react": "^18.3.11", "@types/react-dom": "^18.3.0", "@types/react-swipeable-views": "^0.13.5", "@types/react-transition-group": "^4.4.11", @@ -138,7 +138,7 @@ "marked": "^13.0.3", "playwright": "^1.47.2", "prettier": "^3.3.3", - "tailwindcss": "^3.4.12", + "tailwindcss": "^3.4.13", "yargs": "^17.7.2" } } diff --git a/docs/pages/blog/introducing-the-row-grouping-feature.md b/docs/pages/blog/introducing-the-row-grouping-feature.md index f4a7aae089a900..ca42a453707924 100644 --- a/docs/pages/blog/introducing-the-row-grouping-feature.md +++ b/docs/pages/blog/introducing-the-row-grouping-feature.md @@ -29,7 +29,9 @@ With the new row grouping feature, you can click on the **Director** column menu When you're ready to return to the default view, click on **Stop Grouping by Director** in the same column menu. -grouping and un-grouping by director + ## How to unlock this feature 🔓🎁 @@ -97,7 +99,9 @@ To group our movies by decade, we could use the following: groupingValueGetter: ({ value }) => `${Math.floor(value.getFullYear() / 10)}0's`; ``` -grouping by release decade + ## Share your feedback 🗣 diff --git a/docs/pages/careers.tsx b/docs/pages/careers.tsx index 0e6a697ce6f8ee..f1a88ab4f17403 100644 --- a/docs/pages/careers.tsx +++ b/docs/pages/careers.tsx @@ -62,12 +62,12 @@ const nextRolesData = [ { title: 'Engineering', roles: [ - { - title: 'React Engineer — Docs-infra', - description: - 'You will drive the development and maintenance of the documentation platform that powers all MUI products.', - url: '/careers/react-engineer-docs-infra/', - }, + // { + // title: 'React Engineer — Docs-infra', + // description: + // 'You will drive the development and maintenance of the documentation platform that powers all MUI products.', + // url: '/careers/react-engineer-docs-infra/', + // }, { title: 'React Tech Lead — Core', description: diff --git a/docs/pages/careers/react-engineer-docs-infra.md b/docs/pages/careers/react-engineer-docs-infra.md index b06f0e061c0cbc..6c4177467c2b28 100644 --- a/docs/pages/careers/react-engineer-docs-infra.md +++ b/docs/pages/careers/react-engineer-docs-infra.md @@ -6,7 +6,7 @@ - **Location**: Remote (preference for UTC-5 to UTC+2). - **Type of work**: Full-time (contractor or employee [depending on circumstances](https://mui-org.notion.site/Hiring-FAQ-64763b756ae44c37b47b081f98915501#494af1f358794028beb4b7697b5d3102)). -- **Level**: [confirmed IC3 or above](https://mui-org.notion.site/Leveling-at-MUI-5c30f9bfe65149d697f346447cef9db1). +- **Level**: [IC3 (with high growth potential) or above](https://mui-org.notion.site/Leveling-at-MUI-5c30f9bfe65149d697f346447cef9db1). - We're a **remote** company, we prefer asynchronous communication over meetings. ## The company diff --git a/docs/pages/experiments/docs/headers.md b/docs/pages/experiments/docs/headers.md index e6c82e7712e4ff..a471c24caf5032 100644 --- a/docs/pages/experiments/docs/headers.md +++ b/docs/pages/experiments/docs/headers.md @@ -14,6 +14,8 @@ Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. +## [Header with link](/) + ## Header with Pro plan [](/x/introduction/licensing/#pro-plan 'Pro plan') ## Header with Premium plan [](/x/introduction/licensing/#premium-plan 'premium plan') diff --git a/docs/pages/material-ui/api/badge.json b/docs/pages/material-ui/api/badge.json index 0b1116c85c94c5..cbe3efbc8c8046 100644 --- a/docs/pages/material-ui/api/badge.json +++ b/docs/pages/material-ui/api/badge.json @@ -3,7 +3,7 @@ "anchorOrigin": { "type": { "name": "shape", - "description": "{ horizontal: 'left'
| 'right', vertical: 'bottom'
| 'top' }" + "description": "{ horizontal?: 'left'
| 'right', vertical?: 'bottom'
| 'top' }" }, "default": "{\n vertical: 'top',\n horizontal: 'right',\n}" }, diff --git a/docs/public/_redirects b/docs/public/_redirects index 626cf9bfb95a97..f59df6cfea1099 100644 --- a/docs/public/_redirects +++ b/docs/public/_redirects @@ -20,7 +20,7 @@ /r/custom-component-variants /material-ui/customization/how-to-customize/#adding-new-component-variants 302 /r/migration-v4 /material-ui/migration/migration-v4/ 302 /r/discord https://discord.com/invite/tewQuNeUyS 302 -/r/pigment /blog/introducing-pigment-css/ +/r/pigment /blog/introducing-pigment-css/ 302 # Legacy redirection # Added in chronological order (the last line is the most recent one) diff --git a/docs/public/static/blog/introducing-the-row-grouping-feature/blog1.gif b/docs/public/static/blog/introducing-the-row-grouping-feature/blog1.gif deleted file mode 100644 index e04f4c2808007e..00000000000000 Binary files a/docs/public/static/blog/introducing-the-row-grouping-feature/blog1.gif and /dev/null differ diff --git a/docs/public/static/blog/introducing-the-row-grouping-feature/blog1.mp4 b/docs/public/static/blog/introducing-the-row-grouping-feature/blog1.mp4 new file mode 100644 index 00000000000000..5769cfb6c4230d Binary files /dev/null and b/docs/public/static/blog/introducing-the-row-grouping-feature/blog1.mp4 differ diff --git a/docs/public/static/blog/introducing-the-row-grouping-feature/blog2.gif b/docs/public/static/blog/introducing-the-row-grouping-feature/blog2.gif deleted file mode 100644 index 28a51ce73f868e..00000000000000 Binary files a/docs/public/static/blog/introducing-the-row-grouping-feature/blog2.gif and /dev/null differ diff --git a/docs/public/static/blog/introducing-the-row-grouping-feature/blog2.mp4 b/docs/public/static/blog/introducing-the-row-grouping-feature/blog2.mp4 new file mode 100644 index 00000000000000..8fa327411fbbb4 Binary files /dev/null and b/docs/public/static/blog/introducing-the-row-grouping-feature/blog2.mp4 differ diff --git a/docs/src/components/about/HowToSupport.tsx b/docs/src/components/about/HowToSupport.tsx index 270d092bb82bc0..bc0ac02fdd79f5 100644 --- a/docs/src/components/about/HowToSupport.tsx +++ b/docs/src/components/about/HowToSupport.tsx @@ -21,7 +21,7 @@ function Widget({ }: { children: React.ReactNode; title: string; - icon: React.ReactElement; + icon: React.ReactElement; }) { return ( svg': { m: '0 !important' }, // override the 2px margin-left coming from the Link component diff --git a/docs/src/components/header/HeaderNavBar.tsx b/docs/src/components/header/HeaderNavBar.tsx index 1b9464d6cadb20..778f20e6f207f9 100644 --- a/docs/src/components/header/HeaderNavBar.tsx +++ b/docs/src/components/header/HeaderNavBar.tsx @@ -75,7 +75,7 @@ const PRODUCT_IDS = [ ]; type ProductSubMenuProps = { - icon: React.ReactElement; + icon: React.ReactElement; name: React.ReactNode; description: React.ReactNode; chip?: React.ReactNode; diff --git a/docs/src/components/home/DiamondSponsors.tsx b/docs/src/components/home/DiamondSponsors.tsx index 5d89c01da5672f..35b4052f081fe2 100644 --- a/docs/src/components/home/DiamondSponsors.tsx +++ b/docs/src/components/home/DiamondSponsors.tsx @@ -13,13 +13,13 @@ const DIAMONDs = [ src: '/static/sponsors/octopus-square.svg', name: 'Octopus Deploy', description: 'A unified DevOps automation platform for your team.', - href: 'https://octopus.com/?utm_source=MUI&utm_medium=referral&utm_content=homepage', + href: 'https://octopus.com/?utm_source=mui.com&utm_medium=referral&utm_content=homepage', }, { src: '/static/sponsors/doit-square.svg', name: 'Doit International', description: 'Technology and cloud expertise to buy, optimize and manage public cloud.', - href: 'https://www.doit.com/?utm_source=MUI&utm_medium=referral&utm_content=homepage', + href: 'https://www.doit.com/?utm_source=mui.com&utm_medium=referral&utm_content=homepage', }, { src: '/static/sponsors/marblism-square.svg', diff --git a/docs/src/components/home/GoldSponsors.tsx b/docs/src/components/home/GoldSponsors.tsx index 53108f10136fa8..eb34e67ee4c6bd 100644 --- a/docs/src/components/home/GoldSponsors.tsx +++ b/docs/src/components/home/GoldSponsors.tsx @@ -22,14 +22,14 @@ const GOLDs = [ srcSet: 'https://avatars.githubusercontent.com/u/251374?s=120 3x', name: 'Spotify', description: 'Music service for accessing millions of songs.', - href: 'https://open.spotify.com?utm_source=MUI&utm_medium=referral&utm_content=homepage', + href: 'https://open.spotify.com?utm_source=mui.com&utm_medium=referral&utm_content=homepage', }, { src: 'https://images.opencollective.com/icons8/7fa1641/logo/40.png', srcSet: 'https://images.opencollective.com/icons8/7fa1641/logo/120.png 3x', name: 'Icons8', description: 'API for icons, photos, illustrations, and music.', - href: 'https://icons8.com?utm_source=MUI&utm_medium=referral&utm_content=homepage', + href: 'https://icons8.com?utm_source=mui.com&utm_medium=referral&utm_content=homepage', }, { src: 'https://rxdb.info/files/logo/logo_text.svg', @@ -42,53 +42,53 @@ const GOLDs = [ srcSet: 'https://avatars.githubusercontent.com/u/1262264?s=120 3x', name: 'Text-em-all', description: 'Mass text messaging and automated calling.', - href: 'https://www.text-em-all.com/?utm_source=MUI&utm_medium=referral&utm_content=homepage', + href: 'https://www.text-em-all.com/?utm_source=mui.com&utm_medium=referral&utm_content=homepage', }, { src: '/static/sponsors/megafamous.png', name: 'MegaFamous', description: 'Buy Instagram followers and likes.', - href: 'https://megafamous.com/?utm_source=MUI&utm_medium=referral&utm_content=homepage', + href: 'https://megafamous.com/?utm_source=mui.com&utm_medium=referral&utm_content=homepage', }, { src: 'https://images.opencollective.com/dialmycalls/f5ae9ab/avatar/40.png', srcSet: 'https://images.opencollective.com/dialmycalls/f5ae9ab/avatar/120.png 3x', name: 'DialMyCalls', description: 'Send text messages, calls, and emails.', - href: 'https://www.dialmycalls.com/?utm_source=MUI&utm_medium=referral&utm_content=homepage', + href: 'https://www.dialmycalls.com/?utm_source=mui.com&utm_medium=referral&utm_content=homepage', }, { src: 'https://images.opencollective.com/goread_io/eb6337d/logo/40.png', srcSet: 'https://images.opencollective.com/goread_io/eb6337d/logo/120.png 3x', name: 'Goread.io', description: 'Instagram followers, likes, views, and comments.', - href: 'https://goread.io/?utm_source=MUI&utm_medium=referral&utm_content=homepage', + href: 'https://goread.io/?utm_source=mui.com&utm_medium=referral&utm_content=homepage', }, { src: 'https://images.opencollective.com/copycopterai/e167216/logo/40.png', srcSet: 'https://images.opencollective.com/copycopterai/e167216/logo/120.png 3x', name: 'Copycopter.ai', description: 'Turn prompts into videos at ultra speed.', - href: 'https://copycopter.ai/?utm_source=MUI&utm_medium=referral&utm_content=homepage', + href: 'https://copycopter.ai/?utm_source=mui.com&utm_medium=referral&utm_content=homepage', }, { src: 'https://images.opencollective.com/route4me/71fb6fa/avatar/40.png', srcSet: 'https://images.opencollective.com/route4me/71fb6fa/avatar/120.png 3x', name: 'Route4Me', description: 'Trusted last mile route planning and optimization.', - href: 'https://route4me.com/?utm_source=MUI&utm_medium=referral&utm_content=homepage', + href: 'https://route4me.com/?utm_source=mui.com&utm_medium=referral&utm_content=homepage', }, ]; const BACKLINKs = [ { name: 'Buzzoid', description: 'Instant delivery Instagram followers.', - href: 'https://buzzoid.com/', + href: 'https://buzzoid.com/?utm_source=mui.com&utm_medium=referral&utm_content=homepage', }, { name: 'Twicsy', description: 'Instant delivery Instagram followers.', - href: 'https://twicsy.com/', + href: 'https://twicsy.com/?utm_source=mui.com&utm_medium=referral&utm_content=homepage', }, ]; diff --git a/docs/src/components/home/MaterialDesignComponents.tsx b/docs/src/components/home/MaterialDesignComponents.tsx index e65a553dd8e2b9..ef241db8b9ce32 100644 --- a/docs/src/components/home/MaterialDesignComponents.tsx +++ b/docs/src/components/home/MaterialDesignComponents.tsx @@ -125,7 +125,7 @@ function Demo({ }: { name: string; theme: Theme | undefined; - children: React.ReactElement; + children: React.ReactElement; control?: { prop: string; values: Array; defaultValue?: string }; }) { const [propValue, setPropValue] = React.useState( diff --git a/docs/src/components/home/UserFeedbacks.tsx b/docs/src/components/home/UserFeedbacks.tsx index 8d8316d68bd1e6..c9e280c40132b8 100644 --- a/docs/src/components/home/UserFeedbacks.tsx +++ b/docs/src/components/home/UserFeedbacks.tsx @@ -95,7 +95,7 @@ function Feedback({ avatarSrcSet: string; name: string; role: string; - company?: React.ReactElement; + company?: React.ReactElement; }; }) { return ( diff --git a/docs/src/components/pricing/PricingTable.tsx b/docs/src/components/pricing/PricingTable.tsx index 370b9ad3c4a861..6dd607e3477de8 100644 --- a/docs/src/components/pricing/PricingTable.tsx +++ b/docs/src/components/pricing/PricingTable.tsx @@ -378,7 +378,7 @@ function RowHead({ children, startIcon, ...props -}: BoxProps & { startIcon?: React.ReactElement }) { +}: BoxProps & { startIcon?: React.ReactElement }) { return ( , nested?: boolean) { + function renderList(content: React.ReactElement, nested?: boolean) { return ( > = { +const viewIcons: Record> = { quilt: , module: , agenda: , diff --git a/docs/src/components/typography/SectionHeadline.tsx b/docs/src/components/typography/SectionHeadline.tsx index 87a98753c61487..101cd3c1141d51 100644 --- a/docs/src/components/typography/SectionHeadline.tsx +++ b/docs/src/components/typography/SectionHeadline.tsx @@ -11,7 +11,7 @@ interface SectionHeadlineProps { */ inverted?: boolean; overline?: React.ReactNode; - title: string | React.ReactElement; + title: string | React.ReactElement>; } export default function SectionHeadline(props: SectionHeadlineProps) { diff --git a/docs/src/layouts/HeroContainer.tsx b/docs/src/layouts/HeroContainer.tsx index aef873256e4dc7..bb238bf622e0ad 100644 --- a/docs/src/layouts/HeroContainer.tsx +++ b/docs/src/layouts/HeroContainer.tsx @@ -8,9 +8,9 @@ import { alpha } from '@mui/material/styles'; interface HeroContainerProps { disableMobileHidden?: boolean; disableTabExclusion?: boolean; - left: React.ReactElement; + left: React.ReactElement; linearGradient?: boolean; - right: React.ReactElement; + right: React.ReactElement; rightSx?: BoxProps['sx']; } diff --git a/docs/src/modules/components/ApiPage/definitions/classes.ts b/docs/src/modules/components/ApiPage/definitions/classes.ts index ac8a8ef0e57d3e..055f640e0006a4 100644 --- a/docs/src/modules/components/ApiPage/definitions/classes.ts +++ b/docs/src/modules/components/ApiPage/definitions/classes.ts @@ -60,14 +60,14 @@ export function getClassApiDefinitions(params: GetClassApiDefinitionsParams): Cl if (description.includes('{{conditions}}')) { if (!conditions) { - throw Error(errorMessage(componentName, classDefinition.className, 'conditions')); + throw new Error(errorMessage(componentName, classDefinition.className, 'conditions')); } description = description.replace(/{{conditions}}/, conditions); } if (description.includes('{{nodeName}}')) { if (!nodeName) { - throw Error(errorMessage(componentName, classDefinition.className, 'nodeName')); + throw new Error(errorMessage(componentName, classDefinition.className, 'nodeName')); } description = description.replace(/{{nodeName}}/, nodeName); } diff --git a/docs/src/modules/components/DiamondSponsors.js b/docs/src/modules/components/DiamondSponsors.js index 7061bf89cce463..cf308ce1623c5b 100644 --- a/docs/src/modules/components/DiamondSponsors.js +++ b/docs/src/modules/components/DiamondSponsors.js @@ -49,7 +49,7 @@ export default function DiamondSponsors() { data-ga-event-category="sponsor" data-ga-event-action="docs-premium" data-ga-event-label="octopus.com" - href="https://octopus.com/?utm_source=materialui&utm_medium=referral" + href="https://octopus.com/?utm_source=mui.com&utm_medium=referral" rel="noopener sponsored" target="_blank" > @@ -76,7 +76,7 @@ export default function DiamondSponsors() { data-ga-event-category="sponsor" data-ga-event-action="docs-premium" data-ga-event-label="doit.com" - href="https://www.doit.com/?utm_source=materialui&utm_medium=referral" + href="https://www.doit.com/?utm_source=mui.com&utm_medium=referral" rel="noopener sponsored" target="_blank" > @@ -103,7 +103,7 @@ export default function DiamondSponsors() { data-ga-event-category="sponsor" data-ga-event-action="docs-premium" data-ga-event-label="marblism.com" - href="https://www.marblism.com/?utm_source=mui" + href="https://www.marblism.com/?utm_source=mui.com&utm_medium=referral" rel="noopener sponsored" target="_blank" > diff --git a/docs/src/modules/components/JoyUsageDemo.tsx b/docs/src/modules/components/JoyUsageDemo.tsx index 92f13c2fe8f964..98804104e7b016 100644 --- a/docs/src/modules/components/JoyUsageDemo.tsx +++ b/docs/src/modules/components/JoyUsageDemo.tsx @@ -165,7 +165,7 @@ interface JoyUsageDemoProps { * A function to override the code block result. */ getCodeBlock?: (code: string, props: ComponentProps) => string; - renderDemo: (props: ComponentProps) => React.ReactElement; + renderDemo: (props: ComponentProps) => React.ReactElement; } export default function JoyUsageDemo({ diff --git a/docs/src/modules/components/JoyVariablesDemo.tsx b/docs/src/modules/components/JoyVariablesDemo.tsx index 433d09753ac6a3..a691cc654f9112 100644 --- a/docs/src/modules/components/JoyVariablesDemo.tsx +++ b/docs/src/modules/components/JoyVariablesDemo.tsx @@ -33,7 +33,7 @@ function formatSx(sx: { [k: string]: string | number }) { interface SlotVariablesProps { slot: string; data: Array; - renderField: (item: DataItem) => React.ReactElement; + renderField: (item: DataItem) => React.ReactElement; defaultOpen?: boolean; } @@ -83,7 +83,7 @@ export default function JoyVariablesDemo(props: { componentName: string; childrenAccepted?: boolean; data: Array, { defaultOpen?: boolean } | undefined]>; - renderDemo: (sx: { [k: string]: string | number }) => React.ReactElement; + renderDemo: (sx: { [k: string]: string | number }) => React.ReactElement; renderCode?: (formattedSx: string) => string; }) { const { componentName, data = [], childrenAccepted = false, renderCode } = props; diff --git a/docs/src/modules/components/TemplateFrame.js b/docs/src/modules/components/TemplateFrame.js index d2829105313b96..d3efda66621d17 100644 --- a/docs/src/modules/components/TemplateFrame.js +++ b/docs/src/modules/components/TemplateFrame.js @@ -24,12 +24,7 @@ import PaletteIcon from '@mui/icons-material/PaletteOutlined'; import codeSandbox from 'docs/src/modules/sandbox/CodeSandbox'; import stackBlitz from 'docs/src/modules/sandbox/StackBlitz'; import sourceMaterialTemplates from 'docs/src/modules/material/sourceMaterialTemplates'; - -function pascalCase(str) { - return str - .replace(/[^\w]+(.)/g, (_, chr) => chr.toUpperCase()) - .replace(/^(.)/, (_, chr) => chr.toUpperCase()); -} +import { pascalCase } from 'docs/src/modules/utils/helpers'; const StyledAppBar = styled(AppBar)(({ theme }) => ({ position: 'relative', @@ -196,7 +191,7 @@ const brandingTheme = createTheme({ ...getThemedComponents(), }); -function TemplateFrame({ children }) { +export default function TemplateFrame({ children }) { const router = useRouter(); const templateId = router.pathname.split('/').pop(); const templateName = pascalCase(templateId); @@ -262,17 +257,17 @@ function TemplateFrame({ children }) { '& > *': { flexShrink: 0 }, }} > - + - codeSandbox + stackBlitz .createMaterialTemplate({ ...item, files: { ...item.files, ...materialTemplates.sharedTheme?.files }, @@ -291,26 +286,26 @@ function TemplateFrame({ children }) { } return content; }) - .openSandbox(`/${templateName}`) + .openStackBlitz(`/${templateName}`) } sx={{ alignSelf: 'center', borderRadius: 1 }} > - - + + - + - stackBlitz + codeSandbox .createMaterialTemplate({ ...item, files: { ...item.files, ...materialTemplates.sharedTheme?.files }, @@ -329,12 +324,12 @@ function TemplateFrame({ children }) { } return content; }) - .openStackBlitz(`/${templateName}`) + .openSandbox(`/${templateName}`) } sx={{ alignSelf: 'center', borderRadius: 1 }} > - - + + @@ -358,5 +353,3 @@ function TemplateFrame({ children }) { ); } - -export default TemplateFrame; diff --git a/docs/src/modules/utils/helpers.ts b/docs/src/modules/utils/helpers.ts index 4f9c39a048b20c..a80a491919b191 100644 --- a/docs/src/modules/utils/helpers.ts +++ b/docs/src/modules/utils/helpers.ts @@ -3,7 +3,7 @@ import camelCase from 'lodash/camelCase'; import { LANGUAGES } from 'docs/config'; import { Translate } from '@mui/docs/i18n'; -function pascalCase(str: string) { +export function pascalCase(str: string) { return upperFirst(camelCase(str)); } diff --git a/docs/types/docs.d.ts b/docs/types/docs.d.ts index fc96dea50c386a..4d77ba0ecabb66 100644 --- a/docs/types/docs.d.ts +++ b/docs/types/docs.d.ts @@ -23,7 +23,7 @@ declare module 'docs/src/modules/components/HighlightedCode' { component?: React.ElementType; sx?: object; } - export default function HighlightedCode(props: Props): React.ReactElement; + export default function HighlightedCode(props: Props): React.ReactElement; } declare module 'react-imask'; diff --git a/examples/base-ui-vite-tailwind-ts/README.md b/examples/base-ui-vite-tailwind-ts/README.md index 4545337ebfed16..0faee35fd921a6 100644 --- a/examples/base-ui-vite-tailwind-ts/README.md +++ b/examples/base-ui-vite-tailwind-ts/README.md @@ -2,7 +2,7 @@ [Base UI](https://mui.com/base-ui/) is a library of unstyled React UI components and hooks. -[Vite](https://vitejs.dev/) is a build tool that aims to provide a faster and leaner development experience for modern web projects, consisting of a dev server and a build command +[Vite](https://vite.dev/) is a build tool that aims to provide a faster and leaner development experience for modern web projects, consisting of a dev server and a build command [Tailwind CSS](https://tailwindcss.com/) is a utility-first CSS framework that provides low-level CSS classes that can be composed to build custom UI designs. diff --git a/examples/base-ui-vite-tailwind-ts/src/App.tsx b/examples/base-ui-vite-tailwind-ts/src/App.tsx index d6824d2ec394fe..a2085ff2abeb48 100644 --- a/examples/base-ui-vite-tailwind-ts/src/App.tsx +++ b/examples/base-ui-vite-tailwind-ts/src/App.tsx @@ -14,7 +14,7 @@ export default function App() { is a library of unstyled React UI components and hooks.
  • - + Vite {' '} is a build tool that aims to provide a faster and leaner development experience for modern diff --git a/examples/base-ui-vite-tailwind-ts/vite.config.ts b/examples/base-ui-vite-tailwind-ts/vite.config.ts index 627a3196243d37..4a5def4c3d78f7 100644 --- a/examples/base-ui-vite-tailwind-ts/vite.config.ts +++ b/examples/base-ui-vite-tailwind-ts/vite.config.ts @@ -1,7 +1,7 @@ import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; -// https://vitejs.dev/config/ +// https://vite.dev/config/ export default defineConfig({ plugins: [react()], }); diff --git a/examples/base-ui-vite-tailwind/README.md b/examples/base-ui-vite-tailwind/README.md index 37e997ae9ec3ee..4c846b6b08511d 100644 --- a/examples/base-ui-vite-tailwind/README.md +++ b/examples/base-ui-vite-tailwind/README.md @@ -2,7 +2,7 @@ [Base UI](https://mui.com/base-ui/) is a library of unstyled React UI components and hooks. -[Vite](https://vitejs.dev/) is a build tool that aims to provide a faster and leaner development experience for modern web projects, consisting of a dev server and a build command +[Vite](https://vite.dev/) is a build tool that aims to provide a faster and leaner development experience for modern web projects, consisting of a dev server and a build command [Tailwind CSS](https://tailwindcss.com/) is a utility-first CSS framework that provides low-level CSS classes that can be composed to build custom UI designs. diff --git a/examples/base-ui-vite-tailwind/src/App.jsx b/examples/base-ui-vite-tailwind/src/App.jsx index 3540056055ea91..8bd16d6d813f59 100644 --- a/examples/base-ui-vite-tailwind/src/App.jsx +++ b/examples/base-ui-vite-tailwind/src/App.jsx @@ -12,7 +12,7 @@ export default function App() { is a library of unstyled React UI components and hooks.
  • - + Vite {' '} is a build tool that aims to provide a faster and leaner development experience for modern diff --git a/examples/base-ui-vite-tailwind/vite.config.js b/examples/base-ui-vite-tailwind/vite.config.js index 627a3196243d37..4a5def4c3d78f7 100644 --- a/examples/base-ui-vite-tailwind/vite.config.js +++ b/examples/base-ui-vite-tailwind/vite.config.js @@ -1,7 +1,7 @@ import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; -// https://vitejs.dev/config/ +// https://vite.dev/config/ export default defineConfig({ plugins: [react()], }); diff --git a/examples/joy-ui-vite-ts/README.md b/examples/joy-ui-vite-ts/README.md index dcc482bdfd04d9..fc7f43959f77e0 100644 --- a/examples/joy-ui-vite-ts/README.md +++ b/examples/joy-ui-vite-ts/README.md @@ -2,7 +2,7 @@ [Joy UI](https://mui.com/joy-ui/getting-started/) is a library of beautifully designed React UI components built to spark joy in the development process. -[Vite](https://vitejs.dev/) is a build tool that aims to provide a faster and leaner development experience for modern web projects, consisting of a dev server and a build command. +[Vite](https://vite.dev/) is a build tool that aims to provide a faster and leaner development experience for modern web projects, consisting of a dev server and a build command. ## How to use diff --git a/examples/joy-ui-vite-ts/vite.config.ts b/examples/joy-ui-vite-ts/vite.config.ts index 627a3196243d37..4a5def4c3d78f7 100644 --- a/examples/joy-ui-vite-ts/vite.config.ts +++ b/examples/joy-ui-vite-ts/vite.config.ts @@ -1,7 +1,7 @@ import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; -// https://vitejs.dev/config/ +// https://vite.dev/config/ export default defineConfig({ plugins: [react()], }); diff --git a/examples/material-ui-cra-tailwind-ts/src/index.tsx b/examples/material-ui-cra-tailwind-ts/src/index.tsx index c882daa48dbca7..e713f977f9aa76 100644 --- a/examples/material-ui-cra-tailwind-ts/src/index.tsx +++ b/examples/material-ui-cra-tailwind-ts/src/index.tsx @@ -12,6 +12,7 @@ const root = ReactDOM.createRoot(rootElement!); // All `Portal`-related components need to have the the main app wrapper element as a container // so that the are in the subtree under the element used in the `important` option of the Tailwind's config. const theme = createTheme({ + cssVariables: true, components: { MuiPopover: { defaultProps: { diff --git a/examples/material-ui-cra-tailwind-ts/src/reportWebVitals.ts b/examples/material-ui-cra-tailwind-ts/src/reportWebVitals.ts index 49a2a16e0fbc76..af13188905f025 100644 --- a/examples/material-ui-cra-tailwind-ts/src/reportWebVitals.ts +++ b/examples/material-ui-cra-tailwind-ts/src/reportWebVitals.ts @@ -1,13 +1,13 @@ -import { ReportHandler } from 'web-vitals'; +import { MetricType } from 'web-vitals'; -const reportWebVitals = (onPerfEntry?: ReportHandler) => { +const reportWebVitals = (onPerfEntry?: (metric: MetricType) => void) => { if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { - getCLS(onPerfEntry); - getFID(onPerfEntry); - getFCP(onPerfEntry); - getLCP(onPerfEntry); - getTTFB(onPerfEntry); + import('web-vitals').then(({ onCLS, onINP, onFCP, onLCP, onTTFB }) => { + onCLS(onPerfEntry); + onINP(onPerfEntry); + onFCP(onPerfEntry); + onLCP(onPerfEntry); + onTTFB(onPerfEntry); }); } }; diff --git a/examples/material-ui-cra-ts/src/theme.ts b/examples/material-ui-cra-ts/src/theme.ts index 9677a59f18c4b5..7ba9892c1b1a78 100644 --- a/examples/material-ui-cra-ts/src/theme.ts +++ b/examples/material-ui-cra-ts/src/theme.ts @@ -3,6 +3,7 @@ import { red } from '@mui/material/colors'; // A custom theme for this app const theme = createTheme({ + cssVariables: true, palette: { primary: { main: '#556cd6', diff --git a/examples/material-ui-cra/src/theme.js b/examples/material-ui-cra/src/theme.js index 981cae51e24404..1f5444616cfdbf 100644 --- a/examples/material-ui-cra/src/theme.js +++ b/examples/material-ui-cra/src/theme.js @@ -3,6 +3,7 @@ import { createTheme } from '@mui/material/styles'; // A custom theme for this app const theme = createTheme({ + cssVariables: true, palette: { primary: { main: '#556cd6', diff --git a/examples/material-ui-express-ssr/theme.js b/examples/material-ui-express-ssr/theme.js index 050b11a7d1e1b7..8446cf547a7815 100644 --- a/examples/material-ui-express-ssr/theme.js +++ b/examples/material-ui-express-ssr/theme.js @@ -3,6 +3,7 @@ import { red } from '@mui/material/colors'; // Create a theme instance. const theme = createTheme({ + cssVariables: true, palette: { primary: { main: '#556cd6', diff --git a/examples/material-ui-gatsby/src/theme.js b/examples/material-ui-gatsby/src/theme.js index 981cae51e24404..1f5444616cfdbf 100644 --- a/examples/material-ui-gatsby/src/theme.js +++ b/examples/material-ui-gatsby/src/theme.js @@ -3,6 +3,7 @@ import { createTheme } from '@mui/material/styles'; // A custom theme for this app const theme = createTheme({ + cssVariables: true, palette: { primary: { main: '#556cd6', diff --git a/examples/material-ui-nextjs-pages-router-ts/src/theme.ts b/examples/material-ui-nextjs-pages-router-ts/src/theme.ts index 89d3b46321de59..c2ee80e247748b 100644 --- a/examples/material-ui-nextjs-pages-router-ts/src/theme.ts +++ b/examples/material-ui-nextjs-pages-router-ts/src/theme.ts @@ -10,6 +10,7 @@ export const roboto = Roboto({ // Create a theme instance. const theme = createTheme({ + cssVariables: true, palette: { primary: { main: '#556cd6', diff --git a/examples/material-ui-nextjs-pages-router/src/theme.js b/examples/material-ui-nextjs-pages-router/src/theme.js index 2ed02149914f7d..896e7e8402d2ce 100644 --- a/examples/material-ui-nextjs-pages-router/src/theme.js +++ b/examples/material-ui-nextjs-pages-router/src/theme.js @@ -10,6 +10,7 @@ const roboto = Roboto({ // Create a theme instance. const theme = createTheme({ + cssVariables: true, palette: { primary: { main: '#556cd6', diff --git a/examples/material-ui-nextjs-ts-v4-v5-migration/src/theme.ts b/examples/material-ui-nextjs-ts-v4-v5-migration/src/theme.ts index 2ed02149914f7d..896e7e8402d2ce 100644 --- a/examples/material-ui-nextjs-ts-v4-v5-migration/src/theme.ts +++ b/examples/material-ui-nextjs-ts-v4-v5-migration/src/theme.ts @@ -10,6 +10,7 @@ const roboto = Roboto({ // Create a theme instance. const theme = createTheme({ + cssVariables: true, palette: { primary: { main: '#556cd6', diff --git a/examples/material-ui-nextjs-ts/src/theme.ts b/examples/material-ui-nextjs-ts/src/theme.ts index b105b58c5b2737..b42a2821c83698 100644 --- a/examples/material-ui-nextjs-ts/src/theme.ts +++ b/examples/material-ui-nextjs-ts/src/theme.ts @@ -1,6 +1,6 @@ 'use client'; -import { Roboto } from 'next/font/google'; import { createTheme } from '@mui/material/styles'; +import { Roboto } from 'next/font/google'; const roboto = Roboto({ weight: ['300', '400', '500', '700'], @@ -9,6 +9,7 @@ const roboto = Roboto({ }); const theme = createTheme({ + cssVariables: true, palette: { mode: 'light', }, @@ -18,11 +19,16 @@ const theme = createTheme({ components: { MuiAlert: { styleOverrides: { - root: ({ ownerState }) => ({ - ...(ownerState.severity === 'info' && { - backgroundColor: '#60a5fa', - }), - }), + root: { + variants: [ + { + props: { severity: 'info' }, + style: { + backgroundColor: '#60a5fa', + }, + }, + ], + }, }, }, }, diff --git a/examples/material-ui-nextjs/src/theme.js b/examples/material-ui-nextjs/src/theme.js index b105b58c5b2737..7f6e4eecc6cec8 100644 --- a/examples/material-ui-nextjs/src/theme.js +++ b/examples/material-ui-nextjs/src/theme.js @@ -9,6 +9,7 @@ const roboto = Roboto({ }); const theme = createTheme({ + cssVariables: true, palette: { mode: 'light', }, @@ -18,11 +19,16 @@ const theme = createTheme({ components: { MuiAlert: { styleOverrides: { - root: ({ ownerState }) => ({ - ...(ownerState.severity === 'info' && { - backgroundColor: '#60a5fa', - }), - }), + root: { + variants: [ + { + props: { severity: 'info' }, + style: { + backgroundColor: '#60a5fa', + }, + } + ], + }, }, }, }, diff --git a/examples/material-ui-pigment-css-vite-ts/vite.config.ts b/examples/material-ui-pigment-css-vite-ts/vite.config.ts index a64248b48c1ca6..46ea057cca64dc 100644 --- a/examples/material-ui-pigment-css-vite-ts/vite.config.ts +++ b/examples/material-ui-pigment-css-vite-ts/vite.config.ts @@ -11,7 +11,7 @@ const pigmentConfig = { transformLibraries: ['@mui/material'], }; -// https://vitejs.dev/config/ +// https://vite.dev/config/ export default defineConfig({ plugins: [react(), pigment(pigmentConfig)], }); diff --git a/examples/material-ui-preact/src/theme.js b/examples/material-ui-preact/src/theme.js index 050b11a7d1e1b7..8446cf547a7815 100644 --- a/examples/material-ui-preact/src/theme.js +++ b/examples/material-ui-preact/src/theme.js @@ -3,6 +3,7 @@ import { red } from '@mui/material/colors'; // Create a theme instance. const theme = createTheme({ + cssVariables: true, palette: { primary: { main: '#556cd6', diff --git a/examples/material-ui-remix-ts/app/src/theme.ts b/examples/material-ui-remix-ts/app/src/theme.ts index 050b11a7d1e1b7..8446cf547a7815 100644 --- a/examples/material-ui-remix-ts/app/src/theme.ts +++ b/examples/material-ui-remix-ts/app/src/theme.ts @@ -3,6 +3,7 @@ import { red } from '@mui/material/colors'; // Create a theme instance. const theme = createTheme({ + cssVariables: true, palette: { primary: { main: '#556cd6', diff --git a/examples/material-ui-via-cdn/index.html b/examples/material-ui-via-cdn/index.html index 35d028fb989b3a..0b8d8398478268 100644 --- a/examples/material-ui-via-cdn/index.html +++ b/examples/material-ui-via-cdn/index.html @@ -46,6 +46,7 @@ // Create a theme instance. const theme = createTheme({ + cssVariables: true, palette: { primary: { main: '#556cd6', diff --git a/examples/material-ui-vite-ts/src/theme.tsx b/examples/material-ui-vite-ts/src/theme.tsx index 9677a59f18c4b5..7ba9892c1b1a78 100644 --- a/examples/material-ui-vite-ts/src/theme.tsx +++ b/examples/material-ui-vite-ts/src/theme.tsx @@ -3,6 +3,7 @@ import { red } from '@mui/material/colors'; // A custom theme for this app const theme = createTheme({ + cssVariables: true, palette: { primary: { main: '#556cd6', diff --git a/examples/material-ui-vite-ts/vite.config.ts b/examples/material-ui-vite-ts/vite.config.ts index 627a3196243d37..4a5def4c3d78f7 100644 --- a/examples/material-ui-vite-ts/vite.config.ts +++ b/examples/material-ui-vite-ts/vite.config.ts @@ -1,7 +1,7 @@ import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; -// https://vitejs.dev/config/ +// https://vite.dev/config/ export default defineConfig({ plugins: [react()], }); diff --git a/examples/material-ui-vite/src/theme.js b/examples/material-ui-vite/src/theme.js index 050b11a7d1e1b7..8446cf547a7815 100644 --- a/examples/material-ui-vite/src/theme.js +++ b/examples/material-ui-vite/src/theme.js @@ -3,6 +3,7 @@ import { red } from '@mui/material/colors'; // Create a theme instance. const theme = createTheme({ + cssVariables: true, palette: { primary: { main: '#556cd6', diff --git a/examples/material-ui-vite/vite.config.js b/examples/material-ui-vite/vite.config.js index 627a3196243d37..4a5def4c3d78f7 100644 --- a/examples/material-ui-vite/vite.config.js +++ b/examples/material-ui-vite/vite.config.js @@ -1,7 +1,7 @@ import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; -// https://vitejs.dev/config/ +// https://vite.dev/config/ export default defineConfig({ plugins: [react()], }); diff --git a/package.json b/package.json index 81e19304ae8b34..cd087008d616b1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mui/monorepo", - "version": "6.1.1", + "version": "6.1.2", "private": true, "scripts": { "preinstall": "npx only-allow pnpm", @@ -37,7 +37,7 @@ "docs:typescript:formatted": "tsx ./docs/scripts/formattedTSDemos", "docs:mdicons:synonyms": "cross-env BABEL_ENV=development babel-node --extensions \".tsx,.ts,.js,.mjs\" ./docs/scripts/updateIconSynonyms && pnpm prettier", "docs:zipRules": "cd docs && rm mui-vale.zip && zip -r mui-vale.zip mui-vale && cd ../ && vale sync", - "extract-error-codes": "cross-env MUI_EXTRACT_ERROR_CODES=true lerna run --concurrency 8 build:modern", + "extract-error-codes": "cross-env MUI_EXTRACT_ERROR_CODES=true lerna run --concurrency 1 build:modern", "rsc:build": "tsx ./packages/rsc-builder/buildRsc.ts", "template:screenshot": "cross-env BABEL_ENV=development babel-node --extensions \".tsx,.ts,.js\" ./docs/scripts/generateTemplateScreenshots", "template:update-theme": "cross-env BABEL_ENV=development babel-node --extensions \".tsx,.ts,.js\" ./docs/scripts/updateTemplatesTheme", @@ -100,7 +100,7 @@ }, "dependencies": { "@googleapis/sheets": "^9.3.1", - "@netlify/functions": "^2.8.1", + "@netlify/functions": "^2.8.2", "@slack/bolt": "^3.22.0", "execa": "^9.4.0", "google-auth-library": "^9.14.1" @@ -124,22 +124,21 @@ "@mui/joy": "workspace:*", "@mui/material": "workspace:^", "@mui/utils": "workspace:^", - "@next/eslint-plugin-next": "^14.2.13", + "@next/eslint-plugin-next": "^14.2.14", "@octokit/rest": "^21.0.2", - "@pigment-css/react": "0.0.23", + "@pigment-css/react": "0.0.24", "@playwright/test": "1.47.2", "@types/babel__core": "^7.20.5", "@types/fs-extra": "^11.0.4", - "@types/lodash": "^4.17.9", + "@types/lodash": "^4.17.10", "@types/mocha": "^10.0.8", - "@types/node": "^20.16.5", - "@types/react": "^18.3.4", + "@types/node": "^20.16.10", + "@types/react": "^18.3.11", "@types/yargs": "^17.0.33", "@typescript-eslint/eslint-plugin": "^7.18.0", "@typescript-eslint/parser": "^7.18.0", "babel-loader": "^9.2.1", "babel-plugin-istanbul": "^7.0.0", - "babel-plugin-macros": "^3.1.0", "babel-plugin-module-resolver": "^5.0.2", "babel-plugin-optimize-clsx": "^2.6.2", "babel-plugin-react-remove-properties": "^0.3.0", @@ -158,11 +157,11 @@ "eslint-import-resolver-webpack": "^0.13.9", "eslint-plugin-babel": "^5.3.1", "eslint-plugin-filenames": "^1.3.2", - "eslint-plugin-import": "^2.30.0", + "eslint-plugin-import": "^2.31.0", "eslint-plugin-jsx-a11y": "6.7.1", "eslint-plugin-material-ui": "workspace:^", "eslint-plugin-mocha": "^10.5.0", - "eslint-plugin-react": "^7.37.0", + "eslint-plugin-react": "^7.37.1", "eslint-plugin-react-compiler": "0.0.0-experimental-75b9fd4-20240912", "eslint-plugin-react-hooks": "^4.6.2", "fast-glob": "^3.3.2", @@ -180,7 +179,7 @@ "lodash": "^4.17.21", "markdownlint-cli2": "^0.14.0", "mocha": "^10.7.3", - "nx": "^19.7.3", + "nx": "^19.8.4", "nyc": "^17.1.0", "piscina": "^4.7.0", "postcss-styled-syntax": "^0.6.4", @@ -199,9 +198,9 @@ "webpack-cli": "^5.1.4", "yargs": "^17.7.2" }, - "packageManager": "pnpm@9.11.0", + "packageManager": "pnpm@9.12.0", "engines": { - "pnpm": "9.11.0" + "pnpm": "9.12.0" }, "resolutions": { "@babel/core": "^7.25.2", @@ -215,14 +214,14 @@ "@definitelytyped/header-parser": "^0.2.12", "@definitelytyped/typescript-versions": "^0.1.4", "@definitelytyped/utils": "^0.1.7", - "@types/node": "^20.16.5", - "@types/react": "^18.3.4", + "@types/node": "^20.16.10", + "@types/react": "^18.3.11", "@types/react-dom": "18.3.0", "cross-fetch": "^4.0.0", - "@pigment-css/react": "0.0.23", - "@pigment-css/unplugin": "0.0.23", - "@pigment-css/nextjs-plugin": "0.0.23", - "@pigment-css/vite-plugin": "0.0.23" + "@pigment-css/react": "0.0.24", + "@pigment-css/unplugin": "0.0.24", + "@pigment-css/nextjs-plugin": "0.0.24", + "@pigment-css/vite-plugin": "0.0.24" }, "nyc": { "include": [ diff --git a/packages/mui-babel-macros/__fixtures__/.eslintrc.js b/packages-internal/babel-plugin-minify-errors/__fixtures__/.eslintrc.js similarity index 100% rename from packages/mui-babel-macros/__fixtures__/.eslintrc.js rename to packages-internal/babel-plugin-minify-errors/__fixtures__/.eslintrc.js diff --git a/packages/mui-babel-macros/__fixtures__/error-code-extraction/error-codes.after.json b/packages-internal/babel-plugin-minify-errors/__fixtures__/error-code-extraction/error-codes.after.json similarity index 100% rename from packages/mui-babel-macros/__fixtures__/error-code-extraction/error-codes.after.json rename to packages-internal/babel-plugin-minify-errors/__fixtures__/error-code-extraction/error-codes.after.json diff --git a/packages/mui-babel-macros/__fixtures__/error-code-extraction/error-codes.before.json b/packages-internal/babel-plugin-minify-errors/__fixtures__/error-code-extraction/error-codes.before.json similarity index 100% rename from packages/mui-babel-macros/__fixtures__/error-code-extraction/error-codes.before.json rename to packages-internal/babel-plugin-minify-errors/__fixtures__/error-code-extraction/error-codes.before.json diff --git a/packages-internal/babel-plugin-minify-errors/__fixtures__/error-code-extraction/input.js b/packages-internal/babel-plugin-minify-errors/__fixtures__/error-code-extraction/input.js new file mode 100644 index 00000000000000..0cc2982ad5f3a3 --- /dev/null +++ b/packages-internal/babel-plugin-minify-errors/__fixtures__/error-code-extraction/input.js @@ -0,0 +1,2 @@ +throw /* minify-error */ new Error('exists'); +throw /* minify-error */ new Error('will be created'); diff --git a/packages/mui-babel-macros/__fixtures__/error-code-extraction/output.js b/packages-internal/babel-plugin-minify-errors/__fixtures__/error-code-extraction/output.js similarity index 53% rename from packages/mui-babel-macros/__fixtures__/error-code-extraction/output.js rename to packages-internal/babel-plugin-minify-errors/__fixtures__/error-code-extraction/output.js index 7a1d72c6a355cf..41342d27e3b230 100644 --- a/packages/mui-babel-macros/__fixtures__/error-code-extraction/output.js +++ b/packages-internal/babel-plugin-minify-errors/__fixtures__/error-code-extraction/output.js @@ -1,5 +1,5 @@ import _formatMuiErrorMessage from '@mui/utils/formatMuiErrorMessage'; -throw new Error(process.env.NODE_ENV !== 'production' ? `exists` : _formatMuiErrorMessage(1)); +throw new Error(process.env.NODE_ENV !== 'production' ? 'exists' : _formatMuiErrorMessage(1)); throw new Error( - process.env.NODE_ENV !== 'production' ? `will be created` : _formatMuiErrorMessage(2), + process.env.NODE_ENV !== 'production' ? 'will be created' : _formatMuiErrorMessage(2), ); diff --git a/packages-internal/babel-plugin-minify-errors/__fixtures__/interpolation/error-codes.json b/packages-internal/babel-plugin-minify-errors/__fixtures__/interpolation/error-codes.json new file mode 100644 index 00000000000000..17bea7ce743eec --- /dev/null +++ b/packages-internal/babel-plugin-minify-errors/__fixtures__/interpolation/error-codes.json @@ -0,0 +1,3 @@ +{ + "1": "MUI: %s, %s" +} diff --git a/packages-internal/babel-plugin-minify-errors/__fixtures__/interpolation/input.js b/packages-internal/babel-plugin-minify-errors/__fixtures__/interpolation/input.js new file mode 100644 index 00000000000000..bf99cee19dae5c --- /dev/null +++ b/packages-internal/babel-plugin-minify-errors/__fixtures__/interpolation/input.js @@ -0,0 +1,5 @@ +const foo = 'foo'; +const bar = 'bar'; +throw /* minify-error */ new Error(`MUI: ${foo}, ${bar}`); +throw /* minify-error */ new Error(`MUI: ${foo}` + `, ${bar}`); +throw /* minify-error */ new Error('MUI: ' + `${foo}, ${bar}`); diff --git a/packages-internal/babel-plugin-minify-errors/__fixtures__/interpolation/output.js b/packages-internal/babel-plugin-minify-errors/__fixtures__/interpolation/output.js new file mode 100644 index 00000000000000..7d5887fa7657fd --- /dev/null +++ b/packages-internal/babel-plugin-minify-errors/__fixtures__/interpolation/output.js @@ -0,0 +1,18 @@ +import _formatMuiErrorMessage from '@mui/utils/formatMuiErrorMessage'; +const foo = 'foo'; +const bar = 'bar'; +throw new Error( + process.env.NODE_ENV !== 'production' + ? `MUI: ${foo}, ${bar}` + : _formatMuiErrorMessage(1, foo, bar), +); +throw new Error( + process.env.NODE_ENV !== 'production' + ? `MUI: ${foo}` + `, ${bar}` + : _formatMuiErrorMessage(1, foo, bar), +); +throw new Error( + process.env.NODE_ENV !== 'production' + ? 'MUI: ' + `${foo}, ${bar}` + : _formatMuiErrorMessage(1, foo, bar), +); diff --git a/packages/mui-babel-macros/__fixtures__/literal/error-codes.json b/packages-internal/babel-plugin-minify-errors/__fixtures__/literal/error-codes.json similarity index 100% rename from packages/mui-babel-macros/__fixtures__/literal/error-codes.json rename to packages-internal/babel-plugin-minify-errors/__fixtures__/literal/error-codes.json diff --git a/packages-internal/babel-plugin-minify-errors/__fixtures__/literal/input.js b/packages-internal/babel-plugin-minify-errors/__fixtures__/literal/input.js new file mode 100644 index 00000000000000..63567b147abee5 --- /dev/null +++ b/packages-internal/babel-plugin-minify-errors/__fixtures__/literal/input.js @@ -0,0 +1,6 @@ +throw /* minify-error */ new Error( + 'MUI: Expected valid input target.\n' + 'Did you use `inputComponent`', +); +throw /* minify-error */ new Error( + `MUI: Expected valid input target.\n` + `Did you use \`inputComponent\``, +); diff --git a/packages-internal/babel-plugin-minify-errors/__fixtures__/literal/output.js b/packages-internal/babel-plugin-minify-errors/__fixtures__/literal/output.js new file mode 100644 index 00000000000000..e0705efb04a30e --- /dev/null +++ b/packages-internal/babel-plugin-minify-errors/__fixtures__/literal/output.js @@ -0,0 +1,11 @@ +import _formatMuiErrorMessage from '@mui/utils/formatMuiErrorMessage'; +throw new Error( + process.env.NODE_ENV !== 'production' + ? 'MUI: Expected valid input target.\n' + 'Did you use `inputComponent`' + : _formatMuiErrorMessage(1), +); +throw new Error( + process.env.NODE_ENV !== 'production' + ? `MUI: Expected valid input target.\n` + `Did you use \`inputComponent\`` + : _formatMuiErrorMessage(1), +); diff --git a/packages/mui-babel-macros/__fixtures__/factory-call/error-codes.json b/packages-internal/babel-plugin-minify-errors/__fixtures__/no-error-code-annotation/error-codes.json similarity index 100% rename from packages/mui-babel-macros/__fixtures__/factory-call/error-codes.json rename to packages-internal/babel-plugin-minify-errors/__fixtures__/no-error-code-annotation/error-codes.json diff --git a/packages-internal/babel-plugin-minify-errors/__fixtures__/no-error-code-annotation/input.js b/packages-internal/babel-plugin-minify-errors/__fixtures__/no-error-code-annotation/input.js new file mode 100644 index 00000000000000..be815c1b3427b2 --- /dev/null +++ b/packages-internal/babel-plugin-minify-errors/__fixtures__/no-error-code-annotation/input.js @@ -0,0 +1,3 @@ +throw /* minify-error */ new Error( + 'MUI: Expected valid input target.\n' + 'Did you use inputComponent', +); diff --git a/packages-internal/babel-plugin-minify-errors/__fixtures__/no-error-code-annotation/output.js b/packages-internal/babel-plugin-minify-errors/__fixtures__/no-error-code-annotation/output.js new file mode 100644 index 00000000000000..069754e40df18f --- /dev/null +++ b/packages-internal/babel-plugin-minify-errors/__fixtures__/no-error-code-annotation/output.js @@ -0,0 +1,3 @@ +throw /* FIXME (minify-errors-in-prod): Unminified error message in production build! */ new Error( + 'MUI: Expected valid input target.\n' + 'Did you use inputComponent', +); diff --git a/packages/mui-babel-macros/__fixtures__/no-error-code-annotation/error-codes.json b/packages-internal/babel-plugin-minify-errors/__fixtures__/no-error-code-throw/error-codes.json similarity index 100% rename from packages/mui-babel-macros/__fixtures__/no-error-code-annotation/error-codes.json rename to packages-internal/babel-plugin-minify-errors/__fixtures__/no-error-code-throw/error-codes.json diff --git a/packages-internal/babel-plugin-minify-errors/__fixtures__/no-error-code-throw/input.js b/packages-internal/babel-plugin-minify-errors/__fixtures__/no-error-code-throw/input.js new file mode 100644 index 00000000000000..3c8d1a793d474b --- /dev/null +++ b/packages-internal/babel-plugin-minify-errors/__fixtures__/no-error-code-throw/input.js @@ -0,0 +1 @@ +throw /* minify-error */ new Error('missing'); diff --git a/packages/mui-babel-macros/__fixtures__/no-error-code-throw/error-codes.json b/packages-internal/babel-plugin-minify-errors/__fixtures__/unminifyable-annotation/error-codes.json similarity index 100% rename from packages/mui-babel-macros/__fixtures__/no-error-code-throw/error-codes.json rename to packages-internal/babel-plugin-minify-errors/__fixtures__/unminifyable-annotation/error-codes.json diff --git a/packages-internal/babel-plugin-minify-errors/__fixtures__/unminifyable-annotation/input.js b/packages-internal/babel-plugin-minify-errors/__fixtures__/unminifyable-annotation/input.js new file mode 100644 index 00000000000000..865fba71e2aa10 --- /dev/null +++ b/packages-internal/babel-plugin-minify-errors/__fixtures__/unminifyable-annotation/input.js @@ -0,0 +1,4 @@ +const foo = 'foo'; +const bar = ['bar']; +throw /* minify-error */ new Error(foo); +throw /* minify-error */ new Error(...bar); diff --git a/packages-internal/babel-plugin-minify-errors/__fixtures__/unminifyable-annotation/output.js b/packages-internal/babel-plugin-minify-errors/__fixtures__/unminifyable-annotation/output.js new file mode 100644 index 00000000000000..0d425ce67ef0d8 --- /dev/null +++ b/packages-internal/babel-plugin-minify-errors/__fixtures__/unminifyable-annotation/output.js @@ -0,0 +1,4 @@ +const foo = 'foo'; +const bar = ['bar']; +throw /* FIXME (minify-errors-in-prod): Unminifyable error in production! */ new Error(foo); +throw /* FIXME (minify-errors-in-prod): Unminifyable error in production! */ new Error(...bar); diff --git a/packages-internal/babel-plugin-minify-errors/__fixtures__/unminifyable-throw/error-codes.json b/packages-internal/babel-plugin-minify-errors/__fixtures__/unminifyable-throw/error-codes.json new file mode 100644 index 00000000000000..0967ef424bce67 --- /dev/null +++ b/packages-internal/babel-plugin-minify-errors/__fixtures__/unminifyable-throw/error-codes.json @@ -0,0 +1 @@ +{} diff --git a/packages-internal/babel-plugin-minify-errors/__fixtures__/unminifyable-throw/input.js b/packages-internal/babel-plugin-minify-errors/__fixtures__/unminifyable-throw/input.js new file mode 100644 index 00000000000000..865fba71e2aa10 --- /dev/null +++ b/packages-internal/babel-plugin-minify-errors/__fixtures__/unminifyable-throw/input.js @@ -0,0 +1,4 @@ +const foo = 'foo'; +const bar = ['bar']; +throw /* minify-error */ new Error(foo); +throw /* minify-error */ new Error(...bar); diff --git a/packages-internal/babel-plugin-minify-errors/index.js b/packages-internal/babel-plugin-minify-errors/index.js new file mode 100644 index 00000000000000..390c70a545d0e0 --- /dev/null +++ b/packages-internal/babel-plugin-minify-errors/index.js @@ -0,0 +1,225 @@ +// @ts-check + +const helperModuleImports = require('@babel/helper-module-imports'); +const fs = require('fs'); + +const COMMENT_MARKER = 'minify-error'; + +/** + * @typedef {import('@babel/core')} babel + */ + +/** + * @typedef {{updatedErrorCodes?: boolean, formatMuiErrorMessageIdentifier?: babel.types.Identifier}} PluginState + * @typedef {'annotate' | 'throw' | 'write'} MissingError + * @typedef {{ errorCodesPath: string, missingError: MissingError }} Options + */ + +/** + * + * @param {babel.types} t + * @param {babel.types.Node} node + * @returns {{ message: string, expressions: babel.types.Expression[] } | null} + */ +function extractMessageFromExpression(t, node) { + if (t.isTemplateLiteral(node)) { + return { + message: node.quasis.map((quasi) => quasi.value.cooked).join('%s'), + expressions: node.expressions.map((expression) => { + if (t.isExpression(expression)) { + return expression; + } + throw new Error('Can only evaluate javascript template literals.'); + }), + }; + } + if (t.isStringLiteral(node)) { + return { message: node.value, expressions: [] }; + } + if (t.isBinaryExpression(node) && node.operator === '+') { + if (t.isPrivateName(node.left)) { + // This is only psossible with `in` expressions, e.g. `#foo in {}` + throw new Error('Unreachable'); + } + const left = extractMessageFromExpression(t, node.left); + const right = extractMessageFromExpression(t, node.right); + if (!left || !right) { + return null; + } + return { + message: left.message + right.message, + expressions: [...left.expressions, ...right.expressions], + }; + } + return null; +} + +/** + * + * @param {MissingError} missingError + * @param {babel.NodePath} path + * @returns + */ +function handleUnminifyable(missingError, path) { + switch (missingError) { + case 'annotate': { + // Outputs: + // /* FIXME (minify-errors-in-prod): Unminified error message in production build! */ + // throw new Error(foo) + path.addComment( + 'leading', + ' FIXME (minify-errors-in-prod): Unminifyable error in production! ', + ); + return; + } + case 'throw': { + throw new Error( + `Unminifyable error. You can only use literal strings and template strings as error messages.`, + ); + } + case 'write': { + return; + } + default: { + throw new Error(`Unknown missingError option: ${missingError}`); + } + } +} + +/** + * @param {babel} file + * @param {Options} options + * @returns {babel.PluginObj} + */ +module.exports = function plugin({ types: t }, { errorCodesPath, missingError = 'annotate' }) { + if (!errorCodesPath) { + throw new Error('errorCodesPath is required.'); + } + + const errorCodesContent = fs.readFileSync(errorCodesPath, 'utf8'); + const errorCodes = JSON.parse(errorCodesContent); + + const errorCodesLookup = new Map( + Object.entries(errorCodes).map(([key, value]) => [value, Number(key)]), + ); + + return { + visitor: { + NewExpression(newExpressionPath, state) { + if (!newExpressionPath.get('callee').isIdentifier({ name: 'Error' })) { + return; + } + + if ( + !newExpressionPath.node.leadingComments?.some((comment) => + comment.value.includes(COMMENT_MARKER), + ) + ) { + return; + } + + newExpressionPath.node.leadingComments = newExpressionPath.node.leadingComments.filter( + (comment) => !comment.value.includes(COMMENT_MARKER), + ); + + const messagePath = newExpressionPath.get('arguments')[0]; + if (!messagePath) { + return; + } + + const messageNode = messagePath.node; + if (t.isSpreadElement(messageNode) || t.isArgumentPlaceholder(messageNode)) { + handleUnminifyable(missingError, newExpressionPath); + return; + } + + const message = extractMessageFromExpression(t, messageNode); + + if (!message) { + handleUnminifyable(missingError, newExpressionPath); + return; + } + + let errorCode = errorCodesLookup.get(message.message); + if (errorCode === undefined) { + switch (missingError) { + case 'annotate': { + // Outputs: + // /* FIXME (minify-errors-in-prod): Unminified error message in production build! */ + // throw new Error(`A message with ${interpolation}`) + newExpressionPath.addComment( + 'leading', + ' FIXME (minify-errors-in-prod): Unminified error message in production build! ', + ); + return; + } + case 'throw': { + throw new Error( + `Missing error code for message '${message.message}'. Did you forget to run \`pnpm extract-error-codes\` first?`, + ); + } + case 'write': { + errorCode = errorCodesLookup.size + 1; + errorCodesLookup.set(message.message, errorCode); + state.updatedErrorCodes = true; + break; + } + default: { + throw new Error(`Unknown missingError option: ${missingError}`); + } + } + } + + if (!state.formatMuiErrorMessageIdentifier) { + // Outputs: + // import { formatMuiErrorMessage } from '@mui/utils'; + state.formatMuiErrorMessageIdentifier = helperModuleImports.addDefault( + newExpressionPath, + '@mui/utils/formatMuiErrorMessage', + { nameHint: '_formatMuiErrorMessage' }, + ); + } + + // Outputs: + // `A ${adj} message that contains ${noun}`; + const devMessage = messageNode; + + // Outputs: + // formatMuiErrorMessage(ERROR_CODE, adj, noun) + const prodMessage = t.callExpression( + t.cloneNode(state.formatMuiErrorMessageIdentifier, true), + [t.numericLiteral(errorCode), ...message.expressions], + ); + + // Outputs: + // new Error( + // process.env.NODE_ENV !== "production" + // ? `A message with ${interpolation}` + // : formatProdError('A message with %s', interpolation) + // ) + messagePath.replaceWith( + t.conditionalExpression( + t.binaryExpression( + '!==', + t.memberExpression( + t.memberExpression(t.identifier('process'), t.identifier('env')), + t.identifier('NODE_ENV'), + ), + t.stringLiteral('production'), + ), + devMessage, + prodMessage, + ), + ); + }, + }, + post() { + if (missingError === 'write' && this.updatedErrorCodes) { + const invertedErrorCodes = Object.fromEntries( + Array.from(errorCodesLookup, ([key, value]) => [value, key]), + ); + fs.writeFileSync(errorCodesPath, `${JSON.stringify(invertedErrorCodes, null, 2)}\n`); + } + }, + }; +}; diff --git a/packages/mui-babel-macros/MuiError.macro.test.js b/packages-internal/babel-plugin-minify-errors/index.test.js similarity index 62% rename from packages/mui-babel-macros/MuiError.macro.test.js rename to packages-internal/babel-plugin-minify-errors/index.test.js index cf2cbb3aee402d..4d8b36c5c68882 100644 --- a/packages/mui-babel-macros/MuiError.macro.test.js +++ b/packages-internal/babel-plugin-minify-errors/index.test.js @@ -2,8 +2,8 @@ import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; import { pluginTester } from 'babel-plugin-tester'; -import plugin from 'babel-plugin-macros'; import { expect } from 'chai'; +import plugin from './index'; const temporaryErrorCodesPath = path.join(os.tmpdir(), 'error-codes.json'); const fixturePath = path.resolve(__dirname, './__fixtures__'); @@ -22,17 +22,23 @@ pluginTester({ { title: 'literal', pluginOptions: { - muiError: { errorCodesPath: path.join(fixturePath, 'literal', 'error-codes.json') }, + errorCodesPath: path.join(fixturePath, 'literal', 'error-codes.json'), }, fixture: path.join(fixturePath, 'literal', 'input.js'), output: readOutputFixtureSync('literal', 'output.js'), }, + { + title: 'interpolation', + pluginOptions: { + errorCodesPath: path.join(fixturePath, 'interpolation', 'error-codes.json'), + }, + fixture: path.join(fixturePath, 'interpolation', 'input.js'), + output: readOutputFixtureSync('interpolation', 'output.js'), + }, { title: 'annotates missing error codes', pluginOptions: { - muiError: { - errorCodesPath: path.join(fixturePath, 'no-error-code-annotation', 'error-codes.json'), - }, + errorCodesPath: path.join(fixturePath, 'no-error-code-annotation', 'error-codes.json'), }, fixture: path.join(fixturePath, 'no-error-code-annotation', 'input.js'), output: readOutputFixtureSync('no-error-code-annotation', 'output.js'), @@ -45,10 +51,28 @@ pluginTester({ /: Missing error code for message 'missing'. Did you forget to run `pnpm extract-error-codes` first?/, fixture: path.join(fixturePath, 'no-error-code-throw', 'input.js'), pluginOptions: { - muiError: { - errorCodesPath: path.join(fixturePath, 'no-error-code-throw', 'error-codes.json'), - missingError: 'throw', - }, + errorCodesPath: path.join(fixturePath, 'no-error-code-throw', 'error-codes.json'), + missingError: 'throw', + }, + }, + { + title: 'annotates unminifyable errors', + pluginOptions: { + errorCodesPath: path.join(fixturePath, 'unminifyable-annotation', 'error-codes.json'), + }, + fixture: path.join(fixturePath, 'unminifyable-annotation', 'input.js'), + output: readOutputFixtureSync('unminifyable-annotation', 'output.js'), + }, + { + title: 'can throw on unminifyable errors', + // babel prefixes with filename. + // We're only interested in the message. + error: + /: Unminifyable error. You can only use literal strings and template strings as error messages.?/, + fixture: path.join(fixturePath, 'unminifyable-throw', 'input.js'), + pluginOptions: { + errorCodesPath: path.join(fixturePath, 'unminifyable-throw', 'error-codes.json'), + missingError: 'throw', }, }, { @@ -56,10 +80,8 @@ pluginTester({ fixture: path.join(fixturePath, 'error-code-extraction', 'input.js'), pluginOptions: { - muiError: { - errorCodesPath: temporaryErrorCodesPath, - missingError: 'write', - }, + errorCodesPath: temporaryErrorCodesPath, + missingError: 'write', }, output: readOutputFixtureSync('error-code-extraction', 'output.js'), setup() { @@ -86,16 +108,5 @@ pluginTester({ }; }, }, - { - title: 'throws if not called as a constructor', - error: - /: Encountered `MuiError` outside of a "new expression" i\.e\. `new MuiError\(\)`\. Use `throw new MuiError\(message\)` over `throw MuiError\(message\)`\./, - fixture: path.join(fixturePath, 'factory-call', 'input.js'), - pluginOptions: { - muiError: { - errorCodesPath: path.join(fixturePath, 'factory-call', 'error-codes.json'), - }, - }, - }, ], }); diff --git a/packages-internal/babel-plugin-minify-errors/package.json b/packages-internal/babel-plugin-minify-errors/package.json new file mode 100644 index 00000000000000..07fb856bb99d85 --- /dev/null +++ b/packages-internal/babel-plugin-minify-errors/package.json @@ -0,0 +1,41 @@ +{ + "name": "@mui/internal-babel-plugin-minify-errors", + "version": "1.0.9", + "author": "MUI Team", + "description": "This is an internal package not meant for general use.", + "repository": { + "type": "git", + "url": "git+https://github.com/mui/material-ui.git", + "directory": "packages/mui-babel-plugin-minify-errors" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/mui/material-ui/issues" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "scripts": { + "test": "cd ../../ && cross-env NODE_ENV=test mocha 'packages-internal/babel-plugin-minify-errors/**/*.test.{js,ts,tsx}'" + }, + "dependencies": { + "@babel/helper-module-imports": "^7.24.7" + }, + "devDependencies": { + "@types/babel__helper-module-imports": "^7.18.3", + "babel-plugin-tester": "^11.0.4", + "chai": "^4.5.0" + }, + "sideEffects": false, + "type": "commonjs", + "exports": { + ".": "./index.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages-internal/docs-utils/package.json b/packages-internal/docs-utils/package.json index 55556989ffbc05..0c012628693912 100644 --- a/packages-internal/docs-utils/package.json +++ b/packages-internal/docs-utils/package.json @@ -1,6 +1,6 @@ { "name": "@mui/internal-docs-utils", - "version": "1.0.13", + "version": "1.0.14", "author": "MUI Team", "description": "Utilities for MUI docs. This is an internal package not meant for general use.", "main": "./build/index.js", diff --git a/packages-internal/scripts/package.json b/packages-internal/scripts/package.json index 7fa83a6d8a310c..e7ca9ace747201 100644 --- a/packages-internal/scripts/package.json +++ b/packages-internal/scripts/package.json @@ -1,6 +1,6 @@ { "name": "@mui/internal-scripts", - "version": "1.0.21", + "version": "1.0.22", "author": "MUI Team", "description": "Utilities supporting MUI libraries build and docs generation. This is an internal package not meant for general use.", "main": "build/index.js", @@ -41,9 +41,9 @@ "@types/babel__core": "^7.20.5", "@types/chai": "^4.3.20", "@types/doctrine": "^0.0.9", - "@types/lodash": "^4.17.9", - "@types/node": "^20.16.5", - "@types/react": "^18.3.4", + "@types/lodash": "^4.17.10", + "@types/node": "^20.16.10", + "@types/react": "^18.3.11", "@types/uuid": "^9.0.8", "chai": "^4.5.0", "fast-glob": "^3.3.2", diff --git a/packages-internal/test-utils/package.json b/packages-internal/test-utils/package.json index cfd10b49d2523d..d78d3f0e3b3431 100644 --- a/packages-internal/test-utils/package.json +++ b/packages-internal/test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@mui/internal-test-utils", - "version": "1.0.14", + "version": "1.0.15", "author": "MUI Team", "description": "Utilities for MUI tests. This is an internal package not meant for general use.", "main": "./build/index.js", @@ -57,7 +57,7 @@ "@types/chai-dom": "^1.11.3", "@types/format-util": "^1.0.4", "@types/prop-types": "^15.7.13", - "@types/react": "^18.3.4", + "@types/react": "^18.3.11", "@types/react-dom": "^18.3.0", "@types/sinon": "^17.0.3", "typescript": "^5.6.2" diff --git a/packages-internal/test-utils/src/createRenderer.tsx b/packages-internal/test-utils/src/createRenderer.tsx index f7107db5c1afe8..6943b10d1d9967 100644 --- a/packages-internal/test-utils/src/createRenderer.tsx +++ b/packages-internal/test-utils/src/createRenderer.tsx @@ -285,8 +285,12 @@ export interface MuiRenderToStringResult { hydrate(): MuiRenderResult; } +interface DataAttributes { + [key: `data-${string}`]: string; +} + function render( - element: React.ReactElement, + element: React.ReactElement, configuration: ClientRenderConfiguration, ): MuiRenderResult { const { container, hydrate, wrapper } = configuration; @@ -322,7 +326,7 @@ function render( } function renderToString( - element: React.ReactElement, + element: React.ReactElement, configuration: ServerRenderConfiguration, ): { container: HTMLElement; hydrate(): MuiRenderResult } { const { container, wrapper: Wrapper } = configuration; @@ -449,9 +453,9 @@ function createClock( export interface Renderer { clock: Clock; - render(element: React.ReactElement, options?: RenderOptions): MuiRenderResult; + render(element: React.ReactElement, options?: RenderOptions): MuiRenderResult; renderToString( - element: React.ReactElement, + element: React.ReactElement, options?: RenderOptions, ): MuiRenderToStringResult; } @@ -545,7 +549,7 @@ export function createRenderer(globalOptions: CreateRendererOptions = {}): Rende afterEach(() => { if (!clock.isReal()) { - const error = Error( + const error = new Error( "Can't cleanup before fake timers are restored.\n" + 'Be sure to:\n' + ' 1. Only use `clock` from `createRenderer`.\n' + @@ -593,7 +597,7 @@ export function createRenderer(globalOptions: CreateRendererOptions = {}): Rende return { clock, - render(element: React.ReactElement, options: RenderOptions = {}) { + render(element: React.ReactElement, options: RenderOptions = {}) { if (!prepared) { throw new Error( 'Unable to finish setup before `render()` was called. ' + @@ -608,7 +612,7 @@ export function createRenderer(globalOptions: CreateRendererOptions = {}): Rende wrapper: createWrapper(options), }); }, - renderToString(element: React.ReactElement, options: RenderOptions = {}) { + renderToString(element: React.ReactElement, options: RenderOptions = {}) { if (!prepared) { throw new Error( 'Unable to finish setup before `render()` was called. ' + diff --git a/packages/api-docs-builder-core/package.json b/packages/api-docs-builder-core/package.json index 6478a5699b4829..beef83fed5329f 100644 --- a/packages/api-docs-builder-core/package.json +++ b/packages/api-docs-builder-core/package.json @@ -17,7 +17,7 @@ "devDependencies": { "@types/chai": "^4.3.20", "@types/mocha": "^10.0.8", - "@types/node": "^20.16.5", + "@types/node": "^20.16.10", "@types/sinon": "^17.0.3", "chai": "^4.5.0", "sinon": "^18.0.1", diff --git a/packages/api-docs-builder/package.json b/packages/api-docs-builder/package.json index d12a42d036b3d1..f08c4a1404e17e 100644 --- a/packages/api-docs-builder/package.json +++ b/packages/api-docs-builder/package.json @@ -32,7 +32,7 @@ "@types/doctrine": "^0.0.9", "@types/mdast": "4.0.4", "@types/mocha": "^10.0.8", - "@types/node": "^20.16.5", + "@types/node": "^20.16.10", "@types/react-docgen": "workspace:*", "@types/sinon": "^17.0.3", "chai": "^4.5.0", diff --git a/packages/eslint-plugin-material-ui/src/rules/disallow-active-elements-as-key-event-target.test.js b/packages/eslint-plugin-material-ui/src/rules/disallow-active-elements-as-key-event-target.test.js index 6e90f6c1898800..8ac6281bbea7a9 100644 --- a/packages/eslint-plugin-material-ui/src/rules/disallow-active-elements-as-key-event-target.test.js +++ b/packages/eslint-plugin-material-ui/src/rules/disallow-active-elements-as-key-event-target.test.js @@ -8,7 +8,7 @@ const ruleTester = new eslint.RuleTester({ ruleTester.run('disallow-active-element-as-key-event-target', rule, { valid: [ "import { fireEvent } from '@mui/internal-test-utils';\nfireEvent.keyDown(getByRole('button'), { key: ' ' })", - "import { fireEvent } from '@mui/internal-test-utils';\nfireEvent.keyDown(document.body, { key: 'Esc' })", + "import { fireEvent } from '@mui/internal-test-utils';\nfireEvent.keyDown(document.body, { key: 'Escape' })", "import { fireEvent } from '@mui/internal-test-utils';\nfireEvent.keyUp(document.body, { key: 'Tab' })", ], invalid: [ diff --git a/packages/markdown/package.json b/packages/markdown/package.json index c87b48072aedfd..95a11c87260ac8 100644 --- a/packages/markdown/package.json +++ b/packages/markdown/package.json @@ -1,6 +1,6 @@ { "name": "@mui/internal-markdown", - "version": "1.0.14", + "version": "1.0.15", "author": "MUI Team", "description": "MUI markdown parser. This is an internal package not meant for general use.", "main": "./index.js", diff --git a/packages/markdown/parseMarkdown.js b/packages/markdown/parseMarkdown.js index c0b04c9cf08b1d..14d33f2eaba200 100644 --- a/packages/markdown/parseMarkdown.js +++ b/packages/markdown/parseMarkdown.js @@ -369,8 +369,8 @@ function createRender(context) { headingHtml.includes('${headingHtml}`, - ``, + `${headingHtml}`, + ``, ].join('') : `${headingHtml}`, `