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;