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-6752] - Assign Linodes to Subnet drawer #9687

Merged
merged 39 commits into from
Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
582318e
starting the subnet assign drawer (very much a wip)
coliu-akamai Sep 14, 2023
e61c5aa
assigning linode to subnet logic
coliu-akamai Sep 15, 2023
bf43cbe
accidentally removed queryClient
coliu-akamai Sep 15, 2023
2a6c376
error handling of assigning a linode
coliu-akamai Sep 18, 2023
5318375
using autocomplete for unassigning linodes for now
coliu-akamai Sep 18, 2023
fbd3330
formatting
coliu-akamai Sep 18, 2023
c51340c
unassign linode while in assign drawer
coliu-akamai Sep 18, 2023
572aae2
unit tests and some error handling
coliu-akamai Sep 18, 2023
741019f
error handling and other cleanup
coliu-akamai Sep 19, 2023
169e305
use list not autocomplete, download linodes, dismissable banner
coliu-akamai Sep 19, 2023
ccf410f
cleanup + new naming convention
coliu-akamai Sep 19, 2023
e86ee49
Merge branch 'develop' into feat-m3-6752
coliu-akamai Sep 19, 2023
869a46c
update comments, make csvHeaders consistent with unassign linodes drawer
coliu-akamai Sep 20, 2023
fe35f89
feedback: first pass
coliu-akamai Sep 20, 2023
e664729
feedback: create custom hook + logic for up-to-date subnet
coliu-akamai Sep 20, 2023
26eff18
feedback: pass 2 - custom hook
coliu-akamai Sep 21, 2023
d9c5abf
feedback: pass 3 - revert some changes ;-; + new component
coliu-akamai Sep 21, 2023
62ef660
update changeset
coliu-akamai Sep 21, 2023
f1141ec
storybook and tests for new component
coliu-akamai Sep 21, 2023
a328b4c
feedback: remove styled downloadcsv, put back in useEffect, update na…
coliu-akamai Sep 21, 2023
2cb4fb1
wip
jaalah Sep 22, 2023
105f2b7
Merge pull request #3 from jaalah-akamai/example-1234
coliu-akamai Sep 22, 2023
9cf605d
remove duplicate useEffect, other changes
coliu-akamai Sep 22, 2023
e381a49
update headers
coliu-akamai Sep 22, 2023
afce9c2
Update packages/api-v4/.changeset/pr-9687-changed-1695235363178.md
coliu-akamai Sep 22, 2023
f8cb601
remove reliance on API erroring when unassigning linodes
coliu-akamai Sep 22, 2023
67f5ee3
delete regions.json file take 2...
coliu-akamai Sep 22, 2023
33ae047
Merge branch 'develop' into feat-m3-6752
coliu-akamai Sep 22, 2023
61202a0
update comments, styling, and disabling/error handling
coliu-akamai Sep 22, 2023
79168c2
get back regions file
coliu-akamai Sep 22, 2023
4ec8f85
feedback
coliu-akamai Sep 25, 2023
b982c43
initial feedback
coliu-akamai Sep 25, 2023
3694419
trying to get rid of mui warning
coliu-akamai Sep 25, 2023
ea9ead2
think it's working now
coliu-akamai Sep 25, 2023
52203b3
remove unnecessary dependencies
coliu-akamai Sep 25, 2023
e615eae
documentation
coliu-akamai Sep 25, 2023
8869caa
Merge pull request #4 from coliu-akamai/assign-linodes-test
coliu-akamai Sep 25, 2023
54f5a0c
update available IP address text
coliu-akamai Sep 25, 2023
0ee481a
Update changelog
coliu-akamai Sep 25, 2023
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
4 changes: 2 additions & 2 deletions packages/api-v4/src/linodes/configs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
UpdateConfigInterfaceOrderSchema,
UpdateConfigInterfaceSchema,
UpdateLinodeConfigSchema,
linodeInterfaceSchema,
LinodeInterfaceSchema,
coliu-akamai marked this conversation as resolved.
Show resolved Hide resolved
} from '@linode/validation/lib/linodes.schema';
import { API_ROOT } from '../constants';
import Request, {
Expand Down Expand Up @@ -180,7 +180,7 @@ export const appendConfigInterface = (
)}/configs/${encodeURIComponent(configId)}/interfaces`
),
setMethod('POST'),
setData(data, linodeInterfaceSchema)
setData(data, LinodeInterfaceSchema)
);

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

Subnet Assign Linodes Drawer ([#9687](https://github.com/linode/manager/pull/9687))
20 changes: 16 additions & 4 deletions packages/manager/src/components/DownloadCSV/DownloadCSV.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import { SxProps } from '@mui/system';
import * as React from 'react';
import { CSVLink } from 'react-csv';

import DownloadIcon from 'src/assets/icons/lke-download.svg';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes in this file match @jaalah-akamai 's ticket for the Unassign Linode subnet drawer (copy-paste tbh), since they're super similar -- may have merge conflicts later

import { Button } from 'src/components/Button/Button';
import { StyledLinkButton } from 'src/components/Button/StyledLinkButton';

import type { ButtonType } from 'src/components/Button/Button';

interface DownloadCSVProps {
buttonType?: ButtonType;
buttonType?: 'unstyled' | ButtonType;
children?: React.ReactNode;
className?: string;
csvRef?: React.RefObject<any>;
Expand Down Expand Up @@ -39,6 +41,18 @@ export const DownloadCSV = ({
sx,
text = 'Download CSV',
}: DownloadCSVProps) => {
const renderButton =
buttonType === 'unstyled' ? (
coliu-akamai marked this conversation as resolved.
Show resolved Hide resolved
<StyledLinkButton onClick={onClick} sx={sx}>
<DownloadIcon />
{text}
</StyledLinkButton>
) : (
<Button buttonType={buttonType} onClick={onClick} sx={sx}>
{text}
</Button>
);

return (
<>
<CSVLink
Expand All @@ -50,9 +64,7 @@ export const DownloadCSV = ({
ref={csvRef}
tabIndex={-1}
/>
<Button buttonType={buttonType} onClick={onClick} sx={sx}>
{text}
</Button>
{renderButton}
</>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ afterEach(() => {
});

const props = {
handleAssignLinodes: jest.fn(),
handleDelete: jest.fn(),
handleEdit: jest.fn(),
numLinodes: 1,
Expand All @@ -23,8 +24,8 @@ describe('SubnetActionMenu', () => {
const screen = renderWithTheme(<SubnetActionMenu {...props} />);
const actionMenu = screen.getByLabelText(`Action menu for Subnet subnet-1`);
fireEvent.click(actionMenu);
screen.getByText('Assign Linode');
screen.getByText('Unassign Linode');
screen.getByText('Assign Linodes');
screen.getByText('Unassign Linodes');
screen.getByText('Edit');
screen.getByText('Delete');
});
Expand Down Expand Up @@ -70,4 +71,14 @@ describe('SubnetActionMenu', () => {
fireEvent.click(editButton);
expect(props.handleEdit).toHaveBeenCalled();
});

it('should allow the Assign Linodes button to be clicked', () => {
const screen = renderWithTheme(<SubnetActionMenu {...props} />);
const actionMenu = screen.getByLabelText(`Action menu for Subnet subnet-1`);
fireEvent.click(actionMenu);

const assignButton = screen.getByText('Assign Linodes');
fireEvent.click(assignButton);
expect(props.handleAssignLinodes).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as React from 'react';
import { Action, ActionMenu } from 'src/components/ActionMenu';

interface SubnetActionHandlers {
handleAssignLinodes: (subnet: Subnet) => void;
handleDelete: (subnet: Subnet) => void;
handleEdit: (subnet: Subnet) => void;
}
Expand All @@ -16,24 +17,22 @@ interface Props extends SubnetActionHandlers {
}

export const SubnetActionMenu = (props: Props) => {
const { handleDelete, handleEdit, numLinodes, subnet } = props;

const handleAssignLinode = () => {};
const { handleAssignLinodes, handleDelete, handleEdit, numLinodes, subnet } = props;

const handleUnassignLinode = () => {};

const actions: Action[] = [
{
onClick: () => {
handleAssignLinode();
handleAssignLinodes(subnet);
},
title: 'Assign Linode',
title: 'Assign Linodes',
},
{
onClick: () => {
handleUnassignLinode();
},
title: 'Unassign Linode',
title: 'Unassign Linodes',
},
{
onClick: () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { styled } from '@mui/material/styles';

import { Box } from 'src/components/Box';
import { List } from 'src/components/List';
import { ListItem } from 'src/components/ListItem';

export const StyledButtonBox = styled(Box, { label: 'StyledButtonBox' })(
({ theme }) => ({
display: 'flex',
justifyContent: 'flex-end',
margin: `${theme.spacing(3)} 0px`,
})
);

export const StyledNoAssignedLinodesBox = styled(Box, {
label: 'StyledNoAssignedLinodesBox',
})(({ theme }) => ({
background: theme.bg.main,
display: 'flex',
flexDirection: 'column',
height: '52px',
justifyContent: 'center',
maxWidth: '416px',
paddingLeft: theme.spacing(2),
width: '100%',
}));

export const SelectedOptionsHeader = styled('h4', {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, a lot of the styling in this file matches/copies @jaalah-akamai 's ticket as the lists in the UX mockup are the same -- we could move this styling to a common styling file or have both drawers import their styling from here later!

label: 'SelectedOptionsHeader',
})(({ theme }) => ({
color: theme.color.headline,
fontFamily: theme.font.bold,
fontSize: '14px',
textTransform: 'initial',
}));

export const SelectedOptionsList = styled(List, {
label: 'SelectedOptionsList',
})(({ theme }) => ({
background: theme.bg.main,
maxHeight: '450px',
maxWidth: '416px',
overflow: 'auto',
padding: '5px 0',
width: '100%',
}));

export const SelectedOptionsListItem = styled(ListItem, {
label: 'SelectedOptionsListItem',
})(() => ({
justifyContent: 'space-between',
paddingBottom: 0,
paddingTop: 0,
}));

export const StyledLabel = styled('span', { label: 'StyledLabel' })(
({ theme }) => ({
color: theme.color.label,
fontFamily: theme.font.semiBold,
fontSize: '14px',
})
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { Subnet } from '@linode/api-v4';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unit tests here are pretty basic tbh, will need to be a lot more thorough in the integration tests (see all the cases to test for 😭)

import * as React from 'react';

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

import { fireEvent } from '@testing-library/react';

const props = {
onClose: jest.fn(),
open: true,
subnet: {
id: 1,
label: 'subnet-1',
ipv4: '10.0.0.0/24',
} as Subnet,
vpcId: 1,
vpcRegion: '',
};

describe('Subnet Assign Linodes Drawer', () => {
it('should render a subnet assign linodes drawer', () => {
const { getByText, queryByText } = renderWithTheme(
<SubnetAssignLinodesDrawer {...props} />
);

const header = getByText(
'Assign Linodes to subnet: subnet-1 (10.0.0.0/24)'
);
expect(header).toBeVisible();
const notice = getByText(
'Assigning a Linode to a subnet requires you to reboot the Linode to update its configuration.'
);
expect(notice).toBeVisible();
const helperText = getByText(
`Select the Linodes you would like to assign to this subnet. Only Linodes in this VPC's region are displayed.`
);
expect(helperText).toBeVisible();
const linodeSelect = getByText('Linodes');
expect(linodeSelect).toBeVisible();
const checkbox = getByText(
'Auto-assign a VPC IPv4 address for this Linode'
);
expect(checkbox).toBeVisible();
const ipv4Textbox = queryByText('VPC IPv4');
expect(ipv4Textbox).toBeNull();
const assignButton = getByText('Assign Linode');
expect(assignButton).toBeVisible();
const alreadyAssigned = getByText('Linodes Assigned to Subnet (0)');
expect(alreadyAssigned).toBeVisible();
const doneButton = getByText('Done');
expect(doneButton).toBeVisible();
});

it('should show the IPv4 textbox when the checkmark is clicked', () => {
const { getByText } = renderWithTheme(
<SubnetAssignLinodesDrawer {...props} />
);

const checkbox = getByText(
'Auto-assign a VPC IPv4 address for this Linode'
);
expect(checkbox).toBeVisible();
fireEvent.click(checkbox);

const ipv4Textbox = getByText('VPC IPv4');
expect(ipv4Textbox).toBeVisible();
});

it('should close the drawer', () => {
const { getByText } = renderWithTheme(
<SubnetAssignLinodesDrawer {...props} />
);

const doneButton = getByText('Done');
expect(doneButton).toBeVisible();
fireEvent.click(doneButton);
expect(props.onClose).toHaveBeenCalled();
});
});
Loading