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

[Layout]: Add new Layout components #5267

Closed
wants to merge 70 commits into from
Closed

Conversation

msuntharesan
Copy link

@msuntharesan msuntharesan commented Sep 27, 2016

  • PR has tests / docs demo, and is linted.
  • Commit and PR titles begin with [ComponentName], and are in imperative form: "[Component] Fix leaky abstraction".
  • Description explains the issue / use-case resolved, and auto-closes the related issue(s) (http://tr.im/vFqem).

First of all Thank you for the awesome library. I love how every thing just works. One thing that is missing is a layout component. I've been custom rolling the awesome angular material with react. As I am following the changes to the next branch, I see the potential to contribute my little Layout component. This is only a very basic layout component. Appreciate all the feedback. Thank you.

This is an initial take on layout feature heavily influenced by angular material. There are 2 components exposed here.

  • <Block /> Component. Works very similar to the layout attributes in angular material
  • withLayout HOC. which wraps any component with layout properties which gets rendered as className property.

Also included is the option to set layout properties for media query breakpoints.

Example use of <Block /> component

import { List, ListItem, Toolbar, ToolbarGroup } from 'material-ui';
import { Block } from 'material-ui/Layout';
import { Link } from 'react-router';
import SvgIconHome from 'material-ui/svg-icons/action/home';

const ToolbarBlock = withLayout(Toolbar);
const BlockLink = withLayout(Link);

export default (props) => (
  <Block flex layout="column">
    <Block flex="none">
      <ToolbarBlock layout="row">
        <ToolbarGroup>
          <Block layout="row" align="center" justify="start">
            <Block hide={['xs', 'sm']}>Hello</Block>
            <IconButton><SvgIconHome /></IconButton>
          </Block>
        </ToolbarGroup>
      </ToolbarBlock>
    </Block>
    <Block flex layout="row" scroll>
     <Block flex="33">
        <List>
          <ListItem primaryText="Inbox" />
          <ListItem primaryText="Starred" />
          <ListItem primaryText="Sent mail" />
          <ListItem primaryText="Drafts" />
          <ListItem primaryText="Inbox" />
        </List>
      </Block>
      <List>
        <ListItem primaryText="Inbox" />
        <ListItem primaryText="Starred" />
        <ListItem primaryText="Sent mail" />
        <ListItem primaryText="Drafts" />
        <ListItem primaryText="Inbox" />
      </List>  
    </Block>
  </Block>
);

Suntharesan Mohan added 3 commits September 27, 2016 15:40
Add `priority` and  `isMatch` properties to `createBreakpoints`. priority is large to small.
This is an initial take on layout feature heavily influenced by [angular material](https://material.angularjs.org/latest/layout/introduction). There are 2 components  exposed here.

- `<Block />` Component. Works very similar to the `layout` attributes in **angular material**
- `withLayout` HOC. which wraps any component with layout properties which gets rended as `className` property.

Also included is the option to set layout properties for media query breakpoints.

Example use of `<Block />` component

```jsx
import { List, ListItem, Toolbar, ToolbarGroup } from 'material-ui';
import { Block } from 'material-ui/Layout';
import { Link } from 'react-router';
import SvgIconHome from 'material-ui/svg-icons/action/home';

const ToolbarBlock = withLayout(Toolbar);
const BlockLink = withLayout(Link);

export default (props) => (
  <Block flex layout="column">
    <Block flex="none">
      <ToolbarBlock layout="row">
        <ToolbarGroup>
          <Block layout="row" align="center" justify="start">
            <Block hide={['xs', 'sm']}>Hello</Block>
            <IconButton><SvgIconHome /></IconButton>
          </Block>
        </ToolbarGroup>
      </ToolbarBlock>
    </Block>
    <Block flex layout="column" scroll>
      <List>
        <ListItem primaryText="Inbox" />
        <ListItem primaryText="Starred" />
        <ListItem primaryText="Sent mail" />
        <ListItem primaryText="Drafts" />
        <ListItem primaryText="Inbox" />
      </List>
    </Block>
  </Block>
);
```
@msuntharesan msuntharesan changed the title Next [Layout]: Add a new Layout components Sep 27, 2016
@msuntharesan msuntharesan changed the title [Layout]: Add a new Layout components [Layout]: Add new Layout components Sep 27, 2016
@stunaz stunaz mentioned this pull request Sep 29, 2016
Copy link
Member

@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.

Wow, that seems like a quite evolved solution.
I'm still wondering about the problem solved.
How is the problem solved related to the Material Design Specification?

Well, I start to find some answer to this question under the Layout section. Feels like it's going in the right direction

  • It's based on a 12 column grid system
  • They focus on consistent margins and gutters
  • A persistent drawer pushs the responsive grid
  • A temporary drawer does not affect the grid

return { keys, values, up, down, only, getWidth };
let matched = priority
.reduce((a, k) => {
const media = window.matchMedia(breakpoints[k]);
Copy link
Member

Choose a reason for hiding this comment

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

How does matchMedia behave when doing server-side rendering?

Copy link
Author

@msuntharesan msuntharesan Oct 3, 2016

Choose a reason for hiding this comment

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

Thanks for the feedback. Well I haven't fully thought about it. But one way is to add some listeners on componentWillMount and setState. Currently I've been using redux to monitor media query changes.

Copy link
Member

@oliviertassinari oliviertassinari Oct 3, 2016

Choose a reason for hiding this comment

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

My biggest concern is regarding having a correct layout during the first paint. And not waiting for the js to load in order to get the correct one. As far as I know, we can't use the matchMedia API with this constrainte.
What are we using it for? Why not using the CSS media queries?

Copy link
Author

Choose a reason for hiding this comment

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

Well on the flip side, If we do use media queries for all, the number of classes will go up significantly. I am not sure the performance consequence of JSS rendering so many classes in the style tag. If the performance is negligible This can be done.

Copy link
Member

Choose a reason for hiding this comment

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

Well, from my point of view, taking the server-side rendering into account, we don't have much choice.
The other option is not to render the layout on the server to prevent a flash of content.
Hopefully, JSS is quite performant and that shouldn't be a real issue.

- Some of the flex styles weren't applied as expected.
- add some tests for flex
@muibot muibot added PR: out-of-date The pull request has merge conflicts and can't be merged and removed PR: Needs Review labels Oct 5, 2016
@oliviertassinari
Copy link
Member

I have just came across http://stackoverflow.com/questions/33671469/material-ui-and-grid-system. That's just to add some context on how much people are looking for an answer here.
Layout could be one.

@timothyklim
Copy link

Is there any progress?

@rosskevin
Copy link
Member

@timothyklim see this for an alternative: #3614 (comment)

Suntharesan Mohan added 3 commits November 23, 2016 10:38
- fixed `align` PropTypes values
- use camelCase for all classnames
- make it easy to read
Demo to show whats possible with this component and how flexible (no pun intended) and easy it is to work with.
Copy link
Member

@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.

The new docs demo is quite cool 👍 .
The Angular Layout approach is really interesting.
It's quite powerful. I hope we can move this PR forward into next.
Notice that the component would be a good candidate to write test regression against.

return { keys, values, up, down, only, getWidth };
let matched = priority
.reduce((a, k) => {
const media = window.matchMedia(breakpoints[k]);
Copy link
Member

Choose a reason for hiding this comment

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

Well, from my point of view, taking the server-side rendering into account, we don't have much choice.
The other option is not to render the layout on the server to prevent a flash of content.
Hopefully, JSS is quite performant and that shouldn't be a real issue.

/*
* This is the Base <Block /> Element
*/
export default withLayout('div');
Copy link
Member

Choose a reason for hiding this comment

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

I'm curious about this API decision. What's the benefit of having a Higer order Component over a component that accepts a component property?

order = 0,
offset = 0,
align = 'stretch', // Cross Axis
justify = 'start', // Main Axis
Copy link
Member

Choose a reason for hiding this comment

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

I think that it would be better to use the defaultProps of React over a default argument value.
That would allow us to automatically generate the documentation.

Copy link
Author

Choose a reason for hiding this comment

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

Would the defaultProps have impact on the this component being pure?

md,
lg,
xl,
...others
Copy link
Member

Choose a reason for hiding this comment

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

other to be coherent with the code base.

import { createMountWithContext } from 'test/utils';
import withLayout, { styleSheet } from './with-layout';

describe('withFlex HOC', () => {
Copy link
Member

Choose a reason for hiding this comment

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

withLayout?

Copy link
Author

Choose a reason for hiding this comment

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

naming. I am not 100% set on choosing withLayout over withFlex for the HOC. Care to suggest a name?

import IconButton from 'material-ui/IconButton';
import { teal, deepPurple } from 'material-ui/styles/colors';

const styleSheet = createStyleSheet('Interactive', ({ shadows, palette }) => ({
Copy link
Member

Choose a reason for hiding this comment

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

Interactive is in a global namespace. It would be better to scope it.


const flexOrder = () => ({
flexOrder: { order: 0 },
...generateProps((i) => ({ [`flexOrder${i}`]: { order: i } }), -20, 20),
Copy link
Member

Choose a reason for hiding this comment

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

[-20,20] is quite a big range. Would using inline-style for this be a good approach? Or using a smaller window? I know that angular/material is using this value.

});

const boxSizing = 'border-box';
const generateFlexProps = partialRight(generateProps, [0, 20]);
Copy link
Member

Choose a reason for hiding this comment

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

Again, I'm wondering about this 20 granularity, having a smaller set of possibilities could enforce having a more consistent UI.

Copy link
Author

Choose a reason for hiding this comment

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

I think 12 should do it. as it is used in other grid layouts or make it configurable using theme?

cells.map((cell, i) => (
<Block {...cell} layout="row" align="center">
<IconButton accent onClick={this.setCell(i)}>delete</IconButton>
<Button>{`Cell ${i + 1}`}</Button>
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure we need a button here.

Copy link
Author

Choose a reason for hiding this comment

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

I added the button to make it a menu to set flex value to that block. I don't know if I am overdoing the demo.

{
cells.map((cell, i) => (
<Block {...cell} layout="row" align="center">
<IconButton accent onClick={this.setCell(i)}>delete</IconButton>
Copy link
Member

Choose a reason for hiding this comment

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

For the sake of simplicity, I would keep a fixed number of children.

Copy link
Author

Choose a reason for hiding this comment

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

I had it at 3. Then just for fun I made it dynamic.

@nathanmarks
Copy link
Member

nathanmarks commented Nov 27, 2016

Do we know what's going on with the commits here?

@muibot muibot added the PR: out-of-date The pull request has merge conflicts and can't be merged label Nov 30, 2016
Conflicts:
	src/styles/breakpoints.js
@muibot muibot added PR: Needs Review and removed PR: out-of-date The pull request has merge conflicts and can't be merged labels Dec 2, 2016
from `Interactive` on the demo

also make variables coherent
@msuntharesan
Copy link
Author

@nathanmarks I think I rebased instead of merging. If it is a problem I can close this PR and resubmit

@mbrookes
Copy link
Member

mbrookes commented Dec 2, 2016

@VanthiyaThevan You should be able to clean up on a new branch by cherry picking the relevant commits. I see you're developing directly on next, but in general you might find it easier to work on a separate branch, and keep next in sync with upstream.

also generate property name based on type.

add test for offset and order
@oliviertassinari
Copy link
Member

oliviertassinari commented Dec 5, 2016

@VanthiyaThevan Hey, I have been implementing a LayoutBlock component at the office taking quite some inspiration from this PR (Angular inspiration) and @rosskevin one (flexboxgrid, Bootstrap inspiration). I'm will share the API I have in mind with you.

We have a tradeoff to make between how much things can be customized depending on the screen size and the overall CSS we inject.

@msuntharesan
Copy link
Author

msuntharesan commented Dec 7, 2016

@mbrookes Thanks for the advice. I've already stated doing that.

@oliviertassinari That sounds cool. I would love to see your take on this.

As for the server side rendering and media query, we could incorporate react-media. I've never tried it but seem interesting

@msuntharesan
Copy link
Author

I have a dilemma with regards to the documentation. Right now the document doesn't get generaged because the proptypes are defined inside the HOC. The solutions I came up to get around are

  • Drop the HOC and go with just the Block component
  • switch to it flow type and export it

May be another option is make document generator to look for HOC components as well. Any suggestion is appreciated.

@oliviertassinari
Copy link
Member

oliviertassinari commented Dec 21, 2016

@VanthiyaThevan Hey, I have started working on that Layout component by giving it a shot in #5808. I have been using quite some different library as inspiration, included your PR that helped me. My version is quite simple. I would love to get your feedback.

@oliviertassinari
Copy link
Member

oliviertassinari commented Dec 25, 2016

@VanthiyaThevan Thanks a lot for your effort here 💯 . I wouldn't have been able to get #5808 out without your PR!
If you find anything that could be improved, fell free to raise your voice / open a PR. Thanks.

A preview is available here.

@emaillenin
Copy link

@oliviertassinari Can you share the source of that preview? It has the source only for specific sections. I am looking for the code to make the AppBar with menu responsive.

@oliviertassinari
Copy link
Member

@emaillenin Everything is public, it's from that PR: #5808.

@msuntharesan
Copy link
Author

Thanks. Glad to have been able to help. This is cool. Lot simpler and a lot less classes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
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.