Skip to content

Commit

Permalink
[LoadingButton] Introduce new component (#21389)
Browse files Browse the repository at this point in the history
* wip

* wip

* added position

* renamed to LoadingButton

* fixed children

* fix width change when changing state

* renamed props and component

* Update packages/material-ui/src/BusyButton/BusyButton.js

Co-authored-by: Olivier Tassinari <[email protected]>

* Update docs/src/pages/components/buttons/BusyButtons.js

Co-authored-by: Olivier Tassinari <[email protected]>

* Update docs/src/pages/components/buttons/BusyButtons.js

Co-authored-by: Olivier Tassinari <[email protected]>

* Update packages/material-ui/src/BusyButton/BusyButton.d.ts

Co-authored-by: Olivier Tassinari <[email protected]>

* addressing comments

* fixed position

* comments

* proptypes

* proptypes

* fixed

* Update docs/src/pages/components/buttons/buttons.md

* comments

* Update packages/material-ui/src/BusyButton/BusyButton.js

Co-authored-by: Olivier Tassinari <[email protected]>

* Update packages/material-ui/src/BusyButton/BusyButton.js

Co-authored-by: Olivier Tassinari <[email protected]>

* proptypes

* prettier + docs:api

* yarn proptypes --disable-cache

* switch label

* Update packages/material-ui/src/BusyButton/BusyButton.js

Co-authored-by: Olivier Tassinari <[email protected]>

* Update packages/material-ui/src/BusyButton/BusyButton.js

* comments

* empty commit to run argos twice and compare diff

* moved BusyButton to lab

* docs:api

* fixed description

* renamed BusyButton to LoadingButton

* docs:api

* added classes keys

Co-authored-by: Olivier Tassinari <[email protected]>
  • Loading branch information
mnajdova and oliviertassinari authored Jun 15, 2020
1 parent c6b95d2 commit 7a1262d
Show file tree
Hide file tree
Showing 15 changed files with 534 additions and 2 deletions.
15 changes: 15 additions & 0 deletions docs/pages/api-docs/loading-button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';
import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs';
import { prepareMarkdown } from 'docs/src/modules/utils/parseMarkdown';

const pageFilename = 'api/loading-button';
const requireRaw = require.context('!raw-loader!./', false, /\/loading-button\.md$/);

export default function Page({ docs }) {
return <MarkdownDocs docs={docs} />;
}

Page.getInitialProps = () => {
const { demos, docs } = prepareMarkdown({ pageFilename, requireRaw });
return { demos, docs };
};
72 changes: 72 additions & 0 deletions docs/pages/api-docs/loading-button.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
---
filename: /packages/material-ui-lab/src/LoadingButton/LoadingButton.js
---

<!--- This documentation is automatically generated, do not try to edit it. -->

# LoadingButton API

<p class="description">The API documentation of the LoadingButton React component. Learn more about the props and the CSS customization points.</p>

## Import

```js
import LoadingButton from '@material-ui/lab/LoadingButton';
// or
import { LoadingButton } from '@material-ui/lab';
```

You can learn more about the difference by [reading this guide](/guides/minimizing-bundle-size/).



## Component name

The `MuiLoadingButton` name can be used for providing [default props](/customization/globals/#default-props) or [style overrides](/customization/globals/#css) at the theme level.

## Props

| Name | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| <span class="prop-name">children</span> | <span class="prop-type">node</span> | | The content of the button. |
| <span class="prop-name">classes</span> | <span class="prop-type">object</span> | | Override or extend the styles applied to the component. See [CSS API](#css) below for more details. |
| <span class="prop-name">disabled</span> | <span class="prop-type">bool</span> | <span class="prop-default">false</span> | If `true`, the button will be disabled. |
| <span class="prop-name">pending</span> | <span class="prop-type">bool</span> | <span class="prop-default">false</span> | If `true`, the pending indicator will be shown. |
| <span class="prop-name">pendingIndicator</span> | <span class="prop-type">node</span> | <span class="prop-default">&lt;CircularProgress color="inherit" size={16} /></span> | Element placed before the children if the button is in pending state. |
| <span class="prop-name">pendingPosition</span> | <span class="prop-type">'start'<br>&#124;&nbsp;'end'<br>&#124;&nbsp;'center'</span> | <span class="prop-default">'center'</span> | The pending indicator can be positioned on the start, end, or the center of the button. |

The `ref` is forwarded to the root element.

Any other props supplied will be provided to the root element ([Button](/api/button/)).

## CSS

| Rule name | Global class | Description |
|:-----|:-------------|:------------|
| <span class="prop-name">root</span> | <span class="prop-name">.MuiLoadingButton-root</span> | Styles applied to the root element.
| <span class="prop-name">pending</span> | <span class="prop-name">.MuiLoadingButton-pending</span> | Styles applied to the root element if `pending={true}`.
| <span class="prop-name">pendingIndicator</span> | <span class="prop-name">.MuiLoadingButton-pendingIndicator</span> | Styles applied to the pendingIndicator element.
| <span class="prop-name">pendingIndicatorCenter</span> | <span class="prop-name">.MuiLoadingButton-pendingIndicatorCenter</span> | Styles applied to the pendingIndicator element if `pendingPosition="center"`.
| <span class="prop-name">pendingIndicatorStart</span> | <span class="prop-name">.MuiLoadingButton-pendingIndicatorStart</span> | Styles applied to the pendingIndicator element if `pendingPosition="start"`.
| <span class="prop-name">pendingIndicatorEnd</span> | <span class="prop-name">.MuiLoadingButton-pendingIndicatorEnd</span> | Styles applied to the pendingIndicator element if `pendingPosition="end"`.
| <span class="prop-name">endIconPendingEnd</span> | <span class="prop-name">.MuiLoadingButton-endIconPendingEnd</span> | Styles applied to the endIcon element if `pending={true}` and `pendingPosition="end"`.
| <span class="prop-name">startIconPendingStart</span> | <span class="prop-name">.MuiLoadingButton-startIconPendingStart</span> | Styles applied to the startIcon element if `pending={true}` and `pendingPosition="start"`.
| <span class="prop-name">labelPendingCenter</span> | <span class="prop-name">.MuiLoadingButton-labelPendingCenter</span> | Styles applied to the label element if `pending={true}` and `pendingPosition="center"`.

You can override the style of the component thanks to one of these customization points:

- With a rule name of the [`classes` object prop](/customization/components/#overriding-styles-with-classes).
- With a [global class name](/customization/components/#overriding-styles-with-global-class-names).
- With a theme and an [`overrides` property](/customization/globals/#css).

If that's not sufficient, you can check the [implementation of the component](https://github.com/mui-org/material-ui/blob/master/packages/material-ui-lab/src/LoadingButton/LoadingButton.js) for more detail.

## Inheritance

The props of the [Button](/api/button/) component are also available.
You can take advantage of this behavior to [target nested components](/guides/api/#spread).

## Demos

- [Buttons](/components/buttons/)

30 changes: 30 additions & 0 deletions docs/src/pages/components/buttons/LoadingButtons.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import LoadingButton from '@material-ui/lab/LoadingButton';
import SaveIcon from '@material-ui/icons/Save';

const useStyles = makeStyles((theme) => ({
root: {
'& button': {
margin: theme.spacing(1),
},
},
}));

export default function LoadingButtons() {
const classes = useStyles();

return (
<div className={classes.root}>
<LoadingButton variant="outlined" pending>
Submit
</LoadingButton>
<LoadingButton variant="outlined" pending pendingIndicator="Loading...">
Fetch data
</LoadingButton>
<LoadingButton variant="outlined" pending pendingPosition="start" startIcon={<SaveIcon />}>
Save
</LoadingButton>
</div>
);
}
30 changes: 30 additions & 0 deletions docs/src/pages/components/buttons/LoadingButtons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import LoadingButton from '@material-ui/lab/LoadingButton';
import SaveIcon from '@material-ui/icons/Save';

const useStyles = makeStyles((theme) => ({
root: {
'& button': {
margin: theme.spacing(1),
},
},
}));

export default function LoadingButtons() {
const classes = useStyles();

return (
<div className={classes.root}>
<LoadingButton variant="outlined" pending>
Submit
</LoadingButton>
<LoadingButton variant="outlined" pending pendingIndicator="Loading...">
Fetch data
</LoadingButton>
<LoadingButton variant="outlined" pending pendingPosition="start" startIcon={<SaveIcon />}>
Save
</LoadingButton>
</div>
);
}
64 changes: 64 additions & 0 deletions docs/src/pages/components/buttons/LoadingButtonsTransition.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import LoadingButton from '@material-ui/lab/LoadingButton';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Switch from '@material-ui/core/Switch';
import SaveIcon from '@material-ui/icons/Save';
import SendIcon from '@material-ui/icons/Send';

const useStyles = makeStyles((theme) => ({
root: {
'& button': {
margin: theme.spacing(1),
},
},
switch: {
display: 'block',
},
}));

export default function LoadingButtonsTransition() {
const classes = useStyles();
const [pending, setPending] = React.useState(false);

return (
<div className={classes.root}>
<FormControlLabel
control={
<Switch
checked={pending}
onChange={() => setPending(!pending)}
name="pending"
color="primary"
/>
}
className={classes.switch}
label="Pending"
/>
<LoadingButton variant="outlined" pending={pending}>
Submit
</LoadingButton>
<LoadingButton variant="outlined" pending={pending} pendingIndicator="Loading...">
Fetch data
</LoadingButton>
<LoadingButton
variant="contained"
color="primary"
pending={pending}
pendingPosition="end"
endIcon={<SendIcon />}
>
Send
</LoadingButton>
<LoadingButton
variant="contained"
color="secondary"
pending={pending}
pendingPosition="start"
startIcon={<SaveIcon />}
>
Save
</LoadingButton>
</div>
);
}
64 changes: 64 additions & 0 deletions docs/src/pages/components/buttons/LoadingButtonsTransition.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import LoadingButton from '@material-ui/lab/LoadingButton';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Switch from '@material-ui/core/Switch';
import SaveIcon from '@material-ui/icons/Save';
import SendIcon from '@material-ui/icons/Send';

const useStyles = makeStyles((theme) => ({
root: {
'& button': {
margin: theme.spacing(1),
},
},
switch: {
display: 'block',
},
}));

export default function LoadingButtonsTransition() {
const classes = useStyles();
const [pending, setPending] = React.useState(false);

return (
<div className={classes.root}>
<FormControlLabel
control={
<Switch
checked={pending}
onChange={() => setPending(!pending)}
name="pending"
color="primary"
/>
}
className={classes.switch}
label="Pending"
/>
<LoadingButton variant="outlined" pending={pending}>
Submit
</LoadingButton>
<LoadingButton variant="outlined" pending={pending} pendingIndicator="Loading...">
Fetch data
</LoadingButton>
<LoadingButton
variant="contained"
color="primary"
pending={pending}
pendingPosition="end"
endIcon={<SendIcon />}
>
Send
</LoadingButton>
<LoadingButton
variant="contained"
color="secondary"
pending={pending}
pendingPosition="start"
startIcon={<SaveIcon />}
>
Save
</LoadingButton>
</div>
);
}
12 changes: 11 additions & 1 deletion docs/src/pages/components/buttons/buttons.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Button React component
components: Button, IconButton, ButtonBase
components: Button, IconButton, ButtonBase, LoadingButton
---

# Button
Expand Down Expand Up @@ -94,6 +94,16 @@ Here are some examples of customizing the component. You can learn more about th

🎨 If you are looking for inspiration, you can check [MUI Treasury's customization examples](https://mui-treasury.com/styles/button).

## Loading buttons

The loading buttons can show pending state and disable interactions.

{{"demo": "pages/components/buttons/LoadingButtons.js"}}

Toggle the switch to see the transition between the different states.

{{"demo": "pages/components/buttons/LoadingButtonsTransition.js"}}

## Complex Buttons

The Text Buttons, Contained Buttons, Floating Action Buttons and Icon Buttons are built on top of the same component: the `ButtonBase`.
Expand Down
55 changes: 55 additions & 0 deletions packages/material-ui-lab/src/LoadingButton/LoadingButton.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { ExtendButton, ExtendButtonTypeMap } from '@material-ui/core/Button';
import { OverrideProps } from '@material-ui/core/OverridableComponent';

export type LoadingButtonTypeMap<
P = {},
D extends React.ElementType = 'button'
> = ExtendButtonTypeMap<{
props: P & {
/**
* If `true`, the pending indicator will be shown.
*/
pending?: boolean;
/**
* Element placed before the children if the button is in pending state.
*/
pendingIndicator?: React.ReactNode;
/**
* The pending indicator can be positioned on the start, end, or the center of the button.
*/
pendingPosition?: 'start' | 'end' | 'center';
};
defaultComponent: D;
classKey: LoadingButtonClassKey;
}>;

/**
*
* Demos:
*
* - [Buttons](https://material-ui.com/components/buttons/)
*
* API:
*
* - [LoadingButton API](https://material-ui.com/api/loading-button/)
* - inherits [Button API](https://material-ui.com/api/button/)
*/
declare const LoadingButton: ExtendButton<LoadingButtonTypeMap>;

export type LoadingButtonProps<
D extends React.ElementType = LoadingButtonTypeMap['defaultComponent'],
P = {}
> = OverrideProps<LoadingButtonTypeMap<P, D>, D>;

export type LoadingButtonClassKey =
| 'root'
| 'pending'
| 'pendingIndicator'
| 'pendingIndicatorCenter'
| 'pendingIndicatorStart'
| 'pendingIndicatorEnd'
| 'endIconPendingEnd'
| 'startIconPendingStart'
| 'labelPendingCenter';

export default LoadingButton;
Loading

0 comments on commit 7a1262d

Please sign in to comment.