Material-ui: [AppBar] Composability and the muiName property

Created on 23 Aug 2016  路  12Comments  路  Source: mui-org/material-ui

Nested children inside AppBar obtain different themes

Steps to reproduce

Works as expected:

class NavbarInstance extends React.Component {
    constructor(props) {
        super(props);
    }

    returnHome() {
        FlowRouter.go('landing-page');
    }

    render() {
        return (
            <AppBar
                title="Title"
                onTitleTouchTap={this.returnHome}
                iconElementRight={<FlatButton eventKey={1} label="Login" icon={< UserIcon />} href={FlowRouter.path('users.login')}/>}
            />
    );
    }
}

screen shot 2016-08-23 at 17 47 54

Doesn't work as expected:

class NavbarInstance extends React.Component {
    constructor(props) {
        super(props);
    }

    returnHome() {
        FlowRouter.go('landing-page');
    }

    render() {
        return (
            <AppBar
                title="Title"
                onTitleTouchTap={this.returnHome}
                iconElementRight={< NavRightContent user={this.props.user} />}
            />
    );
    }
}
 //------------------------
class NavRightContent extends React.Component {
    constructor(props) {
        super(props);
    }
    render() {

        return(<FlatButton eventKey={1} label="Login" icon={< UserIcon />} href={FlowRouter.path('users.login')}/>)
    }
}

screen shot 2016-08-23 at 17 49 01

As it can be seen the color, margins and padding are different.

Versions

  • Material-UI: v0.15.4
  • React: v0.15.3
docs

Most helpful comment

@PolGuixe Sorry, I was previously wrong.

  • It's working with stateless as with class component.
  • Here is an example with a stateless functional component:
const MyFlatButton = (props) => (
  <FlatButton {...props} label="Save" />
);

Component.muiName = 'FlatButton';

const AppBarExampleIconButton = () => (
  <AppBar
    title="Title"
    iconElementRight={<MyFlatButton />}
  />
);
  • Here is an example with a class component:
class MyFlatButton extends Component {
  static muiName = 'FlatButton';

  render() {
    return <FlatButton {...this.props} label="Save" />;
  }
}

const AppBarExampleIconButton = () => (
  <AppBar
    title="Title"
    iconElementRight={<MyFlatButton />}
  />
);
  • Now, it would be great to add a section about it in the documentation.

All 12 comments

@PolGuixe This issue isn't limited to the <AppBar />. It's a much broader issue.

  1. We should document this approach as we are going forward with the composability approach.
    You need to apply a static muiName = 'FlatButton' property to your <NavRightContent /> component.
  2. We don't support stateless functional component. I have just tried it and it crashs.

@oliviertassinari you mean to have something like this:

<AppBar
title="Title"
onTitleTouchTap={this.returnHome}
iconElementRight={< NavRightContent muiName='FlatButton' user={this.props.user} />}
/>

I have tried this and it doesn't work, any idea or work-around?

@PolGuixe No, that's the _react-bootstrap_ approach. I'm talking about: https://github.com/callemall/material-ui/blob/master/src/FlatButton/FlatButton.js#L17.

@PolGuixe Sorry, I was previously wrong.

  • It's working with stateless as with class component.
  • Here is an example with a stateless functional component:
const MyFlatButton = (props) => (
  <FlatButton {...props} label="Save" />
);

Component.muiName = 'FlatButton';

const AppBarExampleIconButton = () => (
  <AppBar
    title="Title"
    iconElementRight={<MyFlatButton />}
  />
);
  • Here is an example with a class component:
class MyFlatButton extends Component {
  static muiName = 'FlatButton';

  render() {
    return <FlatButton {...this.props} label="Save" />;
  }
}

const AppBarExampleIconButton = () => (
  <AppBar
    title="Title"
    iconElementRight={<MyFlatButton />}
  />
);
  • Now, it would be great to add a section about it in the documentation.

@oliviertassinari cool it works.

However, I can't solve it in some more complex elements, where what it renders depends in some props. Like in the example below:

class NavRightContent extends Component {
  //TODO:  not always the same type.....
  static muiName = 'FlatButton';

  render() {
   const {user} = this.props
    return user 
      ? <IconMenu> /*Other stuff*/ </IconMenu>
      : <FlatButton {...this.props} label="Save" />;
  }
}

