Skip to content

Commit 5734eaa

Browse files
committed
Initial draft
1 parent c57a832 commit 5734eaa

File tree

9 files changed

+424
-13
lines changed

9 files changed

+424
-13
lines changed

dist/build/static/js/bundle.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/RESIZABLE_WIDGET.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# Resizable Chat Widget Feature
2+
3+
This document provides information on how to use the resizable chat widget feature in the Langflow Embedded Chat component.
4+
5+
## Overview
6+
7+
The resizable chat widget allows users to dynamically adjust the dimensions of the chat window according to their preferences. This feature enhances user experience by providing flexibility in sizing the chat interface.
8+
9+
## Usage
10+
11+
To enable the resizable chat widget, use the following attributes when embedding the chat widget:
12+
13+
```html
14+
<langflow-chat
15+
window_title="Resizable Chat"
16+
flow_id="your-flow-id"
17+
host_url="your-host-url"
18+
width="500"
19+
height="600"
20+
resizable="true"
21+
min_width="300"
22+
min_height="400"
23+
max_width="800"
24+
max_height="900"
25+
></langflow-chat>
26+
```
27+
28+
## Configuration Options
29+
30+
### Required Properties
31+
32+
- `resizable` (boolean): Set to "true" to enable the resize functionality. Default is "false".
33+
34+
### Optional Properties
35+
36+
- `min_width` (number): Minimum width in pixels the chat window can be resized to. Default is 300.
37+
- `min_height` (number): Minimum height in pixels the chat window can be resized to. Default is 400.
38+
- `max_width` (number): Maximum width in pixels the chat window can be resized to. Default is 2000.
39+
- `max_height` (number): Maximum height in pixels the chat window can be resized to. Default is 2000.
40+
41+
## How to Resize
42+
43+
When the resizable feature is enabled, a resize handle appears in the bottom-right corner of the chat window. Users can:
44+
45+
1. Click and hold the resize handle
46+
2. Drag the handle to adjust the size
47+
3. Release to set the new dimensions
48+
49+
The chat window will respect the minimum and maximum size constraints specified in the configuration.
50+
51+
## Example Implementation
52+
53+
Here's a complete example implementation with the resizable feature:
54+
55+
```html
56+
<!DOCTYPE html>
57+
<html lang="en">
58+
<head>
59+
<meta charset="UTF-8">
60+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
61+
<title>Langflow Chat - Resizable Demo</title>
62+
</head>
63+
<body>
64+
<h1>Resizable Chat Widget Demo</h1>
65+
66+
<langflow-chat
67+
window_title="Resizable Chat Widget"
68+
flow_id="your-flow-id"
69+
host_url="your-host-url"
70+
width="500"
71+
height="600"
72+
resizable="true"
73+
min_width="300"
74+
min_height="400"
75+
max_width="800"
76+
max_height="900"
77+
></langflow-chat>
78+
79+
<script src="path/to/langflow-chat.js"></script>
80+
</body>
81+
</html>
82+
```
83+
84+
## Browser Compatibility
85+
86+
The resizable feature is supported in all modern browsers:
87+
88+
- Chrome 60+
89+
- Firefox 55+
90+
- Safari 11+
91+
- Edge 79+
92+
93+
## Troubleshooting
94+
95+
If the resize functionality is not working as expected:
96+
97+
1. Ensure the `resizable` attribute is set to "true"
98+
2. Check that the min/max values are reasonable (min < max)
99+
3. Verify that there are no CSS conflicts affecting the chat window
100+
101+
## Technical Implementation
102+
103+
The resize functionality is implemented using JavaScript event listeners for mouse events. When a resize operation starts:
104+
105+
1. The initial mouse position and chat dimensions are recorded
106+
2. Mouse movement is tracked to calculate new dimensions
107+
3. Size constraints are applied to ensure the window stays within min/max boundaries
108+
4. The window dimensions are updated in real-time
109+
110+
This approach provides a smooth and responsive resize experience.

