Skip to content

Commit 2d13651

Browse files
refactor: [M3-9531] - Nodebalancer routing (Tanstack) (#11858)
* Save progress * save mooaaar progress * Route delete modal * cleanup * tests * settings modals * cleanup * moaar cleanup * moaar cleanup * Added changeset: Nodebalancer routing (Tanstack) * Fix test failure * @feedback @dwiley-akamai * @feedback @dwiley-akamai
1 parent 3a17852 commit 2d13651

36 files changed

+579
-377
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/manager": Tech Stories
3+
---
4+
5+
Nodebalancer routing (Tanstack) ([#11858](https://github.com/linode/manager/pull/11858))

packages/manager/.eslintrc.cjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ module.exports = {
146146
'src/features/Firewalls/**/*',
147147
'src/features/Images/**/*',
148148
'src/features/Longview/**/*',
149+
'src/features/NodeBalancers/**/*',
149150
'src/features/PlacementGroups/**/*',
150151
'src/features/StackScripts/**/*',
151152
'src/features/Volumes/**/*',

packages/manager/src/MainContent.tsx

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,6 @@ const Profile = React.lazy(() =>
129129
default: module.Profile,
130130
}))
131131
);
132-
const NodeBalancers = React.lazy(
133-
() => import('src/features/NodeBalancers/NodeBalancers')
134-
);
135132
const SupportTickets = React.lazy(
136133
() => import('src/features/Support/SupportTickets')
137134
);
@@ -368,10 +365,6 @@ export const MainContent = () => {
368365
<React.Suspense fallback={<SuspenseLoader />}>
369366
<Switch>
370367
<Route component={LinodesRoutes} path="/linodes" />
371-
<Route
372-
component={NodeBalancers}
373-
path="/nodebalancers"
374-
/>
375368
<Route component={Managed} path="/managed" />
376369
<Route
377370
component={ObjectStorage}

packages/manager/src/features/Firewalls/FirewallDetail/Devices/RemoveDeviceDialog.tsx

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,37 @@
1+
import {
2+
linodeQueries,
3+
nodebalancerQueries,
4+
useRemoveFirewallDeviceMutation,
5+
} from '@linode/queries';
16
import { ActionsPanel, Typography } from '@linode/ui';
27
import { useQueryClient } from '@tanstack/react-query';
38
import { useSnackbar } from 'notistack';
49
import * as React from 'react';
510

611
import { ConfirmationDialog } from 'src/components/ConfirmationDialog/ConfirmationDialog';
7-
import {
8-
useRemoveFirewallDeviceMutation,
9-
linodeQueries,
10-
nodebalancerQueries,
11-
} from '@linode/queries';
1212

1313
import type { FirewallDevice } from '@linode/api-v4';
1414

1515
export interface Props {
1616
device: FirewallDevice | undefined;
1717
firewallId: number;
1818
firewallLabel: string;
19+
isFetching?: boolean;
1920
onClose: () => void;
2021
onService: boolean | undefined;
2122
open: boolean;
2223
}
2324

2425
export const RemoveDeviceDialog = React.memo((props: Props) => {
25-
const { device, firewallId, firewallLabel, onClose, onService, open } = props;
26+
const {
27+
device,
28+
firewallId,
29+
firewallLabel,
30+
isFetching,
31+
onClose,
32+
onService,
33+
open,
34+
} = props;
2635

2736
const { enqueueSnackbar } = useSnackbar();
2837
const deviceType = device?.entity.type;
@@ -105,6 +114,7 @@ export const RemoveDeviceDialog = React.memo((props: Props) => {
105114
/>
106115
}
107116
error={error?.[0]?.reason}
117+
isFetching={isFetching}
108118
onClose={onClose}
109119
open={open}
110120
title={dialogTitle}

packages/manager/src/features/NodeBalancers/NodeBalancerCreate.test.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@ import { renderWithTheme } from 'src/utilities/testHelpers';
44

55
import NodeBalancerCreate from './NodeBalancerCreate';
66

7+
const queryMocks = vi.hoisted(() => ({
8+
useNavigate: vi.fn(() => vi.fn()),
9+
}));
10+
11+
vi.mock('@tanstack/react-router', async () => {
12+
const actual = await vi.importActual('@tanstack/react-router');
13+
return {
14+
...actual,
15+
useNavigate: queryMocks.useNavigate,
16+
};
17+
});
18+
719
// Note: see nodeblaancers-create-in-complex-form.spec.ts for an e2e test of this flow
820
describe('NodeBalancerCreate', () => {
921
it('renders all parts of the NodeBalancerCreate page', () => {

packages/manager/src/features/NodeBalancers/NodeBalancerCreate.tsx

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,9 @@ import {
2020
import { scrollErrorIntoView } from '@linode/utilities';
2121
import { useTheme } from '@mui/material';
2222
import useMediaQuery from '@mui/material/useMediaQuery';
23-
import { createLazyRoute } from '@tanstack/react-router';
23+
import { useNavigate } from '@tanstack/react-router';
2424
import { append, clone, compose, defaultTo, lensPath, over } from 'ramda';
2525
import * as React from 'react';
26-
import { useHistory } from 'react-router-dom';
2726

2827
import { CheckoutSummary } from 'src/components/CheckoutSummary/CheckoutSummary';
2928
import { ConfirmationDialog } from 'src/components/ConfirmationDialog/ConfirmationDialog';
@@ -98,6 +97,7 @@ const defaultFieldsStates = {
9897
};
9998

10099
const NodeBalancerCreate = () => {
100+
const navigate = useNavigate();
101101
const { data: agreements } = useAccountAgreements();
102102
const { data: profile } = useProfile();
103103
const { data: regions } = useRegionsQuery();
@@ -109,8 +109,6 @@ const NodeBalancerCreate = () => {
109109
mutateAsync: createNodeBalancer,
110110
} = useNodebalancerCreateMutation();
111111

112-
const history = useHistory();
113-
114112
const [
115113
nodeBalancerFields,
116114
setNodeBalancerFields,
@@ -303,7 +301,10 @@ const NodeBalancerCreate = () => {
303301

304302
createNodeBalancer(nodeBalancerRequestData)
305303
.then((nodeBalancer) => {
306-
history.push(`/nodebalancers/${nodeBalancer.id}/summary`);
304+
navigate({
305+
params: { id: String(nodeBalancer.id) },
306+
to: '/nodebalancers/$id/summary',
307+
});
307308
// Analytics Event
308309
sendCreateNodeBalancerEvent(`Region: ${nodeBalancer.region}`);
309310
})
@@ -807,10 +808,4 @@ export const fieldErrorsToNodePathErrors = (errors: APIError[]) => {
807808
}, []);
808809
};
809810

810-
export const nodeBalancerCreateLazyRoute = createLazyRoute(
811-
'/nodebalancers/create'
812-
)({
813-
component: NodeBalancerCreate,
814-
});
815-
816811
export default NodeBalancerCreate;

packages/manager/src/features/NodeBalancers/NodeBalancerDeleteDialog.test.tsx

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,25 @@
11
import userEvent from '@testing-library/user-event';
22
import * as React from 'react';
33

4+
import { nodeBalancerFactory } from 'src/factories';
45
import { renderWithTheme } from 'src/utilities/testHelpers';
56

67
import { NodeBalancerDeleteDialog } from './NodeBalancerDeleteDialog';
78

89
import type { ManagerPreferences } from '@linode/utilities';
910

1011
const props = {
11-
id: 1,
12-
label: 'nb-1',
13-
onClose: vi.fn(),
12+
isFetching: false,
1413
open: true,
14+
selectedNodeBalancer: nodeBalancerFactory.build(),
1515
};
1616

1717
const preference: ManagerPreferences['type_to_confirm'] = true;
1818

19+
const navigate = vi.fn();
1920
const queryMocks = vi.hoisted(() => ({
21+
useMatch: vi.fn(() => ({})),
22+
useNavigate: vi.fn(() => navigate),
2023
usePreferences: vi.fn().mockReturnValue({}),
2124
}));
2225

@@ -28,6 +31,15 @@ vi.mock('@linode/queries', async () => {
2831
};
2932
});
3033

34+
vi.mock('@tanstack/react-router', async () => {
35+
const actual = await vi.importActual('@tanstack/react-router');
36+
return {
37+
...actual,
38+
useMatch: queryMocks.useMatch,
39+
useNavigate: queryMocks.useNavigate,
40+
};
41+
});
42+
3143
queryMocks.usePreferences.mockReturnValue({
3244
data: preference,
3345
});
@@ -50,7 +62,7 @@ describe('NodeBalancerDeleteDialog', () => {
5062
'Traffic will no longer be routed through this NodeBalancer. Please check your DNS settings and either provide the IP address of another active NodeBalancer, or route traffic directly to your Linode.'
5163
)
5264
).toBeVisible();
53-
expect(getByText('Delete nb-1?')).toBeVisible();
65+
expect(getByText('Delete nodebalancer-id-1?')).toBeVisible();
5466
expect(getByText('NodeBalancer Label')).toBeVisible();
5567
expect(getByText('Cancel')).toBeVisible();
5668
expect(getByText('Delete')).toBeVisible();
@@ -62,6 +74,6 @@ describe('NodeBalancerDeleteDialog', () => {
6274
);
6375

6476
await userEvent.click(getByText('Cancel'));
65-
expect(props.onClose).toHaveBeenCalled();
77+
expect(navigate).toHaveBeenCalled();
6678
});
6779
});

packages/manager/src/features/NodeBalancers/NodeBalancerDeleteDialog.tsx

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,36 @@
1+
import { useNodebalancerDeleteMutation } from '@linode/queries';
12
import { Notice, Typography } from '@linode/ui';
3+
import { useMatch, useNavigate } from '@tanstack/react-router';
24
import * as React from 'react';
3-
import { useHistory } from 'react-router-dom';
45

56
import { TypeToConfirmDialog } from 'src/components/TypeToConfirmDialog/TypeToConfirmDialog';
6-
import { useNodebalancerDeleteMutation } from '@linode/queries';
7+
8+
import type { NodeBalancer } from '@linode/api-v4';
79

810
interface Props {
9-
id: number;
10-
label: string;
11-
onClose: () => void;
11+
isFetching: boolean;
1212
open: boolean;
13+
selectedNodeBalancer: NodeBalancer | undefined;
1314
}
1415

1516
export const NodeBalancerDeleteDialog = ({
16-
id,
17-
label,
18-
onClose,
17+
isFetching,
1918
open,
19+
selectedNodeBalancer,
2020
}: Props) => {
21-
const { error, isPending, mutateAsync } = useNodebalancerDeleteMutation(id);
22-
const { push } = useHistory();
21+
const navigate = useNavigate();
22+
const match = useMatch({
23+
strict: false,
24+
});
25+
const { error, isPending, mutateAsync } = useNodebalancerDeleteMutation(
26+
selectedNodeBalancer?.id ?? -1
27+
);
28+
29+
const label = selectedNodeBalancer?.label;
2330

2431
const onDelete = async () => {
2532
await mutateAsync();
26-
onClose();
27-
push('/nodebalancers');
33+
navigate({ to: '/nodebalancers' });
2834
};
2935

3036
return (
@@ -35,12 +41,20 @@ export const NodeBalancerDeleteDialog = ({
3541
primaryBtnText: 'Delete',
3642
type: 'NodeBalancer',
3743
}}
44+
onClose={
45+
match.routeId === '/nodebalancers/$id/settings/delete'
46+
? () =>
47+
navigate({
48+
params: { id: String(selectedNodeBalancer?.id) },
49+
to: '/nodebalancers/$id/settings',
50+
})
51+
: () => navigate({ to: '/nodebalancers' })
52+
}
3853
errors={error ?? undefined}
3954
expand
4055
label={'NodeBalancer Label'}
41-
loading={isPending}
56+
loading={isPending || isFetching}
4257
onClick={onDelete}
43-
onClose={onClose}
4458
open={open}
4559
title={`Delete ${label}?`}
4660
typographyStyle={{ marginTop: '20px' }}

packages/manager/src/features/NodeBalancers/NodeBalancerDetail/NodeBalancerConfigurations.test.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ const props = {
1616
grants: undefined,
1717
nodeBalancerLabel: 'nb-1',
1818
nodeBalancerRegion: 'us-east',
19+
params: {
20+
nodeBalancerId: '1',
21+
},
1922
};
2023

2124
const loadingTestId = 'circle-progress';

0 commit comments

Comments
 (0)