MM-63648 - markdown images sometimes do not show the more button (#30716)
* MM-63648 - markdown images sometimes do not show the more button * migrate test to testing-library and remove unnecesary props --------- Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
@@ -1,231 +1,19 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`components/post_view/ShowMore should match snapshot 1`] = `
|
||||
<div
|
||||
className="post-message post-message--collapsed"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
className="post-message__text-container"
|
||||
style={
|
||||
Object {
|
||||
"maxHeight": 200,
|
||||
}
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<p>
|
||||
text
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/post_view/ShowMore should match snapshot, PostAttachment on collapsed view 1`] = `
|
||||
<div
|
||||
className="post-message post-message--collapsed post-message--overflow"
|
||||
>
|
||||
<div
|
||||
className="post-message__text-container"
|
||||
style={
|
||||
Object {
|
||||
"maxHeight": 200,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<div
|
||||
className="post-collapse"
|
||||
class="post-message post-message--collapsed"
|
||||
>
|
||||
<div
|
||||
className="post-attachment-collapse__show-more"
|
||||
class="post-message__text-container"
|
||||
style="max-height: 200px;"
|
||||
>
|
||||
<div
|
||||
className="post-collapse__show-more-line"
|
||||
/>
|
||||
<button
|
||||
className="post-collapse__show-more-button"
|
||||
id="showMoreButton"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<span
|
||||
className="fa fa-angle-down"
|
||||
/>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Show more"
|
||||
id="post_info.message.show_more"
|
||||
/>
|
||||
</button>
|
||||
<div
|
||||
className="post-collapse__show-more-line"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/post_view/ShowMore should match snapshot, PostAttachment on expanded view 1`] = `
|
||||
<div
|
||||
className="post-message post-message--expanded post-message--overflow"
|
||||
>
|
||||
<div
|
||||
className="post-message__text-container"
|
||||
style={
|
||||
Object {
|
||||
"maxHeight": undefined,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<div
|
||||
className="post-collapse"
|
||||
>
|
||||
<div
|
||||
className="post-attachment-collapse__show-more"
|
||||
>
|
||||
<div
|
||||
className="post-collapse__show-more-line"
|
||||
/>
|
||||
<button
|
||||
className="post-collapse__show-more-button"
|
||||
id="showMoreButton"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<span
|
||||
className="fa fa-angle-up"
|
||||
/>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Show less"
|
||||
id="post_info.message.show_less"
|
||||
/>
|
||||
</button>
|
||||
<div
|
||||
className="post-collapse__show-more-line"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/post_view/ShowMore should match snapshot, PostMessageView on collapsed view 1`] = `
|
||||
<div
|
||||
className="post-message post-message--collapsed post-message--overflow"
|
||||
>
|
||||
<div
|
||||
className="post-message__text-container"
|
||||
style={
|
||||
Object {
|
||||
"maxHeight": 200,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<div
|
||||
className="post-collapse"
|
||||
>
|
||||
<div
|
||||
className="post-collapse__show-more"
|
||||
>
|
||||
<div
|
||||
className="post-collapse__show-more-line"
|
||||
/>
|
||||
<button
|
||||
className="post-collapse__show-more-button"
|
||||
id="showMoreButton"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<span
|
||||
className="fa fa-angle-down"
|
||||
/>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Show more"
|
||||
id="post_info.message.show_more"
|
||||
/>
|
||||
</button>
|
||||
<div
|
||||
className="post-collapse__show-more-line"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/post_view/ShowMore should match snapshot, PostMessageView on expanded view 1`] = `
|
||||
<div
|
||||
className="post-message post-message--expanded post-message--overflow"
|
||||
>
|
||||
<div
|
||||
className="post-message__text-container"
|
||||
style={
|
||||
Object {
|
||||
"maxHeight": undefined,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<div
|
||||
className="post-collapse"
|
||||
>
|
||||
<div
|
||||
className="post-collapse__show-more"
|
||||
>
|
||||
<div
|
||||
className="post-collapse__show-more-line"
|
||||
/>
|
||||
<button
|
||||
className="post-collapse__show-more-button"
|
||||
id="showMoreButton"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<span
|
||||
className="fa fa-angle-up"
|
||||
/>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Show less"
|
||||
id="post_info.message.show_less"
|
||||
/>
|
||||
</button>
|
||||
<div
|
||||
className="post-collapse__show-more-line"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`components/post_view/ShowMore should match snapshot, PostMessageView on expanded view with compactDisplay 1`] = `
|
||||
<div
|
||||
className="post-message post-message--expanded post-message--overflow"
|
||||
>
|
||||
<div
|
||||
className="post-message__text-container"
|
||||
style={
|
||||
Object {
|
||||
"maxHeight": undefined,
|
||||
}
|
||||
}
|
||||
/>
|
||||
<div
|
||||
className="post-collapse"
|
||||
>
|
||||
<div
|
||||
className="post-collapse__show-more"
|
||||
>
|
||||
<div
|
||||
className="post-collapse__show-more-line"
|
||||
/>
|
||||
<button
|
||||
className="post-collapse__show-more-button"
|
||||
id="showMoreButton"
|
||||
onClick={[Function]}
|
||||
>
|
||||
<span
|
||||
className="fa fa-angle-up"
|
||||
/>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Show less"
|
||||
id="post_info.message.show_less"
|
||||
/>
|
||||
</button>
|
||||
<div
|
||||
className="post-collapse__show-more-line"
|
||||
/>
|
||||
<div>
|
||||
<p>
|
||||
text
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,102 +1,397 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import ShowMore from 'components/post_view/show_more/show_more';
|
||||
|
||||
import {renderWithContext, screen, fireEvent, act} from 'tests/react_testing_utils';
|
||||
|
||||
describe('components/post_view/ShowMore', () => {
|
||||
const children = (<div><p>{'text'}</p></div>);
|
||||
const baseProps = {
|
||||
checkOverflow: 0,
|
||||
isAttachmentText: false,
|
||||
isRHSExpanded: false,
|
||||
isRHSOpen: false,
|
||||
maxHeight: 200,
|
||||
text: 'text',
|
||||
compactDisplay: false,
|
||||
};
|
||||
|
||||
// Helper function to mock the text container's scrollHeight
|
||||
const mockTextContainerScrollHeight = (scrollHeight: number) => {
|
||||
// Mock the scrollHeight property
|
||||
Object.defineProperty(HTMLElement.prototype, 'scrollHeight', {
|
||||
configurable: true,
|
||||
value: scrollHeight,
|
||||
});
|
||||
};
|
||||
|
||||
// Helper function to restore the original scrollHeight behavior
|
||||
const restoreTextContainerScrollHeight = () => {
|
||||
// Delete the mocked scrollHeight property
|
||||
delete (HTMLElement.prototype as any).scrollHeight;
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
restoreTextContainerScrollHeight();
|
||||
});
|
||||
|
||||
test('should match snapshot', () => {
|
||||
const wrapper = shallow(<ShowMore {...baseProps}>{children}</ShowMore>);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
const {container} = renderWithContext(<ShowMore {...baseProps}>{children}</ShowMore>);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot, PostMessageView on collapsed view', () => {
|
||||
const wrapper = shallow(<ShowMore {...baseProps}/>);
|
||||
wrapper.setState({isOverflow: true, isCollapsed: true});
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
test('should render collapsed view when content overflows', () => {
|
||||
// Setup fake timers
|
||||
jest.useFakeTimers();
|
||||
try {
|
||||
// Mock scrollHeight to be greater than maxHeight to simulate overflow
|
||||
mockTextContainerScrollHeight(baseProps.maxHeight + 50);
|
||||
|
||||
const {container} = renderWithContext(
|
||||
<ShowMore {...baseProps}>
|
||||
<div style={{height: '300px'}}>{'Tall content that will overflow'}</div>
|
||||
</ShowMore>,
|
||||
);
|
||||
|
||||
// Manually trigger the overflow check
|
||||
act(() => {
|
||||
// Run the requestAnimationFrame callback
|
||||
jest.runOnlyPendingTimers();
|
||||
});
|
||||
|
||||
// Verify the "Show more" button is rendered
|
||||
const showMoreButton = screen.getByText('Show more');
|
||||
expect(showMoreButton).toBeInTheDocument();
|
||||
|
||||
// Verify the collapsed class is applied
|
||||
expect(container.querySelector('.post-message--collapsed')).toBeInTheDocument();
|
||||
} finally {
|
||||
jest.useRealTimers();
|
||||
}
|
||||
});
|
||||
|
||||
test('should match snapshot, PostMessageView on expanded view', () => {
|
||||
const wrapper = shallow(<ShowMore {...baseProps}/>);
|
||||
wrapper.setState({isOverflow: true, isCollapsed: false});
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
test('should render expanded view when show more button is clicked', () => {
|
||||
// Setup fake timers
|
||||
jest.useFakeTimers();
|
||||
try {
|
||||
// Mock scrollHeight to be greater than maxHeight to simulate overflow
|
||||
mockTextContainerScrollHeight(baseProps.maxHeight + 50);
|
||||
|
||||
const {container} = renderWithContext(
|
||||
<ShowMore {...baseProps}>
|
||||
<div style={{height: '300px'}}>{'Tall content that will overflow'}</div>
|
||||
</ShowMore>,
|
||||
);
|
||||
|
||||
// Manually trigger the overflow check
|
||||
act(() => {
|
||||
// Run the requestAnimationFrame callback
|
||||
jest.runOnlyPendingTimers();
|
||||
});
|
||||
|
||||
// Find and click the "Show more" button
|
||||
const showMoreButton = screen.getByText('Show more');
|
||||
fireEvent.click(showMoreButton);
|
||||
|
||||
// Verify the "Show less" button is now rendered
|
||||
const showLessButton = screen.getByText('Show less');
|
||||
expect(showLessButton).toBeInTheDocument();
|
||||
|
||||
// Verify the expanded class is applied
|
||||
expect(container.querySelector('.post-message--expanded')).toBeInTheDocument();
|
||||
} finally {
|
||||
jest.useRealTimers();
|
||||
}
|
||||
});
|
||||
|
||||
test('should match snapshot, PostAttachment on collapsed view', () => {
|
||||
const wrapper = shallow(
|
||||
<ShowMore
|
||||
{...baseProps}
|
||||
isAttachmentText={true}
|
||||
/>,
|
||||
);
|
||||
wrapper.setState({isOverflow: true, isCollapsed: true});
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
test('should render attachment text in collapsed view', () => {
|
||||
// Setup fake timers
|
||||
jest.useFakeTimers();
|
||||
try {
|
||||
// Mock scrollHeight to be greater than maxHeight to simulate overflow
|
||||
mockTextContainerScrollHeight(baseProps.maxHeight + 50);
|
||||
|
||||
const {container} = renderWithContext(
|
||||
<ShowMore
|
||||
{...baseProps}
|
||||
isAttachmentText={true}
|
||||
>
|
||||
<div style={{height: '300px'}}>{'Attachment text that will overflow'}</div>
|
||||
</ShowMore>,
|
||||
);
|
||||
|
||||
// Manually trigger the overflow check
|
||||
act(() => {
|
||||
// Run the requestAnimationFrame callback
|
||||
jest.runOnlyPendingTimers();
|
||||
});
|
||||
|
||||
// Verify the "Show more" button is rendered
|
||||
const showMoreButton = screen.getByText('Show more');
|
||||
expect(showMoreButton).toBeInTheDocument();
|
||||
|
||||
// Verify the attachment-specific class is applied
|
||||
expect(container.querySelector('.post-attachment-collapse__show-more')).toBeInTheDocument();
|
||||
} finally {
|
||||
jest.useRealTimers();
|
||||
}
|
||||
});
|
||||
|
||||
test('should match snapshot, PostAttachment on expanded view', () => {
|
||||
const wrapper = shallow(
|
||||
<ShowMore
|
||||
{...baseProps}
|
||||
isAttachmentText={true}
|
||||
/>,
|
||||
);
|
||||
wrapper.setState({isOverflow: true, isCollapsed: false});
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
test('should render with compactDisplay', () => {
|
||||
// Setup fake timers
|
||||
jest.useFakeTimers();
|
||||
try {
|
||||
// Mock scrollHeight to be greater than maxHeight to simulate overflow
|
||||
mockTextContainerScrollHeight(baseProps.maxHeight + 50);
|
||||
|
||||
const {container} = renderWithContext(
|
||||
<ShowMore
|
||||
{...baseProps}
|
||||
compactDisplay={true}
|
||||
>
|
||||
<div style={{height: '300px'}}>{'Content with compact display'}</div>
|
||||
</ShowMore>,
|
||||
);
|
||||
|
||||
// Manually trigger the overflow check
|
||||
act(() => {
|
||||
// Run the requestAnimationFrame callback
|
||||
jest.runOnlyPendingTimers();
|
||||
});
|
||||
|
||||
// Expand the content
|
||||
const showMoreButton = screen.getByText('Show more');
|
||||
fireEvent.click(showMoreButton);
|
||||
|
||||
// Verify the component renders correctly with compact display
|
||||
expect(container.querySelector('.post-message--expanded')).toBeInTheDocument();
|
||||
} finally {
|
||||
jest.useRealTimers();
|
||||
}
|
||||
});
|
||||
|
||||
test('should match snapshot, PostMessageView on expanded view with compactDisplay', () => {
|
||||
const wrapper = shallow(
|
||||
<ShowMore
|
||||
{...baseProps}
|
||||
compactDisplay={true}
|
||||
/>,
|
||||
);
|
||||
wrapper.setState({isOverflow: true, isCollapsed: false});
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
test('should check overflow only when text or checkOverflow props change', () => {
|
||||
// Create a spy for requestAnimationFrame
|
||||
const originalRAF = window.requestAnimationFrame;
|
||||
const rafSpy = jest.fn((cb) => {
|
||||
cb(0);
|
||||
return 0;
|
||||
});
|
||||
window.requestAnimationFrame = rafSpy;
|
||||
|
||||
try {
|
||||
// Initial render with no overflow
|
||||
mockTextContainerScrollHeight(baseProps.maxHeight - 50);
|
||||
const {rerender} = renderWithContext(<ShowMore {...baseProps}>{children}</ShowMore>);
|
||||
|
||||
// Reset the RAF spy count
|
||||
rafSpy.mockClear();
|
||||
|
||||
// Change props that SHOULD trigger overflow check
|
||||
rafSpy.mockClear();
|
||||
rerender(
|
||||
<ShowMore
|
||||
{...baseProps}
|
||||
text={'text change'}
|
||||
>{children}</ShowMore>,
|
||||
);
|
||||
expect(rafSpy).toHaveBeenCalled();
|
||||
|
||||
rafSpy.mockClear();
|
||||
rerender(
|
||||
<ShowMore
|
||||
{...baseProps}
|
||||
text={'text another change'}
|
||||
>{children}</ShowMore>,
|
||||
);
|
||||
expect(rafSpy).toHaveBeenCalled();
|
||||
|
||||
rafSpy.mockClear();
|
||||
rerender(
|
||||
<ShowMore
|
||||
{...baseProps}
|
||||
checkOverflow={1}
|
||||
>{children}</ShowMore>,
|
||||
);
|
||||
expect(rafSpy).toHaveBeenCalled();
|
||||
|
||||
// Same checkOverflow value should not trigger another check
|
||||
rafSpy.mockClear();
|
||||
rerender(
|
||||
<ShowMore
|
||||
{...baseProps}
|
||||
checkOverflow={1}
|
||||
>{children}</ShowMore>,
|
||||
);
|
||||
expect(rafSpy).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
// Restore original requestAnimationFrame
|
||||
window.requestAnimationFrame = originalRAF;
|
||||
}
|
||||
});
|
||||
|
||||
test('should call checkTextOverflow', () => {
|
||||
const wrapper = shallow(<ShowMore {...baseProps}/>);
|
||||
const instance = wrapper.instance() as ShowMore;
|
||||
instance.checkTextOverflow = jest.fn();
|
||||
describe('ResizeObserver functionality', () => {
|
||||
let originalResizeObserver: any;
|
||||
|
||||
expect(instance.checkTextOverflow).not.toBeCalled();
|
||||
beforeEach(() => {
|
||||
// Store original implementation
|
||||
originalResizeObserver = window.ResizeObserver;
|
||||
|
||||
wrapper.setProps({isRHSExpanded: true});
|
||||
expect(instance.checkTextOverflow).toBeCalledTimes(1);
|
||||
// Setup fake timers for requestAnimationFrame
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
wrapper.setProps({isRHSExpanded: false});
|
||||
expect(instance.checkTextOverflow).toBeCalledTimes(2);
|
||||
afterEach(() => {
|
||||
// Restore original implementation
|
||||
window.ResizeObserver = originalResizeObserver;
|
||||
|
||||
wrapper.setProps({isRHSOpen: true});
|
||||
expect(instance.checkTextOverflow).toBeCalledTimes(3);
|
||||
// Restore real timers
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
wrapper.setProps({isRHSOpen: false});
|
||||
expect(instance.checkTextOverflow).toBeCalledTimes(4);
|
||||
test('should set up ResizeObserver on mount', () => {
|
||||
// Track observer creation
|
||||
const observeMock = jest.fn();
|
||||
const disconnectMock = jest.fn();
|
||||
|
||||
wrapper.setProps({text: 'text change'});
|
||||
expect(instance.checkTextOverflow).toBeCalledTimes(5);
|
||||
// Mock ResizeObserver
|
||||
window.ResizeObserver = jest.fn().mockImplementation(() => ({
|
||||
observe: observeMock,
|
||||
disconnect: disconnectMock,
|
||||
})) as unknown as typeof ResizeObserver;
|
||||
|
||||
wrapper.setProps({text: 'text another change'});
|
||||
expect(instance.checkTextOverflow).toBeCalledTimes(6);
|
||||
// Render component
|
||||
renderWithContext(<ShowMore {...baseProps}>{children}</ShowMore>);
|
||||
|
||||
wrapper.setProps({checkOverflow: 1});
|
||||
expect(instance.checkTextOverflow).toBeCalledTimes(7);
|
||||
// Verify ResizeObserver was created
|
||||
expect(window.ResizeObserver).toHaveBeenCalled();
|
||||
|
||||
wrapper.setProps({checkOverflow: 1});
|
||||
expect(instance.checkTextOverflow).toBeCalledTimes(7);
|
||||
// Verify observe was called (meaning the text container is being observed)
|
||||
expect(observeMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should check overflow when ResizeObserver detects size changes', () => {
|
||||
// Create a mock ResizeObserver that can be triggered manually
|
||||
let resizeCallback: ResizeObserverCallback | undefined;
|
||||
window.ResizeObserver = jest.fn().mockImplementation((callback) => {
|
||||
resizeCallback = callback;
|
||||
return {
|
||||
observe: jest.fn(),
|
||||
disconnect: jest.fn(),
|
||||
};
|
||||
}) as unknown as typeof ResizeObserver;
|
||||
|
||||
// Mock requestAnimationFrame to execute callback immediately
|
||||
const originalRAF = window.requestAnimationFrame;
|
||||
window.requestAnimationFrame = (cb) => {
|
||||
cb(0);
|
||||
return 0;
|
||||
};
|
||||
|
||||
try {
|
||||
// Mock scrollHeight to be greater than maxHeight after resize
|
||||
mockTextContainerScrollHeight(baseProps.maxHeight - 50);
|
||||
|
||||
// Render component
|
||||
const {container} = renderWithContext(<ShowMore {...baseProps}>{children}</ShowMore>);
|
||||
|
||||
// Verify no overflow initially
|
||||
expect(container.querySelector('.post-message--overflow')).not.toBeInTheDocument();
|
||||
|
||||
// Now simulate a resize that causes overflow
|
||||
mockTextContainerScrollHeight(baseProps.maxHeight + 50);
|
||||
|
||||
// Trigger the ResizeObserver callback
|
||||
if (resizeCallback && container.querySelector('.post-message__text-container')) {
|
||||
const mockEntry = [{
|
||||
target: container.querySelector('.post-message__text-container') as Element,
|
||||
contentRect: {} as DOMRectReadOnly,
|
||||
borderBoxSize: [] as ResizeObserverSize[],
|
||||
contentBoxSize: [] as ResizeObserverSize[],
|
||||
devicePixelContentBoxSize: [] as ResizeObserverSize[],
|
||||
}];
|
||||
|
||||
act(() => {
|
||||
resizeCallback!(mockEntry, {} as ResizeObserver);
|
||||
});
|
||||
}
|
||||
|
||||
// Verify overflow is detected
|
||||
expect(container.querySelector('.post-message--overflow')).toBeInTheDocument();
|
||||
} finally {
|
||||
// Restore original requestAnimationFrame
|
||||
window.requestAnimationFrame = originalRAF;
|
||||
}
|
||||
});
|
||||
|
||||
test('should clean up ResizeObserver on unmount', () => {
|
||||
// Create mock with disconnect spy
|
||||
const disconnectSpy = jest.fn();
|
||||
window.ResizeObserver = jest.fn(() => ({
|
||||
observe: jest.fn(),
|
||||
disconnect: disconnectSpy,
|
||||
})) as unknown as typeof ResizeObserver;
|
||||
|
||||
// Render and unmount component
|
||||
const {unmount} = renderWithContext(<ShowMore {...baseProps}>{children}</ShowMore>);
|
||||
unmount();
|
||||
|
||||
// Verify disconnect was called
|
||||
expect(disconnectSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should handle browsers without ResizeObserver support', () => {
|
||||
// Remove ResizeObserver
|
||||
delete (window as any).ResizeObserver;
|
||||
|
||||
// Render component should not throw error
|
||||
expect(() => {
|
||||
renderWithContext(<ShowMore {...baseProps}>{children}</ShowMore>);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
test('should update isOverflow state when content height changes', () => {
|
||||
// Mock ResizeObserver before rendering
|
||||
window.ResizeObserver = jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
observe: jest.fn(),
|
||||
disconnect: jest.fn(),
|
||||
};
|
||||
}) as unknown as typeof ResizeObserver;
|
||||
|
||||
// Mock requestAnimationFrame to execute callback immediately
|
||||
const originalRAF = window.requestAnimationFrame;
|
||||
window.requestAnimationFrame = (cb) => {
|
||||
cb(0);
|
||||
return 0;
|
||||
};
|
||||
|
||||
try {
|
||||
// Initial render with no overflow
|
||||
mockTextContainerScrollHeight(baseProps.maxHeight - 50);
|
||||
const {container, rerender} = renderWithContext(<ShowMore {...baseProps}>{children}</ShowMore>);
|
||||
|
||||
// Verify no overflow initially
|
||||
expect(container.querySelector('.post-message--overflow')).not.toBeInTheDocument();
|
||||
|
||||
// Now simulate content that overflows
|
||||
mockTextContainerScrollHeight(baseProps.maxHeight + 50);
|
||||
|
||||
// Force a re-render to trigger checkTextOverflow
|
||||
rerender(
|
||||
<ShowMore
|
||||
{...baseProps}
|
||||
checkOverflow={1}
|
||||
>{children}</ShowMore>,
|
||||
);
|
||||
|
||||
// Verify overflow is detected
|
||||
expect(container.querySelector('.post-message--overflow')).toBeInTheDocument();
|
||||
} finally {
|
||||
// Restore original requestAnimationFrame
|
||||
window.requestAnimationFrame = originalRAF;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,8 +13,6 @@ type Props = {
|
||||
children?: React.ReactNode;
|
||||
checkOverflow?: number;
|
||||
isAttachmentText?: boolean;
|
||||
isRHSExpanded: boolean;
|
||||
isRHSOpen: boolean;
|
||||
text?: string;
|
||||
compactDisplay: boolean;
|
||||
overflowType?: AttachmentTextOverflowType;
|
||||
@@ -30,6 +28,7 @@ export default class ShowMore extends React.PureComponent<Props, State> {
|
||||
private maxHeight: number;
|
||||
private textContainer: React.RefObject<HTMLDivElement>;
|
||||
private overflowRef?: number;
|
||||
private resizeObserver: ResizeObserver | null = null;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
@@ -42,16 +41,17 @@ export default class ShowMore extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.checkTextOverflow();
|
||||
this.setupResizeObserver();
|
||||
|
||||
window.addEventListener('resize', this.handleResize);
|
||||
// Initial check for overflow
|
||||
this.checkTextOverflow();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
// Only manually check for overflow when text content changes or when explicitly requested
|
||||
// ResizeObserver will handle size changes caused by other factors
|
||||
if (
|
||||
this.props.text !== prevProps.text ||
|
||||
this.props.isRHSExpanded !== prevProps.isRHSExpanded ||
|
||||
this.props.isRHSOpen !== prevProps.isRHSOpen ||
|
||||
this.props.checkOverflow !== prevProps.checkOverflow
|
||||
) {
|
||||
this.checkTextOverflow();
|
||||
@@ -59,12 +59,39 @@ export default class ShowMore extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('resize', this.handleResize);
|
||||
if (this.overflowRef) {
|
||||
window.cancelAnimationFrame(this.overflowRef);
|
||||
}
|
||||
this.cleanupResizeObserver();
|
||||
}
|
||||
|
||||
setupResizeObserver = () => {
|
||||
if (!this.textContainer.current || !window.ResizeObserver) {
|
||||
// ResizeObserver is not supported in this browser or the container is not available yet
|
||||
return;
|
||||
}
|
||||
|
||||
// Clean up any existing observer before creating a new one
|
||||
// This prevents multiple observers in case setupResizeObserver is called more than once
|
||||
this.cleanupResizeObserver();
|
||||
|
||||
// Create a new ResizeObserver to watch for size changes in the text container
|
||||
this.resizeObserver = new ResizeObserver(() => {
|
||||
// When the size of the text container changes, check if we need to show/hide the "Show More" button
|
||||
this.checkTextOverflow();
|
||||
});
|
||||
|
||||
// Start observing the text container
|
||||
this.resizeObserver.observe(this.textContainer.current);
|
||||
};
|
||||
|
||||
cleanupResizeObserver = () => {
|
||||
if (this.resizeObserver) {
|
||||
this.resizeObserver.disconnect();
|
||||
this.resizeObserver = null;
|
||||
}
|
||||
};
|
||||
|
||||
toggleCollapse = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
@@ -94,10 +121,6 @@ export default class ShowMore extends React.PureComponent<Props, State> {
|
||||
});
|
||||
};
|
||||
|
||||
handleResize = () => {
|
||||
this.checkTextOverflow();
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
isCollapsed,
|
||||
|
||||
Ссылка в новой задаче
Block a user