Material-ui: Where is button with icons and labels in the v1 beta?

Created on 29 Oct 2017  路  28Comments  路  Source: mui-org/material-ui

In v 0.19 there are button with icons like the following :
image

But in the v1 beta, I can't find anything like this in the demo even in the APIs.
I know the latest API can make a button either to have label or icon, but I want to have label and icon both inside a button.

Button enhancement

Most helpful comment

Coming from 0.9, I find the lack of support for button with left icon really cumbersome. Now adding an icon to a button implies importing withStyles, creating always the same style object, and adding the className prop.

In a small mui 0.9 app, I have to do that dozens of times. So of course, my solution is to create a <ButtonWithIcon> component to support that. But then I'm thinking: why is that not in mui 1.0 core? It's not difficult to implement, it's ultra common (even if not in the Google guidelines), and it was supported in 0.x.

Migrating from 0.x is painful enough, it'd be a little smoother if you didn't drop that feature.

Or am I missing something?

All 28 comments

It's gone, instead you can do the following:

import React from "react";
import PropTypes from "prop-types";
import Button from "material-ui/Button";
import { withStyles } from "material-ui/styles";
import AndroidIcon from "material-ui-icons/Android";

const styles = theme => ({
  icon: {
    marginRight: theme.spacing.unit
  }
});

function Index(props) {
  return (
    <Button raised color="accent">
      <AndroidIcon className={props.classes.icon} />
      Super Secret Password
    </Button>
  );
}

Index.propTypes = {
  classes: PropTypes.object.isRequired
};

export default withStyles(styles)(Index);

Live demo: https://codesandbox.io/s/ryqoq502lo

capture d ecran 2017-10-29 a 11 54 03

Keep in mind, there is not button + icon in the official material design specification.

@oliviertassinari Thanks! It would be great if you could include that in the demo, so that others who wants to migrate from v0.18 to v1 would have less frustration.
Just for other readers who faced the same problem, you can actually use both v0.18 and v1 at the same time, please refer to this documentation.

if you could include that in the demo

@wongjiahau Why not. It would be here to show composability. Do you want to take care of it?

@oliviertassinari Sure. Let me make a PR for that.

@oliviertassinari I had already made a PR for that.

8922

Coming from 0.9, I find the lack of support for button with left icon really cumbersome. Now adding an icon to a button implies importing withStyles, creating always the same style object, and adding the className prop.

In a small mui 0.9 app, I have to do that dozens of times. So of course, my solution is to create a <ButtonWithIcon> component to support that. But then I'm thinking: why is that not in mui 1.0 core? It's not difficult to implement, it's ultra common (even if not in the Google guidelines), and it was supported in 0.x.

Migrating from 0.x is painful enough, it'd be a little smoother if you didn't drop that feature.

Or am I missing something?

@fzaninotto The initial motivation for not porting the feature is that it's not inside the Material Design Specification. I have never seen Google doing it on their products either.
The second motivation is that it's as simple as doing the following with v1. I believe it wasn't the case with v0.x

import React from 'react';
import Button from 'material-ui/Button';
import AndroidIcon from 'material-ui-icons/Android';

function App() {
  return (
    <Button raised color="accent">
      <AndroidIcon style={{ marginRight: 8 }} />
      Super Secret Password
    </Button>
  );
}

export default App;

The third motivation is about reducing the API surface. We have been accepting too many high-level API on v0.x.

I have added the waiting for users upvotes tag. So please upvote this issue if you are looking for this abstraction. We will prioritize our effort based on the number of upvotes.

@fzaninotto Check out the documentation I wrote for the button with icons 馃槃

@oliviertassinari Upvoted. But if it's just a matter of effort, I can have someone in my team work on a PR.

@wongjiahau I have read your documentation, see my comment. My point is, this is so common, it should not require so much boilerplate.

@fzaninotto It's not primarily a matter of effort. I want to be sure that supporting this feature will do more good than harm (extra bundle-size, code complexity vs how much frequent are people using it and how simpler it's making userland code).
Thanks for proposing your help :)

@fzaninotto I understand what you mean. By the way I think we should discuss on how the API should look like. Currently this is my suggestion:

interface ButtonWithIconProps {
    leftIcon?: ReactNode,
    rightIcon?: ReactNode
};

So example of usage would be :

<ButtonWithIcon 
        leftIcon={<HelloIcon/>} 
        rightIcon={<ByeIcon/>}> 
            Hey buddy! 
</ButtonWithIcon>

Any suggestion for improvements?

I don't know the material-ui 1.0 API design guidelines, but considering you guys merged <RaisedButton> and <FlatButton> to a single <Button> component, it would make sense to add icon support in the <Button> component.

So the example usage would be:

<Button
        leftIcon={<HelloIcon/>} 
        rightIcon={<ByeIcon/>}> 
            Hey buddy! 
</Button>

I don't know the material-ui 1.0 API design guidelines

@fzaninotto https://material-ui-next.com/guides/api/.

Hey @oliviertassinari , recently I found this kind of design used by Google in their Google Map apps.

Take a look at the start button located at the bottom-right.

gcal1

Not only did they used it on Raised Button, they also used it on Floating Action Button.

gcal2

Therefore, I think we should implement API that allow user to add icon besides labels easily.

@wongjiahau Your font makes these look like mockups, but just checked and there is indeed an button with Icon. (Not sure what to make of the text in the FAB though.)

However, I'm not convinced that this is so hard that it justifies adding icon props:

    <Button>
      <AndroidIcon style={{ marginRight: 8 }} />
      Android
    </Button>