public/index.html

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,70 @@
33
<head>
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6-
<title>Document</title>
6+
<title>Langflow Chat Demo</title>
7+
<style>
8+
body {
9+
width: 100%;
10+
height: 100vh;
11+
margin: 0;
12+
padding: 20px;
13+
font-family: Arial, sans-serif;
14+
display: flex;
15+
flex-direction: column;
16+
}
17+
.demo-section {
18+
margin-bottom: 30px;
19+
padding: 20px;
20+
border: 1px solid #eaeaea;
21+
border-radius: 8px;
22+
}
23+
h1 {
24+
margin-top: 0;
25+
color: #333;
26+
}
27+
h2 {
28+
color: #555;
29+
}
30+
.demo-description {
31+
margin-bottom: 20px;
32+
color: #666;
33+
}
34+
</style>
735
</head>
8-
<body style="width: 100vh; height: 100vh; ">
9-
<langflow-chat window_title="Langflow Chat"
10-
flow_id="4317421c-d4b5-4a22-bc7b-05e8af053b0e"
11-
host_url="http://localhost:7860"
12-
width="700"
36+
<body>
37+
<h1>Langflow Chat Widget Demo</h1>
38+
39+
<div class="demo-section">
40+
<h2>Standard Chat Widget</h2>
41+
<div class="demo-description">
42+
Default chat widget with fixed dimensions.
43+
</div>
44+
<langflow-chat
45+
window_title="Standard Chat"
46+
flow_id="4317421c-d4b5-4a22-bc7b-05e8af053b0e"
47+
host_url="http://localhost:7860"
48+
width="450"
49+
height="650"
1350
></langflow-chat>
51+
</div>
52+
53+
<div class="demo-section">
54+
<h2>Resizable Chat Widget</h2>
55+
<div class="demo-description">
56+
Resizable chat widget - users can drag the bottom-right corner to resize.
57+
</div>
58+
<langflow-chat
59+
window_title="Resizable Chat"
60+
flow_id="4317421c-d4b5-4a22-bc7b-05e8af053b0e"
61+
host_url="http://localhost:7860"
62+
width="500"
63+
height="600"
64+
resizable="true"
65+
min_width="300"
66+
min_height="400"
67+
max_width="800"
68+
max_height="900"
69+
></langflow-chat>
70+
</div>
1471
</body>
1572
</html>
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import React from 'react';
2+
import { render, fireEvent, screen } from '@testing-library/react';
3+
import '@testing-library/jest-dom';
4+
import ChatWindow from '../chatWindow';
5+
6+
// Mock the extractMessageFromOutput and other imported functions
7+
jest.mock('../utils', () => ({
8+
extractMessageFromOutput: jest.fn((output) => output),
9+
getAnimationOrigin: jest.fn(() => 'origin-bottom-right'),
10+
getChatPosition: jest.fn(() => ({ top: '0px', left: '0px' })),
11+
}));
12+
13+
// Mock the sendMessage function
14+
jest.mock('../../controllers', () => ({
15+
sendMessage: jest.fn().mockImplementation(() => Promise.resolve({ data: {} })),
16+
}));
17+
18+
describe('ChatWindow Component', () => {
19+
const defaultProps = {
20+
api_key: 'test-api-key',
21+
flowId: 'test-flow-id',
22+
hostUrl: 'http://test-host-url',
23+
updateLastMessage: jest.fn(),
24+
messages: [],
25+
output_type: 'chat',
26+
input_type: 'chat',
27+
addMessage: jest.fn(),
28+
open: true,
29+
triggerRef: { current: document.createElement('button') } as React.RefObject<HTMLButtonElement>,
30+
sessionId: { current: 'test-session-id' } as React.MutableRefObject<string>,
31+
};
32+
33+
beforeEach(() => {
34+
// Clear mocks before each test
35+
jest.clearAllMocks();
36+
});
37+
38+
test('renders chat window with default props', () => {
39+
render(<ChatWindow {...defaultProps} />);
40+
41+
// Check if chat window is rendered
42+
const chatWindow = screen.getByText('Chat');
43+
expect(chatWindow).toBeInTheDocument();
44+
});
45+
46+
test('does not render resize handle when resizable is false', () => {
47+
render(<ChatWindow {...defaultProps} />);
48+
49+
// Check that resize handle is not rendered
50+
const resizeHandle = document.querySelector('.cl-resize-handle');
51+
expect(resizeHandle).not.toBeInTheDocument();
52+
});
53+
54+
test('renders resize handle when resizable is true', () => {
55+
render(<ChatWindow {...defaultProps} resizable={true} />);
56+
57+
// Check that resize handle is rendered
58+
const resizeHandle = document.querySelector('.cl-resize-handle');
59+
expect(resizeHandle).toBeInTheDocument();
60+
});
61+
62+
test('chat window has resizable class when resizable is true', () => {
63+
render(<ChatWindow {...defaultProps} resizable={true} />);
64+
65+
// Check that chat window has resizable class
66+
const chatWindow = document.querySelector('.cl-window');
67+
expect(chatWindow).toHaveClass('resizable');
68+
});
69+
70+
test('chat window does not have resizable class when resizable is false', () => {
71+
render(<ChatWindow {...defaultProps} />);
72+
73+
// Check that chat window does not have resizable class
74+
const chatWindow = document.querySelector('.cl-window');
75+
expect(chatWindow).not.toHaveClass('resizable');
76+
});
77+
78+
test('resize handle triggers resize events', () => {
79+
render(<ChatWindow {...defaultProps} resizable={true} width={500} height={600} />);
80+
81+
// Get the resize handle
82+
const resizeHandle = document.querySelector('.cl-resize-handle') as HTMLElement;
83+
expect(resizeHandle).toBeInTheDocument();
84+
85+
// Mock mouse events for resize
86+
fireEvent.mouseDown(resizeHandle, { clientX: 0, clientY: 0 });
87+
88+
// Verify that event listeners are attached (indirectly)
89+
// This is challenging to test directly with jsdom, but we can check if the class is applied
90+
const chatWindow = document.querySelector('.cl-window');
91+
expect(chatWindow).toHaveClass('resizable');
92+
});
93+
94+
test('respects min and max size constraints', () => {
95+
render(
96+
<ChatWindow
97+
{...defaultProps}
98+
resizable={true}
99+
width={500}
100+
height={600}
101+
min_width={300}
102+
min_height={400}
103+
max_width={800}
104+
max_height={900}
105+
/>
106+
);
107+
108+
// Get the chat window element
109+
const chatWindow = document.querySelector('.cl-window') as HTMLElement;
110+
expect(chatWindow).toBeInTheDocument();
111+
112+
// Initial size should match props
113+
expect(chatWindow.style.width).toBe('500px');
114+
expect(chatWindow.style.height).toBe('600px');
115+
});
116+
});

0 commit comments

Comments
 (0)