Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Button] Add loading prop support #17810

Closed
wants to merge 1 commit into from

Conversation

oliviertassinari
Copy link
Member

@oliviertassinari oliviertassinari commented Oct 9, 2019

Capture d’écran 2019-10-09 à 19 10 55

      <Button variant="outlined" loading>
        Loading…
      </Button>
      <Button
        variant="contained"
        color="primary"
        loading
        startIcon={<CircularProgress color="inherit" size={16} />}
      >
        Duplicate
      </Button>

Benchmark

The implementation was benchmarked with the following sources. I have noticed that the loading and disabled states are often styled independently.

Capture d’écran 2019-10-09 à 17 59 21
evergreen

Capture d’écran 2019-10-09 à 17 59 38
blueprint

Capture d’écran 2019-10-09 à 18 00 04
vuetify

Capture d’écran 2019-10-09 à 18 00 11
cosmos

Capture d’écran 2019-10-09 à 18 00 24
antd

Capture d’écran 2019-10-09 à 18 00 29
element

Capture d’écran 2019-10-09 à 18 00 41
sancho

Capture d’écran 2019-10-09 à 18 00 49
semantic ui

Capture d’écran 2019-10-09 à 18 00 59
quasar

Capture d’écran 2019-10-09 à 18 01 08
baseui

Capture d’écran 2019-10-09 à 18 01 18
atlaskit

Capture d’écran 2019-10-09 à 18 01 29
chakra

Capture d’écran 2019-10-09 à 18 01 42
orbit

@mui-pr-bot
Copy link

Details of bundle changes.

Comparing: 31d3008...326e15f

