Skip to content

Commit e5929d0

Browse files
committed
Fix AI scribe menu going off screen to the top
If it was opened at the top of the composer, it could go off screen to the top. modalHeight was missing so the calculateModalPosition method could not protect the AI scribe menu to go off screen from the top.
1 parent 235e0d7 commit e5929d0

File tree

2 files changed

+75
-1
lines changed

2 files changed

+75
-1
lines changed

scribe/lib/scribe/ai/presentation/widgets/ai_scribe.dart

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,16 @@ Future<void> showAIScribeDialog({
6767
);
6868

6969
if (buttonPosition != null) {
70+
final categories = availableCategories ?? AIScribeMenuCategory.values;
71+
final modalHeight = hasContent
72+
? (categories.length * AIScribeSizes.menuItemHeight) + AIScribeSizes.fieldSpacing + AIScribeSizes.barHeight
73+
: AIScribeSizes.barHeight;
74+
7075
final position = calculateModalPosition(
7176
context: context,
7277
buttonPosition: buttonPosition,
7378
modalWidth: AIScribeSizes.barWidth,
79+
modalHeight: modalHeight,
7480
);
7581

7682
return PointerInterceptor(
@@ -158,6 +164,15 @@ Future<void> showAIScribeDialog({
158164
);
159165
}
160166

167+
double calculateMenuDialogHeight({
168+
required bool hasContent,
169+
required int categoryCount,
170+
}) {
171+
return hasContent
172+
? (categoryCount * AIScribeSizes.menuItemHeight) + AIScribeSizes.fieldSpacing + AIScribeSizes.barHeight
173+
: AIScribeSizes.barHeight;
174+
}
175+
161176
({double left, double bottom}) calculateModalPosition({
162177
required BuildContext context,
163178
required Offset buttonPosition,

scribe/test/scribe/ai/presentation/widgets/ai_scribe_test.dart

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,66 @@ import 'package:scribe/scribe/ai/presentation/styles/ai_scribe_styles.dart';
44
import 'package:scribe/scribe/ai/presentation/widgets/ai_scribe.dart';
55

66
void main() {
7-
group('calculateModalPosition::', () {
7+
group('calculateMenuDialogHeight::', () {
8+
test('should calculate correct height with content and default 4 categories', () {
9+
// Arrange
10+
const hasContent = true;
11+
const categoryCount = 4;
12+
13+
// Act
14+
final height = calculateMenuDialogHeight(
15+
hasContent: hasContent,
16+
categoryCount: categoryCount,
17+
);
18+
19+
// Assert
20+
// 4 categories × 40px + 8px spacing + 48px bar = 216px
21+
const expectedHeight = (4 * AIScribeSizes.menuItemHeight) +
22+
AIScribeSizes.fieldSpacing +
23+
AIScribeSizes.barHeight;
24+
expect(height, expectedHeight);
25+
expect(height, 216.0);
26+
});
27+
28+
test('should calculate correct height with content and custom categories', () {
29+
// Arrange
30+
const hasContent = true;
31+
const categoryCount = 2;
32+
33+
// Act
34+
final height = calculateMenuDialogHeight(
35+
hasContent: hasContent,
36+
categoryCount: categoryCount,
37+
);
38+
39+
// Assert
40+
// 2 categories × 40px + 8px spacing + 48px bar = 136px
41+
const expectedHeight = (2 * AIScribeSizes.menuItemHeight) +
42+
AIScribeSizes.fieldSpacing +
43+
AIScribeSizes.barHeight;
44+
expect(height, expectedHeight);
45+
expect(height, 136.0);
46+
});
47+
48+
test('should calculate correct height without content (bar only)', () {
49+
// Arrange
50+
const hasContent = false;
51+
const categoryCount = 4; // Should be ignored when hasContent is false
52+
53+
// Act
54+
final height = calculateMenuDialogHeight(
55+
hasContent: hasContent,
56+
categoryCount: categoryCount,
57+
);
58+
59+
// Assert
60+
// Only the bar height
61+
expect(height, AIScribeSizes.barHeight);
62+
expect(height, 48.0);
63+
});
64+
});
65+
66+
group('calculateModalPosition::', () {
867
testWidgets('should adjust left position when modal would go off-screen to the right', (tester) async {
968
// Arrange
1069
const screenSize = Size(800, 600);

0 commit comments

Comments
 (0)