Skip to content

Commit

Permalink
feat: [M3 7015] - Create nodebalancer tab in firewalls landing (#9590)
Browse files Browse the repository at this point in the history
* initial changes

* finished the firewall device landing updates

* Added changeset: Create nodebalancer tab in firewalls landing

* fixed breadcrumb and variable names

* added FirewallDeviceLanding test file

* added initial unit tests

* updated jest.mock function, still not working

* swapped from jest.mock to MSW, still need some work

* fixed unit tests

* fixed tests for FirewallDeviceLanding

---------

Co-authored-by: TylerWJ <tylerwjones99@gmail.com>
  • Loading branch information
tyler-akamai and TylerWJ authored Aug 28, 2023
1 parent e88b324 commit 3cff043
Show file tree
Hide file tree
Showing 7 changed files with 250 additions and 138 deletions.
2 changes: 1 addition & 1 deletion packages/api-v4/src/firewalls/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export type FirewallStatus = 'enabled' | 'disabled' | 'deleted';

export type FirewallRuleProtocol = 'ALL' | 'TCP' | 'UDP' | 'ICMP' | 'IPENCAP';

export type FirewallDeviceEntityType = 'linode';
export type FirewallDeviceEntityType = 'linode' | 'nodebalancer';

export type FirewallPolicyType = 'ACCEPT' | 'DROP';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

Create NodeBalancer tab in Firewalls landing ([#9590](https://github.com/linode/manager/pull/9590))
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import * as React from 'react';

import { firewallDeviceFactory } from 'src/factories';
import { rest, server } from 'src/mocks/testServer';
import { renderWithTheme } from 'src/utilities/testHelpers';

import {
FirewallDeviceLanding,
FirewallDeviceLandingProps,
} from './FirewallDeviceLanding';

import type { FirewallDeviceEntityType } from '@linode/api-v4';

const baseProps = (
type: FirewallDeviceEntityType
): FirewallDeviceLandingProps => ({
disabled: true,
firewallID: 1,
firewallLabel: 'test',
type,
});

const devices = ['linode', 'nodebalancer'];

devices.forEach((device: FirewallDeviceEntityType) => {
describe(`Firewall ${device} device`, () => {
let addButton: HTMLElement;
let permissionNotice: HTMLElement;
let table: HTMLElement;

beforeEach(() => {
server.use(
rest.get('*/firewalls/*', (req, res, ctx) => {
return res(ctx.json(firewallDeviceFactory.buildList(1)));
})
);
const { getByRole, getByTestId } = renderWithTheme(
<FirewallDeviceLanding {...baseProps(device)} />
);
addButton = getByTestId('add-device-button');
permissionNotice = getByRole('alert');
table = getByRole('table');
});

it(`should render an add ${device} button`, () => {
expect(addButton).toBeInTheDocument();
});

it(`should render a disabled add ${device} button`, () => {
expect(addButton).toBeDisabled();
});

it(`should render a permission denied notice`, () => {
expect(permissionNotice).toBeInTheDocument();
});

it(`should render a table`, () => {
expect(table).toBeInTheDocument();
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import Grid from '@mui/material/Unstable_Grid2';
import { styled } from '@mui/material/styles';
import * as React from 'react';

import { Button } from 'src/components/Button/Button';
import { Notice } from 'src/components/Notice/Notice';
import { Typography } from 'src/components/Typography';
import { useAllFirewallDevicesQuery } from 'src/queries/firewalls';

import { AddDeviceDrawer } from './AddDeviceDrawer';
import { FirewallDevicesTable } from './FirewallDevicesTable';
import { RemoveDeviceDialog } from './RemoveDeviceDialog';

import type { FirewallDeviceEntityType } from '@linode/api-v4';

export interface FirewallDeviceLandingProps {
disabled: boolean;
firewallID: number;
firewallLabel: string;
type: FirewallDeviceEntityType;
}

const formattedTypes = {
linode: 'Linode',
nodebalancer: 'NodeBalancer',
};

export const FirewallDeviceLanding = React.memo(
(props: FirewallDeviceLandingProps) => {
const { disabled, firewallID, firewallLabel, type } = props;

const { data: allDevices, error, isLoading } = useAllFirewallDevicesQuery(
firewallID
);

const devices =
allDevices?.filter((device) => device.entity.type === type) || [];

const [
isRemoveDeviceDialogOpen,
setIsRemoveDeviceDialogOpen,
] = React.useState<boolean>(false);

const [selectedDeviceId, setSelectedDeviceId] = React.useState<number>(-1);

const selectedDevice = devices?.find(
(device) => device.id === selectedDeviceId
);

const [addDeviceDrawerOpen, setDeviceDrawerOpen] = React.useState<boolean>(
false
);

const handleClose = () => {
setDeviceDrawerOpen(false);
};

const formattedType = formattedTypes[type];

return (
<>
{disabled ? (
<Notice
text={
"You don't have permissions to modify this Firewall. Please contact an account administrator for details."
}
important
variant="error"
/>
) : null}
<Grid container direction="column">
<Grid style={{ paddingBottom: 0 }}>
<StyledTypography>
The following {formattedType}s have been assigned to this
Firewall. A {formattedType} can only be assigned to a single
Firewall.
</StyledTypography>
</Grid>
<StyledGrid>
<Button
buttonType="primary"
data-testid="add-device-button"
disabled={disabled}
onClick={() => setDeviceDrawerOpen(true)}
>
Add {formattedType}s to Firewall
</Button>
</StyledGrid>
</Grid>
<FirewallDevicesTable
triggerRemoveDevice={(id) => {
setSelectedDeviceId(id);
setIsRemoveDeviceDialogOpen(true);
}}
devices={devices ?? []}
disabled={disabled}
error={error ?? undefined}
loading={isLoading}
/>
<AddDeviceDrawer onClose={handleClose} open={addDeviceDrawerOpen} />
<RemoveDeviceDialog
device={selectedDevice}
firewallId={firewallID}
firewallLabel={firewallLabel}
linodeId={selectedDevice?.entity.id}
onClose={() => setIsRemoveDeviceDialogOpen(false)}
open={isRemoveDeviceDialogOpen}
/>
</>
);
}
);

const StyledTypography = styled(Typography, { label: 'StyledTypography' })(
({ theme }) => ({
fontSize: '0.875rem',
marginTop: theme.spacing(),
[theme.breakpoints.down('lg')]: {
marginLeft: theme.spacing(),
marginRight: theme.spacing(),
},
})
);

const StyledGrid = styled(Grid, { label: 'StyledGrid' })(({ theme }) => ({
'&.MuiGrid-item': {
paddingTop: 0,
},
display: 'flex',
justifyContent: 'flex-end',
marginBottom: theme.spacing(),
[theme.breakpoints.only('sm')]: {
marginRight: theme.spacing(),
},
}));

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ interface Drawer {
ruleIdx?: number;
}

const FirewallRulesLanding = (props: Props) => {
export const FirewallRulesLanding = React.memo((props: Props) => {
const { disabled, firewallID, rules } = props;
const { mutateAsync: updateFirewallRules } = useUpdateFirewallRulesMutation(
firewallID
Expand Down Expand Up @@ -373,7 +373,7 @@ const FirewallRulesLanding = (props: Props) => {
/>
</>
);
};
});

const StyledActionsPanel = styled(ActionsPanel, {
label: 'StyledActionsPanel',
Expand All @@ -386,8 +386,6 @@ const StyledDiv = styled('div', { label: 'StyledDiv' })(({ theme }) => ({
marginTop: theme.spacing(2),
}));

export default React.memo(FirewallRulesLanding);

interface DiscardChangesDialogProps {
handleClose: () => void;
handleDiscard: () => void;
Expand Down
Loading

0 comments on commit 3cff043

Please sign in to comment.