mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-17 17:41:24 -07:00
Improve tracking of animated components. (#12618)
This commit is contained in:
73
packages/cli/src/ui/hooks/useAnimatedScrollbar.test.tsx
Normal file
73
packages/cli/src/ui/hooks/useAnimatedScrollbar.test.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { act } from 'react';
|
||||
import { render } from '../../test-utils/render.js';
|
||||
import { useAnimatedScrollbar } from './useAnimatedScrollbar.js';
|
||||
import { debugState } from '../debug.js';
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
||||
|
||||
const TestComponent = ({ isFocused = false }: { isFocused?: boolean }) => {
|
||||
useAnimatedScrollbar(isFocused, () => {});
|
||||
return null;
|
||||
};
|
||||
|
||||
describe('useAnimatedScrollbar', () => {
|
||||
beforeEach(() => {
|
||||
debugState.debugNumAnimatedComponents = 0;
|
||||
vi.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('should not increment debugNumAnimatedComponents when not focused', () => {
|
||||
render(<TestComponent isFocused={false} />);
|
||||
expect(debugState.debugNumAnimatedComponents).toBe(0);
|
||||
});
|
||||
|
||||
it('should not increment debugNumAnimatedComponents on initial mount even if focused', () => {
|
||||
render(<TestComponent isFocused={true} />);
|
||||
expect(debugState.debugNumAnimatedComponents).toBe(0);
|
||||
});
|
||||
|
||||
it('should increment debugNumAnimatedComponents when becoming focused', () => {
|
||||
const { rerender } = render(<TestComponent isFocused={false} />);
|
||||
expect(debugState.debugNumAnimatedComponents).toBe(0);
|
||||
rerender(<TestComponent isFocused={true} />);
|
||||
expect(debugState.debugNumAnimatedComponents).toBe(1);
|
||||
});
|
||||
|
||||
it('should decrement debugNumAnimatedComponents when becoming unfocused', () => {
|
||||
const { rerender } = render(<TestComponent isFocused={false} />);
|
||||
rerender(<TestComponent isFocused={true} />);
|
||||
expect(debugState.debugNumAnimatedComponents).toBe(1);
|
||||
rerender(<TestComponent isFocused={false} />);
|
||||
expect(debugState.debugNumAnimatedComponents).toBe(0);
|
||||
});
|
||||
|
||||
it('should decrement debugNumAnimatedComponents on unmount', () => {
|
||||
const { rerender, unmount } = render(<TestComponent isFocused={false} />);
|
||||
rerender(<TestComponent isFocused={true} />);
|
||||
expect(debugState.debugNumAnimatedComponents).toBe(1);
|
||||
unmount();
|
||||
expect(debugState.debugNumAnimatedComponents).toBe(0);
|
||||
});
|
||||
|
||||
it('should decrement debugNumAnimatedComponents after animation finishes', async () => {
|
||||
const { rerender } = render(<TestComponent isFocused={false} />);
|
||||
rerender(<TestComponent isFocused={true} />);
|
||||
expect(debugState.debugNumAnimatedComponents).toBe(1);
|
||||
|
||||
// Advance timers by enough time for animation to complete (200 + 1000 + 300 + buffer)
|
||||
await act(async () => {
|
||||
await vi.advanceTimersByTimeAsync(2000);
|
||||
});
|
||||
|
||||
expect(debugState.debugNumAnimatedComponents).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -7,6 +7,7 @@
|
||||
import { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { interpolateColor } from '../themes/color-utils.js';
|
||||
import { debugState } from '../debug.js';
|
||||
|
||||
export function useAnimatedScrollbar(
|
||||
isFocused: boolean,
|
||||
@@ -18,8 +19,13 @@ export function useAnimatedScrollbar(
|
||||
|
||||
const animationFrame = useRef<NodeJS.Timeout | null>(null);
|
||||
const timeout = useRef<NodeJS.Timeout | null>(null);
|
||||
const isAnimatingRef = useRef(false);
|
||||
|
||||
const cleanup = useCallback(() => {
|
||||
if (isAnimatingRef.current) {
|
||||
debugState.debugNumAnimatedComponents--;
|
||||
isAnimatingRef.current = false;
|
||||
}
|
||||
if (animationFrame.current) {
|
||||
clearInterval(animationFrame.current);
|
||||
animationFrame.current = null;
|
||||
@@ -32,6 +38,8 @@ export function useAnimatedScrollbar(
|
||||
|
||||
const flashScrollbar = useCallback(() => {
|
||||
cleanup();
|
||||
debugState.debugNumAnimatedComponents++;
|
||||
isAnimatingRef.current = true;
|
||||
|
||||
const fadeInDuration = 200;
|
||||
const visibleDuration = 1000;
|
||||
@@ -67,10 +75,7 @@ export function useAnimatedScrollbar(
|
||||
);
|
||||
|
||||
if (progress === 1) {
|
||||
if (animationFrame.current) {
|
||||
clearInterval(animationFrame.current);
|
||||
animationFrame.current = null;
|
||||
}
|
||||
cleanup();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user