diff options
author | Quentin Gliech <quentingliech@gmail.com> | 2019-05-22 19:51:33 +0200 |
---|---|---|
committer | Quentin Gliech <quentingliech@gmail.com> | 2019-05-22 19:52:01 +0200 |
commit | 51ca8527dc94dbe619da98e84836340313326f7d (patch) | |
tree | c8e858dc9506d568578040c32b93d928cb101916 | |
parent | d79ef7a7945ba82caeec62cad44dad134c9edfbc (diff) | |
download | git-bug-51ca8527dc94dbe619da98e84836340313326f7d.tar.gz |
webui: Rework pagination
-rw-r--r-- | webui/package-lock.json | 192 | ||||
-rw-r--r-- | webui/package.json | 1 | ||||
-rw-r--r-- | webui/src/list/List.js | 160 | ||||
-rw-r--r-- | webui/src/list/ListQuery.js | 39 |
4 files changed, 237 insertions, 155 deletions
diff --git a/webui/package-lock.json b/webui/package-lock.json index 50f8d797..268525c3 100644 --- a/webui/package-lock.json +++ b/webui/package-lock.json @@ -849,6 +849,11 @@ "resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz", "integrity": "sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==" }, + "@emotion/hash": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.7.1.tgz", + "integrity": "sha512-OYpa/Sg+2GDX+jibUfpZVn1YqSVRpYmTLF2eyAfrFTIJSbwyIrc+YscayoykvaOME/wV4BV0Sa0yqdMrgse6mA==" + }, "@material-ui/core": { "version": "3.9.3", "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-3.9.3.tgz", @@ -892,6 +897,41 @@ "recompose": "0.28.0 - 0.30.0" } }, + "@material-ui/styles": { + "version": "3.0.0-alpha.10", + "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-3.0.0-alpha.10.tgz", + "integrity": "sha512-qJ5eiupBPRCNlMCDZ2G5h8auBtBtm8uT/oCUAJ/FqhO5oC7POLmmvDN1Cq1cgAmqQnaL6uN5mAM1Gc90GpKr9A==", + "requires": { + "@babel/runtime": "^7.2.0", + "@emotion/hash": "^0.7.1", + "@material-ui/utils": "^3.0.0-alpha.2", + "classnames": "^2.2.5", + "deepmerge": "^3.0.0", + "hoist-non-react-statics": "^3.2.1", + "jss": "^10.0.0-alpha.7", + "jss-plugin-camel-case": "^10.0.0-alpha.7", + "jss-plugin-default-unit": "^10.0.0-alpha.7", + "jss-plugin-global": "^10.0.0-alpha.7", + "jss-plugin-nested": "^10.0.0-alpha.7", + "jss-plugin-props-sort": "^10.0.0-alpha.7", + "jss-plugin-rule-value-function": "^10.0.0-alpha.7", + "jss-plugin-vendor-prefixer": "^10.0.0-alpha.7", + "prop-types": "^15.6.0", + "warning": "^4.0.1" + }, + "dependencies": { + "jss": { + "version": "10.0.0-alpha.16", + "resolved": "https://registry.npmjs.org/jss/-/jss-10.0.0-alpha.16.tgz", + "integrity": "sha512-HmKNNnr82TR5jkWjBcbrx/uim2ief588pWp7zsf4GQpL125zRkEaWYL1SXv5bR6bBvAoTtvJsTAOxDIlLxUNZg==", + "requires": { + "@babel/runtime": "^7.3.1", + "is-in-browser": "^1.1.3", + "tiny-warning": "^1.0.2" + } + } + } + }, "@material-ui/system": { "version": "3.0.0-alpha.2", "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-3.0.0-alpha.2.tgz", @@ -3103,7 +3143,8 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true + "bundled": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -3121,11 +3162,13 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true + "bundled": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -3138,15 +3181,18 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "concat-map": { "version": "0.0.1", - "bundled": true + "bundled": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -3249,7 +3295,8 @@ }, "inherits": { "version": "2.0.3", - "bundled": true + "bundled": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -3259,6 +3306,7 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -3271,17 +3319,20 @@ "minimatch": { "version": "3.0.4", "bundled": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "bundled": true + "bundled": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -3298,6 +3349,7 @@ "mkdirp": { "version": "0.5.1", "bundled": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -3370,7 +3422,8 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -3380,6 +3433,7 @@ "once": { "version": "1.4.0", "bundled": true, + "optional": true, "requires": { "wrappy": "1" } @@ -3455,7 +3509,8 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true + "bundled": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -3485,6 +3540,7 @@ "string-width": { "version": "1.0.2", "bundled": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3502,6 +3558,7 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3540,11 +3597,13 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true + "bundled": true, + "optional": true }, "yallist": { "version": "3.0.3", - "bundled": true + "bundled": true, + "optional": true } } }, @@ -6721,7 +6780,8 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true + "bundled": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -6739,11 +6799,13 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true + "bundled": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -6756,15 +6818,18 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "concat-map": { "version": "0.0.1", - "bundled": true + "bundled": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -6867,7 +6932,8 @@ }, "inherits": { "version": "2.0.3", - "bundled": true + "bundled": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -6877,6 +6943,7 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -6889,17 +6956,20 @@ "minimatch": { "version": "3.0.4", "bundled": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "bundled": true + "bundled": true, + "optional": true }, "minipass": { "version": "2.2.4", "bundled": true, + "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -6916,6 +6986,7 @@ "mkdirp": { "version": "0.5.1", "bundled": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -6988,7 +7059,8 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -6998,6 +7070,7 @@ "once": { "version": "1.4.0", "bundled": true, + "optional": true, "requires": { "wrappy": "1" } @@ -7073,7 +7146,8 @@ }, "safe-buffer": { "version": "5.1.1", - "bundled": true + "bundled": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -7103,6 +7177,7 @@ "string-width": { "version": "1.0.2", "bundled": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -7120,6 +7195,7 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -7158,11 +7234,13 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true + "bundled": true, + "optional": true }, "yallist": { "version": "3.0.2", - "bundled": true + "bundled": true, + "optional": true } } }, @@ -9380,6 +9458,76 @@ } } }, + "jss-plugin-camel-case": { + "version": "10.0.0-alpha.7", + "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.0.0-alpha.7.tgz", + "integrity": "sha512-Bwrav1ZB0XywdJW6TaEuFhKe1ZpZvUlESh3jsFOvebA9aFTYNCkmHMEqjA5+u9VMxksl3u77nnZHtukpxkzrBA==", + "requires": { + "@babel/runtime": "^7.0.0", + "hyphenate-style-name": "^1.0.2" + } + }, + "jss-plugin-default-unit": { + "version": "10.0.0-alpha.7", + "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.0.0-alpha.7.tgz", + "integrity": "sha512-auuJUbQaWMxoHOVFPrfZNZpZm9ab8PZeDyvey8nMt2lbokkmZ53UyAnM/1kNsg5BdAXTItcLDxDB3I4gwNU84g==", + "requires": { + "@babel/runtime": "^7.0.0" + } + }, + "jss-plugin-global": { + "version": "10.0.0-alpha.7", + "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.0.0-alpha.7.tgz", + "integrity": "sha512-OWeoW4szLDgRUKviST+xfilqa8O5uXJCW+O3YonheCRTRJg6rRzlE/b5pfYPoU9UtwvY9n7JvwBX5r3c1lMsEQ==", + "requires": { + "@babel/runtime": "^7.0.0" + } + }, + "jss-plugin-nested": { + "version": "10.0.0-alpha.7", + "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.0.0-alpha.7.tgz", + "integrity": "sha512-wsRzuIZXAc6WMjc61mREW9cUrDxgSI7dK/fx5c7a06IDUfSn+83NJ30J/RB4oBnbQW9SijV/muujz7IJqpn9Gw==", + "requires": { + "@babel/runtime": "^7.0.0", + "tiny-warning": "^1.0.2" + } + }, + "jss-plugin-props-sort": { + "version": "10.0.0-alpha.7", + "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.0.0-alpha.7.tgz", + "integrity": "sha512-KXOCaHUk1+KXqE0z3q66/w1fDoy+VsZvI77gLxOqTsTrvIKFLX0jarwXogW3CDlaPQQFTZ6JykJJXtPRTBlstA==", + "requires": { + "@babel/runtime": "^7.0.0" + } + }, + "jss-plugin-rule-value-function": { + "version": "10.0.0-alpha.7", + "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.0.0-alpha.7.tgz", + "integrity": "sha512-ett83hvIM69/LknmrWndrrdiDlfLfP+rneU5qP7gTOWJ7g1P9GuEL1Tc4CWdZUWBX+T58tgIBP0V1pzWCkP0QA==", + "requires": { + "@babel/runtime": "^7.0.0" + } + }, + "jss-plugin-vendor-prefixer": { + "version": "10.0.0-alpha.7", + "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.0.0-alpha.7.tgz", + "integrity": "sha512-YbIVgqq+dLimOBOEYggho1Iuc0roz4PJSZYyaok9n8JnXVIqPnxYJbr8+bMbvzJ5CL3eeJij/e7L2IPCceRKrA==", + "requires": { + "@babel/runtime": "^7.0.0", + "css-vendor": "^1.1.0" + }, + "dependencies": { + "css-vendor": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-1.2.1.tgz", + "integrity": "sha512-ZpwiWxn5jWNJ7NF3DAb/Dc/+c2lRu+fnovej/adCv3VJsULJSjdXEpUwRcq4fnpAAh98Hi7b0GDnlyoNFcdv1g==", + "requires": { + "@babel/runtime": "^7.3.1", + "is-in-browser": "^1.0.2" + } + } + } + }, "jss-props-sort": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/jss-props-sort/-/jss-props-sort-6.0.0.tgz", diff --git a/webui/package.json b/webui/package.json index c5ccef9a..e8ebebac 100644 --- a/webui/package.json +++ b/webui/package.json @@ -5,6 +5,7 @@ "dependencies": { "@material-ui/core": "^3.9.3", "@material-ui/icons": "^3.0.2", + "@material-ui/styles": "^3.0.0-alpha.10", "apollo-boost": "^0.3.1", "graphql": "^14.2.0", "moment": "^2.24.0", diff --git a/webui/src/list/List.js b/webui/src/list/List.js index d36be8a1..45c2c963 100644 --- a/webui/src/list/List.js +++ b/webui/src/list/List.js @@ -1,134 +1,50 @@ -import { withStyles } from '@material-ui/core/styles'; +import { makeStyles } from '@material-ui/styles'; +import IconButton from '@material-ui/core/IconButton'; import Table from '@material-ui/core/Table/Table'; import TableBody from '@material-ui/core/TableBody/TableBody'; -import TablePagination from '@material-ui/core/TablePagination/TablePagination'; +import KeyboardArrowLeft from '@material-ui/icons/KeyboardArrowLeft'; +import KeyboardArrowRight from '@material-ui/icons/KeyboardArrowRight'; import React from 'react'; import BugRow from './BugRow'; -const styles = theme => ({ +const useStyles = makeStyles(theme => ({ main: { maxWidth: 600, margin: 'auto', marginTop: theme.spacing.unit * 4, }, -}); - -class List extends React.Component { - props: { - bugs: Array, - fetchMore: any => any, - classes: any, - }; - - state = { - page: 0, - rowsPerPage: 10, - lastQuery: {}, - }; - - handleChangePage = (event, page) => { - const { bugs, fetchMore } = this.props; - const { rowsPerPage } = this.state; - const pageInfo = bugs.pageInfo; - - if (page === this.state.page + 1) { - if (!pageInfo.hasNextPage) { - return; - } - - const variables = { - after: pageInfo.endCursor, - first: rowsPerPage, - }; - - fetchMore({ - variables, - updateQuery: this.updateQuery, - }); - - this.setState({ page, lastQuery: variables }); - return; - } - - if (page === this.state.page - 1) { - if (!pageInfo.hasPreviousPage) { - return; - } - - const variables = { - before: pageInfo.startCursor, - last: rowsPerPage, - }; - - fetchMore({ - variables, - updateQuery: this.updateQuery, - }); - - this.setState({ page, lastQuery: variables }); - return; - } - - throw new Error('non neighbour page pagination is not supported'); - }; - - handleChangeRowsPerPage = event => { - const { fetchMore } = this.props; - const { lastQuery } = this.state; - const rowsPerPage = event.target.value; - - const variables = lastQuery; - - if (lastQuery.first) { - variables.first = rowsPerPage; - } else if (lastQuery.last) { - variables.last = rowsPerPage; - } else { - variables.first = rowsPerPage; - } - - fetchMore({ - variables, - updateQuery: this.updateQuery, - }); - - this.setState({ rowsPerPage, lastQuery: variables }); - }; - - updateQuery = (previousResult, { fetchMoreResult }) => { - return fetchMoreResult ? fetchMoreResult : previousResult; - }; - - render() { - const { classes, bugs } = this.props; - const { page, rowsPerPage } = this.state; - - return ( - <main className={classes.main}> - <Table className={classes.table}> - <TableBody> - {bugs.edges.map(({ cursor, node }) => ( - <BugRow bug={node} key={cursor} /> - ))} - </TableBody> - </Table> - <TablePagination - component="div" - count={bugs.totalCount} - rowsPerPage={rowsPerPage} - page={page} - backIconButtonProps={{ - 'aria-label': 'Previous Page', - }} - nextIconButtonProps={{ - 'aria-label': 'Next Page', - }} - onChangePage={this.handleChangePage} - onChangeRowsPerPage={this.handleChangeRowsPerPage} - /> - </main> - ); - } + pagination: { + ...theme.typography.overline, + display: 'flex', + alignItems: 'center', + justifyContent: 'flex-end', + }, +})); + +function List({ bugs, nextPage, prevPage }) { + const classes = useStyles(); + const { hasNextPage, hasPreviousPage } = bugs.pageInfo; + return ( + <main className={classes.main}> + <Table className={classes.table}> + <TableBody> + {bugs.edges.map(({ cursor, node }) => ( + <BugRow bug={node} key={cursor} /> + ))} + </TableBody> + </Table> + + <div className={classes.pagination}> + <div>Total: {bugs.totalCount}</div> + <IconButton onClick={prevPage} disabled={!hasPreviousPage}> + <KeyboardArrowLeft /> + </IconButton> + <IconButton onClick={nextPage} disabled={!hasNextPage}> + <KeyboardArrowRight /> + </IconButton> + </div> + </main> + ); } -export default withStyles(styles)(List); +export default List; diff --git a/webui/src/list/ListQuery.js b/webui/src/list/ListQuery.js index 9dbe4e53..869bca79 100644 --- a/webui/src/list/ListQuery.js +++ b/webui/src/list/ListQuery.js @@ -1,13 +1,13 @@ // @flow import CircularProgress from '@material-ui/core/CircularProgress'; import gql from 'graphql-tag'; -import React from 'react'; +import React, { useState } from 'react'; import { Query } from 'react-apollo'; import BugRow from './BugRow'; import List from './List'; const QUERY = gql` - query($first: Int = 10, $last: Int, $after: String, $before: String) { + query($first: Int, $last: Int, $after: String, $before: String) { defaultRepository { bugs: allBugs( first: $first @@ -35,14 +35,31 @@ const QUERY = gql` ${BugRow.fragment} `; -const ListQuery = () => ( - <Query query={QUERY}> - {({ loading, error, data, fetchMore }) => { - if (loading) return <CircularProgress />; - if (error) return <p>Error: {error}</p>; - return <List bugs={data.defaultRepository.bugs} fetchMore={fetchMore} />; - }} - </Query> -); +function ListQuery() { + const [page, setPage] = useState({ first: 10, after: null }); + + const perPage = page.first || page.last; + const nextPage = pageInfo => + setPage({ first: perPage, after: pageInfo.endCursor }); + const prevPage = pageInfo => + setPage({ last: perPage, before: pageInfo.startCursor }); + + return ( + <Query query={QUERY} variables={page}> + {({ loading, error, data }) => { + if (loading) return <CircularProgress />; + if (error) return <p>Error: {error}</p>; + const bugs = data.defaultRepository.bugs; + return ( + <List + bugs={bugs} + nextPage={() => nextPage(bugs.pageInfo)} + prevPage={() => prevPage(bugs.pageInfo)} + /> + ); + }} + </Query> + ); +} export default ListQuery; |