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

[Table] pagination / infinite scrolling #1511

Closed
zachguo opened this issue Aug 25, 2015 · 28 comments
Closed

[Table] pagination / infinite scrolling #1511

zachguo opened this issue Aug 25, 2015 · 28 comments
Labels
component: table This is the name of the generic UI component, not the React module! new feature New feature or request

Comments

@zachguo
Copy link
Contributor

zachguo commented Aug 25, 2015

@jkruder In addition to sorting, we would love to give this a shot, too. Do you have any suggestion about implementation?

@jkruder
Copy link
Contributor

jkruder commented Aug 26, 2015

If you're interested in infinite scrolling, I would take a look at: https://github.com/facebook/fixed-data-table. Might find some inspiration there.

If you want to pursue pagination I would suggest creating the pagination UI; next/prev buttons, page number buttons with an ellipsis and provide a callback that simply communicates what page was selected. This will allow something like redux to be utilized for the paging and then the consumer of the table can populate it with the new table rows. What do you think?

@zachguo
Copy link
Contributor Author

zachguo commented Aug 26, 2015

My rough idea is that the Table would detect and keep track of scrolling positions, and have a callback onScrollToEnd. Pagination logic(either client or server side) would be handled by user outside of Table. Pros and cons of this approach are similar to those of my approach for sorting.

@jkruder
Copy link
Contributor

jkruder commented Aug 26, 2015

The infinite scroll is best implemented when you have all of the data and can control the heights of each row. Currently, we do not enforce a specific row height so determining how many rows to insert at a given point becomes more complicated. My vote would be to keep the table simple and provide paging hooks -- or, create a decorator that will wrap a table and provide pagination controls. See https://www.google.com/design/spec/components/data-tables.html#data-tables-tables-within-cards for an example of the paging controls.

@zachguo
Copy link
Contributor Author

zachguo commented Aug 26, 2015

I see, thanks for sharing!

@sjstebbins
Copy link
Contributor

@jkruder @zachguo I have implemented infinite scrolling as a prop 'infiniteScroll' with true or false acceptance and 'infiniteScrollOffset' which accepts a number indicating at many pixels from the bottom of the table should more rows be loaded on scroll. I am passing mapped state items as children to the tableBody which creates the TableRows based on the 'page' set by scroll position, but I believe I can move all this logic to within the tableBody component itself. I will submit a PR for this soon. Thanks

@oliviertassinari
Copy link
Member

You could also try https://github.com/orgsync/react-list for the infinit scroll.

@alitaheri alitaheri added the new feature New feature or request label Dec 8, 2015
@kodermax
Copy link
Contributor

kodermax commented Jan 6, 2016

When do you plan to do pagination?

@zachguo
Copy link
Contributor Author

zachguo commented Jan 6, 2016

@kodermax logics for pagination can be handled by user outside MUI. For creating a table footer similar to the specs of material design you can try following code snippet.

// footer.jsx

import React from 'react';
import {TableFooter as TF, TableRow, TableRowColumn, FontIcon, IconButton} from 'material-ui';

const styles = {
  footerContent: {
    float: 'right'
  },
  footerText: {
    float: 'right',
    paddingTop: 16,
    height: 16
  }
};

const TableFooter = React.createClass({

  propTypes: {
    offset: React.PropTypes.number.isRequired, // current offset
    total: React.PropTypes.number.isRequired, // total number of rows
    limit: React.PropTypes.number.isRequired, // num of rows in each page
    onPageClick: React.PropTypes.func // what to do after clicking page number
  },

  render() {
    let offset = this.props.offset;
    let total = this.props.total;
    let limit = this.props.limit;
    return (
      <TF adjustForCheckbox={false}>
        <TableRow>
          <TableRowColumn style={styles.footerContent}>
            <IconButton disabled={offset === 0} onClick={this.props.onPageClick.bind(null, offset - limit)}>
              <FontIcon className="material-icons">chevron_left</FontIcon>
            </IconButton>
            <IconButton disabled={offset + limit >= total} onClick={this.props.onPageClick.bind(null, offset + limit)}>
              <FontIcon className="material-icons">chevron_right</FontIcon>
            </IconButton>
          </TableRowColumn>
          <TableRowColumn style={styles.footerText}>
            {Math.min((offset + 1), total) + '-' + Math.min((offset + limit), total) + ' of ' + total}
          </TableRowColumn>
        </TableRow>
      </TF>
    );
  }

});

export default TableFooter;

screen shot 2016-01-06 at 11 54 11 am

