Skip to content

Commit 4f09b12

Browse files
Bangarraju-MicrosoftPrajwal-MicrosoftRoopan-MicrosoftAjitPadhi-MicrosoftPavan-Microsoft
authored
fix: bug(#1653) added tooltip for chat history items. (#1785)
Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: Prajwal-Microsoft <[email protected]> Co-authored-by: Roopan-Microsoft <[email protected]> Co-authored-by: Ajit Padhi <[email protected]> Co-authored-by: Roopan P M <[email protected]> Co-authored-by: Pavan-Microsoft <[email protected]> Co-authored-by: Ross Smith <[email protected]> Co-authored-by: gpickett <[email protected]> Co-authored-by: Francia Riesco <[email protected]> Co-authored-by: Francia Riesco <[email protected]> Co-authored-by: Harmanpreet-Microsoft <[email protected]> Co-authored-by: UtkarshMishra-Microsoft <[email protected]> Co-authored-by: Priyanka-Microsoft <[email protected]> Co-authored-by: Prasanjeet-Microsoft <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Kiran-Siluveru-Microsoft <[email protected]> Co-authored-by: Prashant-Microsoft <[email protected]> Co-authored-by: Rohini-Microsoft <[email protected]> Co-authored-by: Avijit-Microsoft <[email protected]> Co-authored-by: RaviKiran-Microsoft <[email protected]> Co-authored-by: Somesh Joshi <[email protected]> Co-authored-by: Himanshi Agrawal <[email protected]> Co-authored-by: pradeepjha-microsoft <[email protected]> Co-authored-by: Harmanpreet Kaur <[email protected]> Co-authored-by: Harsh-Microsoft <[email protected]> Co-authored-by: Kanchan-Microsoft <[email protected]>
1 parent e8382d3 commit 4f09b12

File tree

2 files changed

+81
-55
lines changed

2 files changed

+81
-55
lines changed

code/frontend/src/components/ChatHistoryListItemCell/ChatHistoryListItemCell.test.tsx

Lines changed: 65 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,17 @@ jest.mock('../../api/api', () => ({
1111
historyDelete: jest.fn()
1212
}))
1313

14+
// Mocking TooltipHost from @fluentui/react
15+
jest.mock('@fluentui/react', () => {
16+
const actual = jest.requireActual('@fluentui/react');
17+
return {
18+
...actual,
19+
TooltipHost: ({ children }: { children: React.ReactNode }) => (
20+
<div data-testid="tooltip-mock">{children}</div>
21+
),
22+
};
23+
});
24+
1425
const conversation: Conversation = {
1526
id: '1',
1627
title: 'Test Chat',
@@ -47,7 +58,7 @@ describe('ChatHistoryListItemCell', () => {
4758
})
4859

4960
test('renders the chat history item', () => {
50-
render(<ChatHistoryListItemCell {...componentProps} />);
61+
render(<ChatHistoryListItemCell {...componentProps} />);
5162
const titleElement = screen.getByText(/Test Chat/i)
5263
expect(titleElement).toBeInTheDocument()
5364
})
@@ -71,10 +82,10 @@ render(<ChatHistoryListItemCell {...componentProps} />);
7182
// Check if the truncated title is in the document
7283
const truncatedTitle = screen.getByText(/A very long title that shoul .../i)
7384
expect(truncatedTitle).toBeInTheDocument()
74-
})
85+
})
7586

7687
test('calls onSelect when clicked', () => {
77-
render(<ChatHistoryListItemCell {...componentProps} />);
88+
render(<ChatHistoryListItemCell {...componentProps} />);
7889
const item = screen.getByLabelText('chat history item')
7990
fireEvent.click(item)
8091
expect(mockOnSelect).toHaveBeenCalledWith(conversation)
@@ -93,7 +104,7 @@ render(<ChatHistoryListItemCell {...componentProps} />);
93104
// Expect that no content related to the title is rendered
94105
const titleElement = screen.queryByText(/Test Chat/i);
95106
expect(titleElement).not.toBeInTheDocument();
96-
})
107+
})
97108

