Skip to content

Commit

Permalink
feat: [M3-7267] - Add AGLB Rule Delete Dialog (#9804)
Browse files Browse the repository at this point in the history
* add initial delete rule dialog

* add basic delete rule test

* Added changeset: Add AGLB Rule Delete Dialog

* Update packages/manager/src/features/LoadBalancers/LoadBalancerDetail/Routes/DeleteRuleDialog.tsx

Co-authored-by: Dajahi Wiley <114682940+dwiley-akamai@users.noreply.github.com>

* rename `Remove` to `Delete`

---------

Co-authored-by: Banks Nussman <banks@nussman.us>
Co-authored-by: Dajahi Wiley <114682940+dwiley-akamai@users.noreply.github.com>
  • Loading branch information
3 people authored Oct 19, 2023
1 parent 54b5f00 commit b0dba9d
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

Add AGLB Rule Delete Dialog ([#9804](https://github.com/linode/manager/pull/9804))
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,54 @@ describe('Akamai Global Load Balancer routes page', () => {
cy.findByText('A backend service is down', { exact: false });
cy.findByText('You reached a rate limit', { exact: false });
});
it('can delete a rule', () => {
const loadbalancer = loadbalancerFactory.build();
const routes = routeFactory.buildList(1, { protocol: 'http' });

mockAppendFeatureFlags({
aglb: makeFeatureFlagData(true),
}).as('getFeatureFlags');
mockGetFeatureFlagClientstream().as('getClientStream');
mockGetLoadBalancer(loadbalancer).as('getLoadBalancer');
mockGetLoadBalancerRoutes(loadbalancer.id, routes).as('getRoutes');

cy.visitWithLogin(`/loadbalancers/${loadbalancer.id}/routes`);
cy.wait([
'@getFeatureFlags',
'@getClientStream',
'@getLoadBalancer',
'@getRoutes',
]);

// Expand the route table
cy.findByLabelText(`route-${routes[0].id} expand row`).click();

// Verify all rules are shown
for (const rule of routes[0].rules) {
cy.findByText(rule.match_condition.match_value).should('be.visible');
}

const indexOfRuleToDelete = 1;

ui.actionMenu
.findByTitle(`Action Menu for Rule ${indexOfRuleToDelete}`)
.click();

ui.actionMenuItem.findByTitle('Delete').click();

mockUpdateRoute(loadbalancer, routes[0]).as('updateRoute');

ui.dialog.findByTitle('Delete Rule?').within(() => {
ui.button.findByTitle('Delete').should('be.visible').click();
});

cy.wait('@updateRoute');

// Verify the deleted rule no longer shows
cy.findByText(
routes[0].rules[indexOfRuleToDelete].match_condition.match_value
).should('not.exist');
});
});

it('can create a Route', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { useLoadBalancerRoutesQuery } from 'src/queries/aglb/routes';

import { CreateRouteDrawer } from './Routes/CreateRouteDrawer';
import { DeleteRouteDialog } from './Routes/DeleteRouteDialog';
import { DeleteRuleDialog } from './Routes/DeleteRuleDialog';
import { RuleDrawer } from './Routes/RuleDrawer';
import { RulesTable } from './RulesTable';

Expand All @@ -39,6 +40,7 @@ export const LoadBalancerRoutes = () => {
const [isAddRuleDrawerOpen, setIsAddRuleDrawerOpen] = useState(false);
const [query, setQuery] = useState<string>();
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
const [isDeleteRuleDialogOpen, setIsDeleteRuleDialogOpen] = useState(false);
const [selectedRouteId, setSelectedRouteId] = useState<number>();
const [selectedRuleIndex, setSelectedRuleIndex] = useState<number>();

Expand Down Expand Up @@ -86,6 +88,12 @@ export const LoadBalancerRoutes = () => {
setSelectedRuleIndex(ruleIndex);
};

const onDeleteRule = (route: Route, ruleIndex: number) => {
setIsDeleteRuleDialogOpen(true);
setSelectedRouteId(route.id);
setSelectedRuleIndex(ruleIndex);
};

const onDeleteRoute = (route: Route) => {
setIsDeleteDialogOpen(true);
setSelectedRouteId(route.id);
Expand Down Expand Up @@ -134,6 +142,7 @@ export const LoadBalancerRoutes = () => {
const InnerTable = (
<RulesTable
loadbalancerId={Number(loadbalancerId)}
onDeleteRule={(index) => onDeleteRule(route, index)}
onEditRule={(index) => onEditRule(route, index)}
route={route}
/>
Expand Down Expand Up @@ -254,6 +263,13 @@ export const LoadBalancerRoutes = () => {
open={isDeleteDialogOpen}
route={selectedRoute}
/>
<DeleteRuleDialog
loadbalancerId={Number(loadbalancerId)}
onClose={() => setIsDeleteRuleDialogOpen(false)}
open={isDeleteRuleDialogOpen}
route={selectedRoute}
ruleIndex={selectedRuleIndex}
/>
</>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React from 'react';

import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel';
import { ConfirmationDialog } from 'src/components/ConfirmationDialog/ConfirmationDialog';
import { useLoadBalancerRouteUpdateMutation } from 'src/queries/aglb/routes';

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

interface Props {
loadbalancerId: number;
onClose: () => void;
open: boolean;
route: Route | undefined;
ruleIndex: number | undefined;
}

export const DeleteRuleDialog = (props: Props) => {
const { loadbalancerId, onClose, open, route, ruleIndex } = props;

const { error, isLoading, mutateAsync } = useLoadBalancerRouteUpdateMutation(
loadbalancerId,
route?.id ?? -1
);

const onDelete = async () => {
if (!route || ruleIndex === undefined) {
return;
}

const newRules = [...route.rules];

newRules.splice(ruleIndex, 1);

await mutateAsync({
rules: newRules,
});
onClose();
};

return (
<ConfirmationDialog
actions={
<ActionsPanel
primaryButtonProps={{
label: 'Delete',
loading: isLoading,
onClick: onDelete,
}}
secondaryButtonProps={{
label: 'Cancel',
onClick: onClose,
}}
/>
}
error={error?.[0]?.reason}
onClose={onClose}
open={open}
title="Delete Rule?"
>
Are you sure you want to delete this rule?
</ConfirmationDialog>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ const mockRoute = routeFactory.build({
describe('RulesTable', () => {
it('renders table headers', () => {
const { getByText } = renderWithTheme(
<RulesTable loadbalancerId={1} onEditRule={jest.fn()} route={mockRoute} />
<RulesTable
loadbalancerId={1}
onDeleteRule={jest.fn()}
onEditRule={jest.fn()}
route={mockRoute}
/>
);
expect(getByText('Execution')).toBeInTheDocument();
expect(getByText('Match Value')).toBeInTheDocument();
Expand All @@ -34,6 +39,7 @@ describe('RulesTable', () => {
const { getByText } = renderWithTheme(
<RulesTable
loadbalancerId={1}
onDeleteRule={jest.fn()}
onEditRule={jest.fn()}
route={{ id: 0, label: 'test', protocol: 'http', rules: [] }}
/>
Expand All @@ -43,7 +49,12 @@ describe('RulesTable', () => {

it('renders rules correctly', () => {
const { getByText } = renderWithTheme(
<RulesTable loadbalancerId={1} onEditRule={jest.fn()} route={mockRoute} />
<RulesTable
loadbalancerId={1}
onDeleteRule={jest.fn()}
onEditRule={jest.fn()}
route={mockRoute}
/>
);

expect(getByText('First')).toBeInTheDocument();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import type { MatchField, Route } from '@linode/api-v4';
interface Props {
loadbalancerId: number;
onEditRule: (ruleIndex: number) => void;
onDeleteRule: (ruleIndex: number) => void;
route: Route;
}

Expand All @@ -43,7 +44,8 @@ const matchFieldMap: Record<MatchField, string> = {
const screenReaderMessage =
'Some screen readers may require you to enter focus mode to interact with Loadbalancer rule list items. In focus mode, press spacebar to begin a drag or tab to access item actions.';

export const RulesTable = ({ loadbalancerId, onEditRule, route }: Props) => {
export const RulesTable = (props: Props) => {
const { loadbalancerId, onEditRule, route, onDeleteRule } = props;
const { label, protocol, rules } = route;
const theme = useTheme();
const { enqueueSnackbar } = useSnackbar();
Expand Down Expand Up @@ -285,7 +287,10 @@ export const RulesTable = ({ loadbalancerId, onEditRule, route }: Props) => {
onClick: () => handleMoveDown(index),
title: 'Move Down',
},
{ onClick: () => null, title: 'Remove' },
{
onClick: () => onDeleteRule(index),
title: 'Delete',
},
]}
ariaLabel={`Action Menu for Rule ${index}`}
/>
Expand Down

0 comments on commit b0dba9d

Please sign in to comment.