diff --git a/src/app/apollo/resolvers/auth.js b/src/app/apollo/resolvers/auth.js index 479ea85f..48d45fdb 100644 --- a/src/app/apollo/resolvers/auth.js +++ b/src/app/apollo/resolvers/auth.js @@ -97,7 +97,7 @@ export default { ); } - if (result.auth.invalid || !result.auth.refreshToken) { + if (result.auth.invalid) { return { __typename: "RefreshTokensMutation", auth: { @@ -107,6 +107,16 @@ export default { }; } + // When no refresh token is present, there is nothing to be done. + if (!result.auth.refreshToken) { + return { + __typename: "RefreshTokensMutation", + auth: { + ...result.auth, + }, + }; + } + const expired = !notBefore || !result.auth.expiresAt || diff --git a/src/lib/component/partial/Pagination.js b/src/lib/component/partial/Pagination.js deleted file mode 100644 index af4ecd67..00000000 --- a/src/lib/component/partial/Pagination.js +++ /dev/null @@ -1,91 +0,0 @@ -import React from "/vendor/react"; -import PropTypes from "prop-types"; -import gql from "/vendor/graphql-tag"; -import { withStyles, TablePagination } from "/vendor/@material-ui/core"; - -const StyledTablePagination = withStyles(theme => ({ - root: { - position: "sticky", - bottom: 0, - backgroundColor: theme.palette.background.paper, - borderTopColor: theme.palette.divider, - borderTopWidth: 1, - borderTopStyle: "solid", - marginTop: -1, - }, -}))(TablePagination); - -class Pagination extends React.PureComponent { - static propTypes = { - pageInfo: PropTypes.object, - limit: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - offset: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - onChangeQuery: PropTypes.func, - }; - - static defaultProps = { - pageInfo: undefined, - limit: undefined, - offset: undefined, - onChangeQuery: undefined, - }; - - static fragments = { - pageInfo: gql` - fragment Pagination_pageInfo on OffsetPageInfo { - totalCount - } - `, - }; - - _handleChangePage = (event, page) => { - if (this.props.onChangeQuery) { - this.props.onChangeQuery({ offset: page * this.props.limit }); - } - }; - - _handleChangeLimit = event => { - if (this.props.onChangeQuery) { - this.props.onChangeQuery({ limit: event.target.value }); - } - }; - - render() { - const { limit: rawLimit, offset: rawOffset, pageInfo } = this.props; - - const limit = parseInt(rawLimit, 10); - const offset = parseInt(rawOffset, 10); - - if (Number.isNaN(limit)) { - throw new TypeError(`Expected numeric limit. Received ${rawLimit}`); - } - - if (Number.isNaN(offset)) { - throw new TypeError(`Expected numeric offset. Received ${rawOffset}`); - } - - // Fall back to a placeholder total count value (equal to page size) while - // pageInfo is undefined during load. - const count = pageInfo ? pageInfo.totalCount : limit; - - // Given that offset isn't strictly a multiple of limit, the current page - // index must be rounded down. In the case that we have a small offset that - // would otherwise round to zero, return 1 to enable the previous page - // button to reset offset to 0. - const page = offset > 0 && offset < limit ? 1 : Math.floor(offset / limit); - - return ( - - ); - } -} - -export default Pagination; diff --git a/src/lib/component/partial/Pagination.stories.js b/src/lib/component/partial/Pagination.stories.js new file mode 100644 index 00000000..4cfade9f --- /dev/null +++ b/src/lib/component/partial/Pagination.stories.js @@ -0,0 +1,21 @@ +import React from "react"; +import withTheme from "/lib/storybook/withTheme"; + +import Pagination from "./Pagination"; + +export default { + title: "partial / Pagination", + component: Pagination, + decorators: [withTheme], +}; + +export const withControls = (args) => ( + null} /> +); +withControls.args = { + pageInfo: { + totalCount: 200, + }, + offset: 0, + limit: 25, +}; diff --git a/src/lib/component/partial/Pagination.tsx b/src/lib/component/partial/Pagination.tsx new file mode 100644 index 00000000..a8cf49b8 --- /dev/null +++ b/src/lib/component/partial/Pagination.tsx @@ -0,0 +1,84 @@ +import React from "react"; +import gql from "graphql-tag"; +import TablePagination from "@material-ui/core/TablePagination"; +import { makeStyles } from "@material-ui/core/styles"; + +const useStyles = makeStyles((theme) => ({ + root: { + position: "sticky", + bottom: 0, + backgroundColor: theme.palette.background.paper, + borderTopColor: theme.palette.divider, + borderTopWidth: 1, + borderTopStyle: "solid", + marginTop: -1, + }, +})); + +interface Props { + pageInfo?: { + totalCount: number; + }; + limit?: number | string; + offset?: number | string; + onChangeQuery: (_: any) => void; +} + +const parseNum = (n?: number | string) => { + const out = parseInt((n || 0) as string, 10); + if (Number.isNaN(out)) { + throw new TypeError(`expected numeric; received ${n}`); + } + return out; +}; + +const Pagination = ({ onChangeQuery, ...props }: Props) => { + const classes = useStyles(); + + const limit = parseNum(props.limit); + const offset = parseNum(props.offset); + + // Fall back to a placeholder total count value (equal to page size) while + // pageInfo is undefined during load. + const count = props.pageInfo ? props.pageInfo.totalCount : limit; + + // Given that offset isn't strictly a multiple of limit, the current page + // index must be rounded down. In the case that we have a small offset that + // would otherwise round to zero, return 1 to enable the previous page + // button to reset offset to 0. + const page = offset > 0 && offset < limit ? 1 : Math.floor(offset / limit); + + const handleChangePage = React.useCallback( + (_: any, page: number) => onChangeQuery({ offset: page * limit }), + [onChangeQuery, limit], + ); + + const handleChangeLimit = React.useCallback( + (event: React.ChangeEvent) => + onChangeQuery({ limit: event.target.value, offset: 0 }), + [onChangeQuery], + ); + + return ( + + ); +}; + +Pagination.fragments = { + pageInfo: gql` + fragment Pagination_pageInfo on OffsetPageInfo { + totalCount + } + `, +}; + +export default Pagination;