98109
test('displays delete and edit buttons on hover', async () => {
99110
const mockAppStateUpdated = {
@@ -132,7 +143,7 @@ render(<ChatHistoryListItemCell {...componentProps} />);
132143
})
133144

134145
test('shows confirmation dialog and deletes item', async () => {
135-
;(historyDelete as jest.Mock).mockResolvedValueOnce({
146+
; (historyDelete as jest.Mock).mockResolvedValueOnce({
136147
ok: true,
137148
json: async () => ({})
138149
})
@@ -156,7 +167,7 @@ render(<ChatHistoryListItemCell {...componentProps} />);
156167
})
157168

158169
test('when delete API fails or return false', async () => {
159-
;(historyDelete as jest.Mock).mockResolvedValueOnce({
170+
; (historyDelete as jest.Mock).mockResolvedValueOnce({
160171
ok: false,
161172
json: async () => ({})
162173
})
@@ -204,10 +215,10 @@ render(<ChatHistoryListItemCell {...componentProps} />);
204215
const appStateWithRequestInitiated = {
205216
...componentProps,
206217
isGenerating: true,
207-
selectedConvId:'1'
218+
selectedConvId: '1'
208219
}
209220

210-
render(<ChatHistoryListItemCell {...appStateWithRequestInitiated} onSelect={mockOnSelect} />);
221+
render(<ChatHistoryListItemCell {...appStateWithRequestInitiated} onSelect={mockOnSelect} />);
211222
const item = screen.getByLabelText('chat history item')
212223
fireEvent.mouseEnter(item)
213224
const deleteButton = screen.getByTitle(/Delete/i)
@@ -218,10 +229,10 @@ render(<ChatHistoryListItemCell {...componentProps} />);
218229
})
219230

220231
test('does not disable buttons when request is not initiated', () => {
221-
render(<ChatHistoryListItemCell {...componentProps} />);
222-
const item = screen.getByLabelText('chat history item')
223-
fireEvent.mouseEnter(item)
224-
const deleteButton = screen.getByTitle(/Delete/i)
232+
render(<ChatHistoryListItemCell {...componentProps} />);
233+
const item = screen.getByLabelText('chat history item')
234+
fireEvent.mouseEnter(item)
235+
const deleteButton = screen.getByTitle(/Delete/i)
225236
const editButton = screen.getByTitle(/Edit/i)
226237

227238
expect(deleteButton).not.toBeDisabled()
@@ -245,12 +256,12 @@ render(<ChatHistoryListItemCell {...componentProps} />);
245256
})
246257

247258
test('handles input onChange and onKeyDown ENTER events correctly', async () => {
248-
;(historyRename as jest.Mock).mockResolvedValueOnce({
259+
; (historyRename as jest.Mock).mockResolvedValueOnce({
249260
ok: true,
250261
json: async () => ({})
251262
})
252263

253-
render(<ChatHistoryListItemCell {...componentProps} />);
264+
render(<ChatHistoryListItemCell {...componentProps} />);
254265
// Simulate hover to reveal Edit button
255266
const item = screen.getByLabelText('chat history item')
256267
fireEvent.mouseEnter(item)
@@ -316,7 +327,7 @@ render(<ChatHistoryListItemCell {...componentProps} />);
316327
test('Should hide the rename from when cancel it.', async () => {
317328
userEvent.setup()
318329

319-
render(<ChatHistoryListItemCell {...componentProps}/>)
330+
render(<ChatHistoryListItemCell {...componentProps} />)
320331
const item = screen.getByLabelText('chat history item')
321332
fireEvent.mouseEnter(item)
322333
// Wait for the Edit button to appear and click it
@@ -336,10 +347,10 @@ render(<ChatHistoryListItemCell {...componentProps} />);
336347

337348
test.skip('handles rename save API failed', async () => {
338349
userEvent.setup()
339-
;(historyRename as jest.Mock).mockRejectedValue({
340-
ok: false,
341-
json: async () => ({})
342-
})
350+
; (historyRename as jest.Mock).mockRejectedValue({
351+
ok: false,
352+
json: async () => ({})
353+
})
343354

344355
render(<ChatHistoryListItemCell {...componentProps} />);
345356
// Simulate hover to reveal Edit button
@@ -380,12 +391,12 @@ render(<ChatHistoryListItemCell {...componentProps} />);
380391
date: new Date().toISOString()
381392
}
382393

383-
;(historyRename as jest.Mock).mockResolvedValueOnce({
384-
ok: false,
385-
json: async () => ({ message: 'Title already exists' })
386-
})
394+
; (historyRename as jest.Mock).mockResolvedValueOnce({
395+
ok: false,
396+
json: async () => ({ message: 'Title already exists' })
397+
})
387398

388-
render(<ChatHistoryListItemCell {...componentProps} />);
399+
render(<ChatHistoryListItemCell {...componentProps} />);
389400
const item = screen.getByLabelText('chat history item')
390401
fireEvent.mouseEnter(item)
391402

@@ -406,7 +417,7 @@ render(<ChatHistoryListItemCell {...componentProps} />);
406417

407418

408419
test('triggers edit functionality when Enter key is pressed', async () => {
409-
;(historyRename as jest.Mock).mockResolvedValueOnce({
420+
; (historyRename as jest.Mock).mockResolvedValueOnce({
410421
ok: true,
411422
json: async () => ({ message: 'Title changed' })
412423
})
@@ -438,12 +449,12 @@ render(<ChatHistoryListItemCell {...componentProps} />);
438449
})
439450

440451
test('successfully saves edited title', async () => {
441-
;(historyRename as jest.Mock).mockResolvedValueOnce({
452+
; (historyRename as jest.Mock).mockResolvedValueOnce({
442453
ok: true,
443454
json: async () => ({})
444455
})
445456

446-
render(<ChatHistoryListItemCell {...componentProps} />);
457+
render(<ChatHistoryListItemCell {...componentProps} />);
447458
const item = screen.getByLabelText('chat history item')
448459
fireEvent.mouseEnter(item)
449460

@@ -500,7 +511,7 @@ render(<ChatHistoryListItemCell {...componentProps} />);
500511
///////
501512

502513
test('opens delete confirmation dialog when Enter key is pressed on the Delete button', async () => {
503-
render(<ChatHistoryListItemCell {...componentProps} />);
514+
render(<ChatHistoryListItemCell {...componentProps} />);
504515
const item = screen.getByLabelText('chat history item')
505516
fireEvent.mouseEnter(item)
506517

@@ -511,7 +522,7 @@ render(<ChatHistoryListItemCell {...componentProps} />);
511522
})
512523

513524
test('opens delete confirmation dialog when Space key is pressed on the Delete button', async () => {
514-
render(<ChatHistoryListItemCell {...componentProps} />);
525+
render(<ChatHistoryListItemCell {...componentProps} />);
515526
const item = screen.getByLabelText('chat history item')
516527
fireEvent.mouseEnter(item)
517528

@@ -522,7 +533,7 @@ render(<ChatHistoryListItemCell {...componentProps} />);
522533
})
523534

524535
test('opens edit input when Space key is pressed on the Edit button', async () => {
525-
render(<ChatHistoryListItemCell {...componentProps} />);
536+
render(<ChatHistoryListItemCell {...componentProps} />);
526537
const item = screen.getByLabelText('chat history item')
527538
fireEvent.mouseEnter(item)
528539

@@ -534,7 +545,7 @@ render(<ChatHistoryListItemCell {...componentProps} />);
534545
})
535546

536547
test('opens edit input when Enter key is pressed on the Edit button', async () => {
537-
render(<ChatHistoryListItemCell {...componentProps} />);
548+
render(<ChatHistoryListItemCell {...componentProps} />);
538549
const item = screen.getByLabelText('chat history item')
539550
fireEvent.mouseEnter(item)
540551

@@ -547,11 +558,11 @@ render(<ChatHistoryListItemCell {...componentProps} />);
547558

548559
test('handles rename save when the updated text is equal to initial text', async () => {
549560
userEvent.setup()
550-
;(historyRename as jest.Mock).mockRejectedValue({
551-
ok: false,
552-
json: async () => ({ message: 'Title not changed' })
553-
})
554-
render(<ChatHistoryListItemCell {...componentProps}/>)
561+
; (historyRename as jest.Mock).mockRejectedValue({
562+
ok: false,
563+
json: async () => ({ message: 'Title not changed' })
564+
})
565+
render(<ChatHistoryListItemCell {...componentProps} />)
555566

556567
// Simulate hover to reveal Edit button
557568
const item = screen.getByLabelText('chat history item')
@@ -573,26 +584,26 @@ render(<ChatHistoryListItemCell {...componentProps} />);
573584
//fireEvent.change(inputItem, { target: { value: 'Test Chat' } });
574585
})
575586
expect(historyRename).not.toHaveBeenCalled()
576-
})
577-
test('Should hide the rename from on Enter or space .', async () => {
578-
userEvent.setup()
579-
580-
render(<ChatHistoryListItemCell {...componentProps}/>)
581-
const item = screen.getByLabelText('chat history item')
582-
fireEvent.mouseEnter(item)
583-
// Wait for the Edit button to appear and click it
584-
await waitFor(() => {
585-
const editButton = screen.getByTitle(/Edit/i)
586-
fireEvent.click(editButton)
587587
})
588+
test('Should hide the rename from on Enter or space .', async () => {
589+
userEvent.setup()
588590

589-
const editButton =screen.getByRole('button', { name: 'cancel edit title' })
590-
fireEvent.keyDown(editButton, { key: 'Enter', code: 'Enter', charCode: 13 })
591+
render(<ChatHistoryListItemCell {...componentProps} />)
592+
const item = screen.getByLabelText('chat history item')
593+
fireEvent.mouseEnter(item)
594+
// Wait for the Edit button to appear and click it
595+
await waitFor(() => {
596+
const editButton = screen.getByTitle(/Edit/i)
597+
fireEvent.click(editButton)
598+
})
591599

592-
// Wait for the error to be hidden after 5 seconds
593-
await waitFor(() => {
594-
const input = screen.queryByLabelText('confirm new title')
595-
expect(input).not.toBeInTheDocument()
600+
const editButton = screen.getByRole('button', { name: 'cancel edit title' })
601+
fireEvent.keyDown(editButton, { key: 'Enter', code: 'Enter', charCode: 13 })
602+
603+
// Wait for the error to be hidden after 5 seconds
604+
await waitFor(() => {
605+
const input = screen.queryByLabelText('confirm new title')
606+
expect(input).not.toBeInTheDocument()
607+
})
596608
})
597609
})
598-
})

code/frontend/src/components/ChatHistoryListItemCell/ChatHistoryListItemCell.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ import {
77
DialogType,
88
IconButton,
99
ITextField,
10+
ITooltipHostStyles,
1011
PrimaryButton,
1112
Stack,
1213
Text,
1314
TextField,
15+
TooltipHost,
1416
} from "@fluentui/react";
1517
import { useBoolean } from "@fluentui/react-hooks";
1618

@@ -30,6 +32,11 @@ interface ChatHistoryListItemCellProps {
3032
toggleToggleSpinner: (toggler: boolean) => void;
3133
}
3234

35+
36+
const calloutProps = { gapSpace: 0 };
37+
const hostStyles: Partial<ITooltipHostStyles> = { root: { display: 'inline-block' } };
38+
39+
3340
export const ChatHistoryListItemCell: React.FC<
3441
ChatHistoryListItemCellProps
3542
> = ({
@@ -51,6 +58,7 @@ export const ChatHistoryListItemCell: React.FC<
5158
const [textFieldFocused, setTextFieldFocused] = useState(false);
5259
const textFieldRef = useRef<ITextField | null>(null);
5360
const isSelected = item?.id === selectedConvId;
61+
const tooltipId = 'tooltip'+ item?.id;
5462
const dialogContentProps = {
5563
type: DialogType.close,
5664
title: "Are you sure you want to delete this item?",
@@ -257,7 +265,14 @@ export const ChatHistoryListItemCell: React.FC<
257265
) : (
258266
<>
259267
<Stack horizontal verticalAlign={"center"} style={{ width: "100%" }}>
260-
<div className={styles.chatTitle}>{truncatedTitle}</div>
268+
<div className={styles.chatTitle}>
269+
<TooltipHost
270+
content={item?.title}
271+
id={tooltipId}
272+
closeDelay={100}
273+
calloutProps={calloutProps}
274+
styles={hostStyles}
275+
>{truncatedTitle} </TooltipHost></div>
261276
{(isSelected || isHovered) && (
262277
<Stack horizontal horizontalAlign="end">
263278
<IconButton

0 commit comments

Comments
 (0)