From 6443544a6bbe3bd6fcbe7699cce51d8913719240 Mon Sep 17 00:00:00 2001 From: Dan Ditomaso Date: Tue, 1 Apr 2025 14:50:41 -0400 Subject: [PATCH] added dialog to warn before clearing all messages --- src/components/CommandPalette.tsx | 6 +- .../ClearMessagesDialog.test.tsx | 53 ++++++++++++++++ .../ClearMessagesDialog.tsx | 63 +++++++++++++++++++ src/components/Dialog/DialogManager.tsx | 7 +++ src/core/stores/deviceStore.ts | 4 +- 5 files changed, 127 insertions(+), 6 deletions(-) create mode 100644 src/components/Dialog/ClearMessagesDialog/ClearMessagesDialog.test.tsx create mode 100644 src/components/Dialog/ClearMessagesDialog/ClearMessagesDialog.tsx diff --git a/src/components/CommandPalette.tsx b/src/components/CommandPalette.tsx index a5d9dc65..20c399aa 100644 --- a/src/components/CommandPalette.tsx +++ b/src/components/CommandPalette.tsx @@ -9,7 +9,6 @@ import { } from "@components/UI/Command.tsx"; import { useAppStore } from "@core/stores/appStore.ts"; import { useDevice, useDeviceStore } from "@core/stores/deviceStore.ts"; -import { use } from "chai"; import { useCommandState } from "cmdk"; import { ArrowLeftRightIcon, @@ -33,8 +32,6 @@ import { XCircleIcon, } from "lucide-react"; import { useEffect } from "react"; -import { useMap } from "react-map-gl/maplibre"; -import { useMessageStore } from "@core/stores/messageStore.ts"; export interface Group { label: string; @@ -64,7 +61,6 @@ export const CommandPalette = () => { selectedDevice, } = useAppStore(); const { getDevices } = useDeviceStore(); - const { clearAllMessages } = useMessageStore(); const { setDialogOpen, setActivePage, connection } = useDevice(); const groups: Group[] = [ @@ -228,7 +224,7 @@ export const CommandPalette = () => { label: "Clear All Stored Message", icon: EraserIcon, action() { - void clearAllMessages(); + setDialogOpen("clearMessages", true); }, }, ], diff --git a/src/components/Dialog/ClearMessagesDialog/ClearMessagesDialog.test.tsx b/src/components/Dialog/ClearMessagesDialog/ClearMessagesDialog.test.tsx new file mode 100644 index 00000000..cf317589 --- /dev/null +++ b/src/components/Dialog/ClearMessagesDialog/ClearMessagesDialog.test.tsx @@ -0,0 +1,53 @@ +import { render, screen, fireEvent } from '@testing-library/react'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { useMessageStore } from "@core/stores/messageStore.ts"; +import { ClearMessagesDialog } from "@components/Dialog/ClearMessagesDialog/ClearMessagesDialog.tsx"; + +vi.mock('@core/stores/messageStore.ts', () => ({ + useMessageStore: vi.fn(() => ({ + clearAllMessages: vi.fn(), + })), +})); + +describe('ClearMessagesDialog', () => { + const mockOnOpenChange = vi.fn(); + const mockClearAllMessages = vi.fn(); + + beforeEach(() => { + vi.mocked(useMessageStore).mockReturnValue({ clearAllMessages: mockClearAllMessages }); + mockOnOpenChange.mockClear(); + mockClearAllMessages.mockClear(); + }); + + it('renders the dialog when open is true', () => { + render(); + expect(screen.getByText('Clear All Messages')).toBeVisible(); + expect(screen.getByText(/This action will clear all message history./)).toBeVisible(); + expect(screen.getByRole('button', { name: 'Dismiss' })).toBeVisible(); + expect(screen.getByRole('button', { name: 'Clear Messages' })).toBeVisible(); + }); + + it('does not render the dialog when open is false', () => { + render(); + expect(screen.queryByText('Clear All Messages')).toBeNull(); + }); + + it('calls onOpenChange with false when the close button is clicked', () => { + render(); + fireEvent.click(screen.getByRole('button', { name: 'Close' })); + expect(mockOnOpenChange).toHaveBeenCalledWith(false); + }); + + it('calls onOpenChange with false when the dismiss button is clicked', () => { + render(); + fireEvent.click(screen.getByRole('button', { name: 'Dismiss' })); + expect(mockOnOpenChange).toHaveBeenCalledWith(false); + }); + + it('calls clearAllMessages and onOpenChange with false when the clear messages button is clicked', () => { + render(); + fireEvent.click(screen.getByRole('button', { name: 'Clear Messages' })); + expect(mockClearAllMessages).toHaveBeenCalledTimes(1); + expect(mockOnOpenChange).toHaveBeenCalledWith(false); + }); +}); \ No newline at end of file diff --git a/src/components/Dialog/ClearMessagesDialog/ClearMessagesDialog.tsx b/src/components/Dialog/ClearMessagesDialog/ClearMessagesDialog.tsx new file mode 100644 index 00000000..4bb27b82 --- /dev/null +++ b/src/components/Dialog/ClearMessagesDialog/ClearMessagesDialog.tsx @@ -0,0 +1,63 @@ + +import { Button } from "@components/UI/Button.tsx"; +import { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@components/UI/Dialog.tsx"; +import { AlertTriangleIcon } from "lucide-react"; +import { useMessageStore } from "@core/stores/messageStore.ts"; + +export interface ClearMessagesDialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; +} + +export const ClearMessagesDialog = ({ + open, + onOpenChange, +}: ClearMessagesDialogProps) => { + const { clearAllMessages } = useMessageStore(); + const handleCloseDialog = () => { + onOpenChange(false); + }; + + return ( + + + + + + + Clear All Messages + + + This action will clear all message history. This cannot be undone. + Are you sure you want to continue? + + + + + + + + + ); +}; diff --git a/src/components/Dialog/DialogManager.tsx b/src/components/Dialog/DialogManager.tsx index 1cfbcb3e..07432c18 100644 --- a/src/components/Dialog/DialogManager.tsx +++ b/src/components/Dialog/DialogManager.tsx @@ -9,6 +9,7 @@ import { ShutdownDialog } from "@components/Dialog/ShutdownDialog.tsx"; import { NodeDetailsDialog } from "@components/Dialog/NodeDetailsDialog/NodeDetailsDialog.tsx"; import { UnsafeRolesDialog } from "@components/Dialog/UnsafeRolesDialog/UnsafeRolesDialog.tsx"; import { RefreshKeysDialog } from "@components/Dialog/RefreshKeysDialog/RefreshKeysDialog.tsx"; +import { ClearMessagesDialog } from "@components/Dialog/ClearMessagesDialog/ClearMessagesDialog.tsx"; export const DialogManager = () => { const { channels, config, dialog, setDialogOpen } = useDevice(); @@ -77,6 +78,12 @@ export const DialogManager = () => { setDialogOpen("refreshKeys", open); }} /> + { + setDialogOpen("clearMessages", open); + }} + /> ); }; diff --git a/src/core/stores/deviceStore.ts b/src/core/stores/deviceStore.ts index 148e9f0b..62b6e4c8 100644 --- a/src/core/stores/deviceStore.ts +++ b/src/core/stores/deviceStore.ts @@ -28,7 +28,8 @@ export type DialogVariant = | "pkiBackup" | "nodeDetails" | "unsafeRoles" - | "refreshKeys"; + | "refreshKeys" + | "clearMessages"; type NodeError = { node: number; @@ -69,6 +70,7 @@ export interface Device { nodeDetails: boolean; unsafeRoles: boolean; refreshKeys: boolean; + clearMessages: boolean; };