Skip to content

Commit eebddfb

Browse files
authored
feat: scroll to the top of the target message element in Channel component (#847)
Copy code Addresses [AC-670](https://sendbird.atlassian.net/browse/AC-670) Currently, users can set the position of the first displayed message using the `startingPoint` (or `scrollToMessage`) prop via the Channel component. However, the scroll stops at the middle of the target message element, requiring users to scroll up to see long content. [AC-670]: https://sendbird.atlassian.net/browse/AC-670?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
1 parent 37dee78 commit eebddfb

File tree

2 files changed

+52
-7
lines changed

2 files changed

+52
-7
lines changed

src/modules/Channel/context/__test__/utils.spec.js

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// const unique = getUniqueListByMessageId(mergedMessages);
55
// return unique;
66

7-
import { mergeAndSortMessages } from "../utils";
7+
import { mergeAndSortMessages, scrollToRenderedMessage } from "../utils";
88

99
const oldMessages = [
1010
{
@@ -55,3 +55,52 @@ describe('mergeAndSortMessages', () => {
5555
]);
5656
});
5757
});
58+
59+
60+
describe('scrollToRenderedMessage', () => {
61+
const mockSetIsScrolled = jest.fn();
62+
const mockRefCurrent = { offsetHeight: 500, querySelectorAll: jest.fn() };
63+
const mockRef = { current: mockRefCurrent };
64+
const initialTimeStamp = 123456789;
65+
66+
beforeEach(() => {
67+
jest.clearAllMocks();
68+
});
69+
70+
it('should handle the case where the element is not found', () => {
71+
mockRefCurrent.querySelectorAll.mockReturnValue([]);
72+
73+
scrollToRenderedMessage(mockRef, initialTimeStamp, mockSetIsScrolled);
74+
75+
// Ensure that scrollTop is not modified
76+
expect(mockRefCurrent.scrollTop).toBe(undefined);
77+
expect(mockSetIsScrolled).toHaveBeenCalledWith(true);
78+
});
79+
80+
it('should handle errors gracefully', () => {
81+
// Mocking an error in the try block
82+
mockRefCurrent.querySelectorAll.mockImplementation(() => {
83+
throw new Error('Mock error');
84+
});
85+
86+
scrollToRenderedMessage(mockRef, initialTimeStamp, mockSetIsScrolled);
87+
88+
// Ensure that scrollTop is not modified
89+
expect(mockRefCurrent.scrollTop).toBe(undefined);
90+
expect(mockSetIsScrolled).toHaveBeenCalledWith(true);
91+
});
92+
93+
94+
it('should scroll to the top of the element', () => {
95+
// Mocking the element
96+
const mockElement = document.createElement('div');
97+
jest.spyOn(mockElement, 'offsetHeight', 'get').mockReturnValue(100);
98+
jest.spyOn(mockElement, 'offsetTop', 'get').mockReturnValue(200);
99+
mockRefCurrent.querySelectorAll.mockReturnValue([mockElement]);
100+
101+
scrollToRenderedMessage(mockRef, initialTimeStamp, mockSetIsScrolled);
102+
// Ensure that scrollTop is modified
103+
expect(mockRefCurrent.scrollTop).toBe(200);
104+
expect(mockSetIsScrolled).toHaveBeenCalledWith(true);
105+
});
106+
});

src/modules/Channel/context/utils.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,8 @@ export const scrollToRenderedMessage = (
1616
// scroll into the message with initialTimeStamp
1717
const element = container.querySelectorAll(`[data-sb-created-at="${initialTimeStamp}"]`)?.[0];
1818
if (element instanceof HTMLElement) {
19-
// Calculate the offset of the element from the top of the container
20-
const containerHeight = container.offsetHeight;
21-
const elementHeight = element.offsetHeight;
22-
const elementOffset = (containerHeight - elementHeight) / 2;
23-
// Set the scroll position of the container to bring the element to the middle
24-
container.scrollTop = element.offsetTop - elementOffset;
19+
// Set the scroll position of the container to bring the element to the top
20+
container.scrollTop = element.offsetTop;
2521
}
2622
} catch {
2723
// do nothing

0 commit comments

Comments
 (0)