diff --git a/docs/data/material/components/button-group/LoadingButtonGroup.js b/docs/data/material/components/button-group/LoadingButtonGroup.js
index 989f028daf7a56..fd146a90620d10 100644
--- a/docs/data/material/components/button-group/LoadingButtonGroup.js
+++ b/docs/data/material/components/button-group/LoadingButtonGroup.js
@@ -1,17 +1,16 @@
import * as React from 'react';
import ButtonGroup from '@mui/material/ButtonGroup';
import Button from '@mui/material/Button';
-import LoadingButton from '@mui/lab/LoadingButton';
import SaveIcon from '@mui/icons-material/Save';
export default function LoadingButtonGroup() {
return (
- Fetch data
- }>
+
+ }>
Save
-
+
);
}
diff --git a/docs/data/material/components/button-group/LoadingButtonGroup.tsx b/docs/data/material/components/button-group/LoadingButtonGroup.tsx
index 989f028daf7a56..fd146a90620d10 100644
--- a/docs/data/material/components/button-group/LoadingButtonGroup.tsx
+++ b/docs/data/material/components/button-group/LoadingButtonGroup.tsx
@@ -1,17 +1,16 @@
import * as React from 'react';
import ButtonGroup from '@mui/material/ButtonGroup';
import Button from '@mui/material/Button';
-import LoadingButton from '@mui/lab/LoadingButton';
import SaveIcon from '@mui/icons-material/Save';
export default function LoadingButtonGroup() {
return (
- Fetch data
- }>
+
+ }>
Save
-
+
);
}
diff --git a/docs/data/material/components/button-group/LoadingButtonGroup.tsx.preview b/docs/data/material/components/button-group/LoadingButtonGroup.tsx.preview
index 51360c91557385..a69903f1fca35c 100644
--- a/docs/data/material/components/button-group/LoadingButtonGroup.tsx.preview
+++ b/docs/data/material/components/button-group/LoadingButtonGroup.tsx.preview
@@ -1,7 +1,7 @@
- Fetch data
- }>
+
+ }>
Save
-
+
\ No newline at end of file
diff --git a/docs/data/material/components/button-group/button-group.md b/docs/data/material/components/button-group/button-group.md
index 6cc23f74925aec..5bceb0b6ab282b 100644
--- a/docs/data/material/components/button-group/button-group.md
+++ b/docs/data/material/components/button-group/button-group.md
@@ -1,7 +1,7 @@
---
productId: material-ui
title: React Button Group component
-components: Button, ButtonGroup, LoadingButton
+components: Button, ButtonGroup
githubLabel: 'component: ButtonGroup'
githubSource: packages/mui-material/src/ButtonGroup
---
@@ -49,10 +49,8 @@ You can remove the elevation with the `disableElevation` prop.
{{"demo": "DisableElevation.js"}}
-## Experimental APIs
+## Loading
-### Loading button
-
-You can use the [``](/material-ui/react-button/#loading-button) from [`@mui/lab`](/material-ui/about-the-lab/) in the button group.
+Use the `loading` prop from `Button` to set buttons in a loading state and disable interactions.
{{"demo": "LoadingButtonGroup.js"}}
diff --git a/docs/data/material/components/buttons/IconButtonWithBadge.js b/docs/data/material/components/buttons/IconButtonWithBadge.js
new file mode 100644
index 00000000000000..6a00cbd44b6180
--- /dev/null
+++ b/docs/data/material/components/buttons/IconButtonWithBadge.js
@@ -0,0 +1,37 @@
+import * as React from 'react';
+import { styled } from '@mui/material/styles';
+import IconButton from '@mui/material/IconButton';
+import Badge, { badgeClasses } from '@mui/material/Badge';
+import Stack from '@mui/material/Stack';
+import SaveIcon from '@mui/icons-material/Save';
+import ShoppingCartIcon from '@mui/icons-material/ShoppingCartOutlined';
+
+const CartBadge = styled(Badge)`
+ .${badgeClasses.badge} {
+ top: -12px;
+ right: -6px;
+ }
+`;
+
+export default function IconButtonWithBadge() {
+ const [loading, setLoading] = React.useState(false);
+ React.useEffect(() => {
+ const timeout = setTimeout(() => {
+ setLoading(false);
+ }, 2000);
+ return () => clearTimeout(timeout);
+ });
+ return (
+
+
+
+
+
+
+ setLoading(true)}>
+
+
+
+
+ );
+}
diff --git a/docs/data/material/components/buttons/IconButtonWithBadge.tsx b/docs/data/material/components/buttons/IconButtonWithBadge.tsx
new file mode 100644
index 00000000000000..6a00cbd44b6180
--- /dev/null
+++ b/docs/data/material/components/buttons/IconButtonWithBadge.tsx
@@ -0,0 +1,37 @@
+import * as React from 'react';
+import { styled } from '@mui/material/styles';
+import IconButton from '@mui/material/IconButton';
+import Badge, { badgeClasses } from '@mui/material/Badge';
+import Stack from '@mui/material/Stack';
+import SaveIcon from '@mui/icons-material/Save';
+import ShoppingCartIcon from '@mui/icons-material/ShoppingCartOutlined';
+
+const CartBadge = styled(Badge)`
+ .${badgeClasses.badge} {
+ top: -12px;
+ right: -6px;
+ }
+`;
+
+export default function IconButtonWithBadge() {
+ const [loading, setLoading] = React.useState(false);
+ React.useEffect(() => {
+ const timeout = setTimeout(() => {
+ setLoading(false);
+ }, 2000);
+ return () => clearTimeout(timeout);
+ });
+ return (
+
+
+
+
+
+
+ setLoading(true)}>
+
+
+
+
+ );
+}
diff --git a/docs/data/material/components/buttons/IconButtonWithBadge.tsx.preview b/docs/data/material/components/buttons/IconButtonWithBadge.tsx.preview
new file mode 100644
index 00000000000000..cfe404e63cb83d
--- /dev/null
+++ b/docs/data/material/components/buttons/IconButtonWithBadge.tsx.preview
@@ -0,0 +1,9 @@
+
+
+
+
+
+ setLoading(true)}>
+
+
+
\ No newline at end of file
diff --git a/docs/data/material/components/buttons/LoadingButtons.js b/docs/data/material/components/buttons/LoadingButtons.js
index f2d71b178ab98d..09d568b124c92d 100644
--- a/docs/data/material/components/buttons/LoadingButtons.js
+++ b/docs/data/material/components/buttons/LoadingButtons.js
@@ -1,25 +1,45 @@
import * as React from 'react';
-import LoadingButton from '@mui/lab/LoadingButton';
+import Button from '@mui/material/Button';
import SaveIcon from '@mui/icons-material/Save';
import Stack from '@mui/material/Stack';
export default function LoadingButtons() {
return (
-
-
- Submit
-
-
- Fetch data
-
-
+
+
+
+ }
+ variant="outlined"
+ >
+ Save
+
+
+ }
variant="outlined"
>
- Save
-
+ Full width
+
+ }
+ variant="outlined"
+ >
+ Full width
+
);
}
diff --git a/docs/data/material/components/buttons/LoadingButtons.tsx b/docs/data/material/components/buttons/LoadingButtons.tsx
index f2d71b178ab98d..09d568b124c92d 100644
--- a/docs/data/material/components/buttons/LoadingButtons.tsx
+++ b/docs/data/material/components/buttons/LoadingButtons.tsx
@@ -1,25 +1,45 @@
import * as React from 'react';
-import LoadingButton from '@mui/lab/LoadingButton';
+import Button from '@mui/material/Button';
import SaveIcon from '@mui/icons-material/Save';
import Stack from '@mui/material/Stack';
export default function LoadingButtons() {
return (
-
-
- Submit
-
-
- Fetch data
-
-
+
+
+
+ }
+ variant="outlined"
+ >
+ Save
+
+
+ }
variant="outlined"
>
- Save
-
+ Full width
+
+ }
+ variant="outlined"
+ >
+ Full width
+
);
}
diff --git a/docs/data/material/components/buttons/LoadingButtons.tsx.preview b/docs/data/material/components/buttons/LoadingButtons.tsx.preview
deleted file mode 100644
index 9578d91a245686..00000000000000
--- a/docs/data/material/components/buttons/LoadingButtons.tsx.preview
+++ /dev/null
@@ -1,14 +0,0 @@
-
- Submit
-
-
- Fetch data
-
-}
- variant="outlined"
->
- Save
-
\ No newline at end of file
diff --git a/docs/data/material/components/buttons/LoadingButtonsTransition.js b/docs/data/material/components/buttons/LoadingButtonsTransition.js
index 21b0f2bd331d26..2278b2684fe7b5 100644
--- a/docs/data/material/components/buttons/LoadingButtonsTransition.js
+++ b/docs/data/material/components/buttons/LoadingButtonsTransition.js
@@ -1,5 +1,5 @@
import * as React from 'react';
-import LoadingButton from '@mui/lab/LoadingButton';
+import Button from '@mui/material/Button';
import Box from '@mui/material/Box';
import FormControlLabel from '@mui/material/FormControlLabel';
import Switch from '@mui/material/Switch';
@@ -27,7 +27,7 @@ export default function LoadingButtonsTransition() {
label="Loading"
/>
button': { m: 1 } }}>
-
Disabled
-
-
+
-
+ }
@@ -54,8 +54,8 @@ export default function LoadingButtonsTransition() {
variant="contained"
>
Send
-
-
+
+
button': { m: 1 } }}>
-
+
-
+
-
+ }
loading={loading}
@@ -92,8 +87,8 @@ export default function LoadingButtonsTransition() {
variant="contained"
>
Send
-
-
+
+
);
diff --git a/docs/data/material/components/buttons/LoadingButtonsTransition.tsx b/docs/data/material/components/buttons/LoadingButtonsTransition.tsx
index 21b0f2bd331d26..2278b2684fe7b5 100644
--- a/docs/data/material/components/buttons/LoadingButtonsTransition.tsx
+++ b/docs/data/material/components/buttons/LoadingButtonsTransition.tsx
@@ -1,5 +1,5 @@
import * as React from 'react';
-import LoadingButton from '@mui/lab/LoadingButton';
+import Button from '@mui/material/Button';
import Box from '@mui/material/Box';
import FormControlLabel from '@mui/material/FormControlLabel';
import Switch from '@mui/material/Switch';
@@ -27,7 +27,7 @@ export default function LoadingButtonsTransition() {
label="Loading"
/>
button': { m: 1 } }}>
-
Disabled
-
-
+
-
+ }
@@ -54,8 +54,8 @@ export default function LoadingButtonsTransition() {
variant="contained"
>
Send
-
-
+
+
button': { m: 1 } }}>
-
+
-
+
-
+ }
loading={loading}
@@ -92,8 +87,8 @@ export default function LoadingButtonsTransition() {
variant="contained"
>
Send
-
-
+
+
);
diff --git a/docs/data/material/components/buttons/LoadingIconButton.js b/docs/data/material/components/buttons/LoadingIconButton.js
new file mode 100644
index 00000000000000..6778d7281d47d7
--- /dev/null
+++ b/docs/data/material/components/buttons/LoadingIconButton.js
@@ -0,0 +1,21 @@
+import * as React from 'react';
+import Tooltip from '@mui/material/Tooltip';
+import IconButton from '@mui/material/IconButton';
+import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';
+
+export default function LoadingIconButton() {
+ const [loading, setLoading] = React.useState(false);
+ React.useEffect(() => {
+ const timeout = setTimeout(() => {
+ setLoading(false);
+ }, 2000);
+ return () => clearTimeout(timeout);
+ });
+ return (
+
+ setLoading(true)} loading={loading}>
+
+
+
+ );
+}
diff --git a/docs/data/material/components/buttons/LoadingIconButton.tsx b/docs/data/material/components/buttons/LoadingIconButton.tsx
new file mode 100644
index 00000000000000..6778d7281d47d7
--- /dev/null
+++ b/docs/data/material/components/buttons/LoadingIconButton.tsx
@@ -0,0 +1,21 @@
+import * as React from 'react';
+import Tooltip from '@mui/material/Tooltip';
+import IconButton from '@mui/material/IconButton';
+import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';
+
+export default function LoadingIconButton() {
+ const [loading, setLoading] = React.useState(false);
+ React.useEffect(() => {
+ const timeout = setTimeout(() => {
+ setLoading(false);
+ }, 2000);
+ return () => clearTimeout(timeout);
+ });
+ return (
+
+ setLoading(true)} loading={loading}>
+
+
+
+ );
+}
diff --git a/docs/data/material/components/buttons/LoadingIconButton.tsx.preview b/docs/data/material/components/buttons/LoadingIconButton.tsx.preview
new file mode 100644
index 00000000000000..9c9a8b0cbf868a
--- /dev/null
+++ b/docs/data/material/components/buttons/LoadingIconButton.tsx.preview
@@ -0,0 +1,5 @@
+
+ setLoading(true)} loading={loading}>
+
+
+
\ No newline at end of file
diff --git a/docs/data/material/components/buttons/buttons.md b/docs/data/material/components/buttons/buttons.md
index 4da25aa7e55f5f..b2f86853e8fd08 100644
--- a/docs/data/material/components/buttons/buttons.md
+++ b/docs/data/material/components/buttons/buttons.md
@@ -1,7 +1,7 @@
---
productId: material-ui
title: React Button component
-components: Button, IconButton, ButtonBase, LoadingButton
+components: Button, IconButton, ButtonBase
materialDesign: https://m2.material.io/components/buttons
githubLabel: 'component: button'
waiAria: https://www.w3.org/WAI/ARIA/apg/patterns/button/
@@ -113,12 +113,34 @@ Use `color` prop to apply theme color palette to component.
{{"demo": "IconButtonColors.js"}}
+### Loading
+
+Use `loading` prop to set icon buttons in a loading state and disable interactions.
+
+{{"demo": "LoadingIconButton.js"}}
+
+### Badge
+
+You can use the [`Badge`](/material-ui/react-badge/) component to add a badge to an `IconButton`.
+
+{{"demo": "IconButtonWithBadge.js"}}
+
## File upload
To create a file upload button, turn the button into a label using `component="label"` and then create a visually-hidden input with type `file`.
{{"demo": "InputFileUpload.js"}}
+## Loading
+
+Use the `loading` prop to set buttons in a loading state and disable interactions.
+
+{{"demo": "LoadingButtons.js"}}
+
+Toggle the loading switch to see the transition between the different states.
+
+{{"demo": "LoadingButtonsTransition.js"}}
+
## Customization
Here are some examples of customizing the component.
@@ -174,15 +196,3 @@ However:
```
This has the advantage of supporting any element, for instance, a link `` element.
-
-## Experimental APIs
-
-### Loading button
-
-[`@mui/lab`](/material-ui/about-the-lab/) offers loading buttons that can show loading state and disable interactions.
-
-{{"demo": "LoadingButtons.js"}}
-
-Toggle the loading switch to see the transition between the different states.
-
-{{"demo": "LoadingButtonsTransition.js"}}
diff --git a/docs/data/material/components/dialogs/ToolpadDialogsNoSnap.js b/docs/data/material/components/dialogs/ToolpadDialogsNoSnap.js
index c51820532be8de..45c5878821cf2a 100644
--- a/docs/data/material/components/dialogs/ToolpadDialogsNoSnap.js
+++ b/docs/data/material/components/dialogs/ToolpadDialogsNoSnap.js
@@ -2,7 +2,6 @@ import * as React from 'react';
import PropTypes from 'prop-types';
import { DialogsProvider, useDialogs } from '@toolpad/core/useDialogs';
import Button from '@mui/material/Button';
-import LoadingButton from '@mui/lab/LoadingButton';
import Dialog from '@mui/material/Dialog';
import Alert from '@mui/material/Alert';
import DialogTitle from '@mui/material/DialogTitle';
@@ -99,13 +98,9 @@ function DemoContent() {
return (
);
diff --git a/docs/data/material/components/dialogs/ToolpadDialogsNoSnap.tsx b/docs/data/material/components/dialogs/ToolpadDialogsNoSnap.tsx
index 1fd85d043d9c2f..1d6aa7dacc434f 100644
--- a/docs/data/material/components/dialogs/ToolpadDialogsNoSnap.tsx
+++ b/docs/data/material/components/dialogs/ToolpadDialogsNoSnap.tsx
@@ -1,7 +1,6 @@
import * as React from 'react';
import { DialogsProvider, useDialogs, DialogProps } from '@toolpad/core/useDialogs';
import Button from '@mui/material/Button';
-import LoadingButton from '@mui/lab/LoadingButton';
import Dialog from '@mui/material/Dialog';
import Alert from '@mui/material/Alert';
import DialogTitle from '@mui/material/DialogTitle';
@@ -80,13 +79,9 @@ function DemoContent() {
return (
);
diff --git a/docs/data/material/migration/upgrade-to-v6/upgrade-to-v6.md b/docs/data/material/migration/upgrade-to-v6/upgrade-to-v6.md
index 00559f268f5d28..c4fcc9af9dd1e4 100644
--- a/docs/data/material/migration/upgrade-to-v6/upgrade-to-v6.md
+++ b/docs/data/material/migration/upgrade-to-v6/upgrade-to-v6.md
@@ -356,9 +356,21 @@ As the `ListItem` no longer supports these props, the class names related to the
+listItemButtonClasses.selected
```
-### Loading Button
+### Button with Loading State
-In v6, the `children` prop passed to the Loading Button component is now wrapped in a `` tag to avoid [issues](https://github.com/mui/material-ui/issues/27853) when using tools to translate websites.
+As of `@mui/material` **v6.2.0**, the `LoadingButton` from Lab has been removed. Loading functionality is now part of the standard `Button` component. Update your import as follows:
+
+```diff
+-import { LoadingButton } from '@mui/lab';
++import { Button } from '@mui/material';
+```
+
+```diff
+-import LoadingButton from '@mui/lab/LoadingButton';
++import Button from '@mui/material/Button';
+```
+
+For more details, see the [Loading section](/material-ui/react-button/#loading-2) in the [Material UI `Button` documentation](/material-ui/react-button/).
### Typography
diff --git a/docs/data/material/pagesApi.js b/docs/data/material/pagesApi.js
index 2c6f1e2aca5155..7c0e8e172b7b08 100644
--- a/docs/data/material/pagesApi.js
+++ b/docs/data/material/pagesApi.js
@@ -70,7 +70,6 @@ module.exports = [
{ pathname: '/material-ui/api/list-item-secondary-action' },
{ pathname: '/material-ui/api/list-item-text' },
{ pathname: '/material-ui/api/list-subheader' },
- { pathname: '/material-ui/api/loading-button' },
{ pathname: '/material-ui/api/masonry' },
{ pathname: '/material-ui/api/menu' },
{ pathname: '/material-ui/api/menu-item' },
diff --git a/docs/pages/blog/2020-q2-update.md b/docs/pages/blog/2020-q2-update.md
index 6d34a639194694..29f38b8eee2ea2 100644
--- a/docs/pages/blog/2020-q2-update.md
+++ b/docs/pages/blog/2020-q2-update.md
@@ -27,7 +27,7 @@ Here are the most significant improvements since March 2020:
Adobe XD and Framer support are also up for consideration if they attract a significant audience, but not until we've polished the Sketch and Figma assets.
-- 🔄 `LoadingButton` – [a new component in the lab](https://mui.com/material-ui/react-button/#loading-button). This work is influenced by the [concurrent UI patterns](https://17.reactjs.org/docs/concurrent-mode-patterns.html) presented by the React team.
+- 🔄 `LoadingButton` – [a new component in the lab](https://v5.mui.com/material-ui/react-button/#loading-button). This work is influenced by the [concurrent UI patterns](https://17.reactjs.org/docs/concurrent-mode-patterns.html) presented by the React team.
diff --git a/docs/pages/blog/mui-core-v5.md b/docs/pages/blog/mui-core-v5.md
index 08bca2d4844f2a..4a2e55c467a95c 100644
--- a/docs/pages/blog/mui-core-v5.md
+++ b/docs/pages/blog/mui-core-v5.md
@@ -602,7 +602,7 @@ Having a separate lab package allows us to release breaking changes when necessa
The following components are now available in the lab:
-- [LoadingButton](/material-ui/react-button/#loading-button). It does what you would expect. It renders the `Button` with a configurable loading/pending state.
+- [LoadingButton](https://v5.mui.com/material-ui/react-button/#loading-button). It does what you would expect. It renders the `Button` with a configurable loading/pending state.
- [FocusTrap](/base-ui/react-focus-trap/). This component traps the keyboard focus within a DOM node. For example, it's used by the Modal to prevent tabbing out of the component for accessibility reasons.
- [Masonry](/material-ui/react-masonry/). One great use case for this component is when using the `Grid` component leads to wasted space. It's frequently used in dashboards.
diff --git a/docs/pages/blog/mui-x-v8-alpha-zero.js b/docs/pages/blog/mui-x-v8-alpha-zero.js
new file mode 100644
index 00000000000000..18973d9dfc7b32
--- /dev/null
+++ b/docs/pages/blog/mui-x-v8-alpha-zero.js
@@ -0,0 +1,7 @@
+import * as React from 'react';
+import TopLayoutBlog from 'docs/src/modules/components/TopLayoutBlog';
+import { docs } from './mui-x-v8-alpha-zero.md?muiMarkdown';
+
+export default function Page() {
+ return ;
+}
diff --git a/docs/pages/blog/mui-x-v8-alpha-zero.md b/docs/pages/blog/mui-x-v8-alpha-zero.md
new file mode 100644
index 00000000000000..2b547337a539c6
--- /dev/null
+++ b/docs/pages/blog/mui-x-v8-alpha-zero.md
@@ -0,0 +1,99 @@
+---
+title: The road to MUI X v8
+description: Explore the planned features and our roadmap to the next major version.
+date: 2024-11-20T00:00:00.000Z
+authors: ['josefreitas']
+tags: ['MUI X', 'Product']
+---
+
+We're kicking off the development of [MUI X v8](https://github.com/mui/mui-x/releases/tag/v8.0.0-alpha.0).
+Following our yearly release cycle, the target for the first stable release is March 2025.
+This major update includes new versions of the Data Grid, Charts, Tree View, and the Date and Time Pickers.
+We're excited to share our roadmap with you and invite you to join us on this journey!
+
+:::warning
+Only MUI X is getting a new version—Material UI will remain in v6 for now.
+Since only MUI X is getting a new version, you can rest assured that all MUI X v8 components will be fully compatible with Material UI v5 and v6.
+::::
+
+## What's the plan to get to the next major release?
+
+Before the official release of MUI X v8, we'll go through a few months of pre-releases.
+During this period, we'll continue with our usual weekly release process and follow [semantic versioning](https://semver.org/).
+While we'll still release bug fixes for v7, these will become less frequent as we progress toward a stable v8 release.
+Our primary focus will be on delivering the new features and implementing the necessary breaking changes in the v8 pre-release packages.
+
+In the alpha phase, we'll introduce all planned breaking changes for this major version. Some APIs may be unstable—not in terms of functionality, but there may be adjustments or renaming of parameters. This phase is expected to last about two to three months.
+
+Following the alpha phase, the beta phase will focus on stabilizing the APIs, fixing bugs, and refining the overall experience. This phase is anticipated to take about one month.
+
+Finally, we'll release the first `v8.0.0` stable by March 2025, and we'll continue improving the components and adding features, but without making any new breaking changes until the next major version.
+
+## What happens to v7?
+
+During v8 pre-releases, v7 will continue to be the official current major, and it will remain supported during this time.
+However, we will only release new v7 versions as needed to patch bugs or add community contributions.
+
+All new features and enhancements will go to v8, and after its first stable release, v7 will officially transition to [long-term support](https://mui.com/x/introduction/support/#long-term-support-lts) status.
+
+## Where's the v8 documentation?
+
+As mentioned above, v7 is still the official version, so by default, the documentation shows v7 features and API.
+The next version's documentation is in the `v8` subdomain.
+
+- [https://next.mui.com/x/introduction/](https://next.mui.com/x/introduction/)
+
+## What's new in v8
+
+The following is a list of enhancements in the pipeline for v8.
+It contains the highlights we aim to include in the first versions:
+
+### Data Grid
+
+- [Drag and drop with touch support](https://github.com/mui/mui-x/issues/15385)
+- [New Toolbar](https://github.com/mui/mui-x/issues/11584)
+- [Row spanning](https://mui.com/x/react-data-grid/row-spanning/) (Now stable)
+- [Pivoting](https://github.com/mui/mui-x/issues/214) [](/x/introduction/licensing/#premium-plan 'Premium plan')
+
+### Date and Time Pickers
+
+- [Improved visual customization](https://github.com/mui/mui-x/issues/14753)
+- [Accessible DOM field by default](https://mui.com/x/react-date-pickers/fields/#accessible-dom-structure)
+- [Time Range Picker](https://github.com/mui/mui-x/issues/4460) [](/x/introduction/licensing/#pro-plan 'Pro plan')
+
+### Tree View
+
+- [Parent/chidren selection propagation](https://github.com/mui/mui-x/issues/12883)
+- [Lazy loading](https://github.com/mui/mui-x/issues/9687)[](/x/introduction/licensing/#pro-plan 'Pro plan')
+- [Virtualization](https://github.com/mui/mui-x/issues/9685)[](/x/introduction/licensing/#pro-plan 'Pro plan')
+
+### Charts
+
+- Improved design
+- [Radar Chart](https://github.com/mui/mui-x/issues/7925)
+- [Improved data zoom](https://github.com/mui/mui-x/issues/15383)[](/x/introduction/licensing/#pro-plan 'Pro plan')
+- [Funnel Chart](https://github.com/mui/mui-x/issues/7929)[](/x/introduction/licensing/#pro-plan 'Pro plan')
+
+:::info
+You can check our [roadmap](https://github.com/orgs/mui/projects/35) for the full live list.
+:::
+
+## How to migrate
+
+As usual, we've prepared a migration guide for each component, and we'll continuously update them as we make any breaking changes during the pre-releases.
+They list every breaking change you may need to update to migrate your codebase.
+
+- [Data Grid](https://next.mui.com/x/migration/migration-data-grid-v7/)
+- [Date and Time Pickers](https://next.mui.com/x/migration/migration-pickers-v7/)
+- [Charts](https://next.mui.com/x/migration/migration-charts-v7/)
+- [Tree View](https://next.mui.com/x/migration/migration-tree-view-v7/)
+
+We strongly encourage you to try the new version—it builds on the solid foundation we established with v7, bringing even more improvements!
+
+## How to get involved
+
+We'd love to hear about your expectations and pain points! Please consider [connecting with us](https://forms.gle/vsBv6CLPz9h57xg8A) for a user interview.
+You'll get an insider's perspective on the development, and you'll be able to help us iterate early on the new features.
+
+We're also happy to receive feedback on new features and bug reports in our [GitHub repository](https://github.com/mui/mui-x/issues/new/choose).
+You can follow every step of the v8 development process through our [changelog](https://github.com/mui/mui-x/releases).
diff --git a/docs/pages/material-ui/api/button.json b/docs/pages/material-ui/api/button.json
index 04a03e76d983d5..996ab60f6e6a63 100644
--- a/docs/pages/material-ui/api/button.json
+++ b/docs/pages/material-ui/api/button.json
@@ -17,6 +17,18 @@
"endIcon": { "type": { "name": "node" } },
"fullWidth": { "type": { "name": "bool" }, "default": "false" },
"href": { "type": { "name": "string" } },
+ "loading": { "type": { "name": "bool" }, "default": "false" },
+ "loadingIndicator": {
+ "type": { "name": "node" },
+ "default": ""
+ },
+ "loadingPosition": {
+ "type": {
+ "name": "enum",
+ "description": "'center'
| 'end'
| 'start'"
+ },
+ "default": "'center'"
+ },
"size": {
"type": {
"name": "union",
@@ -182,6 +194,12 @@
"description": "Styles applied to the endIcon element if supplied.",
"isGlobal": false
},
+ {
+ "key": "endIconLoadingEnd",
+ "className": "MuiButton-endIconLoadingEnd",
+ "description": "Styles applied to the endIcon element if `loading={true}` and `loadingPosition=\"end\"`.",
+ "isGlobal": false
+ },
{
"key": "focusVisible",
"className": "Mui-focusVisible",
@@ -221,6 +239,36 @@
"isGlobal": false,
"isDeprecated": true
},
+ {
+ "key": "loading",
+ "className": "MuiButton-loading",
+ "description": "Styles applied to the root element if `loading={true}`.",
+ "isGlobal": false
+ },
+ {
+ "key": "loadingIndicator",
+ "className": "MuiButton-loadingIndicator",
+ "description": "Styles applied to the loadingIndicator element.",
+ "isGlobal": false
+ },
+ {
+ "key": "loadingIndicatorCenter",
+ "className": "MuiButton-loadingIndicatorCenter",
+ "description": "Styles applied to the loadingIndicator element if `loadingPosition=\"center\"`.",
+ "isGlobal": false
+ },
+ {
+ "key": "loadingIndicatorEnd",
+ "className": "MuiButton-loadingIndicatorEnd",
+ "description": "Styles applied to the loadingIndicator element if `loadingPosition=\"end\"`.",
+ "isGlobal": false
+ },
+ {
+ "key": "loadingIndicatorStart",
+ "className": "MuiButton-loadingIndicatorStart",
+ "description": "Styles applied to the loadingIndicator element if `loadingPosition=\"start\"`.",
+ "isGlobal": false
+ },
{
"key": "outlined",
"className": "MuiButton-outlined",
@@ -327,6 +375,12 @@
"description": "Styles applied to the startIcon element if supplied.",
"isGlobal": false
},
+ {
+ "key": "startIconLoadingStart",
+ "className": "MuiButton-startIconLoadingStart",
+ "description": "Styles applied to the startIcon element if `loading={true}` and `loadingPosition=\"start\"`.",
+ "isGlobal": false
+ },
{
"key": "text",
"className": "MuiButton-text",
diff --git a/docs/pages/material-ui/api/icon-button.json b/docs/pages/material-ui/api/icon-button.json
index 752030e32dcce9..09cb6b9e7dc49e 100644
--- a/docs/pages/material-ui/api/icon-button.json
+++ b/docs/pages/material-ui/api/icon-button.json
@@ -19,6 +19,11 @@
},
"default": "false"
},
+ "loading": { "type": { "name": "bool" }, "default": "false" },
+ "loadingIndicator": {
+ "type": { "name": "node" },
+ "default": ""
+ },
"size": {
"type": {
"name": "union",
@@ -100,6 +105,18 @@
"description": "Styles applied to the root element if `edge=\"start\"`.",
"isGlobal": false
},
+ {
+ "key": "loading",
+ "className": "MuiIconButton-loading",
+ "description": "Styles applied to the root element if `loading={true}`.",
+ "isGlobal": false
+ },
+ {
+ "key": "loadingIndicator",
+ "className": "MuiIconButton-loadingIndicator",
+ "description": "Styles applied to the loadingIndicator element.",
+ "isGlobal": false
+ },
{
"key": "root",
"className": "MuiIconButton-root",
diff --git a/docs/pages/material-ui/api/loading-button.js b/docs/pages/material-ui/api/loading-button.js
deleted file mode 100644
index cab581691ab572..00000000000000
--- a/docs/pages/material-ui/api/loading-button.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import * as React from 'react';
-import ApiPage from 'docs/src/modules/components/ApiPage';
-import mapApiPageTranslations from 'docs/src/modules/utils/mapApiPageTranslations';
-import jsonPageContent from './loading-button.json';
-
-export default function Page(props) {
- const { descriptions, pageContent } = props;
- return ;
-}
-
-Page.getInitialProps = () => {
- const req = require.context(
- 'docs/translations/api-docs/loading-button',
- false,
- /\.\/loading-button.*.json$/,
- );
- const descriptions = mapApiPageTranslations(req);
-
- return {
- descriptions,
- pageContent: jsonPageContent,
- };
-};
diff --git a/docs/pages/material-ui/api/loading-button.json b/docs/pages/material-ui/api/loading-button.json
deleted file mode 100644
index 8155927a65f266..00000000000000
--- a/docs/pages/material-ui/api/loading-button.json
+++ /dev/null
@@ -1,453 +0,0 @@
-{
- "props": {
- "children": { "type": { "name": "node" } },
- "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } },
- "disabled": { "type": { "name": "bool" }, "default": "false" },
- "loading": { "type": { "name": "bool" }, "default": "false" },
- "loadingIndicator": {
- "type": { "name": "node" },
- "default": ""
- },
- "loadingPosition": {
- "type": {
- "name": "custom",
- "description": "'start'
| 'end'
| 'center'"
- },
- "default": "'center'"
- },
- "sx": {
- "type": {
- "name": "union",
- "description": "Array<func
| object
| bool>
| func
| object"
- },
- "additionalInfo": { "sx": true }
- },
- "variant": {
- "type": {
- "name": "union",
- "description": "'contained'
| 'outlined'
| 'text'
| string"
- },
- "default": "'text'"
- }
- },
- "name": "LoadingButton",
- "imports": [
- "import LoadingButton from '@mui/lab/LoadingButton';",
- "import { LoadingButton } from '@mui/lab';"
- ],
- "classes": [
- {
- "key": "colorError",
- "className": "MuiLoadingButton-colorError",
- "description": "Styles applied to the root element if `color=\"error\"`.",
- "isGlobal": false
- },
- {
- "key": "colorInfo",
- "className": "MuiLoadingButton-colorInfo",
- "description": "Styles applied to the root element if `color=\"info\"`.",
- "isGlobal": false
- },
- {
- "key": "colorInherit",
- "className": "MuiLoadingButton-colorInherit",
- "description": "Styles applied to the root element if `color=\"inherit\"`.",
- "isGlobal": false
- },
- {
- "key": "colorPrimary",
- "className": "MuiLoadingButton-colorPrimary",
- "description": "Styles applied to the root element if `color=\"primary\"`.",
- "isGlobal": false
- },
- {
- "key": "colorSecondary",
- "className": "MuiLoadingButton-colorSecondary",
- "description": "Styles applied to the root element if `color=\"secondary\"`.",
- "isGlobal": false
- },
- {
- "key": "colorSuccess",
- "className": "MuiLoadingButton-colorSuccess",
- "description": "Styles applied to the root element if `color=\"success\"`.",
- "isGlobal": false
- },
- {
- "key": "colorWarning",
- "className": "MuiLoadingButton-colorWarning",
- "description": "Styles applied to the root element if `color=\"warning\"`.",
- "isGlobal": false
- },
- {
- "key": "contained",
- "className": "MuiLoadingButton-contained",
- "description": "Styles applied to the root element if `variant=\"contained\"`.",
- "isGlobal": false
- },
- {
- "key": "containedError",
- "className": "MuiLoadingButton-containedError",
- "description": "Styles applied to the root element if `variant=\"contained\"` and `color=\"error\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "containedInfo",
- "className": "MuiLoadingButton-containedInfo",
- "description": "Styles applied to the root element if `variant=\"contained\"` and `color=\"info\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "containedInherit",
- "className": "MuiLoadingButton-containedInherit",
- "description": "Styles applied to the root element if `variant=\"contained\"` and `color=\"inherit\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "containedPrimary",
- "className": "MuiLoadingButton-containedPrimary",
- "description": "Styles applied to the root element if `variant=\"contained\"` and `color=\"primary\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "containedSecondary",
- "className": "MuiLoadingButton-containedSecondary",
- "description": "Styles applied to the root element if `variant=\"contained\"` and `color=\"secondary\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "containedSizeLarge",
- "className": "MuiLoadingButton-containedSizeLarge",
- "description": "Styles applied to the root element if `size=\"large\"` and `variant=\"contained\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "containedSizeMedium",
- "className": "MuiLoadingButton-containedSizeMedium",
- "description": "Styles applied to the root element if `size=\"medium\"` and `variant=\"contained\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "containedSizeSmall",
- "className": "MuiLoadingButton-containedSizeSmall",
- "description": "Styles applied to the root element if `size=\"small\"` and `variant=\"contained\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "containedSuccess",
- "className": "MuiLoadingButton-containedSuccess",
- "description": "Styles applied to the root element if `variant=\"contained\"` and `color=\"success\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "containedWarning",
- "className": "MuiLoadingButton-containedWarning",
- "description": "Styles applied to the root element if `variant=\"contained\"` and `color=\"warning\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "disabled",
- "className": "Mui-disabled",
- "description": "State class applied to the root element if `disabled={true}`.",
- "isGlobal": true
- },
- {
- "key": "disableElevation",
- "className": "MuiLoadingButton-disableElevation",
- "description": "Styles applied to the root element if `disableElevation={true}`.",
- "isGlobal": false
- },
- {
- "key": "endIcon",
- "className": "MuiLoadingButton-endIcon",
- "description": "Styles applied to the endIcon element if supplied.",
- "isGlobal": false
- },
- {
- "key": "endIconLoadingEnd",
- "className": "MuiLoadingButton-endIconLoadingEnd",
- "description": "Styles applied to the endIcon element if `loading={true}` and `loadingPosition=\"end\"`.",
- "isGlobal": false
- },
- {
- "key": "focusVisible",
- "className": "Mui-focusVisible",
- "description": "State class applied to the ButtonBase root element if the button is keyboard focused.",
- "isGlobal": true
- },
- {
- "key": "fullWidth",
- "className": "MuiLoadingButton-fullWidth",
- "description": "Styles applied to the root element if `fullWidth={true}`.",
- "isGlobal": false
- },
- {
- "key": "icon",
- "className": "MuiLoadingButton-icon",
- "description": "Styles applied to the icon element if supplied",
- "isGlobal": false
- },
- {
- "key": "iconSizeLarge",
- "className": "MuiLoadingButton-iconSizeLarge",
- "description": "Styles applied to the icon element if supplied and `size=\"large\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "iconSizeMedium",
- "className": "MuiLoadingButton-iconSizeMedium",
- "description": "Styles applied to the icon element if supplied and `size=\"medium\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "iconSizeSmall",
- "className": "MuiLoadingButton-iconSizeSmall",
- "description": "Styles applied to the icon element if supplied and `size=\"small\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "label",
- "className": "MuiLoadingButton-label",
- "description": "Styles applied to the span element that wraps the children.",
- "isGlobal": false
- },
- {
- "key": "loading",
- "className": "MuiLoadingButton-loading",
- "description": "Styles applied to the root element if `loading={true}`.",
- "isGlobal": false
- },
- {
- "key": "loadingIndicator",
- "className": "MuiLoadingButton-loadingIndicator",
- "description": "Styles applied to the loadingIndicator element.",
- "isGlobal": false
- },
- {
- "key": "loadingIndicatorCenter",
- "className": "MuiLoadingButton-loadingIndicatorCenter",
- "description": "Styles applied to the loadingIndicator element if `loadingPosition=\"center\"`.",
- "isGlobal": false
- },
- {
- "key": "loadingIndicatorEnd",
- "className": "MuiLoadingButton-loadingIndicatorEnd",
- "description": "Styles applied to the loadingIndicator element if `loadingPosition=\"end\"`.",
- "isGlobal": false
- },
- {
- "key": "loadingIndicatorStart",
- "className": "MuiLoadingButton-loadingIndicatorStart",
- "description": "Styles applied to the loadingIndicator element if `loadingPosition=\"start\"`.",
- "isGlobal": false
- },
- {
- "key": "outlined",
- "className": "MuiLoadingButton-outlined",
- "description": "Styles applied to the root element if `variant=\"outlined\"`.",
- "isGlobal": false
- },
- {
- "key": "outlinedError",
- "className": "MuiLoadingButton-outlinedError",
- "description": "Styles applied to the root element if `variant=\"outlined\"` and `color=\"error\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "outlinedInfo",
- "className": "MuiLoadingButton-outlinedInfo",
- "description": "Styles applied to the root element if `variant=\"outlined\"` and `color=\"info\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "outlinedInherit",
- "className": "MuiLoadingButton-outlinedInherit",
- "description": "Styles applied to the root element if `variant=\"outlined\"` and `color=\"inherit\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "outlinedPrimary",
- "className": "MuiLoadingButton-outlinedPrimary",
- "description": "Styles applied to the root element if `variant=\"outlined\"` and `color=\"primary\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "outlinedSecondary",
- "className": "MuiLoadingButton-outlinedSecondary",
- "description": "Styles applied to the root element if `variant=\"outlined\"` and `color=\"secondary\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "outlinedSizeLarge",
- "className": "MuiLoadingButton-outlinedSizeLarge",
- "description": "Styles applied to the root element if `size=\"large\"` and `variant=\"outlined\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "outlinedSizeMedium",
- "className": "MuiLoadingButton-outlinedSizeMedium",
- "description": "Styles applied to the root element if `size=\"medium\"` and `variant=\"outlined\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "outlinedSizeSmall",
- "className": "MuiLoadingButton-outlinedSizeSmall",
- "description": "Styles applied to the root element if `size=\"small\"` and `variant=\"outlined\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "outlinedSuccess",
- "className": "MuiLoadingButton-outlinedSuccess",
- "description": "Styles applied to the root element if `variant=\"outlined\"` and `color=\"success\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "outlinedWarning",
- "className": "MuiLoadingButton-outlinedWarning",
- "description": "Styles applied to the root element if `variant=\"outlined\"` and `color=\"warning\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "root",
- "className": "MuiLoadingButton-root",
- "description": "Styles applied to the root element.",
- "isGlobal": false
- },
- {
- "key": "sizeLarge",
- "className": "MuiLoadingButton-sizeLarge",
- "description": "Styles applied to the root element if `size=\"large\"`.",
- "isGlobal": false
- },
- {
- "key": "sizeMedium",
- "className": "MuiLoadingButton-sizeMedium",
- "description": "Styles applied to the root element if `size=\"medium\"`.",
- "isGlobal": false
- },
- {
- "key": "sizeSmall",
- "className": "MuiLoadingButton-sizeSmall",
- "description": "Styles applied to the root element if `size=\"small\"`.",
- "isGlobal": false
- },
- {
- "key": "startIcon",
- "className": "MuiLoadingButton-startIcon",
- "description": "Styles applied to the startIcon element if supplied.",
- "isGlobal": false
- },
- {
- "key": "startIconLoadingStart",
- "className": "MuiLoadingButton-startIconLoadingStart",
- "description": "Styles applied to the startIcon element if `loading={true}` and `loadingPosition=\"start\"`.",
- "isGlobal": false
- },
- {
- "key": "text",
- "className": "MuiLoadingButton-text",
- "description": "Styles applied to the root element if `variant=\"text\"`.",
- "isGlobal": false
- },
- {
- "key": "textError",
- "className": "MuiLoadingButton-textError",
- "description": "Styles applied to the root element if `variant=\"text\"` and `color=\"error\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "textInfo",
- "className": "MuiLoadingButton-textInfo",
- "description": "Styles applied to the root element if `variant=\"text\"` and `color=\"info\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "textInherit",
- "className": "MuiLoadingButton-textInherit",
- "description": "Styles applied to the root element if `variant=\"text\"` and `color=\"inherit\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "textPrimary",
- "className": "MuiLoadingButton-textPrimary",
- "description": "Styles applied to the root element if `variant=\"text\"` and `color=\"primary\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "textSecondary",
- "className": "MuiLoadingButton-textSecondary",
- "description": "Styles applied to the root element if `variant=\"text\"` and `color=\"secondary\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "textSizeLarge",
- "className": "MuiLoadingButton-textSizeLarge",
- "description": "Styles applied to the root element if `size=\"large\"` and `variant=\"text\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "textSizeMedium",
- "className": "MuiLoadingButton-textSizeMedium",
- "description": "Styles applied to the root element if `size=\"medium\"` and `variant=\"text\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "textSizeSmall",
- "className": "MuiLoadingButton-textSizeSmall",
- "description": "Styles applied to the root element if `size=\"small\"` and `variant=\"text\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "textSuccess",
- "className": "MuiLoadingButton-textSuccess",
- "description": "Styles applied to the root element if `variant=\"text\"` and `color=\"success\"`.",
- "isGlobal": false,
- "isDeprecated": true
- },
- {
- "key": "textWarning",
- "className": "MuiLoadingButton-textWarning",
- "description": "Styles applied to the root element if `variant=\"text\"` and `color=\"warning\"`.",
- "isGlobal": false,
- "isDeprecated": true
- }
- ],
- "spread": true,
- "themeDefaultProps": true,
- "muiName": "MuiLoadingButton",
- "forwardsRefTo": "HTMLButtonElement",
- "filename": "/packages/mui-lab/src/LoadingButton/LoadingButton.js",
- "inheritance": { "component": "Button", "pathname": "/material-ui/api/button/" },
- "demos": "",
- "cssComponent": false
-}
diff --git a/docs/pages/material-ui/api/step-icon.json b/docs/pages/material-ui/api/step-icon.json
index 82d908380447bb..16d31b1020e608 100644
--- a/docs/pages/material-ui/api/step-icon.json
+++ b/docs/pages/material-ui/api/step-icon.json
@@ -55,7 +55,7 @@
"muiName": "MuiStepIcon",
"forwardsRefTo": "SVGSVGElement",
"filename": "/packages/mui-material/src/StepIcon/StepIcon.js",
- "inheritance": null,
+ "inheritance": { "component": "SvgIcon", "pathname": "/material-ui/api/svg-icon/" },
"demos": "",
"cssComponent": false
}
diff --git a/docs/src/pagesApi.js b/docs/src/pagesApi.js
index c9829209aec475..8c040f448a4d23 100644
--- a/docs/src/pagesApi.js
+++ b/docs/src/pagesApi.js
@@ -67,7 +67,6 @@ module.exports = [
{ pathname: '/api-docs/list-item-secondary-action' },
{ pathname: '/api-docs/list-item-text' },
{ pathname: '/api-docs/list-subheader' },
- { pathname: '/api-docs/loading-button' },
{ pathname: '/api-docs/masonry' },
{ pathname: '/api-docs/menu' },
{ pathname: '/api-docs/menu-item' },
diff --git a/docs/translations/api-docs/button/button.json b/docs/translations/api-docs/button/button.json
index 1e9426f65ac3de..dac8f6e775b631 100644
--- a/docs/translations/api-docs/button/button.json
+++ b/docs/translations/api-docs/button/button.json
@@ -24,6 +24,15 @@
"href": {
"description": "The URL to link to when the button is clicked. If defined, an a
element will be used as the root node."
},
+ "loading": {
+ "description": "If true
, the loading indicator is shown and the button becomes disabled."
+ },
+ "loadingIndicator": {
+ "description": "Element placed before the children if the button is in loading state. The node should contain an element with role="progressbar"
with an accessible name. By default we render a CircularProgress
that is labelled by the button itself."
+ },
+ "loadingPosition": {
+ "description": "The loading indicator can be positioned on the start, end, or the center of the button."
+ },
"size": {
"description": "The size of the component. small
is equivalent to the dense button styling."
},
@@ -149,6 +158,11 @@
"nodeName": "the endIcon element",
"conditions": "supplied"
},
+ "endIconLoadingEnd": {
+ "description": "Styles applied to {{nodeName}} if {{conditions}}.",
+ "nodeName": "the endIcon element",
+ "conditions": "loading={true}
and loadingPosition=\"end\"
"
+ },
"focusVisible": {
"description": "State class applied to {{nodeName}} if {{conditions}}.",
"nodeName": "the ButtonBase root element",
@@ -178,6 +192,30 @@
"conditions": "supplied and size=\"small\"
",
"deprecationInfo": "Combine the .MuiButton-icon and .MuiButtonSizeSmall classes instead. See Migrating from deprecated APIs for more details."
},
+ "loading": {
+ "description": "Styles applied to {{nodeName}} if {{conditions}}.",
+ "nodeName": "the root element",
+ "conditions": "loading={true}
"
+ },
+ "loadingIndicator": {
+ "description": "Styles applied to {{nodeName}}.",
+ "nodeName": "the loadingIndicator element"
+ },
+ "loadingIndicatorCenter": {
+ "description": "Styles applied to {{nodeName}} if {{conditions}}.",
+ "nodeName": "the loadingIndicator element",
+ "conditions": "loadingPosition=\"center\"
"
+ },
+ "loadingIndicatorEnd": {
+ "description": "Styles applied to {{nodeName}} if {{conditions}}.",
+ "nodeName": "the loadingIndicator element",
+ "conditions": "loadingPosition=\"end\"
"
+ },
+ "loadingIndicatorStart": {
+ "description": "Styles applied to {{nodeName}} if {{conditions}}.",
+ "nodeName": "the loadingIndicator element",
+ "conditions": "loadingPosition=\"start\"
"
+ },
"outlined": {
"description": "Styles applied to {{nodeName}} if {{conditions}}.",
"nodeName": "the root element",
@@ -264,6 +302,11 @@
"nodeName": "the startIcon element",
"conditions": "supplied"
},
+ "startIconLoadingStart": {
+ "description": "Styles applied to {{nodeName}} if {{conditions}}.",
+ "nodeName": "the startIcon element",
+ "conditions": "loading={true}
and loadingPosition=\"start\"
"
+ },
"text": {
"description": "Styles applied to {{nodeName}} if {{conditions}}.",
"nodeName": "the root element",
diff --git a/docs/translations/api-docs/icon-button/icon-button.json b/docs/translations/api-docs/icon-button/icon-button.json
index 76558bfba72680..03aeb7ec720d0c 100644
--- a/docs/translations/api-docs/icon-button/icon-button.json
+++ b/docs/translations/api-docs/icon-button/icon-button.json
@@ -16,6 +16,12 @@
"edge": {
"description": "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)."
},
+ "loading": {
+ "description": "If true
, the loading indicator is shown and the button becomes disabled."
+ },
+ "loadingIndicator": {
+ "description": "Element placed before the children if the button is in loading state. The node should contain an element with role="progressbar"
with an accessible name. By default we render a CircularProgress
that is labelled by the button itself."
+ },
"size": {
"description": "The size of the component. small
is equivalent to the dense button styling."
},
@@ -74,6 +80,15 @@
"nodeName": "the root element",
"conditions": "edge=\"start\"
"
},
+ "loading": {
+ "description": "Styles applied to {{nodeName}} if {{conditions}}.",
+ "nodeName": "the root element",
+ "conditions": "loading={true}
"
+ },
+ "loadingIndicator": {
+ "description": "Styles applied to {{nodeName}}.",
+ "nodeName": "the loadingIndicator element"
+ },
"root": { "description": "Styles applied to the root element." },
"sizeLarge": {
"description": "Styles applied to {{nodeName}} if {{conditions}}.",
diff --git a/docs/translations/api-docs/loading-button/loading-button.json b/docs/translations/api-docs/loading-button/loading-button.json
deleted file mode 100644
index 9babb1623d14c9..00000000000000
--- a/docs/translations/api-docs/loading-button/loading-button.json
+++ /dev/null
@@ -1,356 +0,0 @@
-{
- "componentDescription": "",
- "propDescriptions": {
- "children": { "description": "The content of the component." },
- "classes": { "description": "Override or extend the styles applied to the component." },
- "disabled": { "description": "If true
, the component is disabled." },
- "loading": {
- "description": "If true
, the loading indicator is shown and the button becomes disabled."
- },
- "loadingIndicator": {
- "description": "Element placed before the children if the button is in loading state. The node should contain an element with role="progressbar"
with an accessible name. By default we render a CircularProgress
that is labelled by the button itself."
- },
- "loadingPosition": {
- "description": "The loading indicator can be positioned on the start, end, or the center of the button."
- },
- "sx": {
- "description": "The system prop that allows defining system overrides as well as additional CSS styles."
- },
- "variant": { "description": "The variant to use." }
- },
- "classDescriptions": {
- "colorError": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "color=\"error\"
"
- },
- "colorInfo": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "color=\"info\"
"
- },
- "colorInherit": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "color=\"inherit\"
"
- },
- "colorPrimary": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "color=\"primary\"
"
- },
- "colorSecondary": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "color=\"secondary\"
"
- },
- "colorSuccess": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "color=\"success\"
"
- },
- "colorWarning": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "color=\"warning\"
"
- },
- "contained": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "variant=\"contained\"
"
- },
- "containedError": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "variant=\"contained\"
and color=\"error\"
",
- "deprecationInfo": "Combine the .MuiButton-contained and .MuiButton-colorError classes instead. See Migrating from deprecated APIs for more details."
- },
- "containedInfo": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "variant=\"contained\"
and color=\"info\"
",
- "deprecationInfo": "Combine the .MuiButton-contained and .MuiButton-colorInfo classes instead. See Migrating from deprecated APIs for more details."
- },
- "containedInherit": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "variant=\"contained\"
and color=\"inherit\"
",
- "deprecationInfo": "Combine the .MuiButton-contained and .MuiButton-colorInherit classes instead. See Migrating from deprecated APIs for more details."
- },
- "containedPrimary": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "variant=\"contained\"
and color=\"primary\"
",
- "deprecationInfo": "Combine the .MuiButton-contained and .MuiButton-colorPrimary classes instead. See Migrating from deprecated APIs for more details."
- },
- "containedSecondary": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "variant=\"contained\"
and color=\"secondary\"
",
- "deprecationInfo": "Combine the .MuiButton-contained and .MuiButton-colorSecondary classes instead. See Migrating from deprecated APIs for more details."
- },
- "containedSizeLarge": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "size=\"large\"
and variant=\"contained\"
",
- "deprecationInfo": "Combine the .MuiButton-sizeLarge and .MuiButton-contained classes instead. See Migrating from deprecated APIs for more details."
- },
- "containedSizeMedium": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "size=\"medium\"
and variant=\"contained\"
",
- "deprecationInfo": "Combine the .MuiButton-sizeMedium and .MuiButton-contained classes instead. See Migrating from deprecated APIs for more details."
- },
- "containedSizeSmall": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "size=\"small\"
and variant=\"contained\"
",
- "deprecationInfo": "Combine the .MuiButton-sizeSmall and .MuiButton-contained classes instead. See Migrating from deprecated APIs for more details."
- },
- "containedSuccess": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "variant=\"contained\"
and color=\"success\"
",
- "deprecationInfo": "Combine the .MuiButton-contained and .MuiButton-colorSuccess classes instead. See Migrating from deprecated APIs for more details."
- },
- "containedWarning": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "variant=\"contained\"
and color=\"warning\"
",
- "deprecationInfo": "Combine the .MuiButton-contained and .MuiButton-colorWarning classes instead. See Migrating from deprecated APIs for more details."
- },
- "disabled": {
- "description": "State class applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "disabled={true}
"
- },
- "disableElevation": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "disableElevation={true}
"
- },
- "endIcon": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the endIcon element",
- "conditions": "supplied"
- },
- "endIconLoadingEnd": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the endIcon element",
- "conditions": "loading={true}
and loadingPosition=\"end\"
"
- },
- "focusVisible": {
- "description": "State class applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the ButtonBase root element",
- "conditions": "the button is keyboard focused"
- },
- "fullWidth": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "fullWidth={true}
"
- },
- "icon": { "description": "Styles applied to the icon element if supplied" },
- "iconSizeLarge": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the icon element",
- "conditions": "supplied and size=\"large\"
",
- "deprecationInfo": "Combine the .MuiButton-icon and .MuiButtonSizeLarge classes instead. See Migrating from deprecated APIs for more details."
- },
- "iconSizeMedium": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the icon element",
- "conditions": "supplied and size=\"medium\"
",
- "deprecationInfo": "Combine the .MuiButton-icon and .MuiButtonSizeMedium classes instead. See Migrating from deprecated APIs for more details."
- },
- "iconSizeSmall": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the icon element",
- "conditions": "supplied and size=\"small\"
",
- "deprecationInfo": "Combine the .MuiButton-icon and .MuiButtonSizeSmall classes instead. See Migrating from deprecated APIs for more details."
- },
- "label": {
- "description": "Styles applied to {{nodeName}}.",
- "nodeName": "the span element that wraps the children"
- },
- "loading": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "loading={true}
"
- },
- "loadingIndicator": {
- "description": "Styles applied to {{nodeName}}.",
- "nodeName": "the loadingIndicator element"
- },
- "loadingIndicatorCenter": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the loadingIndicator element",
- "conditions": "loadingPosition=\"center\"
"
- },
- "loadingIndicatorEnd": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the loadingIndicator element",
- "conditions": "loadingPosition=\"end\"
"
- },
- "loadingIndicatorStart": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the loadingIndicator element",
- "conditions": "loadingPosition=\"start\"
"
- },
- "outlined": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "variant=\"outlined\"
"
- },
- "outlinedError": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "variant=\"outlined\"
and color=\"error\"
",
- "deprecationInfo": "Combine the .MuiButton-outlined and .MuiButton-colorError classes instead. See Migrating from deprecated APIs for more details."
- },
- "outlinedInfo": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "variant=\"outlined\"
and color=\"info\"
",
- "deprecationInfo": "Combine the .MuiButton-outlined and .MuiButton-colorInfo classes instead. See Migrating from deprecated APIs for more details."
- },
- "outlinedInherit": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "variant=\"outlined\"
and color=\"inherit\"
",
- "deprecationInfo": "Combine the .MuiButton-outlined and .MuiButton-colorInherit classes instead. See Migrating from deprecated APIs for more details."
- },
- "outlinedPrimary": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "variant=\"outlined\"
and color=\"primary\"
",
- "deprecationInfo": "Combine the .MuiButton-outlined and .MuiButton-colorPrimary classes instead. See Migrating from deprecated APIs for more details."
- },
- "outlinedSecondary": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "variant=\"outlined\"
and color=\"secondary\"
",
- "deprecationInfo": "Combine the .MuiButton-outlined and .MuiButton-colorSecondary classes instead. See Migrating from deprecated APIs for more details."
- },
- "outlinedSizeLarge": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "size=\"large\"
and variant=\"outlined\"
",
- "deprecationInfo": "Combine the .MuiButton-sizeLarge and .MuiButton-outlined classes instead. See Migrating from deprecated APIs for more details."
- },
- "outlinedSizeMedium": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "size=\"medium\"
and variant=\"outlined\"
",
- "deprecationInfo": "Combine the .MuiButton-sizeMedium and .MuiButton-outlined classes instead. See Migrating from deprecated APIs for more details."
- },
- "outlinedSizeSmall": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "size=\"small\"
and variant=\"outlined\"
",
- "deprecationInfo": "Combine the .MuiButton-sizeSmall and .MuiButton-outlined classes instead. See Migrating from deprecated APIs for more details."
- },
- "outlinedSuccess": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "variant=\"outlined\"
and color=\"success\"
",
- "deprecationInfo": "Combine the .MuiButton-outlined and .MuiButton-colorSuccess classes instead. See Migrating from deprecated APIs for more details."
- },
- "outlinedWarning": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "variant=\"outlined\"
and color=\"warning\"
",
- "deprecationInfo": "Combine the .MuiButton-outlined and .MuiButton-colorWarning classes instead. See Migrating from deprecated APIs for more details."
- },
- "root": { "description": "Styles applied to the root element." },
- "sizeLarge": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "size=\"large\"
"
- },
- "sizeMedium": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "size=\"medium\"
"
- },
- "sizeSmall": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "size=\"small\"
"
- },
- "startIcon": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the startIcon element",
- "conditions": "supplied"
- },
- "startIconLoadingStart": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the startIcon element",
- "conditions": "loading={true}
and loadingPosition=\"start\"
"
- },
- "text": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "variant=\"text\"
"
- },
- "textError": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "variant=\"text\"
and color=\"error\"
",
- "deprecationInfo": "Combine the .MuiButton-text and .MuiButton-colorError classes instead. See Migrating from deprecated APIs for more details."
- },
- "textInfo": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "variant=\"text\"
and color=\"info\"
",
- "deprecationInfo": "Combine the .MuiButton-text and .MuiButton-colorInfo classes instead. See Migrating from deprecated APIs for more details."
- },
- "textInherit": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "variant=\"text\"
and color=\"inherit\"
",
- "deprecationInfo": "Combine the .MuiButton-text and .MuiButton-colorInherit classes instead. See Migrating from deprecated APIs for more details."
- },
- "textPrimary": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "variant=\"text\"
and color=\"primary\"
",
- "deprecationInfo": "Combine the .MuiButton-text and .MuiButton-colorPrimary classes instead. See Migrating from deprecated APIs for more details."
- },
- "textSecondary": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "variant=\"text\"
and color=\"secondary\"
",
- "deprecationInfo": "Combine the .MuiButton-text and .MuiButton-colorSecondary classes instead. See Migrating from deprecated APIs for more details."
- },
- "textSizeLarge": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "size=\"large\"
and variant=\"text\"
",
- "deprecationInfo": "Combine the .MuiButton-sizeLarge and .MuiButton-text classes instead. See Migrating from deprecated APIs for more details."
- },
- "textSizeMedium": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "size=\"medium\"
and variant=\"text\"
",
- "deprecationInfo": "Combine the .MuiButton-sizeMedium and .MuiButton-text classes instead. See Migrating from deprecated APIs for more details."
- },
- "textSizeSmall": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "size=\"small\"
and variant=\"text\"
",
- "deprecationInfo": "Combine the .MuiButton-sizeSmall and .MuiButton-text classes instead. See Migrating from deprecated APIs for more details."
- },
- "textSuccess": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "variant=\"text\"
and color=\"success\"
",
- "deprecationInfo": "Combine the .MuiButton-text and .MuiButton-colorSuccess classes instead. See Migrating from deprecated APIs for more details."
- },
- "textWarning": {
- "description": "Styles applied to {{nodeName}} if {{conditions}}.",
- "nodeName": "the root element",
- "conditions": "variant=\"text\"
and color=\"warning\"
",
- "deprecationInfo": "Combine the .MuiButton-text and .MuiButton-colorWarning classes instead. See Migrating from deprecated APIs for more details."
- }
- }
-}
diff --git a/packages-internal/test-utils/src/describeConformance.tsx b/packages-internal/test-utils/src/describeConformance.tsx
index e3625ea577ff73..64c5600dad12c1 100644
--- a/packages-internal/test-utils/src/describeConformance.tsx
+++ b/packages-internal/test-utils/src/describeConformance.tsx
@@ -617,12 +617,14 @@ function testThemeDefaultProps(
});
describe('default props provider:', () => {
- it('respect custom default props', async function test() {
+ it('respect custom default props', async function test(t = {}) {
const testProp = 'data-id';
const { muiName, render, DefaultPropsProvider } = getOptions();
if (!DefaultPropsProvider) {
- this.skip();
+ // @ts-ignore
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
+ this?.skip?.() ?? t?.skip();
}
if (!muiName) {
@@ -634,6 +636,7 @@ function testThemeDefaultProps(
}
const { container } = await render(
+ // @ts-expect-error we skip it above.
& {
- /** Styles applied to the root element. */
- root?: string;
- /** Styles applied to the span element that wraps the children. */
- label?: string;
- /** Styles applied to the root element if `loading={true}`. */
- loading?: string;
- /** Styles applied to the loadingIndicator element. */
- loadingIndicator?: string;
- /** Styles applied to the loadingIndicator element if `loadingPosition="center"`. */
- loadingIndicatorCenter?: string;
- /** Styles applied to the loadingIndicator element if `loadingPosition="start"`. */
- loadingIndicatorStart?: string;
- /** Styles applied to the loadingIndicator element if `loadingPosition="end"`. */
- loadingIndicatorEnd?: string;
- /** Styles applied to the endIcon element if `loading={true}` and `loadingPosition="end"`. */
- endIconLoadingEnd?: string;
- /** Styles applied to the startIcon element if `loading={true}` and `loadingPosition="start"`. */
- startIconLoadingStart?: string;
- };
- /**
- * If `true`, the loading indicator is shown and the button becomes disabled.
- * @default false
- */
- loading?: boolean;
- /**
- * Element placed before the children if the button is in loading state.
- * The node should contain an element with `role="progressbar"` with an accessible name.
- * By default we render a `CircularProgress` that is labelled by the button itself.
- * @default
- */
- loadingIndicator?: React.ReactNode;
- /**
- * The loading indicator can be positioned on the start, end, or the center of the button.
- * @default 'center'
- */
- loadingPosition?: 'start' | 'end' | 'center';
- /**
- * The system prop that allows defining system overrides as well as additional CSS styles.
- */
- sx?: SxProps;
-}
-
-export type LoadingButtonTypeMap<
- AdditionalProps = {},
- RootComponent extends React.ElementType = 'button',
-> = ExtendButtonTypeMap<{
- props: AdditionalProps & LoadingButtonOwnProps;
- defaultComponent: RootComponent;
-}>;
-
-/**
- *
- * Demos:
- *
- * - [Button Group](https://mui.com/material-ui/react-button-group/)
- * - [Button](https://mui.com/material-ui/react-button/)
- *
- * API:
- *
- * - [LoadingButton API](https://mui.com/material-ui/api/loading-button/)
- * - inherits [Button API](https://mui.com/material-ui/api/button/)
- */
-declare const LoadingButton: ExtendButton;
-
-export type LoadingButtonProps<
- RootComponent extends React.ElementType = LoadingButtonTypeMap['defaultComponent'],
- AdditionalProps = {},
-> = OverrideProps, RootComponent>;
-
-export default LoadingButton;
+export { default } from '@mui/material/Button';
+export * from '@mui/material/Button';
diff --git a/packages/mui-lab/src/LoadingButton/LoadingButton.js b/packages/mui-lab/src/LoadingButton/LoadingButton.js
index 12f0997a888d09..8c4529a715bf4b 100644
--- a/packages/mui-lab/src/LoadingButton/LoadingButton.js
+++ b/packages/mui-lab/src/LoadingButton/LoadingButton.js
@@ -1,350 +1,29 @@
'use client';
import * as React from 'react';
-import PropTypes from 'prop-types';
-import { chainPropTypes } from '@mui/utils';
-import {
- capitalize,
- unstable_useId as useId,
- unstable_memoTheme as memoTheme,
-} from '@mui/material/utils';
-import { unstable_composeClasses as composeClasses } from '@mui/base';
-import { useDefaultProps } from '@mui/material/DefaultPropsProvider';
import Button from '@mui/material/Button';
-import { ButtonGroupContext } from '@mui/material/ButtonGroup';
-import CircularProgress from '@mui/material/CircularProgress';
-import resolveProps from '@mui/utils/resolveProps';
-import { styled } from '../zero-styled';
-import loadingButtonClasses, { getLoadingButtonUtilityClass } from './loadingButtonClasses';
-const useUtilityClasses = (ownerState) => {
- const { loading, loadingPosition, classes } = ownerState;
-
- const slots = {
- root: ['root', loading && 'loading'],
- label: ['label'],
- startIcon: [loading && `startIconLoading${capitalize(loadingPosition)}`],
- endIcon: [loading && `endIconLoading${capitalize(loadingPosition)}`],
- loadingIndicator: [
- 'loadingIndicator',
- loading && `loadingIndicator${capitalize(loadingPosition)}`,
- ],
- };
-
- const composedClasses = composeClasses(slots, getLoadingButtonUtilityClass, classes);
-
- return {
- ...classes, // forward the outlined, color, etc. classes to Button
- ...composedClasses,
- };
+let warnedOnce = false;
+
+const warn = () => {
+ if (!warnedOnce) {
+ console.warn(
+ [
+ 'MUI: The LoadingButton component functionality is now part of the Button component from Material UI.',
+ '',
+ "You should use `import { Button } from '@mui/material'`",
+ "or `import Button from '@mui/material/Button'`",
+ ].join('\n'),
+ );
+
+ warnedOnce = true;
+ }
};
-// TODO use `import rootShouldForwardProp from '../styles/rootShouldForwardProp';` once move to core
-const rootShouldForwardProp = (prop) =>
- prop !== 'ownerState' && prop !== 'theme' && prop !== 'sx' && prop !== 'as' && prop !== 'classes';
-const LoadingButtonRoot = styled(Button, {
- shouldForwardProp: (prop) => rootShouldForwardProp(prop) || prop === 'classes',
- name: 'MuiLoadingButton',
- slot: 'Root',
- overridesResolver: (props, styles) => {
- return [
- styles.root,
- styles.startIconLoadingStart && {
- [`& .${loadingButtonClasses.startIconLoadingStart}`]: styles.startIconLoadingStart,
- },
- styles.endIconLoadingEnd && {
- [`& .${loadingButtonClasses.endIconLoadingEnd}`]: styles.endIconLoadingEnd,
- },
- ];
- },
-})(
- memoTheme(({ theme }) => ({
- display: 'inline-flex',
- [`& .${loadingButtonClasses.startIconLoadingStart}, & .${loadingButtonClasses.endIconLoadingEnd}`]:
- {
- transition: theme.transitions.create(['opacity'], {
- duration: theme.transitions.duration.short,
- }),
- opacity: 0,
- },
- variants: [
- {
- props: {
- loadingPosition: 'center',
- },
- style: {
- transition: theme.transitions.create(['background-color', 'box-shadow', 'border-color'], {
- duration: theme.transitions.duration.short,
- }),
- [`&.${loadingButtonClasses.loading}`]: {
- color: 'transparent',
- },
- },
- },
- {
- props: ({ ownerState }) => ownerState.loadingPosition === 'start' && ownerState.fullWidth,
- style: {
- [`& .${loadingButtonClasses.startIconLoadingStart}, & .${loadingButtonClasses.endIconLoadingEnd}`]:
- {
- transition: theme.transitions.create(['opacity'], {
- duration: theme.transitions.duration.short,
- }),
- opacity: 0,
- marginRight: -8,
- },
- },
- },
- {
- props: ({ ownerState }) => ownerState.loadingPosition === 'end' && ownerState.fullWidth,
- style: {
- [`& .${loadingButtonClasses.startIconLoadingStart}, & .${loadingButtonClasses.endIconLoadingEnd}`]:
- {
- transition: theme.transitions.create(['opacity'], {
- duration: theme.transitions.duration.short,
- }),
- opacity: 0,
- marginLeft: -8,
- },
- },
- },
- ],
- })),
-);
-
-const LoadingButtonLoadingIndicator = styled('span', {
- name: 'MuiLoadingButton',
- slot: 'LoadingIndicator',
- overridesResolver: (props, styles) => {
- const { ownerState } = props;
- return [
- styles.loadingIndicator,
- styles[`loadingIndicator${capitalize(ownerState.loadingPosition)}`],
- ];
- },
-})(
- memoTheme(({ theme }) => ({
- position: 'absolute',
- visibility: 'visible',
- display: 'flex',
- variants: [
- {
- props: {
- loadingPosition: 'start',
- size: 'small',
- },
- style: {
- left: 10,
- },
- },
- {
- props: ({ loadingPosition, ownerState }) =>
- loadingPosition === 'start' && ownerState.size !== 'small',
- style: {
- left: 14,
- },
- },
- {
- props: {
- variant: 'text',
- loadingPosition: 'start',
- },
- style: {
- left: 6,
- },
- },
- {
- props: {
- loadingPosition: 'center',
- },
- style: {
- left: '50%',
- transform: 'translate(-50%)',
- color: (theme.vars || theme).palette.action.disabled,
- },
- },
- {
- props: {
- loadingPosition: 'end',
- size: 'small',
- },
- style: {
- right: 10,
- },
- },
- {
- props: ({ loadingPosition, ownerState }) =>
- loadingPosition === 'end' && ownerState.size !== 'small',
- style: {
- right: 14,
- },
- },
- {
- props: {
- variant: 'text',
- loadingPosition: 'end',
- },
- style: {
- right: 6,
- },
- },
- {
- props: ({ ownerState }) => ownerState.loadingPosition === 'start' && ownerState.fullWidth,
- style: {
- position: 'relative',
- left: -10,
- },
- },
- {
- props: ({ ownerState }) => ownerState.loadingPosition === 'end' && ownerState.fullWidth,
- style: {
- position: 'relative',
- right: -10,
- },
- },
- ],
- })),
-);
+/**
+ * @ignore - do not document.
+ */
+export default React.forwardRef(function DeprecatedLoadingButton(props, ref) {
+ warn();
-const LoadingButtonLabel = styled('span', {
- name: 'MuiLoadingButton',
- slot: 'Label',
- overridesResolver: (props, styles) => {
- return [styles.label];
- },
-})({
- display: 'inherit',
- alignItems: 'inherit',
- justifyContent: 'inherit',
+ return ;
});
-
-const LoadingButton = React.forwardRef(function LoadingButton(inProps, ref) {
- const contextProps = React.useContext(ButtonGroupContext);
- const resolvedProps = resolveProps(contextProps, inProps);
- const props = useDefaultProps({ props: resolvedProps, name: 'MuiLoadingButton' });
- const {
- children,
- disabled = false,
- id: idProp,
- loading = false,
- loadingIndicator: loadingIndicatorProp,
- loadingPosition = 'center',
- variant = 'text',
- ...other
- } = props;
-
- const id = useId(idProp);
- const loadingIndicator = loadingIndicatorProp ?? (
-
- );
-
- const ownerState = {
- ...props,
- disabled,
- loading,
- loadingIndicator,
- loadingPosition,
- variant,
- };
-
- const classes = useUtilityClasses(ownerState);
-
- const loadingButtonLoadingIndicator = loading ? (
-
- {loadingIndicator}
-
- ) : null;
-
- return (
-
- {ownerState.loadingPosition === 'end' ? (
- {children}
- ) : (
- loadingButtonLoadingIndicator
- )}
-
- {ownerState.loadingPosition === 'end' ? (
- loadingButtonLoadingIndicator
- ) : (
- {children}
- )}
-
- );
-});
-
-LoadingButton.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,
- /**
- * If `true`, the component is disabled.
- * @default false
- */
- disabled: PropTypes.bool,
- /**
- * @ignore
- */
- id: PropTypes.string,
- /**
- * If `true`, the loading indicator is shown and the button becomes disabled.
- * @default false
- */
- loading: PropTypes.bool,
- /**
- * Element placed before the children if the button is in loading state.
- * The node should contain an element with `role="progressbar"` with an accessible name.
- * By default we render a `CircularProgress` that is labelled by the button itself.
- * @default
- */
- loadingIndicator: PropTypes.node,
- /**
- * The loading indicator can be positioned on the start, end, or the center of the button.
- * @default 'center'
- */
- loadingPosition: chainPropTypes(PropTypes.oneOf(['start', 'end', 'center']), (props) => {
- if (props.loadingPosition === 'start' && !props.startIcon) {
- return new Error(
- `MUI: The loadingPosition="start" should be used in combination with startIcon.`,
- );
- }
- if (props.loadingPosition === 'end' && !props.endIcon) {
- return new Error(
- `MUI: The loadingPosition="end" should be used in combination with endIcon.`,
- );
- }
- return null;
- }),
- /**
- * 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 'text'
- */
- variant: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([
- PropTypes.oneOf(['contained', 'outlined', 'text']),
- PropTypes.string,
- ]),
-};
-
-export default LoadingButton;
diff --git a/packages/mui-lab/src/LoadingButton/LoadingButton.spec.tsx b/packages/mui-lab/src/LoadingButton/LoadingButton.spec.tsx
deleted file mode 100644
index 198e65ae6ef74c..00000000000000
--- a/packages/mui-lab/src/LoadingButton/LoadingButton.spec.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import * as React from 'react';
-import LoadingButton from '@mui/lab/LoadingButton';
-
-function ClassesTest() {
- return (
-
- Button
-
- );
-}
diff --git a/packages/mui-lab/src/LoadingButton/LoadingButton.test.js b/packages/mui-lab/src/LoadingButton/LoadingButton.test.js
deleted file mode 100644
index 837ef793bcebcc..00000000000000
--- a/packages/mui-lab/src/LoadingButton/LoadingButton.test.js
+++ /dev/null
@@ -1,117 +0,0 @@
-import * as React from 'react';
-import { createRenderer, screen, within } from '@mui/internal-test-utils';
-import { expect } from 'chai';
-import Button, { buttonClasses } from '@mui/material/Button';
-import LoadingButton, { loadingButtonClasses as classes } from '@mui/lab/LoadingButton';
-import ButtonGroup, { buttonGroupClasses } from '@mui/material/ButtonGroup';
-import describeConformance from '../../test/describeConformance';
-
-describe('', () => {
- const { render } = createRenderer();
-
- describeConformance(Conformance?, () => ({
- classes,
- inheritComponent: Button,
- render,
- muiName: 'MuiLoadingButton',
- testVariantProps: { loading: true },
- refInstanceof: window.HTMLButtonElement,
- skip: ['componentProp', 'componentsProp'],
- }));
-
- it('is in tab-order by default', () => {
- render();
-
- expect(screen.getByRole('button')).to.have.property('tabIndex', 0);
- });
-
- it('prop: classes can be appended to MuiButton', () => {
- render();
- const button = screen.getByRole('button');
-
- expect(button).to.have.class('MuiButton-outlined');
- expect(button).to.have.class('loading-button-outlined');
- });
-
- describe('prop: loading', () => {
- it('disables the button', () => {
- render();
-
- const button = screen.getByRole('button');
- expect(button).to.have.property('tabIndex', -1);
- expect(button).to.have.property('disabled', true);
- });
-
- it('cannot be enabled while `loading`', () => {
- render();
-
- expect(screen.getByRole('button')).to.have.property('disabled', true);
- });
-
- it('renders a progressbar that is labelled by the button', () => {
- render(Submit);
-
- const button = screen.getByRole('button');
- const progressbar = within(button).getByRole('progressbar');
- expect(progressbar).toHaveAccessibleName('Submit');
- });
- });
-
- describe('prop: loadingIndicator', () => {
- it('is not rendered by default', () => {
- render(Test);
-
- expect(screen.getByRole('button')).to.have.text('Test');
- });
-
- it('is rendered before the children when `loading`', () => {
- render(
-
- Test
- ,
- );
-
- expect(screen.getByRole('button')).to.have.text('loading…Test');
- });
- });
-
- describe('ButtonGroup works with LoadingButton', () => {
- it('correctly passes props to children', () => {
- const { getByRole } = render(
-
-
- ,
- );
- const button = getByRole('button');
- expect(button).to.have.class(buttonClasses.contained);
- expect(button).to.have.class(buttonClasses.sizeLarge);
- expect(button).to.have.class(buttonClasses.containedSecondary);
- });
-
- it('correctly applies position classes to loading buttons', () => {
- render(
-
- Button 1
- Button 2
- Button 3
- ,
- );
-
- const firstButton = screen.getAllByRole('button')[0];
- const middleButton = screen.getAllByRole('button')[1];
- const lastButton = screen.getAllByRole('button')[2];
-
- expect(firstButton).to.have.class(buttonGroupClasses.firstButton);
- expect(firstButton).not.to.have.class(buttonGroupClasses.middleButton);
- expect(firstButton).not.to.have.class(buttonGroupClasses.lastButton);
-
- expect(middleButton).to.have.class(buttonGroupClasses.middleButton);
- expect(middleButton).not.to.have.class(buttonGroupClasses.firstButton);
- expect(middleButton).not.to.have.class(buttonGroupClasses.lastButton);
-
- expect(lastButton).to.have.class(buttonGroupClasses.lastButton);
- expect(lastButton).not.to.have.class(buttonGroupClasses.middleButton);
- expect(lastButton).not.to.have.class(buttonGroupClasses.firstButton);
- });
- });
-});
diff --git a/packages/mui-lab/src/LoadingButton/index.d.ts b/packages/mui-lab/src/LoadingButton/index.d.ts
index f0fb5e154b2d5f..421603193deae6 100644
--- a/packages/mui-lab/src/LoadingButton/index.d.ts
+++ b/packages/mui-lab/src/LoadingButton/index.d.ts
@@ -1,5 +1,2 @@
export { default } from './LoadingButton';
export * from './LoadingButton';
-
-export { default as loadingButtonClasses } from './loadingButtonClasses';
-export * from './loadingButtonClasses';
diff --git a/packages/mui-lab/src/LoadingButton/index.js b/packages/mui-lab/src/LoadingButton/index.js
index c61aaee2562f05..b12be17a139ad5 100644
--- a/packages/mui-lab/src/LoadingButton/index.js
+++ b/packages/mui-lab/src/LoadingButton/index.js
@@ -1,4 +1 @@
export { default } from './LoadingButton';
-
-export { default as loadingButtonClasses } from './loadingButtonClasses';
-export * from './loadingButtonClasses';
diff --git a/packages/mui-lab/src/LoadingButton/loadingButtonClasses.ts b/packages/mui-lab/src/LoadingButton/loadingButtonClasses.ts
deleted file mode 100644
index d6543a1fa86cab..00000000000000
--- a/packages/mui-lab/src/LoadingButton/loadingButtonClasses.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import generateUtilityClass from '@mui/utils/generateUtilityClass';
-import generateUtilityClasses from '@mui/utils/generateUtilityClasses';
-
-export interface LoadingButtonClasses {
- /** Styles applied to the root element. */
- root: string;
- /** Styles applied to the span element that wraps the children. */
- label: string;
- /** Styles applied to the root element if `loading={true}`. */
- loading: string;
- /** Styles applied to the loadingIndicator element. */
- loadingIndicator: string;
- /** Styles applied to the loadingIndicator element if `loadingPosition="center"`. */
- loadingIndicatorCenter: string;
- /** Styles applied to the loadingIndicator element if `loadingPosition="start"`. */
- loadingIndicatorStart: string;
- /** Styles applied to the loadingIndicator element if `loadingPosition="end"`. */
- loadingIndicatorEnd: string;
- /** Styles applied to the endIcon element if `loading={true}` and `loadingPosition="end"`. */
- endIconLoadingEnd: string;
- /** Styles applied to the startIcon element if `loading={true}` and `loadingPosition="start"`. */
- startIconLoadingStart: string;
-}
-
-export type LoadingButtonClassKey = keyof LoadingButtonClasses;
-
-export function getLoadingButtonUtilityClass(slot: string): string {
- return generateUtilityClass('MuiLoadingButton', slot);
-}
-
-const loadingButtonClasses: LoadingButtonClasses = generateUtilityClasses('MuiLoadingButton', [
- 'root',
- 'label',
- 'loading',
- 'loadingIndicator',
- 'loadingIndicatorCenter',
- 'loadingIndicatorStart',
- 'loadingIndicatorEnd',
- 'endIconLoadingEnd',
- 'startIconLoadingStart',
-]);
-
-export default loadingButtonClasses;
diff --git a/packages/mui-material/src/Button/Button.d.ts b/packages/mui-material/src/Button/Button.d.ts
index c1e3e95f747014..ef7099aea29a1d 100644
--- a/packages/mui-material/src/Button/Button.d.ts
+++ b/packages/mui-material/src/Button/Button.d.ts
@@ -60,6 +60,23 @@ export interface ButtonOwnProps {
* If defined, an `a` element will be used as the root node.
*/
href?: string;
+ /**
+ * If `true`, the loading indicator is shown and the button becomes disabled.
+ * @default false
+ */
+ loading?: boolean;
+ /**
+ * Element placed before the children if the button is in loading state.
+ * The node should contain an element with `role="progressbar"` with an accessible name.
+ * By default we render a `CircularProgress` that is labelled by the button itself.
+ * @default
+ */
+ loadingIndicator?: React.ReactNode;
+ /**
+ * The loading indicator can be positioned on the start, end, or the center of the button.
+ * @default 'center'
+ */
+ loadingPosition?: 'start' | 'end' | 'center';
/**
* The size of the component.
* `small` is equivalent to the dense button styling.
diff --git a/packages/mui-material/src/Button/Button.js b/packages/mui-material/src/Button/Button.js
index 84330ff3940ede..aefb6001639a52 100644
--- a/packages/mui-material/src/Button/Button.js
+++ b/packages/mui-material/src/Button/Button.js
@@ -5,6 +5,8 @@ import clsx from 'clsx';
import resolveProps from '@mui/utils/resolveProps';
import composeClasses from '@mui/utils/composeClasses';
import { alpha } from '@mui/system/colorManipulator';
+import { unstable_useId as useId } from '@mui/material/utils';
+import CircularProgress from '@mui/material/CircularProgress';
import rootShouldForwardProp from '../styles/rootShouldForwardProp';
import { styled } from '../zero-styled';
import memoTheme from '../utils/memoTheme';
@@ -17,11 +19,13 @@ import ButtonGroupContext from '../ButtonGroup/ButtonGroupContext';
import ButtonGroupButtonContext from '../ButtonGroup/ButtonGroupButtonContext';
const useUtilityClasses = (ownerState) => {
- const { color, disableElevation, fullWidth, size, variant, classes } = ownerState;
+ const { color, disableElevation, fullWidth, size, variant, loading, loadingPosition, classes } =
+ ownerState;
const slots = {
root: [
'root',
+ loading && 'loading',
variant,
`${variant}${capitalize(color)}`,
`size${capitalize(size)}`,
@@ -30,9 +34,22 @@ const useUtilityClasses = (ownerState) => {
disableElevation && 'disableElevation',
fullWidth && 'fullWidth',
],
- label: ['label'],
- startIcon: ['icon', 'startIcon', `iconSize${capitalize(size)}`],
- endIcon: ['icon', 'endIcon', `iconSize${capitalize(size)}`],
+ startIcon: [
+ 'icon',
+ 'startIcon',
+ `iconSize${capitalize(size)}`,
+ loading && `startIconLoading${capitalize(loadingPosition)}`,
+ ],
+ endIcon: [
+ 'icon',
+ 'endIcon',
+ `iconSize${capitalize(size)}`,
+ loading && `endIconLoading${capitalize(loadingPosition)}`,
+ ],
+ loadingIndicator: [
+ 'loadingIndicator',
+ loading && `loadingIndicator${capitalize(loadingPosition)}`,
+ ],
};
const composedClasses = composeClasses(slots, getButtonUtilityClass, classes);
@@ -86,6 +103,7 @@ const ButtonRoot = styled(ButtonBase, {
ownerState.color === 'inherit' && styles.colorInherit,
ownerState.disableElevation && styles.disableElevation,
ownerState.fullWidth && styles.fullWidth,
+ ownerState.loading && styles.loading,
];
},
})(
@@ -296,6 +314,22 @@ const ButtonRoot = styled(ButtonBase, {
props: { fullWidth: true },
style: { width: '100%' },
},
+ {
+ props: {
+ loadingPosition: 'center',
+ },
+ style: {
+ transition: theme.transitions.create(
+ ['background-color', 'box-shadow', 'border-color'],
+ {
+ duration: theme.transitions.duration.short,
+ },
+ ),
+ [`&.${buttonClasses.loading}`]: {
+ color: 'transparent',
+ },
+ },
+ },
],
};
}),
@@ -307,9 +341,13 @@ const ButtonStartIcon = styled('span', {
overridesResolver: (props, styles) => {
const { ownerState } = props;
- return [styles.startIcon, styles[`iconSize${capitalize(ownerState.size)}`]];
+ return [
+ styles.startIcon,
+ ownerState.loading && styles.startIconLoadingStart,
+ styles[`iconSize${capitalize(ownerState.size)}`],
+ ];
},
-})({
+})(({ theme }) => ({
display: 'inherit',
marginRight: 8,
marginLeft: -4,
@@ -320,9 +358,24 @@ const ButtonStartIcon = styled('span', {
marginLeft: -2,
},
},
+ {
+ props: { loadingPosition: 'start', loading: true },
+ style: {
+ transition: theme.transitions.create(['opacity'], {
+ duration: theme.transitions.duration.short,
+ }),
+ opacity: 0,
+ },
+ },
+ {
+ props: { loadingPosition: 'start', loading: true, fullWidth: true },
+ style: {
+ marginRight: -8,
+ },
+ },
...commonIconStyles,
],
-});
+}));
const ButtonEndIcon = styled('span', {
name: 'MuiButton',
@@ -330,9 +383,13 @@ const ButtonEndIcon = styled('span', {
overridesResolver: (props, styles) => {
const { ownerState } = props;
- return [styles.endIcon, styles[`iconSize${capitalize(ownerState.size)}`]];
+ return [
+ styles.endIcon,
+ ownerState.loading && styles.endIconLoadingEnd,
+ styles[`iconSize${capitalize(ownerState.size)}`],
+ ];
},
-})({
+})(({ theme }) => ({
display: 'inherit',
marginRight: -4,
marginLeft: 8,
@@ -343,9 +400,111 @@ const ButtonEndIcon = styled('span', {
marginRight: -2,
},
},
+ {
+ props: { loadingPosition: 'end', loading: true },
+ style: {
+ transition: theme.transitions.create(['opacity'], {
+ duration: theme.transitions.duration.short,
+ }),
+ opacity: 0,
+ },
+ },
+ {
+ props: { loadingPosition: 'end', loading: true, fullWidth: true },
+ style: {
+ marginLeft: -8,
+ order: 2,
+ },
+ },
...commonIconStyles,
],
-});
+}));
+
+const ButtonLoadingIndicator = styled('span', {
+ name: 'MuiButton',
+ slot: 'LoadingIndicator',
+ overridesResolver: (props, styles) => styles.loadingIndicator,
+})(({ theme }) => ({
+ display: 'none',
+ position: 'absolute',
+ visibility: 'visible',
+ variants: [
+ { props: { loading: true }, style: { display: 'flex' } },
+ {
+ props: {
+ loadingPosition: 'start',
+ size: 'small',
+ },
+ style: {
+ left: 10,
+ },
+ },
+ {
+ props: ({ loadingPosition, size }) => loadingPosition === 'start' && size !== 'small',
+ style: {
+ left: 14,
+ },
+ },
+ {
+ props: {
+ variant: 'text',
+ loadingPosition: 'start',
+ },
+ style: {
+ left: 6,
+ },
+ },
+ {
+ props: {
+ loadingPosition: 'center',
+ },
+ style: {
+ left: '50%',
+ transform: 'translate(-50%)',
+ color: (theme.vars || theme).palette.action.disabled,
+ },
+ },
+ {
+ props: {
+ loadingPosition: 'end',
+ size: 'small',
+ },
+ style: {
+ right: 10,
+ },
+ },
+ {
+ props: ({ loadingPosition, size }) => loadingPosition === 'end' && size !== 'small',
+ style: {
+ right: 14,
+ },
+ },
+ {
+ props: {
+ variant: 'text',
+ loadingPosition: 'end',
+ },
+ style: {
+ right: 6,
+ },
+ },
+ {
+ props: { loadingPosition: 'start', fullWidth: true },
+ style: {
+ position: 'relative',
+ left: -10,
+ },
+ },
+ {
+ props: { loadingPosition: 'end', fullWidth: true },
+ style: {
+ position: 'relative',
+ right: -10,
+ order: 1,
+ },
+ },
+ ],
+}));
const Button = React.forwardRef(function Button(inProps, ref) {
// props priority: `inProps` > `contextProps` > `themeDefaultProps`
@@ -364,6 +523,10 @@ const Button = React.forwardRef(function Button(inProps, ref) {
endIcon: endIconProp,
focusVisibleClassName,
fullWidth = false,
+ id: idProp,
+ loading = false,
+ loadingIndicator: loadingIndicatorProp,
+ loadingPosition = 'center',
size = 'medium',
startIcon: startIconProp,
type,
@@ -371,6 +534,11 @@ const Button = React.forwardRef(function Button(inProps, ref) {
...other
} = props;
+ const id = useId(idProp);
+ const loadingIndicator = loadingIndicatorProp ?? (
+
+ );
+
const ownerState = {
...props,
color,
@@ -379,6 +547,9 @@ const Button = React.forwardRef(function Button(inProps, ref) {
disableElevation,
disableFocusRipple,
fullWidth,
+ loading,
+ loadingIndicator,
+ loadingPosition,
size,
type,
variant,
@@ -398,6 +569,12 @@ const Button = React.forwardRef(function Button(inProps, ref) {
);
+ const loader = (
+
+ {loading && loadingIndicator}
+
+ );
+
const positionClassName = buttonGroupButtonContextPositionClassName || '';
return (
@@ -405,15 +582,17 @@ const Button = React.forwardRef(function Button(inProps, ref) {
ownerState={ownerState}
className={clsx(contextProps.className, classes.root, className, positionClassName)}
component={component}
- disabled={disabled}
+ disabled={disabled || loading}
focusRipple={!disableFocusRipple}
focusVisibleClassName={clsx(classes.focusVisible, focusVisibleClassName)}
ref={ref}
type={type}
+ id={id}
{...other}
classes={classes}
>
{startIcon}
+ {loader}
{children}
{endIcon}
@@ -493,6 +672,27 @@ Button.propTypes /* remove-proptypes */ = {
* If defined, an `a` element will be used as the root node.
*/
href: PropTypes.string,
+ /**
+ * @ignore
+ */
+ id: PropTypes.string,
+ /**
+ * If `true`, the loading indicator is shown and the button becomes disabled.
+ * @default false
+ */
+ loading: PropTypes.bool,
+ /**
+ * Element placed before the children if the button is in loading state.
+ * The node should contain an element with `role="progressbar"` with an accessible name.
+ * By default we render a `CircularProgress` that is labelled by the button itself.
+ * @default
+ */
+ loadingIndicator: PropTypes.node,
+ /**
+ * The loading indicator can be positioned on the start, end, or the center of the button.
+ * @default 'center'
+ */
+ loadingPosition: PropTypes.oneOf(['center', 'end', 'start']),
/**
* The size of the component.
* `small` is equivalent to the dense button styling.
diff --git a/packages/mui-material/src/Button/Button.spec.tsx b/packages/mui-material/src/Button/Button.spec.tsx
index 832d79dd0599cf..78851774aa5572 100644
--- a/packages/mui-material/src/Button/Button.spec.tsx
+++ b/packages/mui-material/src/Button/Button.spec.tsx
@@ -145,3 +145,17 @@ const ReactRouterLinkTest = () => {
);
};
+
+function ClassesTest() {
+ return (
+
+ );
+}
diff --git a/packages/mui-material/src/Button/Button.test.js b/packages/mui-material/src/Button/Button.test.js
index 5d57fa23d002a0..7d3c1033a2f376 100644
--- a/packages/mui-material/src/Button/Button.test.js
+++ b/packages/mui-material/src/Button/Button.test.js
@@ -1,6 +1,6 @@
import * as React from 'react';
import { expect } from 'chai';
-import { createRenderer, screen, simulateKeyboardDevice } from '@mui/internal-test-utils';
+import { createRenderer, screen, simulateKeyboardDevice, within } from '@mui/internal-test-utils';
import { ClassNames } from '@emotion/react';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import Button, { buttonClasses as classes } from '@mui/material/Button';
@@ -753,4 +753,46 @@ describe('', () => {
expect(getComputedStyle(button).color).to.equal(color);
});
});
+
+ describe('prop: loading', () => {
+ it('disables the button', () => {
+ render();
+
+ const button = screen.getByRole('button');
+ expect(button).to.have.property('tabIndex', -1);
+ expect(button).to.have.property('disabled', true);
+ });
+
+ it('cannot be enabled while `loading`', () => {
+ render();
+
+ expect(screen.getByRole('button')).to.have.property('disabled', true);
+ });
+
+ it('renders a progressbar that is labelled by the button', () => {
+ render();
+
+ const button = screen.getByRole('button');
+ const progressbar = within(button).getByRole('progressbar');
+ expect(progressbar).toHaveAccessibleName('Submit');
+ });
+ });
+
+ describe('prop: loadingIndicator', () => {
+ it('is not rendered by default', () => {
+ render();
+
+ expect(screen.getByRole('button')).to.have.text('Test');
+ });
+
+ it('is rendered before the children when `loading`', () => {
+ render(
+ ,
+ );
+
+ expect(screen.getByRole('button')).to.have.text('loading…Test');
+ });
+ });
});
diff --git a/packages/mui-material/src/Button/buttonClasses.ts b/packages/mui-material/src/Button/buttonClasses.ts
index e6abe7643e4736..3dd6b4ea750fb1 100644
--- a/packages/mui-material/src/Button/buttonClasses.ts
+++ b/packages/mui-material/src/Button/buttonClasses.ts
@@ -176,6 +176,20 @@ export interface ButtonClasses {
colorInfo: string;
/** Styles applied to the root element if `color="warning"`. */
colorWarning: string;
+ /** Styles applied to the root element if `loading={true}`. */
+ loading: string;
+ /** Styles applied to the loadingIndicator element. */
+ loadingIndicator: string;
+ /** Styles applied to the loadingIndicator element if `loadingPosition="center"`. */
+ loadingIndicatorCenter: string;
+ /** Styles applied to the loadingIndicator element if `loadingPosition="start"`. */
+ loadingIndicatorStart: string;
+ /** Styles applied to the loadingIndicator element if `loadingPosition="end"`. */
+ loadingIndicatorEnd: string;
+ /** Styles applied to the endIcon element if `loading={true}` and `loadingPosition="end"`. */
+ endIconLoadingEnd: string;
+ /** Styles applied to the startIcon element if `loading={true}` and `loadingPosition="start"`. */
+ startIconLoadingStart: string;
}
export type ButtonClassKey = keyof ButtonClasses;
@@ -239,6 +253,13 @@ const buttonClasses: ButtonClasses = generateUtilityClasses('MuiButton', [
'iconSizeSmall',
'iconSizeMedium',
'iconSizeLarge',
+ 'loading',
+ 'loadingIndicator',
+ 'loadingIndicatorCenter',
+ 'loadingIndicatorStart',
+ 'loadingIndicatorEnd',
+ 'endIconLoadingEnd',
+ 'startIconLoadingStart',
]);
export default buttonClasses;
diff --git a/packages/mui-material/src/IconButton/IconButton.d.ts b/packages/mui-material/src/IconButton/IconButton.d.ts
index 108d72d09ac0c3..8a0240b3706e5a 100644
--- a/packages/mui-material/src/IconButton/IconButton.d.ts
+++ b/packages/mui-material/src/IconButton/IconButton.d.ts
@@ -47,6 +47,18 @@ export interface IconButtonOwnProps {
* @default false
*/
edge?: 'start' | 'end' | false;
+ /**
+ * If `true`, the loading indicator is shown and the button becomes disabled.
+ * @default false
+ */
+ loading?: boolean;
+ /**
+ * Element placed before the children if the button is in loading state.
+ * The node should contain an element with `role="progressbar"` with an accessible name.
+ * By default we render a `CircularProgress` that is labelled by the button itself.
+ * @default
+ */
+ loadingIndicator?: React.ReactNode;
/**
* The size of the component.
* `small` is equivalent to the dense button styling.
diff --git a/packages/mui-material/src/IconButton/IconButton.js b/packages/mui-material/src/IconButton/IconButton.js
index 780a3ff72d3d19..9057a9c68daaf6 100644
--- a/packages/mui-material/src/IconButton/IconButton.js
+++ b/packages/mui-material/src/IconButton/IconButton.js
@@ -4,6 +4,8 @@ import PropTypes from 'prop-types';
import clsx from 'clsx';
import chainPropTypes from '@mui/utils/chainPropTypes';
import composeClasses from '@mui/utils/composeClasses';
+import { unstable_useId as useId } from '@mui/material/utils';
+import CircularProgress from '@mui/material/CircularProgress';
import { alpha } from '@mui/system/colorManipulator';
import { styled } from '../zero-styled';
import memoTheme from '../utils/memoTheme';
@@ -14,16 +16,18 @@ import capitalize from '../utils/capitalize';
import iconButtonClasses, { getIconButtonUtilityClass } from './iconButtonClasses';
const useUtilityClasses = (ownerState) => {
- const { classes, disabled, color, edge, size } = ownerState;
+ const { classes, disabled, color, edge, size, loading } = ownerState;
const slots = {
root: [
'root',
+ loading && 'loading',
disabled && 'disabled',
color !== 'default' && `color${capitalize(color)}`,
edge && `edge${capitalize(edge)}`,
`size${capitalize(size)}`,
],
+ loadingIndicator: ['loadingIndicator'],
};
return composeClasses(slots, getIconButtonUtilityClass, classes);
@@ -37,6 +41,7 @@ const IconButtonRoot = styled(ButtonBase, {
return [
styles.root,
+ ownerState.loading && styles.loading,
ownerState.color !== 'default' && styles[`color${capitalize(ownerState.color)}`],
ownerState.edge && styles[`edge${capitalize(ownerState.edge)}`],
styles[`size${capitalize(ownerState.size)}`],
@@ -140,9 +145,27 @@ const IconButtonRoot = styled(ButtonBase, {
backgroundColor: 'transparent',
color: (theme.vars || theme).palette.action.disabled,
},
+ [`&.${iconButtonClasses.loading}`]: {
+ color: 'transparent',
+ },
})),
);
+const IconButtonLoadingIndicator = styled('span', {
+ name: 'MuiIconButton',
+ slot: 'LoadingIndicator',
+ overridesResolver: (props, styles) => styles.loadingIndicator,
+})(({ theme }) => ({
+ display: 'none',
+ position: 'absolute',
+ visibility: 'visible',
+ top: '50%',
+ left: '50%',
+ transform: 'translate(-50%, -50%)',
+ color: (theme.vars || theme).palette.action.disabled,
+ variants: [{ props: { loading: true }, style: { display: 'flex' } }],
+}));
+
/**
* Refer to the [Icons](/material-ui/icons/) section of the documentation
* regarding the available icon options.
@@ -157,15 +180,25 @@ const IconButton = React.forwardRef(function IconButton(inProps, ref) {
disabled = false,
disableFocusRipple = false,
size = 'medium',
+ id: idProp,
+ loading = false,
+ loadingIndicator: loadingIndicatorProp,
...other
} = props;
+ const id = useId(idProp);
+ const loadingIndicator = loadingIndicatorProp ?? (
+
+ );
+
const ownerState = {
...props,
edge,
color,
disabled,
disableFocusRipple,
+ loading,
+ loadingIndicator,
size,
};
@@ -173,14 +206,18 @@ const IconButton = React.forwardRef(function IconButton(inProps, ref) {
return (
+
+ {loading && loadingIndicator}
+
{children}
);
@@ -264,6 +301,22 @@ IconButton.propTypes /* remove-proptypes */ = {
* @default false
*/
edge: PropTypes.oneOf(['end', 'start', false]),
+ /**
+ * @ignore
+ */
+ id: PropTypes.string,
+ /**
+ * If `true`, the loading indicator is shown and the button becomes disabled.
+ * @default false
+ */
+ loading: PropTypes.bool,
+ /**
+ * Element placed before the children if the button is in loading state.
+ * The node should contain an element with `role="progressbar"` with an accessible name.
+ * By default we render a `CircularProgress` that is labelled by the button itself.
+ * @default
+ */
+ loadingIndicator: PropTypes.node,
/**
* The size of the component.
* `small` is equivalent to the dense button styling.
diff --git a/packages/mui-material/src/IconButton/IconButton.test.js b/packages/mui-material/src/IconButton/IconButton.test.js
index 827d2c91fb560d..e72515a6b0b309 100644
--- a/packages/mui-material/src/IconButton/IconButton.test.js
+++ b/packages/mui-material/src/IconButton/IconButton.test.js
@@ -1,7 +1,7 @@
import * as React from 'react';
import { expect } from 'chai';
import PropTypes from 'prop-types';
-import { createRenderer, reactMajor } from '@mui/internal-test-utils';
+import { createRenderer, reactMajor, screen, within } from '@mui/internal-test-utils';
import capitalize from '@mui/utils/capitalize';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import IconButton, { iconButtonClasses as classes } from '@mui/material/IconButton';
@@ -161,4 +161,46 @@ describe('', () => {
await ripple.startTouch(getByRole('button'));
expect(container.querySelector('.touch-ripple')).to.equal(null);
});
+
+ describe('prop: loading', () => {
+ it('disables the button', () => {
+ render();
+
+ const button = screen.getByRole('button');
+ expect(button).to.have.property('tabIndex', -1);
+ expect(button).to.have.property('disabled', true);
+ });
+
+ it('cannot be enabled while `loading`', () => {
+ render();
+
+ expect(screen.getByRole('button')).to.have.property('disabled', true);
+ });
+
+ it('renders a progressbar that is labelled by the button', () => {
+ render(Submit);
+
+ const button = screen.getByRole('button');
+ const progressbar = within(button).getByRole('progressbar');
+ expect(progressbar).toHaveAccessibleName('Submit');
+ });
+ });
+
+ describe('prop: loadingIndicator', () => {
+ it('is not rendered by default', () => {
+ render(Test);
+
+ expect(screen.getByRole('button')).to.have.text('Test');
+ });
+
+ it('is rendered before the children when `loading`', () => {
+ render(
+
+ Test
+ ,
+ );
+
+ expect(screen.getByRole('button')).to.have.text('loading…Test');
+ });
+ });
});
diff --git a/packages/mui-material/src/IconButton/iconButtonClasses.ts b/packages/mui-material/src/IconButton/iconButtonClasses.ts
index 72eb0e109c497f..b65ac3b0d85264 100644
--- a/packages/mui-material/src/IconButton/iconButtonClasses.ts
+++ b/packages/mui-material/src/IconButton/iconButtonClasses.ts
@@ -30,6 +30,10 @@ export interface IconButtonClasses {
sizeMedium: string;
/** Styles applied to the root element if `size="large"`. */
sizeLarge: string;
+ /** Styles applied to the root element if `loading={true}`. */
+ loading: string;
+ /** Styles applied to the loadingIndicator element. */
+ loadingIndicator: string;
}
export type IconButtonClassKey = keyof IconButtonClasses;
@@ -53,6 +57,8 @@ const iconButtonClasses: IconButtonClasses = generateUtilityClasses('MuiIconButt
'sizeSmall',
'sizeMedium',
'sizeLarge',
+ 'loading',
+ 'loadingIndicator',
]);
export default iconButtonClasses;
diff --git a/packages/mui-material/src/StepIcon/StepIcon.d.ts b/packages/mui-material/src/StepIcon/StepIcon.d.ts
index d71906a033977e..eae78f0f3fcc14 100644
--- a/packages/mui-material/src/StepIcon/StepIcon.d.ts
+++ b/packages/mui-material/src/StepIcon/StepIcon.d.ts
@@ -1,11 +1,13 @@
import * as React from 'react';
import { SxProps } from '@mui/system';
-import { InternalStandardProps as StandardProps } from '..';
+import { InternalStandardProps as StandardProps, SvgIconOwnProps } from '..';
import { Theme } from '../styles';
import { StepIconClasses } from './stepIconClasses';
export interface StepIconProps
- extends StandardProps, 'children'> {
+ // TODO v7: extend React.HTMLAttributes as svg is root component of StepIcon not div
+ extends StandardProps, 'color' | 'children'>,
+ Omit {
/**
* Whether this step is active.
* @default false
@@ -46,5 +48,6 @@ export type StepIconClasskey = keyof NonNullable;
* API:
*
* - [StepIcon API](https://mui.com/material-ui/api/step-icon/)
+ * - inherits [SvgIcon API](https://mui.com/material-ui/api/svg-icon/)
*/
export default function StepIcon(props: StepIconProps): React.JSX.Element;
diff --git a/packages/mui-material/src/StepIcon/StepIcon.spec.tsx b/packages/mui-material/src/StepIcon/StepIcon.spec.tsx
new file mode 100644
index 00000000000000..3a43cba9d79655
--- /dev/null
+++ b/packages/mui-material/src/StepIcon/StepIcon.spec.tsx
@@ -0,0 +1,5 @@
+import * as React from 'react';
+import StepIcon from '@mui/material/StepIcon';
+
+icon} />;
+icon} titleAccess="title" />;
diff --git a/packages/mui-material/src/StepIcon/StepIcon.test.js b/packages/mui-material/src/StepIcon/StepIcon.test.js
index 1469588b5bba7c..e4e0f7202545e4 100644
--- a/packages/mui-material/src/StepIcon/StepIcon.test.js
+++ b/packages/mui-material/src/StepIcon/StepIcon.test.js
@@ -2,6 +2,7 @@ import * as React from 'react';
import { expect } from 'chai';
import { createRenderer } from '@mui/internal-test-utils';
import StepIcon, { stepIconClasses as classes } from '@mui/material/StepIcon';
+import SvgIcon from '@mui/material/SvgIcon';
import describeConformance from '../../test/describeConformance';
describe('', () => {
@@ -9,7 +10,7 @@ describe('', () => {
describeConformance(, () => ({
classes,
- inheritComponent: 'svg',
+ inheritComponent: SvgIcon,
render,
muiName: 'MuiStepIcon',
testVariantProps: { completed: true },
diff --git a/packages/mui-material/src/styles/ThemeProviderWithVars.test.js b/packages/mui-material/src/styles/ThemeProviderWithVars.test.js
index 6311728b415ca3..57cc558313c691 100644
--- a/packages/mui-material/src/styles/ThemeProviderWithVars.test.js
+++ b/packages/mui-material/src/styles/ThemeProviderWithVars.test.js
@@ -1,8 +1,15 @@
import * as React from 'react';
import { expect } from 'chai';
-import { createRenderer, screen } from '@mui/internal-test-utils';
+import { createRenderer, screen, fireEvent } from '@mui/internal-test-utils';
import Box from '@mui/material/Box';
-import { CssVarsProvider, extendTheme, useTheme } from '@mui/material/styles';
+import {
+ CssVarsProvider,
+ extendTheme,
+ useTheme,
+ ThemeProvider,
+ createTheme,
+ useColorScheme,
+} from '@mui/material/styles';
describe('[Material UI] ThemeProviderWithVars', () => {
let originalMatchmedia;
@@ -360,4 +367,45 @@ describe('[Material UI] ThemeProviderWithVars', () => {
borderBottomRightRadius: '16px',
});
});
+
+ it('warns when using `setMode` without configuring `colorSchemeSelector`', () => {
+ function Test() {
+ const { setMode } = useColorScheme();
+ return ;
+ }
+ render(
+
+
+ ,
+ );
+
+ expect(() => {
+ fireEvent.click(screen.getByText('Dark'));
+ }).toErrorDev([
+ 'MUI: The `setMode` function has no effect if `colorSchemeSelector` is `media` (`media` is the default value).\nTo toggle the mode manually, please configure `colorSchemeSelector` to use a class or data attribute.\nTo learn more, visit https://mui.com/material-ui/customization/css-theme-variables/configuration/#toggling-dark-mode-manually',
+ ]);
+ });
+
+ it('do not warn when using `setMode` with `colorSchemeSelector` that is not `media`', () => {
+ function Test() {
+ const { setMode } = useColorScheme();
+ return ;
+ }
+ render(
+
+
+ ,
+ );
+
+ expect(() => {
+ fireEvent.click(screen.getByText('Dark'));
+ }).not.toErrorDev();
+ });
});
diff --git a/packages/mui-system/src/cssVars/createCssVarsProvider.js b/packages/mui-system/src/cssVars/createCssVarsProvider.js
index 9b521128ec13ab..cfed67e92e4f33 100644
--- a/packages/mui-system/src/cssVars/createCssVarsProvider.js
+++ b/packages/mui-system/src/cssVars/createCssVarsProvider.js
@@ -242,7 +242,21 @@ export default function createCssVarsProvider(options) {
lightColorScheme,
mode,
setColorScheme,
- setMode,
+ setMode:
+ process.env.NODE_ENV === 'production'
+ ? setMode
+ : (newMode) => {
+ if (theme.colorSchemeSelector === 'media') {
+ console.error(
+ [
+ 'MUI: The `setMode` function has no effect if `colorSchemeSelector` is `media` (`media` is the default value).',
+ 'To toggle the mode manually, please configure `colorSchemeSelector` to use a class or data attribute.',
+ 'To learn more, visit https://mui.com/material-ui/customization/css-theme-variables/configuration/#toggling-dark-mode-manually',
+ ].join('\n'),
+ );
+ }
+ setMode(newMode);
+ },
systemMode,
}),
[
@@ -254,6 +268,7 @@ export default function createCssVarsProvider(options) {
setColorScheme,
setMode,
systemMode,
+ theme.colorSchemeSelector,
],
);
diff --git a/test/regressions/fixtures/Button/FullWidthLoadingButtons.js b/test/regressions/fixtures/Button/FullWidthLoadingButtons.js
index e9f544630ef187..038f027e6b4b6a 100644
--- a/test/regressions/fixtures/Button/FullWidthLoadingButtons.js
+++ b/test/regressions/fixtures/Button/FullWidthLoadingButtons.js
@@ -1,5 +1,5 @@
import * as React from 'react';
-import LoadingButton from '@mui/lab/LoadingButton';
+import Button from '@mui/material/Button';
import FormControlLabel from '@mui/material/FormControlLabel';
import Switch from '@mui/material/Switch';
import SaveIcon from '@mui/icons-material/Save';
@@ -27,10 +27,10 @@ export default function FullWidthLoadingButtonsTransition() {
}
label="Loading"
/>
-
+
-
+ }
loading={loading}
@@ -39,8 +39,8 @@ export default function FullWidthLoadingButtonsTransition() {
fullWidth
>
Send
-
-
+
+
);
}