Commit 20335b62 authored by Igor Stuev's avatar Igor Stuev Committed by GitHub

Navigating through lists breaks native browser navigation (#2493)

* Navigating through lists breaks native browser navigation

* tokens test fixes
parent d4612970
...@@ -7,24 +7,40 @@ import { test, expect } from 'playwright/lib'; ...@@ -7,24 +7,40 @@ import { test, expect } from 'playwright/lib';
import Tokens from './Tokens'; import Tokens from './Tokens';
test.beforeEach(async({ mockTextAd }) => { test.beforeEach(async({ mockTextAd, mockAssetResponse }) => {
await mockTextAd(); await mockTextAd();
await mockAssetResponse(tokens.tokenInfoERC20a.icon_url as string, './playwright/mocks/image_svg.svg');
}); });
const allTokens = {
items: [
tokens.tokenInfoERC20a, tokens.tokenInfoERC20b, tokens.tokenInfoERC20c, tokens.tokenInfoERC20d,
tokens.tokenInfoERC721a, tokens.tokenInfoERC721b, tokens.tokenInfoERC721c,
tokens.tokenInfoERC1155a, tokens.tokenInfoERC1155b, tokens.tokenInfoERC1155WithoutName,
],
next_page_params: {
holder_count: 1,
items_count: 1,
name: 'a',
market_cap: '0',
},
};
test('base view +@mobile +@dark-mode', async({ render, mockApiResponse }) => { test('base view +@mobile +@dark-mode', async({ render, mockApiResponse }) => {
const allTokens = {
items: [ await mockApiResponse('tokens', allTokens);
tokens.tokenInfoERC20a, tokens.tokenInfoERC20b, tokens.tokenInfoERC20c, tokens.tokenInfoERC20d,
tokens.tokenInfoERC721a, tokens.tokenInfoERC721b, tokens.tokenInfoERC721c, const component = await render(
tokens.tokenInfoERC1155a, tokens.tokenInfoERC1155b, tokens.tokenInfoERC1155WithoutName, <div>
], <Box h={{ base: '134px', lg: 6 }}/>
next_page_params: { <Tokens/>
holder_count: 1, </div>,
items_count: 1, );
name: 'a',
market_cap: '0', await expect(component).toHaveScreenshot();
}, });
};
test('with search +@mobile +@dark-mode', async({ render, mockApiResponse }) => {
const filteredTokens = { const filteredTokens = {
items: [ items: [
tokens.tokenInfoERC20a, tokens.tokenInfoERC20b, tokens.tokenInfoERC20c, tokens.tokenInfoERC20a, tokens.tokenInfoERC20b, tokens.tokenInfoERC20c,
...@@ -42,10 +58,9 @@ test('base view +@mobile +@dark-mode', async({ render, mockApiResponse }) => { ...@@ -42,10 +58,9 @@ test('base view +@mobile +@dark-mode', async({ render, mockApiResponse }) => {
</div>, </div>,
); );
await expect(component).toHaveScreenshot();
await component.getByRole('textbox', { name: 'Token name or symbol' }).focus(); await component.getByRole('textbox', { name: 'Token name or symbol' }).focus();
await component.getByRole('textbox', { name: 'Token name or symbol' }).fill('foo'); await component.getByRole('textbox', { name: 'Token name or symbol' }).fill('foo');
await component.getByRole('textbox', { name: 'Token name or symbol' }).blur();
await expect(component).toHaveScreenshot(); await expect(component).toHaveScreenshot();
}); });
......
...@@ -367,7 +367,7 @@ describe('if there are multiple pages', () => { ...@@ -367,7 +367,7 @@ describe('if there are multiple pages', () => {
describe('if there is page query param in URL', () => { describe('if there is page query param in URL', () => {
it('sets this param as the page number', async() => { it('sets this param as the page number', async() => {
useRouter.mockReturnValueOnce({ ...router, query: { page: '3' } }); useRouter.mockReturnValue({ ...router, query: { page: '3' } });
const params: Params<'address_txs'> = { const params: Params<'address_txs'> = {
resourceName: 'address_txs', resourceName: 'address_txs',
...@@ -614,6 +614,56 @@ describe('queries with sorting', () => { ...@@ -614,6 +614,56 @@ describe('queries with sorting', () => {
}); });
}); });
describe('router query changes', () => {
it('refetches correct page when page number changes in URL', async() => {
const routerPush = jest.fn(() => Promise.resolve());
const router = {
pathname: '/current-route',
push: routerPush,
query: {
page: '3',
next_page_params: encodeURIComponent(JSON.stringify(responses.page_2.next_page_params)),
},
};
useRouter.mockReturnValue(router);
const params: Params<'address_txs'> = {
resourceName: 'address_txs',
pathParams: { hash: addressMock.hash },
};
fetch.once(JSON.stringify(responses.page_3), responseInit);
fetch.once(JSON.stringify(responses.page_2), responseInit);
const { result, rerender } = renderHook(() => useQueryWithPages(params), { wrapper });
await waitForApiResponse();
expect(result.current.data).toEqual(responses.page_3);
expect(result.current.pagination.page).toBe(3);
// Simulate URL change to page 2
useRouter.mockReturnValue({
...router,
query: {
page: '2',
next_page_params: encodeURIComponent(JSON.stringify(responses.page_1.next_page_params)),
},
});
rerender();
await waitForApiResponse();
expect(result.current.data).toEqual(responses.page_2);
expect(result.current.pagination).toMatchObject({
page: 2,
canGoBackwards: false,
hasNextPage: true,
isLoading: false,
isVisible: true,
});
});
});
async function waitForApiResponse() { async function waitForApiResponse() {
await flushPromises(); await flushPromises();
await act(flushPromises); await act(flushPromises);
......
...@@ -7,6 +7,8 @@ import { animateScroll } from 'react-scroll'; ...@@ -7,6 +7,8 @@ import { animateScroll } from 'react-scroll';
import type { PaginationParams } from './types'; import type { PaginationParams } from './types';
import type { Route } from 'nextjs-routes';
import type { PaginatedResources, PaginationFilters, PaginationSorting, ResourceError, ResourcePayload } from 'lib/api/resources'; import type { PaginatedResources, PaginationFilters, PaginationSorting, ResourceError, ResourcePayload } from 'lib/api/resources';
import { RESOURCES, SORTING_FIELDS } from 'lib/api/resources'; import { RESOURCES, SORTING_FIELDS } from 'lib/api/resources';
import type { Params as UseApiQueryParams } from 'lib/api/useApiQuery'; import type { Params as UseApiQueryParams } from 'lib/api/useApiQuery';
...@@ -26,6 +28,10 @@ type NextPageParams = Record<string, unknown>; ...@@ -26,6 +28,10 @@ type NextPageParams = Record<string, unknown>;
const INITIAL_PAGE_PARAMS = { '1': {} }; const INITIAL_PAGE_PARAMS = { '1': {} };
function getPageFromQuery(query: Route['query']) {
return query?.page && !Array.isArray(query.page) ? Number(query.page) : 1;
}
function getPaginationParamsFromQuery(queryString: string | Array<string> | undefined) { function getPaginationParamsFromQuery(queryString: string | Array<string> | undefined) {
if (queryString) { if (queryString) {
try { try {
...@@ -64,7 +70,7 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({ ...@@ -64,7 +70,7 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const router = useRouter(); const router = useRouter();
const [ page, setPage ] = React.useState<number>(router.query.page && !Array.isArray(router.query.page) ? Number(router.query.page) : 1); const [ page, setPage ] = React.useState<number>(getPageFromQuery(router.query));
const [ pageParams, setPageParams ] = React.useState<Record<number, NextPageParams>>({ const [ pageParams, setPageParams ] = React.useState<Record<number, NextPageParams>>({
[page]: getPaginationParamsFromQuery(router.query.next_page_params), [page]: getPaginationParamsFromQuery(router.query.next_page_params),
}); });
...@@ -221,5 +227,17 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({ ...@@ -221,5 +227,17 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({
}, 0); }, 0);
}, []); }, []);
React.useEffect(() => {
const pageFromQuery = getPageFromQuery(router.query);
const nextPageParamsFromQuery = getPaginationParamsFromQuery(router.query.next_page_params);
setPage(pageFromQuery);
setPageParams(prev => ({
...prev,
[pageFromQuery]: nextPageParamsFromQuery,
}));
setHasPages(pageFromQuery > 1);
}, [ router.query ]);
return { ...queryResult, pagination, onFilterChange, onSortingChange }; return { ...queryResult, pagination, onFilterChange, onSortingChange };
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment