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

Forward cursor single page #1145

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions e2e/kit/src/lib/utils/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export const routes = {
Pagination_query_offset_variable: '/pagination/query/offset-variable',

Pagination_fragment_forward_cursor: '/pagination/fragment/forward-cursor',
Pagination_fragment_forward_cursor_single_page: '/pagination/fragment/forward-cursor-single-page',
Pagination_fragment_backwards_cursor: '/pagination/fragment/backwards-cursor',
Pagination_fragment_bidirectional_cursor: '/pagination/fragment/bidirectional-cursor',
Pagination_fragment_offset: '/pagination/fragment/offset',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<script lang="ts">
import { paginatedFragment, graphql } from '$houdini';

$: queryResult = graphql(`
query UserFragmentForwardsCursorSinglePageQuery @load {
user(id: "1", snapshot: "pagination-fragment-forwards-cursor-single-page") {
...ForwardsCursorSinglePageFragment
}
}
`);

$: fragmentResult = paginatedFragment(
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
$queryResult.data?.user ?? null,
graphql(`
fragment ForwardsCursorSinglePageFragment on User {
friendsConnection(first: 2) @paginate(mode: SinglePage) {
edges {
node {
name
}
}
}
}
`)
);
</script>

<div id="result">
{$fragmentResult.data?.friendsConnection.edges.map(({ node }) => node?.name).join(', ')}
</div>

<div id="pageInfo">
{JSON.stringify($fragmentResult.pageInfo)}
</div>

<button id="next" on:click={() => fragmentResult?.loadNextPage()}>next</button>
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { test } from '@playwright/test';
import { routes } from '../../../../lib/utils/routes.js';
import {
expect_1_gql,
expect_0_gql,
expect_to_be,
expectToContain,
goto
} from '../../../../lib/utils/testsHelper.js';

test.describe('forwards cursor paginatedFragment in SinglePage mode', () => {
test('loadNextPage', async ({ page }) => {
await goto(page, routes.Pagination_fragment_forward_cursor_single_page);

await expect_to_be(page, 'Bruce Willis, Samuel Jackson');

// wait for the api response
await expect_1_gql(page, 'button[id=next]');

// make sure we got the new content
await expect_to_be(page, 'Bruce Willis, Samuel Jackson, Morgan Freeman, Tom Hanks');
});

test('page info tracks connection state', async ({ page }) => {
await goto(page, routes.Pagination_fragment_forward_cursor_single_page);

const data = [
'Morgan Freeman, Tom Hanks',
'Will Smith, Harrison Ford',
'Eddie Murphy, Clint Eastwood'
];

// load the next 3 pages
for (let i = 0; i < 3; i++) {
// wait for the request to resolve
await expect_1_gql(page, 'button[id=next]');

// check the page info
await expect_to_be(page, data[i]);
}

// make sure we have all of the data loaded
await expect_to_be(page, data[2]);

await expectToContain(page, `"hasNextPage":false`);

await expect_0_gql(page, 'button[id=next]');
});
});
2 changes: 1 addition & 1 deletion packages/houdini-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,4 @@
},
"main": "./build/plugin-cjs/index.js",
"types": "./build/plugin/index.d.ts"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export function useDocumentHandle<
getVariables: () => storeValue.variables!,
storeName: artifact.name,
fetch: fetchQuery,
fetchUpdate: async (args, updates = ['append']) => {
fetchUpdate: (args, updates) => {
return observer.send({
...args,
cacheParams: {
Expand Down
7 changes: 4 additions & 3 deletions packages/houdini/src/runtime/cache/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {
GraphQLObject,
GraphQLValue,
NestedList,
RefetchUpdateModes,
SubscriptionSelection,
SubscriptionSpec,
ValueMap,
Expand Down Expand Up @@ -57,7 +58,7 @@ export class Cache {
variables?: {}
parent?: string
layer?: LayerID | null
applyUpdates?: string[]
applyUpdates?: RefetchUpdateModes[]
notifySubscribers?: SubscriptionSpec[]
forceNotify?: boolean
forceStale?: boolean
Expand Down Expand Up @@ -397,7 +398,7 @@ class CacheInternal {
root?: string
layer: Layer
toNotify?: FieldSelection[]
applyUpdates?: string[]
applyUpdates?: RefetchUpdateModes[]
forceNotify?: boolean
forceStale?: boolean
}): FieldSelection[] {
Expand Down Expand Up @@ -1334,7 +1335,7 @@ class CacheInternal {
abstract: boolean
variables: {}
specs: FieldSelection[]
applyUpdates?: string[]
applyUpdates?: RefetchUpdateModes[]
fields: SubscriptionSelection
layer: Layer
forceNotify?: boolean
Expand Down
3 changes: 2 additions & 1 deletion packages/houdini/src/runtime/client/documentStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
SubscriptionSpec,
CachePolicies,
GraphQLVariables,
RefetchUpdateModes,
} from '../lib/types'
import { ArtifactKind } from '../lib/types'
import { cachePolicy } from './plugins'
Expand Down Expand Up @@ -583,7 +584,7 @@ export type ClientPluginContext = {
disableWrite?: boolean
disableRead?: boolean
disableSubscriptions?: boolean
applyUpdates?: string[]
applyUpdates?: RefetchUpdateModes[]
serverSideFallback?: boolean
}
stuff: App.Stuff
Expand Down
61 changes: 43 additions & 18 deletions packages/houdini/src/runtime/lib/pagination.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {
QueryArtifact,
QueryResult,
FetchParams,
RefetchUpdateModes,
} from './types'

export function cursorHandlers<_Data extends GraphQLObject, _Input extends GraphQLVariables>({
Expand All @@ -26,7 +27,10 @@ export function cursorHandlers<_Data extends GraphQLObject, _Input extends Graph
getVariables: () => NonNullable<_Input>
getSession: () => Promise<App.Session>
fetch: FetchFn<_Data, _Input>
fetchUpdate: (arg: SendParams, updates: string[]) => ReturnType<FetchFn<_Data, _Input>>
fetchUpdate: (
arg: SendParams,
updates: RefetchUpdateModes[]
) => ReturnType<FetchFn<_Data, _Input>>
}): CursorHandlers<_Data, _Input> {
// dry up the page-loading logic
const loadPage = async ({
Expand Down Expand Up @@ -61,16 +65,24 @@ export function cursorHandlers<_Data extends GraphQLObject, _Input extends Graph
let isSinglePage = artifact.refetch?.mode === 'SinglePage'

// send the query
await (isSinglePage ? parentFetch : parentFetchUpdate)(
{
if (isSinglePage) {
await parentFetch({
variables: loadVariables,
fetch,
metadata,
policy: isSinglePage ? artifact.policy : CachePolicy.NetworkOnly,
session: await getSession(),
},
isSinglePage ? [] : [where === 'start' ? 'prepend' : 'append']
)
policy: artifact.policy,
})
} else {
await parentFetchUpdate(
{
variables: loadVariables,
fetch,
metadata,
policy: CachePolicy.NetworkOnly,
session: await getSession(),
},
[where === 'start' ? 'prepend' : 'append']
)
}
}

const getPageInfo = () => {
Expand Down Expand Up @@ -236,7 +248,10 @@ export function offsetHandlers<_Data extends GraphQLObject, _Input extends Graph
}: {
artifact: QueryArtifact
fetch: FetchFn<_Data, _Input>
fetchUpdate: (arg: SendParams) => ReturnType<FetchFn<_Data, _Input>>
fetchUpdate: (
arg: SendParams,
updates: RefetchUpdateModes[]
) => ReturnType<FetchFn<_Data, _Input>>
storeName: string
getState: () => _Data | null
getVariables: () => _Input
Expand Down Expand Up @@ -281,14 +296,24 @@ export function offsetHandlers<_Data extends GraphQLObject, _Input extends Graph
let isSinglePage = artifact.refetch?.mode === 'SinglePage'

// send the query
const targetFetch = isSinglePage ? parentFetch : parentFetchUpdate
await targetFetch({
variables: queryVariables as _Input,
fetch,
metadata,
policy: isSinglePage ? artifact.policy : CachePolicy.NetworkOnly,
session: await getSession(),
})
if (isSinglePage) {
await parentFetch({
variables: queryVariables as _Input,
metadata,
policy: artifact.policy,
})
} else {
await parentFetchUpdate(
{
variables: queryVariables as _Input,
fetch,
metadata,
policy: CachePolicy.NetworkOnly,
session: await getSession(),
},
['append']
)
}

// add the page size to the offset so we load the next page next time
const pageSize = queryVariables.limit || artifact.refetch!.pageSize
Expand Down