Material UI for React

Hi all

How have people found using the material-ui npm package with Meteor (specifically 1.3)? Does Meteor bring the CSS in correctly? It seems like the material-ui package is the one the wider JS community has landed on?

Also, am I right in thinking you need to add in your own grid system if you want something like the one in bootstrap? If so, any recommendations?

Finally, would your recommend it, any gotchas or warnings?

1 Like

I’m starting a project with material-ui. It was trivial to load using the directions on the site, and adding a test button gave me a properly styled component. I’ll have more to share when I get a little deeper - I hope others will add here as well. I might just use the bootstrap grid since I know it, but I’ll be curious to hear if there are advantages to using something else.

2 Likes

I like MaterialUI: it’s good looking and has a lot of complex components. I don’t miss much from Bootstrap. I use https://github.com/mbrookes/formsy-material-ui for forms.

For the grid I use http://flexboxgrid.com. I don’t suggest using the bootstrap grid mostly due to size, although it might be possible to just import the bootstrap grid, so less of a size worry there.

Using it on 1.2 from NPM. I think all the styles are inline. Here is my app.browserify.js file:

mui = require('material-ui')
ThemeManager = require('material-ui/lib/styles/theme-manager')
LightRawTheme = require('material-ui/lib/styles/raw-themes/light-raw-theme')
DarkRawTheme = require('material-ui/lib/styles/raw-themes/dark-raw-theme')
CurrentTheme = DarkRawTheme
Colors = require('material-ui/lib/styles/colors')

Using right now material-ui + React seamlessly. I’m a newbie with CSS so not yet using / testing any grid. However, I’m feeling some difficulties with implementing material-ui selectable list with a Mantra app and controlling your state. I ended up doing as the docs suggest and using state into the component.

If you are using state, then don’t call it a Mantra app.

To replace state as in the docs, I use actions.
For example, lets say that in your main layout, you have a AppBar and a LeftNav. The doc tells you to use state to toggle the offcanvas left menu.
To achieve the same result using the Mantra specs, create a layout.js action file with an action like so:

export default {
  toggleNav({LocalState}) {
    LocalState.set('NAVSTATE', !LocalState.get('NAVSTATE'));
  }
};

a container like so:

import Layout from '../components/layouts.main.jsx';
import {useDeps, composeWithTracker, composeAll} from 'mantra-core';

export const composer = ({context}, onData) => {
  const {LocalState} = context();
  const navState = LocalState.get('NAVSTATE') || false;
  onData(null, {navState});
};

export const depsMapper = (context, actions) => ({
  toggleNav: actions.layout.toggleNav,
  context: () => context
});

export default composeAll(
  composeWithTracker(composer),
  useDeps(depsMapper)
)(Layout);

and your layout component like

<div>
        {console.log(this)}
        <AppBar title="Summeet"
          onLeftIconButtonTouchTap={this.props.toggleNav} />
        <LeftNav docked={false}
          width={200}
          open={this.props.navState}
          onRequestChange={this.props.toggleNav}>
          <li>test</li>
        </LeftNav>
        {this.props.content()}
      </div>

I just figured this out right now and this might not be the correct way as I am just getting started with mantra.

Thanks all, that’s really helpful.

Ah that looks good, if I’m reading that right it uses the same classes as Bootstrap, so hopefully my existing (bootstrap-based) layout stuff should carry on working and I can carry on laying things out the way I’m used to?

Well, the first thing I always do when evaluating a new toolkit is to check how well it supports keyboard navigation, which is a key part of supporting accessibility. Open a dialog and see if tab navigation works - can you tab between buttons, and is focus properly trapped in the dialog (i.e. tabbing shouldn’t activate other parts of the doc). The demo page for material-ui fails both of these tests. Of course, most JS widget libs fail in the same way.

2 Likes

Thanks @elgusto. I was thinking to use right now this approach. So the main layout container will be in charge to manage all state of childrens. But I noticed this is only possible if the component or the layout (in this case) is being called from the router. Right ?

I think you should be able to use the container layout directly as long as you inject the app dependencies in it.
const LayoutDefaultCtx = injectDeps(LayoutDefault);

I didn’t have time to look in details how that works yet, I think it comes from the mantra-core in your main.js:

app.loadModule(coreModule);

Unfortunately the props and actions isn’t passed into layout component.
I have this structure of components: MainLayout -> Sidebar -> SidebarList.

My goal here is to store the link’s index clicked by the user on a selectable list and changed that accordingly, but taking out the state management of the list and putting this on the actions.

In actions I’ve created:

actions/main_layout.js

export default {
    setIndexItemSidebar({LocalState}, index) {
        console.log("Index selected: "+index);
        LocalState.set('INDEX_SIDEBAR_ITEM', index);
    }
};

My layout container:

containers/main_layout.js

    import { useDeps, composeWithTracker, composeAll } from 'mantra-core';
    import MainLayout from '../components/main_layout.jsx';

    export const composer = ({context}, onData) => {
    const {LocalState} = context();
      const indexSidebarItem = LocalState.get('INDEX_SIDEBAR_ITEM');
      onData(null, {indexSidebarItem});
    };

    export const depsMapper = (context, actions) => ({
      indexSidebarItem: actions.mainLayout.setIndexItemSidebar,
      context: () => context
    });

    export default composeAll(
      composeWithTracker(composer),
      useDeps(depsMapper)
    )(MainLayout);

And my component:

components/main_layout.jsx

    import React from 'react';
    import Sidebar from './sidebar.jsx';

    const Layout = ({content = () => null }, props) => (
      <div>
        <div>
          {console.log(props.indexSidebarItem(1))}
          <Sidebar indexSidebarItem={props.indexSidebarItem} />
          {content()}
        </div>
      </div>
    );
         
    export default Layout;

Maybe I am doing something totally wrong approach to do this…

Has anyone managed to stop the AppBar scrolling off the top of the page? I’d like the same behaviour you see on the docs page where the AppBar doesn’t scroll and the LeftNav is below the AppBar, but I can’t work out how to achieve that.

Edit: Sussed it:

let divStyle = {
    position: "fixed",
    top:0,
    width:"100%"
}

return (
    <div class="header">
        <AppBar
            title="simple crm"
            iconElementRight={<GlobalSearch id="srch-term" />}
            onLeftIconButtonTouchTap={this.props.toggleLeftNavExpanded}
            style={divStyle}
        />
    </div>
);

I belatedly realised the material-ui repo includes the code for the actual documentation site, so everything needed was right there.

.my-appbar-class {
      position: "fixed",
      height: 55,
      width: "100%",
      top: 0,
      zIndex: 100,
}

And I gave the LeftNav a style of {marginTop: 55, boxShadow: 'none'}

1 Like

I hadn’t heard that layouts aren’t supposed to use state in Mantra. Is there documentation about this? What are the benefits of using context() vs. state?