@oliviertassinari
Copy link
Member

@zachguo That looks good. Do you think we could add it to the lib?

@zachguo
Copy link
Contributor Author

zachguo commented Jan 6, 2016

I can do a PR if users find it helpful.

@kodermax
Copy link
Contributor

kodermax commented Jan 7, 2016

send PR, we will improve

@salfej
Copy link

salfej commented Feb 17, 2016

I'd be interested in a pagination implementation. Been trying out the footer bit @zachguo posted and it looks good.

@Dombo
Copy link

Dombo commented May 2, 2016

@zachguo since the changes in v15.0 pertaining to module imports, my module loading approach would not render the TableFooter component. I couldn't find a suitable workaround for that so instead I slightly modified the structure of your component, see below for anyone interested in using.

import Component from 'react-pure-render/component';
import React, { PropTypes } from 'react';
import ChevronLeft from 'material-ui/svg-icons/navigation/chevron-left';
import ChevronRight from 'material-ui/svg-icons/navigation/chevron-right';
import IconButton from 'material-ui/IconButton';

const styles = {
  footerContent: {
    float: 'right'
  },
  footerText: {
    float: 'right',
    paddingTop: '16px',
    height: '16px'
  }
};

class PagiFooter extends Component {

  constructor(props) {
    super(props);
  }

  static propTypes = {
    offset: PropTypes.number.isRequired, // current offset
    total: PropTypes.number.isRequired, // total number of rows
    limit: PropTypes.number.isRequired, // num of rows in each page
    onPageClick: PropTypes.func.isRequired // what to do after clicking page number
  }

  render() {

    let { offset, total, limit } = this.props;

    return (
        <div style={styles.footerContent}>
          <IconButton disabled={offset === 0} onClick={this.props.onPageClick.bind(null, offset - limit)}>
            <ChevronLeft/>
          </IconButton>
          <IconButton disabled={offset + limit >= total} onClick={this.props.onPageClick.bind(null, offset + limit)}>
            <ChevronRight/>
          </IconButton>
          {Math.min((offset + 1), total) + '-' + Math.min((offset + limit), total) + ' of ' + total}
        </div>
    );
  }

}

export default PagiFooter;

Including the component as so:

          <TableFooter>
            <TableRow>
              <TableRowColumn colSpan="3">
                <PagiFooter offset={1} total={2} limit={3} onPageClick={this.pagiFunction}/>
              </TableRowColumn>
            </TableRow>
          </TableFooter>

I couldn't determine what was causing your component not to render - I believe it might be some sort of namespacing collision with the module loading approach. I'd be curious to hear any possible culprits. Thanks anyway.

@rs6g10
Copy link

rs6g10 commented May 5, 2016

So based on implementation given by @zachguo I was able to add pagination and it works just fine. Thanks for your comments. Following is the complete implementation code (except for methods):

<Table
        height={this.state.height}
        fixedHeader={this.state.fixedHeader}
        fixedFooter={this.state.fixedFooter}
        selectable={false}
        multiSelectable={false}
      >
        <TableHeader displaySelectAll={false} adjustForCheckbox={false}>
          <TableRow>
            {this.tableData ? Object.keys(this.tableData[0]).map((key, idx) => (
              <TableHeaderColumn>{Utils.toHeaderCase(key)}</TableHeaderColumn>
            ))
              : ''
            }

          </TableRow>
        </TableHeader>
        <TableBody
          deselectOnClickaway={this.state.deselectOnClickaway}
          displayRowCheckbox={false}
          showRowHover={this.state.showRowHover}
          stripedRows={this.state.stripedRows}
        >
          {this.tableData ? this.tableData.map((row, index) => (
            <TableRow key={index} selected={row.selected}>
              {Object.keys(row).map((prop, ind) => (
                <TableRowColumn>{row[prop]}</TableRowColumn>
              ))
              }
            </TableRow>
          )) : ''}
        </TableBody>
        <TableFooter>
          <TableRow>
            <TableRowColumn style={styles.footerContent}>
              <IconButton onClick={() => this.getPreviousPage()} disabled={!(this.props.pagination.hasOwnProperty('prev'))}>
                <ChevronLeft/>
              </IconButton>
              <IconButton onClick={() => this.getNextPage()} disabled={!(this.props.pagination.hasOwnProperty('next'))}>
                <ChevronRight/>
              </IconButton>
            </TableRowColumn>
            <TableRowColumn style={styles.footerText} />
          </TableRow>
        </TableFooter>
      </Table>

@mbrookes
Copy link
Member