bundle Size Change Size Gzip Change Gzip
Button ▲ +163 B (+0.21% ) 79.4 kB ▲ +79 B (+0.32% ) 24.6 kB
docs.main ▲ +163 B (+0.03% ) 599 kB ▲ +66 B (+0.03% ) 191 kB
@material-ui/core ▲ +163 B (+0.05% ) 347 kB ▲ +48 B (+0.05% ) 95.1 kB
@material-ui/core[umd] ▲ +160 B (+0.05% ) 306 kB ▲ +53 B (+0.06% ) 88.1 kB
ExpansionPanelSummary ▲ +43 B (+0.06% ) 77.9 kB ▲ +19 B (+0.08% ) 24.5 kB
SpeedDial ▲ +43 B (+0.05% ) 85.9 kB ▲ +19 B (+0.07% ) 27 kB
Tabs ▲ +43 B (+0.05% ) 85.3 kB ▲ +19 B (+0.07% ) 27.1 kB
SpeedDialAction ▲ +43 B (+0.04% ) 115 kB ▲ +18 B (+0.05% ) 36.4 kB
Switch ▲ +43 B (+0.05% ) 80.8 kB ▲ +18 B (+0.07% ) 25.1 kB
Checkbox ▲ +43 B (+0.05% ) 81.6 kB ▲ +17 B (+0.07% ) 25.6 kB
IconButton ▲ +43 B (+0.06% ) 76 kB ▲ +17 B (+0.07% ) 23.7 kB
ListItem ▲ +43 B (+0.06% ) 77 kB ▲ +17 B (+0.07% ) 24 kB
MenuItem ▲ +43 B (+0.06% ) 78 kB ▲ +17 B (+0.07% ) 24.3 kB
Radio ▲ +43 B (+0.05% ) 82.5 kB ▲ +17 B (+0.07% ) 25.9 kB
StepButton ▲ +43 B (+0.05% ) 82.2 kB ▲ +17 B (+0.07% ) 25.8 kB
TableSortLabel ▲ +43 B (+0.06% ) 77.2 kB ▲ +17 B (+0.07% ) 24.4 kB
@material-ui/lab ▲ +43 B (+0.03% ) 145 kB ▲ +16 B (+0.04% ) 45 kB
ButtonBase ▲ +43 B (+0.06% ) 73.9 kB ▲ +16 B (+0.07% ) 23.1 kB
BottomNavigationAction ▲ +43 B (+0.06% ) 75.4 kB ▲ +15 B (+0.06% ) 23.7 kB
CardActionArea ▲ +43 B (+0.06% ) 74.9 kB ▲ +15 B (+0.06% ) 23.6 kB
Tab ▲ +43 B (+0.06% ) 76.2 kB ▲ +15 B (+0.06% ) 24.1 kB
ToggleButton ▲ +43 B (+0.06% ) 76 kB ▲ +15 B (+0.06% ) 23.9 kB
Fab ▲ +43 B (+0.06% ) 76.7 kB ▲ +14 B (+0.06% ) 23.8 kB
TablePagination ▲ +43 B (+0.03% ) 139 kB ▲ +10 B (+0.02% ) 40.6 kB
@material-ui/styles -- 51.8 kB -- 15.6 kB
@material-ui/system -- 15.7 kB -- 4.35 kB
AppBar -- 63.9 kB -- 19.9 kB
Avatar -- 62.7 kB -- 19.6 kB
Backdrop -- 67.7 kB -- 20.9 kB
Badge -- 65.3 kB -- 20.2 kB
BottomNavigation -- 62.4 kB -- 19.5 kB
Box -- 70.8 kB -- 21.3 kB
Breadcrumbs -- 68 kB -- 21.3 kB
ButtonGroup -- 64.2 kB -- 20 kB
Card -- 62.9 kB -- 19.6 kB
CardActions -- 62 kB -- 19.3 kB
CardContent -- 62 kB -- 19.3 kB
CardHeader -- 65.1 kB -- 20.4 kB
CardMedia -- 62.4 kB -- 19.5 kB
Chip -- 70.6 kB -- 21.8 kB
CircularProgress -- 64.1 kB -- 20.1 kB
ClickAwayListener -- 3.85 kB -- 1.55 kB
Collapse -- 67.8 kB -- 20.9 kB
colorManipulator -- 3.83 kB -- 1.52 kB
Container -- 63.1 kB -- 19.6 kB
CssBaseline -- 57.6 kB -- 18 kB
Dialog -- 82.5 kB -- 25.5 kB
DialogActions -- 62.1 kB -- 19.4 kB
DialogContent -- 62.2 kB -- 19.4 kB
DialogContentText -- 64 kB -- 20 kB
DialogTitle -- 64.3 kB -- 20.1 kB
Divider -- 62.6 kB -- 19.6 kB
docs.landing -- 54.3 kB -- 14.3 kB
Drawer -- 84.3 kB -- 26 kB
ExpansionPanel -- 71.1 kB -- 22.1 kB
ExpansionPanelActions -- 62.1 kB -- 19.4 kB
ExpansionPanelDetails -- 61.9 kB -- 19.3 kB
Fade -- 23.1 kB -- 8.03 kB
FilledInput -- 72.9 kB -- 22.6 kB
FormControl -- 64.3 kB -- 19.9 kB
FormControlLabel -- 65.5 kB -- 20.5 kB
FormGroup -- 62 kB -- 19.3 kB
FormHelperText -- 63.3 kB -- 19.7 kB
FormLabel -- 63.2 kB -- 19.5 kB
Grid -- 65.1 kB -- 20.3 kB
GridList -- 62.5 kB -- 19.5 kB
GridListTile -- 63.7 kB -- 19.9 kB
GridListTileBar -- 63.2 kB -- 19.7 kB
Grow -- 23.7 kB -- 8.15 kB
Hidden -- 66.1 kB -- 20.6 kB
Icon -- 62.8 kB -- 19.6 kB
Input -- 72 kB -- 22.4 kB
InputAdornment -- 65.1 kB -- 20.4 kB
InputBase -- 70.2 kB -- 22 kB
InputLabel -- 65.1 kB -- 20.2 kB
LinearProgress -- 65.3 kB -- 20.3 kB
Link -- 66.6 kB -- 21 kB
List -- 62.4 kB -- 19.3 kB
ListItemAvatar -- 62.1 kB -- 19.3 kB
ListItemIcon -- 62.2 kB -- 19.4 kB
ListItemSecondaryAction -- 62 kB -- 19.3 kB
ListItemText -- 65 kB -- 20.3 kB
ListSubheader -- 62.8 kB -- 19.6 kB
Menu -- 88.2 kB -- 27.6 kB
MenuList -- 66 kB -- 20.5 kB
MobileStepper -- 67.7 kB -- 21 kB
Modal -- 14.3 kB -- 4.96 kB
NativeSelect -- 76.4 kB -- 24 kB
NoSsr -- 2.19 kB -- 1.04 kB
OutlinedInput -- 73.5 kB -- 22.8 kB
Paper -- 62.4 kB -- 19.3 kB
Popover -- 82.6 kB -- 25.4 kB
Popper -- 28.3 kB -- 10.2 kB
Portal -- 2.87 kB -- 1.29 kB
RadioGroup -- 63.2 kB -- 19.7 kB
Rating -- 69.8 kB -- 22.2 kB
RootRef -- 4.43 kB -- 1.67 kB
Select -- 113 kB -- 33.5 kB
Skeleton -- 62.5 kB -- 19.5 kB
Slide -- 25.1 kB -- 8.66 kB
Slider -- 75.3 kB -- 23.7 kB
Snackbar -- 77.1 kB -- 24 kB
SnackbarContent -- 65.8 kB -- 20.6 kB
SpeedDialIcon -- 64.6 kB -- 20.2 kB
Step -- 62.6 kB -- 19.5 kB
StepConnector -- 62.7 kB -- 19.6 kB
StepContent -- 68.9 kB -- 21.4 kB
StepIcon -- 64.7 kB -- 20.1 kB
StepLabel -- 68.6 kB -- 21.4 kB
Stepper -- 64.9 kB -- 20.3 kB
styles/createMuiTheme -- 16.2 kB -- 5.78 kB
SvgIcon -- 63 kB -- 19.6 kB
SwipeableDrawer -- 90.7 kB -- 28 kB
Table -- 62.4 kB -- 19.4 kB
TableBody -- 62.1 kB -- 19.3 kB
TableCell -- 64.1 kB -- 20 kB
TableFooter -- 62.1 kB -- 19.3 kB
TableHead -- 62.1 kB -- 19.3 kB
TableRow -- 62.5 kB -- 19.5 kB
TextareaAutosize -- 5.06 kB -- 2.11 kB
TextField -- 121 kB -- 35.4 kB
ToggleButtonGroup -- 63.2 kB -- 19.8 kB
Toolbar -- 62.3 kB -- 19.4 kB
Tooltip -- 99 kB -- 31.3 kB
TreeItem -- 73.4 kB -- 23 kB
TreeView -- 66 kB -- 20.6 kB
Typography -- 63.6 kB -- 19.8 kB
useMediaQuery -- 2.49 kB -- 1.05 kB
Zoom -- 23.1 kB -- 8.04 kB

