Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [M3 7015] - Create nodebalancer tab in firewalls landing #9590

Merged
merged 11 commits into from
Aug 28, 2023
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,64 @@
import * as React from 'react';

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

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

const linodeProps: FirewallDeviceLandingProps = {
disabled: true,
firewallID: 1,
firewallLabel: 'test',
type: 'linode',
};

const nodeBalancerProps: FirewallDeviceLandingProps = {
...linodeProps,
type: 'nodebalancer',
};

jest.mock('src/queries/firewalls.ts', () => ({
useAllFirewallDevicesQuery: jest.fn().mockReturnValue({
data: [],
error: null,
isLoading: false,
}),
}));

describe('Firewall linode device', () => {
let addLinodesButton: HTMLElement;

beforeEach(() => {
const { getByTestId } = renderWithTheme(
<FirewallDeviceLanding {...linodeProps} />
);
addLinodesButton = getByTestId('add-device-button');
});

it.only('should render an add Linodes button', () => {
expect(addLinodesButton).toBeInTheDocument();
});

it('should render a disabled add Linodes button', () => {
expect(addLinodesButton).toBeDisabled();
});
});

carrillo-erik marked this conversation as resolved.
Show resolved Hide resolved
describe('Firewall nodebalancer device', () => {
let addNodeBalancersButton: HTMLElement;

beforeEach(() => {
const { getByTestId } = renderWithTheme(
<FirewallDeviceLanding {...nodeBalancerProps} />
);
addNodeBalancersButton = getByTestId('add-device-button');
});
it('should render an add NodeBalancer button', () => {
expect(addNodeBalancersButton).toBeInTheDocument();
});
it('should render a disabled add NodeBalancer button', () => {
expect(addNodeBalancersButton).toBeDisabled();
});
});
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."
}
error
important
/>
) : 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-linode-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.

carrillo-erik marked this conversation as resolved.
Show resolved Hide resolved
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