Material-ui: [List] nestedItems support in @next

Created on 7 Jul 2017  路  14Comments  路  Source: mui-org/material-ui

@oliviertassinari is the nestedItems prop removed from the new ListItem component or is it about to be ported sooner or later?

Thanks for your work!

List question

Most helpful comment

Long story made short: wrap ListItem in a div and an adjacent Collapse container with nested ListItems and pad it when nesting.
Credits to @oliviertassinari (I think Fade component is not working flawlessly as Collapse!).

out

My solution for reference:

import React from 'react';
import PropTypes from 'prop-types';
import autobind from 'autobind-decorator';
import { connect } from 'react-redux';
import { startCase, map } from 'lodash-es';

import classNames from 'classnames';

import { withStyles, createStyleSheet } from 'material-ui/styles';

import Paper from 'material-ui/Paper';
import List, { ListItem, ListItemSubheader, ListItemIcon, ListItemText, ListItemSecondaryAction } from 'material-ui/List';
import Badge from 'material-ui/Badge';
import IconButton from 'material-ui/IconButton';
import Divider from 'material-ui/Divider';
import Switch from 'material-ui/Switch';
import Collapse from 'material-ui/transitions/Collapse';

import {
    NavigationExpandMore as ExpandMoreIcon,
    NavigationExpandLess as ExpandLessIcon,
} from '@damianobarbati/mdi';

const styleSheet = createStyleSheet('Nav', theme => ({
    root: {
    },
    subList: {
        paddingLeft: '18px',
    },
}));

@connect((state, props) => ({
}))
@withStyles(styleSheet)
export default class Nav extends React.Component {
    static propTypes = {
        dispatch: PropTypes.func.isRequired,
        classes: PropTypes.object,
        entries: PropTypes.array.isRequired,
        secondaryEntries: PropTypes.array,
    }

    componentWillMount () {
        this.setState({});
    }

    render () {
        const { classes, entries, secondaryEntries = [] } = this.props;

        const renderListItem = ({ name, caption, icon, secondaryIcon, notificationsCount, action, secondaryAction, entries = [] }, nested = false) => {
            const expanded = !!this.state[name];
            const expand = () => this.setState({ [name]: !expanded });

            return (
                <div key={name} className={classNames({ [classes.subList]: nested })} >
                    <ListItem dense={false} disableGutters={false} button={true}>

                        { icon && notificationsCount &&
                        <ListItemIcon onClick={action}>
                            <Badge badgeContent={notificationsCount || ''}>{icon}</Badge>
                        </ListItemIcon>
                        }
                        { icon && !notificationsCount &&
                        <ListItemIcon className={entries.length ? classes.listItemIconWithNested : ''} onClick={action}>
                            {icon}
                        </ListItemIcon>
                        }
                        <ListItemText primary={name} secondary={caption} onClick={action} className={entries.length ? classes.listItemTextWithNested : ''} />
                        { (secondaryIcon || !!entries.length) &&
                        <ListItemSecondaryAction onClick={entries.length ? expand : secondaryAction} className={entries.length ? classes.listItemSecondaryActionWithNested : ''}>
                            <IconButton onClick={entries.length ? expand : secondaryAction}>{entries.length ? (!expanded ? <ExpandMoreIcon /> : <ExpandLessIcon />) : secondaryIcon}</IconButton>
                        </ListItemSecondaryAction>
                        }
                    </ListItem>
                    { !!entries.length &&
                    <Collapse  in={expanded} transitionDuration={'auto'} unmountOnExit>
                        { entries.map(entry => renderListItem(entry, true)) }
                    </Collapse>
                    }
                </div>
            );
        };

        return (
            <Paper className={classes.root}>
                <List>
                    { entries.map(entry => renderListItem(entry)) }
                </List>

                <Divider />

                <List>
                    { secondaryEntries.map(entry => renderListItem(entry)) }
                </List>
            </Paper>
        );
    }
}

All 14 comments

Do we need it? The documentation is nesting the ListItem without much issue. I guess extra flexibility is allowing us to remove that feature.

Where is the doc using nested ListItem? I tried to browse the /docs path of the alpha but I didn't find nested ListItem 馃

BTW GitHub search feature sucks, you cannot search in specific branch :

Here is the AppDrawerNavItem nesting the ListItem.

@damianobarbati ListItem can accept other ListItems as children. There is no need for a dedicated _nestedItems_ property since the _children_ property can serve this purpose.

Sounds fair, I'm closing it.

@oliviertassinari @kgregory I'm adopting the docs solution with further css tweaking which is necessary to proper align text and secondary action icon: the previous nestedItems taking care of everything was handy!

