mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 08:50:15 +01:00
✅ fix: Emojis rendering in SplitText Animation (#7460)
This commit is contained in:
parent
af96666ff4
commit
e86842fd19
2 changed files with 55 additions and 6 deletions
38
client/src/components/ui/SplitText.spec.tsx
Normal file
38
client/src/components/ui/SplitText.spec.tsx
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { render } from '@testing-library/react';
|
||||||
|
import SplitText from './SplitText';
|
||||||
|
|
||||||
|
// Mock IntersectionObserver
|
||||||
|
class MockIntersectionObserver {
|
||||||
|
observe = jest.fn();
|
||||||
|
unobserve = jest.fn();
|
||||||
|
disconnect = jest.fn();
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(window, 'IntersectionObserver', {
|
||||||
|
writable: true,
|
||||||
|
configurable: true,
|
||||||
|
value: MockIntersectionObserver,
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('SplitText', () => {
|
||||||
|
it('renders emojis correctly', () => {
|
||||||
|
const emojis = ['🚧', '❤️🔥', '💜', '🦎', '❌', '✅', '⚠️'];
|
||||||
|
const originalText = emojis.join('');
|
||||||
|
|
||||||
|
const { container } = render(<SplitText text={originalText} />);
|
||||||
|
const textSpans = container.querySelectorAll('p > span > span.inline-block');
|
||||||
|
|
||||||
|
// Reconstruct the text by joining all span contents
|
||||||
|
const reconstructedText = Array.from(textSpans)
|
||||||
|
.map((span) => span.textContent)
|
||||||
|
.join('')
|
||||||
|
.trim();
|
||||||
|
// Compare the reconstructed text with the original
|
||||||
|
expect(reconstructedText).toBe(originalText);
|
||||||
|
|
||||||
|
// Check the first character specifically as the reconstructed text could hide issues
|
||||||
|
for (let i = 0; i < emojis.length; i++) {
|
||||||
|
expect(Array.from(textSpans)[i].textContent).toBe(emojis[i]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -15,6 +15,17 @@ interface SplitTextProps {
|
||||||
onLineCountChange?: (lineCount: number) => void;
|
onLineCountChange?: (lineCount: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const splitGraphemes = (text: string): string[] => {
|
||||||
|
if (typeof Intl !== 'undefined' && Intl.Segmenter) {
|
||||||
|
const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
|
||||||
|
const segments = segmenter.segment(text);
|
||||||
|
return Array.from(segments).map((s) => s.segment);
|
||||||
|
} else {
|
||||||
|
// Fallback for browsers without Intl.Segmenter
|
||||||
|
return [...text];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const SplitText: React.FC<SplitTextProps> = ({
|
const SplitText: React.FC<SplitTextProps> = ({
|
||||||
text = '',
|
text = '',
|
||||||
className = '',
|
className = '',
|
||||||
|
|
@ -28,7 +39,7 @@ const SplitText: React.FC<SplitTextProps> = ({
|
||||||
onLetterAnimationComplete,
|
onLetterAnimationComplete,
|
||||||
onLineCountChange,
|
onLineCountChange,
|
||||||
}) => {
|
}) => {
|
||||||
const words = text.split(' ').map((word) => word.split(''));
|
const words = text.split(' ').map(splitGraphemes);
|
||||||
const letters = words.flat();
|
const letters = words.flat();
|
||||||
const [inView, setInView] = useState(false);
|
const [inView, setInView] = useState(false);
|
||||||
const ref = useRef<HTMLParagraphElement>(null);
|
const ref = useRef<HTMLParagraphElement>(null);
|
||||||
|
|
@ -40,12 +51,12 @@ const SplitText: React.FC<SplitTextProps> = ({
|
||||||
from: animationFrom,
|
from: animationFrom,
|
||||||
to: inView
|
to: inView
|
||||||
? async (next: (props: any) => Promise<void>) => {
|
? async (next: (props: any) => Promise<void>) => {
|
||||||
await next(animationTo);
|
await next(animationTo);
|
||||||
animatedCount.current += 1;
|
animatedCount.current += 1;
|
||||||
if (animatedCount.current === letters.length && onLetterAnimationComplete) {
|
if (animatedCount.current === letters.length && onLetterAnimationComplete) {
|
||||||
onLetterAnimationComplete();
|
onLetterAnimationComplete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
: animationFrom,
|
: animationFrom,
|
||||||
delay: i * delay,
|
delay: i * delay,
|
||||||
config: { easing },
|
config: { easing },
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue