diff --git a/packages/compass-assistant/src/compass-assistant-drawer.tsx b/packages/compass-assistant/src/compass-assistant-drawer.tsx index 4e4de8bb231..09b3ad98503 100644 --- a/packages/compass-assistant/src/compass-assistant-drawer.tsx +++ b/packages/compass-assistant/src/compass-assistant-drawer.tsx @@ -1,8 +1,14 @@ -import React, { useContext } from 'react'; -import { DrawerSection } from '@mongodb-js/compass-components'; +import React, { useCallback, useContext } from 'react'; +import { + DrawerSection, + Icon, + IconButton, + showConfirmation, +} from '@mongodb-js/compass-components'; import { AssistantChat } from './assistant-chat'; import { ASSISTANT_DRAWER_ID, + AssistantActionsContext, AssistantContext, } from './compass-assistant-provider'; import { usePreference } from 'compass-preferences-model/provider'; @@ -16,9 +22,23 @@ export const CompassAssistantDrawer: React.FunctionComponent<{ autoOpen?: boolean; }> = ({ autoOpen }) => { const chat = useContext(AssistantContext); + const { clearChat } = useContext(AssistantActionsContext); const enableAIAssistant = usePreference('enableAIAssistant'); + const handleClearChat = useCallback(async () => { + const confirmed = await showConfirmation({ + title: 'Clear this chat?', + description: + 'The current chat will be cleared, and chat history will not be retrievable.', + buttonText: 'Clear chat', + variant: 'danger', + }); + if (confirmed) { + clearChat(); + } + }, [clearChat]); + if (!enableAIAssistant) { return null; } @@ -32,7 +52,27 @@ export const CompassAssistantDrawer: React.FunctionComponent<{ return ( + MongoDB Assistant + { + void handleClearChat(); + }} + title="Clear chat" + data-testid="assistant-clear-chat" + > + + + + } label="MongoDB Assistant" glyph="Sparkle" autoOpen={autoOpen} diff --git a/packages/compass-assistant/src/compass-assistant-provider.spec.tsx b/packages/compass-assistant/src/compass-assistant-provider.spec.tsx index 436ce1903b1..412526dd846 100644 --- a/packages/compass-assistant/src/compass-assistant-provider.spec.tsx +++ b/packages/compass-assistant/src/compass-assistant-provider.spec.tsx @@ -4,6 +4,8 @@ import { screen, userEvent, waitFor, + waitForElementToBeRemoved, + within, } from '@mongodb-js/testing-library-compass'; import { AssistantProvider, @@ -40,6 +42,19 @@ const TestComponent: React.FunctionComponent<{ }; describe('AssistantProvider', function () { + const mockMessages: AssistantMessage[] = [ + { + id: '1', + role: 'user', + parts: [{ type: 'text', text: 'Test message' }], + }, + { + id: '2', + role: 'assistant', + parts: [{ type: 'text', text: 'Test assistant message' }], + }, + ]; + it('always renders children', function () { render(, { preferences: { enableAIAssistant: true }, @@ -92,18 +107,6 @@ describe('AssistantProvider', function () { } it('displays messages in the chat feed', async function () { - const mockMessages: AssistantMessage[] = [ - { - id: '1', - role: 'user', - parts: [{ type: 'text', text: 'Test message' }], - }, - { - id: '2', - role: 'assistant', - parts: [{ type: 'text', text: 'Test assistant message' }], - }, - ]; const mockChat = createMockChat({ messages: mockMessages }); await renderOpenAssistantDrawer(mockChat); @@ -186,6 +189,66 @@ describe('AssistantProvider', function () { expect(screen.getByText('Hello assistant!')).to.exist; }); }); + + describe('clear chat button', function () { + it('clears the chat when the user clicks and confirms', async function () { + const mockChat = createMockChat({ messages: mockMessages }); + + await renderOpenAssistantDrawer(mockChat); + + const clearButton = screen.getByTestId('assistant-clear-chat'); + userEvent.click(clearButton); + + await waitFor(() => { + expect(screen.getByTestId('confirmation-modal')).to.exist; + }); + + // There should be messages in the chat + expect(screen.getByTestId('assistant-message-1')).to.exist; + expect(screen.getByTestId('assistant-message-2')).to.exist; + + const modal = screen.getByTestId('confirmation-modal'); + const confirmButton = within(modal).getByText('Clear chat'); + userEvent.click(confirmButton); + + await waitForElementToBeRemoved(() => + screen.getByTestId('confirmation-modal') + ); + + expect(mockChat.messages).to.be.empty; + expect(screen.queryByTestId('assistant-message-1')).to.not.exist; + expect(screen.queryByTestId('assistant-message-2')).to.not.exist; + }); + + it('does not clear the chat when the user clicks the button and cancels', async function () { + const mockChat = createMockChat({ messages: mockMessages }); + + await renderOpenAssistantDrawer(mockChat); + + const clearButton = screen.getByTestId('assistant-clear-chat'); + userEvent.click(clearButton); + + await waitFor(() => { + expect(screen.getByTestId('confirmation-modal')).to.exist; + }); + + // There should be messages in the chat + expect(screen.getByTestId('assistant-message-1')).to.exist; + expect(screen.getByTestId('assistant-message-2')).to.exist; + + const modal = screen.getByTestId('confirmation-modal'); + const cancelButton = within(modal).getByText('Cancel'); + userEvent.click(cancelButton); + + await waitForElementToBeRemoved(() => + screen.getByTestId('confirmation-modal') + ); + + expect(mockChat.messages).to.deep.equal(mockMessages); + expect(screen.getByTestId('assistant-message-1')).to.exist; + expect(screen.getByTestId('assistant-message-2')).to.exist; + }); + }); }); describe('CompassAssistantProvider', function () { diff --git a/packages/compass-assistant/src/compass-assistant-provider.tsx b/packages/compass-assistant/src/compass-assistant-provider.tsx index e63aef9072e..d0d740bbe54 100644 --- a/packages/compass-assistant/src/compass-assistant-provider.tsx +++ b/packages/compass-assistant/src/compass-assistant-provider.tsx @@ -32,10 +32,12 @@ type AssistantActionsContextType = { namespace: string; explainPlan: string; }) => void; + clearChat: () => void; }; export const AssistantActionsContext = createContext({ interpretExplainPlan: () => {}, + clearChat: () => {}, }); export function useAssistantActions(): AssistantActionsContextType & { @@ -70,6 +72,9 @@ export const AssistantProvider: React.FunctionComponent< {} ); }, + clearChat: () => { + chat.messages = []; + }, }); const { openDrawer } = useDrawerActions();