Skip to content
Merged
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
5 changes: 5 additions & 0 deletions packages/manager/.changeset/pr-12424-changed-1751354758031.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Changed
---

Update usePagination hook to use tanstack router instead of react router ([#12424](https://github.com/linode/manager/pull/12424))
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as React from 'react';
import { accountUserFactory } from 'src/factories/accountUsers';
import { makeResourcePage } from 'src/mocks/serverHandlers';
import { http, HttpResponse, server } from 'src/mocks/testServer';
import { renderWithTheme } from 'src/utilities/testHelpers';
import { renderWithThemeAndRouter } from 'src/utilities/testHelpers';

import { UserSSHKeyPanel } from './UserSSHKeyPanel';

Expand All @@ -25,7 +25,7 @@ describe('UserSSHKeyPanel', () => {
return HttpResponse.json(makeResourcePage([]), { status: 403 });
})
);
const { queryByTestId } = renderWithTheme(
const { queryByTestId } = await renderWithThemeAndRouter(
<UserSSHKeyPanel authorizedUsers={[]} setAuthorizedUsers={vi.fn()} />
);
await waitFor(() => {
Expand All @@ -49,7 +49,7 @@ describe('UserSSHKeyPanel', () => {
return HttpResponse.json(makeResourcePage([]), { status: 403 });
})
);
const { getByText } = renderWithTheme(
const { getByText } = await renderWithThemeAndRouter(
<UserSSHKeyPanel authorizedUsers={[]} setAuthorizedUsers={vi.fn()} />
);
await waitFor(() => {
Expand All @@ -72,7 +72,7 @@ describe('UserSSHKeyPanel', () => {
return HttpResponse.json(makeResourcePage(users));
})
);
const { getByText } = renderWithTheme(
const { getByText } = await renderWithThemeAndRouter(
<UserSSHKeyPanel authorizedUsers={[]} setAuthorizedUsers={vi.fn()} />
);
await waitFor(() => {
Expand Down Expand Up @@ -100,7 +100,7 @@ describe('UserSSHKeyPanel', () => {
setAuthorizedUsers: vi.fn(),
};

const { getByRole, getByText } = renderWithTheme(
const { getByRole, getByText } = await renderWithThemeAndRouter(
<UserSSHKeyPanel {...props} />
);
await waitFor(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import React from 'react';
import { accountFactory } from 'src/factories';
import { makeResourcePage } from 'src/mocks/serverHandlers';
import { http, HttpResponse, server } from 'src/mocks/testServer';
import { renderWithThemeAndHookFormContext } from 'src/utilities/testHelpers';
import {
renderWithThemeAndHookFormContext,
renderWithThemeAndRouter,
wrapWithFormContext,
} from 'src/utilities/testHelpers';

import { Security } from './Security';

Expand All @@ -30,9 +34,10 @@ describe('Security', () => {
});

it('should render a SSH Keys heading', async () => {
const { getAllByText } = renderWithThemeAndHookFormContext({
const component = wrapWithFormContext({
component: <Security />,
});
const { getAllByText } = await renderWithThemeAndRouter(component);

const heading = getAllByText('SSH Keys')[0];

Expand All @@ -41,9 +46,10 @@ describe('Security', () => {
});

it('should render an "Add An SSH Key" button', async () => {
const { getByText } = renderWithThemeAndHookFormContext({
const component = wrapWithFormContext({
component: <Security />,
});
const { getByText } = await renderWithThemeAndRouter(component);

const addSSHKeyButton = getByText('Add an SSH Key');

Expand Down Expand Up @@ -118,9 +124,11 @@ describe('Security', () => {
})
);

const { findByText } = renderWithThemeAndHookFormContext({
const component = wrapWithFormContext({
component: <Security />,
options: { flags: { linodeDiskEncryption: true } },
});
const { findByText } = await renderWithThemeAndRouter(component, {
flags: { linodeDiskEncryption: true },
});

const heading = await findByText('Disk Encryption');
Expand All @@ -146,12 +154,13 @@ describe('Security', () => {
})
);

const { findByLabelText } =
renderWithThemeAndHookFormContext<LinodeCreateFormValues>({
component: <Security />,
options: { flags: { linodeDiskEncryption: true } },
useFormOptions: { defaultValues: { region: region.id } },
});
const component = wrapWithFormContext<LinodeCreateFormValues>({
component: <Security />,
useFormOptions: { defaultValues: { region: region.id } },
});
const { findByLabelText } = await renderWithThemeAndRouter(component, {
flags: { linodeDiskEncryption: true },
});

await findByLabelText(
'Disk encryption is not available in the selected region. Select another region to use Disk Encryption.'
Expand All @@ -175,12 +184,14 @@ describe('Security', () => {
})
);

const { findByLabelText, getByLabelText } =
renderWithThemeAndHookFormContext<LinodeCreateFormValues>({
component: <Security />,
options: { flags: { linodeDiskEncryption: true } },
useFormOptions: { defaultValues: { region: region.id } },
});
const component = wrapWithFormContext<LinodeCreateFormValues>({
component: <Security />,
useFormOptions: { defaultValues: { region: region.id } },
});
const { findByLabelText, getByLabelText } = await renderWithThemeAndRouter(
component,
{ flags: { linodeDiskEncryption: true } }
);

await findByLabelText(
'Distributed Compute Instances are encrypted. This setting can not be changed.'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { screen } from '@testing-library/react';
import * as React from 'react';

import { accountUserFactory } from 'src/factories/accountUsers';
import { makeResourcePage } from 'src/mocks/serverHandlers';
import { http, HttpResponse, server } from 'src/mocks/testServer';
import { renderWithThemeAndHookFormContext } from 'src/utilities/testHelpers';
import {
renderWithThemeAndRouter,
wrapWithFormContext,
} from 'src/utilities/testHelpers';

import { ImageAndPassword } from './ImageAndPassword';

Expand All @@ -22,32 +24,36 @@ const props = {
};

describe('ImageAndPassword', () => {
it('should render an Image Select', () => {
renderWithThemeAndHookFormContext({
it('should render an Image Select', async () => {
const component = wrapWithFormContext({
component: <ImageAndPassword {...props} />,
});
const { getByRole } = await renderWithThemeAndRouter(component);

expect(screen.getByRole('combobox'));
expect(screen.getByRole('combobox')).toBeEnabled();
expect(getByRole('combobox')).toBeVisible();
expect(getByRole('combobox')).toBeEnabled();
});
it('should render a password error if defined', async () => {
const errorMessage = 'Unable to set password.';
const { findByText } = renderWithThemeAndHookFormContext({
const component = wrapWithFormContext({
component: <ImageAndPassword {...props} passwordError={errorMessage} />,
});
const { findByText } = await renderWithThemeAndRouter(component);

const passwordError = await findByText(errorMessage, undefined, {
timeout: 2500,
});
expect(passwordError).toBeVisible();
});
it('should render an SSH Keys section', async () => {
const { getByText } = renderWithThemeAndHookFormContext({
const component = wrapWithFormContext({
component: <ImageAndPassword {...props} />,
});
const { getByText } = await renderWithThemeAndRouter(component);

expect(getByText('SSH Keys', { selector: 'h2' })).toBeVisible();
});

it('should render ssh keys for each user on the account', async () => {
const users = accountUserFactory.buildList(3, { ssh_keys: ['my-ssh-key'] });

Expand All @@ -57,9 +63,10 @@ describe('ImageAndPassword', () => {
})
);

const { findByText } = renderWithThemeAndHookFormContext({
const component = wrapWithFormContext({
component: <ImageAndPassword {...props} />,
});
const { findByText } = await renderWithThemeAndRouter(component);

for (const user of users) {
const username = await findByText(user.username);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import * as React from 'react';

import { makeResourcePage } from 'src/mocks/serverHandlers';
import { http, HttpResponse, server } from 'src/mocks/testServer';
import { mockMatchMedia, renderWithTheme } from 'src/utilities/testHelpers';
import {
mockMatchMedia,
renderWithThemeAndRouter,
} from 'src/utilities/testHelpers';

import { NodeBalancersLanding } from './NodeBalancersLanding';

Expand Down Expand Up @@ -39,7 +42,7 @@ describe('NodeBalancersLanding', () => {
})
);

const { getByTestId, getByText } = renderWithTheme(
const { getByTestId, getByText } = await renderWithThemeAndRouter(
<NodeBalancersLanding />
);

Expand All @@ -64,7 +67,7 @@ describe('NodeBalancersLanding', () => {
})
);

const { getByTestId, getByText } = renderWithTheme(
const { getByTestId, getByText } = await renderWithThemeAndRouter(
<NodeBalancersLanding />
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { screen } from '@testing-library/react';
import * as React from 'react';

import { renderWithTheme } from 'src/utilities/testHelpers';
import { renderWithThemeAndRouter } from 'src/utilities/testHelpers';

import { AccessKeyLanding } from './AccessKeyLanding';

Expand All @@ -14,8 +13,10 @@ const props = {
};

describe('AccessKeyLanding', () => {
it('should render a table of access keys', () => {
renderWithTheme(<AccessKeyLanding {...props} />);
expect(screen.getByTestId('data-qa-access-key-table')).toBeInTheDocument();
it('should render a table of access keys', async () => {
const { getByTestId } = await renderWithThemeAndRouter(
<AccessKeyLanding {...props} />
);
expect(getByTestId('data-qa-access-key-table')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import * as React from 'react';

import { makeResourcePage } from 'src/mocks/serverHandlers';
import { http, HttpResponse, server } from 'src/mocks/testServer';
import { mockMatchMedia, renderWithTheme } from 'src/utilities/testHelpers';
import {
mockMatchMedia,
renderWithThemeAndRouter,
} from 'src/utilities/testHelpers';

import { SSHKeys } from './SSHKeys';

Expand All @@ -21,7 +24,9 @@ describe('SSHKeys', () => {
})
);

const { getByTestId, getByText } = renderWithTheme(<SSHKeys />);
const { getByTestId, getByText } = await renderWithThemeAndRouter(
<SSHKeys />
);

// Check for table headers
getByText('Label');
Expand Down
33 changes: 16 additions & 17 deletions packages/manager/src/hooks/usePagination.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useMutatePreferences, usePreferences } from '@linode/queries';
import { useHistory, useLocation } from 'react-router-dom';
import { useLocation, useNavigate } from '@tanstack/react-router';

import { MIN_PAGE_SIZE } from 'src/components/PaginationFooter/PaginationFooter.constants';

Expand All @@ -26,35 +26,34 @@ export const usePagination = (
);
const { mutateAsync: updatePreferences } = useMutatePreferences();

const history = useHistory();
const navigate = useNavigate();
const location = useLocation();

const preferedPageSize = preferenceKey
? (pageSizePreferences?.[preferenceKey] ?? MIN_PAGE_SIZE)
: MIN_PAGE_SIZE;
const pageKey = queryParamsPrefix ? `${queryParamsPrefix}-page` : 'page';
const pageSizeKey = queryParamsPrefix
? `${queryParamsPrefix}-pageSize`
: 'pageSize';

const searchParams = new URLSearchParams(location.search);
const searchParamPage = searchParams.get(pageKey);
const searchParamPageSize = searchParams.get(pageSizeKey);

const preferedPageSize = preferenceKey
? (pageSizePreferences?.[preferenceKey] ?? MIN_PAGE_SIZE)
: MIN_PAGE_SIZE;
const searchParams: { [key: string]: any } = {
...location.search,
};
const searchParamPage = searchParams[pageKey];
const searchParamPageSize = searchParams[pageSizeKey];

const page = searchParamPage ? Number(searchParamPage) : initialPage;
const pageSize = searchParamPageSize
? Number(searchParamPageSize)
: preferedPageSize;
const page = searchParamPage ? searchParamPage : initialPage;
const pageSize = searchParamPageSize ? searchParamPageSize : preferedPageSize;

const setPage = (p: number) => {
searchParams.set(pageKey, String(p));
history.replace(`?${searchParams.toString()}`);
searchParams[pageKey] = p;
navigate({ to: location.pathname, search: searchParams });
};

const setPageSize = (size: number) => {
searchParams.set(pageSizeKey, String(size));
history.replace(`?${searchParams.toString()}`);
searchParams[pageSizeKey] = size;
navigate({ to: location.pathname, search: searchParams });
};

const handlePageSizeChange = (newPageSize: number) => {
Expand Down
10 changes: 10 additions & 0 deletions packages/manager/src/utilities/testHelpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,16 @@ interface RenderWithThemeAndHookFormOptions<T extends FieldValues> {
useFormOptions?: UseFormProps<T>;
}

export const wrapWithFormContext = <T extends FieldValues>(
options: RenderWithThemeAndHookFormOptions<T>
) => {
return (
<FormContextWrapper {...options.useFormOptions}>
{options.component}
</FormContextWrapper>
);
};

export const renderWithThemeAndHookFormContext = <T extends FieldValues>(
options: RenderWithThemeAndHookFormOptions<T>
) => {
Expand Down