diff --git a/ui/pages/Tokens.pw.tsx b/ui/pages/Tokens.pw.tsx index 58c3fe6cd4..53e0215580 100644 --- a/ui/pages/Tokens.pw.tsx +++ b/ui/pages/Tokens.pw.tsx @@ -7,24 +7,40 @@ import { test, expect } from 'playwright/lib'; import Tokens from './Tokens'; -test.beforeEach(async({ mockTextAd }) => { +test.beforeEach(async({ mockTextAd, mockAssetResponse }) => { 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 }) => { - 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', - }, - }; + + await mockApiResponse('tokens', allTokens); + + const component = await render( +
+ + +
, + ); + + await expect(component).toHaveScreenshot(); +}); + +test('with search +@mobile +@dark-mode', async({ render, mockApiResponse }) => { const filteredTokens = { items: [ tokens.tokenInfoERC20a, tokens.tokenInfoERC20b, tokens.tokenInfoERC20c, @@ -42,10 +58,9 @@ test('base view +@mobile +@dark-mode', async({ render, mockApiResponse }) => { , ); - await expect(component).toHaveScreenshot(); - 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' }).blur(); await expect(component).toHaveScreenshot(); }); diff --git a/ui/pages/__screenshots__/Tokens.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png b/ui/pages/__screenshots__/Tokens.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png index ca396488ac..4a76b74064 100644 Binary files a/ui/pages/__screenshots__/Tokens.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png and b/ui/pages/__screenshots__/Tokens.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-1.png differ diff --git a/ui/pages/__screenshots__/Tokens.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-2.png b/ui/pages/__screenshots__/Tokens.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-2.png deleted file mode 100644 index 5545d2c47c..0000000000 Binary files a/ui/pages/__screenshots__/Tokens.pw.tsx_dark-color-mode_base-view-mobile-dark-mode-2.png and /dev/null differ diff --git a/ui/pages/__screenshots__/Tokens.pw.tsx_dark-color-mode_with-search-mobile-dark-mode-1.png b/ui/pages/__screenshots__/Tokens.pw.tsx_dark-color-mode_with-search-mobile-dark-mode-1.png new file mode 100644 index 0000000000..bebe8a71cf Binary files /dev/null and b/ui/pages/__screenshots__/Tokens.pw.tsx_dark-color-mode_with-search-mobile-dark-mode-1.png differ diff --git a/ui/pages/__screenshots__/Tokens.pw.tsx_default_base-view-mobile-dark-mode-1.png b/ui/pages/__screenshots__/Tokens.pw.tsx_default_base-view-mobile-dark-mode-1.png index 359dae45df..dddce9d35e 100644 Binary files a/ui/pages/__screenshots__/Tokens.pw.tsx_default_base-view-mobile-dark-mode-1.png and b/ui/pages/__screenshots__/Tokens.pw.tsx_default_base-view-mobile-dark-mode-1.png differ diff --git a/ui/pages/__screenshots__/Tokens.pw.tsx_default_base-view-mobile-dark-mode-2.png b/ui/pages/__screenshots__/Tokens.pw.tsx_default_base-view-mobile-dark-mode-2.png deleted file mode 100644 index 5c5a727d96..0000000000 Binary files a/ui/pages/__screenshots__/Tokens.pw.tsx_default_base-view-mobile-dark-mode-2.png and /dev/null differ diff --git a/ui/pages/__screenshots__/Tokens.pw.tsx_default_bridged-tokens-base-view-1.png b/ui/pages/__screenshots__/Tokens.pw.tsx_default_bridged-tokens-base-view-1.png index 47e895c8dc..cb8ab1c9cf 100644 Binary files a/ui/pages/__screenshots__/Tokens.pw.tsx_default_bridged-tokens-base-view-1.png and b/ui/pages/__screenshots__/Tokens.pw.tsx_default_bridged-tokens-base-view-1.png differ diff --git a/ui/pages/__screenshots__/Tokens.pw.tsx_default_bridged-tokens-base-view-2.png b/ui/pages/__screenshots__/Tokens.pw.tsx_default_bridged-tokens-base-view-2.png index a029be2866..3724329db4 100644 Binary files a/ui/pages/__screenshots__/Tokens.pw.tsx_default_bridged-tokens-base-view-2.png and b/ui/pages/__screenshots__/Tokens.pw.tsx_default_bridged-tokens-base-view-2.png differ diff --git a/ui/pages/__screenshots__/Tokens.pw.tsx_default_with-search-mobile-dark-mode-1.png b/ui/pages/__screenshots__/Tokens.pw.tsx_default_with-search-mobile-dark-mode-1.png new file mode 100644 index 0000000000..5597c05d2c Binary files /dev/null and b/ui/pages/__screenshots__/Tokens.pw.tsx_default_with-search-mobile-dark-mode-1.png differ diff --git a/ui/pages/__screenshots__/Tokens.pw.tsx_mobile_base-view-mobile-dark-mode-1.png b/ui/pages/__screenshots__/Tokens.pw.tsx_mobile_base-view-mobile-dark-mode-1.png index 70e89b251a..f1e9a4cd7b 100644 Binary files a/ui/pages/__screenshots__/Tokens.pw.tsx_mobile_base-view-mobile-dark-mode-1.png and b/ui/pages/__screenshots__/Tokens.pw.tsx_mobile_base-view-mobile-dark-mode-1.png differ diff --git a/ui/pages/__screenshots__/Tokens.pw.tsx_mobile_base-view-mobile-dark-mode-2.png b/ui/pages/__screenshots__/Tokens.pw.tsx_mobile_base-view-mobile-dark-mode-2.png deleted file mode 100644 index 1af1787cd2..0000000000 Binary files a/ui/pages/__screenshots__/Tokens.pw.tsx_mobile_base-view-mobile-dark-mode-2.png and /dev/null differ diff --git a/ui/pages/__screenshots__/Tokens.pw.tsx_mobile_with-search-mobile-dark-mode-1.png b/ui/pages/__screenshots__/Tokens.pw.tsx_mobile_with-search-mobile-dark-mode-1.png new file mode 100644 index 0000000000..1fc18184a0 Binary files /dev/null and b/ui/pages/__screenshots__/Tokens.pw.tsx_mobile_with-search-mobile-dark-mode-1.png differ diff --git a/ui/shared/pagination/useQueryWithPages.test.tsx b/ui/shared/pagination/useQueryWithPages.test.tsx index f0b263d511..8fadab934d 100644 --- a/ui/shared/pagination/useQueryWithPages.test.tsx +++ b/ui/shared/pagination/useQueryWithPages.test.tsx @@ -367,7 +367,7 @@ describe('if there are multiple pages', () => { describe('if there is page query param in URL', () => { 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'> = { resourceName: 'address_txs', @@ -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() { await flushPromises(); await act(flushPromises); diff --git a/ui/shared/pagination/useQueryWithPages.ts b/ui/shared/pagination/useQueryWithPages.ts index ddf4b3a7e4..df554bb91b 100644 --- a/ui/shared/pagination/useQueryWithPages.ts +++ b/ui/shared/pagination/useQueryWithPages.ts @@ -7,6 +7,8 @@ import { animateScroll } from 'react-scroll'; import type { PaginationParams } from './types'; +import type { Route } from 'nextjs-routes'; + import type { PaginatedResources, PaginationFilters, PaginationSorting, ResourceError, ResourcePayload } from 'lib/api/resources'; import { RESOURCES, SORTING_FIELDS } from 'lib/api/resources'; import type { Params as UseApiQueryParams } from 'lib/api/useApiQuery'; @@ -26,6 +28,10 @@ type NextPageParams = Record; 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 | undefined) { if (queryString) { try { @@ -64,7 +70,7 @@ export default function useQueryWithPages({ const queryClient = useQueryClient(); const router = useRouter(); - const [ page, setPage ] = React.useState(router.query.page && !Array.isArray(router.query.page) ? Number(router.query.page) : 1); + const [ page, setPage ] = React.useState(getPageFromQuery(router.query)); const [ pageParams, setPageParams ] = React.useState>({ [page]: getPaginationParamsFromQuery(router.query.next_page_params), }); @@ -221,5 +227,17 @@ export default function useQueryWithPages({ }, 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 }; }