Skip to content

Commit 08b193f

Browse files
committed
persist input text when switching display modes
1 parent d8f0aa5 commit 08b193f

File tree

8 files changed

+96
-1
lines changed

8 files changed

+96
-1
lines changed

workspaces/lightspeed/plugins/lightspeed/report.api.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ import { ChatbotDisplayMode } from '@patternfly/chatbot';
1313
import { ConfigApi } from '@backstage/core-plugin-api';
1414
import { FetchApi } from '@backstage/core-plugin-api';
1515
import { JSX as JSX_2 } from 'react/jsx-runtime';
16+
import { PathParams } from '@backstage/core-plugin-api';
1617
import { PropsWithChildren } from 'react';
1718
import { RouteRef } from '@backstage/core-plugin-api';
1819
import { SourcesCardProps } from '@patternfly/chatbot';
20+
import { SubRouteRef } from '@backstage/core-plugin-api';
1921

2022
// @public
2123
export type Attachment = {
@@ -163,10 +165,12 @@ export const LightspeedChatContainer: () => JSX_2.Element;
163165
export interface LightspeedDrawerContextType {
164166
currentConversationId?: string;
165167
displayMode: ChatbotDisplayMode;
168+
draftMessage: string;
166169
drawerWidth: number;
167170
isChatbotActive: boolean;
168171
setCurrentConversationId: (id: string | undefined) => void;
169172
setDisplayMode: (mode: ChatbotDisplayMode) => void;
173+
setDraftMessage: (message: string) => void;
170174
setDrawerWidth: React.Dispatch<React.SetStateAction<number>>;
171175
toggleChatbot: () => void;
172176
}
@@ -192,6 +196,7 @@ export const LightspeedPage: () => JSX_2.Element;
192196
// @public
193197
export const lightspeedPlugin: BackstagePlugin< {
194198
root: RouteRef<undefined>;
199+
lightspeedConversation: SubRouteRef<PathParams<"/conversation/:conversationId">>;
195200
}, {}, {}>;
196201

197202
// @public

workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,14 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { MouseEvent, useCallback, useEffect, useMemo, useState } from 'react';
17+
import {
18+
ChangeEvent,
19+
MouseEvent,
20+
useCallback,
21+
useEffect,
22+
useMemo,
23+
useState,
24+
} from 'react';
1825
import { FileRejection } from 'react-dropzone/.';
1926

2027
import { makeStyles } from '@material-ui/core';
@@ -148,6 +155,8 @@ export const LightspeedChat = ({
148155
setDisplayMode,
149156
currentConversationId: routeConversationId,
150157
setCurrentConversationId,
158+
draftMessage,
159+
setDraftMessage,
151160
} = useLightspeedDrawerContext();
152161
const {
153162
uploadError,
@@ -292,6 +301,7 @@ export const LightspeedChat = ({
292301
handleInputPrompt(message.toString(), getAttachments(fileContents));
293302
setIsSendButtonDisabled(true);
294303
setFileContents([]);
304+
setDraftMessage('');
295305
};
296306

297307
const onNewChat = useCallback(() => {
@@ -300,6 +310,7 @@ export const LightspeedChat = ({
300310
setMessages([]);
301311
setFileContents([]);
302312
setUploadError({ message: null });
313+
setDraftMessage('');
303314
setConversationId(TEMP_CONVERSATION_ID);
304315
setNewChatCreated(true);
305316
setCurrentConversationId(undefined);
@@ -312,6 +323,7 @@ export const LightspeedChat = ({
312323
conversationId,
313324
setFileContents,
314325
setUploadError,
326+
setDraftMessage,
315327
displayMode,
316328
setCurrentConversationId,
317329
]);
@@ -491,12 +503,14 @@ export const LightspeedChat = ({
491503
setCurrentConversationId(newConvId);
492504
setFileContents([]);
493505
setUploadError({ message: null });
506+
setDraftMessage('');
494507
scrollToBottomRef.current?.scrollToBottom();
495508
},
496509
[
497510
setConversationId,
498511
setUploadError,
499512
setFileContents,
513+
setDraftMessage,
500514
scrollToBottomRef,
501515
setCurrentConversationId,
502516
],
@@ -545,6 +559,11 @@ export const LightspeedChat = ({
545559
handleFileUpload(data);
546560
};
547561

562+
const handleDraftMessage = (
563+
_e: ChangeEvent<HTMLTextAreaElement>,
564+
value: string | number,
565+
) => setDraftMessage(value as any);
566+
548567
const onAttachRejected = (data: FileRejection[]) => {
549568
data.forEach(attachment => {
550569
const hasInvalidTypeError = attachment.errors.some(
@@ -701,6 +720,8 @@ export const LightspeedChat = ({
701720
hasAttachButton
702721
handleAttach={handleAttach}
703722
hasMicrophoneButton
723+
value={draftMessage}
724+
onChange={handleDraftMessage}
704725
buttonProps={{
705726
attach: {
706727
inputTestId: 'attachment-input',

workspaces/lightspeed/plugins/lightspeed/src/components/LightspeedDrawerContext.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,15 @@ export interface LightspeedDrawerContextType {
5757
* Pass undefined to clear the conversation (example: for new chat)
5858
*/
5959
setCurrentConversationId: (id: string | undefined) => void;
60+
/**
61+
* The message in the chat input box
62+
* Used to preserve input content when switching between display modes
63+
*/
64+
draftMessage: string;
65+
/**
66+
* To save the input message as a draft when switching modes
67+
*/
68+
setDraftMessage: (message: string) => void;
6069
}
6170

6271
/**

workspaces/lightspeed/plugins/lightspeed/src/components/LightspeedDrawerProvider.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export const LightspeedDrawerProvider = ({ children }: PropsWithChildren) => {
6060
const [currentConversationIdState, setCurrentConversationIdState] = useState<
6161
string | undefined
6262
>(undefined);
63+
const [draftMessage, setDraftMessageState] = useState<string>('');
6364
const openedViaFABRef = useRef<boolean>(false);
6465

6566
const isLightspeedRoute = location.pathname.startsWith('/lightspeed');
@@ -148,6 +149,10 @@ export const LightspeedDrawerProvider = ({ children }: PropsWithChildren) => {
148149
[displayModeState, isLightspeedRoute, navigate],
149150
);
150151

152+
const setDraftMessage = useCallback((message: string) => {
153+
setDraftMessageState(message);
154+
}, []);
155+
151156
// Set display mode with route handling for embedded/fullscreen
152157
const setDisplayMode = useCallback(
153158
(mode: ChatbotDisplayMode, conversationIdParam?: string) => {
@@ -199,6 +204,8 @@ export const LightspeedDrawerProvider = ({ children }: PropsWithChildren) => {
199204
setDrawerWidth,
200205
currentConversationId: currentConversationIdState,
201206
setCurrentConversationId,
207+
draftMessage,
208+
setDraftMessage,
202209
}),
203210
[
204211
isOpen,
@@ -209,6 +216,8 @@ export const LightspeedDrawerProvider = ({ children }: PropsWithChildren) => {
209216
setDrawerWidth,
210217
currentConversationIdState,
211218
setCurrentConversationId,
219+
draftMessage,
220+
setDraftMessage,
212221
],
213222
);
214223

workspaces/lightspeed/plugins/lightspeed/src/components/__tests__/LightspeedChat.test.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ describe('LightspeedChat', () => {
169169
setDrawerWidth: jest.fn(),
170170
currentConversationId: undefined,
171171
setCurrentConversationId: mockSetCurrentConversationId,
172+
draftMessage: '',
173+
setDraftMessage: jest.fn(),
172174
});
173175

174176
localStorage.clear();
@@ -484,6 +486,8 @@ describe('LightspeedChat', () => {
484486
setDrawerWidth: jest.fn(),
485487
currentConversationId: undefined,
486488
setCurrentConversationId: mockSetCurrentConversationId,
489+
draftMessage: '',
490+
setDraftMessage: jest.fn(),
487491
});
488492

489493
render(setupLightspeedChat());
@@ -511,6 +515,8 @@ describe('LightspeedChat', () => {
511515
setDrawerWidth: jest.fn(),
512516
currentConversationId: undefined,
513517
setCurrentConversationId: mockSetCurrentConversationId,
518+
draftMessage: '',
519+
setDraftMessage: jest.fn(),
514520
});
515521

516522
render(setupLightspeedChat());
@@ -538,6 +544,8 @@ describe('LightspeedChat', () => {
538544
setDrawerWidth: jest.fn(),
539545
currentConversationId: undefined,
540546
setCurrentConversationId: mockSetCurrentConversationId,
547+
draftMessage: '',
548+
setDraftMessage: jest.fn(),
541549
});
542550

543551
render(setupLightspeedChat());

workspaces/lightspeed/plugins/lightspeed/src/components/__tests__/LightspeedDrawerStateExposer.test.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ describe('LightspeedDrawerStateExposer', () => {
3737
setDrawerWidth: mockSetDrawerWidth,
3838
currentConversationId: undefined,
3939
setCurrentConversationId: jest.fn(),
40+
draftMessage: '',
41+
setDraftMessage: jest.fn(),
4042
...overrides,
4143
});
4244

workspaces/lightspeed/plugins/lightspeed/src/components/__tests__/LightspeedFAB.test.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ describe('LightspeedFAB', () => {
3737
setDrawerWidth: jest.fn(),
3838
currentConversationId: undefined,
3939
setCurrentConversationId: jest.fn(),
40+
draftMessage: '',
41+
setDraftMessage: jest.fn(),
4042
...overrides,
4143
});
4244

workspaces/lightspeed/plugins/lightspeed/src/hooks/__tests__/useLightspeedDrawerContext.test.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ describe('useLightspeedDrawerContext', () => {
3030
setDrawerWidth: jest.fn(),
3131
currentConversationId: 'test-conv-id',
3232
setCurrentConversationId: jest.fn(),
33+
draftMessage: '',
34+
setDraftMessage: jest.fn(),
3335
};
3436

3537
it('should return context value when used within provider', () => {
@@ -217,4 +219,41 @@ describe('useLightspeedDrawerContext', () => {
217219
result.current.setCurrentConversationId('new-conv-id');
218220
expect(mockSetConversationId).toHaveBeenCalledWith('new-conv-id');
219221
});
222+
223+
it('should return draftMessage from context', () => {
224+
const wrapper = ({ children }: { children: React.ReactNode }) => (
225+
<LightspeedDrawerContext.Provider
226+
value={{ ...mockContextValue, draftMessage: 'test draft message' }}
227+
>
228+
{children}
229+
</LightspeedDrawerContext.Provider>
230+
);
231+
232+
const { result } = renderHook(() => useLightspeedDrawerContext(), {
233+
wrapper,
234+
});
235+
236+
expect(result.current.draftMessage).toBe('test draft message');
237+
});
238+
239+
it('should provide working setDraftMessage function', () => {
240+
const mockSetDraftMessage = jest.fn();
241+
const wrapper = ({ children }: { children: React.ReactNode }) => (
242+
<LightspeedDrawerContext.Provider
243+
value={{
244+
...mockContextValue,
245+
setDraftMessage: mockSetDraftMessage,
246+
}}
247+
>
248+
{children}
249+
</LightspeedDrawerContext.Provider>
250+
);
251+
252+
const { result } = renderHook(() => useLightspeedDrawerContext(), {
253+
wrapper,
254+
});
255+
256+
result.current.setDraftMessage('new draft message');
257+
expect(mockSetDraftMessage).toHaveBeenCalledWith('new draft message');
258+
});
220259
});

0 commit comments

Comments
 (0)