mbrookes commented May 5, 2016

@rs6g10 - that looks like a docs example in the making. Care to submit a PR?

@zachguo
Copy link
Contributor Author

zachguo commented May 10, 2016

@Dombo I upgraded MUI to v0.15 today and ran into same issue. I don't know what happened, thanks for your workaround.

@zachguo
Copy link
Contributor Author

zachguo commented May 10, 2016

@Dombo hmm, even your workaround didn't work for me. What I got is just an empty tr... PagiFooter was ignored and not rendered at all...

Actually no matter what I put into

<TableFooter adjustForCheckbox={false}>
  <TableRow>
    <TableRowColumn colSpan="3">
      ...
    </TableRowColumn>
  </TableRow>
</TableFooter>

got ignored.... Do you know why?

@Dombo
Copy link

Dombo commented May 10, 2016

@zachguo I also struggled with that issue - my guess is that it's related to the TableRow component.

      var rowColumns = _react2.default.Children.map(this.props.children, function (child, columnNumber) {
        if (_react2.default.isValidElement(child)) {
          return _react2.default.cloneElement(child, {
            columnNumber: columnNumber,
            hoverable: _this2.props.hoverable,
            key: _this2.props.rowNumber + '-' + columnNumber,
            onClick: _this2.onCellClick,
            onHover: _this2.onCellHover,
            onHoverExit: _this2.onCellHoverExit,
            style: (0, _simpleAssign2.default)({}, styles.cell, child.props.style)
          });
        }
      });

      return _react2.default.createElement(
        'tr',
        _extends({
          className: className,
          style: prepareStyles((0, _simpleAssign2.default)(styles.root, style))
        }, other),
        rowColumns
      );

Specifically

 if (_react2.default.isValidElement(child)) {

Is my PagiFooter somehow not a valid react element?

@zachguo
Copy link
Contributor Author

zachguo commented May 10, 2016

@Dombo Thanks for your reply! I don't think that's the case, it didn't work even if I simply put a string inside TableRowColumn... 😕

@pulse00
Copy link

pulse00 commented May 12, 2016

@Dombo @zachguo i figured it out. When you create a custom component wrapping TableFooter, you need to

  1. Omit the inner TableFooter element
  2. Make the wrapping component extend React.Component
  3. And set the muiName property on the wrapped component:
class MyCustomTableFooter extends React.Component {
  render() {
    return (
      <TableRow>
        <TableRowColumn>
          <h1>This is my custom table footer</h1>
        </TableRowColumn>
      </TableRow>
    );
  }
}

MyCustomTableFooter.muiName = 'TableFooter';

@arition
Copy link

arition commented Jul 26, 2016

@Dombo @zachguo
I think it is a bug caused by <TableFooter adjustForCheckbox={false}>
If adjustForCheckbox={false} is removed, the footer shows as usual.
Related Issues: #4581

@nathanmarks
Copy link
Member

@zachguo In the next major version we'll be including a regular table and an example of a material styled infinite/virtualized data grid component.

@NeXTs
Copy link

NeXTs commented Aug 10, 2016

@nathanhere and when you plan to release new major version?

@lucasbento lucasbento added the component: table This is the name of the generic UI component, not the React module! label Oct 15, 2016
@rodrigowippel
Copy link

@rs6g10 @Dombo
Nice example, It helped a lot!

Can you give an ideia how to brake the list in pages?

Thanks,

@0xBADCA7
Copy link

0xBADCA7 commented Nov 2, 2016

Meanwhile I'll just leave this here https://github.com/ultimate-pagination/react-ultimate-pagination-material-ui

@rodrigowippel
Copy link

@Dombo Could you post your method to calculate the offset?

@greatermeans
Copy link

has anyone made any progress for infinite horizontal scroll?

@oliviertassinari oliviertassinari added component: table This is the name of the generic UI component, not the React module! and removed component: table This is the name of the generic UI component, not the React module! labels Dec 18, 2016
@zachguo
Copy link
Contributor Author

zachguo commented Jan 19, 2017

I gonna close this, guys. Few takeaways from this thread:

  • logics for pagination and infinite scrolling should be handled by user/you not MUI
  • examples for TableFooter can be found above
  • "next major version we'll be including a regular table and an example of a material styled infinite/virtualized data grid component." [Table] pagination / infinite scrolling #1511 (comment)

If you are open to design specs other than Material Design, take a look at Ant Design's table component.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: table This is the name of the generic UI component, not the React module! new feature New feature or request
Projects
None yet
Development

No branches or pull requests