diff --git a/.changeset/strange-dragons-guess.md b/.changeset/strange-dragons-guess.md new file mode 100644 index 00000000..06eb01db --- /dev/null +++ b/.changeset/strange-dragons-guess.md @@ -0,0 +1,6 @@ +--- +"@supabase-cache-helpers/postgrest-fetcher": patch +"@supabase-cache-helpers/postgrest-swr": patch +--- + +fix: ordering of cursor pagination diff --git a/packages/postgrest-fetcher/src/cursor-pagination-fetcher.ts b/packages/postgrest-fetcher/src/cursor-pagination-fetcher.ts index f2959403..40f83363 100644 --- a/packages/postgrest-fetcher/src/cursor-pagination-fetcher.ts +++ b/packages/postgrest-fetcher/src/cursor-pagination-fetcher.ts @@ -41,6 +41,7 @@ export const createCursorPaginationFetcher = < } const { data } = await query.throwOnError(); + // cannot be null because of .throwOnError() return data as Result[]; }; diff --git a/packages/postgrest-swr/__tests__/query/use-cursor-infinite-scroll-query.spec.tsx b/packages/postgrest-swr/__tests__/query/use-cursor-infinite-scroll-query.spec.tsx index 10de1e8e..be94269f 100644 --- a/packages/postgrest-swr/__tests__/query/use-cursor-infinite-scroll-query.spec.tsx +++ b/packages/postgrest-swr/__tests__/query/use-cursor-infinite-scroll-query.spec.tsx @@ -40,7 +40,7 @@ describe('useCursorInfiniteScrollQuery', () => { provider = new Map(); }); - it('should load correctly', async () => { + it('should load correctly ascending', async () => { function Page() { const { data, loadMore, isValidating, error } = useCursorInfiniteScrollQuery( @@ -98,6 +98,64 @@ describe('useCursorInfiniteScrollQuery', () => { expect(list.childElementCount).toEqual(3); }); + it('should load correctly descending', async () => { + function Page() { + const { data, loadMore, isValidating, error } = + useCursorInfiniteScrollQuery( + client + .from('contact') + .select('id,username') + .ilike('username', `${testRunPrefix}%`) + .order('username', { ascending: false }) + .limit(1), + { path: 'username' }, + { revalidateOnFocus: false } + ); + + return ( +
+ {loadMore && ( +
loadMore()} /> + )} +
+ {(data ?? []).map((p) => ( +
{p.username}
+ ))} +
+
+ ); + } + + renderWithConfig(, { provider: () => provider }); + await screen.findByText( + `${testRunPrefix}-username-4`, + {}, + { timeout: 10000 } + ); + const list = screen.getByTestId('list'); + expect(list.childElementCount).toEqual(1); + + fireEvent.click( + await screen.findByTestId('loadMore', {}, { timeout: 10000 }) + ); + await screen.findByText( + `${testRunPrefix}-username-3`, + {}, + { timeout: 10000 } + ); + + expect(list.childElementCount).toEqual(2); + + fireEvent.click(screen.getByTestId('loadMore')); + await screen.findByText( + `${testRunPrefix}-username-2`, + {}, + { timeout: 10000 } + ); + + expect(list.childElementCount).toEqual(3); + }); + it('should stop at lastCursor', async () => { function Page() { const { data, loadMore, isValidating, error } = @@ -186,7 +244,7 @@ describe('useCursorInfiniteScrollQuery', () => { ); }); - it('should stop if no more data', async () => { + it('should stop if no more data ascending', async () => { function Page() { const { data, loadMore, isValidating, error } = useCursorInfiniteScrollQuery( @@ -254,4 +312,73 @@ describe('useCursorInfiniteScrollQuery', () => { expect(screen.queryByTestId('loadMore')).toBeNull(); }); + + it('should stop if no more data desc', async () => { + function Page() { + const { data, loadMore, isValidating, error } = + useCursorInfiniteScrollQuery( + client + .from('contact') + .select('id,username') + .ilike('username', `${testRunPrefix}%`) + .order('username', { ascending: false }) + .limit(2), + { + path: 'username', + } + ); + + return ( +
+ {loadMore && ( +
loadMore()} /> + )} +
{`isValidating: ${isValidating}`}
+
+ {(data ?? []).map((p) => ( +
{p.username}
+ ))} +
+
+ ); + } + + renderWithConfig(, { provider: () => provider }); + const list = screen.getByTestId('list'); + + await screen.findByText( + `${testRunPrefix}-username-4`, + {}, + { timeout: 10000 } + ); + await screen.findByText( + `${testRunPrefix}-username-3`, + {}, + { timeout: 10000 } + ); + expect(list.childElementCount).toEqual(2); + + fireEvent.click(screen.getByTestId('loadMore')); + await screen.findByText( + `${testRunPrefix}-username-2`, + {}, + { timeout: 10000 } + ); + await screen.findByText( + `${testRunPrefix}-username-1`, + {}, + { timeout: 10000 } + ); + expect(list.childElementCount).toEqual(4); + + await screen.findByText('isValidating: false', {}, { timeout: 10000 }); + + fireEvent.click(screen.getByTestId('loadMore')); + + await screen.findByText('isValidating: false', {}, { timeout: 10000 }); + + expect(list.childElementCount).toEqual(4); + + expect(screen.queryByTestId('loadMore')).toBeNull(); + }); }); diff --git a/packages/postgrest-swr/src/lib/create-key-getter.ts b/packages/postgrest-swr/src/lib/create-key-getter.ts index 0843150e..19f39602 100644 --- a/packages/postgrest-swr/src/lib/create-key-getter.ts +++ b/packages/postgrest-swr/src/lib/create-key-getter.ts @@ -97,12 +97,12 @@ export const createCursorKeyGetter = < throw new Error(`No ordering key found for path ${orderingKey}`); } - const [a, ascending, b] = orderingKey.split('.'); + const [a, ascending, b] = orderingValue.split('.'); setFilterValue( query['url'].searchParams, path, - ascending === 'asc' ? 'lt' : 'gt', + ascending === 'asc' ? 'gt' : 'lt', lastValue ); diff --git a/packages/postgrest-swr/src/query/use-cursor-infinite-scroll-query.ts b/packages/postgrest-swr/src/query/use-cursor-infinite-scroll-query.ts index 8f85e108..27a51fa6 100644 --- a/packages/postgrest-swr/src/query/use-cursor-infinite-scroll-query.ts +++ b/packages/postgrest-swr/src/query/use-cursor-infinite-scroll-query.ts @@ -105,7 +105,7 @@ function useCursorInfiniteScrollQuery< throw new Error(`No ordering key found for path ${orderingKey}`); } - const [column, ascending, _] = orderingKey.split('.'); + const [column, ascending, _] = orderingValue.split('.'); // cursor value is the gt or lt filter on the order key const q = new URLSearchParams(decodedKey.queryKey); @@ -115,6 +115,7 @@ function useCursorInfiniteScrollQuery< const filter = filters.find((f) => f.startsWith(`${ascending === 'asc' ? 'gt' : 'lt'}.`) ); + if (!filter) { return { cursor: undefined,