Skip to content

Commit

Permalink
add enableLoadingWrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
siriwatknp committed Dec 3, 2024
1 parent 52f690e commit f317e69
Show file tree
Hide file tree
Showing 10 changed files with 109 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export default function LoadingButtonsTransition() {
<Button
size="small"
onClick={handleClick}
enableLoadingWrapper
loading={loading}
variant="outlined"
disabled
Expand All @@ -39,6 +40,7 @@ export default function LoadingButtonsTransition() {
<Button
size="small"
onClick={handleClick}
enableLoadingWrapper
loading={loading}
loadingIndicator="Loading…"
variant="outlined"
Expand All @@ -49,6 +51,7 @@ export default function LoadingButtonsTransition() {
size="small"
onClick={handleClick}
endIcon={<SendIcon />}
enableLoadingWrapper
loading={loading}
loadingPosition="end"
variant="contained"
Expand All @@ -59,6 +62,7 @@ export default function LoadingButtonsTransition() {
size="small"
color="secondary"
onClick={handleClick}
enableLoadingWrapper
loading={loading}
loadingPosition="start"
startIcon={<SaveIcon />}
Expand All @@ -68,11 +72,18 @@ export default function LoadingButtonsTransition() {
</Button>
</Box>
<Box sx={{ '& > button': { m: 1 } }}>
<Button onClick={handleClick} loading={loading} variant="outlined" disabled>
<Button
onClick={handleClick}
enableLoadingWrapper
loading={loading}
variant="outlined"
disabled
>
Disabled
</Button>
<Button
onClick={handleClick}
enableLoadingWrapper
loading={loading}
loadingIndicator="Loading…"
variant="outlined"
Expand All @@ -82,6 +93,7 @@ export default function LoadingButtonsTransition() {
<Button
onClick={handleClick}
endIcon={<SendIcon />}
enableLoadingWrapper
loading={loading}
loadingPosition="end"
variant="contained"
Expand All @@ -91,6 +103,7 @@ export default function LoadingButtonsTransition() {
<Button
color="secondary"
onClick={handleClick}
enableLoadingWrapper
loading={loading}
loadingPosition="start"
startIcon={<SaveIcon />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export default function LoadingButtonsTransition() {
<Button
size="small"
onClick={handleClick}
enableLoadingWrapper
loading={loading}
variant="outlined"
disabled
Expand All @@ -39,6 +40,7 @@ export default function LoadingButtonsTransition() {
<Button
size="small"
onClick={handleClick}
enableLoadingWrapper
loading={loading}
loadingIndicator="Loading…"
variant="outlined"
Expand All @@ -49,6 +51,7 @@ export default function LoadingButtonsTransition() {
size="small"
onClick={handleClick}
endIcon={<SendIcon />}
enableLoadingWrapper
loading={loading}
loadingPosition="end"
variant="contained"
Expand All @@ -59,6 +62,7 @@ export default function LoadingButtonsTransition() {
size="small"
color="secondary"
onClick={handleClick}
enableLoadingWrapper
loading={loading}
loadingPosition="start"
startIcon={<SaveIcon />}
Expand All @@ -68,11 +72,18 @@ export default function LoadingButtonsTransition() {
</Button>
</Box>
<Box sx={{ '& > button': { m: 1 } }}>
<Button onClick={handleClick} loading={loading} variant="outlined" disabled>
<Button
onClick={handleClick}
enableLoadingWrapper
loading={loading}
variant="outlined"
disabled
>
Disabled
</Button>
<Button
onClick={handleClick}
enableLoadingWrapper
loading={loading}
loadingIndicator="Loading…"
variant="outlined"
Expand All @@ -82,6 +93,7 @@ export default function LoadingButtonsTransition() {
<Button
onClick={handleClick}
endIcon={<SendIcon />}
enableLoadingWrapper
loading={loading}
loadingPosition="end"
variant="contained"
Expand All @@ -91,6 +103,7 @@ export default function LoadingButtonsTransition() {
<Button
color="secondary"
onClick={handleClick}
enableLoadingWrapper
loading={loading}
loadingPosition="start"
startIcon={<SaveIcon />}
Expand Down
4 changes: 4 additions & 0 deletions docs/data/material/components/buttons/buttons.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@ Toggle the loading switch to see the transition between the different states.

{{"demo": "LoadingButtonsTransition.js"}}

:::info
The above demo sets the `enableLoadingWrapper` prop on buttons to prevent [Google Translation Crash](https://github.com/mui/material-ui/issues/27853) after toggling the loading state.
:::

## Customization

Here are some examples of customizing the component.
Expand Down
7 changes: 7 additions & 0 deletions docs/pages/material-ui/api/button.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"disableElevation": { "type": { "name": "bool" }, "default": "false" },
"disableFocusRipple": { "type": { "name": "bool" }, "default": "false" },
"disableRipple": { "type": { "name": "bool" }, "default": "false" },
"enableLoadingWrapper": { "type": { "name": "bool" }, "default": "false" },
"endIcon": { "type": { "name": "node" } },
"fullWidth": { "type": { "name": "bool" }, "default": "false" },
"href": { "type": { "name": "string" } },
Expand Down Expand Up @@ -269,6 +270,12 @@
"description": "Styles applied to the loadingIndicator element if `loadingPosition=\"start\"`.",
"isGlobal": false
},
{
"key": "loadingWrapper",
"className": "MuiButton-loadingWrapper",
"description": "Styles applied to the loadingWrapper element.",
"isGlobal": false
},
{
"key": "outlined",
"className": "MuiButton-outlined",
Expand Down
7 changes: 7 additions & 0 deletions docs/translations/api-docs/button/button.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
"disableRipple": {
"description": "If <code>true</code>, the ripple effect is disabled.<br>⚠️ Without a ripple there is no styling for :focus-visible by default. Be sure to highlight the element by applying separate styles with the <code>.Mui-focusVisible</code> class."
},
"enableLoadingWrapper": {
"description": "If <code>true</code>, the loader is rendered in a span element. The span element is always present regardless of the loading state. Use this prop to prevent React error when the loading state is toggled with Google translation. See <a href=\"https://github.com/mui/material-ui/issues/27853\">https://github.com/mui/material-ui/issues/27853</a> for more details."
},
"endIcon": { "description": "Element placed after the children." },
"fullWidth": {
"description": "If <code>true</code>, the button will take up the full width of its container."
Expand Down Expand Up @@ -216,6 +219,10 @@
"nodeName": "the loadingIndicator element",
"conditions": "<code>loadingPosition=\"start\"</code>"
},
"loadingWrapper": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the loadingWrapper element"
},
"outlined": {
"description": "Styles applied to {{nodeName}} if {{conditions}}.",
"nodeName": "the root element",
Expand Down
6 changes: 6 additions & 0 deletions packages/mui-material/src/Button/Button.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ export interface ButtonOwnProps {
* @default false
*/
disableFocusRipple?: boolean;
/**
* If `true`, the loader is rendered in a span element. The span element is always present regardless of the loading state.
* Use this prop to prevent React error when the loading state is toggled with Google translation. See https://github.com/mui/material-ui/issues/27853 for more details.
* @default false
*/
enableLoadingWrapper?: boolean;
/**
* Element placed after the children.
*/
Expand Down
19 changes: 17 additions & 2 deletions packages/mui-material/src/Button/Button.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const useUtilityClasses = (ownerState) => {
'loadingIndicator',
loading && `loadingIndicator${capitalize(loadingPosition)}`,
],
loadingWrapper: ['loadingWrapper'],
};

const composedClasses = composeClasses(slots, getButtonUtilityClass, classes);
Expand Down Expand Up @@ -520,6 +521,7 @@ const Button = React.forwardRef(function Button(inProps, ref) {
disabled = false,
disableElevation = false,
disableFocusRipple = false,
enableLoadingWrapper = false,
endIcon: endIconProp,
focusVisibleClassName,
fullWidth = false,
Expand Down Expand Up @@ -571,7 +573,7 @@ const Button = React.forwardRef(function Button(inProps, ref) {

const loader = (
<ButtonLoadingIndicator className={classes.loadingIndicator} ownerState={ownerState}>
{loading && loadingIndicator}
{loadingIndicator}
</ButtonLoadingIndicator>
);

Expand All @@ -592,7 +594,14 @@ const Button = React.forwardRef(function Button(inProps, ref) {
classes={classes}
>
{startIcon}
{loader}
{enableLoadingWrapper ? (
// use plain HTML span to minimize the runtime overhead
<span className={classes.loadingWrapper} style={{ display: 'contents' }}>
{loading && loader}
</span>
) : (
<React.Fragment>{loading && loader}</React.Fragment>
)}
{children}
{endIcon}
</ButtonRoot>
Expand Down Expand Up @@ -654,6 +663,12 @@ Button.propTypes /* remove-proptypes */ = {
* @default false
*/
disableRipple: PropTypes.bool,
/**
* If `true`, the loader is rendered in a span element. The span element is always present regardless of the loading state.
* Use this prop to prevent React error when the loading state is toggled with Google translation. See https://github.com/mui/material-ui/issues/27853 for more details.
* @default false
*/
enableLoadingWrapper: PropTypes.bool,
/**
* Element placed after the children.
*/
Expand Down
14 changes: 14 additions & 0 deletions packages/mui-material/src/Button/Button.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,20 @@ const buttonTest = () => (
}
<Button startIcon={<FakeIcon />}>Start Icon</Button>
<Button endIcon={<FakeIcon />}>endIcon</Button>
<Button enableLoadingWrapper>endIcon</Button>
<Button loading>Button</Button>
<Button loading loadingPosition="center">
Button
</Button>
<Button loading loadingPosition="start">
Button
</Button>
<Button loading loadingPosition="end">
Button
</Button>
<Button loading loadingIndicator={<span>Loading</span>}>
Button
</Button>
</div>
);

Expand Down
24 changes: 23 additions & 1 deletion packages/mui-material/src/Button/Button.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { expect } from 'chai';
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';
import Button, { buttonClasses, buttonClasses as classes } from '@mui/material/Button';
import ButtonBase, { touchRippleClasses } from '@mui/material/ButtonBase';
import describeConformance from '../../test/describeConformance';
import * as ripple from '../../test/ripple';
Expand Down Expand Up @@ -795,4 +795,26 @@ describe('<Button />', () => {
expect(screen.getByRole('button')).to.have.text('loading…Test');
});
});

describe('prop: enableLoadingWrapper', () => {
it('is not rendered by default', () => {
const { container } = render(<Button>Test</Button>);

expect(container.querySelector(`.${buttonClasses.loadingWrapper}`)).to.equal(null);
});

it('is always rendered before the children', () => {
const { container, rerender } = render(<Button enableLoadingWrapper>Test</Button>);

expect(container.querySelector(`.${buttonClasses.loadingWrapper}`)).not.to.equal(null);

rerender(
<Button enableLoadingWrapper loading>
Test
</Button>,
);

expect(container.querySelector(`.${buttonClasses.loadingWrapper}`)).not.to.equal(null);
});
});
});
3 changes: 3 additions & 0 deletions packages/mui-material/src/Button/buttonClasses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ export interface ButtonClasses {
colorWarning: string;
/** Styles applied to the root element if `loading={true}`. */
loading: string;
/** Styles applied to the loadingWrapper element. */
loadingWrapper: string;
/** Styles applied to the loadingIndicator element. */
loadingIndicator: string;
/** Styles applied to the loadingIndicator element if `loadingPosition="center"`. */
Expand Down Expand Up @@ -254,6 +256,7 @@ const buttonClasses: ButtonClasses = generateUtilityClasses('MuiButton', [
'iconSizeMedium',
'iconSizeLarge',
'loading',
'loadingWrapper',
'loadingIndicator',
'loadingIndicatorCenter',
'loadingIndicatorStart',
Expand Down

0 comments on commit f317e69

Please sign in to comment.