(function SkeletonBox(
- {height, width, className, style, ...props},
+ {height, width, className, style, delay = false, ...props},
ref,
) {
+ const [isVisible, setIsVisible] = useState(!delay)
+
+ useEffect(() => {
+ if (delay) {
+ const timeoutId = setTimeout(
+ () => {
+ setIsVisible(true)
+ },
+ typeof delay === 'number' ? delay : delay === 'short' ? 300 : 1000,
+ )
+
+ return () => clearTimeout(timeoutId)
+ }
+ }, [delay])
+
+ if (!isVisible) {
+ return null
+ }
+
return (
}
diff --git a/packages/react/src/Skeleton/__tests__/SkeletonBox.test.tsx b/packages/react/src/Skeleton/__tests__/SkeletonBox.test.tsx
index 541b26fb973..a60cba6c2fa 100644
--- a/packages/react/src/Skeleton/__tests__/SkeletonBox.test.tsx
+++ b/packages/react/src/Skeleton/__tests__/SkeletonBox.test.tsx
@@ -1,8 +1,9 @@
import {render} from '@testing-library/react'
-import {describe, expect, it} from 'vitest'
+import {beforeEach, afterEach, describe, expect, it, vi} from 'vitest'
import {SkeletonBox} from '../SkeletonBox'
import classes from '../SkeletonBox.module.css'
import {implementsClassName} from '../../utils/testing'
+import {act} from 'react'
describe('SkeletonBox', () => {
implementsClassName(SkeletonBox, classes.SkeletonBox)
@@ -12,4 +13,138 @@ describe('SkeletonBox', () => {
expect(container.firstChild).toHaveStyle('height: 100px')
expect(container.firstChild).toHaveStyle('width: 200px')
})
+
+ describe('delay behavior', () => {
+ beforeEach(() => {
+ vi.useFakeTimers()
+ })
+
+ afterEach(() => {
+ vi.restoreAllMocks()
+ vi.useRealTimers()
+ })
+
+ it('should render immediately when no delay is provided', () => {
+ const {container} = render()
+ expect(container.querySelector('div')).toBeInTheDocument()
+ })
+
+ it('should not render immediately when delay is "short"', () => {
+ const {container} = render()
+ expect(container.querySelector('div')).not.toBeInTheDocument()
+ })
+
+ it('should render after 300ms when delay is "short"', () => {
+ const {container} = render()
+
+ // Not visible initially
+ expect(container.querySelector('div')).not.toBeInTheDocument()
+
+ // Advance timers by less than 300ms
+ act(() => {
+ vi.advanceTimersByTime(250)
+ })
+
+ // Still not visible
+ expect(container.querySelector('div')).not.toBeInTheDocument()
+
+ // Advance timers to complete the short delay (300ms)
+ act(() => {
+ vi.advanceTimersByTime(50)
+ })
+
+ // Now it should be visible
+ expect(container.querySelector('div')).toBeInTheDocument()
+ })
+
+ it('should not render immediately when delay is "long"', () => {
+ const {container} = render()
+ expect(container.querySelector('div')).not.toBeInTheDocument()
+ })
+
+ it('should render after 1000ms when delay is "long"', () => {
+ const {container} = render()
+
+ // Not visible initially
+ expect(container.querySelector('div')).not.toBeInTheDocument()
+
+ // Advance timers by less than 1000ms
+ act(() => {
+ vi.advanceTimersByTime(800)
+ })
+
+ // Still not visible
+ expect(container.querySelector('div')).not.toBeInTheDocument()
+
+ // Advance timers to complete the long delay (1000ms)
+ act(() => {
+ vi.advanceTimersByTime(200)
+ })
+
+ // Now it should be visible
+ expect(container.querySelector('div')).toBeInTheDocument()
+ })
+
+ it('should cleanup timeout on unmount when delay is "short"', () => {
+ const {unmount} = render()
+
+ // Unmount before the delay completes
+ unmount()
+
+ // Advance timers to see if there are any side effects
+ vi.advanceTimersByTime(300)
+
+ // No errors should occur
+ expect(true).toBe(true)
+ })
+
+ it('should cleanup timeout on unmount when delay is "long"', () => {
+ const {unmount} = render()
+
+ // Unmount before the delay completes
+ unmount()
+
+ // Advance timers to see if there are any side effects
+ vi.advanceTimersByTime(1000)
+
+ // No errors should occur
+ expect(true).toBe(true)
+ })
+
+ it('should render after custom delay when delay is a number', () => {
+ const {container} = render()
+
+ // Not visible initially
+ expect(container.querySelector('div')).not.toBeInTheDocument()
+
+ // Advance timers by less than the custom delay (500ms)
+ act(() => {
+ vi.advanceTimersByTime(400)
+ })
+
+ // Still not visible
+ expect(container.querySelector('div')).not.toBeInTheDocument()
+
+ // Advance timers to complete the custom delay
+ act(() => {
+ vi.advanceTimersByTime(100)
+ })
+
+ // Now it should be visible
+ expect(container.querySelector('div')).toBeInTheDocument()
+ })
+
+ it('should cleanup timeout on unmount when delay is a number', () => {
+ const {unmount} = render()
+
+ // Unmount before the delay completes
+ unmount()
+
+ // Advance timers to see if there are any side effects
+ vi.advanceTimersByTime(500)
+
+ // No errors should occur
+ expect(true).toBe(true)
+ })
+ })
})