Skip to content

Commit

Permalink
Prefer existing.pageInfo cursors in relayStylePagination read function.
Browse files Browse the repository at this point in the history
  • Loading branch information
benjamn committed Jun 28, 2021
1 parent edab367 commit 330808b
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 6 deletions.
2 changes: 1 addition & 1 deletion src/cache/inmemory/__tests__/policies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3462,7 +3462,7 @@ describe("type policies", function () {
edges,
pageInfo: {
__typename: "PageInfo",
startCursor: thirdPageInfo.startCursor,
startCursor: fourthPageInfo.startCursor,
endCursor: fifthPageInfo.endCursor,
hasPreviousPage: false,
hasNextPage: true,
Expand Down
109 changes: 108 additions & 1 deletion src/utilities/policies/__tests__/relayStylePagination.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,116 @@
import { FieldFunctionOptions, InMemoryCache, isReference, makeReference } from '../../../core';
import { FieldFunctionOptions, InMemoryCache, isReference, makeReference, StoreObject } from '../../../cache';
import { relayStylePagination, TRelayPageInfo } from '../pagination';

describe('relayStylePagination', () => {
const policy = relayStylePagination();

describe('read', () => {
const fakeEdges = [
{ node: { __ref: "A" }, cursor: "cursorA" },
{ node: { __ref: "B" }, cursor: "cursorB" },
{ node: { __ref: "C" }, cursor: "cursorC" },
];

const fakeReadOptions = {
canRead() { return true },
readField(key, obj: StoreObject) {
return obj && obj[key];
},
} as FieldFunctionOptions;

it("should prefer existing.pageInfo.startCursor", () => {
const resultWithStartCursor = policy.read!({
edges: fakeEdges,
pageInfo: {
startCursor: "preferredStartCursor",
hasPreviousPage: false,
hasNextPage: true,
} as TRelayPageInfo,
}, fakeReadOptions);

expect(
resultWithStartCursor &&
resultWithStartCursor.pageInfo
).toEqual({
startCursor: "preferredStartCursor",
endCursor: "cursorC",
hasPreviousPage: false,
hasNextPage: true,
});
});

it("should prefer existing.pageInfo.endCursor", () => {
const resultWithEndCursor = policy.read!({
edges: fakeEdges,
pageInfo: {
endCursor: "preferredEndCursor",
hasPreviousPage: false,
hasNextPage: true,
} as TRelayPageInfo,
}, fakeReadOptions);

expect(
resultWithEndCursor &&
resultWithEndCursor.pageInfo
).toEqual({
startCursor: "cursorA",
endCursor: "preferredEndCursor",
hasPreviousPage: false,
hasNextPage: true,
});
});

it("should prefer existing.pageInfo.{start,end}Cursor", () => {
const resultWithEndCursor = policy.read!({
edges: fakeEdges,
pageInfo: {
startCursor: "preferredStartCursor",
endCursor: "preferredEndCursor",
hasPreviousPage: false,
hasNextPage: true,
},
}, fakeReadOptions);

expect(
resultWithEndCursor &&
resultWithEndCursor.pageInfo
).toEqual({
startCursor: "preferredStartCursor",
endCursor: "preferredEndCursor",
hasPreviousPage: false,
hasNextPage: true,
});
});

it("should override pageInfo.{start,end}Cursor if empty strings", () => {
const resultWithEndCursor = policy.read!({
edges: [
{ node: { __ref: "A" }, cursor: "" },
{ node: { __ref: "B" }, cursor: "cursorB" },
{ node: { __ref: "C" }, cursor: "" },
{ node: { __ref: "D" }, cursor: "cursorD" },
{ node: { __ref: "E" } },
],
pageInfo: {
startCursor: "",
endCursor: "",
hasPreviousPage: false,
hasNextPage: true,
},
}, fakeReadOptions);

expect(
resultWithEndCursor &&
resultWithEndCursor.pageInfo
).toEqual({
startCursor: "cursorB",
endCursor: "cursorD",
hasPreviousPage: false,
hasNextPage: true,
});
});
});

describe('merge', () => {
const merge = policy.merge;
// The merge function should exist, make TS aware
Expand Down
17 changes: 13 additions & 4 deletions src/utilities/policies/pagination.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,20 +98,29 @@ export function relayStylePagination<TNode = Reference>(
if (!existing) return;

const edges: TRelayEdge<TNode>[] = [];
let startCursor = "";
let endCursor = "";
let firstEdgeCursor = "";
let lastEdgeCursor = "";
existing.edges.forEach(edge => {
// Edges themselves could be Reference objects, so it's important
// to use readField to access the edge.edge.node property.
if (canRead(readField("node", edge))) {
edges.push(edge);
if (edge.cursor) {
startCursor = startCursor || edge.cursor;
endCursor = edge.cursor;
firstEdgeCursor = firstEdgeCursor || edge.cursor;
lastEdgeCursor = edge.cursor || lastEdgeCursor;
}
}
});

let {
startCursor,
endCursor,
} = existing.pageInfo || {};
// If existing.pageInfo.{start,end}Cursor are undefined or "", default to
// firstEdgeCursor and/or lastEdgeCursor.
startCursor = startCursor || firstEdgeCursor;
endCursor = endCursor || lastEdgeCursor;

return {
// Some implementations return additional Connection fields, such
// as existing.totalCount. These fields are saved by the merge
Expand Down

0 comments on commit 330808b

Please sign in to comment.