diff --git a/.codesandbox/ci.json b/.codesandbox/ci.json index 71283d06fdff0c..57e18e57f08442 100644 --- a/.codesandbox/ci.json +++ b/.codesandbox/ci.json @@ -12,7 +12,6 @@ "packages/mui-icons-material", "packages/mui-joy", "packages/mui-lab", - "packages/mui-material-next", "packages/mui-material-nextjs", "packages/mui-material", "packages/mui-private-theming", @@ -37,7 +36,6 @@ "@mui/internal-scripts": "packages-internal/scripts", "@mui/joy": "packages/mui-joy/build", "@mui/lab": "packages/mui-lab/build", - "@mui/material-next": "packages/mui-material-next/build", "@mui/material-nextjs": "packages/mui-material-nextjs/build", "@mui/material": "packages/mui-material/build", "@mui/private-theming": "packages/mui-private-theming/build", diff --git a/babel.config.js b/babel.config.js index 9b3ca62593f763..48ca2abea4ce50 100644 --- a/babel.config.js +++ b/babel.config.js @@ -30,7 +30,6 @@ module.exports = function getBabelConfig(api) { '@mui/private-theming': resolveAliasPath('./packages/mui-private-theming/src'), '@mui/base': resolveAliasPath('./packages/mui-base/src'), '@mui/utils': resolveAliasPath('./packages/mui-utils/src'), - '@mui/material-next': resolveAliasPath('./packages/mui-material-next/src'), '@mui/joy': resolveAliasPath('./packages/mui-joy/src'), '@pigment-css/react': resolveAliasPath('./packages/pigment-css-react/src'), '@mui/internal-docs-utils': resolveAliasPath('./packages-internal/docs-utils/src'), diff --git a/docs/data/material/components/badges/BadgeMaterialYouPlayground.js b/docs/data/material/components/badges/BadgeMaterialYouPlayground.js deleted file mode 100644 index 08edf42bd77ff3..00000000000000 --- a/docs/data/material/components/badges/BadgeMaterialYouPlayground.js +++ /dev/null @@ -1,48 +0,0 @@ -import * as React from 'react'; -import Badge from '@mui/material-next/Badge'; -import MailIcon from '@mui/icons-material/Mail'; -import MaterialYouUsageDemo from 'docs/src/modules/components/MaterialYouUsageDemo'; - -export default function BadgeMaterialYouPlayground() { - return ( - ( - - - - )} - /> - ); -} diff --git a/docs/data/material/components/badges/badges.md b/docs/data/material/components/badges/badges.md index c92c9b90b995b4..143dd849cd3bea 100644 --- a/docs/data/material/components/badges/badges.md +++ b/docs/data/material/components/badges/badges.md @@ -71,18 +71,3 @@ You can't rely on the content of the badge to be announced correctly. You should provide a full description, for instance, with `aria-label`: {{"demo": "AccessibleBadges.js"}} - -## Experimental APIs - -### Material Design 3 - -The default Material UI Badge component follows the Material Design 2 specs. -To use the [M3](https://m3.material.io/) version, install the experimental `@mui/material-next` package. - -```js -import Badge from '@mui/material-next/Badge'; -``` - -{{"demo": "BadgeMaterialYouPlayground.js", "hideToolbar": true, "bg": "playground"}} - -To learn more about Material UI's M3 implementation, visit the [M3 Components documentation](/material-ui/guides/material-3-components/). diff --git a/docs/data/material/components/button-group/ButtonGroupMaterialYouPlayground.js b/docs/data/material/components/button-group/ButtonGroupMaterialYouPlayground.js deleted file mode 100644 index 46b92778394528..00000000000000 --- a/docs/data/material/components/button-group/ButtonGroupMaterialYouPlayground.js +++ /dev/null @@ -1,51 +0,0 @@ -import * as React from 'react'; -import Button from '@mui/material-next/Button'; -import ButtonGroup from '@mui/material-next/ButtonGroup'; -import FavoriteBorder from '@mui/icons-material/FavoriteBorder'; -import MaterialYouUsageDemo from 'docs/src/modules/components/MaterialYouUsageDemo'; - -export default function ButtonGroupMaterialYouPlayground() { - return ( - ( - - - - - - )} - /> - ); -} diff --git a/docs/data/material/components/button-group/button-group.md b/docs/data/material/components/button-group/button-group.md index 73f026b74dd4dd..a0645298d04bb6 100644 --- a/docs/data/material/components/button-group/button-group.md +++ b/docs/data/material/components/button-group/button-group.md @@ -55,16 +55,3 @@ You can remove the elevation with the `disableElevation` prop. You can use the [``](/material-ui/react-button/#loading-button) from [`@mui/lab`](/material-ui/about-the-lab/) in the button group. {{"demo": "LoadingButtonGroup.js"}} - -### Material Design 3 - -The default Material UI Button Group component follows the Material Design 2 specs. -To use the [M3](https://m3.material.io/) version, install the experimental `@mui/material-next` package. - -```js -import ButtonGroup from '@mui/material-next/ButtonGroup'; -``` - -{{"demo": "ButtonGroupMaterialYouPlayground.js", "hideToolbar": true, "bg": "playground"}} - -To learn more about Material UI's M3 implementation, visit the [M3 Components documentation](/material-ui/guides/material-3-components/). diff --git a/docs/data/material/components/buttons/ButtonMaterialYouPlayground.js b/docs/data/material/components/buttons/ButtonMaterialYouPlayground.js deleted file mode 100644 index c3de24f10e9a5e..00000000000000 --- a/docs/data/material/components/buttons/ButtonMaterialYouPlayground.js +++ /dev/null @@ -1,52 +0,0 @@ -import * as React from 'react'; -import Box from '@mui/material/Box'; -import Button from '@mui/material-next/Button'; -import FavoriteBorder from '@mui/icons-material/FavoriteBorder'; -import MaterialYouUsageDemo from 'docs/src/modules/components/MaterialYouUsageDemo'; - -export default function ButtonMaterialYouPlayground() { - const [variant, setVariant] = React.useState('text'); - return ( - setVariant(e.target.value), - }, - ...(variant === 'filled' || variant === 'text' || variant === 'outlined' - ? [ - { - propName: 'color', - knob: 'select', - defaultValue: 'primary', - options: ['primary', 'secondary', 'tertiary'], - }, - ] - : []), - { - propName: 'size', - knob: 'select', - options: ['small', 'medium', 'large'], - defaultValue: 'medium', - }, - { - propName: 'disabled', - knob: 'switch', - defaultValue: false, - }, - ]} - renderDemo={(props) => ( - - - - - )} - /> - ); -} diff --git a/docs/data/material/components/buttons/buttons.md b/docs/data/material/components/buttons/buttons.md index 35089bd278cab9..247609d8aa46e9 100644 --- a/docs/data/material/components/buttons/buttons.md +++ b/docs/data/material/components/buttons/buttons.md @@ -196,18 +196,3 @@ To prevent this, ensure that the contents of the Loading Button are nested insid Submit ``` - -::: - -### Material Design 3 - -The default Material UI Button component follows the Material Design 2 specs. -To use the [M3](https://m3.material.io/) version, install the experimental `@mui/material-next` package. - -```js -import Button from '@mui/material-next/Button'; -``` - -{{"demo": "ButtonMaterialYouPlayground.js", "hideToolbar": true, "bg": "playground"}} - -To learn more about Material UI's M3 implementation, visit the [M3 Components documentation](/material-ui/guides/material-3-components/). diff --git a/docs/data/material/components/chips/ChipMaterialYouPlayground.js b/docs/data/material/components/chips/ChipMaterialYouPlayground.js deleted file mode 100644 index 6da30fac769676..00000000000000 --- a/docs/data/material/components/chips/ChipMaterialYouPlayground.js +++ /dev/null @@ -1,59 +0,0 @@ -import * as React from 'react'; -import Box from '@mui/material/Box'; -import Chip from '@mui/material-next/Chip'; -import MaterialYouUsageDemo from 'docs/src/modules/components/MaterialYouUsageDemo'; - -export default function ChipMaterialYouPlayground() { - return ( - ( - - - alert('Clicked M3 Chip')} - /> - alert('Deleted M3 Chip')} - /> - - )} - /> - ); -} diff --git a/docs/data/material/components/chips/chips.md b/docs/data/material/components/chips/chips.md index d3e3ec3947c82b..65063426ca27a8 100644 --- a/docs/data/material/components/chips/chips.md +++ b/docs/data/material/components/chips/chips.md @@ -97,21 +97,6 @@ gain depth while clicked or touched. {{"demo": "ChipsPlayground.js", "hideToolbar": true}} -## Experimental API - -### Material Design 3 - -The default Material UI Chip component follows the Material Design 2 specs. -To use the [M3](https://m3.material.io/) version, install the experimental `@mui/material-next` package. - -```js -import Chip from '@mui/material-next/Chip'; -``` - -{{"demo": "ChipMaterialYouPlayground.js", "hideToolbar": true, "bg": "playground"}} - -To learn more about Material UI's M3 implementation, visit the [M3 Components documentation](/material-ui/guides/material-3-components/). - ## Accessibility If the Chip is deletable or clickable then it is a button in tab order. When the Chip is focused (for example when tabbing) releasing (`keyup` event) `Backspace` or `Delete` will call the `onDelete` handler while releasing `Escape` will blur the Chip. diff --git a/docs/data/material/components/dividers/DividerMaterialYouPlayground.js b/docs/data/material/components/dividers/DividerMaterialYouPlayground.js deleted file mode 100644 index 06d2148815bb43..00000000000000 --- a/docs/data/material/components/dividers/DividerMaterialYouPlayground.js +++ /dev/null @@ -1,80 +0,0 @@ -import * as React from 'react'; -import Divider from '@mui/material-next/Divider'; -import MaterialYouUsageDemo from 'docs/src/modules/components/MaterialYouUsageDemo'; -import { - Avatar, - Box, - List, - ListItem, - ListItemAvatar, - ListItemText, -} from '@mui/material'; -import ImageIcon from '@mui/icons-material/Image'; -import WorkIcon from '@mui/icons-material/Work'; - -const listStyle = { - p: 0, - width: 300, - borderRadius: 2, - border: '1px solid', - borderColor: 'divider', - backgroundColor: 'background.paper', -}; - -export default function DividerMaterialYouPlayground() { - return ( - { - const content = ( -

- Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus id - dignissim justo. Nulla ut facilisis ligula. -

- ); - - return props.orientation === 'horizontal' ? ( - - - - - - - - - - - - - - - - - - - - ) : ( - - {content} - - {content} - - ); - }} - /> - ); -} diff --git a/docs/data/material/components/dividers/dividers.md b/docs/data/material/components/dividers/dividers.md index ec36d7a1c12296..f04e1470606b84 100644 --- a/docs/data/material/components/dividers/dividers.md +++ b/docs/data/material/components/dividers/dividers.md @@ -90,18 +90,3 @@ The Divider component is composed of a root `
`. ``` - -## Experimental APIs - -### Material Design 3 - -The default Material UI Divider component follows the Material Design 2 specs. -To use the [M3](https://m3.material.io/) version, install the experimental `@mui/material-next` package. - -```js -import Divider from '@mui/material-next/Divider'; -``` - -{{"demo": "DividerMaterialYouPlayground.js", "hideToolbar": true, "bg": "playground"}} - -To learn more about Material UI's M3 implementation, visit the [M3 Components documentation](/material-ui/guides/material-3-components/). diff --git a/docs/data/material/components/progress/ProgressMaterialYouPlayground.js b/docs/data/material/components/progress/ProgressMaterialYouPlayground.js deleted file mode 100644 index d29e2e3ee0f6b3..00000000000000 --- a/docs/data/material/components/progress/ProgressMaterialYouPlayground.js +++ /dev/null @@ -1,74 +0,0 @@ -import * as React from 'react'; -import CircularProgress from '@mui/material-next/CircularProgress'; -import LinearProgress from '@mui/material-next/LinearProgress'; -import Box from '@mui/material/Box'; -import MaterialYouUsageDemo from 'docs/src/modules/components/MaterialYouUsageDemo'; - -export default function ProgressMaterialYouPlayground() { - const [value, setValue] = React.useState(0); - const [valueBuffer, setValueBuffer] = React.useState(0); - const [type, setType] = React.useState('CircularProgress'); - React.useEffect(() => { - const interval = setInterval(() => { - setValue((val) => (val >= 100 ? 0 : val + 10)); - setValueBuffer(Math.floor(Math.random() * 10)); - }, 1000); - return () => { - clearInterval(interval); - }; - }, []); - return ( - setType(e.target.value), - }, - { - propName: 'variant', - knob: 'select', - options: [ - 'indeterminate', - 'determinate', - ...(type === 'LinearProgress' ? ['buffer', 'query'] : []), - ], - defaultValue: 'indeterminate', - }, - { - propName: 'color', - knob: 'select', - options: ['primary', 'secondary', 'tertiary'], - defaultValue: 'primary', - }, - { propName: 'fourColor', knob: 'switch', defaultValue: false }, - ]} - renderDemo={({ type: progressType, ...props }) => ( - - {type === 'CircularProgress' ? ( - - ) : ( - - )} - - )} - /> - ); -} diff --git a/docs/data/material/components/progress/progress.md b/docs/data/material/components/progress/progress.md index ce23277fddb346..45deb2642dd285 100644 --- a/docs/data/material/components/progress/progress.md +++ b/docs/data/material/components/progress/progress.md @@ -150,19 +150,3 @@ You can solve the latter with: } } ``` - -## Experimental APIs - -### Material Design 3 - -The default Material UI Progress components follow the Material Design 2 specs. -To use the [M3](https://m3.material.io/) version, install the experimental `@mui/material-next` package. - -```js -import CircularProgress from '@mui/material-next/CircularProgress'; -import LinearProgress from '@mui/material-next/LinearProgress'; -``` - -{{"demo": "ProgressMaterialYouPlayground.js", "hideToolbar": true, "bg": "playground"}} - -To learn more about Material UI's M3 implementation, visit the [M3 Components documentation](/material-ui/guides/material-3-components/). diff --git a/docs/data/material/components/slider/SliderMaterialYouPlayground.js b/docs/data/material/components/slider/SliderMaterialYouPlayground.js deleted file mode 100644 index 7755d9f82a193d..00000000000000 --- a/docs/data/material/components/slider/SliderMaterialYouPlayground.js +++ /dev/null @@ -1,49 +0,0 @@ -import * as React from 'react'; -import Slider from '@mui/material-next/Slider'; -import Box from '@mui/material/Box'; -import MaterialYouUsageDemo from 'docs/src/modules/components/MaterialYouUsageDemo'; - -export default function SliderMaterialYouPlayground() { - return ( - ( - - Hello world - - )} - /> - ); -} diff --git a/docs/data/material/components/slider/slider.md b/docs/data/material/components/slider/slider.md index dae86fd2c14743..2420303b5989cb 100644 --- a/docs/data/material/components/slider/slider.md +++ b/docs/data/material/components/slider/slider.md @@ -165,18 +165,3 @@ You can solve the issue with: left: calc(-50% - 4px); } ``` - -## Experimental APIs - -### Material Design 3 - -The default Material UI Slider component follows the Material Design 2 specs. -To use the [M3](https://m3.material.io/) version, install the experimental `@mui/material-next` package. - -```js -import Slider from '@mui/material-next/Slider'; -``` - -{{"demo": "SliderMaterialYouPlayground.js", "hideToolbar": true, "bg": "playground"}} - -To learn more about Material UI's M3 implementation, visit the [M3 Components documentation](/material-ui/guides/material-3-components/). diff --git a/docs/data/material/guides/material-3-components/MD3AndV5Usage.js b/docs/data/material/guides/material-3-components/MD3AndV5Usage.js deleted file mode 100644 index ad1859b3fe3126..00000000000000 --- a/docs/data/material/guides/material-3-components/MD3AndV5Usage.js +++ /dev/null @@ -1,22 +0,0 @@ -import * as React from 'react'; -import MD2Button from '@mui/material/Button'; -import { ThemeProvider, createTheme } from '@mui/material/styles'; -import MD3Button from '@mui/material-next/Button'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -import { Stack } from '@mui/system'; - -const md2Theme = createTheme(); -const md3Theme = extendTheme(); - -export default function MD3AndV5Usage() { - return ( - - - MD2 Button - - MD3 Button - - - - ); -} diff --git a/docs/data/material/guides/material-3-components/MD3AndV5Usage.tsx b/docs/data/material/guides/material-3-components/MD3AndV5Usage.tsx deleted file mode 100644 index ad1859b3fe3126..00000000000000 --- a/docs/data/material/guides/material-3-components/MD3AndV5Usage.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import * as React from 'react'; -import MD2Button from '@mui/material/Button'; -import { ThemeProvider, createTheme } from '@mui/material/styles'; -import MD3Button from '@mui/material-next/Button'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -import { Stack } from '@mui/system'; - -const md2Theme = createTheme(); -const md3Theme = extendTheme(); - -export default function MD3AndV5Usage() { - return ( - - - MD2 Button - - MD3 Button - - - - ); -} diff --git a/docs/data/material/guides/material-3-components/MD3AndV5Usage.tsx.preview b/docs/data/material/guides/material-3-components/MD3AndV5Usage.tsx.preview deleted file mode 100644 index 0d31189e938a00..00000000000000 --- a/docs/data/material/guides/material-3-components/MD3AndV5Usage.tsx.preview +++ /dev/null @@ -1,8 +0,0 @@ - - - MD2 Button - - MD3 Button - - - \ No newline at end of file diff --git a/docs/data/material/guides/material-3-components/MD3ButtonUsage.js b/docs/data/material/guides/material-3-components/MD3ButtonUsage.js deleted file mode 100644 index f4154f9ace96f7..00000000000000 --- a/docs/data/material/guides/material-3-components/MD3ButtonUsage.js +++ /dev/null @@ -1,11 +0,0 @@ -import * as React from 'react'; -import Button from '@mui/material-next/Button'; -import { CssVarsProvider } from '@mui/material-next/styles'; - -export default function MD3ButtonUsage() { - return ( - - - - ); -} diff --git a/docs/data/material/guides/material-3-components/MD3ButtonUsage.tsx b/docs/data/material/guides/material-3-components/MD3ButtonUsage.tsx deleted file mode 100644 index f4154f9ace96f7..00000000000000 --- a/docs/data/material/guides/material-3-components/MD3ButtonUsage.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import * as React from 'react'; -import Button from '@mui/material-next/Button'; -import { CssVarsProvider } from '@mui/material-next/styles'; - -export default function MD3ButtonUsage() { - return ( - - - - ); -} diff --git a/docs/data/material/guides/material-3-components/MD3ButtonUsage.tsx.preview b/docs/data/material/guides/material-3-components/MD3ButtonUsage.tsx.preview deleted file mode 100644 index 0b4230ecf84025..00000000000000 --- a/docs/data/material/guides/material-3-components/MD3ButtonUsage.tsx.preview +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/docs/data/material/guides/material-3-components/MD3Theming.js b/docs/data/material/guides/material-3-components/MD3Theming.js deleted file mode 100644 index 1b228594ee281f..00000000000000 --- a/docs/data/material/guides/material-3-components/MD3Theming.js +++ /dev/null @@ -1,38 +0,0 @@ -import * as React from 'react'; -import Button from '@mui/material-next/Button'; -import { extendTheme, CssVarsProvider } from '@mui/material-next/styles'; - -const customTheme = extendTheme({ - ref: { - palette: { - primary: { - 0: '#000000', - 10: '#002020', - 20: '#003738', - 30: '#004F51', - 40: '#00696B', - 50: '#008587', - 60: '#00A1A3', - 70: '#00BEC1', - 80: '#2DDBDE', - 90: '#8FF3F4', - 95: '#B2FEFF', - 99: '#F1FFFF', - 100: '#FFFFFF', - }, - }, - }, - // cssVarPrefix is only required if multiple themes coexist - // on the same page like on this guide - cssVarPrefix: 'turquoise-md3', -}); - -export default function MD3Theming() { - return ( - - - - ); -} diff --git a/docs/data/material/guides/material-3-components/MD3Theming.tsx b/docs/data/material/guides/material-3-components/MD3Theming.tsx deleted file mode 100644 index 1b228594ee281f..00000000000000 --- a/docs/data/material/guides/material-3-components/MD3Theming.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import * as React from 'react'; -import Button from '@mui/material-next/Button'; -import { extendTheme, CssVarsProvider } from '@mui/material-next/styles'; - -const customTheme = extendTheme({ - ref: { - palette: { - primary: { - 0: '#000000', - 10: '#002020', - 20: '#003738', - 30: '#004F51', - 40: '#00696B', - 50: '#008587', - 60: '#00A1A3', - 70: '#00BEC1', - 80: '#2DDBDE', - 90: '#8FF3F4', - 95: '#B2FEFF', - 99: '#F1FFFF', - 100: '#FFFFFF', - }, - }, - }, - // cssVarPrefix is only required if multiple themes coexist - // on the same page like on this guide - cssVarPrefix: 'turquoise-md3', -}); - -export default function MD3Theming() { - return ( - - - - ); -} diff --git a/docs/data/material/guides/material-3-components/MD3Theming.tsx.preview b/docs/data/material/guides/material-3-components/MD3Theming.tsx.preview deleted file mode 100644 index 610c1ac7a22b56..00000000000000 --- a/docs/data/material/guides/material-3-components/MD3Theming.tsx.preview +++ /dev/null @@ -1,5 +0,0 @@ - - - \ No newline at end of file diff --git a/docs/data/material/guides/material-3-components/material-3-components.md b/docs/data/material/guides/material-3-components/material-3-components.md deleted file mode 100644 index 18959de10999aa..00000000000000 --- a/docs/data/material/guides/material-3-components/material-3-components.md +++ /dev/null @@ -1,133 +0,0 @@ -# Material Design 3 Components - -

Try out Material UI's implementation of M3 and learn how to contribute to the project.

- -## Material UI and M3 - -Material Design 3 (M3), also referred to as [Material You](https://m3.material.io), is the latest version of Google's design system. -The primary Material UI package (`@mui/material`) currently implements Material Design 2. -M3 implementation is a work in progress, targeted for completion in late 2024. -You can try out Material UI's M3 components as they're developed using the `@mui/material-next` package. - -:::warning -The M3 components are currently in alpha and subject to change. -::: - -## Supported components - -Visit the [All Components page](/material-ui/all-components/) to see which components support M3—look for the green M3 indicator. -All components that have M3 versions have a corresponding playground on their page. -For example, here's the [M3 Button playground](/material-ui/react-button/#material-design-3). - -## Getting started with M3 components - -The M3 components are included in the `@mui/material-next` package. -The following guide explains how to get started using them. - -### Installation - -Run one of the following commands to add `@mui/material-next` to your project: - - - -```bash npm -npm install @mui/material-next @emotion/react @emotion/styled -``` - -```bash yarn -yarn add @mui/material-next @emotion/react @emotion/styled -``` - -```bash pnpm -pnpm add @mui/material-next @emotion/react @emotion/styled -``` - - - -#### Peer dependencies - - - -Please note that [react](https://www.npmjs.com/package/react) and [react-dom](https://www.npmjs.com/package/react-dom) are peer dependencies, meaning you should ensure they are installed before installing the Material UI Next package. - -```json -"peerDependencies": { - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" -}, -``` - -#### Roboto font - -Material UI uses the [Roboto](https://fonts.google.com/specimen/Roboto) font by default. -Add it to your project via Fontsource, or with the Google Fonts CDN. - - - -```bash npm -npm install @fontsource/roboto -``` - -```bash yarn -yarn add @fontsource/roboto -``` - -```bash pnpm -pnpm add @fontsource/roboto -``` - - - -Then you can import it in your entry point like this: - -```tsx -import '@fontsource/roboto/300.css'; -import '@fontsource/roboto/400.css'; -import '@fontsource/roboto/500.css'; -import '@fontsource/roboto/700.css'; -``` - -:::info -Fontsource can be configured to load specific subsets, weights and styles. Material UI's default typography configuration relies only on the 300, 400, 500, and 700 font weights. -::: - -To install Roboto through the Google Web Fonts CDN, add the following code inside your project's tag: - -```html - - - -``` - -### Usage - -After [installation](/material-ui/guides/material-3-components/#installation), you can import any component from `@mui/material-next` just as you would do with the stable Material UI package. - -{{"demo": "MD3ButtonUsage.js"}} - -:::warning -If your application uses the `ThemeProvider` from `@mui/material`, you must include `CssVarsProvider` from `@mui/material-next` in the tree above the M3 components. -The following example shows how to do this. -::: - -{{"demo": "MD3AndV5Usage.js", "defaultCodeOpen": false}} - -### Theming - -Use the `extendTheme` function to modify the default theme. -The theme structure follows [M3 specifications](https://m3.material.io/styles/color/system/overview). -For example, if you wanted to modify the primary color, you would provide the [color tones](https://m3.material.io/styles/color/system/how-the-system-works#e1e92a3b-8702-46b6-8132-58321aa600bd) via `ref.palette.primary`: - -{{"demo": "MD3Theming.js"}} - -:::success -You can use Material Design's [Figma Material Theme Builder](https://www.figma.com/community/plugin/1034969338659738588/material-theme-builder) plugin to generate palette tones. -::: - -## Stable release - -The stable release of the M3 components is tentatively targeted for Q4 2024 in Material UI v7. -To follow the progress or contribute to the project, check out [the M3 GitHub issue](https://github.com/mui/material-ui/issues/29345). diff --git a/docs/next.config.mjs b/docs/next.config.mjs index a0ec4b89d6df8c..af21536fa13b78 100644 --- a/docs/next.config.mjs +++ b/docs/next.config.mjs @@ -112,7 +112,6 @@ export default withDocsInfra({ '@mui/private-theming': path.resolve(workspaceRoot, 'packages/mui-private-theming/src'), '@mui/utils': path.resolve(workspaceRoot, 'packages/mui-utils/src'), '@mui/base': path.resolve(workspaceRoot, 'packages/mui-base/src'), - '@mui/material-next': path.resolve(workspaceRoot, 'packages/mui-material-next/src'), '@mui/material-nextjs': path.resolve(workspaceRoot, 'packages/mui-material-nextjs/src'), '@mui/joy': path.resolve(workspaceRoot, 'packages/mui-joy/src'), }, diff --git a/docs/package.json b/docs/package.json index 9a4ffd81fe41cd..ec0bb7a25352b9 100644 --- a/docs/package.json +++ b/docs/package.json @@ -38,7 +38,6 @@ "@mui/lab": "workspace:*", "@mui/internal-markdown": "workspace:^", "@mui/material": "workspace:^", - "@mui/material-next": "workspace:*", "@mui/styled-engine": "workspace:^", "@mui/styled-engine-sc": "workspace:^", "@mui/styles": "workspace:^", diff --git a/docs/pages/blog/2023-material-ui-v6-and-beyond.md b/docs/pages/blog/2023-material-ui-v6-and-beyond.md index b03c4efa309ef5..24bebf7f41ee2f 100644 --- a/docs/pages/blog/2023-material-ui-v6-and-beyond.md +++ b/docs/pages/blog/2023-material-ui-v6-and-beyond.md @@ -40,14 +40,6 @@ It's tentatively planned for Q4 of 2024. Side-to-side comparison of a Card component using Material Design 2 and 3, respectively. -The development for this version is already in progress, though! -See which components already support the M3 specs, through the experimental `@mui/material-next` package, by visiting the newly released [All Components page](/material-ui/all-components/). - -```diff --import Button from '@mui/material/Button'; -+import Button from '@mui/material-next/Button'; -``` - ## From design to development In addition to the updates to the React library, we've also been working on a long-requested Figma plug-in to help bridge the gap between designers and developers using Material UI. diff --git a/docs/pages/experiments/material-next/menu.tsx b/docs/pages/experiments/material-next/menu.tsx deleted file mode 100644 index edc8eb85162937..00000000000000 --- a/docs/pages/experiments/material-next/menu.tsx +++ /dev/null @@ -1,339 +0,0 @@ -import * as React from 'react'; -import { ThemeProvider, createTheme } from '@mui/material/styles'; -import Button from '@mui/material/Button'; -import Divider from '@mui/material/Divider'; -import { useDropdown, DropdownContext } from '@mui/base/useDropdown'; -import Menu, { MenuProps } from '@mui/material-next/Menu'; -import MenuItem from '@mui/material-next/MenuItem'; -import StableMenu from '@mui/material/Menu'; -import StableMenuItem from '@mui/material/MenuItem'; -import StableList from '@mui/material/List'; -import StableListItem from '@mui/material/ListItem'; -import StableListItemText from '@mui/material/ListItemText'; -import { useMenuButton } from '@mui/base/useMenuButton'; - -const theme = createTheme(); - -const MenuButton = React.forwardRef>( - function MenuButton( - props: React.PropsWithChildren<{}>, - forwardedRef: React.ForwardedRef, - ) { - const { getRootProps: getButtonProps } = useMenuButton({ rootRef: forwardedRef }); - - return - - Profile - My account - Subscription - - Logout - - - ); -} - -function StableComponentUsage(props: { - MenuListProps?: { disabledItemsFocusable?: boolean }; - autoFocus?: boolean; -}) { - const [anchorEl, setAnchorEl] = React.useState(null); - const open = Boolean(anchorEl); - const handleClick = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); - }; - const handleClose = () => { - setAnchorEl(null); - }; - - return ( -
- - - Profile - My account - Subscription - - Logout - -
- ); -} - -const selectedMenuOptions = [ - 'Show some love to MUI', - 'Show all notification content', - 'Hide sensitive notification content', - 'Hide all notification content', -]; - -function SelectedMenuLegacy() { - const [anchorEl, setAnchorEl] = React.useState(null); - const [selectedIndex, setSelectedIndex] = React.useState(1); - const open = Boolean(anchorEl); - const handleClickListItem = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); - }; - - const handleMenuItemClick = (event: React.MouseEvent, index: number) => { - setSelectedIndex(index); - setAnchorEl(null); - }; - - const handleClose = () => { - setAnchorEl(null); - }; - - return ( -
- - - - - - - {selectedMenuOptions.map((option, index) => ( - handleMenuItemClick(event, index)} - > - {option} - - ))} - -
- ); -} - -function SelectedMenuDropdown() { - const { contextValue: dropdownContextValue } = useDropdown(); - - const [selectedIndex, setSelectedIndex] = React.useState(1); - const handleMenuItemClick = (event: React.MouseEvent, index: number) => { - setSelectedIndex(index); - }; - - return ( - - - {selectedMenuOptions[selectedIndex]} - - - {selectedMenuOptions.map((option, index) => ( - handleMenuItemClick(event, index)} - > - {option} - - ))} - - - ); -} - -function SelectedMenuStable() { - const [anchorEl, setAnchorEl] = React.useState(null); - const [selectedIndex, setSelectedIndex] = React.useState(1); - const open = Boolean(anchorEl); - const handleClickListItem = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); - }; - - const handleMenuItemClick = (event: React.MouseEvent, index: number) => { - setSelectedIndex(index); - setAnchorEl(null); - }; - - const handleClose = () => { - setAnchorEl(null); - }; - - return ( -
- - - - - - - {selectedMenuOptions.map((option, index) => ( - handleMenuItemClick(event, index)} - > - {option} - - ))} - -
- ); -} - -export default function BasicMenu() { - return ( - - - - -

Disabled item focusable

- - - - {/* This is not working at this point */} -

Auto focus false

- - - - {/* This is not working at this point */} -

Varaiant selectedMenu

-
Legacy
- -
Dropdown
- -
Stable
- -
- ); -} diff --git a/docs/pages/experiments/md3/buttons.tsx b/docs/pages/experiments/md3/buttons.tsx deleted file mode 100644 index d36e8616b41e6c..00000000000000 --- a/docs/pages/experiments/md3/buttons.tsx +++ /dev/null @@ -1,219 +0,0 @@ -import * as React from 'react'; -import TextField from '@mui/material/TextField'; -import Stack from '@mui/material/Stack'; -import MD2Button, { ButtonProps as MD2ButtonProps } from '@mui/material/Button'; -import { unstable_capitalize as capitalize } from '@mui/utils'; -import DeleteIcon from '@mui/icons-material/Delete'; -import SendIcon from '@mui/icons-material/Send'; -import Button, { ButtonProps } from '@mui/material-next/Button'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -import CssBaseline from '@mui/material/CssBaseline'; -import { customPalette, ModeSwitcher } from '.'; - -const variants: ButtonProps['variant'][] = [ - 'elevated', - 'filled', - 'filledTonal', - 'outlined', - 'text', -]; -const colors: ButtonProps['color'][] = ['primary', 'secondary', 'tertiary']; -const sizes: ButtonProps['size'][] = ['small', 'medium', 'large']; - -const md2Variants: MD2ButtonProps['variant'][] = ['contained', 'outlined', 'text']; -const md2Colors: MD2ButtonProps['color'][] = [ - 'primary', - 'secondary', - 'success', - 'error', - 'info', - 'warning', -]; - -function DemoComponents() { - const [radius, setRadius] = React.useState('10'); - const [gap, setGap] = React.useState('0.5'); - - return ( - -

Enabled

- - {variants.map((variant) => ( - - ))} - -

Enabled without a ripple effect

- - {variants.map((variant) => ( - - ))} - -

Disabled

- - {variants.map((variant) => ( - - ))} - -

Colors

- - {colors.map((color) => ( - - ))} - - - {colors.map((color) => ( - - ))} - - - {colors.map((color) => ( - - ))} - -

Extended buttons

- - {colors.map((color) => ( - - ))} - - - - {colors.map((color) => ( - - ))} - - -

Sizes

- - {sizes.map((size) => ( - - ))} - - - {sizes.map((size) => ( - - ))} - -

sx prop showcase

- - - - - MD2 Button - - -

CSS vars playground

- - - { - setRadius(e.target.value); - }} - /> - { - setGap(e.target.value); - }} - /> - - - {sizes.map((size) => ( - - ))} - - -

Material Design 2 Buttons

- - {md2Variants.map((variant) => ( - - {capitalize(variant as string)} - - ))} - - - {md2Colors.map((color) => ( - - {capitalize(color as string)} - - ))} - -
- ); -} - -// custom M3 theme -const cssVarsTheme = extendTheme({ - ref: { - palette: customPalette, - }, -}); - -export default function App() { - return ( - - - - - - - - ); -} diff --git a/docs/pages/experiments/md3/index.tsx b/docs/pages/experiments/md3/index.tsx deleted file mode 100644 index c67299fe14ec18..00000000000000 --- a/docs/pages/experiments/md3/index.tsx +++ /dev/null @@ -1,136 +0,0 @@ -import * as React from 'react'; -import Tooltip from '@mui/material/Tooltip'; -import IconButton from '@mui/material/IconButton'; -import DarkIcon from '@mui/icons-material/DarkModeOutlined'; -import LightIcon from '@mui/icons-material/LightModeOutlined'; -import { useColorScheme } from '@mui/material-next/styles'; - -export const customPalette = { - primary: { - '0': '#000000', - '10': '#3e001f', - '20': '#640036', - '30': '#8d004e', - '40': '#b70b68', - '50': '#d83181', - '60': '#fa4d9b', - '70': '#ff83b3', - '80': '#ffb0ca', - '90': '#ffd9e3', - '95': '#ffecf0', - '99': '#fffbff', - '100': '#ffffff', - }, - secondary: { - '0': '#000000', - '10': '#2b151d', - '20': '#422932', - '30': '#5a3f48', - '40': '#74565f', - '50': '#8e6f78', - '60': '#a98892', - '70': '#c5a2ac', - '80': '#e2bdc7', - '90': '#ffd9e3', - '95': '#ffecf0', - '99': '#fffbff', - '100': '#ffffff', - }, - tertiary: { - '0': '#000000', - '10': '#2f1500', - '20': '#48290c', - '30': '#623f21', - '40': '#7d5636', - '50': '#996e4c', - '60': '#b58763', - '70': '#d2a17c', - '80': '#f0bc95', - '90': '#ffdcc3', - '95': '#ffede3', - '99': '#fffbff', - '100': '#ffffff', - }, - neutral: { - '0': '#000000', - '10': '#201a1c', - '17': '#2e282a', - '20': '#352f30', - '22': '#393335', - '30': '#4c4546', - '40': '#645c5e', - '50': '#7e7577', - '60': '#988e90', - '70': '#b3a9aa', - '80': '#cfc4c5', - '90': '#ebe0e1', - '92': '#f1e5e6', - '95': '#faeef0', - '96': '#f7f2fa', - '99': '#fffbff', - '100': '#ffffff', - }, - neutralVariant: { - '0': '#000000', - '10': '#23191c', - '20': '#392d31', - '30': '#514347', - '40': '#695b5e', - '50': '#837377', - '60': '#9e8c91', - '70': '#b9a7ab', - '80': '#d5c2c6', - '90': '#f2dde2', - '95': '#ffecf0', - '99': '#fffbff', - '100': '#ffffff', - }, - error: { - '0': '#000000', - '10': '#410002', - '20': '#690005', - '30': '#93000a', - '40': '#ba1a1a', - '50': '#de3730', - '60': '#ff5449', - '70': '#ff897d', - '80': '#ffb4ab', - '90': '#ffdad6', - '95': '#ffedea', - '99': '#fffbff', - '100': '#ffffff', - }, -}; - -export function ModeSwitcher() { - const { mode, setMode } = useColorScheme(); - const [mounted, setMounted] = React.useState(false); - - React.useEffect(() => { - setMounted(true); - }, []); - - if (!mounted) { - return null; - } - - return ( - - { - if (mode === 'light') { - setMode('dark'); - } else { - setMode('light'); - } - }} - > - {mode === 'light' ? : } - - - ); -} - -export default function Page() { - return
Empty
; -} diff --git a/docs/pages/experiments/md3/inputs.tsx b/docs/pages/experiments/md3/inputs.tsx deleted file mode 100644 index ddcf6005c1c2b7..00000000000000 --- a/docs/pages/experiments/md3/inputs.tsx +++ /dev/null @@ -1,212 +0,0 @@ -import * as React from 'react'; -import Stack from '@mui/material/Stack'; -import { - FilledInput as Md2FilledInput, - FormControl as Md2FormControl, - InputLabel as Md2InputLabel, - InputAdornment as Md2InputAdornment, - FormHelperText as Md2FormHelperText, -} from '@mui/material'; -import { ThemeProvider, createTheme } from '@mui/material/styles'; -import { FilledInput, FormControl, InputLabel, FormHelperText } from '@mui/material-next'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -import SearchIcon from '@mui/icons-material/Search'; -import HighlightOffIcon from '@mui/icons-material/HighlightOff'; -import { ModeSwitcher } from '.'; - -const md2Theme = createTheme(); - -const md3Theme = extendTheme(); - -export default function MaterialYouInputs() { - return ( - - - -
MD2
- - - Primary - - - md2 primary helper text - - - - Secondary - - - md2 secondary helper text - - - - - - Primary adornments - - - - } - /> - - - Secondary adornments - $} - /> - - -
-
- - - - -
MD3
- -
- - - - Primary - - Primary helper text - - - Secondary - - Secondary helper text - - - Tertiary - - Tertiary helper text - - - - - - Primary disabled - - Primary helper text - - - Secondary disabled - - Secondary helper text - - - Tertiary disabled - - Tertiary helper text - - - - - - Primary error - - Primary helper text - - - Secondary error - - Secondary helper text - - - Tertiary error - - Tertiary helper text - - - - - - WIP Primary adornments - - - - } - /> - - - WIP Secondary adornments - - - - } - endAdornment={ - - - - } - /> - - -
-
-
- ); -} diff --git a/docs/pages/material-ui/guides/material-3-components.js b/docs/pages/material-ui/guides/material-3-components.js deleted file mode 100644 index 656ad19c0f7a11..00000000000000 --- a/docs/pages/material-ui/guides/material-3-components.js +++ /dev/null @@ -1,7 +0,0 @@ -import * as React from 'react'; -import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; -import * as pageProps from 'docs/data/material/guides/material-3-components/material-3-components.md?muiMarkdown'; - -export default function Page() { - return ; -} diff --git a/docs/src/modules/components/MaterialUIComponents/MaterialDataDisplayComponents.js b/docs/src/modules/components/MaterialUIComponents/MaterialDataDisplayComponents.js index 0cc2d5f800e61a..6e14e91093bcbc 100644 --- a/docs/src/modules/components/MaterialUIComponents/MaterialDataDisplayComponents.js +++ b/docs/src/modules/components/MaterialUIComponents/MaterialDataDisplayComponents.js @@ -20,7 +20,7 @@ const dataDisplayComponents = [ link: '/material-ui/react-badge/', md1: false, md2: false, - md3: true, + md3: false, noGuidelines: false, }, { @@ -30,7 +30,7 @@ const dataDisplayComponents = [ link: '/material-ui/react-chip/', md1: false, md2: true, - md3: true, + md3: false, noGuidelines: false, }, { @@ -40,7 +40,7 @@ const dataDisplayComponents = [ link: '/material-ui/react-divider/', md1: false, md2: true, - md3: true, + md3: false, noGuidelines: false, }, { diff --git a/docs/src/modules/components/MaterialUIComponents/MaterialFeedbackComponents.js b/docs/src/modules/components/MaterialUIComponents/MaterialFeedbackComponents.js index 35e112ad076503..c3cc1c11a174fa 100644 --- a/docs/src/modules/components/MaterialUIComponents/MaterialFeedbackComponents.js +++ b/docs/src/modules/components/MaterialUIComponents/MaterialFeedbackComponents.js @@ -40,7 +40,7 @@ const feedbackComponents = [ link: '/material-ui/react-progress/', md1: false, md2: true, - md3: true, + md3: false, noGuidelines: false, }, { diff --git a/docs/src/modules/components/MaterialUIComponents/MaterialInputComponents.js b/docs/src/modules/components/MaterialUIComponents/MaterialInputComponents.js index 4037c016012df8..1d67aa2ffacb36 100644 --- a/docs/src/modules/components/MaterialUIComponents/MaterialInputComponents.js +++ b/docs/src/modules/components/MaterialUIComponents/MaterialInputComponents.js @@ -20,7 +20,7 @@ const inputComponents = [ link: '/material-ui/react-button/', md1: false, md2: true, - md3: true, + md3: false, noGuidelines: false, }, { @@ -90,7 +90,7 @@ const inputComponents = [ link: '/material-ui/react-slider/', md1: false, md2: true, - md3: true, + md3: false, noGuidelines: false, }, { diff --git a/docs/src/modules/components/MaterialYouUsageDemo.tsx b/docs/src/modules/components/MaterialYouUsageDemo.tsx deleted file mode 100644 index fe6b2ed0213af6..00000000000000 --- a/docs/src/modules/components/MaterialYouUsageDemo.tsx +++ /dev/null @@ -1,364 +0,0 @@ -import * as React from 'react'; -import { alpha } from '@mui/material/styles'; -import ReplayRoundedIcon from '@mui/icons-material/ReplayRounded'; -import Box from '@mui/material/Box'; -import Divider from '@mui/material/Divider'; -import FormControl from '@mui/material/FormControl'; -import FormLabel, { formLabelClasses } from '@mui/material/FormLabel'; -import IconButton from '@mui/material/IconButton'; -import MenuItem from '@mui/material/MenuItem'; -import Select from '@mui/material/Select'; -import Switch from '@mui/material/Switch'; -import Typography from '@mui/material/Typography'; -import { - extendTheme, - CssVarsProvider as MaterialYouCssVarsProvider, -} from '@mui/material-next/styles'; -import BrandingProvider from 'docs/src/BrandingProvider'; -import HighlightedCode from 'docs/src/modules/components/HighlightedCode'; - -const materialYouTheme = extendTheme(); -const shallowEqual = (item1: { [k: string]: any }, item2: { [k: string]: any }) => { - let equal = true; - Object.entries(item1).forEach(([key, value]: [string, any]) => { - if (item2[key] !== value) { - equal = false; - } - }); - return equal; -}; - -const defaultGetCodeBlock = (code: string) => code; - -function createCode( - data: { - name: string; - props: Record; - childrenAccepted?: boolean; - }, - getCodeBlock = defaultGetCodeBlock, -) { - const { props: inProps, name, childrenAccepted } = data; - const closedJsx = childrenAccepted ? '>' : '/>'; - let code = `<${name}`; - const props = Object.entries(inProps).sort((a, b) => a[0].localeCompare(b[0])); - - if (!Object.keys(props).length) { - code = `${code} ${closedJsx}`; - } else { - let children = ''; - props.forEach((prop) => { - if (prop[0] !== 'children' && prop[1] !== undefined) { - if (props.length <= 2) { - if (typeof prop[1] === 'boolean') { - code = `${code} ${prop[0]}${prop[1] ? '' : '={false}'}`; - } else if (typeof prop[1] === 'function') { - code = `${code} ${prop[0]}={${(prop[1] as Function).toString()}}`; - } else { - code = `${code} ${prop[0]}=${ - typeof prop[1] === 'number' ? `{${prop[1]}}` : `"${prop[1]}"` - }`; - } - } else if (typeof prop[1] === 'function') { - code = `${code}\n ${prop[0]}={${(prop[1] as Function).toString()}}`; - } else if (typeof prop[1] === 'boolean') { - code = `${code}\n ${prop[0]}${prop[1] ? '' : '={false}'}`; - } else { - code = `${code}\n ${prop[0]}=${ - typeof prop[1] === 'number' ? `{${prop[1]}}` : `"${prop[1]}"` - }`; - } - } else { - children = prop[1] as string; - } - }); - if (children) { - code = `${code}${props.length > 2 ? `\n>` : '>'}\n ${children}\n`; - } else { - code = `${code}${props.length > 2 ? `\n${closedJsx}` : `${childrenAccepted ? '>' : ' />'}`}`; - } - } - - return getCodeBlock(code); -} - -export const prependLinesSpace = (code: string, size: number = 2) => { - const newCode: string[] = []; - code.split('\n').forEach((line) => { - newCode.push(`${Array(size).fill(' ').join('')}${line}`); - }); - return newCode.join('\n'); -}; - -interface MaterialYouUsageDemoProps { - /** - * Name of the component to show in the code block. - */ - componentName: string; - /** - * For displaying the close bracket of the component in the code block. - * if `true`, shows '>' otherwise shows '/>' - */ - childrenAccepted?: boolean; - /** - * Configuration - */ - data: Array<{ - /** - * Name of the prop, for example 'children' - */ - propName: Extract; - /** - * The controller to be used: - * - `switch`: render the switch component for boolean - * - `color`: render the built-in color selector - * - `select`: render - * - `radio`: render group of radios - */ - knob?: - | 'switch' - | 'color' - | 'select' - | 'input' - | 'radio' - | 'controlled' - | 'number' - | 'placement'; - /** - * The options for these knobs: `select` and `radio` - */ - options?: Array; - /** - * The labels for these knobs: `radio` - */ - labels?: Array; - /** - * The default value to be used by the components. - * If exists, it will be injected to the `renderDemo` callback but it will not show - * in the code block. - * - * To make it appears in the code block, specified `codeBlockDisplay: true` - */ - defaultValue?: string | number | boolean; - /** - * If not specify (`undefined`), the prop displays when user change the value - * If `true`, the prop with defaultValue will always display in the code block. - * If `false`, the prop does not display in the code block. - */ - codeBlockDisplay?: boolean; - onChange?: (event: React.SyntheticEvent) => void; - }>; - /** - * A function to override the code block result. - */ - getCodeBlock?: (code: string, props: ComponentProps) => string; - renderDemo: (props: ComponentProps) => React.ReactElement; -} - -export default function MaterialYouUsageDemo({ - componentName, - childrenAccepted = false, - data, - renderDemo, - getCodeBlock = defaultGetCodeBlock, -}: MaterialYouUsageDemoProps) { - const initialProps = {} as { [k in keyof T]: any }; - let demoProps = {} as { [k in keyof T]: any }; - let codeBlockProps = {} as { [k in keyof T]: any }; - data.forEach((p) => { - demoProps[p.propName] = p.defaultValue; - if (p.codeBlockDisplay) { - initialProps[p.propName] = p.defaultValue; - } - if (!p.knob) { - codeBlockProps[p.propName] = p.defaultValue; - } - }); - const [props, setProps] = React.useState(initialProps as T); - demoProps = { ...demoProps, ...props }; - codeBlockProps = { ...props, ...codeBlockProps }; - data.forEach((p) => { - if (p.codeBlockDisplay === false) { - delete codeBlockProps[p.propName]; - } - }); - - return ( - - - - - {renderDemo(demoProps)} - - - - getCodeBlock(code, demoProps), - )} - language="jsx" - sx={{ display: { xs: 'none', md: 'block' } }} - /> - - - ({ - flexShrink: 0, - gap: 2, - borderLeft: '1px solid', - borderColor: theme.palette.grey[200], - background: alpha(theme.palette.grey[50], 0.5), - minWidth: '250px', - [`:where(${theme.vars ? '[data-mui-color-scheme="dark"]' : '.mode-dark'}) &`]: { - borderColor: alpha(theme.palette.grey[900], 0.8), - backgroundColor: alpha(theme.palette.grey[900], 0.3), - }, - })} - > - - - Playground - - setProps(initialProps as T)} - sx={{ - visibility: !shallowEqual(props, initialProps) ? 'visible' : 'hidden', - }} - > - - - - - - {data.map(({ propName, knob, options = [], defaultValue, onChange }) => { - const resolvedValue = props[propName] ?? defaultValue; - if (!knob) { - return null; - } - if (knob === 'switch') { - return ( - - - {propName} - - { - setProps((latestProps) => ({ - ...latestProps, - [propName]: event.target.checked, - })); - onChange?.(event); - }} - /> - - ); - } - if (knob === 'select') { - return ( - - - {propName} - - - - ); - } - return null; - })} - - - - ); -} diff --git a/docs/src/modules/sandbox/Dependencies.ts b/docs/src/modules/sandbox/Dependencies.ts index 1f0f69ae774749..12f82f92ad9799 100644 --- a/docs/src/modules/sandbox/Dependencies.ts +++ b/docs/src/modules/sandbox/Dependencies.ts @@ -103,7 +103,6 @@ export default function SandboxDependencies( '@mui/private-classnames': getMuiPackageVersion('classnames'), '@mui/base': getMuiPackageVersion('base'), '@mui/utils': getMuiPackageVersion('utils'), - '@mui/material-next': getMuiPackageVersion('material-next'), '@mui/material-nextjs': getMuiPackageVersion('material-nextjs'), '@mui/joy': getMuiPackageVersion('joy'), }; diff --git a/packages/mui-icons-material/test/generated-types/tsconfig.json b/packages/mui-icons-material/test/generated-types/tsconfig.json index b724ef2117368f..7d97d6b4ec2a58 100644 --- a/packages/mui-icons-material/test/generated-types/tsconfig.json +++ b/packages/mui-icons-material/test/generated-types/tsconfig.json @@ -23,8 +23,6 @@ "@mui/lab/*": ["./mui-lab/src/*"], "@mui/internal-markdown": ["./markdown"], "@mui/internal-markdown/*": ["./markdown/*"], - "@mui/material-next": ["./mui-material-next/src"], - "@mui/material-next/*": ["./mui-material-next/src/*"], "@mui/material-nextjs": ["./mui-material-nextjs/src"], "@mui/material-nextjs/*": ["./mui-material-nextjs/src/*"], "@mui/material": ["./mui-material/src"], diff --git a/packages/mui-material-next/CONTRIBUTING.md b/packages/mui-material-next/CONTRIBUTING.md deleted file mode 100644 index 510b45892d8197..00000000000000 --- a/packages/mui-material-next/CONTRIBUTING.md +++ /dev/null @@ -1,34 +0,0 @@ -# Contributing - -The Material Design 3 components are targeted for v7, so they will be developed on the `material-next` package. - -The progress for each component will be tracked in a separate GitHub issue. If you wish to contribute to the migration go to a component's linked issue to see what tasks are missing (see progress tracker [here](https://github.com/mui/material-ui/issues/29345)). - -If the issue doesn't exist, create it, name it `[][material-next] Add component`, and assign @DiegoAndai. The issue has to contain (at least) a task for each of the steps below (use the [Slider issue](https://github.com/mui/material-ui/issues/37527) as a template). - -## Steps - -1. Copy component files from `material` to `material-next`, including tests, types, and utils. Keep in mind to: - - Add component export to `packages/mui-material-next/src/index.ts` - - Change imports from `@mui/material` to `@mui/material-next` - - If there are utils that don't exist in `material-next`, add them by copying from `material` - - Some utils imported from `../utils` might already exist in `@mui/utils`, if so, use the latter -2. Migrate component to TypeScript. The extension `.d.ts` should be replaced with `.types.ts` (except for `index.d.ts` which won't be necessary) -3. Remove deprecated `components` (note the plural `s`, it's not the same as the `component` prop) and `componentsProps` props, replacing them with `slots` and `slotProps` -4. Drop support for `ThemeProvider` in favor of `CssVarsProvider`. In practice, this means: - - Consuming tokens from `theme.vars` instead of `theme` - - In tests, using `CssVarsProvider` and `extendTheme` (both imported from `@mui/material-next/styles`) instead of `ThemeProvider` and `createTheme`, as well as providing the same `CssVarsProvier` and `extendTheme` to `describeConformance`'s `ThemeProvider` and `createTheme` options. -5. Implement M3 design specs. Add missing tokens if necessary. Use [material-web tokens](https://github.com/material-components/material-web/tree/main/tokens) as a reference for token values -6. Add component playground to the docs, take the [Slider playground](https://mui.com/material-ui/react-slider/#material-3-slider) as an example -7. Refactor styles to use component CSS Variables, following [material-web tokens](https://github.com/material-components/material-web/tree/main/tokens) and Joy UI's equivalent component (if it exists) as guides. - -## Other things to keep in mind - -- Except for the first step, there's no particular order to follow, but the proposed order has provided the best experience so far -- For every step, checking the components that are already in `material-next` will be really helpful -- Try to avoid breaking changes, keeping the component's API the same: - - An exception to this is to use M3 nomenclature and naming conventions, even if it would be a breaking change. - - If breaking changes are inevitable, then document them right away in `/packages/mui-material-next/migration.md` and add the `breaking change` label to your PR. -- Divide the work in whatever way makes more sense. One PR for a few steps or one PR for each step, however keep in mind that smaller pull requests will be reviewed and merged faster -- Let everyone know what you're working on so we can keep the work coordinated and avoid overlap -- Keep [running tests](https://github.com/mui/material-ui/blob/master/test/README.md) every step of the way, this will help to identify changes as soon as possible and avoid hard debugging sessions at the end diff --git a/packages/mui-material-next/README.md b/packages/mui-material-next/README.md deleted file mode 100644 index 43ec9270f9102b..00000000000000 --- a/packages/mui-material-next/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# @mui/material-next - -[Material Design 3](https://m3.material.io/) components built using [`@mui/base`](https://mui.com/base-ui/getting-started/overview/) with TypeScript. - -This package is a nursery for components that will ultimately replace those found in `@mui/material`. - -Follow the [migration guide](/packages/mui-material-next/migration.md) to migrate from `@mui/material` to `@mui/material-next`. - -## Contributing - -Read the [contributing guide](/CONTRIBUTING.md) to learn about our development process, how to propose bug fixes and improvements, and how to build and test your changes. - -Contributing to Material UI is about more than just issues and pull requests! -There are many other ways to [support Material UI](https://mui.com/material-ui/getting-started/faq/#mui-is-awesome-how-can-i-support-the-project) beyond contributing to the code base. diff --git a/packages/mui-material-next/migration.md b/packages/mui-material-next/migration.md deleted file mode 100644 index f409872beebee7..00000000000000 --- a/packages/mui-material-next/migration.md +++ /dev/null @@ -1,614 +0,0 @@ -# Migration - -This is a reference guide on how to migrate from @mui/material to @mui/material-next. - -## Breaking changes: components - -This section lists all breaking changes related to Material Design 3 (M3) components and how to address them. - -## Overarching changes - -These are the changes that apply to all components. - -### Remove `components` and `componentsProps` props - -The deprecated `components` and `componentsProps` props are removed in `@mui/material-next`. -If you were using these, then you can use `slots` and `slotProps` props instead, which have the same functionality and API. -Here's an example of the change using the Badge component: - -```diff - slot test }} -+ slots={{ badge: (props) => slot test }} -- componentsProps={{ badge: { id: "test-id" } }} -+ slotProps={{ badge: { id: "test-id" } }} - > - - -``` - -### Remove composed CSS classes and `styleOverrides` keys - -Classes composed of two or more existing classes are removed in `@mui/material-next`. -For example, the `MuiChip-filledPrimary` class is removed in favor of the `MuiChip-filled` and `MuiChip-colorPrimary` classes. -Composed `styleOverrides` keys are also removed. -Following the example above, the chip component's `filledPrimary` `styleOverrides` key is removed. -The specific classes and keys removed are listed in each component's section, as well as instructions on how to replace them. - -## Badge - -### Changed default color - -The default value for the color prop was changed to `"error"`. - -### Removed the "default" color option - -The `"default"` option is no longer accepted for the color prop. - -### Removed combined anchor-overlap styleOverrides keys - -The following `styleOverrides` `MuiBadge` keys were removed: - -- `anchorOriginTopLeftCircular` -- `anchorOriginTopLeftRectangular` -- `anchorOriginTopRightCircular` -- `anchorOriginTopRightRectangular` -- `anchorOriginBottomLeftCircular` -- `anchorOriginBottomLeftRectangular` -- `anchorOriginBottomRightCircular` -- `anchorOriginBottomRightRectangular` - -You can replace them by using a CSS selector inside the `MuiBadge` `styleOverrides` `badge` key. -The following example replaces the usage of `anchorOriginTopLeftCircular` with a CSS selector including the `MuiBadge-overlapCircular` class and the `MuiBadge-anchorOriginBottomLeft` class - -```diff - const theme = extendTheme({ - components: { - MuiBadge: { - styleOverrides: { -- anchorOriginBottomLeftCircular: { -+ badge: { -+ "&.MuiBadge-anchorOriginBottomLeft.MuiBadge-overlapCircular": { - background: "fuchsia" - } - } - } - } - } - }); -``` - -### Removed combined anchor-overlap classes - -The following classes were removed: - -- `MuiBadge-anchorOriginTopLeftCircular` -- `MuiBadge-anchorOriginTopLeftRectangular` -- `MuiBadge-anchorOriginTopRightCircular` -- `MuiBadge-anchorOriginTopRightRectangular` -- `MuiBadge-anchorOriginBottomLeftCircular` -- `MuiBadge-anchorOriginBottomLeftRectangular` -- `MuiBadge-anchorOriginBottomRightCircular` -- `MuiBadge-anchorOriginBottomRightRectangular` - -You can replace them with the `anchorOrigin` value and `overlap` value classes combined in a CSS selector. -The following example replaces the `MuiBadge-anchorOriginBottomLeftCircular` class using `MuiBadge-anchorOriginBottomLeft` and `MuiBadge-overlapCircular`: - -```diff -- .MuiBadge-anchorOriginBottomLeftCircular -+ .MuiBadge-anchorOriginBottomLeft.MuiBadge-overlapCircular -``` - -## Button - -See the [ButtonBase](https://github.com/mui/material-ui/blob/master/packages/mui-material-next/migration.md#buttonbase) section for more breaking changes. - -## ButtonBase - -The breaking changes in this section apply to the following components: - -- `Button` -- `ButtonBase` - - -So the examples below are interchangeable for these components. - -### Removed focusRipple - -The `focusRipple` prop was removed as ripples are absent in Material Design 3's focused states. - -### Prevent default on `key-up` and `key-down` events - -If you need to prevent default on a `key-up` and/or `key-down` event, then besides calling `preventDefault` you'll need to set `event.defaultMuiPrevented` to `true` as follows: - -```diff - const onKeyDown = (event) => { - event.preventDefault(); -+ event.defaultMuiPrevented = true; - }; - - const onKeyUp = (event) => { - event.preventDefault(); -+ event.defaultMuiPrevented = true; - }; - - -``` - -This is to ensure that default is prevented when the `ButtonBase` root is not a native button, for example, when the root element used is a `span`. - -## FilledInput - -### Removed `inputProps` - -`inputProps` are removed in favor of `slotProps.input`: - -```diff - -``` - -## FormControl - -### Renamed `FormControlState` - -The `FormControlState` interface was renamed to `FormControlContextValue`: - -```diff --import { FormControlState } from '@mui/material'; -+import { FormControlContextValue } from '@mui/material-next'; -``` - -### Removed the `standard` variant - -The standard variant is no longer supported in M3, use the `filled` or `outlined` variants instead. - -## FormLabel - -### Removed the `standard` variant - -The standard variant is no longer supported in M3, use the `filled` or `outlined` variants instead. - -## InputBase - -### Removed `inputProps` - -`inputProps` are deprecated in favor of `slotProps.input`: - -```diff - -``` - -## InputLabel - -### Removed the `standard` variant - -The standard variant is no longer supported in M3, use the `filled` or `outlined` variants instead. - -## Chip - -### Removed the "default" color option - -The `"default"` option is no longer accepted for the color prop. - -### Removed combined styleOverrides keys - -The following `styleOverrides` `MuiChip` keys were removed: - -- `clickableColorPrimary` -- `clickableColorSecondary` -- `deletableColorPrimary` -- `deletableColorSecondary` -- `outlinedPrimary` -- `outlinedSecondary` -- `filledPrimary` -- `filledSecondary` -- `avatarSmall` -- `avatarMedium` -- `avatarColorPrimary` -- `avatarColorSecondary` -- `iconSmall` -- `iconMedium` -- `iconColorPrimary` -- `iconColorSecondary` -- `labelSmall` -- `labelMedium` -- `deleteIconSmall` -- `deleteIconMedium` -- `deleteIconColorPrimary` -- `deleteIconColorSecondary` -- `deleteIconOutlinedColorPrimary` -- `deleteIconOutlinedColorSecondary` -- `deleteIconFilledColorPrimary` -- `deleteIconFilledColorSecondary` - -You can replace them by using the variants API and CSS Selectors. -The following example replaces the usage of `filledPrimary` with the variants API: - -```diff - const theme = extendTheme({ - components: { - MuiBadge: { -- styleOverrides: { -- filledPrimary: { -- background: "fuchsia" -- } -- } -+ variants: [ -+ { -+ props: { variant: 'filled', color: 'primary' }, -+ style: { -+ background: "fuchsia" -+ }, -+ }, -+ ], - } - } - }); -``` - -### Removed combined classes - -The following classes were removed: - -- `MuiChip-clickableColorPrimary` -- `MuiChip-clickableColorSecondary` -- `MuiChip-deletableColorPrimary` -- `MuiChip-deletableColorSecondary` -- `MuiChip-outlinedPrimary` -- `MuiChip-outlinedSecondary` -- `MuiChip-filledPrimary` -- `MuiChip-filledSecondary` -- `MuiChip-avatarSmall` -- `MuiChip-avatarMedium` -- `MuiChip-avatarColorPrimary` -- `MuiChip-avatarColorSecondary` -- `MuiChip-iconSmall` -- `MuiChip-iconMedium` -- `MuiChip-iconColorPrimary` -- `MuiChip-iconColorSecondary` -- `MuiChip-labelSmall` -- `MuiChip-labelMedium` -- `MuiChip-deleteIconSmall` -- `MuiChip-deleteIconMedium` -- `MuiChip-deleteIconColorPrimary` -- `MuiChip-deleteIconColorSecondary` -- `MuiChip-deleteIconOutlinedColorPrimary` -- `MuiChip-deleteIconOutlinedColorSecondary` -- `MuiChip-deleteIconFilledColorPrimary` -- `MuiChip-deleteIconFilledColorSecondary` - -You can replace them by combining classes with a CSS selector. -The following example replaces the `MuiChip-filledPrimary` class using `MuiChip-filled` and `MuiChip-colorPrimary`: - -```diff -- .MuiChip-filledPrimary -+ .MuiChip-filled.MuiChip-colorPrimary -``` - -### `skipFocusWhenDisabled` replaced with `focusableWhenDisabled` - -The `skipFocusWhenDisabled` prop was replaced with `focusableWhenDisabled`, which is `false` by default. -Because of this, the default behavior changed: - -- In `@mui/material`, disabled chips are focusable by default -- In `@mui/material-next`, disabled chips are _not_ focusable by default - -If you were using the `skipFocusWhenDisabled` prop to make disabled chips not focusable (which is `@mui/material-next`'s default behavior), then you can remove the prop as follows: - -```diff - -``` - -If you wish to maintain @mui/material's default behavior, then you can achieve that as follows: - -```diff - -``` - -If you were using the `skipFocusWhenDisabled` prop to explicitly make disabled chips focusable, then you can replace it with `focusableWhenDisabled` as follows: - -```diff - -``` - -## Slider - -### Thumb and Value Label slots must accept refs - -If you are using the `thumb` or `valueLabel` Slider slots, then make sure the components accept a `ref` and forward it to the outermost element: - -```diff --const ValueLabel = ({ value, ...props }) => { -+const ValueLabel = React.forwardRef(({ value, ...props }, ref) => { - return ( -- -+ - {value} - - ); -- }; -+ }); - --const Thumb = ({ style, ...props }) => { -+const Thumb = React.forwardRef(({ style, ...props }, ref) => { -- return ; -+ return ; --}; -+}); - - -``` - -This is required in `@mui/material-next` as it's used to apply the overlap styles to these slots. For more info take a look into [M3's Slider overlapping handles guidelines](https://m3.material.io/components/sliders/guidelines#ad5ceb95-a690-4ddd-8243-53a8e13bdab6). - -## Divider - -### Removed the "light" prop and class - -The `"light"` prop is no longer accepted for the Divider component. - -If you were using the `light` prop to create a lighter Divider (which is not supported in version 6), please remove the prop as shown below: - -```diff - -``` - -### Remove composed classes and `styleOverrides` keys - -The following classes were removed: - -- `MuiDivider-withChildrenVertical` - -The `MuiDivider-withChildrenVertical` class has been removed. To replace it, you can use the `MuiDivider-withChildren` class along with the `MuiDivider-vertical` class. Here's an updated example: - -```diff -- .MuiDivider-withChildrenVertical -+ .MuiDivider-withChildren.MuiDivider-vertical -``` - -## LinearProgress - -### Removed combined styleOverrides keys - -The following `styleOverrides` `MuiLinearProgress` keys were removed: - -- `dashedColorPrimary` -- `dashedColorSecondary` -- `barColorPrimary` -- `barColorSecondary` -- `bar1Indeterminate` -- `bar1Determinate` -- `bar1Buffer` -- `bar2Indeterminate` -- `bar2Buffer` - -The following `styleOverrides` `MuiLinearProgress` keys were added: - -- `bar1` -- `bar2` - -You can replace them by using the variants API and CSS Selectors. -The following example replaces the usage of `dashedPrimary` with the variants API: - -```diff - const theme = extendTheme({ - components: { - MuiLinearProgress: { - styleOverrides: { -- dashedColorPrimary: { -- background: "fuchsia" -- } -+ root: { -+ "&.MuiLinearProgress-colorPrimary > .MuiLinearProgress-dashed": { -+ background: "fuchsia" -+ } -+ } - } - } - } - }); -``` - -### Removed combined classes - -The following classes were removed: - -- `MuiLinearProgress-dashedColorPrimary` -- `MuiLinearProgress-dashedColorSecondary` -- `MuiLinearProgress-barColorPrimary` -- `MuiLinearProgress-barColorSecondary` -- `MuiLinearProgress-bar1Indeterminate` -- `MuiLinearProgress-bar1Determinate` -- `MuiLinearProgress-bar1Buffer` -- `MuiLinearProgress-bar2Indeterminate` -- `MuiLinearProgress-bar2Buffer` - -The following classes were added: - -- `MuiLinearProgress-bar1` -- `MuiLinearProgress-bar2` - -You can replace them by combining classes with a CSS selector. -The following example replaces the `MuiLinearProgress-dashedColorPrimary` class using `MuiLinearProgress-dashed` and `MuiLinearProgress-colorPrimary`: - -```diff -- .MuiLinearProgress-dashedColorPrimary -+ .MuiLinearProgress-colorPrimary .MuiLinearProgress-dashed -``` - -## CircularProgress - -### Removed combined styleOverrides keys - -The following `styleOverrides` `MuiCircularProgress` keys were removed: - -- `circleDeterminate` -- `circleIndeterminate` -- `circleDisableShrink` - -The following `styleOverrides` `MuiCircularProgress` keys were added: - -- `disableShrink` - -You can replace them by using the variants API and CSS Selectors. -The following example replaces the usage of `circleDeterminate` with the variants API: - -```diff - const theme = extendTheme({ - components: { - MuiCircularProgress: { - styleOverrides: { -- circleDeterminate: { -- background: "fuchsia" -- } -+ root: { -+ "&.MuiCircularProgress-determinate .MuiCircularProgress-circle": { -+ background: "fuchsia" -+ } -+ } - } - } - } - }); -``` - -### Removed combined classes - -The following classes were removed: - -- `MuiCircularProgress-circleDeterminate` -- `MuiCircularProgress-circleIndeterminate` -- `MuiCircularProgress-circleDisableShrink` - -The following classes were added: - -- `MuiCircularProgress-disableShrink` - -You can replace them by combining classes with a CSS selector. -The following example replaces the `MuiCircularProgress-circleDeterminate` class using `MuiCircularProgress-circle` and `MuiCircularProgress-determinate`: - -```diff -- .MuiCircularProgress-circleDeterminate -+ .MuiCircularProgress-determinate .MuiCircularProgress-circle -``` - -## ButtonGroup - -### Removed combined styleOverrides keys - -The following `styleOverrides` `MuiButtonGroup` keys were removed: - -- `contained` -- `groupedHorizontal` -- `groupedVertical` -- `groupedContained` -- `groupedText` -- `groupedOutlined` -- `groupedContainedHorizontal` -- `groupedTextHorizontal` -- `groupedOutlinedHorizontal` -- `groupedContainedVertical` -- `groupedTextVertical` -- `groupedOutlinedVertical` -- `groupedContainedPrimary` -- `groupedTextPrimary` -- `groupedOutlinedPrimary` -- `groupedContainedSecondary` -- `groupedTextSecondary` -- `groupedOutlinedSecondary` - -The following `styleOverrides` `MuiButtonGroup` keys were added: - -- `primary` -- `secondary` -- `tertiary` -- `filled` -- `filledPartial` -- `elevated` - -You can replace them by using the variants API and CSS Selectors. -The following example replaces the usage of `groupedOutlined` with the variants API: - -```diff - const theme = extendTheme({ - components: { - MuiButtonGroup: { - styleOverrides: { -- groupedOutlined: { -- background: "fuchsia" -- } -+ outlined: { -+ ".MuiButtonGroup-grouped": { -+ background: "fuchsia" -+ } -+ } - } - } - } - }); -``` - -### Removed combined classes - -The following classes were removed: - -- `MuiButtonGroup-contained` -- `MuiButtonGroup-groupedHorizontal` -- `MuiButtonGroup-groupedVertical` -- `MuiButtonGroup-groupedText` -- `MuiButtonGroup-groupedTextHorizontal` -- `MuiButtonGroup-groupedTextVertical` -- `MuiButtonGroup-groupedTextPrimary` -- `MuiButtonGroup-groupedTextSecondary` -- `MuiButtonGroup-groupedOutlined` -- `MuiButtonGroup-groupedOutlinedHorizontal` -- `MuiButtonGroup-groupedOutlinedVertical` -- `MuiButtonGroup-groupedOutlinedPrimary` -- `MuiButtonGroup-groupedOutlinedSecondary` -- `MuiButtonGroup-groupedContained` -- `MuiButtonGroup-groupedContainedHorizontal` -- `MuiButtonGroup-groupedContainedVertical` -- `MuiButtonGroup-groupedContainedPrimary` -- `MuiButtonGroup-groupedContainedSecondary` - -The following classes were added: - -- `MuiButtonGroup-filled` -- `MuiButtonGroup-filledTonal` -- `MuiButtonGroup-elevated` -- `MuiButtonGroup-primary` -- `MuiButtonGroup-secondary` -- `MuiButtonGroup-tertiary` - -You can replace them by combining classes with a CSS selector. -The following example replaces the `MuiButtonGroup-groupedOutlined` class using `MuiButtonGroup-grouped` and `MuiButtonGroup-outline`: - -```diff -- .MuiButtonGroup-groupedOutlined -+ .MuiButtonGroup-outlined .MuiButtonGroup-grouped -``` diff --git a/packages/mui-material-next/package.json b/packages/mui-material-next/package.json deleted file mode 100644 index e73e78032b3f16..00000000000000 --- a/packages/mui-material-next/package.json +++ /dev/null @@ -1,99 +0,0 @@ -{ - "name": "@mui/material-next", - "version": "6.0.0-alpha.126", - "private": false, - "author": "MUI Team", - "description": "v6-alpha: React components that implement Google's Material Design", - "main": "./src/index.ts", - "keywords": [ - "react", - "react-component", - "mui", - "material-ui", - "material design" - ], - "repository": { - "type": "git", - "url": "https://github.com/mui/material-ui.git", - "directory": "packages/mui-material-next" - }, - "license": "MIT", - "bugs": { - "url": "https://github.com/mui/material-ui/issues" - }, - "homepage": "https://mui.com/material-ui/", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "scripts": { - "build": "pnpm build:modern && pnpm build:node && pnpm build:stable && pnpm build:types && pnpm build:copy-files", - "build:modern": "node ../../scripts/build.mjs modern", - "build:node": "node ../../scripts/build.mjs node", - "build:stable": "node ../../scripts/build.mjs stable", - "build:copy-files": "node ../../scripts/copyFiles.mjs", - "build:types": "node ../../scripts/buildTypes.mjs", - "prebuild": "rimraf build tsconfig.build.tsbuildinfo", - "release": "pnpm build && pnpm publish", - "test": "cd ../../ && cross-env NODE_ENV=test mocha 'packages/mui-material-next/**/*.test.{js,ts,tsx}'", - "typescript": "tsc -p tsconfig.json", - "typescript:module-augmentation": "node scripts/testModuleAugmentation.js" - }, - "dependencies": { - "@babel/runtime": "^7.23.9", - "@mui/base": "workspace:*", - "@mui/material": "workspace:^", - "@mui/system": "workspace:^", - "@mui/types": "workspace:^", - "@mui/utils": "workspace:^", - "@types/react-transition-group": "^4.4.10", - "clsx": "^2.1.0", - "prop-types": "^15.8.1", - "react-is": "^18.2.0", - "react-transition-group": "^4.4.5" - }, - "devDependencies": { - "@emotion/react": "^11.11.4", - "@mui-internal/test-utils": "workspace:^", - "@mui/internal-babel-macros": "workspace:^", - "@testing-library/user-event": "^14.5.2", - "@types/chai": "^4.3.12", - "@types/prop-types": "^15.7.11", - "@types/react": "^18.2.55", - "@types/react-dom": "^18.2.19", - "@types/react-is": "^18.2.4", - "@types/sinon": "^10.0.20", - "chai": "^4.4.1", - "lodash": "^4.17.21", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-router-dom": "^6.21.3", - "sinon": "^15.2.0" - }, - "peerDependencies": { - "@emotion/react": "^11.5.0", - "@emotion/styled": "^11.3.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - } - }, - "sideEffects": false, - "publishConfig": { - "access": "public", - "directory": "build" - }, - "engines": { - "node": ">=12.0.0" - } -} diff --git a/packages/mui-material-next/src/Badge/Badge.spec.tsx b/packages/mui-material-next/src/Badge/Badge.spec.tsx deleted file mode 100644 index 5432f311480f9e..00000000000000 --- a/packages/mui-material-next/src/Badge/Badge.spec.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import * as React from 'react'; -import { expectType } from '@mui/types'; -import Badge, { BadgeOwnerState } from '@mui/material-next/Badge'; - -function classesTest() { - return ( - -
Hello World
-
- ); -} - -; - -; - -// `size` -; -; - -// @ts-expect-error there is no 'medium' size -; - -// `variant` -; -; - -// @ts-expect-error there is no variant `filled` -; - -// `color` -; -; -; -; -; -; -; - -// @ts-expect-error there is no color `default` -; - -; - -; - - { - expectType(ownerState); - return { - id: 'test', - }; - }, - badge: (ownerState) => { - expectType(ownerState); - return { - id: 'test', - }; - }, - }} -/>; diff --git a/packages/mui-material-next/src/Badge/Badge.test.tsx b/packages/mui-material-next/src/Badge/Badge.test.tsx deleted file mode 100644 index 3a21443b79da0a..00000000000000 --- a/packages/mui-material-next/src/Badge/Badge.test.tsx +++ /dev/null @@ -1,342 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { createRenderer } from '@mui-internal/test-utils'; -import Badge, { badgeClasses as classes } from '@mui/material-next/Badge'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -import describeConformance from '../../test/describeConformance'; - -function findBadgeRoot(container: HTMLElement) { - return container?.firstChild; -} - -function findBadge(container: HTMLElement) { - return (findBadgeRoot(container) as HTMLElement)?.querySelector('span') ?? null; -} - -describe('', () => { - const { render } = createRenderer(); - - const defaultProps = { - children: ( -
- Hello World -
- ), - badgeContent: 10, - }; - - describeConformance( - -
- , - () => ({ - ThemeProvider: CssVarsProvider, - createTheme: extendTheme, - classes, - inheritComponent: 'span', - render, - refInstanceof: window.HTMLSpanElement, - muiName: 'MuiBadge', - testVariantProps: { color: 'secondary', size: 'small' }, - slots: { - root: { - expectedClassName: classes.root, - }, - badge: { - expectedClassName: classes.badge, - }, - }, - skip: [ - 'componentsProp', - 'slotPropsCallback', // not supported yet - ], - }), - ); - - it('renders children and badgeContent', () => { - const children =
; - const badge =
; - const { container, getByTestId } = render({children}); - expect(container.firstChild).to.contain(getByTestId('child')); - expect(container.firstChild).to.contain(getByTestId('badge')); - }); - - it('applies customized classes', () => { - const customClasses = { - root: 'test-root', - anchorOriginTopRight: 'test-anchorOriginTopRight', - badge: 'test-badge', - colorSecondary: 'test-colorSecondary', - small: 'test-small', - dot: 'test-dot', - invisible: 'test-invisible', - overlapCircular: 'test-overlapCircular', - }; - - const { container } = render( - , - ); - - expect(findBadgeRoot(container)).to.have.class(customClasses.root); - expect(findBadge(container)).to.have.class(customClasses.anchorOriginTopRight); - expect(findBadge(container)).to.have.class(customClasses.badge); - expect(findBadge(container)).to.have.class(customClasses.colorSecondary); - expect(findBadge(container)).to.have.class(customClasses.small); - expect(findBadge(container)).to.have.class(customClasses.dot); - expect(findBadge(container)).to.have.class(customClasses.invisible); - expect(findBadge(container)).to.have.class(customClasses.overlapCircular); - }); - - it('renders children', () => { - const { container, getByTestId } = render( - , - ); - expect(container.firstChild).to.contain(getByTestId('children')); - }); - - describe('prop: color', () => { - it('should have the colorPrimary class when color="primary"', () => { - const { container } = render(); - expect(findBadge(container)).to.have.class(classes.colorPrimary); - }); - - it('should have the colorSecondary class when color="secondary"', () => { - const { container } = render(); - expect(findBadge(container)).to.have.class(classes.colorSecondary); - }); - - it('should have the colorError class when color="error"', () => { - const { container } = render(); - expect(findBadge(container)).to.have.class(classes.colorError); - }); - }); - - describe('prop: invisible', () => { - it('should default to false', () => { - const { container } = render(); - expect(findBadge(container)).not.to.have.class(classes.invisible); - }); - - it('should render without the invisible class when set to false', () => { - const { container } = render(); - expect(findBadge(container)).not.to.have.class(classes.invisible); - }); - - it('should render with the invisible class when set to true', () => { - const { container } = render(); - expect(findBadge(container)).to.have.class(classes.invisible); - }); - - it('should render with the invisible class when empty and not dot', () => { - let container; - container = render().container; - expect(findBadge(container)).to.have.class(classes.invisible); - container = render().container; - expect(findBadge(container)).to.have.class(classes.invisible); - container = render( - , - ).container; - expect(findBadge(container)).not.to.have.class(classes.invisible); - }); - - it('should render with the invisible class when empty and not small', () => { - let container; - container = render().container; - expect(findBadge(container)).to.have.class(classes.invisible); - container = render().container; - expect(findBadge(container)).to.have.class(classes.invisible); - container = render( - , - ).container; - expect(findBadge(container)).not.to.have.class(classes.invisible); - }); - - it('should render with invisible class when invisible and showZero are set to false and content is 0', () => { - const { container } = render(); - expect(findBadge(container)).to.have.class(classes.invisible); - expect(findBadge(container)).to.have.text(''); - }); - - it('should not render with invisible class when invisible and showZero are set to false and content is not 0', () => { - const { container } = render(); - expect(findBadge(container)).not.to.have.class(classes.invisible); - expect(findBadge(container)).to.have.text('1'); - }); - }); - - describe('prop: showZero', () => { - it('should default to false', () => { - const { container } = render(); - expect(findBadge(container)).to.have.class(classes.invisible); - }); - - it('should render without the invisible class when false and badgeContent is not 0', () => { - const { container } = render(); - expect(findBadge(container)).not.to.have.class(classes.invisible); - }); - - it('should render without the invisible class when true and badgeContent is 0', () => { - const { container } = render(); - expect(findBadge(container)).not.to.have.class(classes.invisible); - }); - - it('should render with the invisible class when false and badgeContent is 0', () => { - const { container } = render(); - expect(findBadge(container)).to.have.class(classes.invisible); - }); - }); - - describe('prop: variant', () => { - it('should default to standard', () => { - const { container } = render(); - expect(findBadge(container)).to.have.class(classes.badge); - expect(findBadge(container)).to.have.class(classes.standard); - expect(findBadge(container)).not.to.have.class(classes.dot); - }); - - it('should render with the standard class when variant="standard"', () => { - const { container } = render(); - expect(findBadge(container)).to.have.class(classes.badge); - expect(findBadge(container)).to.have.class(classes.standard); - expect(findBadge(container)).not.to.have.class(classes.dot); - }); - - it('should not render badgeContent when variant="dot"', () => { - const { container } = render(); - expect(findBadge(container)).to.have.class(classes.badge); - expect(findBadge(container)).to.have.class(classes.dot); - expect(findBadge(container)).to.have.text(''); - expect(findBadge(container)).not.to.have.class(classes.standard); - }); - }); - - describe('prop: size', () => { - it('should default to large', () => { - const { container } = render(); - expect(findBadge(container)).to.have.class(classes.badge); - expect(findBadge(container)).to.have.class(classes.large); - expect(findBadge(container)).not.to.have.class(classes.small); - }); - - it('should render with the large class when size="large"', () => { - const { container } = render(); - expect(findBadge(container)).to.have.class(classes.badge); - expect(findBadge(container)).to.have.class(classes.large); - expect(findBadge(container)).not.to.have.class(classes.small); - }); - - it('should not render badgeContent when size="small"', () => { - const { container } = render(); - expect(findBadge(container)).to.have.class(classes.badge); - expect(findBadge(container)).to.have.class(classes.small); - expect(findBadge(container)).to.have.text(''); - expect(findBadge(container)).not.to.have.class(classes.large); - }); - }); - - describe('prop: max', () => { - it('should default to 99', () => { - const { container } = render(); - expect(findBadge(container)).to.have.text('99+'); - }); - - it('should cap badgeContent', () => { - const { container } = render(); - expect(findBadge(container)).to.have.text('999+'); - }); - - it('should not cap if badgeContent and max are equal', () => { - const { container } = render(); - expect(findBadge(container)).to.have.text('1000'); - }); - - it('should not cap if badgeContent is lower than max', () => { - const { container } = render(); - expect(findBadge(container)).to.have.text('50'); - }); - }); - - describe('prop: anchorOrigin', () => { - it('should apply style for top right by default', () => { - const { container } = render(); - expect(findBadge(container)).to.have.class(classes.anchorOriginTopRight); - }); - - it('should apply style for top left', () => { - const { container } = render( - , - ); - expect(findBadge(container)).to.have.class(classes.anchorOriginTopLeft); - }); - - it('should apply style for top right', () => { - const { container } = render( - , - ); - expect(findBadge(container)).to.have.class(classes.anchorOriginTopRight); - }); - - it('should apply style for bottom left', () => { - const { container } = render( - , - ); - expect(findBadge(container)).to.have.class(classes.anchorOriginBottomLeft); - }); - - it('should apply style for bottom right', () => { - const { container } = render( - , - ); - expect(findBadge(container)).to.have.class(classes.anchorOriginBottomRight); - }); - }); - - describe('prop: overlap', () => { - it('should apply style for rectangular by default', () => { - const { container } = render(); - expect(findBadge(container)).to.have.class(classes.overlapRectangular); - }); - - it('should apply style for rectangular', () => { - const { container } = render(); - expect(findBadge(container)).to.have.class(classes.overlapRectangular); - }); - - it('should apply style for circular', () => { - const { container } = render(); - expect(findBadge(container)).to.have.class(classes.overlapCircular); - }); - }); - - it('retains anchorOrigin, content, color, max, overlap, size, and variant when invisible is true for consistent disappearing transition', () => { - const { container, setProps } = render( - , - ); - - setProps({ - badgeContent: 0, - color: 'primary', - size: 'large', - variant: 'standard', - overlap: 'circular', - anchorOrigin: { - vertical: 'bottom', - horizontal: 'left', - }, - }); - - expect(findBadge(container)).to.have.text(''); - expect(findBadge(container)).to.have.class(classes.colorSecondary); - expect(findBadge(container)).to.have.class(classes.small); - expect(findBadge(container)).to.have.class(classes.dot); - expect(findBadge(container)).to.have.class(classes.anchorOriginTopRight); - }); -}); diff --git a/packages/mui-material-next/src/Badge/Badge.tsx b/packages/mui-material-next/src/Badge/Badge.tsx deleted file mode 100644 index 0f402cd369306e..00000000000000 --- a/packages/mui-material-next/src/Badge/Badge.tsx +++ /dev/null @@ -1,378 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { usePreviousProps, unstable_capitalize as capitalize } from '@mui/utils'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import { useBadge } from '@mui/base/useBadge'; -import { useSlotProps } from '@mui/base'; -import { OverridableComponent } from '@mui/types'; -import styled from '../styles/styled'; -import useTheme from '../styles/useTheme'; -import useThemeProps from '../styles/useThemeProps'; -import badgeClasses, { getBadgeUtilityClass } from './badgeClasses'; -import { BadgeOwnerState, BadgeProps, BadgeTypeMap } from './Badge.types'; -import { MD3ColorSchemeTokens } from '../styles'; - -const useUtilityClasses = (ownerState: BadgeOwnerState) => { - const { color, anchorOrigin, invisible, overlap, size, variant, classes = {} } = ownerState; - - const slots = { - root: ['root'], - badge: [ - 'badge', - size, - variant, - invisible && 'invisible', - `anchorOrigin${capitalize(anchorOrigin.vertical)}${capitalize(anchorOrigin.horizontal)}`, - `overlap${capitalize(overlap)}`, - `color${capitalize(color)}`, - ], - }; - - return composeClasses(slots, getBadgeUtilityClass, classes); -}; - -const BadgeRoot = styled('span', { - name: 'MuiBadge', - slot: 'Root', - overridesResolver: (props, styles) => styles.root, -})({ - position: 'relative', - display: 'inline-flex', - // For correct alignment with the text. - verticalAlign: 'middle', - flexShrink: 0, -}); - -const BadgeBadge = styled('span', { - name: 'MuiBadge', - slot: 'Badge', - overridesResolver: (props, styles) => { - const { ownerState }: { ownerState: BadgeOwnerState } = props; - - return [ - styles.badge, - styles[ownerState.size], - styles[ownerState.variant], - styles[ - `anchorOrigin${capitalize(ownerState.anchorOrigin.vertical)}${capitalize( - ownerState.anchorOrigin.horizontal, - )}` - ], - styles[`overlap${capitalize(ownerState.overlap)}`], - styles[`color${capitalize(ownerState.color)}`], - ownerState.invisible && styles.invisible, - ]; - }, -})<{ ownerState: BadgeOwnerState }>(({ theme, ownerState }) => { - const { vars: tokens } = theme; - - const letterSpacing = `${ - theme.sys.typescale.label.small.tracking / theme.sys.typescale.label.small.size - }rem`; - - const verticalPositionProperty = ownerState.anchorOrigin.vertical === 'top' ? 'bottom' : 'top'; - const horizontalPositionProperty = - ownerState.anchorOrigin.horizontal === 'left' ? 'right' : 'left'; - - return { - '--md-comp-badge-large-size': '16px', - '--md-comp-badge-small-size': '6px', - '--md-comp-badge-size': 'var(--md-comp-badge-large-size)', - '--md-comp-badge-padding-x': '4px', - '--md-comp-badge-inset': 'calc(var(--md-comp-badge-size) - 4px)', - ...(ownerState.size === 'small' && { - '--md-comp-badge-size': 'var(--md-comp-badge-small-size)', - '--md-comp-badge-inset': 'var(--md-comp-badge-size)', - '--md-comp-badge-padding-x': '0px', - }), - ...(ownerState.overlap === 'circular' && { - '--md-comp-badge-inset': 'calc(var(--md-comp-badge-size) / 2 + 14%)', - }), - display: 'flex', - flexDirection: 'row', - flexWrap: 'wrap', - justifyContent: 'center', - alignContent: 'center', - alignItems: 'center', - position: 'absolute', - boxSizing: 'border-box', - fontFamily: tokens.sys.typescale.label.small.family, - fontWeight: tokens.sys.typescale.label.small.weight, - fontSize: theme.typography.pxToRem(theme.sys.typescale.label.small.size), - lineHeight: `calc(${tokens.sys.typescale.label.small.lineHeight} / ${tokens.sys.typescale.label.small.size})`, - letterSpacing, - backgroundColor: tokens.sys.color[ownerState.color], - color: tokens.sys.color[`on${capitalize(ownerState.color)}` as keyof MD3ColorSchemeTokens], - minWidth: 'var(--md-comp-badge-size)', - height: 'var(--md-comp-badge-size)', - paddingRight: `calc(var(--md-comp-badge-padding-x) - ${letterSpacing})`, - paddingLeft: 'var(--md-comp-badge-padding-x)', - borderRadius: tokens.sys.shape.corner.full, - [verticalPositionProperty]: `calc(100% - var(--md-comp-badge-inset))`, - [horizontalPositionProperty]: `calc(100% - var(--md-comp-badge-inset))`, - transform: 'scale(1)', - transformOrigin: 'calc(var(--md-comp-badge-size) / 2)', - zIndex: 1, // Render the badge on top of potential ripples. - transition: theme.transitions.create('transform', { - easing: theme.transitions.easing.easeInOut, - duration: theme.transitions.duration.enteringScreen, - }), - [`&.${badgeClasses.invisible}`]: { - transform: 'scale(0)', - transition: theme.transitions.create('transform', { - easing: theme.transitions.easing.easeInOut, - duration: theme.transitions.duration.leavingScreen, - }), - }, - }; -}); - -const defaultLtRAnchorOrigin = { - vertical: 'top', - horizontal: 'right', -}; - -const defaultRtLAnchorOrigin = { - vertical: 'top', - horizontal: 'left', -}; - -function isSmall(size?: BadgeProps['size'], variant?: BadgeProps['variant']): boolean { - if (size) { - // size takes precedence - return size === 'small'; - } - - return variant === 'dot'; -} - -/** - * - * Demos: - * - * - [Badge](https://mui.com/material-ui/react-badge/) - * - * API: - * - * - [Badge API](https://mui.com/material-ui/api/badge/) - */ -const Badge = React.forwardRef(function Badge< - BaseComponentType extends React.ElementType = BadgeTypeMap['defaultComponent'], ->(inProps: BadgeProps, ref: React.ForwardedRef) { - const props = useThemeProps({ props: inProps, name: 'MuiBadge' }); - - const theme = useTheme(); - const defaultAnchorOrigin = - theme.direction === 'rtl' ? defaultRtLAnchorOrigin : defaultLtRAnchorOrigin; - - const { - anchorOrigin: anchorOriginProp = defaultAnchorOrigin, - className, - classes: classesProp, - component, - children, - overlap: overlapProp = 'rectangular', - color: colorProp = 'error', - invisible: invisibleProp = false, - max: maxProp = 99, - badgeContent: badgeContentProp, - slots = {}, - slotProps = {}, - showZero = false, - size: sizeProp, - variant: variantProp, - ...other - } = props; - - const { - badgeContent, - invisible: invisibleFromHook, - max, - displayValue: displayValueFromHook, - } = useBadge({ - max: maxProp, - invisible: invisibleProp, - badgeContent: badgeContentProp, - showZero, - }); - - const prevProps = usePreviousProps({ - anchorOrigin: anchorOriginProp, - color: colorProp, - overlap: overlapProp, - size: sizeProp, - variant: variantProp, - badgeContent: badgeContentProp, - }); - - const invisible = invisibleFromHook || (badgeContent == null && !isSmall(sizeProp, variantProp)); - - const { - color = colorProp, - overlap = overlapProp, - anchorOrigin = anchorOriginProp, - size = sizeProp, - variant = variantProp, - } = invisible ? prevProps : props; - - const displayValue = !isSmall(size, variant) ? displayValueFromHook : undefined; - - const ownerState = { - ...props, - badgeContent, - invisible, - max, - displayValue, - showZero, - anchorOrigin, - color, - overlap, - size: isSmall(size, variant) ? 'small' : 'large', - variant: isSmall(size, variant) ? 'dot' : 'standard', - }; - - const classes = useUtilityClasses(ownerState); - - const RootSlot = slots.root ?? BadgeRoot; - const BadgeSlot = slots.badge ?? BadgeBadge; - - const rootProps = useSlotProps({ - elementType: RootSlot, - externalSlotProps: slotProps.root, - externalForwardedProps: other, - additionalProps: { - ref, - as: component, - }, - ownerState, - className: [classes.root, className], - }); - - const badgeProps = useSlotProps({ - elementType: BadgeSlot, - externalSlotProps: slotProps.badge, - ownerState, - className: classes.badge, - }); - - return ( - - {children} - {displayValue} - - ); -}) as OverridableComponent; - -Badge.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * The anchor of the badge. - * @default { - * vertical: 'top', - * horizontal: 'right', - * } - */ - anchorOrigin: PropTypes.shape({ - horizontal: PropTypes.oneOf(['left', 'right']).isRequired, - vertical: PropTypes.oneOf(['bottom', 'top']).isRequired, - }), - /** - * The content rendered within the badge. - */ - badgeContent: PropTypes.node, - /** - * The badge will be added relative to this node. - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'error' - */ - color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['error', 'info', 'primary', 'secondary', 'success', 'tertiary', 'warning']), - PropTypes.string, - ]), - /** - * The component used for the root node. - * Either a string to use a HTML element or a component. - */ - component: PropTypes.elementType, - /** - * If `true`, the badge is invisible. - * @default false - */ - invisible: PropTypes.bool, - /** - * Max count to show. - * @default 99 - */ - max: PropTypes.number, - /** - * Wrapped shape the badge should overlap. - * @default 'rectangular' - */ - overlap: PropTypes.oneOf(['circular', 'rectangular']), - /** - * Controls whether the badge is hidden when `badgeContent` is zero. - * @default false - */ - showZero: PropTypes.bool, - /** - * The size to use. - * @default 'large' - */ - size: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['small', 'large']), - PropTypes.string, - ]), - /** - * The props used for each slot inside the Badge. - * @default {} - */ - slotProps: PropTypes.shape({ - badge: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - }), - /** - * The components used for each slot inside the Badge. - * Either a string to use a HTML element or a component. - * @default {} - */ - slots: PropTypes.shape({ - badge: PropTypes.elementType, - root: PropTypes.elementType, - }), - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), - PropTypes.func, - PropTypes.object, - ]), - /** - * The variant to use. - * @default 'standard' - * @deprecated Use `size = 'small' | 'large'` instead - */ - variant: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['dot', 'standard']), - PropTypes.string, - ]), -} as any; - -export default Badge; diff --git a/packages/mui-material-next/src/Badge/Badge.types.ts b/packages/mui-material-next/src/Badge/Badge.types.ts deleted file mode 100644 index fe5eaae5429514..00000000000000 --- a/packages/mui-material-next/src/Badge/Badge.types.ts +++ /dev/null @@ -1,133 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { OverridableStringUnion, OverrideProps, PartiallyRequired } from '@mui/types'; -import { SlotComponentProps } from '@mui/base'; -import { Theme } from '../styles'; -import { BadgeClasses } from './badgeClasses'; - -export interface BadgePropsSizeOverrides {} - -export interface BadgePropsVariantOverrides {} - -export interface BadgePropsColorOverrides {} - -export interface BadgeOrigin { - vertical: 'top' | 'bottom'; - horizontal: 'left' | 'right'; -} - -export interface BadgeSlots { - /** - * The component that renders the root. - * @default 'span' - */ - root?: React.ElementType; - /** - * The component that renders the badge. - * @default 'span' - */ - badge?: React.ElementType; -} - -export interface BadgeTypeMap< - DefaultComponent extends React.ElementType = 'span', - AdditionalProps = {}, -> { - props: AdditionalProps & { - /** - * The anchor of the badge. - * @default { - * vertical: 'top', - * horizontal: 'right', - * } - */ - anchorOrigin?: BadgeOrigin; - /** - * The content rendered within the badge. - */ - badgeContent?: React.ReactNode; - /** - * The badge will be added relative to this node. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial; - /** - * @ignore - */ - className?: string; - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'error' - */ - color?: OverridableStringUnion< - 'primary' | 'secondary' | 'tertiary' | 'error' | 'info' | 'warning' | 'success', - BadgePropsColorOverrides - >; - /** - * If `true`, the badge is invisible. - * @default false - */ - invisible?: boolean; - /** - * Max count to show. - * @default 99 - */ - max?: number; - /** - * Wrapped shape the badge should overlap. - * @default 'rectangular' - */ - overlap?: 'rectangular' | 'circular'; - /** - * Controls whether the badge is hidden when `badgeContent` is zero. - * @default false - */ - showZero?: boolean; - /** - * The props used for each slot inside the Badge. - * @default {} - */ - slotProps?: { - root?: SlotComponentProps<'span', {}, BadgeOwnerState>; - badge?: SlotComponentProps<'span', {}, BadgeOwnerState>; - }; - /** - * The components used for each slot inside the Badge. - * Either a string to use a HTML element or a component. - * @default {} - */ - slots?: BadgeSlots; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps; - /** - * The size to use. - * @default 'large' - */ - size?: OverridableStringUnion<'small' | 'large', BadgePropsSizeOverrides>; - /** - * The variant to use. - * @default 'standard' - * @deprecated Use `size = 'small' | 'large'` instead - */ - variant?: OverridableStringUnion<'dot' | 'standard', BadgePropsVariantOverrides>; - }; - defaultComponent: DefaultComponent; -} - -export type BadgeProps< - RootComponent extends React.ElementType = BadgeTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps, RootComponent>; - -export interface BadgeOwnerState - extends PartiallyRequired< - BadgeProps, - 'anchorOrigin' | 'color' | 'overlap' | 'size' | 'variant' - > {} diff --git a/packages/mui-material-next/src/Badge/badgeClasses.ts b/packages/mui-material-next/src/Badge/badgeClasses.ts deleted file mode 100644 index 153f3bc7341248..00000000000000 --- a/packages/mui-material-next/src/Badge/badgeClasses.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { - unstable_generateUtilityClasses as generateUtilityClasses, - unstable_generateUtilityClass as generateUtilityClass, -} from '@mui/utils'; - -export interface BadgeClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the badge `span` element. */ - badge: string; - /** Styles applied to the badge `span` element if `size="small"`. */ - small: string; - /** Styles applied to the badge `span` element if `size="large"`. */ - large: string; - /** Styles applied to the badge `span` element if `variant="dot"`. */ - dot: string; - /** Styles applied to the badge `span` element if `variant="standard"`. */ - standard: string; - /** Styles applied to the badge `span` element if `anchorOrigin={{ 'top', 'right' }}`. */ - anchorOriginTopRight: string; - /** Styles applied to the badge `span` element if `anchorOrigin={{ 'bottom', 'right' }}`. */ - anchorOriginBottomRight: string; - /** Styles applied to the badge `span` element if `anchorOrigin={{ 'top', 'left' }}`. */ - anchorOriginTopLeft: string; - /** Styles applied to the badge `span` element if `anchorOrigin={{ 'bottom', 'left' }}`. */ - anchorOriginBottomLeft: string; - /** State class applied to the badge `span` element if `invisible={true}`. */ - invisible: string; - /** Styles applied to the badge `span` element if `color="error"`. */ - colorError: string; - /** Styles applied to the badge `span` element if `color="primary"`. */ - colorPrimary: string; - /** Styles applied to the badge `span` element if `color="secondary"`. */ - colorSecondary: string; - /** Styles applied to the badge `span` element if `color="tertiary"`. */ - colorTertiary: string; - /** Styles applied to the badge `span` element if `color="info"`. */ - colorInfo: string; - /** Styles applied to the badge `span` element if `color="warning"`. */ - colorWarning: string; - /** Styles applied to the badge `span` element if `color="success"`. */ - colorSuccess: string; - /** Styles applied to the badge `span` element if `overlap="rectangular"`. */ - overlapRectangular: string; - /** Styles applied to the badge `span` element if `overlap="circular"`. */ - overlapCircular: string; -} - -export type BadgeClassKey = keyof BadgeClasses; - -export function getBadgeUtilityClass(slot: string): string { - return generateUtilityClass('MuiBadge', slot); -} - -const badgeClasses: BadgeClasses = generateUtilityClasses('MuiBadge', [ - 'root', - 'badge', - 'small', - 'large', - 'dot', - 'standard', - 'anchorOriginTopRight', - 'anchorOriginBottomRight', - 'anchorOriginTopLeft', - 'anchorOriginBottomLeft', - 'invisible', - 'colorError', - 'colorPrimary', - 'colorSecondary', - 'colorTertiary', - 'colorInfo', - 'colorWarning', - 'colorSuccess', - 'overlapRectangular', - 'overlapCircular', -]); - -export default badgeClasses; diff --git a/packages/mui-material-next/src/Badge/index.ts b/packages/mui-material-next/src/Badge/index.ts deleted file mode 100644 index 8887c322ea8710..00000000000000 --- a/packages/mui-material-next/src/Badge/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './Badge'; -export * from './Badge.types'; -export { default as badgeClasses } from './badgeClasses'; -export * from './badgeClasses'; diff --git a/packages/mui-material-next/src/Button/Button.spec.tsx b/packages/mui-material-next/src/Button/Button.spec.tsx deleted file mode 100644 index 1260f9caa21a3e..00000000000000 --- a/packages/mui-material-next/src/Button/Button.spec.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import * as React from 'react'; -import { Link as ReactRouterLink, LinkProps } from 'react-router-dom'; -import { expectType } from '@mui/types'; -import Button, { ButtonProps } from '@mui/material-next/Button'; - -const log = console.log; - -const TestOverride = React.forwardRef((props, ref) => ( -
-)); - -const FakeIcon =
Icon
; - -const buttonTest = () => ( -
- - - - - - - - - - - - - - - {/* By default the underlying component is a button element */} - - {/* If an href is provided, an anchor is used */} - - {/* If a component prop is specified, use that: */} - - component="div" - ref={(elem) => { - expectType(elem); - }} - onClick={(e) => { - expectType, typeof e>(e); - log(e); - }} - > - Div - - { - // Can't have an onClick handler if the overriding component doesn't specify one: - // @ts-expect-error - component={TestOverride} onClick={log}> - TestOverride - - } - - -
-); - -const ReactRouterLinkTest = () => { - function ButtonLink(props: ButtonProps) { - return - ); -}; diff --git a/packages/mui-material-next/src/Button/Button.test.js b/packages/mui-material-next/src/Button/Button.test.js deleted file mode 100644 index bf13fae97f51b5..00000000000000 --- a/packages/mui-material-next/src/Button/Button.test.js +++ /dev/null @@ -1,239 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { spy } from 'sinon'; -import { createRenderer, fireEvent, act } from '@mui-internal/test-utils'; -import { camelCase } from 'lodash'; -import Button, { buttonClasses as classes } from '@mui/material-next/Button'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -import describeConformance from '../../test/describeConformance'; - -describe(', () => ({ - classes, - render, - inheritComponent: 'button', - refInstanceof: window.HTMLButtonElement, - muiName: 'MuiButton', - testDeepOverrides: { slotName: 'startIcon', slotClassName: classes.startIcon }, - testVariantProps: { variant: 'contained', fullWidth: true }, - testStateOverrides: { prop: 'size', value: 'small', styleKey: 'sizeSmall' }, - ThemeProvider: CssVarsProvider, - createTheme: extendTheme, - skip: ['componentsProp'], - })); - - it('should render with the root, text and colorPrimary classes but no others', () => { - const { getByRole } = render(); - const button = getByRole('button'); - - expect(button).to.have.class(classes.root); - expect(button).to.have.class(classes.text); - expect(button).to.have.class(classes.colorPrimary); - expect(button).not.to.have.class(classes.filled); - expect(button).not.to.have.class(classes.filledTonal); - expect(button).not.to.have.class(classes.outlined); - expect(button).not.to.have.class(classes.elevated); - expect(button).not.to.have.class(classes.colorSecondary); - expect(button).not.to.have.class(classes.colorTertiary); - expect(button).not.to.have.class(classes.sizeSmall); - expect(button).not.to.have.class(classes.sizeLarge); - }); - - it('should render a text secondary button', () => { - const { getByRole } = render(); - const button = getByRole('button'); - - expect(button).to.have.class(classes.root); - expect(button).to.have.class(classes.text); - expect(button).to.have.class(classes.colorSecondary); - expect(button).not.to.have.class(classes.colorPrimary); - }); - - ['filled', 'filledTonal', 'outlined', 'elevated'].forEach((variant) => { - it(`should render an ${variant} button`, () => { - const { getByRole } = render(); - const button = getByRole('button'); - - expect(button).to.have.class(classes.root); - expect(button).to.have.class(classes[variant]); - expect(button).to.have.class(classes.colorPrimary); - expect(button).not.to.have.class(classes.text); - }); - - // these two variants do not support different colors - if (variant !== 'elevated' && variant !== 'filledTonal') { - it(`should render an ${variant} secondary button`, () => { - const { getByRole } = render( - , - ); - const button = getByRole('button'); - - expect(button).to.have.class(classes.root); - expect(button).to.have.class(classes[variant]); - expect(button).to.have.class(classes.colorSecondary); - expect(button).not.to.have.class(classes.text); - expect(button).not.to.have.class(classes.colorPrimary); - }); - - it(`should render an ${variant} tertiary button`, () => { - const { getByRole } = render( - , - ); - const button = getByRole('button'); - - expect(button).to.have.class(classes.root); - expect(button).to.have.class(classes[variant]); - expect(button).to.have.class(classes.colorTertiary); - expect(button).not.to.have.class(classes.text); - expect(button).not.to.have.class(classes.colorPrimary); - }); - } - }); - - it('should render a small button', () => { - const { getByRole } = render(); - const button = getByRole('button'); - - expect(button).to.have.class(classes.root); - expect(button).to.have.class(classes.sizeSmall); - expect(button).not.to.have.class(classes.sizeMedium); - expect(button).not.to.have.class(classes.sizeLarge); - }); - - it('should render a large button', () => { - const { getByRole } = render(); - const button = getByRole('button'); - - expect(button).to.have.class(classes.root); - expect(button).to.have.class(classes.sizeLarge); - expect(button).not.to.have.class(classes.sizeMedium); - expect(button).not.to.have.class(classes.sizeSmall); - }); - - it('should render a button with startIcon', () => { - const { getByRole } = render(); - const button = getByRole('button'); - const startIcon = button.querySelector(`.${classes.startIcon}`); - - expect(button).to.have.class(classes.root); - expect(button).to.have.class(classes.text); - expect(startIcon).not.to.have.class(classes.endIcon); - }); - - it('should render a button with endIcon', () => { - const { getByRole } = render(); - const button = getByRole('button'); - const endIcon = button.querySelector(`.${classes.endIcon}`); - - expect(button).to.have.class(classes.root); - expect(button).to.have.class(classes.text); - expect(endIcon).not.to.have.class(classes.startIcon); - }); - - it('can disable the elevation', () => { - const { getByRole } = render(); - const button = getByRole('button'); - - expect(button).to.have.class(classes.disableElevation); - }); - - describe('server-side', () => { - before(function beforeHook() { - // Only run the test on node. - if (!/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - }); - - it('should server-side render', () => { - const { container } = renderToString(); - expect(container.firstChild).to.have.text('Hello World'); - }); - }); - - it('should automatically change the button to an anchor element when href is provided', () => { - const { container } = render(); - const button = container.firstChild; - - expect(button).to.have.property('nodeName', 'A'); - expect(button).not.to.have.attribute('role'); - expect(button).not.to.have.attribute('type'); - expect(button).to.have.attribute('href', 'https://google.com'); - }); - - it('should render disabled class', () => { - const disabledClassName = 'testDisabledClassName'; - const { container } = render( - eventLog.push('blur')} - onFocus={() => eventLog.push('focus')} - onFocusVisible={() => eventLog.push('focus-visible')} - > - Hello - -
- ); - } - const { getByText } = render(); - const buttonBase = getByText('Hello'); - const focusRetarget = getByText('you cannot escape me'); - simulatePointerDevice(); - - focusVisible(buttonBase); - - expect(focusRetarget).toHaveFocus(); - expect(eventLog).to.deep.equal(['focus-visible', 'focus', 'blur']); - expect(buttonBase).not.to.have.class(classes.focusVisible); - }); - - it('onFocusVisibleHandler() should propagate call to onFocusVisible prop', () => { - const onFocusVisibleSpy = spy(); - const { getByRole } = render( - - Hello - , - ); - simulatePointerDevice(); - - focusVisible(getByRole('button')); - - expect(onFocusVisibleSpy.calledOnce).to.equal(true); - expect(onFocusVisibleSpy.firstCall.args).to.have.lengthOf(1); - }); - - it('can be autoFocused', () => { - // as of react@16.8.6 autoFocus causes focus to be emitted before refs - // so we need to check if we're resilient against it - const { getByText } = render(Hello); - - expect(getByText('Hello')).toHaveFocus(); - }); - }); - - describe('event: keydown', () => { - describe('prop: onKeyDown', () => { - it('call it when keydown events are dispatched', () => { - const onKeyDownSpy = spy(); - const { getByText } = render( - - Hello - , - ); - - fireEvent.keyDown(getByText('Hello')); - - expect(onKeyDownSpy.callCount).to.equal(1); - }); - }); - - describe('prop: disableTouchRipple', () => { - it('creates no ripples on click', () => { - const { getByText } = render( - - Hello - , - ); - const button = getByText('Hello'); - - fireEvent.click(button); - - expect(button).not.to.have.class('ripple-visible'); - }); - }); - - describe('prop: disableRipple', () => { - const touchRippleTestId = 'touch-ripple-test-id'; - - it('is enabled by default', () => { - const { getAllByTestId } = render( - - Hello World - , - ); - - expect(getAllByTestId(touchRippleTestId).length).to.equal(1); - }); - - it('removes the TouchRipple when disableRipple is true', () => { - const { queryByTestId } = render( - - Hello World - , - ); - - expect(queryByTestId(touchRippleTestId)).to.equal(null); - }); - }); - - describe('keyboard accessibility for non interactive elements', () => { - it('does not call onClick when a spacebar is pressed on the element but prevents the default', () => { - const onKeyDown = spy(); - const onClickSpy = spy(); - const { getByRole } = render( - - Hello - , - ); - const button = getByRole('button'); - - act(() => { - button.focus(); - fireEvent.keyDown(button, { - key: ' ', - }); - }); - - expect(onClickSpy.callCount).to.equal(0); - expect(onKeyDown.callCount).to.equal(1); - expect(onKeyDown.firstCall.args[0]).to.have.property('defaultPrevented', true); - }); - - it('does call onClick when a spacebar is released on the element', () => { - const onClickSpy = spy(); - const { getByRole } = render( - - Hello - , - ); - const button = getByRole('button'); - - act(() => { - button.focus(); - fireEvent.keyUp(button, { - key: ' ', - }); - }); - - expect(onClickSpy.callCount).to.equal(1); - expect(onClickSpy.firstCall.args[0]).to.have.property('defaultPrevented', false); - }); - - it('does not call onClick when a spacebar is released and the default is prevented', () => { - const onClickSpy = spy(); - const onKeyUp: MuiCancellableEventHandler = (event) => { - event.preventDefault(); - event.defaultMuiPrevented = true; - }; - const { getByRole } = render( - - Hello - , - ); - const button = getByRole('button'); - - act(() => { - button.focus(); - fireEvent.keyUp(button, { - key: ' ', - }); - }); - - expect(onClickSpy.callCount).to.equal(0); - }); - - it('calls onClick when Enter is pressed on the element', () => { - const onClickSpy = spy(); - const { getByRole } = render( - - Hello - , - ); - const button = getByRole('button'); - - act(() => { - button.focus(); - fireEvent.keyDown(button, { - key: 'Enter', - }); - }); - - expect(onClickSpy.calledOnce).to.equal(true); - expect(onClickSpy.firstCall.args[0]).to.have.property('defaultPrevented', true); - }); - - it('does not call onClick if Enter was pressed on a child', () => { - const onClickSpy = spy(); - const onKeyDownSpy = spy(); - render( - - - , - ); - - fireEvent.keyDown(screen.getByRole('textbox'), { - key: 'Enter', - }); - - expect(onKeyDownSpy.callCount).to.equal(1); - expect(onClickSpy.callCount).to.equal(0); - }); - - it('does not call onClick if Space was released on a child', () => { - const onClickSpy = spy(); - const onKeyUpSpy = spy(); - render( - - - , - ); - - fireEvent.keyUp(screen.getByRole('textbox'), { - key: ' ', - }); - - expect(onKeyUpSpy.callCount).to.equal(1); - expect(onClickSpy.callCount).to.equal(0); - }); - - it('prevents default with an anchor and empty href', () => { - const onClickSpy = spy(); - const { getByRole } = render( - - Hello - , - ); - const button = getByRole('button'); - - act(() => { - button.focus(); - fireEvent.keyDown(button, { key: 'Enter' }); - }); - - expect(onClickSpy.calledOnce).to.equal(true); - expect(onClickSpy.firstCall.args[0]).to.have.property('defaultPrevented', true); - }); - - it('should ignore anchors with href', () => { - const onClick = spy(); - const onKeyDown = spy(); - const { getByText } = render( - - Hello - , - ); - const button = getByText('Hello'); - - act(() => { - button.focus(); - fireEvent.keyDown(button, { - key: 'Enter', - }); - }); - - expect(onClick.callCount).to.equal(0); - expect(onKeyDown.callCount).to.equal(1); - expect(onKeyDown.firstCall.args[0]).to.have.property('defaultPrevented', false); - }); - }); - }); - - describe('prop: action', () => { - it('should be able to focus visible the button', () => { - /** - * @type {React.RefObject} - */ - const buttonActionsRef = React.createRef(); - const { getByText } = render( - - Hello - , - ); - - // @ts-ignore - expect(typeof buttonActionsRef.current.focusVisible).to.equal('function'); - - act(() => { - // @ts-ignore - buttonActionsRef.current.focusVisible(); - }); - - expect(getByText('Hello')).toHaveFocus(); - expect(getByText('Hello')).to.match('.focusVisible'); - }); - }); - - describe('warnings', () => { - beforeEach(() => { - PropTypes.resetWarningCache(); - }); - - it('warns on invalid `component` prop: ref forward', function test() { - // Only run the test on node. On the browser the thrown error is not caught - if (!/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - - function Component(props: React.HTMLAttributes) { - return - )); - - // cant match the error message here because flakiness with mocha watchmode - - expect(() => { - render(); - }).toErrorDev('Please make sure the children prop is rendered in this custom component.'); - }); - }); - - describe('prop: type', () => { - it('is `button` by default', () => { - render(); - - expect(screen.getByRole('button')).to.have.property('type', 'button'); - }); - - it('can be changed to other button types', () => { - render(); - - expect(screen.getByRole('button')).to.have.property('type', 'submit'); - }); - - it('allows non-standard values', () => { - render(); - - expect(screen.getByRole('button')).to.have.attribute('type', 'fictional-type'); - // By spec non-supported types result in the default type for ` - , - () => ({ - classes, - inheritComponent: 'div', - render, - refInstanceof: window.HTMLDivElement, - testComponentPropWith: 'span', - muiName: 'MuiButtonGroup', - testVariantProps: { variant: 'filled' }, - skip: ['componentsProp'], - ThemeProvider: CssVarsProvider, - createTheme: extendTheme, - }), - ); - - it('should render with the root class but no others', () => { - const { container } = render( - - - , - ); - const buttonGroup = container.firstChild; - expect(buttonGroup).to.have.class(classes.root); - expect(buttonGroup).not.to.have.class(classes.filled); - expect(buttonGroup).not.to.have.class(classes.fullWidth); - }); - - it('should render an outlined button', () => { - const { getByRole } = render( - - - , - ); - const button = getByRole('button'); - const buttonGroup = getByRole('group'); - expect(button).to.have.class(buttonClasses.outlined); - expect(button).to.have.class(classes.grouped); - expect(buttonGroup).to.have.class(classes.outlined); - expect(buttonGroup).to.have.class(classes.primary); - expect(buttonGroup).not.to.have.class(classes.secondary); - }); - - it('can render an outlined primary button', () => { - const { getByRole } = render( - - - , - ); - const button = getByRole('button'); - const buttonGroup = getByRole('group'); - expect(button).to.have.class(buttonClasses.outlined); - expect(button).to.have.class(buttonClasses.colorPrimary); - expect(button).to.have.class(classes.grouped); - expect(buttonGroup).to.have.class(classes.outlined); - expect(buttonGroup).to.have.class(classes.primary); - expect(buttonGroup).not.to.have.class(classes.secondary); - }); - - it('can render a filled button', () => { - const { getByRole } = render( - - - , - ); - const button = getByRole('button'); - const buttonGroup = getByRole('group'); - expect(button).to.have.class(buttonClasses.filled); - expect(button).to.have.class(classes.grouped); - expect(buttonGroup).to.have.class(classes.filled); - expect(buttonGroup).to.have.class(classes.primary); - expect(buttonGroup).not.to.have.class(classes.secondary); - }); - - it('can render a small button', () => { - const { getByRole } = render( - - - , - ); - const button = getByRole('button'); - expect(button).to.have.class(buttonClasses.outlined); - expect(button).to.have.class(buttonClasses.sizeSmall); - }); - - it('can render a large button', () => { - const { getByRole } = render( - - - , - ); - const button = getByRole('button'); - expect(button).to.have.class(buttonClasses.outlined); - expect(button).to.have.class(buttonClasses.sizeLarge); - }); - - it('should have a ripple by default', () => { - const props = { TouchRippleProps: { classes: { root: 'touchRipple' } } }; - const { container } = render( - - - , - ); - expect(container.querySelector('.touchRipple')).not.to.equal(null); - }); - - it('can disable the elevation', () => { - const { getByRole } = render( - - - , - ); - const button = getByRole('button'); - expect(button).to.have.class(buttonClasses.disableElevation); - }); - - it('can disable the ripple', () => { - const props = { TouchRippleProps: { classes: { root: 'touchRipple' } } }; - const { container } = render( - - - , - ); - expect(container.querySelector('.touchRipple')).to.equal(null); - }); - - it('should not be fullWidth by default', () => { - const { container, getByRole } = render( - - - , - ); - const button = getByRole('button'); - const buttonGroup = container.firstChild; - expect(buttonGroup).not.to.have.class(classes.fullWidth); - expect(button).not.to.have.class(buttonClasses.fullWidth); - }); - - it('can pass fullWidth to Button', () => { - const { container, getByRole } = render( - - - , - ); - const buttonGroup = container.firstChild; - const button = getByRole('button'); - expect(buttonGroup).to.have.class(classes.fullWidth); - expect(button).to.have.class(buttonClasses.fullWidth); - }); - - it('classes.grouped should be merged with Button className', () => { - render( - - - , - ); - expect(screen.getByRole('button')).to.have.class(classes.grouped); - expect(screen.getByRole('button')).to.have.class('foo-bar'); - }); - - it('should forward the context to children', () => { - let context: ButtonGroupContextType | null; - render( - - - {(value) => { - context = value; - return - - , - ); - - expect(screen.getByRole('button')).to.have.class(buttonClasses.outlined); - expect(screen.getByRole('button')).to.have.class(buttonClasses.sizeSmall); - expect(screen.getByRole('button')).to.have.class(buttonClasses.colorSecondary); - }); - }); - - describe('position classes', () => { - it('correctly applies position classes to buttons', () => { - render( - - - - - , - ); - - const firstButton = screen.getAllByRole('button')[0]; - const middleButton = screen.getAllByRole('button')[1]; - const lastButton = screen.getAllByRole('button')[2]; - - expect(firstButton).to.have.class(classes.firstButton); - expect(firstButton).not.to.have.class(classes.middleButton); - expect(firstButton).not.to.have.class(classes.lastButton); - - expect(middleButton).to.have.class(classes.middleButton); - expect(middleButton).not.to.have.class(classes.firstButton); - expect(middleButton).not.to.have.class(classes.lastButton); - - expect(lastButton).to.have.class(classes.lastButton); - expect(lastButton).not.to.have.class(classes.middleButton); - expect(lastButton).not.to.have.class(classes.firstButton); - }); - - it('does not apply any position classes to a single button', () => { - render( - - - , - ); - - const button = screen.getByRole('button'); - - expect(button).not.to.have.class(classes.firstButton); - expect(button).not.to.have.class(classes.middleButton); - expect(button).not.to.have.class(classes.lastButton); - }); - }); -}); diff --git a/packages/mui-material-next/src/ButtonGroup/ButtonGroup.tsx b/packages/mui-material-next/src/ButtonGroup/ButtonGroup.tsx deleted file mode 100644 index e61482b570cf53..00000000000000 --- a/packages/mui-material-next/src/ButtonGroup/ButtonGroup.tsx +++ /dev/null @@ -1,371 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import { getValidReactChildren } from '@mui/utils'; -import { OverridableComponent } from '@mui/types'; -import styled from '../styles/styled'; -import useThemeProps from '../styles/useThemeProps'; -import buttonGroupClasses, { getButtonGroupUtilityClass } from './buttonGroupClasses'; -import ButtonGroupContext from './ButtonGroupContext'; -import ButtonGroupButtonContext from './ButtonGroupButtonContext'; -import { ButtonGroupOwnerState, ButtonGroupProps, ButtonGroupTypeMap } from './ButtonGroup.types'; - -const useUtilityClasses = (ownerState: ButtonGroupOwnerState) => { - const { classes, color, disabled, disableElevation, fullWidth, orientation, variant } = - ownerState; - - const slots = { - root: [ - 'root', - variant, - color, - orientation === 'vertical' && 'vertical', - fullWidth && 'fullWidth', - disableElevation && 'disableElevation', - ], - grouped: ['grouped', disabled && 'disabled'], - firstButton: ['firstButton'], - lastButton: ['lastButton'], - middleButton: ['middleButton'], - }; - - return composeClasses(slots, getButtonGroupUtilityClass, classes); -}; - -const ButtonGroupRoot = styled('div', { - name: 'MuiButtonGroup', - slot: 'Root', - overridesResolver(props, styles) { - const { - ownerState: { color, disableElevation, fullWidth, orientation, variant }, - } = props; - - return [ - { [`& .${buttonGroupClasses.grouped}`]: styles.grouped }, - { - [`& .${buttonGroupClasses.firstButton}`]: styles.firstButton, - }, - { - [`& .${buttonGroupClasses.lastButton}`]: styles.lastButton, - }, - { - [`& .${buttonGroupClasses.middleButton}`]: styles.middleButton, - }, - styles.root, - styles[color], - styles[variant], - disableElevation === true && styles.disableElevation, - fullWidth && styles.fullWidth, - orientation === 'vertical' && styles.vertical, - ]; - }, -})<{ ownerState: ButtonGroupOwnerState }>( - ({ - theme: { vars: tokens }, - ownerState: { disabled, disableElevation, fullWidth, orientation, variant }, - }) => ({ - display: 'inline-flex', - borderRadius: tokens.sys.shape.corner.full, - ...(variant === 'elevated' && { - boxShadow: tokens.sys.elevation[1], - }), - ...((disableElevation || disabled) && { - boxShadow: 'none', - }), - ...(fullWidth && { - width: '100%', - }), - ...(orientation === 'vertical' && { - flexDirection: 'column', - }), - [`& .${buttonGroupClasses.grouped}`]: { - minWidth: 40, - '&:hover, &:focus': { - boxShadow: 'none', - }, - boxShadow: 'none', - }, - [`& .${buttonGroupClasses.firstButton},& .${buttonGroupClasses.middleButton}`]: { - ...(orientation === 'horizontal' && { - borderTopRightRadius: 0, - borderBottomRightRadius: 0, - }), - ...(orientation === 'vertical' && { - borderBottomRightRadius: 0, - borderBottomLeftRadius: 0, - }), - ...(variant === 'filled' && - orientation === 'horizontal' && { - borderRight: `1px solid ${tokens.sys.color.outline}`, - [`&.${buttonGroupClasses.disabled}`]: { - borderRightColor: `rgba(${tokens.sys.color.outline}, 0.12)`, - }, - }), - ...(variant === 'filled' && - orientation === 'vertical' && { - borderBottom: `1px solid ${tokens.sys.color.outline}`, - [`&.${buttonGroupClasses.disabled}`]: { - borderBottomColor: `rgba(${tokens.sys.color.outline}, 0.12)`, - }, - }), - ...(variant === 'outlined' && - orientation === 'horizontal' && { - borderRightColor: 'transparent', - }), - ...(variant === 'outlined' && - orientation === 'vertical' && { - borderBottomColor: 'transparent', - }), - ...((variant === 'text' || variant === 'filledTonal' || variant === 'elevated') && - orientation === 'horizontal' && { - borderRight: `1px solid ${tokens.sys.color.outlineVariant}`, - [`&.${buttonGroupClasses.disabled}`]: { - borderRightColor: `rgba(${tokens.sys.color.outlineVariant}, 0.12)`, - }, - }), - ...((variant === 'text' || variant === 'filledTonal' || variant === 'elevated') && - orientation === 'vertical' && { - borderBottom: `1px solid ${tokens.sys.color.outlineVariant}`, - [`&.${buttonGroupClasses.disabled}`]: { - borderBottomColor: `rgba(${tokens.sys.color.outlineVariant}, 0.12)`, - }, - }), - '&:hover': { - ...(variant === 'outlined' && - orientation === 'horizontal' && { - borderRightColor: 'currentColor', - }), - ...(variant === 'outlined' && - orientation === 'vertical' && { - borderBottomColor: 'currentColor', - }), - }, - }, - [`& .${buttonGroupClasses.lastButton},& .${buttonGroupClasses.middleButton}`]: { - ...(orientation === 'horizontal' && { - borderTopLeftRadius: 0, - borderBottomLeftRadius: 0, - }), - ...(orientation === 'vertical' && { - borderTopRightRadius: 0, - borderTopLeftRadius: 0, - }), - ...(variant === 'outlined' && - orientation === 'horizontal' && { - marginLeft: -1, - }), - ...(variant === 'outlined' && - orientation === 'vertical' && { - marginTop: -1, - }), - }, - }), -); - -/** - * - * Demos: - * - * - [Button Group](https://mui.com/material-ui/react-button-group/) - * - * API: - * - * - [ButtonGroup API](https://mui.com/material-ui/api/button-group/) - */ -const ButtonGroup = React.forwardRef(function ButtonGroup< - BaseComponentType extends React.ElementType = ButtonGroupTypeMap['defaultComponent'], ->(inProps: ButtonGroupProps, ref: React.ForwardedRef) { - const props = useThemeProps({ props: inProps, name: 'MuiButtonGroup' }); - const { - children, - className, - color = 'primary', - component = 'div', - disabled = false, - disableElevation = false, - disableTouchRipple = false, - disableRipple = false, - fullWidth = false, - orientation = 'horizontal', - size = 'medium', - variant = 'outlined', - ...other - } = props; - - const ownerState = { - ...props, - color, - component, - disabled, - disableElevation, - disableTouchRipple, - disableRipple, - fullWidth, - orientation, - size, - variant, - }; - - const classes = useUtilityClasses(ownerState); - - const context = React.useMemo( - () => ({ - className: classes.grouped, - color, - disabled, - disableElevation, - disableTouchRipple, - disableRipple, - fullWidth, - size, - variant, - }), - [ - color, - disabled, - disableElevation, - disableTouchRipple, - disableRipple, - fullWidth, - size, - variant, - classes.grouped, - ], - ); - - const validChildren = getValidReactChildren(children); - const childrenCount = validChildren.length; - - const getButtonPositionClassName = (index: number) => { - const isFirstButton = index === 0; - const isLastButton = index === childrenCount - 1; - - if (isFirstButton && isLastButton) { - return ''; - } - if (isFirstButton) { - return classes.firstButton; - } - if (isLastButton) { - return classes.lastButton; - } - return classes.middleButton; - }; - - return ( - - - {validChildren.map((child, index) => { - return ( - - {child} - - ); - })} - - - ); -}) as OverridableComponent; - -ButtonGroup.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * The content of the component. - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'primary' - */ - color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['primary', 'secondary', 'tertiary']), - PropTypes.string, - ]), - /** - * The component used for the root node. - * Either a string to use a HTML element or a component. - */ - component: PropTypes.elementType, - /** - * If `true`, the component is disabled. - * @default false - */ - disabled: PropTypes.bool, - /** - * If `true`, no elevation is used. - * @default false - */ - disableElevation: PropTypes.bool, - /** - * If `true`, the button ripple effect is disabled. - * @default false - */ - disableRipple: PropTypes.bool, - /** - * If `true`, the touch ripple effect is disabled. - * @default false - */ - disableTouchRipple: PropTypes.bool, - /** - * If `true`, the buttons will take up the full width of its container. - * @default false - */ - fullWidth: PropTypes.bool, - /** - * The component orientation (layout flow direction). - * @default 'horizontal' - */ - orientation: PropTypes.oneOf(['horizontal', 'vertical']), - /** - * The size of the component. - * `small` is equivalent to the dense button styling. - * @default 'medium' - */ - size: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['small', 'medium', 'large']), - PropTypes.string, - ]), - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), - PropTypes.func, - PropTypes.object, - ]), - /** - * The variant to use. - * @default 'outlined' - */ - variant: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['elevated', 'filled', 'filledTonal', 'outlined', 'text']), - PropTypes.string, - ]), -} as any; - -export default ButtonGroup; diff --git a/packages/mui-material-next/src/ButtonGroup/ButtonGroup.types.ts b/packages/mui-material-next/src/ButtonGroup/ButtonGroup.types.ts deleted file mode 100644 index 0a4e713b418a0d..00000000000000 --- a/packages/mui-material-next/src/ButtonGroup/ButtonGroup.types.ts +++ /dev/null @@ -1,107 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { OverridableStringUnion, OverrideProps, PartiallyRequired } from '@mui/types'; -import { Theme } from '../styles'; -import { ButtonGroupClasses } from './buttonGroupClasses'; - -export interface ButtonGroupPropsColorOverrides {} -export interface ButtonGroupPropsVariantOverrides {} -export interface ButtonGroupPropsSizeOverrides {} - -export interface ButtonGroupOwnProps { - /** - * The content of the component. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial; - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'primary' - */ - color?: OverridableStringUnion< - 'primary' | 'secondary' | 'tertiary', - ButtonGroupPropsColorOverrides - >; - /** - * If `true`, the component is disabled. - * @default false - */ - disabled?: boolean; - /** - * If `true`, no elevation is used. - * @default false - */ - disableElevation?: boolean; - /** - * If `true`, the button ripple effect is disabled. - * @default false - */ - disableRipple?: boolean; - /** - * If `true`, the touch ripple effect is disabled. - * @default false - */ - disableTouchRipple?: boolean; - /** - * If `true`, the buttons will take up the full width of its container. - * @default false - */ - fullWidth?: boolean; - /** - * The component orientation (layout flow direction). - * @default 'horizontal' - */ - orientation?: 'vertical' | 'horizontal'; - /** - * The size of the component. - * `small` is equivalent to the dense button styling. - * @default 'medium' - */ - size?: OverridableStringUnion<'small' | 'medium' | 'large', ButtonGroupPropsSizeOverrides>; - /** - * The variant to use. - * @default 'outlined' - */ - variant?: OverridableStringUnion< - 'text' | 'outlined' | 'filled' | 'filledTonal' | 'elevated', - ButtonGroupPropsVariantOverrides - >; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps; -} - -export interface ButtonGroupTypeMap< - AdditionalProps = {}, - RootComponent extends React.ElementType = 'div', -> { - props: AdditionalProps & ButtonGroupOwnProps; - defaultComponent: RootComponent; -} - -export type ButtonGroupProps< - RootComponent extends React.ElementType = ButtonGroupTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps, RootComponent> & { - component?: React.ElementType; -}; - -export interface ButtonGroupOwnerState - extends PartiallyRequired< - ButtonGroupProps, - | 'color' - | 'disabled' - | 'disableElevation' - | 'disableRipple' - | 'disableTouchRipple' - | 'fullWidth' - | 'orientation' - | 'size' - | 'variant' - > {} diff --git a/packages/mui-material-next/src/ButtonGroup/ButtonGroupButtonContext.ts b/packages/mui-material-next/src/ButtonGroup/ButtonGroupButtonContext.ts deleted file mode 100644 index a93c0436fb729a..00000000000000 --- a/packages/mui-material-next/src/ButtonGroup/ButtonGroupButtonContext.ts +++ /dev/null @@ -1,14 +0,0 @@ -import * as React from 'react'; - -type ButtonPositionClassName = string; - -/** - * @ignore - internal component. - */ -const ButtonGroupButtonContext = React.createContext(null); - -if (process.env.NODE_ENV !== 'production') { - ButtonGroupButtonContext.displayName = 'ButtonGroupButtonContext'; -} - -export default ButtonGroupButtonContext; diff --git a/packages/mui-material-next/src/ButtonGroup/ButtonGroupContext.ts b/packages/mui-material-next/src/ButtonGroup/ButtonGroupContext.ts deleted file mode 100644 index 179c343793af2e..00000000000000 --- a/packages/mui-material-next/src/ButtonGroup/ButtonGroupContext.ts +++ /dev/null @@ -1,25 +0,0 @@ -import * as React from 'react'; -import type { ButtonGroupProps } from './ButtonGroup.types'; - -export interface ButtonGroupContextType { - className?: string; - color?: ButtonGroupProps['color']; - disabled?: boolean; - disableElevation?: boolean; - disableTouchRipple?: boolean; - disableRipple?: boolean; - fullWidth?: boolean; - size?: ButtonGroupProps['size']; - variant?: ButtonGroupProps['variant']; -} - -/** - * @ignore - internal component. - */ -const ButtonGroupContext = React.createContext(null); - -if (process.env.NODE_ENV !== 'production') { - ButtonGroupContext.displayName = 'ButtonGroupContext'; -} - -export default ButtonGroupContext; diff --git a/packages/mui-material-next/src/ButtonGroup/buttonGroupClasses.ts b/packages/mui-material-next/src/ButtonGroup/buttonGroupClasses.ts deleted file mode 100644 index 9eba539d56689b..00000000000000 --- a/packages/mui-material-next/src/ButtonGroup/buttonGroupClasses.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { - unstable_generateUtilityClass as generateUtilityClass, - unstable_generateUtilityClasses as generateUtilityClasses, -} from '@mui/utils'; - -export interface ButtonGroupClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if `variant="text"`. */ - text: string; - /** Styles applied to the root element if `variant="outlined"`. */ - outlined: string; - /** Styles applied to the root element if `variant="filled"`. */ - filled: string; - /** Styles applied to the root element if `variant="filledTonal"`. */ - filledTonal: string; - /** Styles applied to the root element if `variant="elevated"`. */ - elevated: string; - /** State class applied to the root element if `color="primary"`. */ - primary: string; - /** State class applied to the toot element if `color="secondary"`. */ - secondary: string; - /** State class applied to the root element if `color="tertiary"`. */ - tertiary: string; - /** Styles applied to the root element if `disableElevation={true}`. */ - disableElevation: string; - /** State class applied to the child elements if `disabled={true}`. */ - disabled: string; - /** Styles applied to the first button in the button group. */ - firstButton: string; - /** Styles applied to the root element if `fullWidth={true}`. */ - fullWidth: string; - /** Styles applied to the root element if `orientation="vertical"`. */ - vertical: string; - /** Styles applied to the children. */ - grouped: string; - /** Styles applied to the last button in the button group. */ - lastButton: string; - /** Styles applied to buttons in the middle of the button group. */ - middleButton: string; -} - -export type ButtonGroupClassKey = keyof ButtonGroupClasses; - -export function getButtonGroupUtilityClass(slot: string): string { - return generateUtilityClass('MuiButtonGroup', slot); -} - -const buttonGroupClasses: ButtonGroupClasses = generateUtilityClasses('MuiButtonGroup', [ - 'root', - 'text', - 'outlined', - 'filled', - 'filledTonal', - 'elevated', - 'primary', - 'secondary', - 'tertiary', - 'disableElevation', - 'disabled', - 'firstButton', - 'fullWidth', - 'vertical', - 'grouped', - 'lastButton', - 'middleButton', -]); - -export default buttonGroupClasses; diff --git a/packages/mui-material-next/src/ButtonGroup/index.ts b/packages/mui-material-next/src/ButtonGroup/index.ts deleted file mode 100644 index 36b1a38cd0f773..00000000000000 --- a/packages/mui-material-next/src/ButtonGroup/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -'use client'; -export { default } from './ButtonGroup'; -export { default as buttonGroupClasses } from './buttonGroupClasses'; -export * from './buttonGroupClasses'; -export { default as ButtonGroupButtonContext } from './ButtonGroupButtonContext'; -export { default as ButtonGroupContext } from './ButtonGroupContext'; diff --git a/packages/mui-material-next/src/Chip/Chip.test.tsx b/packages/mui-material-next/src/Chip/Chip.test.tsx deleted file mode 100644 index afe4eadf86d1b6..00000000000000 --- a/packages/mui-material-next/src/Chip/Chip.test.tsx +++ /dev/null @@ -1,754 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { spy, stub } from 'sinon'; -import userEvent from '@testing-library/user-event'; -import { - act, - createRenderer, - fireEvent, - focusVisible, - simulatePointerDevice, - programmaticFocusTriggersFocusVisible, -} from '@mui-internal/test-utils'; -import { hexToRgb } from '@mui/system'; -import { unstable_capitalize as capitalize } from '@mui/utils'; -import { createTheme } from '@mui/material/styles'; -import Avatar from '@mui/material/Avatar'; -import Chip, { chipClasses as classes } from '@mui/material-next/Chip'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -import CheckBox from '../internal/svg-icons/CheckBox'; -import { ChipProps } from './Chip.types'; -import describeConformance from '../../test/describeConformance'; - -// TODO: remove after migrating SvgIcon to support Material Design 3 colors -const MaterialV5DefaultTheme = createTheme(); - -describe('', () => { - let originalMatchmedia: typeof window.matchMedia; - - beforeEach(() => { - originalMatchmedia = window.matchMedia; - window.matchMedia = () => - ({ - addListener: () => {}, - removeListener: () => {}, - }) as unknown as MediaQueryList; - }); - - afterEach(() => { - window.matchMedia = originalMatchmedia; - }); - - const { render } = createRenderer(); - - describeConformance(, () => ({ - ThemeProvider: CssVarsProvider, - createTheme: extendTheme, - classes, - inheritComponent: 'div', - render, - muiName: 'MuiChip', - testDeepOverrides: { slotName: 'label', slotClassName: classes.label }, - testVariantProps: { variant: 'outlined' }, - testStateOverrides: { prop: 'size', value: 'small', styleKey: 'sizeSmall' }, - refInstanceof: window.HTMLDivElement, - testComponentPropWith: 'span', - skip: ['componentsProp'], - })); - - describe('text only', () => { - it('is not in tab order', () => { - const { container } = render(); - - expect(container.querySelectorAll('[tabindex]')).to.have.length(0); - }); - - it('should renders certain classes and contains a label', () => { - const { container } = render(); - - const chip = container.querySelector(`.${classes.root}`); - const label = container.querySelector(`.${classes.label}`); - - expect(label).to.have.tagName('span'); - expect(label).to.have.text('My text Chip'); - - expect(chip).to.have.class(classes.root); - expect(chip).not.to.have.class(classes.colorPrimary); - expect(chip).not.to.have.class(classes.colorSecondary); - expect(chip).not.to.have.class(classes.clickable); - expect(chip).not.to.have.class(classes.deletable); - }); - - it('should render with the color class name based on the color prop', () => { - const colorOptions: NonNullable[] = [ - 'primary', - 'secondary', - 'info', - 'error', - 'warning', - 'success', - ]; - colorOptions.forEach((color) => { - const { container } = render(); - const chip = container.querySelector(`.${classes.root}`); - expect(chip).to.have.class(classes[`color${capitalize(color)}` as keyof typeof classes]); - }); - }); - }); - - describe('clickable chip', () => { - it('renders as a button in taborder with the label as the accessible name', () => { - const { getByRole } = render( {}} />); - - const button = getByRole('button'); - expect(button).to.have.property('tabIndex', 0); - expect(button).toHaveAccessibleName('My Chip'); - }); - - it('should render link with the button base', () => { - const { getByTestId } = render( - , - ); - - const chipRoot = getByTestId('root'); - - expect(chipRoot).to.have.class('MuiButtonBase-root'); - expect(chipRoot).to.have.tagName('a'); - }); - - it('should render ripple', () => { - const { getByTestId } = render( - , - ); - - const chipRoot = getByTestId('root'); - - expect(chipRoot.querySelector('.MuiTouchRipple-root')).not.to.equal(null); - }); - - it('should disable ripple when MuiButtonBase has disableRipple in theme', () => { - const theme = extendTheme({ - components: { - MuiButtonBase: { - defaultProps: { - disableRipple: true, - }, - }, - }, - }); - - const { getByTestId } = render( - - - , - ); - - const chipRoot = getByTestId('root'); - - expect(chipRoot).to.have.class('MuiButtonBase-root'); - expect(chipRoot.querySelector('.MuiTouchRipple-root')).to.equal(null); - }); - - it('should apply user value of tabIndex', () => { - const { getByRole } = render( {}} tabIndex={5} />); - - expect(getByRole('button')).to.have.property('tabIndex', 5); - }); - - it('should render with the root and clickable class', () => { - const { container } = render( {}} />); - - const chip = container.querySelector(`.${classes.root}`); - expect(chip).to.have.class(classes.root); - expect(chip).to.have.class(classes.clickable); - }); - - it('should render with the root, clickable, and colorPrimary class', () => { - const { getByRole } = render( {}} color="primary" />); - - const button = getByRole('button'); - expect(button).to.have.class(classes.root); - expect(button).to.have.class(classes.colorPrimary); - expect(button).to.have.class(classes.clickable); - }); - - it('should render with the root, outlined, clickable, and colorPrimary class', () => { - const { container } = render( - {}} variant="outlined" />, - ); - - const chip = container.querySelector(`.${classes.root}`); - expect(chip).to.have.class(classes.root); - expect(chip).to.have.class(classes.colorPrimary); - expect(chip).to.have.class(classes.clickable); - expect(chip).to.have.class(classes.outlined); - }); - - it('should render with the root, outlined, clickable, and colorSecondary class', () => { - const { getByRole } = render( - {}} variant="outlined" />, - ); - - const button = getByRole('button'); - expect(button).to.have.class(classes.root); - expect(button).to.have.class(classes.colorSecondary); - expect(button).to.have.class(classes.clickable); - expect(button).to.have.class(classes.outlined); - }); - - it('should render with the root, filled, clickable, and colorPrimary class', () => { - const { getByRole } = render( - {}} variant="filled" />, - ); - - const chip = getByRole('button'); - expect(chip).to.have.class(classes.root); - expect(chip).to.have.class(classes.colorPrimary); - expect(chip).to.have.class(classes.clickable); - expect(chip).to.have.class(classes.filled); - }); - - it('should not be focused when a deletable chip is disabled', () => { - const { getByTestId } = render( - {}} />, - ); - - const chip = getByTestId('chip'); - - simulatePointerDevice(); - act(() => { - fireEvent.keyDown(document.body, { key: 'Tab' }); - }); - - expect(chip).to.have.class(classes.root); - expect(chip).not.to.have.class(classes.focusVisible); - }); - - it('should render with the root, filled, clickable, and colorSecondary class', () => { - const { getByRole } = render( - {}} variant="filled" />, - ); - - const chip = getByRole('button'); - expect(chip).to.have.class(classes.root); - expect(chip).to.have.class(classes.colorSecondary); - expect(chip).to.have.class(classes.clickable); - expect(chip).to.have.class(classes.filled); - }); - }); - - describe('deletable Avatar chip', () => { - it('should render a button in tab order with the avatar', () => { - const { container, getByRole } = render( - MB} - label="Text Avatar Chip" - onDelete={() => {}} - />, - ); - - expect(getByRole('button')).to.have.property('tabIndex', 0); - expect(container.querySelector('#avatar')).not.to.equal(null); - }); - - it('should not create ripples', () => { - const { getByTestId } = render( - MB} onDelete={() => {}} />, - ); - - const chipRoot = getByTestId('root'); - - expect(chipRoot.querySelector('.MuiTouchRipple-root')).to.equal(null); - }); - - it('should apply user value of tabIndex', () => { - const { container, getByRole } = render( - MB} - label="Text Avatar Chip" - onDelete={() => {}} - tabIndex={5} - />, - ); - - expect(getByRole('button')).to.have.property('tabIndex', 5); - const elementsInTabOrder = Array.from(container.querySelectorAll('[tabIndex]')).filter( - (element) => (element as HTMLElement).tabIndex >= 0, - ); - expect(elementsInTabOrder).to.have.length(1); - }); - - it('fires onDelete when clicking the delete icon', () => { - const handleDelete = spy(); - const { getByTestId } = render( - MB} - label="Text Avatar Chip" - onDelete={handleDelete} - deleteIcon={
} - />, - ); - const deleteIcon = getByTestId('delete-icon'); - - fireEvent.click(deleteIcon); - - expect(handleDelete.callCount).to.equal(1); - }); - - it('should stop propagation when clicking the delete icon', () => { - const handleClick = spy(); - const { getByTestId } = render( - MB} - label="Text Avatar Chip" - onClick={handleClick} - onDelete={() => {}} - deleteIcon={
} - />, - ); - const deleteIcon = getByTestId('delete-icon'); - - fireEvent.click(deleteIcon); - - expect(handleClick.callCount).to.equal(0); - }); - - it('should render with the root, deletable classes', () => { - const { container } = render( - MB} - label="Text Avatar Chip" - onDelete={() => {}} - />, - ); - - const chip = container.querySelector(`.${classes.root}`); - expect(chip).to.have.class(classes.deletable); - }); - - it('should render with the root, deletable, and colorPrimary classes', () => { - const { container } = render( - MB} - label="Text Avatar Chip" - onDelete={() => {}} - color="primary" - />, - ); - - const chip = container.querySelector(`.${classes.root}`); - expect(chip).to.have.class(classes.colorPrimary); - expect(chip).to.have.class(classes.deletable); - }); - - it('should render with the root, deletable, and colorSecondary classes', () => { - const { container } = render( - MB} - label="Text Avatar Chip" - onDelete={() => {}} - color="secondary" - />, - ); - - const chip = container.querySelector(`.${classes.root}`); - expect(chip).to.have.class(classes.colorSecondary); - expect(chip).to.have.class(classes.deletable); - }); - }); - - describe('prop: deleteIcon', () => { - it('should render a default icon with the root, deletable and deleteIcon classes', () => { - const { getByRole, getByTestId } = render( - {}} />, - ); - - const chip = getByRole('button'); - const icon = getByTestId('ClearIcon'); - - expect(chip).to.have.class(classes.deletable); - expect(chip).to.contain(icon); - expect(icon).to.have.class(classes.deleteIcon); - }); - - it('should render default icon with the root, deletable, colorPrimary, and deleteIcon classes', () => { - const { container, getByTestId } = render( - {}} color="primary" />, - ); - - const chip = container.querySelector(`.${classes.root}`); - expect(chip).to.have.class(classes.colorPrimary); - expect(chip).to.have.class(classes.deletable); - const icon = getByTestId('ClearIcon'); - expect(icon).to.have.class(classes.deleteIcon); - }); - - it('should render default icon with the root, deletable, colorSecondary, and deleteIcon classes', () => { - const { container, getByTestId } = render( - {}} color="secondary" />, - ); - - const chip = container.querySelector(`.${classes.root}`); - expect(chip).to.have.class(classes.colorSecondary); - expect(chip).to.have.class(classes.deletable); - const icon = getByTestId('ClearIcon'); - expect(icon).to.have.class(classes.deleteIcon); - }); - - it('should render default icon with the root, deletable, deleteIcon primary class and deleteIcon filled primary class', () => { - const { container, getByTestId } = render( - {}} - color="primary" - variant="filled" - />, - ); - - const chip = container.querySelector(`.${classes.root}`); - expect(chip).to.have.class(classes.colorPrimary); - expect(chip).to.have.class(classes.deletable); - const icon = getByTestId('ClearIcon'); - expect(icon).to.have.class(classes.deleteIcon); - }); - - it('should render default icon with the root, deletable, deleteIcon primary class and deleteIcon outlined primary class', () => { - const { container, getByTestId } = render( - {}} - color="primary" - variant="outlined" - />, - ); - - const chip = container.querySelector(`.${classes.root}`); - expect(chip).to.have.class(classes.colorPrimary); - expect(chip).to.have.class(classes.deletable); - const icon = getByTestId('ClearIcon'); - expect(icon).to.have.class(classes.deleteIcon); - }); - - it('accepts a custom icon', () => { - const handleDelete = spy(); - const { getByTestId } = render( - } />, - ); - - fireEvent.click(getByTestId('CheckBoxIcon')); - - expect(handleDelete.callCount).to.equal(1); - }); - }); - - describe('reacts to keyboard chip', () => { - it('should call onKeyDown when a key is pressed', () => { - const handleKeydown = stub().callsFake((event) => event.key); - const { getByRole } = render( {}} onKeyDown={handleKeydown} />); - const chip = getByRole('button'); - act(() => { - chip.focus(); - }); - - fireEvent.keyDown(chip, { key: 'p' }); - - expect(handleKeydown.callCount).to.equal(1); - expect(handleKeydown.firstCall.returnValue).to.equal('p'); - }); - - it('should unfocus when a esc key is pressed', () => { - const handleBlur = spy(); - const { getByRole } = render( {}} />); - const chip = getByRole('button'); - act(() => { - chip.focus(); - }); - - fireEvent.keyUp(chip, { key: 'Escape' }); - - expect(handleBlur.callCount).to.equal(1); - expect(chip).not.toHaveFocus(); - }); - - it('should call onClick when `space` is released ', async function test() { - const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); - // userEvent.setup() requires Safari 14 or up to work - // We can remove this check once we drop support for Safari 13 - if (isSafari) { - this.skip(); - } - - const user = userEvent.setup(); - const handleClick = spy(); - const { getByRole } = render(); - const chip = getByRole('button'); - act(() => { - chip.focus(); - }); - - await user.keyboard('{ >}'); // press space without releasing - - expect(handleClick.callCount).to.equal(0); - - await user.keyboard('{/ }'); // release space - - expect(handleClick.callCount).to.equal(1); - }); - - it('should call onClick when `enter` is pressed ', async () => { - const handleClick = spy(); - const { getByRole } = render(); - const chip = getByRole('button'); - act(() => { - chip.focus(); - }); - - await userEvent.keyboard('{enter}'); - - expect(handleClick.callCount).to.equal(1); - }); - - describe('prop: onDelete', () => { - ['Backspace', 'Delete'].forEach((key) => { - it(`should call onDelete '${key}' is released`, () => { - const handleDelete = spy(); - const handleKeyDown = spy(); - const { getAllByRole } = render( - {}} onKeyDown={handleKeyDown} onDelete={handleDelete} />, - ); - const chip = getAllByRole('button')[0]; - act(() => { - chip.focus(); - }); - - fireEvent.keyDown(chip, { key }); - - // defaultPrevented? - expect(handleKeyDown.callCount).to.equal(1); - expect(handleKeyDown.firstCall.args[0]).to.have.property('defaultPrevented', true); - expect(handleDelete.callCount).to.equal(0); - - fireEvent.keyUp(chip, { key }); - - expect(handleDelete.callCount).to.equal(1); - }); - }); - - it('should not prevent default on input', () => { - const handleKeyDown = spy(); - const { getByTestId } = render( - } onKeyDown={handleKeyDown} />, - ); - const input = getByTestId('input'); - - act(() => { - input.focus(); - }); - fireEvent.keyDown(input, { key: 'Backspace' }); - - expect(handleKeyDown.callCount).to.equal(1); - expect(handleKeyDown.firstCall.args[0]).to.have.property('defaultPrevented', false); - }); - }); - - describe('with children that generate events', () => { - ['Backspace', 'Delete'].forEach((key) => { - it(`should not call onDelete for child keyup event when '${key}' is released`, () => { - const handleDelete = spy(); - const handleKeyUp = spy(); - const { getByTestId } = render( - - } - />, - ); - - fireEvent.keyUp(getByTestId('input'), { key }); - - expect(handleKeyUp.callCount).to.equal(1); - expect(handleDelete.callCount).to.equal(0); - }); - }); - - it(`should not call onClick for child keyup event when 'Space' is released`, () => { - const handleClick = spy(); - const handleKeyUp = spy(); - const { getByTestId } = render( - - } - />, - ); - - fireEvent.keyUp(getByTestId('input'), { key: ' ' }); - expect(handleKeyUp.callCount).to.equal(1); - expect(handleClick.callCount).to.equal(0); - }); - - it(`should not call onClick for child keydown event when 'Enter' is pressed`, () => { - const handleClick = spy(); - const handleKeyDown = spy(); - const { getByTestId } = render( - - } - />, - ); - - fireEvent.keyDown(getByTestId('input'), { key: 'Enter' }); - expect(handleKeyDown.callCount).to.equal(1); - expect(handleClick.callCount).to.equal(0); - }); - - it('should not call onClick for child event when `space` is released', () => { - const handleClick = spy(); - const handleKeyUp = spy(); - const { getByTestId } = render( - - } - />, - ); - - fireEvent.keyUp(getByTestId('input'), { key: ' ' }); - - expect(handleClick.callCount).to.equal(0); - expect(handleKeyUp.callCount).to.equal(1); - }); - - it('should not call onClick for child event when `enter` is pressed', () => { - const handleClick = spy(); - const handleKeyDown = spy(); - const { getByTestId } = render( - - } - />, - ); - - fireEvent.keyDown(getByTestId('input'), { key: 'Enter' }); - - expect(handleClick.callCount).to.equal(0); - expect(handleKeyDown.callCount).to.equal(1); - }); - }); - }); - - describe('prop: icon', () => { - it('should render the icon', () => { - const { getByTestId } = render(} />); - - expect(getByTestId('test-icon')).to.have.class(classes.icon); - }); - - it("should not override the icon's custom color", () => { - const { getByTestId } = render( - - } />, - } color="error" />, - , - ); - - expect(getByTestId('test-icon')).toHaveComputedStyle({ - color: hexToRgb(MaterialV5DefaultTheme.palette.success.main), - }); - expect(getByTestId('test-icon2')).toHaveComputedStyle({ - color: hexToRgb(MaterialV5DefaultTheme.palette.success.main), - }); - }); - }); - - describe('prop: size', () => { - it('should render with the sizeSmall class', () => { - const { container } = render(); - - const chip = container.querySelector(`.${classes.root}`); - expect(chip).to.have.class(classes.sizeSmall); - }); - }); - - describe('event: focus', () => { - it('has a focus-visible polyfill', () => { - const { getByTestId } = render( - {}} />, - ); - const chip = getByTestId('root'); - simulatePointerDevice(); - - expect(chip).not.to.have.class(classes.focusVisible); - - act(() => { - chip.focus(); - }); - - if (programmaticFocusTriggersFocusVisible()) { - expect(chip).to.have.class(classes.focusVisible); - } else { - expect(chip).not.to.have.class(classes.focusVisible); - } - - focusVisible(chip); - - expect(chip).to.have.class(classes.focusVisible); - }); - - it('should reset the focused state', () => { - const { getByTestId, setProps } = render( - {}} />, - ); - const chip = getByTestId('root'); - - simulatePointerDevice(); - focusVisible(chip); - - expect(chip).to.have.class(classes.focusVisible); - - setProps({ disabled: true }); - - expect(chip).not.to.have.class(classes.focusVisible); - }); - }); - - describe('CSS vars', () => { - it('should not throw when there is theme value is CSS variable', () => { - const theme = extendTheme(); - theme.palette = theme.colorSchemes.light.palette; - theme.palette.text = { - ...theme.palette.text, - primary: 'var(--mui-palette-grey-900)', - }; - expect(() => - render( - - - , - ), - ).not.to.throw(); - }); - }); -}); diff --git a/packages/mui-material-next/src/Chip/Chip.tsx b/packages/mui-material-next/src/Chip/Chip.tsx deleted file mode 100644 index 13d4e20900a53f..00000000000000 --- a/packages/mui-material-next/src/Chip/Chip.tsx +++ /dev/null @@ -1,560 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { EventHandlers, unstable_composeClasses as composeClasses, useSlotProps } from '@mui/base'; -import { - unstable_capitalize as capitalize, - unstable_useForkRef as useForkRef, - unstable_unsupportedProp as unsupportedProp, -} from '@mui/utils'; -import { OverridableComponent } from '@mui/types'; -import ClearIcon from '../internal/svg-icons/Clear'; -import { useThemeProps, styled, MD3ColorSchemeTokens } from '../styles'; -import ButtonBase from '../ButtonBase'; -import chipClasses, { getChipUtilityClass } from './chipClasses'; -import { ChipOwnerState, ChipProps, ChipTypeMap } from './Chip.types'; - -const useUtilityClasses = (ownerState: ChipOwnerState) => { - const { classes, disabled, size, color, hasDeleteIcon, clickable, variant } = ownerState; - - const slots = { - root: [ - 'root', - variant, - disabled && 'disabled', - `size${capitalize(size)}`, - color && `color${capitalize(color)}`, - clickable && 'clickable', - hasDeleteIcon && 'deletable', - ], - label: ['label'], - avatar: ['avatar'], - icon: ['icon'], - deleteIcon: ['deleteIcon'], - }; - - const composedClasses = composeClasses(slots, getChipUtilityClass, classes); - - return { - ...classes, // forward the focused, disabled, etc. classes to the ButtonBase - ...composedClasses, - }; -}; - -const ChipRoot = styled('div', { - name: 'MuiChip', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - const { color, clickable, hasDeleteIcon, size, variant } = ownerState; - - return [ - { [`& .${chipClasses.avatar}`]: styles.avatar }, - { [`& .${chipClasses.icon}`]: styles.icon }, - { [`& .${chipClasses.deleteIcon}`]: styles.deleteIcon }, - styles.root, - styles[`size${capitalize(size)}`], - color && styles[`color${capitalize(color)}`], - clickable && styles.clickable, - hasDeleteIcon && styles.deletable, - styles[variant], - ]; - }, -})<{ ownerState: ChipOwnerState }>(({ theme, ownerState }) => { - const { vars: tokens } = theme; - - const containerColor = { - filled: - tokens.sys.color[`${ownerState.color ?? 'secondary'}Container` as keyof MD3ColorSchemeTokens], - outlined: 'transparent', - elevated: - tokens.sys.color[ - (ownerState.color - ? `${ownerState.color}Container` - : 'surfaceContainerLow') as keyof MD3ColorSchemeTokens - ], - }; - - const labelTextColor = { - filled: - tokens.sys.color[ - `on${capitalize(ownerState.color ?? 'secondary')}Container` as keyof MD3ColorSchemeTokens - ], - outlined: tokens.sys.color.onSurface, - elevated: - tokens.sys.color[ - (ownerState.color - ? `on${capitalize(ownerState.color)}Container` - : 'onSurface') as keyof MD3ColorSchemeTokens - ], - }; - - const containerElevation = { - filled: tokens.sys.elevation[0], - outlined: tokens.sys.elevation[0], - elevated: tokens.sys.elevation[1], - }; - - const disabledContainerColor = { - filled: `rgba(${tokens.sys.color.onSurfaceChannel} / 0.12)`, - outlined: 'transparent', - elevated: `rgba(${tokens.sys.color.onSurfaceChannel} / 0.12)`, - }; - - const disabledContainerBorder = { - filled: null, - outlined: `1px solid rgba(${tokens.sys.color.onSurfaceChannel} / 0.12)`, - elevated: null, - }; - - const disabledElevation = { - filled: tokens.sys.elevation[0], - outlined: tokens.sys.elevation[0], - elevated: tokens.sys.elevation[0], - }; - - const stateLayerBackgroundColor = { - filled: tokens.sys.color.onSecondaryContainer, - outlined: tokens.sys.color.onSurfaceVariant, - elevated: tokens.sys.color.onSurfaceVariant, - }; - - const hoveredContainerElevation = { - filled: tokens.sys.elevation[1], - outlined: tokens.sys.elevation[0], - elevated: tokens.sys.elevation[2], - }; - - const focusedContainerElevation = { - filled: tokens.sys.elevation[0], - outlined: tokens.sys.elevation[0], - elevated: tokens.sys.elevation[1], - }; - - const pressedContainerElevation = { - filled: tokens.sys.elevation[1], - outlined: tokens.sys.elevation[0], - elevated: tokens.sys.elevation[1], - }; - - const letterSpacing = `${ - theme.sys.typescale.label.large.tracking / theme.sys.typescale.label.large.size - }rem`; - - return { - '--md-comp-chip-container-color': containerColor[ownerState.variant], - '--md-comp-chip-label-padding-y': '16px', - '--md-comp-chip-label-padding-left': 'var(--md-comp-chip-label-padding-y)', - '--md-comp-chip-label-padding-right': 'var(--md-comp-chip-label-padding-y)', - '--md-comp-chip-icon-size': '18px', - position: 'relative', - maxWidth: '100%', - fontFamily: tokens.sys.typescale.label.large.family, - fontSize: theme.typography.pxToRem(theme.sys.typescale.label.large.size), // the pxToRem should be moved to typescale in the future - fontWeight: tokens.sys.typescale.label.large.weight, - lineHeight: `calc(${tokens.sys.typescale.label.large.lineHeight} / ${theme.sys.typescale.label.large.size})`, - letterSpacing, - display: 'inline-flex', - alignItems: 'center', - justifyContent: 'center', - height: 32, - color: labelTextColor[ownerState.variant], - backgroundColor: 'var(--md-comp-chip-container-color)', - borderRadius: tokens.sys.shape.corner.small, - whiteSpace: 'nowrap', - overflow: 'hidden', - transition: theme.transitions.create(['background-color', 'box-shadow']), - boxSizing: 'border-box', - boxShadow: containerElevation[ownerState.variant], - ...(ownerState.size === 'small' && { - '--md-comp-chip-label-padding-y': '10px', - '--md-comp-chip-icon-size': '16px', - height: 24, - }), - ...(ownerState.variant === 'outlined' && { - border: `1px solid ${tokens.sys.color[ownerState.color ?? 'outline']}`, - '--md-comp-chip-label-padding-left': 'calc(var(--md-comp-chip-label-padding-y) - 1px)', - '--md-comp-chip-label-padding-right': 'calc(var(--md-comp-chip-label-padding-y) - 1px)', - }), - ...((ownerState.hasIcon || ownerState.hasAvatar) && { - '--md-comp-chip-label-padding-left': ownerState.size === 'small' ? '6px' : '8px', - }), - ...(ownerState.hasDeleteIcon && { - '--md-comp-chip-label-padding-right': '5px', - cursor: 'auto', - }), - [`&.${chipClasses.disabled}`]: { - backgroundColor: disabledContainerColor[ownerState.variant], - border: disabledContainerBorder[ownerState.variant], - color: `rgba(${tokens.sys.color.onSurfaceChannel} / 0.38)`, - boxShadow: disabledElevation[ownerState.variant], - pointerEvents: 'none', - }, - ...((ownerState.clickable || ownerState.hasDeleteIcon) && { - [`&.${chipClasses.focusVisible}`]: { - boxShadow: focusedContainerElevation[ownerState.variant], - '--md-comp-chip-container-color': `color-mix(in srgb, ${ - stateLayerBackgroundColor[ownerState.variant] - } calc(${tokens.sys.state.focus.stateLayerOpacity} * 100%), ${ - containerColor[ownerState.variant] - })`, - }, - }), - ...(ownerState.clickable && { - WebkitTapHighlightColor: 'transparent', - cursor: 'pointer', - '&:hover': { - boxShadow: hoveredContainerElevation[ownerState.variant], - backgroundColor: `color-mix(in srgb, ${ - stateLayerBackgroundColor[ownerState.variant] - } calc(${ - tokens.sys.state.hover.stateLayerOpacity - } * 100%), var(--md-comp-chip-container-color))`, - }, - '&:active': { - boxShadow: pressedContainerElevation[ownerState.variant], - }, - }), - [`& .${chipClasses.avatar}`]: { - width: 24, - height: 24, - marginLeft: 4, - color: - tokens.sys.color[ - `on${capitalize(ownerState.color ?? 'secondary')}` as keyof MD3ColorSchemeTokens - ], - backgroundColor: tokens.sys.color[ownerState.color ?? 'secondary'], - fontSize: theme.typography.pxToRem(theme.sys.typescale.label.large.size), // the pxToRem should be moved to typescale in the future - ...(ownerState.size === 'small' && { - height: 18, - width: 18, - fontSize: theme.typography.pxToRem(theme.sys.typescale.label.small.size), // the pxToRem should be moved to typescale in the future - }), - }, - [`& .${chipClasses.icon}`]: { - height: 'var(--md-comp-chip-icon-size)', - width: 'var(--md-comp-chip-icon-size)', - marginLeft: 8, - }, - [`& .${chipClasses.deleteIcon}`]: { - boxSizing: 'content-box', - zIndex: 1, - height: 'var(--md-comp-chip-icon-size)', - width: 'var(--md-comp-chip-icon-size)', - padding: 3, - marginRight: 5, - ...(ownerState.size === 'small' && { - padding: 1, - }), - WebkitTapHighlightColor: 'transparent', - cursor: 'pointer', - borderRadius: tokens.sys.shape.corner.full, - '&:hover': { - backgroundColor: `color-mix(in srgb, ${ - stateLayerBackgroundColor[ownerState.variant] - } calc(${ - tokens.sys.state.hover.stateLayerOpacity - } * 100%), var(--md-comp-chip-container-color))`, - }, - }, - }; -}); - -const ChipLabel = styled('span', { - name: 'MuiChip', - slot: 'Label', -})<{ ownerState: ChipOwnerState }>({ - overflow: 'hidden', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - paddingLeft: 'var(--md-comp-chip-label-padding-left)', - paddingRight: 'var(--md-comp-chip-label-padding-right)', -}); - -function isDeleteKeyboardEvent(keyboardEvent: React.KeyboardEvent) { - return keyboardEvent.key === 'Backspace' || keyboardEvent.key === 'Delete'; -} - -/** - * Chips represent complex entities in small blocks, such as a contact. - * - * Demos: - * - * - [Chip](https://mui.com/material-ui/react-chip/) - * - * API: - * - * - [Chip API](https://mui.com/material-ui/api/chip/) - */ -const Chip = React.forwardRef(function Chip< - BaseComponentType extends React.ElementType = ChipTypeMap['defaultComponent'], ->(inProps: ChipProps, ref: React.ForwardedRef) { - const props = useThemeProps({ props: inProps, name: 'MuiChip' }); - const { - avatar: avatarProp, - className, - clickable: clickableProp, - color, - component: ComponentProp, - deleteIcon: deleteIconProp, - disabled = false, - icon: iconProp, - label, - onClick, - onDelete, - onKeyDown, - onKeyUp, - size = 'medium', - variant = 'filled', - ...other - } = props; - - const chipRef = React.useRef(null); - const handleRef = useForkRef(chipRef, ref); - - const handleDeleteIconClick = (event: React.MouseEvent) => { - // Stop the event from bubbling up to the `Chip` - event.stopPropagation(); - if (onDelete) { - onDelete(event); - } - }; - - const handleKeyDown = (event: React.KeyboardEvent) => { - // Ignore events from children of `Chip`. - if (event.currentTarget === event.target && isDeleteKeyboardEvent(event)) { - // Will be handled in keyUp, otherwise some browsers - // might init navigation - event.preventDefault(); - } - - if (onKeyDown) { - onKeyDown(event); - } - }; - - const handleKeyUp = (event: React.KeyboardEvent) => { - // Ignore events from children of `Chip`. - if (event.currentTarget === event.target) { - if (onDelete && isDeleteKeyboardEvent(event)) { - onDelete(event); - } else if (event.key === 'Escape' && chipRef.current) { - chipRef.current.blur(); - } - } - - if (onKeyUp) { - onKeyUp(event); - } - }; - - const clickable = clickableProp !== false && onClick ? true : clickableProp; - const isButton = clickable || !!onDelete; - - const component = isButton ? ButtonBase : ComponentProp || 'div'; - - const hasIcon = !!iconProp && React.isValidElement(iconProp); - const hasDeleteIcon = !!onDelete; - const hasAvatar = !!avatarProp && React.isValidElement(avatarProp); - - const ownerState: ChipOwnerState = { - ...props, - component, - disabled, - size, - color, - clickable, - variant, - hasIcon, - hasDeleteIcon, - hasAvatar, - }; - - const classes = useUtilityClasses(ownerState); - - const buttonProps = { - disabled: clickable && disabled, - component: ComponentProp, - focusVisibleClassName: chipClasses.focusVisible, - ...(onDelete && { disableRipple: true }), - }; - - let deleteIcon = null; - if (onDelete) { - deleteIcon = - deleteIconProp && React.isValidElement(deleteIconProp) ? ( - React.cloneElement(deleteIconProp as React.ReactElement, { - className: clsx((deleteIconProp.props as any)?.className, classes.deleteIcon), - onClick: handleDeleteIconClick, - }) - ) : ( - - ); - } - - let avatar = null; - if (hasAvatar) { - avatar = React.cloneElement(avatarProp as React.ReactElement, { - className: clsx(classes.avatar, (avatarProp.props as any)?.className), - }); - } - - let icon = null; - if (hasIcon) { - icon = React.cloneElement(iconProp as React.ReactElement, { - className: clsx(classes.icon, (iconProp.props as any)?.className), - }); - } - - if (process.env.NODE_ENV !== 'production') { - if (avatar && icon) { - console.error( - 'MUI: The Chip component can not handle the avatar ' + - 'and the icon prop at the same time. Pick one.', - ); - } - } - - const rootProps = useSlotProps({ - elementType: ChipRoot, - getSlotProps: (otherHandlers: EventHandlers) => ({ - ...otherHandlers, - onKeyDown: handleKeyDown, - onKeyUp: handleKeyUp, - }), - externalForwardedProps: { - onClick, - ...other, - }, - externalSlotProps: {}, - additionalProps: { - as: component, - ref: handleRef, - ...(isButton && buttonProps), - }, - ownerState, - className: [classes.root, className], - }); - - return ( - - {avatar || icon} - - {label} - - {deleteIcon} - - ); -}) as OverridableComponent; - -Chip.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * The Avatar element to display. - */ - avatar: PropTypes.element, - /** - * This prop isn't supported. - * Use the `component` prop if you need to change the children structure. - */ - children: unsupportedProp, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * If `true`, the chip will appear clickable, and will raise when pressed, - * even if the onClick prop is not defined. - * If `false`, the chip will not appear clickable, even if onClick prop is defined. - * This can be used, for example, - * along with the component prop to indicate an anchor Chip is clickable. - * Note: this controls the UI and does not affect the onClick event. - */ - clickable: PropTypes.bool, - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - */ - color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['primary', 'secondary', 'tertiary', 'error', 'info', 'success', 'warning']), - PropTypes.string, - ]), - /** - * The component used for the root node. - * Either a string to use a HTML element or a component. - */ - component: PropTypes.elementType, - /** - * Override the default delete icon element. Shown only if `onDelete` is set. - */ - deleteIcon: PropTypes.element, - /** - * If `true`, the component is disabled. - * @default false - */ - disabled: PropTypes.bool, - /** - * Icon element. - */ - icon: PropTypes.element, - /** - * The content of the component. - */ - label: PropTypes.node, - /** - * @ignore - */ - onClick: PropTypes.func, - /** - * Callback fired when the delete icon is clicked. - * If set, the delete icon will be shown. - */ - onDelete: PropTypes.func, - /** - * @ignore - */ - onKeyDown: PropTypes.func, - /** - * @ignore - */ - onKeyUp: PropTypes.func, - /** - * The size of the component. - * @default 'medium' - */ - size: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['medium', 'small']), - PropTypes.string, - ]), - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), - PropTypes.func, - PropTypes.object, - ]), - /** - * @ignore - */ - tabIndex: PropTypes.number, - /** - * The variant to use. - * @default 'filled' - */ - variant: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['filled', 'outlined', 'elevated']), - PropTypes.string, - ]), -} as any; - -export default Chip; diff --git a/packages/mui-material-next/src/Chip/Chip.types.ts b/packages/mui-material-next/src/Chip/Chip.types.ts deleted file mode 100644 index 5595d7abdb8a7f..00000000000000 --- a/packages/mui-material-next/src/Chip/Chip.types.ts +++ /dev/null @@ -1,108 +0,0 @@ -import * as React from 'react'; -import { OverridableStringUnion, OverrideProps } from '@mui/types'; -import { SxProps } from '../styles/Theme.types'; -import { ChipClasses } from './chipClasses'; - -export interface ChipPropsVariantOverrides {} - -export interface ChipPropsSizeOverrides {} - -export interface ChipPropsColorOverrides {} - -export interface ChipTypeMap< - AdditionalProps = {}, - RootComponent extends React.ElementType = 'div', -> { - props: AdditionalProps & { - /** - * The Avatar element to display. - */ - avatar?: React.ReactElement; - /** - * This prop isn't supported. - * Use the `component` prop if you need to change the children structure. - */ - children?: null; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial; - /** - * If `true`, the chip will appear clickable, and will raise when pressed, - * even if the onClick prop is not defined. - * If `false`, the chip will not appear clickable, even if onClick prop is defined. - * This can be used, for example, - * along with the component prop to indicate an anchor Chip is clickable. - * Note: this controls the UI and does not affect the onClick event. - */ - clickable?: boolean; - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - */ - color?: OverridableStringUnion< - 'primary' | 'secondary' | 'tertiary' | 'error' | 'info' | 'success' | 'warning', - ChipPropsColorOverrides - >; - /** - * Override the default delete icon element. Shown only if `onDelete` is set. - */ - deleteIcon?: React.ReactElement; - /** - * If `true`, the component is disabled. - * @default false - */ - disabled?: boolean; - /** - * Icon element. - */ - icon?: React.ReactElement; - /** - * The content of the component. - */ - label?: React.ReactNode; - /** - * Callback fired when the delete icon is clicked. - * If set, the delete icon will be shown. - */ - onDelete?: React.EventHandler; - /** - * The size of the component. - * @default 'medium' - */ - size?: OverridableStringUnion<'small' | 'medium', ChipPropsSizeOverrides>; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps; - /** - * @ignore - */ - tabIndex?: number; - /** - * The variant to use. - * @default 'filled' - */ - variant?: OverridableStringUnion<'filled' | 'outlined' | 'elevated', ChipPropsVariantOverrides>; - }; - defaultComponent: RootComponent; -} - -export type ChipProps< - RootComponent extends React.ElementType = ChipTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps, RootComponent> & { - component?: React.ElementType; -}; - -export interface ChipOwnerState extends ChipProps { - variant: NonNullable; - size: NonNullable; - /** - * color for the icon component - */ - hasIcon: boolean; - hasDeleteIcon: boolean; - hasAvatar: boolean; -} diff --git a/packages/mui-material-next/src/Chip/chipClasses.ts b/packages/mui-material-next/src/Chip/chipClasses.ts deleted file mode 100644 index 7c4f1fc48e5bd9..00000000000000 --- a/packages/mui-material-next/src/Chip/chipClasses.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { - unstable_generateUtilityClasses as generateUtilityClasses, - unstable_generateUtilityClass as generateUtilityClass, -} from '@mui/utils'; - -export interface ChipClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if `size="small"`. */ - sizeSmall: string; - /** Styles applied to the root element if `size="medium"`. */ - sizeMedium: string; - /** Styles applied to the root element if `color="error"`. */ - colorError: string; - /** Styles applied to the root element if `color="info"`. */ - colorInfo: string; - /** Styles applied to the root element if `color="primary"`. */ - colorPrimary: string; - /** Styles applied to the root element if `color="secondary"`. */ - colorSecondary: string; - /** Styles applied to the root element if `color="tertiary"`. */ - colorTertiary: string; - /** Styles applied to the root element if `color="success"`. */ - colorSuccess: string; - /** Styles applied to the root element if `color="warning"`. */ - colorWarning: string; - /** State class applied to the root element if `disabled={true}`. */ - disabled: string; - /** Styles applied to the root element if `onClick` is defined or `clickable={true}`. */ - clickable: string; - /** Styles applied to the root element if `onDelete` is defined. */ - deletable: string; - /** Styles applied to the root element if `variant="outlined"`. */ - outlined: string; - /** Styles applied to the root element if `variant="filled"`. */ - filled: string; - /** Styles applied to the avatar element. */ - avatar: string; - /** Styles applied to the icon element. */ - icon: string; - /** Styles applied to the label `span` element. */ - label: string; - /** Styles applied to the deleteIcon element. */ - deleteIcon: string; - /** State class applied to the root element if keyboard focused. */ - focusVisible: string; -} - -export type ChipClassKey = keyof ChipClasses; - -export function getChipUtilityClass(slot: string): string { - return generateUtilityClass('MuiChip', slot); -} - -const chipClasses: ChipClasses = generateUtilityClasses('MuiChip', [ - 'root', - 'sizeSmall', - 'sizeMedium', - 'colorError', - 'colorInfo', - 'colorPrimary', - 'colorSecondary', - 'colorTertiary', - 'colorSuccess', - 'colorWarning', - 'disabled', - 'clickable', - 'deletable', - 'outlined', - 'filled', - 'avatar', - 'icon', - 'label', - 'deleteIcon', - 'focusVisible', -]); - -export default chipClasses; diff --git a/packages/mui-material-next/src/Chip/index.ts b/packages/mui-material-next/src/Chip/index.ts deleted file mode 100644 index 81875ce0035fcc..00000000000000 --- a/packages/mui-material-next/src/Chip/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './Chip'; - -export { default as chipClasses } from './chipClasses'; -export * from './chipClasses'; diff --git a/packages/mui-material-next/src/CircularProgress/CircularProgress.test.tsx b/packages/mui-material-next/src/CircularProgress/CircularProgress.test.tsx deleted file mode 100644 index 80d77d35675a24..00000000000000 --- a/packages/mui-material-next/src/CircularProgress/CircularProgress.test.tsx +++ /dev/null @@ -1,191 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { createRenderer } from '@mui-internal/test-utils'; -import CircularProgress, { - circularProgressClasses as classes, -} from '@mui/material-next/CircularProgress'; -import { CssVarsProvider, extendTheme } from '../styles'; -import describeConformance from '../../test/describeConformance'; - -describe('', () => { - const { render } = createRenderer(); - - describeConformance(, () => ({ - classes, - inheritComponent: 'span', - render, - muiName: 'MuiCircularProgress', - testDeepOverrides: { slotName: 'circle', slotClassName: classes.circle }, - testVariantProps: { variant: 'determinate' }, - refInstanceof: window.HTMLSpanElement, - ThemeProvider: CssVarsProvider, - createTheme: extendTheme, - skip: ['componentProp', 'componentsProp'], - })); - - it('should render with the primary color by default', () => { - const { getByRole } = render(); - const circularProgress = getByRole('progressbar'); - expect(circularProgress).to.have.class(classes.colorPrimary); - }); - - it('should render with the primary color', () => { - const { getByRole } = render(); - const circularProgress = getByRole('progressbar'); - expect(circularProgress).to.have.class(classes.colorPrimary); - }); - - it('should render with the secondary color', () => { - const { getByRole } = render(); - const circularProgress = getByRole('progressbar'); - expect(circularProgress).to.have.class(classes.colorSecondary); - }); - - it('should render with the tertiary color', () => { - const { getByRole } = render(); - const circularProgress = getByRole('progressbar'); - expect(circularProgress).to.have.class(classes.colorTertiary); - }); - - it('should contain an SVG with the svg class, and a circle with the circle class', () => { - const { container, getByRole } = render(); - const circularProgress = getByRole('progressbar'); - const svg = container.querySelector('svg'); - const circle = container.querySelector('circle'); - expect(svg).to.have.tagName('svg'); - expect(circularProgress).to.have.class(classes.indeterminate); - expect(circle).to.have.tagName('circle'); - expect(circle).to.have.class(classes.circle); - }); - - it('should render indeterminate variant by default', () => { - const { container, getByRole } = render(); - const circularProgress = getByRole('progressbar'); - const circle = container.querySelector('circle'); - expect(circularProgress).to.have.class(classes.root); - expect(circularProgress).to.have.class(classes.indeterminate); - expect(circle).to.have.class(classes.circle); - }); - - it('should render with a different size', async function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - // only run on browser, because getComputedStyle only works in browser - this.skip(); - } - const { container, getByRole } = render(); - const circularProgress = getByRole('progressbar'); - const circularProgressStyle = window.getComputedStyle(circularProgress); - expect(circularProgress).to.have.class(classes.root); - expect(circularProgressStyle.width).to.equal('60px', 'should have width correctly set'); - expect(circularProgressStyle.height).to.equal('60px', 'should have height correctly set'); - const svg = container.querySelector('svg'); - expect(svg).to.have.tagName('svg'); - const circle = container.querySelector('circle'); - expect(circle).to.have.tagName('circle'); - expect(circle).to.have.attribute('cx', '48'); - expect(circle).to.have.attribute('cy', '48'); - }); - - describe('prop: variant="determinate"', () => { - it('should render with determinate classes', () => { - const { container, getByRole } = render(); - const circularProgress = getByRole('progressbar'); - expect(circularProgress).to.have.class(classes.root); - expect(circularProgress).not.to.have.class(classes.indeterminate); - const svg = container.querySelector('svg'); - expect(svg).to.have.tagName('svg'); - const circle = container.querySelector('circle'); - expect(circle).to.have.class(classes.circle); - }); - - it('should set strokeDasharray of circle', () => { - const { container, getByRole } = render( - , - ); - const circularProgress = getByRole('progressbar'); - expect(circularProgress).to.have.class(classes.root); - const circle = container.querySelector('circle')!; - expect(circle.style.strokeDasharray).to.match( - /138\.230?(px)?/gm, - 'should have strokeDasharray set', - ); - expect(circle.style.strokeDashoffset).to.equal( - '41.469px', - 'should have strokeDashoffset set', - ); - expect(circularProgress).to.have.attribute('aria-valuenow', '70'); - }); - }); - - describe('prop: disableShrink ', () => { - it('should default to false', () => { - const { container, getByRole } = render(); - const circularProgress = getByRole('progressbar'); - expect(circularProgress).to.have.class(classes.root); - expect(circularProgress).not.to.have.class(classes.disableShrink); - const circle = container.querySelector('circle'); - expect(circle).to.have.tagName('circle'); - expect(circle).to.have.class(classes.circle); - }); - - it('should render without disableShrink class when set to false', () => { - const { container, getByRole } = render( - , - ); - const circularProgress = getByRole('progressbar'); - expect(circularProgress).to.have.class(classes.root); - expect(circularProgress).not.to.have.class(classes.disableShrink); - const circle = container.querySelector('circle'); - expect(circle).to.have.tagName('circle'); - expect(circle).to.have.class(classes.circle); - }); - - it('should render with disableShrink class when set to true', () => { - const { container, getByRole } = render( - , - ); - const circularProgress = getByRole('progressbar'); - expect(circularProgress).to.have.class(classes.root); - expect(circularProgress).to.have.class(classes.disableShrink); - const circle = container.querySelector('circle'); - expect(circle).to.have.tagName('circle'); - expect(circle).to.have.class(classes.circle); - }); - }); - - describe('prop: fourColor ', () => { - it('should default to false', () => { - const { container, getByRole } = render(); - const circularProgress = getByRole('progressbar'); - expect(circularProgress).to.have.class(classes.root); - expect(circularProgress).not.to.have.class(classes.fourColor); - const circle = container.querySelector('circle'); - expect(circle).to.have.tagName('circle'); - expect(circle).to.have.class(classes.circle); - }); - - it('should render without fourColor class when set to false', () => { - const { container, getByRole } = render( - , - ); - const circularProgress = getByRole('progressbar'); - expect(circularProgress).to.have.class(classes.root); - expect(circularProgress).not.to.have.class(classes.fourColor); - const circle = container.querySelector('circle'); - expect(circle).to.have.tagName('circle'); - expect(circle).to.have.class(classes.circle); - }); - - it('should render with fourColor class when set to true', () => { - const { container, getByRole } = render( - , - ); - const circularProgress = getByRole('progressbar'); - expect(circularProgress).to.have.class(classes.root); - expect(circularProgress).to.have.class(classes.fourColor); - const circle = container.querySelector('circle'); - expect(circle).to.have.tagName('circle'); - expect(circle).to.have.class(classes.circle); - }); - }); -}); diff --git a/packages/mui-material-next/src/CircularProgress/CircularProgress.tsx b/packages/mui-material-next/src/CircularProgress/CircularProgress.tsx deleted file mode 100644 index b5e290ff44e59e..00000000000000 --- a/packages/mui-material-next/src/CircularProgress/CircularProgress.tsx +++ /dev/null @@ -1,359 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { chainPropTypes, unstable_capitalize as capitalize } from '@mui/utils'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import { keyframes } from '@mui/system'; -import { OverridableComponent } from '@mui/types'; -import useThemeProps from '../styles/useThemeProps'; -import styled from '../styles/styled'; -import { getCircularProgressUtilityClass } from './circularProgressClasses'; -import { - CircularProgressOwnerState, - CircularProgressProps, - CircularProgressTypeMap, -} from './CircularProgress.types'; - -const SIZE = 48; -const ARC_DURATION = 1333; // milliseconds -const CYCLE_DURATION = 4 * ARC_DURATION; // milliseconds -const LINEAR_ROTATE_DURATION = (ARC_DURATION * 360) / 306; // milliseconds - -const circularRotateKeyframe = keyframes` - to { - transform: rotate(360deg); - } -`; - -const circularDashKeyframe = keyframes` - 0% { - stroke-dasharray: 10px, 200px; - stroke-dashoffset: 0px; - } - - 50% { - stroke-dasharray: 139px, 10px; - stroke-dashoffset: -10px; - } - - 100% { - stroke-dasharray: 139px, 129px; - stroke-dashoffset: -139px; - } -`; - -const fourColorKeyframe = keyframes` - 0% { - stroke: var(--md-comp-linear-progress-indicator-four-color-active-indicator-one-color); - } - 15% { - stroke: var(--md-comp-linear-progress-indicator-four-color-active-indicator-one-color); - } - 25% { - stroke: var(--md-comp-linear-progress-indicator-four-color-active-indicator-two-color); - } - 40% { - stroke: var(--md-comp-linear-progress-indicator-four-color-active-indicator-two-color); - } - 50% { - stroke: var(--md-comp-linear-progress-indicator-four-color-active-indicator-three-color); - } - 65% { - stroke: var(--md-comp-linear-progress-indicator-four-color-active-indicator-three-color); - } - 75% { - stroke: var(--md-comp-linear-progress-indicator-four-color-active-indicator-four-color); - } - 90% { - stroke: var(--md-comp-linear-progress-indicator-four-color-active-indicator-four-color); - } - 100% { - stroke: var(--md-comp-linear-progress-indicator-four-color-active-indicator-one-color); - } -`; - -const useUtilityClasses = (ownerState: CircularProgressOwnerState) => { - const { classes, variant, color, disableShrink, fourColor } = ownerState; - - const slots = { - root: [ - 'root', - variant, - `color${capitalize(color)}`, - fourColor && 'fourColor', - disableShrink && 'disableShrink', - ], - svg: ['svg'], - circle: ['circle'], - }; - - return composeClasses(slots, getCircularProgressUtilityClass, classes); -}; - -const CircularProgressRoot = styled('span', { - name: 'MuiCircularProgress', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [ - styles.root, - styles[ownerState.variant], - styles[`color${capitalize(ownerState.color)}`], - ownerState.fourColor && styles.fourColor, - ownerState.disableShrink && styles.disableShrink, - ]; - }, -})<{ ownerState: CircularProgressOwnerState }>(({ ownerState, theme: { vars: tokens } }) => ({ - '--md-comp-linear-progress-indicator-active-indicator-color': - ownerState.color !== 'inherit' ? tokens.sys.color[ownerState.color] : 'currentColor', - '--md-comp-linear-progress-indicator-active-indicator-width': ownerState.thickness, - '--md-comp-linear-progress-indicator-size': ownerState.size, - '--md-comp-linear-progress-indicator-four-color-active-indicator-one-color': - tokens.sys.color.primary, - '--md-comp-linear-progress-indicator-four-color-active-indicator-two-color': - tokens.sys.color.onPrimaryContainer, - '--md-comp-linear-progress-indicator-four-color-active-indicator-three-color': - tokens.sys.color.tertiary, - '--md-comp-linear-progress-indicator-four-color-active-indicator-four-color': - tokens.sys.color.onTertiaryContainer, - display: 'inline-block', - height: 'var(--md-comp-linear-progress-indicator-size)', - width: 'var(--md-comp-linear-progress-indicator-size)', - ...(ownerState.variant === 'determinate' && { - transition: `transform ${tokens.sys.motion.duration.medium2} ${tokens.sys.motion.easing.legacy}`, - }), - ...(ownerState.variant === 'indeterminate' && { - animation: `linear infinite ${circularRotateKeyframe}`, - animationDuration: `${LINEAR_ROTATE_DURATION}ms`, - }), -})); - -const CircularProgressSVG = styled('svg', { - name: 'MuiCircularProgress', - slot: 'Svg', - overridesResolver: (props, styles) => styles.svg, -})<{ ownerState: CircularProgressOwnerState }>({ - display: 'block', // Keeps the progress centered -}); - -const CircularProgressCircle = styled('circle', { - name: 'MuiCircularProgress', - slot: 'Circle', - overridesResolver: (props, styles) => styles.circle, -})<{ ownerState: CircularProgressOwnerState }>(({ ownerState, theme: { vars: tokens } }) => ({ - stroke: 'var(--md-comp-linear-progress-indicator-active-indicator-color)', - strokeWidth: 'var(--md-comp-linear-progress-indicator-active-indicator-width)', - // Use butt to follow the specification, by chance, it's already the default CSS value. - // strokeLinecap: 'butt', - ...(ownerState.variant === 'determinate' && { - transition: `stroke-dashoffset ${tokens.sys.motion.duration.medium2} ${tokens.sys.motion.easing.legacy}`, - }), - ...(ownerState.variant === 'indeterminate' && { - // Some default value that looks fine waiting for the animation to kicks in. - strokeDasharray: '10px, 200px', - strokeDashoffset: 0, // Add the unit to fix a Edge 16 and below bug. - }), - ...(ownerState.variant === 'indeterminate' && - !ownerState.disableShrink && { - animation: circularDashKeyframe, - animationDuration: `${ARC_DURATION}ms, ${CYCLE_DURATION}ms`, - animationIterationCount: 'infinite', - animationFillMode: 'both', - animationTimingFunction: tokens.sys.motion.easing.legacy, - ...(ownerState.fourColor && { - animationName: `${circularDashKeyframe}, ${fourColorKeyframe}`, - }), - }), -})); - -/** - * ## ARIA - * - * If the progress bar is describing the loading progress of a particular region of a page, - * you should use `aria-describedby` to point to the progress bar, and set the `aria-busy` - * attribute to `true` on that region until it has finished loading. - * - * Demos: - * - * - [Progress](https://mui.com/material-ui/react-progress/) - * - * API: - * - * - [CircularProgress API](https://mui.com/material-ui/api/circular-progress/) - */ -const CircularProgress = React.forwardRef(function CircularProgress< - BaseComponentType extends React.ElementType = CircularProgressTypeMap['defaultComponent'], ->(inProps: CircularProgressProps, ref: React.ForwardedRef) { - const props = useThemeProps({ props: inProps, name: 'MuiCircularProgress' }); - const { - className, - color = 'primary', - disableShrink = false, - fourColor = false, - style, - size = 48, - thickness = 4, - value = 0, - variant = 'indeterminate', - ...other - } = props; - - const ownerState = { - ...props, - color, - disableShrink, - fourColor, - size: typeof size === 'number' ? `${size}px` : size, - thickness, - value, - variant, - }; - - const classes = useUtilityClasses(ownerState); - - const circleStyle: React.CSSProperties = {}; - const rootStyle: React.CSSProperties = {}; - const rootProps: React.DetailedHTMLProps< - React.HTMLAttributes, - HTMLSpanElement - > = {}; - - if (variant === 'determinate') { - const circumference = 2 * Math.PI * ((SIZE - thickness) / 2); - circleStyle.strokeDasharray = circumference.toFixed(3); - rootProps['aria-valuenow'] = Math.round(value); - circleStyle.strokeDashoffset = `${(((100 - value) / 100) * circumference).toFixed(3)}px`; - rootStyle.transform = 'rotate(-90deg)'; - } - - return ( - - - - - - ); -}) as OverridableComponent; - -CircularProgress.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * @ignore - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'primary' - */ - color: PropTypes.oneOf([ - 'error', - 'info', - 'inherit', - 'primary', - 'secondary', - 'success', - 'tertiary', - 'warning', - ]), - /** - * If `true`, the shrink animation is disabled. - * This only works if variant is `indeterminate`. - * @default false - */ - disableShrink: chainPropTypes(PropTypes.bool, (props) => { - if (props.disableShrink && props.variant && props.variant !== 'indeterminate') { - return new Error( - 'MUI: You have provided the `disableShrink` prop ' + - 'with a variant other than `indeterminate`. This will have no effect.', - ); - } - return null; - }), - /** - * If `true`, the component render indeterminate mode using four colors instead of one. - * This only works if variant is `indeterminate`. - * @default false - */ - fourColor: chainPropTypes(PropTypes.bool, (props) => { - if (props.fourColor && props.variant && props.variant !== 'indeterminate') { - return new Error( - 'MUI: You have provided the `fourColor` prop ' + - 'with a variant other than `indeterminate`. This will have no effect.', - ); - } - return null; - }), - /** - * The size of the component. - * If using a number, the pixel unit is assumed. - * If using a string, you need to provide the CSS unit, for example '3rem'. - * @default 48 - */ - size: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - /** - * @ignore - */ - style: PropTypes.object, - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), - PropTypes.func, - PropTypes.object, - ]), - /** - * The thickness of the circle. - * @default 4 - */ - thickness: PropTypes.number, - /** - * The value of the progress indicator for the determinate variant. - * Value between 0 and 100. - * @default 0 - */ - value: PropTypes.number, - /** - * The variant to use. - * Use indeterminate when there is no progress value. - * @default 'indeterminate' - */ - variant: PropTypes.oneOf(['determinate', 'indeterminate']), -} as any; - -export default CircularProgress; diff --git a/packages/mui-material-next/src/CircularProgress/CircularProgress.types.ts b/packages/mui-material-next/src/CircularProgress/CircularProgress.types.ts deleted file mode 100644 index c8585439fc1dca..00000000000000 --- a/packages/mui-material-next/src/CircularProgress/CircularProgress.types.ts +++ /dev/null @@ -1,83 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { OverridableStringUnion, OverrideProps, PartiallyRequired } from '@mui/types'; -import { Theme } from '../styles'; -import { CircularProgressClasses } from './circularProgressClasses'; - -export interface CircularProgressPropsColorOverrides {} - -export interface CircularProgressOwnProps { - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial; - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'primary' - */ - color?: OverridableStringUnion< - 'primary' | 'secondary' | 'tertiary' | 'error' | 'info' | 'success' | 'warning' | 'inherit', - CircularProgressPropsColorOverrides - >; - /** - * If `true`, the shrink animation is disabled. - * This only works if variant is `indeterminate`. - * @default false - */ - disableShrink?: boolean; - /** - * If `true`, the component render indeterminate mode using four colors instead of one. - * This only works if variant is `indeterminate`. - * @default false - */ - fourColor?: boolean; - /** - * The size of the component. - * If using a number, the pixel unit is assumed. - * If using a string, you need to provide the CSS unit, for example '3rem'. - * @default 48 - */ - size?: number | string; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps; - /** - * The thickness of the circle. - * @default 4 - */ - thickness?: number; - /** - * The value of the progress indicator for the determinate variant. - * Value between 0 and 100. - * @default 0 - */ - value?: number; - /** - * The variant to use. - * Use indeterminate when there is no progress value. - * @default 'indeterminate' - */ - variant?: 'determinate' | 'indeterminate'; -} - -export interface CircularProgressTypeMap< - AdditionalProps = {}, - RootComponentType extends React.ElementType = 'span', -> { - props: CircularProgressOwnProps & AdditionalProps; - defaultComponent: RootComponentType; -} - -export type CircularProgressProps< - RootComponentType extends React.ElementType = CircularProgressTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps, RootComponentType>; - -export interface CircularProgressOwnerState - extends PartiallyRequired< - CircularProgressProps, - 'color' | 'disableShrink' | 'fourColor' | 'size' | 'thickness' | 'value' | 'variant' - > {} diff --git a/packages/mui-material-next/src/CircularProgress/circularProgressClasses.ts b/packages/mui-material-next/src/CircularProgress/circularProgressClasses.ts deleted file mode 100644 index 62a9b71c57be87..00000000000000 --- a/packages/mui-material-next/src/CircularProgress/circularProgressClasses.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { - unstable_generateUtilityClasses as generateUtilityClasses, - unstable_generateUtilityClass as generateUtilityClass, -} from '@mui/utils'; - -export interface CircularProgressClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if `variant="determinate"`. */ - determinate: string; - /** Styles applied to the root element if `variant="indeterminate"`. */ - indeterminate: string; - /** Styles applied to the root element if `color="primary"`. */ - colorPrimary: string; - /** Styles applied to the root element if `color="secondary"`. */ - colorSecondary: string; - /** Styles applied to the root element if `color="tertiary"`. */ - colorTertiary: string; - /** Styles applied to the root element if `fourColor={true}`. */ - fourColor: string; - /** Styles applied to the root element if `disableShrink={true}`. */ - disableShrink: string; - /** Styles applied to the svg element. */ - svg: string; - /** Styles applied to the `circle` svg path. */ - circle: string; -} - -export type CircularProgressClassKey = keyof CircularProgressClasses; - -export function getCircularProgressUtilityClass(slot: string): string { - return generateUtilityClass('MuiCircularProgress', slot); -} - -const circularProgressClasses: CircularProgressClasses = generateUtilityClasses( - 'MuiCircularProgress', - [ - 'root', - 'determinate', - 'indeterminate', - 'colorPrimary', - 'colorSecondary', - 'colorTertiary', - 'fourColor', - 'disableShrink', - 'svg', - 'circle', - ], -); - -export default circularProgressClasses; diff --git a/packages/mui-material-next/src/CircularProgress/index.ts b/packages/mui-material-next/src/CircularProgress/index.ts deleted file mode 100644 index 0541327cc71d2d..00000000000000 --- a/packages/mui-material-next/src/CircularProgress/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './CircularProgress'; - -export { default as circularProgressClasses } from './circularProgressClasses'; -export * from './circularProgressClasses'; diff --git a/packages/mui-material-next/src/Divider/Divider.test.tsx b/packages/mui-material-next/src/Divider/Divider.test.tsx deleted file mode 100644 index 4622af20f6f404..00000000000000 --- a/packages/mui-material-next/src/Divider/Divider.test.tsx +++ /dev/null @@ -1,145 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { createRenderer } from '@mui-internal/test-utils'; -import Divider, { dividerClasses as classes } from '@mui/material-next/Divider'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -import describeConformance from '../../test/describeConformance'; - -describe('', () => { - const { render } = createRenderer(); - - describeConformance(, () => ({ - classes, - inheritComponent: 'hr', - render, - muiName: 'MuiDivider', - refInstanceof: window.HTMLHRElement, - testComponentPropWith: 'div', - testVariantProps: { orientation: 'vertical', flexItem: true, textAlign: 'left' }, - ThemeProvider: CssVarsProvider, - createTheme: extendTheme, - skip: ['componentsProp'], - })); - - it('should set the absolute class', () => { - const { container } = render(); - expect(container.firstChild).to.have.class(classes.absolute); - }); - - it('should set the flexItem class', () => { - const { container } = render(); - expect(container.firstChild).to.have.class(classes.flexItem); - }); - - describe('prop: children', () => { - it('should render with the children', () => { - const text = 'test content'; - const { container } = render({text}); - expect(container.querySelectorAll('span').length).to.equal(1); - expect(container.querySelectorAll('span')[0].textContent).to.equal(text); - }); - - it('should set the default text class', () => { - const { container } = render(content); - expect(container.firstChild).to.have.class(classes.withChildren); - }); - - describe('prop: orientation', () => { - it('should set the textVertical class', () => { - const { container } = render(content); - expect(container.querySelectorAll(`.${classes.wrapperVertical}`).length).to.equal(1); - }); - }); - - describe('prop: textAlign', () => { - it('should set the textAlignRight class', () => { - const { container } = render(content); - expect(container.querySelectorAll(`.${classes.textAlignRight}`).length).to.equal(1); - }); - - it('should set the textAlignLeft class', () => { - const { container } = render(content); - expect(container.querySelectorAll(`.${classes.textAlignLeft}`).length).to.equal(1); - }); - - it('should not set the textAlignRight class if orientation="vertical"', () => { - const { container } = render( - - content - , - ); - expect(container.querySelectorAll(`.${classes.textAlignRight}`).length).to.equal(0); - }); - - it('should not set the textAlignLeft class if orientation="vertical"', () => { - const { container } = render( - - content - , - ); - expect(container.querySelectorAll(`.${classes.textAlignLeft}`).length).to.equal(0); - }); - }); - }); - - describe('prop: variant', () => { - it('should default to variant="fullWidth"', () => { - const { container } = render(); - expect(container.firstChild).not.to.have.class(classes.inset); - expect(container.firstChild).not.to.have.class(classes.middle); - }); - - describe('prop: variant="fullWidth" ', () => { - it('should render with the root and default class', () => { - const { container } = render(); - expect(container.firstChild).to.have.class(classes.root); - }); - }); - - describe('prop: variant="inset" ', () => { - it('should set the inset class', () => { - const { container } = render(); - expect(container.firstChild).to.have.class(classes.inset); - }); - }); - - describe('prop: variant="middle" with default orientation (horizontal)', () => { - it('should set the middle class', () => { - const { container } = render(); - expect(container.firstChild).to.have.class(classes.middle); - expect(container.firstChild).toHaveComputedStyle({ - marginLeft: '16px', - marginRight: '16px', - }); - }); - }); - - describe('prop: variant="middle" with orientation="vertical"', () => { - it('should set the middle class with marginTop & marginBottom styles', () => { - const { container } = render(); - expect(container.firstChild).toHaveComputedStyle({ - marginTop: '16px', - marginBottom: '16px', - }); - }); - }); - }); - - describe('role', () => { - it('avoids adding implicit aria semantics', () => { - const { container } = render(); - expect(container.firstChild).not.to.have.attribute('role'); - }); - - it('adds a proper role if none is specified', () => { - const { container } = render(); - expect(container.firstChild).to.have.attribute('role', 'separator'); - }); - - it('overrides the computed role with the provided one', () => { - // presentation is the only valid aria role - const { container } = render(); - expect(container.firstChild).to.have.attribute('role', 'presentation'); - }); - }); -}); diff --git a/packages/mui-material-next/src/Divider/Divider.tsx b/packages/mui-material-next/src/Divider/Divider.tsx deleted file mode 100644 index 1c8ce1610605e4..00000000000000 --- a/packages/mui-material-next/src/Divider/Divider.tsx +++ /dev/null @@ -1,306 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import { OverridableComponent } from '@mui/types'; -import styled from '../styles/styled'; -import useThemeProps from '../styles/useThemeProps'; -import { getDividerUtilityClass } from './dividerClasses'; -import { DividerOwnerState, DividerProps, DividerTypeMap } from './Divider.types'; - -const useUtilityClasses = (ownerState: DividerOwnerState) => { - const { absolute, children, classes, flexItem, orientation, textAlign, variant } = ownerState; - - const slots = { - root: [ - 'root', - absolute && 'absolute', - variant, - orientation === 'vertical' && 'vertical', - flexItem && 'flexItem', - !!children && 'withChildren', - textAlign === 'right' && orientation !== 'vertical' && 'textAlignRight', - textAlign === 'left' && orientation !== 'vertical' && 'textAlignLeft', - ], - wrapper: ['wrapper', orientation === 'vertical' && 'wrapperVertical'], - }; - - return composeClasses(slots, getDividerUtilityClass, classes); -}; - -const DividerRoot = styled('div', { - name: 'MuiDivider', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [ - styles.root, - ownerState.absolute && styles.absolute, - styles[ownerState.variant], - ownerState.orientation === 'vertical' && styles.vertical, - ownerState.flexItem && styles.flexItem, - ownerState.children && styles.withChildren, - ownerState.textAlign === 'right' && - ownerState.orientation !== 'vertical' && - styles.textAlignRight, - ownerState.textAlign === 'left' && - ownerState.orientation !== 'vertical' && - styles.textAlignLeft, - ]; - }, -})<{ ownerState: DividerOwnerState }>( - ({ theme, ownerState }) => { - const { vars: tokens } = theme; - - return { - margin: 0, // Reset browser default style. - flexShrink: 0, - borderWidth: 0, - borderStyle: 'solid', - borderColor: tokens.sys.color.outlineVariant, - borderBottomWidth: '1px', - ...(ownerState.absolute && { - position: 'absolute', - bottom: 0, - left: 0, - width: '100%', - }), - ...(ownerState.variant === 'inset' && - ownerState.orientation === 'horizontal' && { - marginLeft: '16px', - }), - ...(ownerState.variant === 'inset' && - ownerState.orientation === 'vertical' && { - marginTop: '16px', - }), - ...(ownerState.variant === 'middle' && - ownerState.orientation === 'horizontal' && { - marginLeft: '16px', - marginRight: '16px', - }), - ...(ownerState.variant === 'middle' && - ownerState.orientation === 'vertical' && { - marginTop: '16px', - marginBottom: '16px', - }), - ...(ownerState.orientation === 'vertical' && { - height: '100%', - borderBottomWidth: 0, - borderRightWidth: '1px', - }), - ...(ownerState.flexItem && { - alignSelf: 'stretch', - height: 'auto', - }), - }; - }, - ({ ownerState }) => ({ - ...(ownerState.children && { - display: 'flex', - whiteSpace: 'nowrap', - textAlign: 'center', - border: 0, - '&::before, &::after': { - content: '""', - alignSelf: 'center', - }, - }), - }), - ({ theme: { vars: tokens }, ownerState }) => ({ - ...(ownerState.children && - ownerState.orientation !== 'vertical' && { - '&::before, &::after': { - width: '100%', - borderTop: `1px solid ${tokens.sys.color.outlineVariant}`, - }, - }), - }), - ({ theme: { vars: tokens }, ownerState }) => ({ - ...(ownerState.children && - ownerState.orientation === 'vertical' && { - flexDirection: 'column', - '&::before, &::after': { - height: '100%', - borderLeft: `1px solid ${tokens.sys.color.outlineVariant}`, - }, - }), - }), - ({ ownerState }) => ({ - ...(ownerState.textAlign === 'right' && - ownerState.orientation !== 'vertical' && { - '&::before': { - width: '90%', - }, - '&::after': { - width: '10%', - }, - }), - ...(ownerState.textAlign === 'left' && - ownerState.orientation !== 'vertical' && { - '&::before': { - width: '10%', - }, - '&::after': { - width: '90%', - }, - }), - }), -); - -const DividerWrapper = styled('span', { - name: 'MuiDivider', - slot: 'Wrapper', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [styles.wrapper, ownerState.orientation === 'vertical' && styles.wrapperVertical]; - }, -})<{ ownerState: DividerOwnerState }>(({ theme, ownerState }) => ({ - display: 'inline-block', - paddingLeft: `calc(${theme.spacing(1)} * 1.2)`, - paddingRight: `calc(${theme.spacing(1)} * 1.2)`, - ...(ownerState.orientation === 'vertical' && { - paddingTop: `calc(${theme.spacing(1)} * 1.2)`, - paddingBottom: `calc(${theme.spacing(1)} * 1.2)`, - }), -})); - -/** - * Dividers separate content into clear groups. - * - * Demos: - * - * - [Divider](https://mui.com/material-ui/react-divider/) - * - [Lists](https://mui.com/material-ui/react-list/) - * - * API: - * - * - [Divider API](https://mui.com/material-ui/api/divider/) - */ -const Divider = React.forwardRef(function Divider< - BaseComponentType extends React.ElementType = DividerTypeMap['defaultComponent'], ->(inProps: DividerProps, ref: React.ForwardedRef) { - const props = useThemeProps({ - props: inProps, - name: 'MuiDivider', - }); - - const { - absolute = false, - children, - className, - component = children ? 'div' : 'hr', - flexItem = false, - orientation = 'horizontal', - role = component !== 'hr' ? 'separator' : undefined, - textAlign = 'center', - variant = 'fullWidth', - ...other - } = props; - - const ownerState: DividerOwnerState = { - ...props, - absolute, - component, - flexItem, - orientation, - role, - textAlign, - variant, - }; - - const classes = useUtilityClasses(ownerState); - - return ( - - {children ? ( - - {children} - - ) : null} - - ); -}) as OverridableComponent; - -/** - * The following flag is used to ensure that this component isn't tabbable i.e. - * does not get highlight/focus inside of MUI List. - */ -// @ts-ignore internal logic -Divider.muiSkipListHighlight = true; - -Divider.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * Absolutely position the element. - * @default false - */ - absolute: PropTypes.bool, - /** - * The content of the component. - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The component used for the root node. - * Either a string to use a HTML element or a component. - */ - component: PropTypes.elementType, - /** - * If `true`, a vertical divider will have the correct height when used in flex container. - * (By default, a vertical divider will have a calculated height of `0px` if it is the child of a flex container.) - * @default false - */ - flexItem: PropTypes.bool, - /** - * The component orientation. - * @default 'horizontal' - */ - orientation: PropTypes.oneOf(['horizontal', 'vertical']), - /** - * @ignore - */ - role: PropTypes /* @typescript-to-proptypes-ignore */.string, - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), - PropTypes.func, - PropTypes.object, - ]), - /** - * The text alignment. - * @default 'center' - */ - textAlign: PropTypes.oneOf(['center', 'left', 'right']), - /** - * The variant to use. - * @default 'fullWidth' - */ - variant: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['fullWidth', 'inset', 'middle']), - PropTypes.string, - ]), -} as any; - -export default Divider; diff --git a/packages/mui-material-next/src/Divider/Divider.types.ts b/packages/mui-material-next/src/Divider/Divider.types.ts deleted file mode 100644 index 9d95204aaa8a6d..00000000000000 --- a/packages/mui-material-next/src/Divider/Divider.types.ts +++ /dev/null @@ -1,66 +0,0 @@ -import * as React from 'react'; -import { OverridableStringUnion, OverrideProps } from '@mui/types'; -import { SxProps } from '@mui/system'; -import { Theme } from '../styles'; -import { DividerClasses } from './dividerClasses'; - -export interface DividerPropsVariantOverrides {} - -export interface DividerOwnerState extends DividerProps {} - -export interface DividerTypeMap< - AdditionalProps = {}, - RootComponent extends React.ElementType = 'hr', -> { - props: AdditionalProps & { - /** - * Absolutely position the element. - * @default false - */ - absolute?: boolean; - /** - * The content of the component. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial; - /** - * If `true`, a vertical divider will have the correct height when used in flex container. - * (By default, a vertical divider will have a calculated height of `0px` if it is the child of a flex container.) - * @default false - */ - flexItem?: boolean; - /** - * The component orientation. - * @default 'horizontal' - */ - orientation?: 'horizontal' | 'vertical'; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps; - /** - * The text alignment. - * @default 'center' - */ - textAlign?: 'center' | 'right' | 'left'; - /** - * The variant to use. - * @default 'fullWidth' - */ - variant?: OverridableStringUnion< - 'fullWidth' | 'inset' | 'middle', - DividerPropsVariantOverrides - >; - }; - defaultComponent: RootComponent; -} - -export type DividerProps< - RootComponent extends React.ElementType = DividerTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps, RootComponent> & { - component?: React.ElementType; -}; diff --git a/packages/mui-material-next/src/Divider/dividerClasses.ts b/packages/mui-material-next/src/Divider/dividerClasses.ts deleted file mode 100644 index e397f1d44a4643..00000000000000 --- a/packages/mui-material-next/src/Divider/dividerClasses.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { - unstable_generateUtilityClasses as generateUtilityClasses, - unstable_generateUtilityClass as generateUtilityClass, -} from '@mui/utils'; - -export interface DividerClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if `absolute={true}`. */ - absolute: string; - /** Styles applied to the root element if `variant="inset"`. */ - inset: string; - /** Styles applied to the root element if `variant="fullWidth"`. */ - fullWidth: string; - /** Styles applied to the root element if `variant="middle"`. */ - middle: string; - /** Styles applied to the root element if `orientation="vertical"`. */ - vertical: string; - /** Styles applied to the root element if `flexItem={true}`. */ - flexItem: string; - /** Styles applied to the root element if divider have text. */ - withChildren: string; - /** Styles applied to the root element if `textAlign="right" orientation="horizontal"`. */ - textAlignRight: string; - /** Styles applied to the root element if `textAlign="left" orientation="horizontal"`. */ - textAlignLeft: string; - /** Styles applied to the span children element if `orientation="horizontal"`. */ - wrapper: string; - /** Styles applied to the span children element if `orientation="vertical"`. */ - wrapperVertical: string; -} - -export type DividerClassKey = keyof DividerClasses; - -export function getDividerUtilityClass(slot: string): string { - return generateUtilityClass('MuiDivider', slot); -} - -const dividerClasses: DividerClasses = generateUtilityClasses('MuiDivider', [ - 'root', - 'absolute', - 'fullWidth', - 'inset', - 'middle', - 'flexItem', - 'vertical', - 'withChildren', - 'textAlignRight', - 'textAlignLeft', - 'wrapper', - 'wrapperVertical', -]); - -export default dividerClasses; diff --git a/packages/mui-material-next/src/Divider/index.ts b/packages/mui-material-next/src/Divider/index.ts deleted file mode 100644 index 6f37070be42115..00000000000000 --- a/packages/mui-material-next/src/Divider/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './Divider'; - -export { default as dividerClasses } from './dividerClasses'; -export * from './dividerClasses'; diff --git a/packages/mui-material-next/src/FilledInput/FilledInput.spec.tsx b/packages/mui-material-next/src/FilledInput/FilledInput.spec.tsx deleted file mode 100644 index 85956d7f730606..00000000000000 --- a/packages/mui-material-next/src/FilledInput/FilledInput.spec.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import * as React from 'react'; -import FilledInput from '@mui/material-next/FilledInput'; - -function TestHiddenLabel() { - return ; -} diff --git a/packages/mui-material-next/src/FilledInput/FilledInput.test.tsx b/packages/mui-material-next/src/FilledInput/FilledInput.test.tsx deleted file mode 100644 index 032dbf30375e1e..00000000000000 --- a/packages/mui-material-next/src/FilledInput/FilledInput.test.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { ClassNames } from '@emotion/react'; -import { createRenderer } from '@mui-internal/test-utils'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -import FilledInput, { filledInputClasses as classes } from '@mui/material-next/FilledInput'; -import InputBase from '@mui/material-next/InputBase'; -import describeConformance from '../../test/describeConformance'; - -describe('', () => { - const { render } = createRenderer(); - - describeConformance(, () => ({ - ThemeProvider: CssVarsProvider, - createTheme: extendTheme, - classes, - inheritComponent: InputBase, - render, - refInstanceof: window.HTMLDivElement, - muiName: 'MuiFilledInput', - testDeepOverrides: { slotName: 'input', slotClassName: classes.input }, - testVariantProps: { variant: 'contained', fullWidth: true }, - testStateOverrides: { prop: 'size', value: 'small', styleKey: 'sizeSmall' }, - slots: { - root: { expectedClassName: classes.root }, - input: { expectedClassName: classes.input, testWithElement: 'input' }, - }, - skip: [ - 'componentProp', - 'componentsProp', - 'slotPropsCallback', // not supported yet - ], - })); - - it('should have the underline class', () => { - const { getByTestId } = render(); - const root = getByTestId('test-input'); - expect(root).not.to.equal(null); - expect(root).to.have.class(classes.underline); - }); - - it('color={undefined} should not result in crash', () => { - expect(() => { - render(); - }).not.toErrorDev(); - }); - - it('can disable the underline', () => { - const { getByTestId } = render(); - const root = getByTestId('test-input'); - expect(root).not.to.have.class(classes.underline); - }); - - it('should forward classes to InputBase', () => { - render(); - expect(document.querySelector('.error')).not.to.equal(null); - }); - - it('should respect the classes coming from InputBase', () => { - const { getByTestId } = render( - , - ); - const root = getByTestId('test-input'); - expect(root).toHaveComputedStyle({ marginTop: '10px' }); - }); - - describe('Emotion compatibility', () => { - it('classes.root should overwrite built-in styles.', () => { - const { getByTestId } = render( - - {({ css }) => ( - - )} - , - ); - const input = getByTestId('root'); - - expect(getComputedStyle(input).position).to.equal('static'); - }); - - it('className should overwrite classes.root and built-in styles.', () => { - const { getByTestId } = render( - - {({ css }) => ( - - )} - , - ); - const input = getByTestId('root'); - - expect(getComputedStyle(input).position).to.equal('sticky'); - }); - }); -}); diff --git a/packages/mui-material-next/src/FilledInput/FilledInput.tsx b/packages/mui-material-next/src/FilledInput/FilledInput.tsx deleted file mode 100644 index b5d9bbc5af2c2f..00000000000000 --- a/packages/mui-material-next/src/FilledInput/FilledInput.tsx +++ /dev/null @@ -1,508 +0,0 @@ -'use client'; -import * as React from 'react'; -import { refType } from '@mui/utils'; -import PropTypes from 'prop-types'; -import { unstable_composeClasses as composeClasses, useSlotProps } from '@mui/base'; -import { DefaultComponentProps, OverrideProps } from '@mui/types'; -import { useThemeProps, styled } from '../styles'; -import { rootShouldForwardProp } from '../styles/styled'; -import { - InputBaseRoot, - InputBaseInput, - rootOverridesResolver as inputBaseRootOverridesResolver, - inputOverridesResolver as inputBaseInputOverridesResolver, -} from '../InputBase/InputBase'; -import InputBase from '../InputBase'; -import { InputBaseOwnerState } from '../InputBase/InputBase.types'; -import filledInputClasses, { getFilledInputUtilityClass } from './filledInputClasses'; -import { FilledInputOwnerState, FilledInputProps, FilledInputTypeMap } from './FilledInput.types'; - -const useUtilityClasses = (ownerState: FilledInputOwnerState) => { - const { classes, disableUnderline } = ownerState; - - const slots = { - root: ['root', !disableUnderline && 'underline'], - input: ['input'], - }; - - const composedClasses = composeClasses(slots, getFilledInputUtilityClass, classes); - - return { - ...classes, // forward classes to the InputBase - ...composedClasses, - }; -}; - -const FilledInputRoot = styled(InputBaseRoot, { - shouldForwardProp: (prop) => rootShouldForwardProp(prop) || prop === 'classes', - name: 'MuiFilledInput', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [ - ...inputBaseRootOverridesResolver(props, styles), - !ownerState.disableUnderline && styles.underline, - ]; - }, -})<{ ownerState: FilledInputOwnerState }>(({ theme, ownerState }) => { - const { vars: tokens } = theme; - - return { - '--md-comp-filled-input-active-indicator-color': tokens.sys.color.onSurfaceVariant, - '--md-comp-filled-input-container-color': tokens.sys.color.surfaceContainerHighest, - '--md-comp-filled-input-disabled-container-color': tokens.sys.color.onSurface, - '--md-comp-filled-input-disabled-container-opacity': 0.04, - '--md-comp-filled-input-disabled-active-indicator-color': tokens.sys.color.onSurface, - '--md-comp-filled-input-disabled-active-indicator-opacity': 0.38, - '--md-comp-filled-input-error-active-indicator-color': tokens.sys.color.error, - '--md-comp-filled-input-error-hover-active-indicator-color': tokens.sys.color.onErrorContainer, - '--md-comp-filled-input-focus-active-indicator-color': - tokens.sys.color[ownerState.color ?? 'primary'], - '--md-comp-filled-input-hover-active-indicator-color': tokens.sys.color.onSurface, - '--md-comp-filled-input-hover-state-layer-opacity': tokens.sys.state.hover.stateLayerOpacity, - position: 'relative', - backgroundColor: 'var(--md-comp-filled-input-container-color)', - borderTopLeftRadius: tokens.shape.borderRadius, - borderTopRightRadius: tokens.shape.borderRadius, - transition: theme.transitions.create('background-color', { - duration: theme.transitions.duration.shorter, - easing: theme.transitions.easing.easeOut, - }), - '&:hover': { - backgroundColor: - 'color-mix(in srgb, var(--md-comp-filled-input-hover-active-indicator-color) calc(var(--md-comp-filled-input-hover-state-layer-opacity) * 100%), var(--md-comp-filled-input-container-color))', - // Reset on touch devices, it doesn't add specificity - '@media (hover: none)': { - backgroundColor: 'var(--md-comp-filled-input-container-color)', - }, - }, - [`&.${filledInputClasses.focused}`]: { - backgroundColor: 'var(--md-comp-filled-input-container-color)', - '&::after': { - borderColor: 'var(--md-comp-filled-input-focus-active-indicator-color)', - }, - }, - [`&.${filledInputClasses.disabled}`]: { - backgroundColor: - 'color-mix(in srgb, var(--md-comp-filled-input-disabled-container-color) calc(var(--md-comp-filled-input-disabled-container-opacity) * 100%), transparent)', - }, - ...(!ownerState.disableUnderline && { - '&::after': { - borderBottom: '2px solid var(--md-comp-filled-input-active-indicator-color)', - left: 0, - bottom: 0, - // Doing the other way around crash on IE11 "''" https://github.com/cssinjs/jss/issues/242 - content: '""', - position: 'absolute', - right: 0, - transform: 'scaleX(0)', - transition: theme.transitions.create('transform', { - duration: theme.transitions.duration.shorter, - easing: theme.transitions.easing.easeOut, - }), - pointerEvents: 'none', // Transparent to the hover style. - }, - [`&.${filledInputClasses.focused}:after`]: { - // translateX(0) is a workaround for Safari transform scale bug - // See https://github.com/mui/material-ui/issues/31766 - transform: 'scaleX(1) translateX(0)', - }, - [`&.${filledInputClasses.error}`]: { - '&::before, &::after': { - borderBottomColor: 'var(--md-comp-filled-input-error-active-indicator-color)', - }, - }, - '&::before': { - borderBottom: '1px solid var(--md-comp-filled-input-active-indicator-color)', - left: 0, - bottom: 0, - // Doing the other way around crash on IE11 "''" https://github.com/cssinjs/jss/issues/242 - content: '"\\00a0"', - position: 'absolute', - right: 0, - transition: theme.transitions.create('border-bottom-color', { - duration: theme.transitions.duration.shorter, - }), - pointerEvents: 'none', // Transparent to the hover style. - }, - [`&:hover:not(.${filledInputClasses.disabled}, .${filledInputClasses.error}):before`]: { - borderBottom: '1px solid var(--md-comp-filled-input-active-indicator-color)', - }, - [`&.${filledInputClasses.disabled}:before`]: { - borderColor: - 'color-mix(in srgb, var(--md-comp-filled-input-disabled-active-indicator-color) calc(var(--md-comp-filled-input-disabled-active-indicator-opacity) * 100%), transparent)', - }, - }), - ...(ownerState.startAdornment && { - paddingLeft: 12, - }), - ...(ownerState.endAdornment && { - paddingRight: 12, - }), - ...(ownerState.multiline && { - padding: '25px 12px 8px', - ...(ownerState.size === 'small' && { - paddingTop: 21, - paddingBottom: 4, - }), - ...(ownerState.hiddenLabel && { - paddingTop: 16, - paddingBottom: 17, - }), - }), - }; -}); - -const FilledInputInput = styled(InputBaseInput, { - name: 'MuiFilledInput', - slot: 'Input', - overridesResolver: inputBaseInputOverridesResolver, -})<{ ownerState: FilledInputOwnerState }>(({ theme, ownerState }) => { - const { vars: tokens } = theme; - - return { - paddingTop: 25, - paddingRight: 16, - paddingBottom: 8, - paddingLeft: 16, - ...(!tokens - ? { - [theme.getColorSchemeSelector('light')]: { - '&:-webkit-autofill': { - WebkitBoxShadow: null, - WebkitTextFillColor: null, - caretColor: null, - borderTopLeftRadius: 'inherit', - borderTopRightRadius: 'inherit', - }, - }, - [theme.getColorSchemeSelector('dark')]: { - '&:-webkit-autofill': { - WebkitBoxShadow: '0 0 0 100px #266798 inset', - WebkitTextFillColor: '#fff', - caretColor: '#fff', - borderTopLeftRadius: 'inherit', - borderTopRightRadius: 'inherit', - }, - }, - } - : { - '&:-webkit-autofill': { - borderTopLeftRadius: 'inherit', - borderTopRightRadius: 'inherit', - }, - // this could be undefined in unit tests - ...(theme.getColorSchemeSelector && { - [theme.getColorSchemeSelector('dark')]: { - '&:-webkit-autofill': { - WebkitBoxShadow: '0 0 0 100px #266798 inset', - WebkitTextFillColor: '#fff', - caretColor: '#fff', - }, - }, - }), - }), - ...(ownerState.size === 'small' && { - paddingTop: 21, - paddingBottom: 4, - }), - ...(ownerState.hiddenLabel && { - paddingTop: 16, - paddingBottom: 17, - }), - ...(ownerState.multiline && { - paddingTop: 0, - paddingBottom: 0, - paddingLeft: 0, - paddingRight: 0, - }), - ...(ownerState.startAdornment && { - paddingLeft: 0, - }), - ...(ownerState.endAdornment && { - paddingRight: 0, - }), - ...(ownerState.hiddenLabel && - ownerState.size === 'small' && { - paddingTop: 8, - paddingBottom: 9, - }), - }; -}); - -const FilledInput = React.forwardRef(function FilledInput< - RootComponentType extends React.ElementType, ->(inProps: FilledInputProps, forwardedRef: React.ForwardedRef) { - const props = useThemeProps({ props: inProps, name: 'MuiFilledInput' }); - - const { - disableUnderline, - fullWidth = false, - hiddenLabel, // declare here to prevent spreading to DOM - inputComponent = 'input', - multiline = false, - type = 'text', - slotProps = {}, - slots = {}, - ...other - } = props; - - const ownerState: FilledInputOwnerState = { - ...props, - disableUnderline, - fullWidth, - inputComponent, - multiline, - type, - }; - - const classes = useUtilityClasses(ownerState); - - const Root = slots.root ?? FilledInputRoot; - const Input = slots.input ?? FilledInputInput; - - const rootProps = useSlotProps({ - elementType: Root, - externalSlotProps: slotProps.root, - additionalProps: { - ref: forwardedRef, - fullWidth, - inputComponent, - multiline, - type, - }, - externalForwardedProps: other, - ownerState: ownerState as FilledInputOwnerState & InputBaseOwnerState, - className: [classes.root], - }); - - const inputProps = useSlotProps({ - elementType: Input, - externalSlotProps: slotProps.input, - ownerState: ownerState as FilledInputOwnerState & InputBaseOwnerState, - className: [classes.input], - }); - - return ( - - ); -}) as FilledInputComponent; - -interface FilledInputComponent { - ( - props: { - /** - * The component used for the input node. - * Either a string to use a HTML element or a component. - * @default 'input' - */ - inputComponent?: C; - } & OverrideProps, - ): JSX.Element | null; - (props: DefaultComponentProps): JSX.Element | null; - propTypes?: any; - muiName?: string; -} - -FilledInput.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * This prop helps users to fill forms faster, especially on mobile devices. - * The name can be confusing, as it's more like an autofill. - * You can learn more about it [following the specification](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill). - */ - autoComplete: PropTypes.string, - /** - * If `true`, the `input` element is focused during the first mount. - */ - autoFocus: PropTypes.bool, - /** - * @ignore - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * The prop defaults to the value (`'primary'`) inherited from the parent FormControl component. - */ - color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['error', 'info', 'primary', 'secondary', 'success', 'tertiary', 'warning']), - PropTypes.string, - ]), - /** - * The default value. Use when the component is not controlled. - */ - defaultValue: PropTypes.any, - /** - * If `true`, the component is disabled. - * The prop defaults to the value (`false`) inherited from the parent FormControl component. - */ - disabled: PropTypes.bool, - /** - * If `true`, the input will not have an underline. - */ - disableUnderline: PropTypes.bool, - /** - * End `InputAdornment` for this component. - */ - endAdornment: PropTypes.node, - /** - * If `true`, the `input` will indicate an error. - * The prop defaults to the value (`false`) inherited from the parent FormControl component. - */ - error: PropTypes.bool, - /** - * If `true`, the `input` will take up the full width of its container. - * @default false - */ - fullWidth: PropTypes.bool, - /** - * If `true`, the label is hidden. - * This is used to increase density for a `FilledInput`. - * Be sure to add `aria-label` to the `input` element. - * @default false - */ - hiddenLabel: PropTypes.bool, - /** - * The id of the `input` element. - */ - id: PropTypes.string, - /** - * The component used for the input node. - * Either a string to use a HTML element or a component. - * @default 'input' - */ - inputComponent: PropTypes /* @typescript-to-proptypes-ignore */.elementType, - /** - * Pass a ref to the `input` element. - */ - inputRef: refType, - /** - * If `dense`, will adjust vertical spacing. This is normally obtained via context from - * FormControl. - * The prop defaults to the value (`'none'`) inherited from the parent FormControl component. - */ - margin: PropTypes.oneOf(['dense', 'none']), - /** - * Maximum number of rows to display when multiline option is set to true. - */ - maxRows: PropTypes.number, - /** - * Minimum number of rows to display when multiline option is set to true. - */ - minRows: PropTypes.number, - /** - * If `true`, a `textarea` element is rendered. - * @default false - */ - multiline: PropTypes.bool, - /** - * Name attribute of the `input` element. - */ - name: PropTypes.string, - /** - * Callback fired when the value is changed. - * - * @param {React.ChangeEvent} event The event source of the callback. - * You can pull out the new value by accessing `event.target.value` (string). - */ - onChange: PropTypes.func, - /** - * The short hint displayed in the `input` before the user enters a value. - */ - placeholder: PropTypes.string, - /** - * It prevents the user from changing the value of the field - * (not from interacting with the field). - */ - readOnly: PropTypes.bool, - /** - * If `true`, the `input` element is required. - * The prop defaults to the value (`false`) inherited from the parent FormControl component. - */ - required: PropTypes.bool, - /** - * Number of rows to display when multiline option is set to true. - */ - rows: PropTypes.number, - /** - * The props used for each slot inside the Input. - * @default {} - */ - slotProps: PropTypes.shape({ - input: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - }), - /** - * The components used for each slot inside the InputBase. - * Either a string to use a HTML element or a component. - * @default {} - */ - slots: PropTypes.shape({ - input: PropTypes.elementType, - root: PropTypes.elementType, - }), - /** - * Start `InputAdornment` for this component. - */ - startAdornment: PropTypes.node, - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), - PropTypes.func, - PropTypes.object, - ]), - /** - * Type of the `input` element. It should be [a valid HTML5 input type](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Form_%3Cinput%3E_types). - * @default 'text' - */ - type: PropTypes /* @typescript-to-proptypes-ignore */.oneOf([ - 'button', - 'checkbox', - 'color', - 'date', - 'datetime-local', - 'email', - 'file', - 'hidden', - 'image', - 'month', - 'number', - 'password', - 'radio', - 'range', - 'reset', - 'search', - 'submit', - 'tel', - 'text', - 'time', - 'url', - 'week', - ]), - /** - * The value of the `input` element, required for a controlled component. - */ - value: PropTypes.any, -} as any; - -FilledInput.muiName = 'Input'; - -export default FilledInput; diff --git a/packages/mui-material-next/src/FilledInput/FilledInput.types.ts b/packages/mui-material-next/src/FilledInput/FilledInput.types.ts deleted file mode 100644 index fe229efca59b7f..00000000000000 --- a/packages/mui-material-next/src/FilledInput/FilledInput.types.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { SxProps } from '@mui/system'; -// TODO v6: port to material-next -// eslint-disable-next-line no-restricted-imports -import { InternalStandardProps as StandardProps } from '@mui/material'; -import { OverrideProps, Simplify } from '@mui/types'; -import { Theme } from '../styles/Theme.types'; -import { InputBaseProps } from '../InputBase/InputBase.types'; -import { FilledInputClasses } from './filledInputClasses'; - -export interface FilledInputOwnProps - extends StandardProps> { - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial; - /** - * If `true`, the label is hidden. - * This is used to increase density for a `FilledInput`. - * Be sure to add `aria-label` to the `input` element. - * @default false - */ - hiddenLabel?: boolean; - /** - * If `true`, the input will not have an underline. - */ - disableUnderline?: boolean; - /** - * The components used for each slot inside the InputBase. - * Either a string to use a HTML element or a component. - * @default {} - */ - slots?: FilledInputSlots; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps; -} - -export interface FilledInputSlots { - /** - * The component that renders the root. - * @default 'div' - */ - root?: React.ElementType; - /** - * The component that renders the input. - * @default 'input' - */ - input?: React.ElementType; -} - -export interface FilledInputTypeMap< - AdditionalProps = {}, - RootComponentType extends React.ElementType = 'div', -> { - props: FilledInputOwnProps & AdditionalProps; - defaultComponent: RootComponentType; -} - -export type FilledInputProps< - RootComponentType extends React.ElementType = FilledInputTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps, RootComponentType> & { - inputComponent?: React.ElementType; -}; - -export type FilledInputOwnerState = Simplify< - FilledInputOwnProps & { - disableUnderline?: boolean; - fullWidth: boolean; - inputComponent: React.ElementType; - multiline: boolean; - type?: React.InputHTMLAttributes['type']; - } ->; diff --git a/packages/mui-material-next/src/FilledInput/filledInputClasses.ts b/packages/mui-material-next/src/FilledInput/filledInputClasses.ts deleted file mode 100644 index b9935def8117cd..00000000000000 --- a/packages/mui-material-next/src/FilledInput/filledInputClasses.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { - unstable_generateUtilityClass as generateUtilityClass, - unstable_generateUtilityClasses as generateUtilityClasses, -} from '@mui/utils'; -import { inputBaseClasses } from '../InputBase'; - -export interface FilledInputClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if color secondary. */ - colorSecondary: string; - /** Styles applied to the root element unless `disableUnderline={true}`. */ - underline: string; - /** State class applied to the root element if the component is focused. */ - focused: string; - /** State class applied to the root element if `disabled={true}`. */ - disabled: string; - /** Styles applied to the root element if `startAdornment` is provided. */ - adornedStart: string; - /** Styles applied to the root element if `endAdornment` is provided. */ - adornedEnd: string; - /** State class applied to the root element if `error={true}`. */ - error: string; - /** Styles applied to the input element if `size="small"`. */ - sizeSmall: string; - /** Styles applied to the root element if `multiline={true}`. */ - multiline: string; - /** Styles applied to the root element if `hiddenLabel={true}`. */ - hiddenLabel: string; - /** Styles applied to the input element. */ - input: string; - /** Styles applied to the input element if `size="small"`. */ - inputSizeSmall: string; - /** Styles applied to the `input` if in ``. */ - inputHiddenLabel: string; - /** Styles applied to the input element if `multiline={true}`. */ - inputMultiline: string; - /** Styles applied to the input element if `startAdornment` is provided. */ - inputAdornedStart: string; - /** Styles applied to the input element if `endAdornment` is provided. */ - inputAdornedEnd: string; - /** Styles applied to the input element if `type="search"`. */ - inputTypeSearch: string; -} - -export type FilledInputClassKey = keyof FilledInputClasses; - -export function getFilledInputUtilityClass(slot: string): string { - return generateUtilityClass('MuiFilledInput', slot); -} - -const filledInputClasses: FilledInputClasses = { - ...inputBaseClasses, - ...generateUtilityClasses('MuiFilledInput', ['root', 'underline', 'input']), -}; - -export default filledInputClasses; diff --git a/packages/mui-material-next/src/FilledInput/index.ts b/packages/mui-material-next/src/FilledInput/index.ts deleted file mode 100644 index d1afd2384ad9b5..00000000000000 --- a/packages/mui-material-next/src/FilledInput/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './FilledInput'; - -export { default as filledInputClasses } from './filledInputClasses'; -export * from './filledInputClasses'; diff --git a/packages/mui-material-next/src/FormControl/FormControl.test.tsx b/packages/mui-material-next/src/FormControl/FormControl.test.tsx deleted file mode 100644 index e764a8181c2e73..00000000000000 --- a/packages/mui-material-next/src/FormControl/FormControl.test.tsx +++ /dev/null @@ -1,481 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { spy } from 'sinon'; -import { ClassNames } from '@emotion/react'; -import { act, createRenderer, fireEvent } from '@mui-internal/test-utils'; -import FormControl, { formControlClasses as classes } from '@mui/material-next/FormControl'; -import FilledInput from '@mui/material-next/FilledInput'; -import InputBase from '@mui/material-next/InputBase'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -// TODO v6: replace with material-next/Select -import Select from '@mui/material/Select'; -import useFormControl from './useFormControl'; -import describeConformance from '../../test/describeConformance'; - -type TestFormControlledComponent = { - onFilled: () => {}; - onEmpty: () => {}; - onFocus: () => {}; - onBlur: () => {}; -}; - -describe('', () => { - const { render } = createRenderer(); - - interface TestComponentProps { - contextCallback: (context: ReturnType) => void; - } - - function TestComponent(props: TestComponentProps) { - const context = useFormControl(); - React.useEffect(() => { - props.contextCallback(context); - }); - return null; - } - - describeConformance(, () => ({ - classes, - inheritComponent: 'div', - ThemeProvider: CssVarsProvider, - createTheme: extendTheme, - render, - refInstanceof: window.HTMLDivElement, - muiName: 'MuiFormControl', - slots: { - root: { - expectedClassName: classes.root, - testWithElement: 'fieldset', - }, - }, - testRootOverrides: { slotName: 'root', slotClassName: classes.root }, - testComponentPropWith: 'fieldset', - testVariantProps: { margin: 'dense' }, - skip: ['componentsProp'], - })); - - describe('initial state', () => { - it('should have no margin', () => { - const { container } = render(); - const root = container.firstChild; - - expect(root).not.to.have.class(classes.marginNormal); - }); - - it('can have the margin normal class', () => { - const { container } = render(); - const root = container.firstChild; - - expect(root).to.have.class(classes.marginNormal); - }); - - it('can have the margin dense class', () => { - const { container } = render(); - const root = container.firstChild; - - expect(root).to.have.class(classes.marginDense); - expect(root).not.to.have.class(classes.marginNormal); - }); - - it('should not be filled initially', () => { - const readContext = spy(); - render( - - - , - ); - expect(readContext.args[0][0]).to.have.property('filled', false); - }); - - it('should not be focused initially', () => { - const readContext = spy(); - render( - - - , - ); - expect(readContext.args[0][0]).to.have.property('focused', false); - }); - }); - - describe('prop: required', () => { - it('should not apply it to the DOM', () => { - const { container } = render(); - expect(container.firstChild).not.to.have.attribute('required'); - }); - }); - - describe('prop: disabled', () => { - it('will be unfocused if it gets disabled', () => { - const readContext = spy(); - const { container, setProps } = render( - - - - , - ); - expect(readContext.args[0][0]).to.have.property('focused', false); - - act(() => { - container.querySelector('input')?.focus(); - }); - expect(readContext.lastCall.args[0]).to.have.property('focused', true); - - setProps({ disabled: true }); - expect(readContext.lastCall.args[0]).to.have.property('focused', false); - }); - }); - - describe('prop: focused', () => { - it('should display input in focused state', () => { - const readContext = spy(); - const { container } = render( - - - - , - ); - - expect(readContext.args[0][0]).to.have.property('focused', true); - container.querySelector('input')?.blur(); - expect(readContext.args[0][0]).to.have.property('focused', true); - }); - - it('ignores focused when disabled', () => { - const readContext = spy(); - render( - - - - , - ); - expect(readContext.args[0][0]).to.include({ disabled: true, focused: false }); - }); - }); - - describe('registering input', () => { - it("should warn if more than one input is rendered regardless how it's nested", () => { - expect(() => { - render( - - -
- {/* should work regardless how it's nested */} - -
-
, - ); - }).toErrorDev([ - 'MUI: There are multiple `InputBase` components inside a FormControl.\nThis creates visual inconsistencies, only use one `InputBase`.', - // React 18 Strict Effects run mount effects twice - React.version.startsWith('18') && - 'MUI: There are multiple `InputBase` components inside a FormControl.\nThis creates visual inconsistencies, only use one `InputBase`.', - ]); - }); - - it('should not warn if only one input is rendered', () => { - expect(() => { - render( - - - , - ); - }).not.toErrorDev(); - }); - - it('should not warn when toggling between inputs', () => { - // this will ensure that deregistering was called during unmount - function ToggleFormInputs() { - const [flag, setFlag] = React.useState(true); - - return ( - - {flag ? ( - - ) : ( - // TODO v6: use material-next/Select - - )} - - - ); - } - - const { getByText } = render(); - expect(() => { - fireEvent.click(getByText('toggle')); - }).not.toErrorDev(); - }); - }); - - describe('input', () => { - it('should be filled when a value is set', () => { - const readContext = spy(); - render( - - - - , - ); - expect(readContext.args[0][0]).to.have.property('filled', true); - }); - - it('should be filled when a value is set through slotProps.input', () => { - const readContext = spy(); - render( - - - - , - ); - expect(readContext.args[0][0]).to.have.property('filled', true); - }); - - it('should be filled when a defaultValue is set', () => { - const readContext = spy(); - render( - - - - , - ); - expect(readContext.args[0][0]).to.have.property('filled', true); - }); - - it('should not be adornedStart with an endAdornment', () => { - const readContext = spy(); - render( - - } /> - - , - ); - expect(readContext.args[0][0]).to.have.property('adornedStart', false); - }); - - it('should be adornedStart with a startAdornment', () => { - const readContext = spy(); - render( - - } /> - - , - ); - expect(readContext.args[0][0]).to.have.property('adornedStart', true); - }); - }); - - // TODO v6: needs material-next/Select + FormControl integrated - // eslint-disable-next-line mocha/no-skipped-tests - describe.skip('select', () => { - it('should not be adorned without a startAdornment', () => { - const readContext = spy(); - render( - - } />} /> - - , - ); - expect(readContext.args[0][0].adornedStart, 'true'); - }); - }); - - describe('useFormControl', () => { - const FormController = React.forwardRef((_, ref) => { - const formControl = useFormControl(); - React.useImperativeHandle(ref, () => formControl, [formControl]); - return null; - }); - - const FormControlled = React.forwardRef(function FormControlled(props, ref) { - return ( - - - - ); - }); - - describe('from props', () => { - it('should have the required prop from the instance', () => { - const formControlRef = React.createRef(); - const { setProps } = render(); - - expect(formControlRef.current).to.have.property('required', false); - - setProps({ required: true }); - expect(formControlRef.current).to.have.property('required', true); - }); - - it('should have the error prop from the instance', () => { - const formControlRef = React.createRef(); - const { setProps } = render(); - - expect(formControlRef.current).to.have.property('error', false); - - setProps({ error: true }); - expect(formControlRef.current).to.have.property('error', true); - }); - - it('should have the margin prop from the instance', () => { - const formControlRef = React.createRef(); - const { setProps } = render(); - - expect(formControlRef.current).to.have.property('size', 'medium'); - - setProps({ size: 'small' }); - expect(formControlRef.current).to.have.property('size', 'small'); - }); - - it('should have the fullWidth prop from the instance', () => { - const formControlRef = React.createRef(); - const { setProps } = render(); - - expect(formControlRef.current).to.have.property('fullWidth', false); - - setProps({ fullWidth: true }); - expect(formControlRef.current).to.have.property('fullWidth', true); - }); - }); - - describe('callbacks', () => { - describe('onFilled', () => { - it('should set the filled state', () => { - const formControlRef = React.createRef(); - render(); - - expect(formControlRef.current).to.have.property('filled', false); - - act(() => { - formControlRef.current?.onFilled(); - }); - - expect(formControlRef.current).to.have.property('filled', true); - - act(() => { - formControlRef.current?.onFilled(); - }); - - expect(formControlRef.current).to.have.property('filled', true); - }); - }); - - describe('onEmpty', () => { - it('should clean the filled state', () => { - const formControlRef = React.createRef(); - render(); - - act(() => { - formControlRef.current?.onFilled(); - }); - - expect(formControlRef.current).to.have.property('filled', true); - - act(() => { - formControlRef.current?.onEmpty(); - }); - - expect(formControlRef.current).to.have.property('filled', false); - - act(() => { - formControlRef.current?.onEmpty(); - }); - - expect(formControlRef.current).to.have.property('filled', false); - }); - }); - - describe('handleFocus', () => { - it('should set the focused state', () => { - const formControlRef = React.createRef(); - render(); - expect(formControlRef.current).to.have.property('focused', false); - - act(() => { - formControlRef.current?.onFocus(); - }); - - expect(formControlRef.current).to.have.property('focused', true); - - act(() => { - formControlRef.current?.onFocus(); - }); - - expect(formControlRef.current).to.have.property('focused', true); - }); - }); - - describe('handleBlur', () => { - it('should clear the focused state', () => { - const formControlRef = React.createRef(); - render(); - expect(formControlRef.current).to.have.property('focused', false); - - act(() => { - formControlRef.current?.onFocus(); - }); - - expect(formControlRef.current).to.have.property('focused', true); - - act(() => { - formControlRef.current?.onBlur(); - }); - - expect(formControlRef.current).to.have.property('focused', false); - - act(() => { - formControlRef.current?.onBlur(); - }); - - expect(formControlRef.current).to.have.property('focused', false); - }); - }); - }); - }); - - describe('Emotion compatibility', () => { - it('classes.root should overwrite built-in styles.', () => { - const { getByTestId } = render( - - {({ css }) => ( - - )} - , - ); - const root = getByTestId('root'); - - expect(getComputedStyle(root).display).to.equal('inline'); - }); - - it('className should overwrite classes.root and built-in styles.', () => { - const { getByTestId } = render( - - {({ css }) => ( - - )} - , - ); - const root = getByTestId('root'); - - expect(getComputedStyle(root).display).to.equal('inline-block'); - }); - }); -}); diff --git a/packages/mui-material-next/src/FormControl/FormControl.tsx b/packages/mui-material-next/src/FormControl/FormControl.tsx deleted file mode 100644 index 68aac7ad936019..00000000000000 --- a/packages/mui-material-next/src/FormControl/FormControl.tsx +++ /dev/null @@ -1,366 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import { useSlotProps } from '@mui/base'; -import { OverridableComponent } from '@mui/types'; -import { - unstable_capitalize as capitalize, - unstable_isMuiElement as isMuiElement, -} from '@mui/utils'; -import useThemeProps from '../styles/useThemeProps'; -import styled from '../styles/styled'; -import { isFilled, isAdornedStart } from '../InputBase/utils'; -import FormControlContext from './FormControlContext'; -import { FormControlTypeMap, FormControlOwnerState, FormControlProps } from './FormControl.types'; -import { getFormControlUtilityClasses } from './formControlClasses'; - -const useUtilityClasses = (ownerState: FormControlOwnerState) => { - const { classes, margin, fullWidth } = ownerState; - const slots = { - root: ['root', margin !== 'none' && `margin${capitalize(margin)}`, fullWidth && 'fullWidth'], - }; - - return composeClasses(slots, getFormControlUtilityClasses, classes); -}; - -const FormControlRoot = styled('div', { - name: 'MuiFormControl', - slot: 'Root', - overridesResolver: ({ ownerState }, styles) => { - return [ - styles.root, - styles[`margin${capitalize(ownerState.margin)}`], - ownerState.fullWidth && styles.fullWidth, - ]; - }, -})<{ ownerState: FormControlOwnerState }>(({ ownerState }) => ({ - display: 'inline-flex', - flexDirection: 'column', - position: 'relative', - // Reset fieldset default style. - minWidth: 0, - padding: 0, - margin: 0, - border: 0, - verticalAlign: 'top', // Fix alignment issue on Safari. - ...(ownerState.margin === 'normal' && { - marginTop: 16, - marginBottom: 8, - }), - ...(ownerState.margin === 'dense' && { - marginTop: 8, - marginBottom: 4, - }), - ...(ownerState.fullWidth && { - width: '100%', - }), -})); - -/** - * Provides context such as filled/focused/error/required for form inputs. - * Relying on the context provides high flexibility and ensures that the state always stays - * consistent across the children of the `FormControl`. - * This context is used by the following components: - * - * - FormLabel - * - FormHelperText - * - Input - * - InputLabel - * - * You can find one composition example below and more going to [the demos](/material-ui/react-text-field/#components). - * - * ```jsx - * - * Email address - * - * We'll never share your email. - * - * ``` - * - * ⚠️ Only one `InputBase` can be used within a FormControl because it creates visual inconsistencies. - * For instance, only one input can be focused at the same time, the state shouldn't be shared. - */ -const FormControl = React.forwardRef(function FormControl< - RootComponentType extends React.ElementType = FormControlTypeMap['defaultComponent'], ->(inProps: FormControlProps, forwardedRef: React.ForwardedRef) { - const props = useThemeProps({ props: inProps, name: 'MuiFormControl' }); - const { - children, - classes: classesProp = {}, - color = 'primary', - component: componentProp, - disabled = false, - error = false, - focused: visuallyFocused, - fullWidth = false, - hiddenLabel = false, - margin = 'none', - required = false, - size = 'medium', - slotProps = {}, - slots = {}, - variant = 'outlined', - ...other - } = props; - - const [adornedStart, setAdornedStart] = React.useState(() => { - // We need to iterate through the children and find the Input in order - // to fully support server-side rendering. - let initialAdornedStart = false; - - if (children) { - React.Children.forEach(children, (child) => { - if (!isMuiElement(child, ['Input', 'Select'])) { - return; - } - - const input = - React.isValidElement(child) && isMuiElement(child, ['Select']) - ? child.props.input - : child; - - if (input && isAdornedStart(input.props)) { - initialAdornedStart = true; - } - }); - } - return initialAdornedStart; - }); - - const [filled, setFilled] = React.useState(() => { - // We need to iterate through the children and find the Input in order - // to fully support server-side rendering. - let initialFilled = false; - - if (children) { - React.Children.forEach(children, (child) => { - if (!isMuiElement(child, ['Input', 'Select'])) { - return; - } - - if ( - React.isValidElement(child) && - (isFilled(child.props, true) || isFilled(child.props.slotProps?.input, true)) - ) { - initialFilled = true; - } - }); - } - - return initialFilled; - }); - - const [focusedState, setFocused] = React.useState(false); - if (disabled && focusedState) { - setFocused(false); - } - - const focused = visuallyFocused !== undefined && !disabled ? visuallyFocused : focusedState; - - let registerEffect: undefined | (() => () => void); - if (process.env.NODE_ENV !== 'production') { - // eslint-disable-next-line react-hooks/rules-of-hooks - const registeredInput = React.useRef(false); - registerEffect = () => { - if (registeredInput.current) { - console.error( - [ - 'MUI: There are multiple `InputBase` components inside a FormControl.', - 'This creates visual inconsistencies, only use one `InputBase`.', - ].join('\n'), - ); - } - - registeredInput.current = true; - return () => { - registeredInput.current = false; - }; - }; - } - - const ownerState = { - ...props, - classes: classesProp, - color, - component: componentProp, - disabled, - error, - filled, - focused, - fullWidth, - hiddenLabel, - margin, - required, - size, - variant, - }; - - const classes = useUtilityClasses(ownerState); - - const childContext = React.useMemo(() => { - return { - adornedStart, - setAdornedStart, - color, - disabled, - error, - filled, - focused, - fullWidth, - hiddenLabel, - size, - onBlur: () => { - setFocused(false); - }, - onEmpty: () => { - setFilled(false); - }, - onFilled: () => { - setFilled(true); - }, - onFocus: () => { - setFocused(true); - }, - registerEffect, - required, - variant, - }; - }, [ - adornedStart, - color, - disabled, - error, - filled, - focused, - fullWidth, - hiddenLabel, - registerEffect, - required, - size, - variant, - ]); - - const Root = slots.root ?? FormControlRoot; - const rootProps = useSlotProps({ - elementType: Root, - externalSlotProps: slotProps.root, - externalForwardedProps: other, - additionalProps: { - ref: forwardedRef, - as: componentProp, - }, - ownerState, - className: classes.root, - }); - - return ( - - {children} - - ); -}) as OverridableComponent; - -FormControl.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * The content of the component. - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'primary' - */ - color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['primary', 'secondary', 'error', 'info', 'success', 'warning']), - PropTypes.string, - ]), - /** - * The component used for the root node. - * Either a string to use a HTML element or a component. - */ - component: PropTypes.elementType, - /** - * If `true`, the label, input and helper text should be displayed in a disabled state. - * @default false - */ - disabled: PropTypes.bool, - /** - * If `true`, the label is displayed in an error state. - * @default false - */ - error: PropTypes.bool, - /** - * If `true`, the component is displayed in focused state. - */ - focused: PropTypes.bool, - /** - * If `true`, the component will take up the full width of its container. - * @default false - */ - fullWidth: PropTypes.bool, - /** - * If `true`, the label is hidden. - * This is used to increase density for a `FilledInput`. - * Be sure to add `aria-label` to the `input` element. - * @default false - */ - hiddenLabel: PropTypes.bool, - /** - * If `dense` or `normal`, will adjust vertical spacing of this and contained components. - * @default 'none' - */ - margin: PropTypes.oneOf(['dense', 'none', 'normal']), - /** - * If `true`, the label will indicate that the `input` is required. - * @default false - */ - required: PropTypes.bool, - /** - * The size of the component. - * @default 'medium' - */ - size: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['medium', 'small']), - PropTypes.string, - ]), - /** - * The props used for each slot inside the FormControl. - * @default {} - */ - slotProps: PropTypes.shape({ - root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - }), - /** - * The components used for each slot inside the FormControl. - * Either a string to use a HTML element or a component. - * @default {} - */ - slots: PropTypes.shape({ - root: PropTypes.elementType, - }), - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), - PropTypes.func, - PropTypes.object, - ]), - /** - * The variant to use. - * @default 'outlined' - */ - variant: PropTypes.oneOf(['filled', 'outlined']), -} as any; - -export default FormControl; diff --git a/packages/mui-material-next/src/FormControl/FormControl.types.ts b/packages/mui-material-next/src/FormControl/FormControl.types.ts deleted file mode 100644 index 272ca958a160f9..00000000000000 --- a/packages/mui-material-next/src/FormControl/FormControl.types.ts +++ /dev/null @@ -1,93 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { FormControlOwnProps as BaseFormControlOwnProps } from '@mui/base/FormControl'; -import { OverridableStringUnion, OverrideProps, Simplify } from '@mui/types'; -import { Theme } from '../styles'; -import { FormControlClasses } from './formControlClasses'; - -export interface FormControlPropsSizeOverrides {} -export interface FormControlPropsColorOverrides {} - -export interface FormControlOwnProps extends BaseFormControlOwnProps { - /** - * The content of the component. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial; - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'primary' - */ - color?: OverridableStringUnion< - 'primary' | 'secondary' | 'tertiary' | 'error' | 'info' | 'success' | 'warning', - FormControlPropsColorOverrides - >; - /** - * If `true`, the component will take up the full width of its container. - * @default false - */ - fullWidth?: boolean; - /** - * If `true`, the component is displayed in focused state. - */ - focused?: boolean; - /** - * If `true`, the label is hidden. - * This is used to increase density for a `FilledInput`. - * Be sure to add `aria-label` to the `input` element. - * @default false - */ - hiddenLabel?: boolean; - /** - * If `dense` or `normal`, will adjust vertical spacing of this and contained components. - * @default 'none' - */ - margin?: 'dense' | 'normal' | 'none'; - /** - * The size of the component. - * @default 'medium' - */ - size?: OverridableStringUnion<'small' | 'medium', FormControlPropsSizeOverrides>; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps; - /** - * The variant to use. - * @default 'outlined' - */ - variant?: 'outlined' | 'filled'; -} - -export interface FormControlTypeMap< - AdditionalProps = {}, - RootComponentType extends React.ElementType = 'div', -> { - props: FormControlOwnProps & AdditionalProps; - defaultComponent: RootComponentType; -} - -export type FormControlProps< - RootComponentType extends React.ElementType = FormControlTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps, RootComponentType> & { - component?: React.ElementType; -}; - -type MaterialDesignOwnerStateKeys = - | 'classes' - | 'color' - | 'margin' - | 'size' - | 'fullWidth' - | 'hiddenLabel' - | 'variant'; - -export type FormControlOwnerState = Simplify< - Required> & FormControlProps ->; diff --git a/packages/mui-material-next/src/FormControl/FormControlContext.ts b/packages/mui-material-next/src/FormControl/FormControlContext.ts deleted file mode 100644 index 16cb4e9c83ca55..00000000000000 --- a/packages/mui-material-next/src/FormControl/FormControlContext.ts +++ /dev/null @@ -1,36 +0,0 @@ -import * as React from 'react'; -import { FormControlProps } from './FormControl.types'; - -type ContextFromPropsKey = - | 'color' - | 'disabled' - | 'error' - | 'fullWidth' - | 'hiddenLabel' - | 'margin' - | 'required' - | 'size' - | 'variant'; - -export interface FormControlContextValue extends Pick { - adornedStart: boolean; - filled: boolean; - focused: boolean; - onBlur: (event?: React.FocusEvent) => void; - onFocus: (event: React.FocusEvent) => void; - onEmpty: () => void; - onFilled: () => void; - registerEffect: undefined | (() => () => void); - setAdornedStart: React.Dispatch>; -} - -/** - * @internal - */ -const FormControlContext = React.createContext(undefined); - -if (process.env.NODE_ENV !== 'production') { - FormControlContext.displayName = 'FormControlContext'; -} - -export default FormControlContext; diff --git a/packages/mui-material-next/src/FormControl/formControlClasses.ts b/packages/mui-material-next/src/FormControl/formControlClasses.ts deleted file mode 100644 index adcdd4ff8cfd16..00000000000000 --- a/packages/mui-material-next/src/FormControl/formControlClasses.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { - unstable_generateUtilityClass as generateUtilityClass, - unstable_generateUtilityClasses as generateUtilityClasses, -} from '@mui/utils'; - -export interface FormControlClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if `margin="normal"`. */ - marginNormal: string; - /** Styles applied to the root element if `margin="dense"`. */ - marginDense: string; - /** Styles applied to the root element if `fullWidth={true}`. */ - fullWidth: string; -} - -export type FormControlClassKey = keyof FormControlClasses; - -export function getFormControlUtilityClasses(slot: string): string { - return generateUtilityClass('MuiFormControl', slot); -} - -const formControlClasses: FormControlClasses = generateUtilityClasses('MuiFormControl', [ - 'root', - 'marginNone', - 'marginNormal', - 'marginDense', - 'fullWidth', - 'disabled', -]); - -export default formControlClasses; diff --git a/packages/mui-material-next/src/FormControl/formControlState.js b/packages/mui-material-next/src/FormControl/formControlState.js deleted file mode 100644 index 86c999f26e24c6..00000000000000 --- a/packages/mui-material-next/src/FormControl/formControlState.js +++ /dev/null @@ -1,15 +0,0 @@ -// TODO v6: decide whether to update/refactor this, keep as-is, or drop it -export default function formControlState({ props, states, muiFormControl }) { - // for every prop in `states` that is undefined, set it with the value from formControlContext - return states.reduce((acc, state) => { - acc[state] = props[state]; - - if (muiFormControl) { - if (typeof props[state] === 'undefined') { - acc[state] = muiFormControl[state]; - } - } - - return acc; - }, {}); -} diff --git a/packages/mui-material-next/src/FormControl/index.ts b/packages/mui-material-next/src/FormControl/index.ts deleted file mode 100644 index 4112e5a979f5f1..00000000000000 --- a/packages/mui-material-next/src/FormControl/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -'use client'; -export { default } from './FormControl'; -export { default as useFormControl } from './useFormControl'; - -export { default as formControlClasses } from './formControlClasses'; -export * from './formControlClasses'; diff --git a/packages/mui-material-next/src/FormControl/useFormControl.ts b/packages/mui-material-next/src/FormControl/useFormControl.ts deleted file mode 100644 index 8cf6ac69fbce7f..00000000000000 --- a/packages/mui-material-next/src/FormControl/useFormControl.ts +++ /dev/null @@ -1,7 +0,0 @@ -'use client'; -import * as React from 'react'; -import FormControlContext, { FormControlContextValue } from './FormControlContext'; - -export default function useFormControl(): FormControlContextValue | undefined { - return React.useContext(FormControlContext); -} diff --git a/packages/mui-material-next/src/FormHelperText/FormHelperText.spec.tsx b/packages/mui-material-next/src/FormHelperText/FormHelperText.spec.tsx deleted file mode 100644 index 02e801acbf0683..00000000000000 --- a/packages/mui-material-next/src/FormHelperText/FormHelperText.spec.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import * as React from 'react'; -import { expectType } from '@mui/types'; -import FormHelperText from '@mui/material-next/FormHelperText'; -import { FormHelperTextProps } from './FormHelperText.types'; - -const CustomComponent: React.FC<{ stringProp: string; numberProp: number }> = - function CustomComponent() { - return
; - }; - -const props1: FormHelperTextProps<'div'> = { - component: 'div', - onChange: (event) => { - expectType, typeof event>(event); - }, -}; - -const props2: FormHelperTextProps = { - onChange: (event) => { - expectType, typeof event>(event); - }, -}; - -const props3: FormHelperTextProps = { - component: CustomComponent, - stringProp: '2', - numberProp: 2, -}; - -const props4: FormHelperTextProps = { - component: CustomComponent, - stringProp: '2', - numberProp: 2, - // @ts-expect-error CustomComponent does not accept incorrectProp - incorrectProp: 3, -}; - -// @ts-expect-error missing props -const props5: FormHelperTextProps = { - component: CustomComponent, -}; - -const TestComponent = () => { - return ( - - - - - - { - // @ts-expect-error missing props - - } - { - expectType, typeof event>(event); - }} - /> - - ); -}; diff --git a/packages/mui-material-next/src/FormHelperText/FormHelperText.test.tsx b/packages/mui-material-next/src/FormHelperText/FormHelperText.test.tsx deleted file mode 100644 index 06830cec394053..00000000000000 --- a/packages/mui-material-next/src/FormHelperText/FormHelperText.test.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { createRenderer } from '@mui-internal/test-utils'; -import FormHelperText, { - formHelperTextClasses as classes, - FormHelperTextClasses, -} from '@mui/material-next/FormHelperText'; -import FormControl from '@mui/material-next/FormControl'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -import describeConformance from '../../test/describeConformance'; - -describe('', () => { - let originalMatchmedia: typeof window.matchMedia; - - beforeEach(() => { - originalMatchmedia = window.matchMedia; - window.matchMedia = () => - ({ - addListener: () => {}, - removeListener: () => {}, - }) as unknown as MediaQueryList; - }); - - afterEach(() => { - window.matchMedia = originalMatchmedia; - }); - - const { render } = createRenderer(); - - describeConformance(, () => ({ - classes, - inheritComponent: 'p', - render, - refInstanceof: window.HTMLParagraphElement, - testComponentPropWith: 'div', - muiName: 'MuiFormHelperText', - testVariantProps: { size: 'small' }, - ThemeProvider: CssVarsProvider, - createTheme: extendTheme, - slots: { - root: { - expectedClassName: classes.root, - }, - }, - skip: ['componentsProp'], - })); - - describe('prop: error', () => { - it('should have an error class', () => { - const { container } = render(); - expect(container.firstChild).to.have.class(classes.error); - }); - }); - - describe('with FormControl', () => { - ['error', 'disabled'].forEach((visualState) => { - describe(visualState, () => { - function FormHelperTextInFormControl(props: { children: React.ReactNode }) { - return ( - - Foo - - ); - } - - it(`should have the ${visualState} class`, () => { - const { getByText } = render( - Foo, - ); - - expect(getByText(/Foo/)).to.have.class( - classes[visualState as keyof FormHelperTextClasses], - ); - }); - - it('should be overrideable by props', () => { - const { getByText, setProps } = render( - - Foo - , - ); - - expect(getByText(/Foo/)).not.to.have.class( - classes[visualState as keyof FormHelperTextClasses], - ); - - setProps({ [visualState]: true }); - expect(getByText(/Foo/)).to.have.class( - classes[visualState as keyof FormHelperTextClasses], - ); - }); - }); - }); - - describe('size', () => { - describe('small margin FormControl', () => { - it('should have the small class', () => { - const { getByText } = render( - - Foo - , - ); - - expect(getByText(/Foo/)).to.have.class(classes.sizeSmall); - }); - }); - - it('should be overrideable by props', () => { - function FormHelperTextInFormControl(props: { children: React.ReactNode }) { - return ( - - Foo - - ); - } - - const { getByText, setProps } = render( - Foo, - ); - - expect(getByText(/Foo/)).not.to.have.class(classes.sizeSmall); - setProps({ size: 'small' }); - expect(getByText(/Foo/)).to.have.class(classes.sizeSmall); - }); - }); - }); -}); diff --git a/packages/mui-material-next/src/FormHelperText/FormHelperText.tsx b/packages/mui-material-next/src/FormHelperText/FormHelperText.tsx deleted file mode 100644 index 3f63eb73d17947..00000000000000 --- a/packages/mui-material-next/src/FormHelperText/FormHelperText.tsx +++ /dev/null @@ -1,235 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { unstable_composeClasses as composeClasses, useSlotProps } from '@mui/base'; -import { OverridableComponent } from '@mui/types'; -import { unstable_capitalize as capitalize } from '@mui/utils'; -import { useThemeProps, styled } from '../styles'; -import useFormControl from '../FormControl/useFormControl'; -import formHelperTextClasses, { getFormHelperTextUtilityClasses } from './formHelperTextClasses'; -import { - FormHelperTextOwnerState, - FormHelperTextProps, - FormHelperTextTypeMap, -} from './FormHelperText.types'; - -const useUtilityClasses = (ownerState: FormHelperTextOwnerState) => { - const { classes, contained, size, disabled, error, filled, focused, required } = ownerState; - const slots = { - root: [ - 'root', - disabled && 'disabled', - error && 'error', - size && `size${capitalize(size)}`, - contained && 'contained', - focused && 'focused', - filled && 'filled', - required && 'required', - ], - }; - - return composeClasses(slots, getFormHelperTextUtilityClasses, classes); -}; - -const FormHelperTextRoot = styled('p', { - name: 'MuiFormHelperText', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [ - styles.root, - ownerState.size && styles[`size${capitalize(ownerState.size)}`], - ownerState.contained && styles.contained, - ownerState.filled && styles.filled, - ]; - }, -})<{ ownerState: FormHelperTextOwnerState }>(({ theme, ownerState }) => { - const { vars: tokens } = theme; - - const pxFontSize = theme.sys.typescale.body.small.size; - const lineHeight = `calc(${tokens.sys.typescale.body.small.lineHeight} / ${pxFontSize})`; - - return { - '--md-comp-form-helper-text-color': tokens.sys.color.onSurfaceVariant, - '--md-comp-form-helper-text-font-family': tokens.sys.typescale.body.small.family, - '--md-comp-form-helper-text-font-size': theme.typography.pxToRem(pxFontSize), // the pxToRem should be moved to typescale in the future, - '--md-comp-form-helper-text-font-weight': tokens.sys.typescale.body.small.weight, - '--md-comp-form-helper-text-line-height': lineHeight, - '--md-comp-form-helper-text-disabled-color': tokens.sys.color.onSurface, - '--md-comp-form-helper-text-disabled-opacity': 0.38, - '--md-comp-form-helper-text-error-color': tokens.sys.color.error, - color: 'var(--md-comp-form-helper-text-color)', - fontFamily: 'var(--md-comp-form-helper-text-font-family)', - fontSize: 'var(--md-comp-form-helper-text-font-size)', - lineHeight: `var(--md-comp-form-helper-text-line-height)`, - textAlign: 'left', - marginTop: 3, - marginRight: 0, - marginBottom: 0, - marginLeft: 0, - [`&.${formHelperTextClasses.disabled}`]: { - color: - 'color-mix(in srgb, var(--md-comp-form-helper-text-disabled-color) calc(var(--md-comp-form-helper-text-disabled-opacity) * 100%), transparent)', - }, - [`&.${formHelperTextClasses.error}`]: { - color: 'var(--md-comp-form-helper-text-error-color)', - }, - ...(ownerState.size === 'small' && { - marginTop: 4, - }), - ...(ownerState.contained && { - marginLeft: 16, - marginRight: 16, - }), - }; -}); - -const FormHelperText = React.forwardRef(function FormHelperText< - RootComponentType extends React.ElementType = FormHelperTextTypeMap['defaultComponent'], ->(inProps: FormHelperTextProps, forwardedRef: React.ForwardedRef) { - const props = useThemeProps({ props: inProps, name: 'MuiFormHelperText' }); - const { - children, - component = 'p', - disabled: disabledProp, - error: errorProp, - filled: filledProp, - focused: focusedProp, - margin, - required: requiredProp, - size: sizeProp, - variant: variantProp, - slots = {}, - slotProps = {}, - ...other - } = props; - - const muiFormControl = useFormControl(); - - const variant = variantProp ?? muiFormControl?.variant ?? ''; - - const ownerState: FormHelperTextOwnerState = { - ...props, - component, - contained: variant === 'filled' || variant === 'outlined', - variant, - size: sizeProp ?? muiFormControl?.size, - disabled: disabledProp ?? muiFormControl?.disabled, - error: errorProp ?? muiFormControl?.error, - filled: filledProp ?? muiFormControl?.filled, - focused: focusedProp ?? muiFormControl?.focused, - required: requiredProp ?? muiFormControl?.required, - }; - - const classes = useUtilityClasses(ownerState); - - const RootSlot = slots?.root ?? FormHelperTextRoot; - - const rootProps = useSlotProps({ - elementType: RootSlot, - externalSlotProps: slotProps.root, - externalForwardedProps: other, - additionalProps: { - as: component, - ref: forwardedRef, - }, - ownerState, - className: [classes.root], - }); - - return ( - - {children === ' ' ? ( - // notranslate needed while Google Translate will not fix zero-width space issue - - ) : ( - children - )} - - ); -}) as OverridableComponent; - -FormHelperText.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * The content of the component. - * - * If `' '` is provided, the component reserves one line height for displaying a future message. - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The component used for the root node. - * Either a string to use a HTML element or a component. - */ - component: PropTypes.elementType, - /** - * If `true`, the helper text should be displayed in a disabled state. - */ - disabled: PropTypes.bool, - /** - * If `true`, helper text should be displayed in an error state. - */ - error: PropTypes.bool, - /** - * If `true`, the helper text should use filled classes key. - */ - filled: PropTypes.bool, - /** - * If `true`, the helper text should use focused classes key. - */ - focused: PropTypes.bool, - /** - * If `dense`, will adjust vertical spacing. This is normally obtained via context from - * FormControl. - */ - margin: PropTypes.oneOf(['dense']), - /** - * If `true`, the helper text should use required classes key. - */ - required: PropTypes.bool, - /** - * The props used for each slot inside the FormHelperText. - * @default {} - */ - slotProps: PropTypes.shape({ - root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - }), - /** - * The components used for each slot inside the FormHelperText. - * Either a string to use a HTML element or a component. - * @default {} - */ - slots: PropTypes.shape({ - root: PropTypes.elementType, - }), - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), - PropTypes.func, - PropTypes.object, - ]), - /** - * The variant to use. - * @default 'outlined' - */ - variant: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['filled', 'outlined', 'standard']), - PropTypes.string, - ]), -} as any; - -export default FormHelperText; diff --git a/packages/mui-material-next/src/FormHelperText/FormHelperText.types.ts b/packages/mui-material-next/src/FormHelperText/FormHelperText.types.ts deleted file mode 100644 index 81dce8e3fa70aa..00000000000000 --- a/packages/mui-material-next/src/FormHelperText/FormHelperText.types.ts +++ /dev/null @@ -1,102 +0,0 @@ -import * as React from 'react'; -import { SlotComponentProps } from '@mui/base'; -import { SxProps } from '@mui/system'; -import { OverrideProps, OverridableStringUnion } from '@mui/types'; -import { Theme } from '../styles'; -import { FormControlProps } from '../FormControl/FormControl.types'; -import { FormHelperTextClasses } from './formHelperTextClasses'; - -export interface FormHelperTextPropsVariantOverrides {} - -export interface FormHelperTextOwnProps { - /** - * The content of the component. - * - * If `' '` is provided, the component reserves one line height for displaying a future message. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial; - /** - * @ignore - */ - className?: string; - /** - * If `true`, the helper text should be displayed in a disabled state. - */ - disabled?: boolean; - /** - * If `true`, helper text should be displayed in an error state. - */ - error?: boolean; - /** - * If `true`, the helper text should use filled classes key. - */ - filled?: boolean; - /** - * If `true`, the helper text should use focused classes key. - */ - focused?: boolean; - /** - * If `dense`, will adjust vertical spacing. This is normally obtained via context from - * FormControl. - */ - margin?: 'dense'; - /** - * If `true`, the helper text should use required classes key. - */ - required?: boolean; - /** - * The props used for each slot inside the FormHelperText. - * @default {} - */ - slotProps?: { - root?: SlotComponentProps<'p', {}, FormHelperTextOwnerState>; - }; - /** - * The components used for each slot inside the FormHelperText. - * Either a string to use a HTML element or a component. - * @default {} - */ - slots?: FormHelperTextSlots; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps; - /** - * The variant to use. - * @default 'outlined' - */ - variant?: OverridableStringUnion<'outlined' | 'filled', FormHelperTextPropsVariantOverrides>; -} - -export interface FormHelperTextSlots { - /** - * The component that renders the root. - * @default 'p' - */ - root?: React.ElementType; -} - -export interface FormHelperTextTypeMap< - AdditionalProps = {}, - RootComponent extends React.ElementType = 'p', -> { - props: AdditionalProps & FormHelperTextOwnProps; - defaultComponent: RootComponent; -} - -export type FormHelperTextProps< - RootComponent extends React.ElementType = FormHelperTextTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps, RootComponent> & { - component?: React.ElementType; -}; - -export interface FormHelperTextOwnerState extends FormHelperTextProps { - contained: boolean; - size: NonNullable; - variant: NonNullable; -} diff --git a/packages/mui-material-next/src/FormHelperText/formHelperTextClasses.ts b/packages/mui-material-next/src/FormHelperText/formHelperTextClasses.ts deleted file mode 100644 index 4b9afb25838503..00000000000000 --- a/packages/mui-material-next/src/FormHelperText/formHelperTextClasses.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { - unstable_generateUtilityClasses as generateUtilityClasses, - unstable_generateUtilityClass as generateUtilityClass, -} from '@mui/utils'; - -export interface FormHelperTextClasses { - /** Styles applied to the root element. */ - root: string; - /** State class applied to the root element if `error={true}`. */ - error: string; - /** State class applied to the root element if `disabled={true}`. */ - disabled: string; - /** Styles applied to the root element if `size="small"`. */ - sizeSmall: string; - /** Styles applied to the root element if `variant="filled"` or `variant="outlined"`. */ - contained: string; - /** State class applied to the root element if `focused={true}`. */ - focused: string; - /** State class applied to the root element if `filled={true}`. */ - filled: string; - /** State class applied to the root element if `required={true}`. */ - required: string; -} - -export type FormHelperTextClassKey = keyof FormHelperTextClasses; - -export function getFormHelperTextUtilityClasses(slot: string): string { - return generateUtilityClass('MuiFormHelperText', slot); -} - -const formHelperTextClasses: FormHelperTextClasses = generateUtilityClasses('MuiFormHelperText', [ - 'root', - 'error', - 'disabled', - 'sizeSmall', - 'sizeMedium', - 'contained', - 'focused', - 'filled', - 'required', -]); - -export default formHelperTextClasses; diff --git a/packages/mui-material-next/src/FormHelperText/index.ts b/packages/mui-material-next/src/FormHelperText/index.ts deleted file mode 100644 index e2e5b0a06e3444..00000000000000 --- a/packages/mui-material-next/src/FormHelperText/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './FormHelperText'; - -export { default as formHelperTextClasses } from './formHelperTextClasses'; -export * from './formHelperTextClasses'; diff --git a/packages/mui-material-next/src/FormLabel/FormLabel.test.tsx b/packages/mui-material-next/src/FormLabel/FormLabel.test.tsx deleted file mode 100644 index 4abf9cdc3f5291..00000000000000 --- a/packages/mui-material-next/src/FormLabel/FormLabel.test.tsx +++ /dev/null @@ -1,226 +0,0 @@ -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { expect } from 'chai'; -import { act, createRenderer } from '@mui-internal/test-utils'; -import FormLabel, { formLabelClasses as classes } from '@mui/material-next/FormLabel'; -import FormControl, { useFormControl } from '@mui/material-next/FormControl'; -import { CssVarsProvider, extendTheme } from '@mui/material-next/styles'; -import describeConformance from '../../test/describeConformance'; - -describe('', () => { - let originalMatchmedia: typeof window.matchMedia; - - beforeEach(() => { - originalMatchmedia = window.matchMedia; - window.matchMedia = () => - ({ - addListener: () => {}, - removeListener: () => {}, - }) as unknown as MediaQueryList; - }); - - afterEach(() => { - window.matchMedia = originalMatchmedia; - }); - - const { render } = createRenderer(); - - describeConformance(, () => ({ - classes, - inheritComponent: 'label', - render, - refInstanceof: window.HTMLLabelElement, - testComponentPropWith: 'div', - muiName: 'MuiFormLabel', - testVariantProps: { color: 'secondary' }, - ThemeProvider: CssVarsProvider, - createTheme: extendTheme, - slots: { - root: { - expectedClassName: classes.root, - }, - }, - skip: ['componentsProp'], - })); - - describe('prop: required', () => { - it('should visually show an asterisk but not include it in the a11y tree', () => { - const { container } = render(name); - - expect(container.querySelector('label')).to.have.text('name\u2009*'); - expect(container.querySelectorAll(`.${classes.asterisk}`)).to.have.lengthOf(1); - expect(container.querySelectorAll(`.${classes.asterisk}`)[0]).toBeAriaHidden(); - }); - - it('should not show an asterisk by default', () => { - const { container } = render(name); - - expect(container.querySelector('label')).to.have.text('name'); - expect(container.querySelectorAll(`.${classes.asterisk}`)).to.have.lengthOf(0); - }); - }); - - describe('prop: error', () => { - it('should have an error class', () => { - const { container } = render(); - - expect(container.querySelectorAll(`.${classes.asterisk}`)).to.have.lengthOf(1); - expect(container.querySelector(`.${classes.asterisk}`)).to.have.class(classes.error); - expect(container.querySelectorAll(`.${classes.asterisk}`)[0]).toBeAriaHidden(); - expect(container.firstChild).to.have.class(classes.error); - }); - }); - - describe('with FormControl', () => { - describe('error', () => { - function Wrapper(props: { children?: React.ReactNode }) { - return ; - } - - it(`should have the error class`, () => { - const { container } = render(, { - wrapper: Wrapper, - }); - - expect(container.querySelector('label')).to.have.class(classes.error); - }); - - it('should be overridden by props', () => { - const { container, setProps } = render( - , - { - wrapper: Wrapper, - }, - ); - - expect(container.querySelector('label')).not.to.have.class(classes.error); - - setProps({ error: true }); - expect(container.querySelector('label')).to.have.class(classes.error); - }); - }); - - describe('focused', () => { - const FormController = React.forwardRef((_, ref) => { - const formControl = useFormControl(); - React.useImperativeHandle(ref, () => formControl, [formControl]); - return null; - }); - - it(`should have the focused class`, () => { - const formControlRef = React.createRef<{ onFocus: () => void }>(); - const { container } = render( - - - - , - ); - - expect(container.querySelector('label')).not.to.have.class(classes.focused); - - act(() => { - formControlRef.current?.onFocus(); - }); - expect(container.querySelector('label')).to.have.class(classes.focused); - }); - - it('should be overridden by props', () => { - const formControlRef = React.createRef<{ onFocus: () => void }>(); - function Wrapper({ children }: { children?: React.ReactNode }) { - return ( - - {children} - - - ); - } - Wrapper.propTypes = { children: PropTypes.node }; - const { container, setProps } = render(, { - wrapper: Wrapper, - }); - act(() => { - formControlRef.current?.onFocus(); - }); - - expect(container.querySelector('label')).to.have.class(classes.focused); - - setProps({ focused: false }); - expect(container.querySelector('label')).not.to.have.class(classes.focused); - - setProps({ focused: true }); - expect(container.querySelector('label')).to.have.class(classes.focused); - }); - }); - - describe('required', () => { - it('should show an asterisk', () => { - const { container } = render( - - name - , - ); - - expect(container).to.have.text('name\u2009*'); - }); - - it('should be overridden by props', () => { - const { container, setProps } = render(name, { - wrapper: (props) => , - }); - - expect(container).to.have.text('name'); - - setProps({ required: true }); - expect(container).to.have.text('name\u2009*'); - }); - }); - }); - - const theme = extendTheme({ - components: { - MuiFormLabel: { - styleOverrides: { - root: { - [`&.${classes.focused}`]: { - mixBlendMode: 'darken', - }, - [`&.${classes.error}`]: { - mixBlendMode: 'lighten', - }, - }, - }, - }, - }, - }); - - describe('prop: color', () => { - it('should have color secondary class', () => { - const { container } = render(); - expect(container.firstChild).to.have.class(classes.colorSecondary); - }); - - it('should have the focused class and style', () => { - const { container, getByTestId } = render( - - - , - ); - expect(container.querySelector(`.${classes.colorSecondary}`)).to.have.class(classes.focused); - expect(getByTestId('FormLabel')).toHaveComputedStyle({ - mixBlendMode: 'darken', - }); - }); - - it('should have the error class and style, even when focused', () => { - const { container, getByTestId } = render( - - - , - ); - expect(container.querySelector(`.${classes.colorSecondary}`)).to.have.class(classes.error); - expect(getByTestId('FormLabel')).toHaveComputedStyle({ - mixBlendMode: 'lighten', - }); - }); - }); -}); diff --git a/packages/mui-material-next/src/FormLabel/FormLabel.tsx b/packages/mui-material-next/src/FormLabel/FormLabel.tsx deleted file mode 100644 index 68e7048c2c4c13..00000000000000 --- a/packages/mui-material-next/src/FormLabel/FormLabel.tsx +++ /dev/null @@ -1,225 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import { unstable_composeClasses as composeClasses, useSlotProps } from '@mui/base'; -import { OverridableComponent } from '@mui/types'; -import { unstable_capitalize as capitalize } from '@mui/utils'; -import { useThemeProps, styled } from '../styles'; -import useFormControl from '../FormControl/useFormControl'; -import formLabelClasses, { getFormLabelUtilityClasses } from './formLabelClasses'; -import { FormLabelProps, FormLabelTypeMap, FormLabelOwnerState } from './FormLabel.types'; - -const useUtilityClasses = (ownerState: FormLabelOwnerState) => { - const { classes, color, focused, disabled, error, filled, required } = ownerState; - const slots = { - root: [ - 'root', - `color${capitalize(color)}`, - disabled && 'disabled', - error && 'error', - filled && 'filled', - focused && 'focused', - required && 'required', - ], - asterisk: ['asterisk', error && 'error'], - }; - - return composeClasses(slots, getFormLabelUtilityClasses, classes); -}; - -export const FormLabelRoot = styled('label', { - name: 'MuiFormLabel', - slot: 'Root', - overridesResolver: ({ ownerState }, styles) => { - return [ - styles.root, - ownerState.color === 'secondary' && styles.colorSecondary, - ownerState.filled && styles.filled, - ]; - }, -})<{ ownerState: FormLabelOwnerState }>(({ theme, ownerState }) => { - const { vars: tokens } = theme; - - const pxFontSize = tokens.sys.typescale.body.large.size; - - const letterSpacing = `${theme.sys.typescale.body.large.tracking / pxFontSize}rem`; - - return { - '--md-comp-form-label-color': tokens.sys.color.secondary, - '--md-comp-form-label-font-family': tokens.sys.typescale.body.large.family, - '--md-comp-form-label-font-size': theme.typography.pxToRem(pxFontSize), // the pxToRem should be moved to typescale in the future, - '--md-comp-form-label-font-weight': tokens.sys.typescale.body.large.weight, - '--md-comp-form-label-letter-spacing': letterSpacing, - '--md-comp-form-label-line-height': '1.5rem', - '--md-comp-form-label-disabled-color': tokens.sys.color.onSurface, - '--md-comp-form-label-disabled-opacity': 0.38, - '--md-comp-form-label-error-color': tokens.sys.color.error, - '--md-comp-form-label-focus-color': tokens.sys.color[ownerState.color], - color: 'var(--md-comp-form-label-color)', - fontFamily: 'var(--md-comp-form-label-font-family)', - fontSize: 'var(--md-comp-form-label-font-size)', - fontWeight: 'var(--md-comp-form-label-font-weight)', - lineHeight: 'var(--md-comp-form-label-line-height)', - padding: 0, - position: 'relative', - [`&.${formLabelClasses.focused}`]: { - color: 'var(--md-comp-form-label-focus-color)', - }, - [`&.${formLabelClasses.disabled}`]: { - color: - 'color-mix(in srgb, var(--md-comp-form-label-disabled-color) calc(var(--md-comp-form-label-disabled-opacity) * 100%), transparent)', - }, - [`&.${formLabelClasses.error}`]: { - color: 'var(--md-comp-form-label-error-color)', - }, - }; -}); - -const AsteriskComponent = styled('span', { - name: 'MuiFormLabel', - slot: 'Asterisk', - overridesResolver: (_props, styles) => styles.asterisk, -})<{ ownerState: FormLabelOwnerState }>(() => ({ - [`&.${formLabelClasses.error}`]: { - color: 'var(--md-comp-form-label-error-color)', - }, -})); - -const FormLabel = React.forwardRef(function FormLabel< - RootComponentType extends React.ElementType = FormLabelTypeMap['defaultComponent'], ->(inProps: FormLabelProps, forwardedRef: React.ForwardedRef) { - const props = useThemeProps({ props: inProps, name: 'MuiFormLabel' }); - const { - children, - color: colorProp = 'primary', - component = 'label', - disabled: disabledProp, - error: errorProp, - filled: filledProp, - focused: focusedProp, - required: requiredProp, - slots = {}, - slotProps = {}, - ...other - } = props; - - const muiFormControl = useFormControl(); - - const required = requiredProp ?? muiFormControl?.required; - - const ownerState: FormLabelOwnerState = { - ...props, - color: muiFormControl?.color ?? colorProp, - component, - disabled: muiFormControl?.disabled ?? disabledProp, - error: errorProp ?? muiFormControl?.error, - filled: muiFormControl?.filled ?? filledProp, - focused: focusedProp ?? muiFormControl?.focused, - required, - }; - - const classes = useUtilityClasses(ownerState); - - const RootSlot = slots?.root ?? FormLabelRoot; - - const rootProps = useSlotProps({ - elementType: RootSlot, - externalSlotProps: slotProps.root, - externalForwardedProps: other, - additionalProps: { - as: component, - ref: forwardedRef, - }, - ownerState, - className: [classes.root], - }); - - return ( - - {children} - {required && ( - -  {'*'} - - )} - - ); -}) as OverridableComponent; - -FormLabel.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * The content of the component. - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - */ - color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['error', 'info', 'primary', 'secondary', 'tertiary', 'success', 'warning']), - PropTypes.string, - ]), - /** - * The component used for the root node. - * Either a string to use a HTML element or a component. - */ - component: PropTypes.elementType, - /** - * If `true`, the label should be displayed in a disabled state. - */ - disabled: PropTypes.bool, - /** - * If `true`, the label is displayed in an error state. - */ - error: PropTypes.bool, - /** - * If `true`, the label should use filled classes key. - */ - filled: PropTypes.bool, - /** - * If `true`, the input of this label is focused (used by `FormGroup` components). - */ - focused: PropTypes.bool, - /** - * If `true`, the label will indicate that the `input` is required. - */ - required: PropTypes.bool, - /** - * The props used for each slot inside the FormLabel. - * @default {} - */ - slotProps: PropTypes.shape({ - root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), - }), - /** - * The components used for each slot inside the FormLabel. - * Either a string to use a HTML element or a component. - * @default {} - */ - slots: PropTypes.shape({ - root: PropTypes.elementType, - }), - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), - PropTypes.func, - PropTypes.object, - ]), -} as any; - -export default FormLabel; diff --git a/packages/mui-material-next/src/FormLabel/FormLabel.types.ts b/packages/mui-material-next/src/FormLabel/FormLabel.types.ts deleted file mode 100644 index 13f6df5f685f68..00000000000000 --- a/packages/mui-material-next/src/FormLabel/FormLabel.types.ts +++ /dev/null @@ -1,101 +0,0 @@ -import * as React from 'react'; -import { SlotComponentProps } from '@mui/base'; -import { SxProps } from '@mui/system'; -import { OverridableStringUnion, OverrideProps, OverridableTypeMap } from '@mui/types'; -import { Theme } from '../styles'; -import { FormLabelClasses } from './formLabelClasses'; - -export interface FormLabelPropsColorOverrides {} - -export interface FormLabelOwnProps { - /** - * The content of the component. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial; - /** - * @ignore - */ - className?: string; - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - */ - color?: OverridableStringUnion< - 'primary' | 'secondary' | 'tertiary' | 'error' | 'info' | 'warning' | 'success', - FormLabelPropsColorOverrides - >; - /** - * If `true`, the label should be displayed in a disabled state. - */ - disabled?: boolean; - /** - * If `true`, the label is displayed in an error state. - */ - error?: boolean; - /** - * If `true`, the label should use filled classes key. - */ - filled?: boolean; - /** - * If `true`, the input of this label is focused (used by `FormGroup` components). - */ - focused?: boolean; - /** - * If `true`, the label will indicate that the `input` is required. - */ - required?: boolean; - /** - * The props used for each slot inside the FormLabel. - * @default {} - */ - slotProps?: { - root?: SlotComponentProps<'label', {}, FormLabelOwnerState>; - }; - /** - * The components used for each slot inside the FormLabel. - * Either a string to use a HTML element or a component. - * @default {} - */ - slots?: FormLabelSlots; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps; -} - -export interface FormLabelSlots { - /** - * The component that renders the root. - * @default 'span' - */ - root?: React.ElementType; -} - -export interface FormLabelTypeMap< - AdditionalProps = {}, - RootComponent extends React.ElementType = 'label', -> { - props: AdditionalProps & FormLabelOwnProps; - defaultComponent: RootComponent; -} - -export interface ExtendFormLabelTypeMap { - props: TypeMap['props'] & Pick; - defaultComponent: TypeMap['defaultComponent']; -} - -export type FormLabelProps< - RootComponent extends React.ElementType = FormLabelTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps, RootComponent> & { - component?: React.ElementType; -}; - -export interface FormLabelOwnerState extends FormLabelProps { - color: NonNullable; -} diff --git a/packages/mui-material-next/src/FormLabel/formLabelClasses.ts b/packages/mui-material-next/src/FormLabel/formLabelClasses.ts deleted file mode 100644 index b1b69e98466e86..00000000000000 --- a/packages/mui-material-next/src/FormLabel/formLabelClasses.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { - unstable_generateUtilityClass as generateUtilityClass, - unstable_generateUtilityClasses as generateUtilityClasses, -} from '@mui/utils'; - -export interface FormLabelClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if the color is secondary. */ - colorSecondary: string; - /** State class applied to the root element if `focused={true}`. */ - focused: string; - /** State class applied to the root element if `disabled={true}`. */ - disabled: string; - /** State class applied to the root element if `error={true}`. */ - error: string; - /** State class applied to the root element if `filled={true}`. */ - filled: string; - /** State class applied to the root element if `required={true}`. */ - required: string; - /** Styles applied to the asterisk element. */ - asterisk: string; -} - -export type FormLabelClassKey = keyof FormLabelClasses; - -export function getFormLabelUtilityClasses(slot: string): string { - return generateUtilityClass('MuiFormLabel', slot); -} - -const formLabelClasses: FormLabelClasses = generateUtilityClasses('MuiFormLabel', [ - 'root', - 'colorSecondary', - 'focused', - 'disabled', - 'error', - 'filled', - 'required', - 'asterisk', -]); - -export default formLabelClasses; diff --git a/packages/mui-material-next/src/FormLabel/index.ts b/packages/mui-material-next/src/FormLabel/index.ts deleted file mode 100644 index db05e838ce6da7..00000000000000 --- a/packages/mui-material-next/src/FormLabel/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -'use client'; -export { default } from './FormLabel'; -export * from './FormLabel'; - -export { default as formLabelClasses } from './formLabelClasses'; -export * from './formLabelClasses'; diff --git a/packages/mui-material-next/src/IconButton/IconButton.d.ts b/packages/mui-material-next/src/IconButton/IconButton.d.ts deleted file mode 100644 index d81b2dc621da6d..00000000000000 --- a/packages/mui-material-next/src/IconButton/IconButton.d.ts +++ /dev/null @@ -1,91 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { OverridableStringUnion, OverrideProps } from '@mui/types'; -import { Theme } from '@mui/material'; -import { ExtendButtonBase, ExtendButtonBaseTypeMap } from '../ButtonBase/ButtonBase.types'; -import { IconButtonClasses } from './iconButtonClasses'; - -export interface IconButtonPropsColorOverrides {} - -export interface IconButtonPropsSizeOverrides {} - -export interface IconButtonOwnProps { - /** - * The icon to display. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial; - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'default' - */ - color?: OverridableStringUnion< - 'inherit' | 'default' | 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning', - IconButtonPropsColorOverrides - >; - /** - * If `true`, the component is disabled. - * @default false - */ - disabled?: boolean; - /** - * If `true`, the keyboard focus ripple is disabled. - * @default false - */ - disableFocusRipple?: boolean; - /** - * If given, uses a negative margin to counteract the padding on one - * side (this is often helpful for aligning the left or right - * side of the icon with content above or below, without ruining the border - * size and shape). - * @default false - */ - edge?: 'start' | 'end' | false; - /** - * The size of the component. - * `small` is equivalent to the dense button styling. - * @default 'medium' - */ - size?: OverridableStringUnion<'small' | 'medium' | 'large', IconButtonPropsSizeOverrides>; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps; -} - -export type IconButtonTypeMap< - AdditionalProps = {}, - RootComponent extends React.ElementType = 'button', -> = ExtendButtonBaseTypeMap<{ - props: AdditionalProps & IconButtonOwnProps; - defaultComponent: RootComponent; -}>; - -/** - * Refer to the [Icons](https://mui.com/material-ui/icons/) section of the documentation - * regarding the available icon options. - * - * Demos: - * - * - [Button](https://mui.com/material-ui/react-button/) - * - * API: - * - * - [IconButton API](https://mui.com/material-ui/api/icon-button/) - * - inherits [ButtonBase API](https://mui.com/material-ui/api/button-base/) - */ -declare const IconButton: ExtendButtonBase; - -export type IconButtonProps< - RootComponent extends React.ElementType = IconButtonTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps, RootComponent> & { - component?: React.ElementType; -}; - -export default IconButton; diff --git a/packages/mui-material-next/src/IconButton/IconButton.js b/packages/mui-material-next/src/IconButton/IconButton.js deleted file mode 100644 index b8fafc50b3f520..00000000000000 --- a/packages/mui-material-next/src/IconButton/IconButton.js +++ /dev/null @@ -1,251 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { chainPropTypes, unstable_capitalize as capitalize } from '@mui/utils'; -import { unstable_composeClasses as composeClasses } from '@mui/base'; -import { alpha } from '@mui/system'; -import ButtonBase from '@mui/material/ButtonBase'; -import styled from '../styles/styled'; -import useThemeProps from '../styles/useThemeProps'; -import iconButtonClasses, { getIconButtonUtilityClass } from './iconButtonClasses'; - -const useUtilityClasses = (ownerState) => { - const { classes, disabled, color, edge, size } = ownerState; - - const slots = { - root: [ - 'root', - disabled && 'disabled', - color !== 'default' && `color${capitalize(color)}`, - edge && `edge${capitalize(edge)}`, - `size${capitalize(size)}`, - ], - }; - - return composeClasses(slots, getIconButtonUtilityClass, classes); -}; - -const IconButtonRoot = styled(ButtonBase, { - name: 'MuiIconButton', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [ - styles.root, - ownerState.color !== 'default' && styles[`color${capitalize(ownerState.color)}`], - ownerState.edge && styles[`edge${capitalize(ownerState.edge)}`], - styles[`size${capitalize(ownerState.size)}`], - ]; - }, -})( - ({ theme, ownerState }) => ({ - textAlign: 'center', - flex: '0 0 auto', - fontSize: theme.typography.pxToRem(24), - padding: 8, - borderRadius: '50%', - overflow: 'visible', // Explicitly set the default value to solve a bug on IE11. - color: (theme.vars || theme).palette.action.active, - transition: theme.transitions.create('background-color', { - duration: theme.transitions.duration.shortest, - }), - ...(!ownerState.disableRipple && { - '&:hover': { - backgroundColor: theme.vars - ? `rgba(${theme.vars.palette.action.activeChannel} / ${theme.vars.palette.action.hoverOpacity})` - : alpha(theme.palette.action.active, theme.palette.action.hoverOpacity), - // Reset on touch devices, it doesn't add specificity - '@media (hover: none)': { - backgroundColor: 'transparent', - }, - }, - }), - ...(ownerState.edge === 'start' && { - marginLeft: ownerState.size === 'small' ? -3 : -12, - }), - ...(ownerState.edge === 'end' && { - marginRight: ownerState.size === 'small' ? -3 : -12, - }), - }), - ({ theme, ownerState }) => { - const palette = (theme.vars || theme).palette?.[ownerState.color]; - return { - ...(ownerState.color === 'inherit' && { - color: 'inherit', - }), - ...(ownerState.color !== 'inherit' && - ownerState.color !== 'default' && { - color: palette?.main, - ...(!ownerState.disableRipple && { - '&:hover': { - ...(palette && { - backgroundColor: theme.vars - ? `rgba(${palette.mainChannel} / ${theme.vars.palette.action.hoverOpacity})` - : alpha(palette.main, theme.palette.action.hoverOpacity), - }), - // Reset on touch devices, it doesn't add specificity - '@media (hover: none)': { - backgroundColor: 'transparent', - }, - }, - }), - }), - ...(ownerState.size === 'small' && { - padding: 5, - fontSize: theme.typography.pxToRem(18), - }), - ...(ownerState.size === 'large' && { - padding: 12, - fontSize: theme.typography.pxToRem(28), - }), - [`&.${iconButtonClasses.disabled}`]: { - backgroundColor: 'transparent', - color: (theme.vars || theme).palette.action.disabled, - }, - }; - }, -); - -/** - * Refer to the [Icons](/material-ui/icons/) section of the documentation - * regarding the available icon options. - */ -const IconButton = React.forwardRef(function IconButton(inProps, ref) { - const props = useThemeProps({ props: inProps, name: 'MuiIconButton' }); - const { - edge = false, - children, - className, - color = 'default', - disabled = false, - disableFocusRipple = false, - size = 'medium', - ...other - } = props; - - const ownerState = { - ...props, - edge, - color, - disabled, - disableFocusRipple, - size, - }; - - const classes = useUtilityClasses(ownerState); - - return ( - - {children} - - ); -}); - -IconButton.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * The icon to display. - */ - children: chainPropTypes(PropTypes.node, (props) => { - const found = React.Children.toArray(props.children).some( - (child) => React.isValidElement(child) && child.props.onClick, - ); - - if (found) { - return new Error( - [ - 'MUI: You are providing an onClick event listener to a child of a button element.', - 'Prefer applying it to the IconButton directly.', - 'This guarantees that the whole - -
- ); - } - const { container, getByRole } = render(); - const openSelect = container.querySelector('#open-select'); - act(() => { - openSelect.focus(); - }); - fireEvent.click(openSelect); - - const option = getByRole('option'); - expect(option).toHaveFocus(); - fireEvent.click(option); - - expect(container.querySelectorAll(classes.focused).length).to.equal(0); - expect(openSelect).toHaveFocus(); - }); - - it('should allow to control closing by passing onClose props', () => { - function ControlledWrapper() { - const [open, setOpen] = React.useState(false); - - return ( - - ); - } - const { getByRole, queryByRole } = render(); - - fireEvent.mouseDown(getByRole('combobox')); - expect(getByRole('listbox')).not.to.equal(null); - - act(() => { - getByRole('option').click(); - }); - // react-transition-group uses one extra commit for exit to completely remove - // it from the DOM. but it's at least immediately inaccessible. - // It's desired that this fails one day. The additional tick required to remove - // this from the DOM is not a feature - expect(getByRole('listbox', { hidden: true })).toBeInaccessible(); - clock.tick(0); - - expect(queryByRole('listbox', { hidden: true })).to.equal(null); - }); - - it('should be open when initially true', () => { - const { getByRole } = render( - , - ); - - expect(getByRole('listbox')).not.to.equal(null); - }); - - it('open only with the left mouse button click', () => { - // Test for https://github.com/mui/material-ui/issues/19250#issuecomment-578620934 - // Right/middle mouse click shouldn't open the Select - const { getByRole, queryByRole } = render( - , - ); - - const trigger = getByRole('combobox'); - - // If clicked by the right/middle mouse button, no options list should be opened - fireEvent.mouseDown(trigger, { button: 1 }); - expect(queryByRole('listbox')).to.equal(null); - - fireEvent.mouseDown(trigger, { button: 2 }); - expect(queryByRole('listbox')).to.equal(null); - }); - }); - - describe('prop: autoWidth', () => { - it('should take the trigger parent element width into account by default', () => { - const { container, getByRole, getByTestId } = render( - , - ); - const parentEl = container.querySelector('.MuiInputBase-root'); - const button = getByRole('combobox'); - stub(parentEl, 'clientWidth').get(() => 14); - - fireEvent.mouseDown(button); - expect(getByTestId('paper').style).to.have.property('minWidth', '14px'); - }); - - it('should not take the trigger parent element width into account when autoWidth is true', () => { - const { container, getByRole, getByTestId } = render( - , - ); - const parentEl = container.querySelector('.MuiInputBase-root'); - const button = getByRole('combobox'); - stub(parentEl, 'clientWidth').get(() => 14); - - fireEvent.mouseDown(button); - expect(getByTestId('paper').style).to.have.property('minWidth', ''); - }); - }); - - describe('prop: multiple', () => { - it('should serialize multiple select value', () => { - const { container, getAllByRole } = render( - , - ); - const options = getAllByRole('option'); - - expect(container.querySelector('input')).to.have.property('value', '10,30'); - expect(options[0]).to.have.attribute('aria-selected', 'true'); - expect(options[1]).not.to.have.attribute('aria-selected', 'true'); - expect(options[2]).to.have.attribute('aria-selected', 'true'); - }); - - it('should have aria-multiselectable=true when multiple is true', () => { - const { getByRole } = render( - , - ); - - fireEvent.mouseDown(getByRole('combobox')); - - expect(getByRole('listbox')).to.have.attribute('aria-multiselectable', 'true'); - }); - - it('should serialize multiple select display value', () => { - const { getByRole } = render( - , - ); - - expect(getByRole('combobox')).to.have.text('Ten, Twenty, Thirty'); - }); - - it('should not throw an error if `value` is an empty array', () => { - expect(() => { - render(); - }).not.to.throw(); - }); - - it("selects value based on their stringified equality when they're not objects", () => { - const { getAllByRole } = render( - , - ); - const options = getAllByRole('option'); - - expect(options[0]).to.have.attribute('aria-selected', 'true'); - expect(options[1]).to.have.attribute('aria-selected', 'true'); - expect(options[2]).not.to.have.attribute('aria-selected', 'true'); - }); - - it("selects values based on strict equality if they're objects", () => { - const obj1 = { id: 1 }; - const obj2 = { id: 2 }; - const obj3 = { id: 3 }; - const { getAllByRole } = render( - , - ); - const options = getAllByRole('option'); - - expect(options[0]).to.have.attribute('aria-selected', 'true'); - expect(options[1]).not.to.have.attribute('aria-selected', 'true'); - expect(options[2]).to.have.attribute('aria-selected', 'true'); - }); - - describe('errors', () => { - it('should throw if non array', function test() { - // TODO is this fixed? - if (!/jsdom/.test(window.navigator.userAgent)) { - // can't catch render errors in the browser for unknown reason - // tried try-catch + error boundary + window onError preventDefault - this.skip(); - } - - const errorRef = React.createRef(); - expect(() => { - render( - - - , - ); - }).toErrorDev([ - 'MUI: The `value` prop must be an array', - // React 18 Strict Effects run mount effects twice - React.version.startsWith('18') && 'MUI: The `value` prop must be an array', - 'The above error occurred in the component', - ]); - const { - current: { errors }, - } = errorRef; - expect(errors).to.have.length(1); - expect(errors[0].toString()).to.include('MUI: The `value` prop must be an array'); - }); - }); - - describe('prop: onChange', () => { - it('should call onChange when clicking an item', () => { - function ControlledSelectInput(props) { - const { onChange } = props; - const [values, clickedValue] = React.useReducer((currentValues, valueClicked) => { - if (currentValues.indexOf(valueClicked) === -1) { - return currentValues.concat(valueClicked); - } - return currentValues.filter((value) => { - return value !== valueClicked; - }); - }, []); - - const handleChange = (event) => { - onChange(event); - clickedValue(event.target.value); - }; - - return ( - - ); - } - const onChange = stub().callsFake((event) => { - return { - name: event.target.name, - value: event.target.value, - }; - }); - const { getByRole, getAllByRole } = render(); - - fireEvent.mouseDown(getByRole('combobox')); - const options = getAllByRole('option'); - fireEvent.click(options[2]); - - expect(onChange.callCount).to.equal(1); - expect(onChange.firstCall.returnValue).to.deep.equal({ name: 'age', value: [30] }); - - act(() => { - options[0].click(); - }); - - expect(onChange.callCount).to.equal(2); - expect(onChange.secondCall.returnValue).to.deep.equal({ name: 'age', value: [30, 10] }); - }); - }); - - it('should apply multiple class to `select` slot', () => { - const { container } = render( - , - ); - - expect(container.querySelector(`.${classes.select}`)).to.have.class(classes.multiple); - }); - - it('should be able to override `multiple` rule name in `select` slot', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - - const selectStyle = { - marginLeft: '10px', - marginTop: '10px', - }; - - const multipleStyle = { - marginTop: '14px', - }; - - const theme = createTheme({ - components: { - MuiSelect: { - styleOverrides: { - select: selectStyle, - multiple: multipleStyle, - }, - }, - }, - }); - - const { container } = render( - - - , - ); - - const combinedStyle = { ...selectStyle, ...multipleStyle }; - - expect(container.getElementsByClassName(classes.select)[0]).to.toHaveComputedStyle( - combinedStyle, - ); - }); - }); - - describe('prop: autoFocus', () => { - it('should focus select after Select did mount', () => { - const { getByRole } = render(); - - expect(ref.current.node).to.have.tagName('input'); - - setProps({ - value: '', - }); - expect(ref.current.node).to.have.tagName('input'); - }); - - describe('prop: inputRef', () => { - it('should be able to return the input node via a ref object', () => { - const ref = React.createRef(); - render( is still used. - it('should be able focus the trigger imperatively', () => { - const ref = React.createRef(); - const { getByRole } = render(); - - expect(getByRole('combobox')).not.to.have.attribute('id'); - }); - - it('should have select-`name` id when name is provided', () => { - const { getByRole } = render(', () => { - const { container } = render( - , - ); - - expect(getByRole('combobox', { name: 'A select' })).to.have.property('tagName', 'SELECT'); - }); - }); - - it('prevents the default when releasing Space on the children', () => { - const keyUpSpy = spy(); - render( - , - ); - - fireEvent.keyUp(screen.getAllByRole('option')[0], { key: ' ' }); - - expect(keyUpSpy.callCount).to.equal(1); - expect(keyUpSpy.firstCall.args[0]).to.have.property('defaultPrevented', true); - }); - - it('should pass onClick prop to MenuItem', () => { - const onClick = spy(); - const { getAllByRole } = render( - , - ); - - const options = getAllByRole('option'); - fireEvent.click(options[0]); - - expect(onClick.callCount).to.equal(1); - }); - - // https://github.com/testing-library/react-testing-library/issues/322 - // https://twitter.com/devongovett/status/1248306411508916224 - it('should handle the browser autofill event and simple testing-library API', () => { - const onChangeHandler = spy(); - const { container, getByRole } = render( - , - ); - fireEvent.change(container.querySelector('input[name="country"]'), { - target: { - value: 'france', - }, - }); - - expect(onChangeHandler.calledOnce).to.equal(true); - expect(getByRole('combobox')).to.have.text('France'); - }); - - it('should support native form validation', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - // see https://github.com/jsdom/jsdom/issues/123 - this.skip(); - } - - const handleSubmit = spy((event) => { - // avoid karma reload. - event.preventDefault(); - }); - function Form(props) { - return ( -
- -
} - open - onBlur={handleBlur} - onFocus={handleFocus} - onMouseEnter={handleMouseEnter} - onMouseLeave={handleMouseLeave} - onClose={handleClose} - message="message" - autoHideDuration={autoHideDuration} - />, - ); - - expect(handleClose.callCount).to.equal(0); - - clock.tick(autoHideDuration / 2); - userInteraction.enter(container.querySelector('div')); - - if (userInteraction.type === 'keyboard') { - expect(handleFocus.callCount).to.equal(1); - } else { - expect(handleMouseEnter.callCount).to.equal(1); - } - - clock.tick(autoHideDuration / 2); - userInteraction.leave(container.querySelector('div')); - - if (userInteraction.type === 'keyboard') { - expect(handleBlur.callCount).to.equal(1); - } else { - expect(handleMouseLeave.callCount).to.equal(1); - } - expect(handleClose.callCount).to.equal(0); - - clock.tick(2e3); - - expect(handleClose.callCount).to.equal(1); - expect(handleClose.args[0]).to.deep.equal([null, 'timeout']); - }); - - it('should not call onClose with not timeout after user interaction', () => { - const handleClose = spy(); - const autoHideDuration = 2e3; - const resumeHideDuration = 3e3; - - const { container } = render( - undo} - open - onClose={handleClose} - message="message" - autoHideDuration={autoHideDuration} - resumeHideDuration={resumeHideDuration} - />, - ); - - expect(handleClose.callCount).to.equal(0); - - clock.tick(autoHideDuration / 2); - userInteraction.enter(container.querySelector('div')); - clock.tick(autoHideDuration / 2); - userInteraction.leave(container.querySelector('div')); - - expect(handleClose.callCount).to.equal(0); - - clock.tick(2e3); - - expect(handleClose.callCount).to.equal(0); - }); - - it('should call onClose when timer done after user interaction', () => { - const handleClose = spy(); - const autoHideDuration = 2e3; - const resumeHideDuration = 3e3; - - const { container } = render( - undo} - open - onClose={handleClose} - message="message" - autoHideDuration={autoHideDuration} - resumeHideDuration={resumeHideDuration} - />, - ); - - expect(handleClose.callCount).to.equal(0); - - clock.tick(autoHideDuration / 2); - userInteraction.enter(container.querySelector('div')); - clock.tick(autoHideDuration / 2); - userInteraction.leave(container.querySelector('div')); - - expect(handleClose.callCount).to.equal(0); - - clock.tick(resumeHideDuration); - - expect(handleClose.callCount).to.equal(1); - expect(handleClose.args[0]).to.deep.equal([null, 'timeout']); - }); - - it('should call onClose immediately after user interaction when 0', () => { - const handleClose = spy(); - const autoHideDuration = 6e3; - const resumeHideDuration = 0; - const { setProps, container } = render( - undo} - open - onClose={handleClose} - message="message" - autoHideDuration={autoHideDuration} - resumeHideDuration={resumeHideDuration} - />, - ); - - setProps({ open: true }); - - expect(handleClose.callCount).to.equal(0); - - userInteraction.enter(container.querySelector('div')); - clock.tick(100); - userInteraction.leave(container.querySelector('div')); - clock.tick(resumeHideDuration); - - expect(handleClose.callCount).to.equal(1); - expect(handleClose.args[0]).to.deep.equal([null, 'timeout']); - }); - }); - }); - - describe('prop: disableWindowBlurListener', () => { - it('should pause auto hide when not disabled and window lost focus', () => { - const handleClose = spy(); - const autoHideDuration = 2e3; - render( - , - ); - - act(() => { - const bEvent = new window.Event('blur', { - bubbles: false, - cancelable: false, - }); - window.dispatchEvent(bEvent); - }); - - expect(handleClose.callCount).to.equal(0); - - clock.tick(autoHideDuration); - - expect(handleClose.callCount).to.equal(0); - - act(() => { - const fEvent = new window.Event('focus', { - bubbles: false, - cancelable: false, - }); - window.dispatchEvent(fEvent); - }); - - expect(handleClose.callCount).to.equal(0); - - clock.tick(autoHideDuration); - - expect(handleClose.callCount).to.equal(1); - expect(handleClose.args[0]).to.deep.equal([null, 'timeout']); - }); - - it('should not pause auto hide when disabled and window lost focus', () => { - const handleClose = spy(); - const autoHideDuration = 2e3; - render( - , - ); - - act(() => { - const event = new window.Event('blur', { bubbles: false, cancelable: false }); - window.dispatchEvent(event); - }); - - expect(handleClose.callCount).to.equal(0); - - clock.tick(autoHideDuration); - - expect(handleClose.callCount).to.equal(1); - expect(handleClose.args[0]).to.deep.equal([null, 'timeout']); - }); - }); - - describe('prop: open', () => { - it('should not render anything when closed', () => { - const { container } = render(); - expect(container).to.have.text(''); - }); - - it('should be able show it after mounted', () => { - const { container, setProps } = render(); - expect(container).to.have.text(''); - setProps({ open: true }); - expect(container).to.have.text('Hello, World!'); - }); - }); - - describe('prop: children', () => { - it('should render the children', () => { - const nodeRef = React.createRef(); - const children =
; - const { container } = render({children}); - expect(container).to.contain(nodeRef.current); - }); - }); - - describe('prop: TransitionComponent', () => { - it('should use a Grow by default', () => { - const childRef = React.createRef(); - render( - -
- , - ); - expect(childRef.current.style.transform).to.contain('scale'); - }); - - it('accepts a different component that handles the transition', () => { - const transitionRef = React.createRef(); - function Transition() { - return
; - } - const { container } = render(); - expect(container).to.contain(transitionRef.current); - }); - }); - - describe('prop: transitionDuration', () => { - it('should render the default theme values by default', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - - const theme = createTheme(); - const enteringScreenDurationInSeconds = theme.transitions.duration.enteringScreen / 1000; - const { getByTestId } = render( - -
Foo
-
, - ); - - const child = getByTestId('child'); - expect(child).toHaveComputedStyle({ - transitionDuration: `${enteringScreenDurationInSeconds}s, 0.15s`, - }); - }); - - it('should render the custom theme values', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - - const theme = createTheme({ - transitions: { - duration: { - enteringScreen: 1, - }, - }, - }); - - const { getByTestId } = render( - - -
Foo
-
-
, - ); - - const child = getByTestId('child'); - expect(child).toHaveComputedStyle({ transitionDuration: '0.001s, 0.001s' }); - }); - - it('should render the values provided via prop', function test() { - if (/jsdom/.test(window.navigator.userAgent)) { - this.skip(); - } - - const { getByTestId } = render( - -
Foo
-
, - ); - - const child = getByTestId('child'); - expect(child).toHaveComputedStyle({ transitionDuration: '0.001s, 0.001s' }); - }); - }); -}); diff --git a/packages/mui-material-next/src/Snackbar/index.d.ts b/packages/mui-material-next/src/Snackbar/index.d.ts deleted file mode 100644 index e55c472b4faa67..00000000000000 --- a/packages/mui-material-next/src/Snackbar/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default } from './Snackbar'; -export * from './Snackbar'; - -export { default as snackbarClasses } from './snackbarClasses'; -export * from './snackbarClasses'; diff --git a/packages/mui-material-next/src/Snackbar/index.js b/packages/mui-material-next/src/Snackbar/index.js deleted file mode 100644 index c21dbfa25ec91d..00000000000000 --- a/packages/mui-material-next/src/Snackbar/index.js +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './Snackbar'; - -export { default as snackbarClasses } from './snackbarClasses'; -export * from './snackbarClasses'; diff --git a/packages/mui-material-next/src/Snackbar/snackbarClasses.ts b/packages/mui-material-next/src/Snackbar/snackbarClasses.ts deleted file mode 100644 index 201882ecc5539c..00000000000000 --- a/packages/mui-material-next/src/Snackbar/snackbarClasses.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { - unstable_generateUtilityClasses as generateUtilityClasses, - unstable_generateUtilityClass as generateUtilityClass, -} from '@mui/utils'; - -export interface SnackbarClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if `anchorOrigin={{ 'top', 'center' }}`. */ - anchorOriginTopCenter: string; - /** Styles applied to the root element if `anchorOrigin={{ 'bottom', 'center' }}`. */ - anchorOriginBottomCenter: string; - /** Styles applied to the root element if `anchorOrigin={{ 'top', 'right' }}`. */ - anchorOriginTopRight: string; - /** Styles applied to the root element if `anchorOrigin={{ 'bottom', 'right' }}`. */ - anchorOriginBottomRight: string; - /** Styles applied to the root element if `anchorOrigin={{ 'top', 'left' }}`. */ - anchorOriginTopLeft: string; - /** Styles applied to the root element if `anchorOrigin={{ 'bottom', 'left' }}`. */ - anchorOriginBottomLeft: string; -} - -export type SnackbarClassKey = keyof SnackbarClasses; - -export function getSnackbarUtilityClass(slot: string): string { - return generateUtilityClass('MuiSnackbar', slot); -} - -const snackbarClasses: SnackbarClasses = generateUtilityClasses('MuiSnackbar', [ - 'root', - 'anchorOriginTopCenter', - 'anchorOriginBottomCenter', - 'anchorOriginTopRight', - 'anchorOriginBottomRight', - 'anchorOriginTopLeft', - 'anchorOriginBottomLeft', -]); - -export default snackbarClasses; diff --git a/packages/mui-material-next/src/SnackbarContent/SnackbarContent.d.ts b/packages/mui-material-next/src/SnackbarContent/SnackbarContent.d.ts deleted file mode 100644 index e75358e1e5b3af..00000000000000 --- a/packages/mui-material-next/src/SnackbarContent/SnackbarContent.d.ts +++ /dev/null @@ -1,41 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { InternalStandardProps as StandardProps, PaperProps, Theme } from '@mui/material'; -import { SnackbarContentClasses } from './snackbarContentClasses'; - -export interface SnackbarContentProps extends StandardProps { - /** - * The action to display. It renders after the message, at the end of the snackbar. - */ - action?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial; - /** - * The message to display. - */ - message?: React.ReactNode; - /** - * The ARIA role attribute of the element. - * @default 'alert' - */ - role?: PaperProps['role']; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps; -} - -/** - * - * Demos: - * - * - [Snackbar](https://mui.com/material-ui/react-snackbar/) - * - * API: - * - * - [SnackbarContent API](https://mui.com/material-ui/api/snackbar-content/) - * - inherits [Paper API](https://mui.com/material-ui/api/paper/) - */ -export default function SnackbarContent(props: SnackbarContentProps): JSX.Element; diff --git a/packages/mui-material-next/src/SnackbarContent/SnackbarContent.js b/packages/mui-material-next/src/SnackbarContent/SnackbarContent.js deleted file mode 100644 index 53cdccbac54284..00000000000000 --- a/packages/mui-material-next/src/SnackbarContent/SnackbarContent.js +++ /dev/null @@ -1,135 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import { emphasize } from '@mui/system'; -import styled from '@mui/material/styles/styled'; -import useThemeProps from '@mui/material/styles/useThemeProps'; -import Paper from '@mui/material/Paper'; -import { getSnackbarContentUtilityClass } from './snackbarContentClasses'; - -const useUtilityClasses = (ownerState) => { - const { classes } = ownerState; - - const slots = { - root: ['root'], - action: ['action'], - message: ['message'], - }; - - return composeClasses(slots, getSnackbarContentUtilityClass, classes); -}; - -const SnackbarContentRoot = styled(Paper, { - name: 'MuiSnackbarContent', - slot: 'Root', - overridesResolver: (props, styles) => styles.root, -})(({ theme }) => { - const emphasis = theme.palette.mode === 'light' ? 0.8 : 0.98; - const backgroundColor = emphasize(theme.palette.background.default, emphasis); - - return { - ...theme.typography.body2, - color: theme.vars - ? theme.vars.palette.SnackbarContent.color - : theme.palette.getContrastText(backgroundColor), - backgroundColor: theme.vars ? theme.vars.palette.SnackbarContent.bg : backgroundColor, - display: 'flex', - alignItems: 'center', - flexWrap: 'wrap', - padding: '6px 16px', - borderRadius: (theme.vars || theme).shape.borderRadius, - flexGrow: 1, - [theme.breakpoints.up('sm')]: { - flexGrow: 'initial', - minWidth: 288, - }, - }; -}); - -const SnackbarContentMessage = styled('div', { - name: 'MuiSnackbarContent', - slot: 'Message', - overridesResolver: (props, styles) => styles.message, -})({ - padding: '8px 0', -}); - -const SnackbarContentAction = styled('div', { - name: 'MuiSnackbarContent', - slot: 'Action', - overridesResolver: (props, styles) => styles.action, -})({ - display: 'flex', - alignItems: 'center', - marginLeft: 'auto', - paddingLeft: 16, - marginRight: -8, -}); - -const SnackbarContent = React.forwardRef(function SnackbarContent(inProps, ref) { - const props = useThemeProps({ props: inProps, name: 'MuiSnackbarContent' }); - const { action, className, message, role = 'alert', ...other } = props; - const ownerState = props; - const classes = useUtilityClasses(ownerState); - - return ( - - - {message} - - {action ? ( - - {action} - - ) : null} - - ); -}); - -SnackbarContent.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * The action to display. It renders after the message, at the end of the snackbar. - */ - action: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The message to display. - */ - message: PropTypes.node, - /** - * The ARIA role attribute of the element. - * @default 'alert' - */ - role: PropTypes /* @typescript-to-proptypes-ignore */.string, - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), - PropTypes.func, - PropTypes.object, - ]), -}; - -export default SnackbarContent; diff --git a/packages/mui-material-next/src/SnackbarContent/SnackbarContent.test.js b/packages/mui-material-next/src/SnackbarContent/SnackbarContent.test.js deleted file mode 100644 index 5a43c787ffa00e..00000000000000 --- a/packages/mui-material-next/src/SnackbarContent/SnackbarContent.test.js +++ /dev/null @@ -1,64 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { createRenderer } from '@mui-internal/test-utils'; -import Paper from '@mui/material/Paper'; -import SnackbarContent, { - snackbarContentClasses as classes, -} from '@mui/material-next/SnackbarContent'; -import describeConformance from '../../test/describeConformance'; - -describe('', () => { - const { render } = createRenderer(); - - describeConformance(, () => ({ - classes, - inheritComponent: Paper, - render, - muiName: 'MuiSnackbarContent', - refInstanceof: window.HTMLDivElement, - skip: ['componentProp', 'componentsProp', 'themeVariants'], - })); - - describe('prop: action', () => { - it('should render the action', () => { - const action = action; - const { container } = render( - , - ); - expect(container.querySelector(`.${classes.action}`)).to.have.class(classes.action); - expect(container.querySelector(`.${classes.action}`)).to.contain('span'); - }); - - it('should render an array of elements', () => { - const action0 = action0; - const action1 = action1; - const { getByText } = render( - , - ); - expect(getByText('action0')).not.to.equal(null); - expect(getByText('action1')).not.to.equal(null); - }); - }); - - describe('prop: message', () => { - it('should render the message', () => { - const message = 'message prop text'; - const { getByRole } = render({message}} />); - expect(getByRole('alert')).to.have.text(message); - }); - }); - - describe('prop: role', () => { - it('renders the default role', () => { - const { getByRole } = render(); - expect(getByRole('alert')).to.have.text('alert message'); - }); - - it('can override the role', () => { - const { queryByRole } = render( - , - ); - expect(queryByRole('alertdialog')).to.have.text('alertdialog message'); - }); - }); -}); diff --git a/packages/mui-material-next/src/SnackbarContent/index.d.ts b/packages/mui-material-next/src/SnackbarContent/index.d.ts deleted file mode 100644 index 658d42f88a58ea..00000000000000 --- a/packages/mui-material-next/src/SnackbarContent/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default } from './SnackbarContent'; -export * from './SnackbarContent'; - -export { default as snackbarContentClasses } from './snackbarContentClasses'; -export * from './snackbarContentClasses'; diff --git a/packages/mui-material-next/src/SnackbarContent/index.js b/packages/mui-material-next/src/SnackbarContent/index.js deleted file mode 100644 index bdea04e7db6591..00000000000000 --- a/packages/mui-material-next/src/SnackbarContent/index.js +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './SnackbarContent'; - -export { default as snackbarContentClasses } from './snackbarContentClasses'; -export * from './snackbarContentClasses'; diff --git a/packages/mui-material-next/src/SnackbarContent/snackbarContentClasses.ts b/packages/mui-material-next/src/SnackbarContent/snackbarContentClasses.ts deleted file mode 100644 index 2ca54fe7230814..00000000000000 --- a/packages/mui-material-next/src/SnackbarContent/snackbarContentClasses.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { - unstable_generateUtilityClasses as generateUtilityClasses, - unstable_generateUtilityClass as generateUtilityClass, -} from '@mui/utils'; - -export interface SnackbarContentClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the message wrapper element. */ - message: string; - /** Styles applied to the action wrapper element if `action` is provided. */ - action: string; -} - -export type SnackbarContentClassKey = keyof SnackbarContentClasses; - -export function getSnackbarContentUtilityClass(slot: string): string { - return generateUtilityClass('MuiSnackbarContent', slot); -} - -const snackbarContentClasses: SnackbarContentClasses = generateUtilityClasses( - 'MuiSnackbarContent', - ['root', 'message', 'action'], -); - -export default snackbarContentClasses; diff --git a/packages/mui-material-next/src/Switch/Switch.test.tsx b/packages/mui-material-next/src/Switch/Switch.test.tsx deleted file mode 100644 index 85cfa8c9cf8e9e..00000000000000 --- a/packages/mui-material-next/src/Switch/Switch.test.tsx +++ /dev/null @@ -1,145 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { act, createRenderer, fireEvent } from '@mui-internal/test-utils'; -import Switch, { switchClasses as classes } from '@mui/material-next/Switch'; -import FormControl from '@mui/material/FormControl'; -import describeConformance from '../../test/describeConformance'; - -describe('', () => { - const { render } = createRenderer(); - - describeConformance(, () => ({ - classes, - render, - muiName: 'MuiSwitch', - testDeepOverrides: [ - { slotName: 'track', slotClassName: classes.track }, - { slotName: 'input', slotClassName: classes.input }, - ], - refInstanceof: window.HTMLSpanElement, - skip: ['componentProp', 'componentsProp', 'propsSpread', 'themeDefaultProps', 'themeVariants'], - })); - - describe('styleSheet', () => { - it('should have the classes required for SwitchBase', () => { - expect(classes).to.include.all.keys(['root', 'checked', 'disabled']); - }); - }); - - specify('should render an .thumb element inside the .switchBase element', () => { - const { container } = render( - , - ); - - expect(container.querySelector('.switch-base .thumb')).not.to.equal(null); - }); - - it('should render the track as the 2nd child', () => { - const { - container: { firstChild: root }, - } = render(); - - expect(root?.childNodes[1]).to.have.property('tagName', 'SPAN'); - expect(root?.childNodes[1]).to.have.class(classes.track); - }); - - it('renders a `role="checkbox"` with the Unchecked state by default', () => { - const { getByRole } = render(); - - expect(getByRole('checkbox')).to.have.property('checked', false); - }); - - it('renders a checkbox with the Checked state when checked', () => { - const { getByRole } = render(); - - expect(getByRole('checkbox')).to.have.property('checked', true); - }); - - specify('the switch can be disabled', () => { - const { getByRole } = render(); - - expect(getByRole('checkbox')).to.have.property('disabled', true); - }); - - specify('the switch can be readonly', () => { - const { getByRole } = render(); - - expect(getByRole('checkbox')).to.have.property('readOnly', true); - }); - - specify('renders a custom icon when provided', () => { - const { getByTestId } = render(} />); - - expect(getByTestId('icon')).toBeVisible(); - }); - - specify('renders a custom checked icon when provided', () => { - const { getByTestId } = render( - } />, - ); - - expect(getByTestId('icon')).toBeVisible(); - }); - - specify('the Checked state changes after change events', () => { - const { getByRole } = render(); - - // how a user would trigger it - act(() => { - getByRole('checkbox').click(); - fireEvent.change(getByRole('checkbox'), { target: { checked: '' } }); - }); - - expect(getByRole('checkbox')).to.have.property('checked', false); - }); - - it('should not show warnings when custom `type` is provided', () => { - expect(() => render()).not.toErrorDev(); - }); - - describe('with FormControl', () => { - describe('enabled', () => { - it('should not have the disabled class', () => { - const { getByRole } = render( - - - , - ); - - expect(getByRole('checkbox')).not.to.have.attribute('disabled'); - }); - - it('should be overridden by props', () => { - const { getByRole } = render( - - - , - ); - - expect(getByRole('checkbox')).to.have.attribute('disabled'); - }); - }); - - describe('disabled', () => { - it('should have the disabled class', () => { - const { getByRole } = render( - - - , - ); - - expect(getByRole('checkbox')).to.have.attribute('disabled'); - }); - - it('should be overridden by props', () => { - const { getByRole } = render( - - - , - ); - - expect(getByRole('checkbox')).not.to.have.attribute('disabled'); - }); - }); - }); -}); diff --git a/packages/mui-material-next/src/Switch/Switch.tsx b/packages/mui-material-next/src/Switch/Switch.tsx deleted file mode 100644 index d14652ce1fda39..00000000000000 --- a/packages/mui-material-next/src/Switch/Switch.tsx +++ /dev/null @@ -1,362 +0,0 @@ -'use client'; -// @inheritedComponent IconButton -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { refType, unstable_capitalize as capitalize } from '@mui/utils'; -import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; -import { alpha, darken, lighten } from '@mui/system'; -import SwitchBase from '@mui/material/internal/SwitchBase'; -import { OverridableComponent } from '@mui/types'; -import useThemeProps from '../styles/useThemeProps'; -import styled from '../styles/styled'; -import switchClasses, { getSwitchUtilityClass } from './switchClasses'; -import { SwitchOwnerState, SwitchProps, SwitchTypeMap } from './Switch.types'; - -const useUtilityClasses = (ownerState: SwitchOwnerState) => { - const { classes, edge, size, color, checked, disabled } = ownerState; - - const slots = { - root: ['root', edge && `edge${capitalize(edge)}`, `size${capitalize(size)}`], - switchBase: [ - 'switchBase', - `color${capitalize(color)}`, - checked && 'checked', - disabled && 'disabled', - ], - thumb: ['thumb'], - track: ['track'], - input: ['input'], - }; - - const composedClasses = composeClasses(slots, getSwitchUtilityClass, classes); - - return { - ...classes, // forward the disabled and checked classes to the SwitchBase - ...composedClasses, - }; -}; - -const SwitchRoot = styled('span', { - name: 'MuiSwitch', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [ - styles.root, - ownerState.edge && styles[`edge${capitalize(ownerState.edge)}`], - styles[`size${capitalize(ownerState.size)}`], - ]; - }, -})<{ ownerState: SwitchOwnerState }>(({ ownerState }) => ({ - display: 'inline-flex', - width: 34 + 12 * 2, - height: 14 + 12 * 2, - overflow: 'hidden', - padding: 12, - boxSizing: 'border-box', - position: 'relative', - flexShrink: 0, - zIndex: 0, // Reset the stacking context. - verticalAlign: 'middle', // For correct alignment with the text. - '@media print': { - colorAdjust: 'exact', - }, - ...(ownerState.edge === 'start' && { - marginLeft: -8, - }), - ...(ownerState.edge === 'end' && { - marginRight: -8, - }), - ...(ownerState.size === 'small' && { - width: 40, - height: 24, - padding: 7, - [`& .${switchClasses.thumb}`]: { - width: 16, - height: 16, - }, - [`& .${switchClasses.switchBase}`]: { - padding: 4, - [`&.${switchClasses.checked}`]: { - transform: 'translateX(16px)', - }, - }, - }), -})); - -const SwitchSwitchBase = styled(SwitchBase, { - name: 'MuiSwitch', - slot: 'SwitchBase', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [ - styles.switchBase, - { [`& .${switchClasses.input}`]: styles.input }, - ownerState.color !== 'default' && styles[`color${capitalize(ownerState.color)}`], - ]; - }, -})<{ ownerState: SwitchOwnerState }>( - ({ theme }) => ({ - position: 'absolute', - top: 0, - left: 0, - zIndex: 1, // Render above the focus ripple. - color: theme.vars - ? theme.vars.palette.Switch.defaultColor - : `${theme.palette.mode === 'light' ? theme.palette.common.white : theme.palette.grey[300]}`, - transition: theme.transitions.create(['left', 'transform'], { - duration: theme.transitions.duration.shortest, - }), - [`&.${switchClasses.checked}`]: { - transform: 'translateX(20px)', - }, - [`&.${switchClasses.disabled}`]: { - color: theme.vars - ? theme.vars.palette.Switch.defaultDisabledColor - : `${theme.palette.mode === 'light' ? theme.palette.grey[100] : theme.palette.grey[600]}`, - }, - [`&.${switchClasses.checked} + .${switchClasses.track}`]: { - opacity: 0.5, - }, - [`&.${switchClasses.disabled} + .${switchClasses.track}`]: { - opacity: theme.vars - ? theme.vars.opacity.switchTrackDisabled - : `${theme.palette.mode === 'light' ? 0.12 : 0.2}`, - }, - [`& .${switchClasses.input}`]: { - left: '-100%', - width: '300%', - }, - }), - ({ theme, ownerState }) => ({ - '&:hover': { - backgroundColor: theme.vars - ? `rgba(${theme.vars.palette.action.activeChannel} / ${theme.vars.palette.action.hoverOpacity})` - : alpha(theme.palette.action.active, theme.palette.action.hoverOpacity), - // Reset on touch devices, it doesn't add specificity - '@media (hover: none)': { - backgroundColor: 'transparent', - }, - }, - ...(ownerState.color !== 'default' && { - [`&.${switchClasses.checked}`]: { - color: (theme.vars || theme).palette[ownerState.color].main, - '&:hover': { - backgroundColor: theme.vars - ? `rgba(${theme.vars.palette[ownerState.color].mainChannel} / ${ - theme.vars.palette.action.hoverOpacity - })` - : alpha(theme.palette[ownerState.color].main, theme.palette.action.hoverOpacity), - '@media (hover: none)': { - backgroundColor: 'transparent', - }, - }, - [`&.${switchClasses.disabled}`]: { - color: theme.vars - ? theme.vars.palette.Switch[`${ownerState.color}DisabledColor`] - : `${ - theme.palette.mode === 'light' - ? lighten(theme.palette[ownerState.color].main, 0.62) - : darken(theme.palette[ownerState.color].main, 0.55) - }`, - }, - }, - [`&.${switchClasses.checked} + .${switchClasses.track}`]: { - backgroundColor: (theme.vars || theme).palette[ownerState.color].main, - }, - }), - }), -); - -const SwitchTrack = styled('span', { - name: 'MuiSwitch', - slot: 'Track', - overridesResolver: (props, styles) => styles.track, -})<{ ownerState: SwitchOwnerState }>(({ theme }) => ({ - height: '100%', - width: '100%', - borderRadius: 14 / 2, - zIndex: -1, - transition: theme.transitions.create(['opacity', 'background-color'], { - duration: theme.transitions.duration.shortest, - }), - backgroundColor: theme.vars - ? theme.vars.palette.common.onBackground - : `${theme.palette.mode === 'light' ? theme.palette.common.black : theme.palette.common.white}`, - opacity: theme.vars - ? theme.vars.opacity.switchTrack - : `${theme.palette.mode === 'light' ? 0.38 : 0.3}`, -})); - -const SwitchThumb = styled('span', { - name: 'MuiSwitch', - slot: 'Thumb', - overridesResolver: (props, styles) => styles.thumb, -})<{ ownerState: SwitchOwnerState }>(({ theme }) => ({ - boxShadow: (theme.vars || theme).shadows[1], - backgroundColor: 'currentColor', - width: 20, - height: 20, - borderRadius: '50%', -})); - -/** - * - * Demos: - * - * - [Switch](https://mui.com/material-ui/react-switch/) - * - [Transfer List](https://mui.com/material-ui/react-transfer-list/) - * - * API: - * - * - [Switch API](https://mui.com/material-ui/api/switch/) - * - inherits [IconButton API](https://mui.com/material-ui/api/icon-button/) - */ -const Switch = React.forwardRef(function Switch< - BaseComponentType extends React.ElementType = SwitchTypeMap['defaultComponent'], ->(inProps: SwitchProps, ref: React.ForwardedRef) { - const props = useThemeProps({ props: inProps, name: 'MuiSwitch' }); - const { className, color = 'primary', edge = false, size = 'medium', sx, ...other } = props; - - const ownerState = { - ...props, - color, - edge, - size, - }; - - const classes = useUtilityClasses(ownerState); - const icon = ; - - return ( - - - - - ); -}) as OverridableComponent; - -Switch.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * If `true`, the component is checked. - */ - checked: PropTypes.bool, - /** - * The icon to display when the component is checked. - */ - checkedIcon: PropTypes.node, - /** - * @ignore - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'primary' - */ - color: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['default', 'primary', 'secondary', 'error', 'info', 'success', 'warning']), - PropTypes.string, - ]), - /** - * The default checked state. Use when the component is not controlled. - */ - defaultChecked: PropTypes.bool, - /** - * If `true`, the component is disabled. - */ - disabled: PropTypes.bool, - /** - * If `true`, the ripple effect is disabled. - * @default false - */ - disableRipple: PropTypes.bool, - /** - * If given, uses a negative margin to counteract the padding on one - * side (this is often helpful for aligning the left or right - * side of the icon with content above or below, without ruining the border - * size and shape). - * @default false - */ - edge: PropTypes.oneOf(['end', 'start', false]), - /** - * The icon to display when the component is unchecked. - */ - icon: PropTypes.node, - /** - * The id of the `input` element. - */ - id: PropTypes.string, - /** - * [Attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Attributes) applied to the `input` element. - */ - inputProps: PropTypes.object, - /** - * Pass a ref to the `input` element. - */ - inputRef: refType, - /** - * Callback fired when the state is changed. - * - * @param {React.ChangeEvent} event The event source of the callback. - * You can pull out the new value by accessing `event.target.value` (string). - * You can pull out the new checked state by accessing `event.target.checked` (boolean). - */ - onChange: PropTypes.func, - /** - * If `true`, the `input` element is required. - * @default false - */ - required: PropTypes.bool, - /** - * The size of the component. - * `small` is equivalent to the dense switch styling. - * @default 'medium' - */ - size: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([ - PropTypes.oneOf(['medium', 'small']), - PropTypes.string, - ]), - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), - PropTypes.func, - PropTypes.object, - ]), - /** - * The value of the component. The DOM API casts this to a string. - * The browser uses "on" as the default value. - */ - value: PropTypes.any, -} as any; - -export default Switch; diff --git a/packages/mui-material-next/src/Switch/Switch.types.ts b/packages/mui-material-next/src/Switch/Switch.types.ts deleted file mode 100644 index 4eff5796daf7c3..00000000000000 --- a/packages/mui-material-next/src/Switch/Switch.types.ts +++ /dev/null @@ -1,72 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { OverridableStringUnion, OverrideProps, PartiallyRequired } from '@mui/types'; -// eslint-disable-next-line no-restricted-imports -import { InternalStandardProps as StandardProps } from '@mui/material'; -import type { SwitchBaseProps } from '@mui/material/internal/SwitchBase'; -import { Theme } from '../styles'; -import { SwitchClasses } from './switchClasses'; - -export interface SwitchPropsSizeOverrides {} - -export interface SwitchPropsColorOverrides {} - -export interface SwitchOwnProps - extends StandardProps { - /** - * The icon to display when the component is checked. - */ - checkedIcon?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial; - /** - * The color of the component. - * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). - * @default 'primary' - */ - color?: OverridableStringUnion< - 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning' | 'default', - SwitchPropsColorOverrides - >; - /** - * If `true`, the component is disabled. - */ - disabled?: boolean; - /** - * The icon to display when the component is unchecked. - */ - icon?: React.ReactNode; - /** - * The size of the component. - * `small` is equivalent to the dense switch styling. - * @default 'medium' - */ - size?: OverridableStringUnion<'small' | 'medium', SwitchPropsSizeOverrides>; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps; - /** - * The value of the component. The DOM API casts this to a string. - * The browser uses "on" as the default value. - */ - value?: unknown; -} - -export interface SwitchTypeMap< - AdditionalProps = {}, - RootComponentType extends React.ElementType = 'span', -> { - props: SwitchOwnProps & AdditionalProps; - defaultComponent: RootComponentType; -} - -export type SwitchProps< - RootComponentType extends React.ElementType = SwitchTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps, RootComponentType>; - -export interface SwitchOwnerState extends PartiallyRequired {} diff --git a/packages/mui-material-next/src/Switch/index.ts b/packages/mui-material-next/src/Switch/index.ts deleted file mode 100644 index 6e53e01c5c3dba..00000000000000 --- a/packages/mui-material-next/src/Switch/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './Switch'; - -export { default as switchClasses } from './switchClasses'; -export * from './switchClasses'; diff --git a/packages/mui-material-next/src/Switch/switchClasses.ts b/packages/mui-material-next/src/Switch/switchClasses.ts deleted file mode 100644 index e7dd2e99a31ed9..00000000000000 --- a/packages/mui-material-next/src/Switch/switchClasses.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { - unstable_generateUtilityClasses as generateUtilityClasses, - unstable_generateUtilityClass as generateUtilityClass, -} from '@mui/utils'; - -export interface SwitchClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if `edge="start"`. */ - edgeStart: string; - /** Styles applied to the root element if `edge="end"`. */ - edgeEnd: string; - /** Styles applied to the internal `SwitchBase` component's `root` class. */ - switchBase: string; - /** Styles applied to the internal SwitchBase component's root element if `color="primary"`. */ - colorPrimary: string; - /** Styles applied to the internal SwitchBase component's root element if `color="secondary"`. */ - colorSecondary: string; - /** Styles applied to the root element if `size="small"`. */ - sizeSmall: string; - /** Styles applied to the root element if `size="medium"`. */ - sizeMedium: string; - /** State class applied to the internal `SwitchBase` component's `checked` class. */ - checked: string; - /** State class applied to the internal SwitchBase component's disabled class. */ - disabled: string; - /** Styles applied to the internal SwitchBase component's input element. */ - input: string; - /** Styles used to create the thumb passed to the internal `SwitchBase` component `icon` prop. */ - thumb: string; - /** Styles applied to the track element. */ - track: string; -} - -export type SwitchClassKey = keyof SwitchClasses; - -export function getSwitchUtilityClass(slot: string): string { - return generateUtilityClass('MuiSwitch', slot); -} - -const switchClasses: SwitchClasses = generateUtilityClasses('MuiSwitch', [ - 'root', - 'edgeStart', - 'edgeEnd', - 'switchBase', - 'colorPrimary', - 'colorSecondary', - 'sizeSmall', - 'sizeMedium', - 'checked', - 'disabled', - 'input', - 'thumb', - 'track', -]); - -export default switchClasses; diff --git a/packages/mui-material-next/src/Tab/Tab.d.ts b/packages/mui-material-next/src/Tab/Tab.d.ts deleted file mode 100644 index 50dd2de45ddf01..00000000000000 --- a/packages/mui-material-next/src/Tab/Tab.d.ts +++ /dev/null @@ -1,83 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { Theme } from '@mui/material/styles'; -import { ExtendButtonBase, ExtendButtonBaseTypeMap } from '@mui/material/ButtonBase'; -import { OverrideProps } from '@mui/material/OverridableComponent'; -import { TabClasses } from './tabClasses'; - -export interface TabOwnProps { - /** - * This prop isn't supported. - * Use the `component` prop if you need to change the children structure. - */ - children?: null; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial; - /** - * If `true`, the component is disabled. - * @default false - */ - disabled?: boolean; - /** - * If `true`, the keyboard focus ripple is disabled. - * @default false - */ - disableFocusRipple?: boolean; - /** - * The icon to display. - */ - icon?: string | React.ReactElement; - /** - * The position of the icon relative to the label. - * @default 'top' - */ - iconPosition?: 'top' | 'bottom' | 'start' | 'end'; - /** - * The label element. - */ - label?: React.ReactNode; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps; - /** - * You can provide your own value. Otherwise, we fallback to the child position index. - */ - value?: any; - /** - * Tab labels appear in a single row. - * They can use a second line if needed. - * @default false - */ - wrapped?: boolean; -} - -export type TabTypeMap< - AdditionalProps = {}, - RootComponent extends React.ElementType = 'div', -> = ExtendButtonBaseTypeMap<{ - props: AdditionalProps & TabOwnProps; - defaultComponent: RootComponent; -}>; - -/** - * - * Demos: - * - * - [Tabs](https://mui.com/components/tabs/) - * - * API: - * - * - [Tab API](https://mui.com/api/tab/) - * - inherits [ButtonBase API](https://mui.com/api/button-base/) - */ -declare const Tab: ExtendButtonBase; - -export type TabProps< - RootComponent extends React.ElementType = TabTypeMap['defaultComponent'], - AdditionalProps = {}, -> = OverrideProps, RootComponent>; - -export default Tab; diff --git a/packages/mui-material-next/src/Tab/Tab.js b/packages/mui-material-next/src/Tab/Tab.js deleted file mode 100644 index 766c386b76ec5e..00000000000000 --- a/packages/mui-material-next/src/Tab/Tab.js +++ /dev/null @@ -1,272 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { unstable_composeClasses as composeClasses } from '@mui/base'; -import { useTab } from '@mui/base/useTab'; -// TODO: use useButton hook here -import ButtonBase from '@mui/material/ButtonBase'; -import { - unstable_capitalize as capitalize, - unstable_unsupportedProp as unsupportedProp, -} from '@mui/utils'; -import { styled, useThemeProps } from '@mui/material/styles'; -import tabClasses, { getTabUtilityClass } from './tabClasses'; -import TabsListContext from '../Tabs/TabsListContext'; - -const useUtilityClasses = (ownerState) => { - const { classes, textColor, fullWidth, wrapped, icon, label, selected, disabled } = ownerState; - - const slots = { - root: [ - 'root', - icon && label && 'labelIcon', - `textColor${capitalize(textColor)}`, - fullWidth && 'fullWidth', - wrapped && 'wrapped', - selected && 'selected', - disabled && 'disabled', - ], - iconWrapper: ['iconWrapper'], - }; - - return composeClasses(slots, getTabUtilityClass, classes); -}; - -const TabRoot = styled(ButtonBase, { - name: 'MuiTab', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [ - styles.root, - ownerState.label && ownerState.icon && styles.labelIcon, - styles[`textColor${capitalize(ownerState.textColor)}`], - ownerState.fullWidth && styles.fullWidth, - ownerState.wrapped && styles.wrapped, - ]; - }, -})(({ theme, ownerState }) => ({ - ...theme.typography.button, - maxWidth: 360, - minWidth: 90, - position: 'relative', - minHeight: 48, - flexShrink: 0, - padding: '12px 16px', - overflow: 'hidden', - whiteSpace: 'normal', - textAlign: 'center', - ...(ownerState.label && { - flexDirection: - ownerState.iconPosition === 'top' || ownerState.iconPosition === 'bottom' ? 'column' : 'row', - }), - lineHeight: 1.25, - ...(ownerState.icon && - ownerState.label && { - minHeight: 72, - paddingTop: 9, - paddingBottom: 9, - [`& > .${tabClasses.iconWrapper}`]: { - ...(ownerState.iconPosition === 'top' && { - marginBottom: 6, - }), - ...(ownerState.iconPosition === 'bottom' && { - marginTop: 6, - }), - ...(ownerState.iconPosition === 'start' && { - marginRight: theme.spacing(1), - }), - ...(ownerState.iconPosition === 'end' && { - marginLeft: theme.spacing(1), - }), - }, - }), - ...(ownerState.textColor === 'inherit' && { - color: 'inherit', - opacity: 0.6, // same opacity as theme.palette.text.secondary - [`&.${tabClasses.selected}`]: { - opacity: 1, - }, - [`&.${tabClasses.disabled}`]: { - opacity: theme.palette.action.disabledOpacity, - }, - }), - ...(ownerState.textColor === 'primary' && { - color: theme.palette.text.secondary, - [`&.${tabClasses.selected}`]: { - color: theme.palette.primary.main, - }, - [`&.${tabClasses.disabled}`]: { - color: theme.palette.text.disabled, - }, - }), - ...(ownerState.textColor === 'secondary' && { - color: theme.palette.text.secondary, - [`&.${tabClasses.selected}`]: { - color: theme.palette.secondary.main, - }, - [`&.${tabClasses.disabled}`]: { - color: theme.palette.text.disabled, - }, - }), - ...(ownerState.fullWidth && { - flexShrink: 1, - flexGrow: 1, - flexBasis: 0, - maxWidth: 'none', - }), - ...(ownerState.wrapped && { - fontSize: theme.typography.pxToRem(12), - }), -})); - -const Tab = React.forwardRef(function Tab(inProps, ref) { - const props = useThemeProps({ props: inProps, name: 'MuiTab' }); - const { - className, - disableFocusRipple = false, - // eslint-disable-next-line react/prop-types - fullWidth: fullWidthProp, - icon: iconProp, - iconPosition = 'top', - // eslint-disable-next-line react/prop-types - indicator: indicatorProp, - label, - // eslint-disable-next-line react/prop-types - textColor: textColorProp = 'inherit', - value, - wrapped = false, - ...other - } = props; - - const { disabled, selected, getRootProps } = useTab({ ...props, rootRef: ref }); - - let indicator = indicatorProp; - let textColor = textColorProp; - let fullWidth = fullWidthProp; - - const tabsListContext = React.useContext(TabsListContext); - if (tabsListContext != null) { - indicator ??= tabsListContext.indicator; - textColor ??= tabsListContext.textColor; - fullWidth ??= tabsListContext.fullWidth; - } - - const ownerState = { - ...props, - disabled, - disableFocusRipple, - selected, - icon: !!iconProp, - iconPosition, - label: !!label, - fullWidth, - textColor, - wrapped, - }; - - const classes = useUtilityClasses(ownerState); - const icon = - iconProp && label && React.isValidElement(iconProp) - ? React.cloneElement(iconProp, { - className: clsx(classes.iconWrapper, iconProp.props.className), - }) - : iconProp; - - return ( - - {iconPosition === 'top' || iconPosition === 'start' ? ( - - {icon} - {label} - - ) : ( - - {label} - {icon} - - )} - - {selected && indicator} - - ); -}); - -Tab.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * This prop isn't supported. - * Use the `component` prop if you need to change the children structure. - */ - children: unsupportedProp, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * If `true`, the component is disabled. - * @default false - */ - disabled: PropTypes.bool, - /** - * If `true`, the keyboard focus ripple is disabled. - * @default false - */ - disableFocusRipple: PropTypes.bool, - /** - * If `true`, the ripple effect is disabled. - * - * ⚠️ Without a ripple there is no styling for :focus-visible by default. Be sure - * to highlight the element by applying separate styles with the `.Mui-focusVisible` class. - * @default false - */ - disableRipple: PropTypes.bool, - /** - * The icon to display. - */ - icon: PropTypes.oneOfType([PropTypes.element, PropTypes.string]), - /** - * The position of the icon relative to the label. - * @default 'top' - */ - iconPosition: PropTypes.oneOf(['bottom', 'end', 'start', 'top']), - /** - * The label element. - */ - label: PropTypes.node, - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), - PropTypes.func, - PropTypes.object, - ]), - /** - * You can provide your own value. Otherwise, we fallback to the child position index. - */ - value: PropTypes.any, - /** - * Tab labels appear in a single row. - * They can use a second line if needed. - * @default false - */ - wrapped: PropTypes.bool, -}; - -export default Tab; diff --git a/packages/mui-material-next/src/Tab/Tab.test.js b/packages/mui-material-next/src/Tab/Tab.test.js deleted file mode 100644 index 24f1ac4aba1620..00000000000000 --- a/packages/mui-material-next/src/Tab/Tab.test.js +++ /dev/null @@ -1,170 +0,0 @@ -import ButtonBase from '@mui/material/ButtonBase'; -import Tab, { tabClasses as classes } from '@mui/material/Tab'; -import { expect } from 'chai'; -import * as React from 'react'; -import { spy } from 'sinon'; -import { act, createRenderer, fireEvent } from '@mui-internal/test-utils'; -import describeConformance from '../../test/describeConformance'; - -describe('', () => { - const { render } = createRenderer(); - - describeConformance(, () => ({ - classes, - inheritComponent: ButtonBase, - render, - muiName: 'MuiTab', - testVariantProps: { variant: 'foo' }, - refInstanceof: window.HTMLButtonElement, - skip: ['componentProp', 'componentsProp'], - })); - - it('should have a ripple by default', () => { - const { container } = render(); - - expect(container.querySelector('.touch-ripple')).not.to.equal(null); - }); - - it('can disable the ripple', () => { - const { container } = render( - , - ); - - expect(container.querySelector('.touch-ripple')).to.equal(null); - }); - - it('should have a focusRipple by default', () => { - const { container, getByRole } = render( - , - ); - // simulate pointer device - fireEvent.pointerDown(document.body); - - act(() => { - fireEvent.keyDown(document.body, { key: 'Tab' }); - // jsdom doesn't actually support tab focus, we need to do it manually - getByRole('tab').focus(); - }); - - expect(container.querySelector('.focus-ripple')).not.to.equal(null); - }); - - it('can disable the focusRipple', () => { - const { container, getByRole } = render( - , - ); - // simulate pointer device - fireEvent.pointerDown(document.body); - - act(() => { - fireEvent.keyDown(document.body, { key: 'Tab' }); - // jsdom doesn't actually support tab focus, we need to do it manually - getByRole('tab').focus(); - }); - - expect(container.querySelector('.focus-ripple')).to.equal(null); - }); - - describe('prop: selected', () => { - it('should render with the selected and root classes', () => { - const { getByRole } = render(); - - const tab = getByRole('tab'); - expect(tab).to.have.class(classes.root); - expect(tab).to.have.class(classes.selected); - expect(tab).to.have.class(classes.textColorSecondary); - expect(tab).to.have.attribute('aria-selected', 'true'); - }); - }); - - describe('prop: disabled', () => { - it('should render with the disabled and root classes', () => { - const { getByRole } = render(); - - const tab = getByRole('tab'); - expect(tab).to.have.class(classes.root); - expect(tab).to.have.class(classes.disabled); - expect(tab).to.have.class(classes.textColorSecondary); - }); - }); - - describe('prop: onClick', () => { - it('should be called when a click is triggered', () => { - const handleClick = spy(); - const { getByRole } = render(); - - getByRole('tab').click(); - - expect(handleClick.callCount).to.equal(1); - }); - }); - - describe('prop: label', () => { - it('should render label', () => { - const { getByRole } = render(); - - expect(getByRole('tab')).to.have.text('foo'); - }); - }); - - describe('prop: wrapped', () => { - it('should add the wrapped class', () => { - const { getByRole } = render(); - - expect(getByRole('tab')).to.have.class(classes.wrapped); - }); - }); - - describe('prop: icon', () => { - it('should render icon element', () => { - const { getByTestId } = render(} />); - - expect(getByTestId('icon')).not.to.equal(null); - }); - - it('should add a classname when passed together with label', () => { - const { getByRole } = render(} label="foo" />); - const wrapper = getByRole('tab').children[0]; - expect(wrapper).to.have.class(classes.iconWrapper); - expect(wrapper).to.have.class('test-icon'); - }); - - it('should have bottom margin when passed together with label', () => { - const { getByRole } = render(} label="foo" />); - const wrapper = getByRole('tab').children[0]; - expect(wrapper).toHaveComputedStyle({ marginBottom: '6px' }); - }); - }); - - describe('prop: textColor', () => { - it('should support the inherit value', () => { - const { getByRole } = render(); - - const tab = getByRole('tab'); - expect(tab).to.have.class(classes.selected); - expect(tab).to.have.class(classes.textColorInherit); - expect(tab).to.have.class(classes.root); - }); - }); - - describe('prop: fullWidth', () => { - it('should have the fullWidth class', () => { - const { getByRole } = render(); - - expect(getByRole('tab')).to.have.class(classes.fullWidth); - }); - }); - - describe('prop: style', () => { - it('should be able to override everything', () => { - const { getByRole } = render( - , - ); - - const { style } = getByRole('tab'); - expect(style).to.have.property('width', '80%'); - expect(style).to.have.property('color', 'red'); - expect(style).to.have.property('alignText', 'center'); - }); - }); -}); diff --git a/packages/mui-material-next/src/Tab/index.d.ts b/packages/mui-material-next/src/Tab/index.d.ts deleted file mode 100644 index dae620b149a0af..00000000000000 --- a/packages/mui-material-next/src/Tab/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default } from './Tab'; -export * from './Tab'; - -export { default as tabClasses } from './tabClasses'; -export * from './tabClasses'; diff --git a/packages/mui-material-next/src/Tab/index.js b/packages/mui-material-next/src/Tab/index.js deleted file mode 100644 index 29213284d8cbd8..00000000000000 --- a/packages/mui-material-next/src/Tab/index.js +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './Tab'; - -export { default as tabClasses } from './tabClasses'; -export * from './tabClasses'; diff --git a/packages/mui-material-next/src/Tab/tabClasses.ts b/packages/mui-material-next/src/Tab/tabClasses.ts deleted file mode 100644 index ef995aeb7b3c23..00000000000000 --- a/packages/mui-material-next/src/Tab/tabClasses.ts +++ /dev/null @@ -1,46 +0,0 @@ -import generateUtilityClass from '@mui/utils/generateUtilityClass'; -import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; - -export interface TabClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if both `icon` and `label` are provided. */ - labelIcon: string; - /** Styles applied to the root element if the parent [`Tabs`](/material-ui/api/tabs/) has `textColor="inherit"`. */ - textColorInherit: string; - /** Styles applied to the root element if the parent [`Tabs`](/material-ui/api/tabs/) has `textColor="primary"`. */ - textColorPrimary: string; - /** Styles applied to the root element if the parent [`Tabs`](/material-ui/api/tabs/) has `textColor="secondary"`. */ - textColorSecondary: string; - /** State class applied to the root element if `selected={true}` (controlled by the Tabs component). */ - selected: string; - /** State class applied to the root element if `disabled={true}` (controlled by the Tabs component). */ - disabled: string; - /** Styles applied to the root element if `fullWidth={true}` (controlled by the Tabs component). */ - fullWidth: string; - /** Styles applied to the root element if `wrapped={true}`. */ - wrapped: string; - /** Styles applied to the wrapper element of `icon` if `icon` is provided. */ - iconWrapper: string; -} - -export type TabClassKey = keyof TabClasses; - -export function getTabUtilityClass(slot: string): string { - return generateUtilityClass('MuiTab', slot); -} - -const tabClasses: TabClasses = generateUtilityClasses('MuiTab', [ - 'root', - 'labelIcon', - 'textColorInherit', - 'textColorPrimary', - 'textColorSecondary', - 'selected', - 'disabled', - 'fullWidth', - 'wrapped', - 'iconWrapper', -]); - -export default tabClasses; diff --git a/packages/mui-material-next/src/TabScrollButton/TabScrollButton.d.ts b/packages/mui-material-next/src/TabScrollButton/TabScrollButton.d.ts deleted file mode 100644 index 45675384cc2b59..00000000000000 --- a/packages/mui-material-next/src/TabScrollButton/TabScrollButton.d.ts +++ /dev/null @@ -1,43 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { InternalStandardProps as StandardProps, Theme } from '@mui/material'; -import { TabScrollButtonClasses } from './tabScrollButtonClasses'; - -export interface TabScrollButtonProps extends StandardProps> { - /** - * The content of the component. - */ - children?: React.ReactNode; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial; - /** - * The direction the button should indicate. - */ - direction: 'left' | 'right'; - /** - * If `true`, the component is disabled. - */ - disabled?: boolean; - /** - * The component orientation (layout flow direction). - */ - orientation: 'horizontal' | 'vertical'; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps; -} - -/** - * - * Demos: - * - * - [Tabs](https://mui.com/components/tabs/) - * - * API: - * - * - [TabScrollButton API](https://mui.com/api/tab-scroll-button/) - */ -export default function TabScrollButton(props: TabScrollButtonProps): JSX.Element; diff --git a/packages/mui-material-next/src/TabScrollButton/TabScrollButton.js b/packages/mui-material-next/src/TabScrollButton/TabScrollButton.js deleted file mode 100644 index e154b58258adb9..00000000000000 --- a/packages/mui-material-next/src/TabScrollButton/TabScrollButton.js +++ /dev/null @@ -1,117 +0,0 @@ -'use client'; -/* eslint-disable jsx-a11y/aria-role */ -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { unstable_composeClasses as composeClasses } from '@mui/base'; -import KeyboardArrowLeft from '@mui/material/internal/svg-icons/KeyboardArrowLeft'; -import KeyboardArrowRight from '@mui/material/internal/svg-icons/KeyboardArrowRight'; -// TODO: use useButton hook here -import ButtonBase from '@mui/material/ButtonBase'; -import { styled, useTheme, useThemeProps } from '@mui/material/styles'; -import tabScrollButtonClasses, { getTabScrollButtonUtilityClass } from './tabScrollButtonClasses'; - -const useUtilityClasses = (ownerState) => { - const { classes, orientation, disabled } = ownerState; - - const slots = { - root: ['root', orientation, disabled && 'disabled'], - }; - - return composeClasses(slots, getTabScrollButtonUtilityClass, classes); -}; - -const TabScrollButtonRoot = styled(ButtonBase, { - name: 'MuiTabScrollButton', - slot: 'Root', - overridesResolver: (props, styles) => { - const { ownerState } = props; - - return [styles.root, ownerState.orientation && styles[ownerState.orientation]]; - }, -})(({ ownerState }) => ({ - width: 40, - flexShrink: 0, - opacity: 0.8, - [`&.${tabScrollButtonClasses.disabled}`]: { - opacity: 0, - }, - ...(ownerState.orientation === 'vertical' && { - width: '100%', - height: 40, - '& svg': { - transform: `rotate(${ownerState.isRtl ? -90 : 90}deg)`, - }, - }), -})); - -const TabScrollButton = React.forwardRef(function TabScrollButton(inProps, ref) { - const props = useThemeProps({ props: inProps, name: 'MuiTabScrollButton' }); - const { className, direction, orientation, disabled, ...other } = props; - - const theme = useTheme(); - const isRtl = theme.direction === 'rtl'; - - const ownerState = { isRtl, ...props }; - - const classes = useUtilityClasses(ownerState); - - return ( - - {direction === 'left' ? ( - - ) : ( - - )} - - ); -}); - -TabScrollButton.propTypes /* remove-proptypes */ = { - // ┌────────────────────────────── Warning ──────────────────────────────┐ - // │ These PropTypes are generated from the TypeScript type definitions. │ - // │ To update them, edit the d.ts file and run `pnpm proptypes`. │ - // └─────────────────────────────────────────────────────────────────────┘ - /** - * The content of the component. - */ - children: PropTypes.node, - /** - * Override or extend the styles applied to the component. - */ - classes: PropTypes.object, - /** - * @ignore - */ - className: PropTypes.string, - /** - * The direction the button should indicate. - */ - direction: PropTypes.oneOf(['left', 'right']).isRequired, - /** - * If `true`, the component is disabled. - */ - disabled: PropTypes.bool, - /** - * The component orientation (layout flow direction). - */ - orientation: PropTypes.oneOf(['horizontal', 'vertical']).isRequired, - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), - PropTypes.func, - PropTypes.object, - ]), -}; - -export default TabScrollButton; diff --git a/packages/mui-material-next/src/TabScrollButton/TabScrollButton.test.js b/packages/mui-material-next/src/TabScrollButton/TabScrollButton.test.js deleted file mode 100644 index 20032aa756f97f..00000000000000 --- a/packages/mui-material-next/src/TabScrollButton/TabScrollButton.test.js +++ /dev/null @@ -1,55 +0,0 @@ -import * as React from 'react'; -import { expect } from 'chai'; -import { createRenderer } from '@mui-internal/test-utils'; -import TabScrollButton, { - tabScrollButtonClasses as classes, -} from '@mui/material-next/TabScrollButton'; -import describeConformance from '../../test/describeConformance'; - -describe('', () => { - const defaultProps = { - direction: 'left', - orientation: 'horizontal', - }; - const { render } = createRenderer(); - - describeConformance(, () => ({ - classes, - inheritComponent: 'div', - render, - muiName: 'MuiTabScrollButton', - testVariantProps: { orientation: 'vertical' }, - refInstanceof: window.HTMLDivElement, - skip: ['componentProp', 'componentsProp'], - })); - - it('should render as a button with the root class', () => { - const { container } = render(); - const button = container.firstChild; - expect(button).to.have.class(classes.root); - }); - - describe('prop: disabled', () => { - it('should render with a opacity of 0', () => { - const { container } = render(); - const button = container.firstChild; - expect(button).to.have.class(classes.disabled); - }); - }); - - describe('prop: direction', () => { - it('should render with the left icon', () => { - const { getAllByTestId } = render( - , - ); - expect(getAllByTestId('KeyboardArrowLeftIcon').length).to.equal(1); - }); - - it('should render with the right icon', () => { - const { getAllByTestId } = render( - , - ); - expect(getAllByTestId('KeyboardArrowRightIcon').length).to.equal(1); - }); - }); -}); diff --git a/packages/mui-material-next/src/TabScrollButton/index.d.ts b/packages/mui-material-next/src/TabScrollButton/index.d.ts deleted file mode 100644 index 6a0eacea7df14d..00000000000000 --- a/packages/mui-material-next/src/TabScrollButton/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { default } from './TabScrollButton'; -export * from './TabScrollButton'; - -export { default as tabScrollButtonClasses } from './tabScrollButtonClasses'; -export * from './tabScrollButtonClasses'; diff --git a/packages/mui-material-next/src/TabScrollButton/index.js b/packages/mui-material-next/src/TabScrollButton/index.js deleted file mode 100644 index 15aa330441752b..00000000000000 --- a/packages/mui-material-next/src/TabScrollButton/index.js +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; -export { default } from './TabScrollButton'; - -export { default as tabScrollButtonClasses } from './tabScrollButtonClasses'; -export * from './tabScrollButtonClasses'; diff --git a/packages/mui-material-next/src/TabScrollButton/tabScrollButtonClasses.ts b/packages/mui-material-next/src/TabScrollButton/tabScrollButtonClasses.ts deleted file mode 100644 index 57e770c16184dc..00000000000000 --- a/packages/mui-material-next/src/TabScrollButton/tabScrollButtonClasses.ts +++ /dev/null @@ -1,24 +0,0 @@ -import generateUtilityClass from '@mui/utils/generateUtilityClass'; -import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; - -export interface TabScrollButtonClasses { - /** Styles applied to the root element. */ - root: string; - /** Styles applied to the root element if `orientation="vertical"`. */ - vertical: string; - /** State class applied to the root element if `disabled={true}`. */ - disabled: string; -} - -export type TabScrollButtonClassKey = keyof TabScrollButtonClasses; - -export function getTabScrollButtonUtilityClass(slot: string): string { - return generateUtilityClass('MuiTabScrollButton', slot); -} - -const tabScrollButtonClasses: TabScrollButtonClasses = generateUtilityClasses( - 'MuiTabScrollButton', - ['root', 'vertical', 'horizontal', 'disabled'], -); - -export default tabScrollButtonClasses; diff --git a/packages/mui-material-next/src/TablePagination/TablePagination.d.ts b/packages/mui-material-next/src/TablePagination/TablePagination.d.ts deleted file mode 100644 index 8acb44381a0ebc..00000000000000 --- a/packages/mui-material-next/src/TablePagination/TablePagination.d.ts +++ /dev/null @@ -1,183 +0,0 @@ -import * as React from 'react'; -import { SxProps } from '@mui/system'; -import { Theme } from '@mui/material/styles'; -import { OverridableComponent, OverrideProps } from '@mui/material/OverridableComponent'; -import { TableCellProps } from '@mui/material/TableCell'; -import { IconButtonProps } from '@mui/material/IconButton'; -import { SelectProps } from '@mui/material/Select'; -import { TablePaginationClasses } from './tablePaginationClasses'; - -export interface LabelDisplayedRowsArgs { - from: number; - to: number; - count: number; - page: number; -} - -export interface TablePaginationActionsProps extends React.HTMLAttributes { - backIconButtonProps?: Partial; - /** - * Override or extend the styles applied to the component. - */ - classes?: {}; - count: number; - /** - * Accepts a function which returns a string value that provides a user-friendly name for the current page. - * This is important for screen reader users. - * - * For localization purposes, you can use the provided [translations](/material-ui/guides/localization/). - * @param {string} type The link or button type to format ('first' | 'last' | 'next' | 'previous'). - * @returns {string} - */ - getItemAriaLabel: (type: 'first' | 'last' | 'next' | 'previous') => string; - nextIconButtonProps?: Partial; - onPageChange: (event: React.MouseEvent | null, page: number) => void; - page: number; - rowsPerPage: number; - showFirstButton: boolean; - showLastButton: boolean; -} - -/** - * This type is kept for compatibility. Use `TablePaginationOwnProps` instead. - */ -export type TablePaginationBaseProps = Omit; - -export interface TablePaginationOwnProps extends TablePaginationBaseProps { - /** - * The component used for displaying the actions. - * Either a string to use a HTML element or a component. - * @default TablePaginationActions - */ - ActionsComponent?: React.ElementType; - /** - * Props applied to the back arrow [`IconButton`](/material-ui/api/icon-button/) component. - */ - backIconButtonProps?: Partial; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial; - /** - * The total number of rows. - * - * To enable server side pagination for an unknown number of items, provide -1. - */ - count: number; - /** - * Accepts a function which returns a string value that provides a user-friendly name for the current page. - * This is important for screen reader users. - * - * For localization purposes, you can use the provided [translations](/material-ui/guides/localization/). - * @param {string} type The link or button type to format ('first' | 'last' | 'next' | 'previous'). - * @returns {string} - * @default function defaultGetAriaLabel(type) { - * return `Go to ${type} page`; - * } - */ - getItemAriaLabel?: (type: 'first' | 'last' | 'next' | 'previous') => string; - /** - * Customize the displayed rows label. Invoked with a `{ from, to, count, page }` - * object. - * - * For localization purposes, you can use the provided [translations](/material-ui/guides/localization/). - * @default function defaultLabelDisplayedRows({ from, to, count }) { - * return `${from}–${to} of ${count !== -1 ? count : `more than ${to}`}`; - * } - */ - labelDisplayedRows?: (paginationInfo: LabelDisplayedRowsArgs) => React.ReactNode; - /** - * Customize the rows per page label. - * - * For localization purposes, you can use the provided [translations](/material-ui/guides/localization/). - * @default 'Rows per page:' - */ - labelRowsPerPage?: React.ReactNode; - /** - * Props applied to the next arrow [`IconButton`](/material-ui/api/icon-button/) element. - */ - nextIconButtonProps?: Partial; - /** - * Callback fired when the page is changed. - * - * @param {React.MouseEvent | null} event The event source of the callback. - * @param {number} page The page selected. - */ - onPageChange: (event: React.MouseEvent | null, page: number) => void; - /** - * Callback fired when the number of rows per page is changed. - * - * @param {React.ChangeEvent} event The event source of the callback. - */ - onRowsPerPageChange?: React.ChangeEventHandler; - /** - * The zero-based index of the current page. - */ - page: number; - /** - * The number of rows per page. - * - * Set -1 to display all the rows. - */ - rowsPerPage: number; - /** - * Customizes the options of the rows per page select field. If less than two options are - * available, no select field will be displayed. - * Use -1 for the value with a custom label to show all the rows. - * @default [10, 25, 50, 100] - */ - rowsPerPageOptions?: ReadonlyArray< - | number - | { - value: number; - label: string; - } - >; - /** - * Props applied to the rows per page [`Select`](/material-ui/api/select/) element. - * @default {} - */ - SelectProps?: Partial; - /** - * If `true`, show the first-page button. - * @default false - */ - showFirstButton?: boolean; - /** - * If `true`, show the last-page button. - * @default false - */ - showLastButton?: boolean; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps; -} - -export interface TablePaginationTypeMap { - props: AdditionalProps & TablePaginationOwnProps; - defaultComponent: RootComponent; -} - -/** - * A `TableCell` based component for placing inside `TableFooter` for pagination. - * - * Demos: - * - * - [Tables](https://mui.com/components/tables/) - * - * API: - * - * - [TablePagination API](https://mui.com/api/table-pagination/) - * - inherits [TableCell API](https://mui.com/api/table-cell/) - */ -declare const TablePagination: OverridableComponent< - TablePaginationTypeMap<{}, React.JSXElementConstructor> ->; - -export type TablePaginationProps< - RootComponent extends React.ElementType = React.JSXElementConstructor, - AdditionalProps = {}, -> = OverrideProps, RootComponent>; - -export default TablePagination; diff --git a/packages/mui-material-next/src/TablePagination/TablePagination.js b/packages/mui-material-next/src/TablePagination/TablePagination.js deleted file mode 100644 index 8b2411adefdac5..00000000000000 --- a/packages/mui-material-next/src/TablePagination/TablePagination.js +++ /dev/null @@ -1,399 +0,0 @@ -'use client'; -import * as React from 'react'; -import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import { unstable_useId as useId, chainPropTypes, integerPropType } from '@mui/utils'; -import { unstable_composeClasses as composeClasses, appendOwnerState } from '@mui/base'; -import { TablePagination as BaseTablePagination } from '@mui/base/TablePagination'; -import { styled, useThemeProps } from '@mui/material/styles'; -import InputBase from '@mui/material/InputBase'; -import MenuItem from '@mui/material/MenuItem'; -import Select from '@mui/material/Select'; -import TableCell from '@mui/material/TableCell'; -import Toolbar from '@mui/material/Toolbar'; -import IconButton from '@mui/material/IconButton'; -import LastPageIcon from '@mui/material/internal/svg-icons/LastPage'; -import FirstPageIcon from '@mui/material/internal/svg-icons/FirstPage'; -import KeyboardArrowLeft from '@mui/material/internal/svg-icons/KeyboardArrowLeft'; -import KeyboardArrowRight from '@mui/material/internal/svg-icons/KeyboardArrowRight'; -import tablePaginationClasses, { getTablePaginationUtilityClass } from './tablePaginationClasses'; - -// This component is needed as the IconButton does not merge the ownerState -// coming from props. This results in the prop overriding the internal ownerState -const CustomIconButton = React.forwardRef((props, ref) => { - // eslint-disable-next-line react/prop-types - const { ownerState, ...other } = props; - return ; -}); - -const TablePaginationRoot = styled('td', { - name: 'MuiTablePagination', - slot: 'Root', - overridesResolver: (props, styles) => styles.root, -})(({ theme }) => ({ - overflow: 'auto', - color: theme.palette.text.primary, - fontSize: theme.typography.pxToRem(14), - // Increase the specificity to override TableCell. - '&:last-child': { - padding: 0, - }, -})); - -const TablePaginationToolbar = styled(Toolbar, { - name: 'MuiTablePagination', - slot: 'Toolbar', - overridesResolver: (props, styles) => ({ - [`& .${tablePaginationClasses.actions}`]: styles.actions, - ...styles.toolbar, - }), -})(({ theme }) => ({ - minHeight: 52, - paddingRight: 2, - [`${theme.breakpoints.up('xs')} and (orientation: landscape)`]: { - minHeight: 52, - }, - [theme.breakpoints.up('sm')]: { - minHeight: 52, - paddingRight: 2, - }, - [`& .${tablePaginationClasses.actions}`]: { - flexShrink: 0, - marginLeft: 20, - }, -})); - -const TablePaginationSpacer = styled('div', { - name: 'MuiTablePagination', - slot: 'Spacer', - overridesResolver: (props, styles) => styles.spacer, -})({ - flex: '1 1 100%', -}); - -const TablePaginationSelectLabel = styled('p', { - name: 'MuiTablePagination', - slot: 'SelectLabel', - overridesResolver: (props, styles) => styles.selectLabel, -})(({ theme }) => ({ - ...theme.typography.body2, - flexShrink: 0, -})); - -const TablePaginationSelect = styled(Select, { - name: 'MuiTablePagination', - slot: 'Select', - overridesResolver: (props, styles) => ({ - [`& .${tablePaginationClasses.selectIcon}`]: styles.selectIcon, - [`& .${tablePaginationClasses.select}`]: styles.select, - ...styles.input, - ...styles.selectRoot, - }), -})({ - color: 'inherit', - fontSize: 'inherit', - flexShrink: 0, - marginRight: 32, - marginLeft: 8, - [`& .${tablePaginationClasses.select}`]: { - paddingLeft: 8, - paddingRight: 24, - textAlign: 'right', - textAlignLast: 'right', // Align