@damianobarbati Do you see room for a good abstraction here? That's something we could think of, just want to keep things as simple and flexible as possible.

@oliviertassinari honestly the only scenario I can see for nesting/using children here is to render nested list items just like the old nestedItems used to do: every other case can be handled with the left/middle/right content/icon.
That's why I don't think we should abstract here but rather provide the specific and most used case without having the devs taking care of this everytime.

This is my personal opinion though: I agree with keeping everything as clean as possible and let devs extends functionalities when needed! :)

Long story made short: wrap ListItem in a div and an adjacent Collapse container with nested ListItems and pad it when nesting.
Credits to @oliviertassinari (I think Fade component is not working flawlessly as Collapse!).

out

My solution for reference:

import React from 'react';
import PropTypes from 'prop-types';
import autobind from 'autobind-decorator';
import { connect } from 'react-redux';
import { startCase, map } from 'lodash-es';

import classNames from 'classnames';

import { withStyles, createStyleSheet } from 'material-ui/styles';

import Paper from 'material-ui/Paper';
import List, { ListItem, ListItemSubheader, ListItemIcon, ListItemText, ListItemSecondaryAction } from 'material-ui/List';
import Badge from 'material-ui/Badge';
import IconButton from 'material-ui/IconButton';
import Divider from 'material-ui/Divider';
import Switch from 'material-ui/Switch';
import Collapse from 'material-ui/transitions/Collapse';

import {
    NavigationExpandMore as ExpandMoreIcon,
    NavigationExpandLess as ExpandLessIcon,
} from '@damianobarbati/mdi';

const styleSheet = createStyleSheet('Nav', theme => ({
    root: {
    },
    subList: {
        paddingLeft: '18px',
    },
}));

@connect((state, props) => ({
}))
@withStyles(styleSheet)
export default class Nav extends React.Component {
    static propTypes = {
        dispatch: PropTypes.func.isRequired,
        classes: PropTypes.object,
        entries: PropTypes.array.isRequired,
        secondaryEntries: PropTypes.array,
    }

    componentWillMount () {
        this.setState({});
    }

    render () {
        const { classes, entries, secondaryEntries = [] } = this.props;

        const renderListItem = ({ name, caption, icon, secondaryIcon, notificationsCount, action, secondaryAction, entries = [] }, nested = false) => {
            const expanded = !!this.state[name];
            const expand = () => this.setState({ [name]: !expanded });

            return (
                <div key={name} className={classNames({ [classes.subList]: nested })} >
                    <ListItem dense={false} disableGutters={false} button={true}>

                        { icon && notificationsCount &&
                        <ListItemIcon onClick={action}>
                            <Badge badgeContent={notificationsCount || ''}>{icon}</Badge>
                        </ListItemIcon>
                        }
                        { icon && !notificationsCount &&
                        <ListItemIcon className={entries.length ? classes.listItemIconWithNested : ''} onClick={action}>
                            {icon}
                        </ListItemIcon>
                        }
                        <ListItemText primary={name} secondary={caption} onClick={action} className={entries.length ? classes.listItemTextWithNested : ''} />
                        { (secondaryIcon || !!entries.length) &&
                        <ListItemSecondaryAction onClick={entries.length ? expand : secondaryAction} className={entries.length ? classes.listItemSecondaryActionWithNested : ''}>
                            <IconButton onClick={entries.length ? expand : secondaryAction}>{entries.length ? (!expanded ? <ExpandMoreIcon /> : <ExpandLessIcon />) : secondaryIcon}</IconButton>
                        </ListItemSecondaryAction>
                        }
                    </ListItem>
                    { !!entries.length &&
                    <Collapse  in={expanded} transitionDuration={'auto'} unmountOnExit>
                        { entries.map(entry => renderListItem(entry, true)) }
                    </Collapse>
                    }
                </div>
            );
        };

        return (
            <Paper className={classes.root}>
                <List>
                    { entries.map(entry => renderListItem(entry)) }
                </List>

                <Divider />

                <List>
                    { secondaryEntries.map(entry => renderListItem(entry)) }
                </List>
            </Paper>
        );
    }
}

@damianobarbati Thanks for sharing the solution 馃槃 !

Also, why not using the material-ui-icons package?

Because I'm heavily using "community" material-design-icons as well 馃槩
I had to create a repo merging both the worlds here => https://www.npmjs.com/package/@damianobarbati/mdi

Oh, ok with https://materialdesignicons.com/ as well.

Yep!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mattmiddlesworth picture mattmiddlesworth  路  3Comments

sys13 picture sys13  路  3Comments

TimoRuetten picture TimoRuetten  路  3Comments

finaiized picture finaiized  路  3Comments

rbozan picture rbozan  路  3Comments