mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-03-22 23:30:36 +08:00
fix(editor): chat cannot scroll on windows (#14677)
fix #14529 fix #14612 replace #14614 #14657 #### PR Dependency Tree * **PR #14677** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Tests** * Added test coverage for scroll position tracking and pinned scroll behavior in AI chat * Added test suite verifying scroll-to-end and scroll-to-position functionality * **New Features** * Introduced configurable scrollable option for text rendering in AI chat components, allowing control over scroll behavior <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* @vitest-environment happy-dom
|
||||
*/
|
||||
import { describe, expect, test, vi } from 'vitest';
|
||||
|
||||
import { AIChatContent } from './ai-chat-content';
|
||||
|
||||
describe('AIChatContent pinned scroll tracking', () => {
|
||||
test('records scroll position from the chat messages host', async () => {
|
||||
let scrollEndHandler: (() => void) | undefined;
|
||||
|
||||
const chatMessages = {
|
||||
scrollTop: 256,
|
||||
updateComplete: Promise.resolve(),
|
||||
addEventListener: vi.fn((event: string, handler: EventListener) => {
|
||||
if (event === 'scrollend') {
|
||||
scrollEndHandler = handler as () => void;
|
||||
}
|
||||
}),
|
||||
};
|
||||
|
||||
const content = {
|
||||
chatMessagesRef: { value: chatMessages },
|
||||
_scrollListenersInitialized: false,
|
||||
lastScrollTop: undefined,
|
||||
} as unknown as AIChatContent;
|
||||
|
||||
(AIChatContent.prototype as any)._initializeScrollListeners.call(content);
|
||||
await chatMessages.updateComplete;
|
||||
await Promise.resolve();
|
||||
|
||||
expect(chatMessages.addEventListener).toHaveBeenCalledWith(
|
||||
'scrollend',
|
||||
expect.any(Function)
|
||||
);
|
||||
|
||||
scrollEndHandler?.();
|
||||
|
||||
expect((content as any).lastScrollTop).toBe(256);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* @vitest-environment happy-dom
|
||||
*/
|
||||
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
||||
|
||||
import { AIChatMessages } from './ai-chat-messages';
|
||||
|
||||
describe('AIChatMessages scrolling', () => {
|
||||
beforeEach(() => {
|
||||
vi.stubGlobal('requestAnimationFrame', (cb: FrameRequestCallback) => {
|
||||
cb(0);
|
||||
return 1;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
test('scrollToEnd scrolls the host element', () => {
|
||||
const scrollTo = vi.fn();
|
||||
const element = {
|
||||
scrollTo,
|
||||
} as unknown as AIChatMessages;
|
||||
|
||||
Object.defineProperty(element, 'scrollHeight', {
|
||||
configurable: true,
|
||||
value: 480,
|
||||
});
|
||||
|
||||
AIChatMessages.prototype.scrollToEnd.call(element);
|
||||
|
||||
expect(scrollTo).toHaveBeenCalledWith({
|
||||
top: 480,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
});
|
||||
|
||||
test('scrollToPos scrolls the host element', () => {
|
||||
const scrollTo = vi.fn();
|
||||
const element = {
|
||||
scrollTo,
|
||||
} as unknown as AIChatMessages;
|
||||
|
||||
AIChatMessages.prototype.scrollToPos.call(element, 128);
|
||||
|
||||
expect(scrollTo).toHaveBeenCalledWith({ top: 128 });
|
||||
});
|
||||
});
|
||||
@@ -32,6 +32,7 @@ export class ChatContentRichText extends WithDisposable(ShadowlessElement) {
|
||||
extensions: this.extensions,
|
||||
affineFeatureFlagService: this.affineFeatureFlagService,
|
||||
theme: this.theme,
|
||||
scrollable: false,
|
||||
})(text, this.state)}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +85,7 @@ export type TextRendererOptions = {
|
||||
testId?: string;
|
||||
affineFeatureFlagService?: FeatureFlagService;
|
||||
theme?: Signal<ColorScheme>;
|
||||
scrollable?: boolean;
|
||||
};
|
||||
|
||||
// todo: refactor it for more general purpose usage instead of AI only?
|
||||
@@ -140,9 +141,12 @@ export class TextRenderer extends SignalWatcher(
|
||||
}
|
||||
|
||||
.text-renderer-container {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.text-renderer-container.scrollable {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
padding: 0;
|
||||
overscroll-behavior-y: none;
|
||||
}
|
||||
.text-renderer-container.show-scrollbar::-webkit-scrollbar {
|
||||
@@ -325,6 +329,7 @@ export class TextRenderer extends SignalWatcher(
|
||||
const classes = classMap({
|
||||
'text-renderer-container': true,
|
||||
'custom-heading': !!customHeading,
|
||||
scrollable: this.options.scrollable !== false,
|
||||
});
|
||||
const theme = this.options.theme?.value;
|
||||
return html`
|
||||
|
||||
Reference in New Issue
Block a user