Generated by 🚫 dangerJS against 326e15f

Copy link
Member Author

@oliviertassinari oliviertassinari left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two other concerns to take into account:

  • The button width change when the circular progress is added or when the button label changes
  • The button with circular progress only is no supported

loading: {
pointerEvents: 'none', // Disable link interactions
cursor: 'default',
opacity: 0.4,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same value as the disabled rating.

@@ -260,7 +262,7 @@ const ButtonBase = React.forwardRef(function ButtonBase(props, ref) {
className={clsx(
classes.root,
{
[classes.disabled]: disabled,
[classes.disabled]: disabledProp,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to introduce a private loading prop to disable the interaction without applying the disabled CSS. I don't like that, but I can't think of a better approach.

variant="contained"
color="primary"
loading
startIcon={<CircularProgress color="inherit" size={16} />}
Copy link
Member Author

@oliviertassinari oliviertassinari Oct 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most implementations bundle the circular directly in the button. I'm not confident this is best from a bundle size perspective. I'm wondering. It might also complexify the API to expose in case people want to change how the loading is displayed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a general statement not targeted at anyone particular: I'm getting weary of this "props everything" approach. React introduced "component based ui development" not "props based ui development". It's ok to add another LoadableButton where one could flip the switch and we provide the spinner.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"BusyButton" 🙂

@oliviertassinari oliviertassinari added component: button This is the name of the generic UI component, not the React module! new feature New feature or request labels Oct 9, 2019
@oliviertassinari oliviertassinari marked this pull request as ready for review October 9, 2019 21:01
@@ -162,6 +162,12 @@ export const styles = theme => ({
focusVisible: {},
/* Pseudo-class applied to the root element if `disabled={true}`. */
disabled: {},
/* Styles applied to the root element if `loading={true}`. */
loading: {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not actually related to loading (it's only used in this context). What this actually conveys is that the button is "deactivated" or "not actionable" or "temporary disabled". loading implies that there is another indicator.

As I said below I'd be perfectly fine with introducing a separate component that overrides Button properly.

Copy link
Member Author

@oliviertassinari oliviertassinari Oct 11, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I said below I'd be perfectly fine with introducing a separate component that overrides Button properly.

The loading state is transient, how would you switch between the loading and non-loading state with a new component? In this regard, I think that a switch of a prop has the potential to be simpler to use.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think transient applies to a loading state or any UI in that matter.

But even then you can still have the loading prop which then actually conveys the proper meaning by switching styles and adding an indicator.

@oliviertassinari oliviertassinari added PR: needs revision The pull request can't be merged. More details is available on the code review or fails in the CI and removed PR: needs review labels Oct 17, 2019
@oliviertassinari
Copy link
Member Author

oliviertassinari commented Oct 17, 2019

I'm closing, it doesn't seem to be a very frequent user request (while I expect it to be frequently needed) but it seems to require more effort to reach a consensus at least, we have: https://material-ui.com/components/progress/#interactive-integration.

Thanks for the review guys.

@oliviertassinari
Copy link
Member Author

oliviertassinari commented Oct 17, 2019

more effort to reach a consensus

Well, actually, BusyButton might be a good enough solution, but I would rather focus on different problems. If somebody wants to resume this effort 👍.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: button This is the name of the generic UI component, not the React module! new feature New feature or request PR: needs revision The pull request can't be merged. More details is available on the code review or fails in the CI
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants