Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

Add dialog modal for Delete action for Logs Stream and Destination ([#12956](https://github.com/linode/manager/pull/12956))
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { useDeleteDestinationMutation } from '@linode/queries';
import { ActionsPanel } from '@linode/ui';
import { enqueueSnackbar } from 'notistack';
import * as React from 'react';

import { ConfirmationDialog } from 'src/components/ConfirmationDialog/ConfirmationDialog';

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

interface Props {
destination: Destination | undefined;
onClose: () => void;
open: boolean;
}

export const DeleteDestinationDialog = React.memo((props: Props) => {
const { onClose, open, destination } = props;
const {
mutateAsync: deleteDestination,
isPending,
error,
} = useDeleteDestinationMutation();

const handleDelete = () => {
const { id, label } = destination as Destination;
deleteDestination({
id,
}).then(() => {
onClose();
return enqueueSnackbar(`Destination ${label} deleted successfully`, {
variant: 'success',
});
});
};

const actions = (
<ActionsPanel
primaryButtonProps={{
label: 'Delete',
loading: isPending,
disabled: false,
onClick: handleDelete,
}}
secondaryButtonProps={{ label: 'Cancel', onClick: onClose }}
style={{ padding: 0 }}
/>
);

return (
<ConfirmationDialog
actions={actions}
error={error}
onClose={onClose}
open={open}
title="Delete Destination"
>
Are you sure you want to delete &#34;{destination?.label}&#34;
destination?
</ConfirmationDialog>
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { TableRow } from 'src/components/TableRow';
import { getDestinationTypeOption } from 'src/features/Delivery/deliveryUtils';
import { DestinationActionMenu } from 'src/features/Delivery/Destinations/DestinationActionMenu';

import type { DestinationHandlers } from './DestinationActionMenu';
import type { Destination } from '@linode/api-v4';
import type { DestinationHandlers } from 'src/features/Delivery/Destinations/DestinationActionMenu';

interface DestinationTableRowProps extends DestinationHandlers {
destination: Destination;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { screen } from '@testing-library/react';
import { screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import * as React from 'react';
import { beforeEach, describe, expect } from 'vitest';
Expand Down Expand Up @@ -136,6 +136,12 @@ describe('Destinations Landing Table', () => {
await userEvent.click(screen.getByText(itemText));
};

const checkClosedModal = async (modal: HTMLElement) => {
await waitFor(() => {
expect(modal).not.toBeInTheDocument();
});
};

describe('given action menu', () => {
beforeEach(() => {
queryMocks.useDestinationsQuery.mockReturnValue({
Expand Down Expand Up @@ -173,9 +179,30 @@ describe('Destinations Landing Table', () => {
await clickOnActionMenu();
await clickOnActionMenuItem('Delete');

const deleteDestinationModal = screen.getByText('Delete Destination');
expect(deleteDestinationModal).toBeInTheDocument();

// get modal Cancel button
const cancelModalDialogButton = screen.getByRole('button', {
name: 'Cancel',
});
await userEvent.click(cancelModalDialogButton);
await checkClosedModal(deleteDestinationModal);

await clickOnActionMenu();
await clickOnActionMenuItem('Delete');

// get delete Destination button
const deleteDestinationButton = screen.getByRole('button', {
name: 'Delete',
});
await userEvent.click(deleteDestinationButton);

expect(mockDeleteDestinationMutation).toHaveBeenCalledWith({
id: 1,
});

await checkClosedModal(deleteDestinationModal);
});
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import {
useDeleteDestinationMutation,
useDestinationsQuery,
} from '@linode/queries';
import { useDestinationsQuery } from '@linode/queries';
import { CircleProgress, ErrorState, Hidden } from '@linode/ui';
import { TableBody, TableHead, TableRow } from '@mui/material';
import Table from '@mui/material/Table';
import { useNavigate, useSearch } from '@tanstack/react-router';
import { enqueueSnackbar } from 'notistack';
import * as React from 'react';

import { PaginationFooter } from 'src/components/PaginationFooter/PaginationFooter';
Expand All @@ -18,20 +14,24 @@ import {
DESTINATIONS_TABLE_DEFAULT_ORDER_BY,
DESTINATIONS_TABLE_PREFERENCE_KEY,
} from 'src/features/Delivery/Destinations/constants';
import { DeleteDestinationDialog } from 'src/features/Delivery/Destinations/DeleteDestinationDialog';
import { DestinationsLandingEmptyState } from 'src/features/Delivery/Destinations/DestinationsLandingEmptyState';
import { DestinationTableRow } from 'src/features/Delivery/Destinations/DestinationTableRow';
import { DeliveryTabHeader } from 'src/features/Delivery/Shared/DeliveryTabHeader/DeliveryTabHeader';
import { useOrderV2 } from 'src/hooks/useOrderV2';
import { usePaginationV2 } from 'src/hooks/usePaginationV2';
import { getAPIErrorOrDefault } from 'src/utilities/errorUtils';

import type { Destination } from '@linode/api-v4';
import type { DestinationHandlers } from 'src/features/Delivery/Destinations/DestinationActionMenu';

export const DestinationsLanding = () => {
const navigate = useNavigate();
const { mutateAsync: deleteDestination } = useDeleteDestinationMutation();
const destinationsUrl = '/logs/delivery/destinations';
const [deleteDialogOpen, setDeleteDialogOpen] =
React.useState<boolean>(false);
const [deleteDestinationSelection, setDeleteDestinationSelection] =
React.useState<Destination | undefined>();

const search = useSearch({
from: destinationsUrl,
shouldThrow: false,
Expand Down Expand Up @@ -104,31 +104,18 @@ export const DestinationsLanding = () => {
navigate({ to: `/logs/delivery/destinations/${id}/edit` });
};

const handleDelete = ({ id, label }: Destination) => {
deleteDestination({
id,
})
.then(() => {
return enqueueSnackbar(`Destination ${label} deleted successfully`, {
variant: 'success',
});
})
.catch((error) => {
return enqueueSnackbar(
getAPIErrorOrDefault(
error,
`There was an issue deleting your destination`
)[0].reason,
{
variant: 'error',
}
);
});
const openDeleteDialog = (destination: Destination) => {
setDeleteDestinationSelection(destination);
setDeleteDialogOpen(true);
};

const closeDeleteDialog = () => {
setDeleteDialogOpen(false);
};

const handlers: DestinationHandlers = {
onEdit: handleEdit,
onDelete: handleDelete,
onDelete: openDeleteDialog,
};

return (
Expand Down Expand Up @@ -213,6 +200,11 @@ export const DestinationsLanding = () => {
page={pagination.page}
pageSize={pagination.pageSize}
/>
<DeleteDestinationDialog
destination={deleteDestinationSelection}
onClose={closeDeleteDialog}
open={deleteDialogOpen}
/>
</>
)}
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { useDeleteStreamMutation } from '@linode/queries';
import { ActionsPanel } from '@linode/ui';
import { enqueueSnackbar } from 'notistack';
import * as React from 'react';

import { ConfirmationDialog } from 'src/components/ConfirmationDialog/ConfirmationDialog';

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

interface Props {
onClose: () => void;
open: boolean;
stream: Stream | undefined;
}

export const DeleteStreamDialog = React.memo((props: Props) => {
const { onClose, open, stream } = props;
const {
mutateAsync: deleteStream,
isPending,
error,
} = useDeleteStreamMutation();

const handleDelete = () => {
const { id, label } = stream as Stream;
deleteStream({
id,
}).then(() => {
onClose();
return enqueueSnackbar(`Stream ${label} deleted successfully`, {
variant: 'success',
});
});
};

const actions = (
<ActionsPanel
primaryButtonProps={{
label: 'Delete',
loading: isPending,
disabled: false,
onClick: handleDelete,
}}
secondaryButtonProps={{ label: 'Cancel', onClick: onClose }}
style={{ padding: 0 }}
/>
);

return (
<ConfirmationDialog
actions={actions}
error={error}
onClose={onClose}
open={open}
title="Delete Stream"
>
Are you sure you want to delete &#34;{stream?.label}&#34; stream?
</ConfirmationDialog>
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import * as React from 'react';

import { ActionMenu } from 'src/components/ActionMenu/ActionMenu';

export interface Handlers {
export interface StreamHandlers {
onDelete: (stream: Stream) => void;
onDisableOrEnable: (stream: Stream) => void;
onEdit: (stream: Stream) => void;
}

interface StreamActionMenuProps extends Handlers {
interface StreamActionMenuProps extends StreamHandlers {
stream: Stream;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import {
} from 'src/features/Delivery/deliveryUtils';
import { StreamActionMenu } from 'src/features/Delivery/Streams/StreamActionMenu';

import type { Handlers as StreamHandlers } from './StreamActionMenu';
import type { Stream, StreamStatus } from '@linode/api-v4';
import type { StreamHandlers } from 'src/features/Delivery/Streams/StreamActionMenu';

interface StreamTableRowProps extends StreamHandlers {
stream: Stream;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { screen, within } from '@testing-library/react';
import { screen, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import * as React from 'react';
import { beforeEach, describe, expect } from 'vitest';
Expand Down Expand Up @@ -144,6 +144,12 @@ describe('Streams Landing Table', () => {
await userEvent.click(screen.getByText(itemText));
};

const checkClosedModal = async (modal: HTMLElement) => {
await waitFor(() => {
expect(modal).not.toBeInTheDocument();
});
};

describe('given action menu', () => {
beforeEach(() => {
queryMocks.useStreamsQuery.mockReturnValue({
Expand Down Expand Up @@ -224,9 +230,30 @@ describe('Streams Landing Table', () => {
await clickOnActionMenu();
await clickOnActionMenuItem('Delete');

const deleteStreamModal = screen.getByText('Delete Stream');
expect(deleteStreamModal).toBeInTheDocument();

// get modal Cancel button
const cancelModalDialogButton = screen.getByRole('button', {
name: 'Cancel',
});
await userEvent.click(cancelModalDialogButton);
await checkClosedModal(deleteStreamModal);

await clickOnActionMenu();
await clickOnActionMenuItem('Delete');

// get delete Stream button
const deleteStreamButton = screen.getByRole('button', {
name: 'Delete',
});
await userEvent.click(deleteStreamButton);

expect(mockDeleteStreamMutation).toHaveBeenCalledWith({
id: 1,
});

await checkClosedModal(deleteStreamModal);
});
});
});
Expand Down
Loading