It's not hard, it's cumbersome, because:

  1. Material-ui encourages the use of JSS instead of inline CSS
  2. Inline CSS require a bit more work than your example because of the infamous {{

Also, it used to be supported in the previous version, so it's a basic expectation for long times users that it keeps on working.

Material-ui encourages the use of JSS instead of inline CSS

Material-UI supports the use of plan old CSS, or your choice of styling solution: https://material-ui-next.com/guides/interoperability/

  1. Inline CSS require a bit more work than your example because of the infamous {{

Sure, but in the context of a larger component, putting your styles in an array is not a huge burden - you'll need some sort of styling solution whether Button natively styles your icon or not.

As @oliviertassinari said, not ruling it out, but relative to https://github.com/mui-org/material-ui/issues/8891#issuecomment-343532901 or compared to the container styling / code boilerplate that some other components need, I'm not convinced it's that big of an issue.

Putting my money where my mouth is - if this bubbles up to be a top-5 issue, I'll take responsibility for implementing it myself. 馃榿

Again, if this is a matter of implementing them, I can take care of it. There are already many votes on the issue, it would ease the otherwise hugely painful migration from 0.9. I really don't understand what you guys expect.

@mbrookes Sounds like we both agree. Material-UI focuses on providing high-quality reusable building blocks. v0.x used to abstract more logic. v1.x tries to maximize flexibility at the expense of abstracting fewer things and making people writing abstraction on top of us.
I don't think increasing the Button bundle size and API is worth it. I encourage anyone building a wrapping component on top of us if it's a pattern they use extensively like you would create a global CSS rule for Bootstrap: https://github.com/twbs/bootstrap/issues/24358

Maybe the answer is to expose a new ButtonIcon component 馃?

<Button>
  <ButtonIcon position="left">
    <AndroidIcon />
  </ButtonIcon>
  Android
</Button>

IconDecorator

@oliviertassinari I suggest that we could make a IconDecorator component, which could decorate a component with an icon. The reason is because Button is not the only component that requires icon, people also want TextField to have icon (#2932).

Example

For text field

<IconDecorator icon={<PeopleIcon/>} position="left" iconStyle={customStyle}>
    <TextField label="Name"/>
</IconDecorator>

For button

<IconDecorator icon={<ArrowRightIcon/>} position="right" iconStyle={customStyle}>
    <Button raised={true}>Go</Button>
</IconDecorator>

Advantage

This method will not break or pollute the current API, thus there will be no breaking changes.

@wongjiahau While that might work for signifier icons on form components, the button icon is a child of the botton component, so it gets messy. (I missed that on the referenced post).

@mbrookes I got what you mean, so the question now goes to whether we want a single IconDecorator API for every component that demands for icon decoration or we will implement different icon decorator API for every distinct element.

Let me analyze it a bit.

Universal IconDecorator

Advantage

  • Easy for user to use, because there's consistency for every type of component.

    Disadvantage

  • The internal implementation might be messy or chaotic, because the hierarchy of icon might be different for each type of element. For example, icon is a child of button, but it is a sibling of text field.

  • Unneeded code will be imported since every implementation is embedded within a single class (unless we can do some export hacks)

Distinct IconDecorator for each component

Advantage

  • Implementation will be clean
  • Code base will be easier to be maintained.

    Disadvantage

  • Maybe hard for user to use, since the API looks different for each component.

Conclusion

To summarize, there's actually only a single problem, do we want to make it easier for the developers (of this library) or the users?

First, let me tell you that you guys are doing an awesome job in the library in general. But.

I don't understand your reasoning. In this issue, I read that, as Google never implemented that pattern, it wasn't worth doing it. I also read that, given enough user votes, you would consider implementing it.

Then, when presented with a Google implementation and many votes, you find another reason for not doing it.

I'm not convinced it's that big of an issue.

I'm telling you that, from a user's point of view, and for someone who actually made the job of migrating a 0.9 app, it is. Not taking that opinion into account, even given my effort to help you guys make the migration smoother, is discouraging.

I don't think increasing the Button bundle size and API is worth it.

What size are we talking about? Adding support for leftIcon and rightIcon props would take what, 4 lines?

Do we want to make it easier for the developers (of this library) or the users?

I'm terrified that the answer to this question isn't obvious for you... And I understand that this is the source of all my pains with material-ui 1.0. It's just not designed with a user-centric point of view.

I'm really sorry that this simple request leads to such a painful discussion for everyone, but the outcome is, in my opinion, a glimpse of what material-ui is becoming - good or bad.

Adding support for leftIcon and rightIcon props would take what, 4 lines?

@fzaninotto I'm expecting it to be ~30 lines of code.

I'm terrified that the answer to this question isn't obvious for you

It's 30 lines of code that everybody are going to have to pay for a feature they might not need. I won't consider 5 upvotes enough to prioritize the feature. You might have realized it. v1 made an important turned. It's no longer just about prototyping. It's about providing strong foundations people can build on top of for high expectation customer-facing UI. It comes with tradeoff. The API is lower level. We are not ready yet to have higher level APIs.

People voice for their use cases. I never had the button icon use case. So I would rather make the Button as light as possible. Also, we try to minimize the API surface to make the project maintainable. I hope you better understand our decision process.

even given my effort to help you guys make the migration smoother

Thank you for this!

@fzaninotto Now, I'm wondering about adding a fullWidth property or block property to the Button. What is your thought on it? (I have used this "mode" many times in userland, adding it here will make the process easier for me. We used to have the property in v0.x). It's 10 LOCs.

@fzaninotto I agree with you, I also have the pain of migrating because of this button icon. Maybe just create a pull request that implement <ButtonWithIcon> as you suggested previously. I will upvote on it.

The feature was added back in #17600.

Was this page helpful?
0 / 5 - 0 ratings