const AppBarExampleIconButton = () => (
  <AppBar
    title="Title"
    iconElementRight={< NavRightContent />}
  />
);

Any ideas?


Right now I am handling the logic inside the AppBar's components. Like:

 <AppBar
    title="Title"
iconElementRight={user ? <NavBarLoggedMenu context={context} user={user} /> : <NavBarLogin context={context} />}
  />

But this method has some limitations and I don't think that it is the best way.

I would be nice if we could set the muiName of the <NavRightContent/> dynamically.

I have realised that way I am trying to do is different and a bit more complex. Hence iconElementRight may not be the best way. I think I should be using a different approach.
I have created a different issue to discuss it: https://github.com/callemall/material-ui/issues/5231

Right now I am handling the logic inside the AppBar's components. Like:

Yeah, that's the best approach you can use as the component is designed right now.
IMHO, that's good enough.
The other way I can think of to improve this point would be to use the _context_.

How about when the component is connect-ed with redux?

Here's my code:

// LoginContainer.js

class Login extends React.Component {
  constructor(props) {
    super(props)
  }

  render() {
    return (<FlatButton onClick={this.props.onLoginClick} label='Login' />)
  }

  static muiName = 'FlatButton'
}

Login.propTypes = {
  isAuthenticated: React.PropTypes.bool,
  isAuthenticated: React.PropTypes.object,
  onLoginClick: React.PropTypes.func.isRequired,
  onLogoutClick: React.PropTypes.func.isRequired
}

const mapStateToProps = (state) => {
  const { isAuthenticated, profile } = state.auth
  return {
    isAuthenticated,
    profile
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    onLoginClick: () => dispatch(loginRequest()),
    onLogoutClick: () => dispatch(logoutSuccess())
  }
}

const LoginContainer = connect(
  mapStateToProps,
  mapDispatchToProps
)(Login)

export default LoginContainer

And the AppBar is simply <AppBar title='Timed' iconElementRight={<LoginContainer />} />

I still have wrong paddings, margins and color, even after putting static muiName = 'FlatButton':
screen shot 2016-11-09 at 10 31 20 am

@amaurymartiny Try the following. That should work.

const LoginContainer = connect(
  mapStateToProps,
  mapDispatchToProps
)(Login);
LoginContainer.muiName = 'FlatButton';

export default LoginContainer

I have the same problem as the original poster @PolGuixe and tried to use muiName like @oliviertassinari explained above, but it didn't work for a more complex component such as an IconMenu. What would be the correct way to solve the theme issues below?

I have these components:

class ClickMeButton extends Component {
    static muiName = "FlatButton";

    render() {
        return (
            <FlatButton {...this.props} label="Click me" />
        );
    }
}

class MyMenu extends Component {
    static muiName = "IconMenu";

    render(){
        return (
            <IconMenu {...this.props} iconButtonElement={<ClickMeButton />}>
                <MenuItem primaryText="Item 1"/>
                <MenuItem primaryText="Item 2"/>
                <MenuItem primaryText="Item 3"/>
            </IconMenu>
        );
    }
}

Now, when I create an AppBar doing this:

class MyAppBar extends Component {
    render() {
        return (
            <AppBar
                title="My App Bar"
                iconElementRight={<ClickMeButton />}
            />
        );
    }
}

the theme is picked up correctly:
correct_theme

However, when I do this:

class MyAppBar extends Component {
    render() {
        return (
            <AppBar
                title="My App Bar"
                iconElementRight={<MyMenu/>}
            />
        );
    }
}

the theme is NOT picked up for the button:
incorrect_theme

How can I make Material-UI use the correct theme for my ClickMeButton in the AppBar?

@oliviertassinari , as to the code example:

const MyFlatButton = (props) => (
  <FlatButton {...props} label="Save" />
);

Component.muiName = 'FlatButton';

const AppBarExampleIconButton = () => (
  <AppBar
    title="Title"
    iconElementRight={<MyFlatButton />}
  />
);

Did you mean MyFlatButton.muiName = 'FlatButton' ?
Thanks for your help.

@norpoint It's an old issue. I don't remember, but you sound like on the right track.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sys13 picture sys13  路  3Comments

finaiized picture finaiized  路  3Comments

rbozan picture rbozan  路  3Comments

newoga picture newoga  路  3Comments

revskill10 picture revskill10  路  3Comments