@shohojdhara/atomix 0.4.1 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/atomix.css +9351 -9259
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +4 -4
- package/dist/atomix.min.css.map +1 -1
- package/dist/charts.d.ts +12 -19
- package/dist/charts.js +555 -358
- package/dist/charts.js.map +1 -1
- package/dist/core.d.ts +21 -24
- package/dist/core.js +435 -265
- package/dist/core.js.map +1 -1
- package/dist/forms.d.ts +11 -18
- package/dist/forms.js +411 -257
- package/dist/forms.js.map +1 -1
- package/dist/heavy.d.ts +14 -21
- package/dist/heavy.js +409 -254
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +38 -41
- package/dist/index.esm.js +731 -487
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +733 -492
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/package.json +1 -1
- package/scripts/atomix-cli.js +34 -1
- package/src/components/AtomixGlass/AtomixGlass.tsx +82 -54
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +17 -18
- package/src/components/AtomixGlass/README.md +5 -5
- package/src/components/AtomixGlass/stories/Customization.stories.tsx +2 -2
- package/src/components/AtomixGlass/stories/Examples.stories.tsx +42 -42
- package/src/components/AtomixGlass/stories/Modes.stories.tsx +5 -5
- package/src/components/AtomixGlass/stories/Overview.stories.tsx +3 -3
- package/src/components/AtomixGlass/stories/Performance.stories.tsx +2 -2
- package/src/components/AtomixGlass/stories/Playground.stories.tsx +45 -45
- package/src/components/AtomixGlass/stories/Shaders.stories.tsx +3 -3
- package/src/components/Badge/Badge.stories.tsx +1 -1
- package/src/components/Badge/Badge.tsx +1 -1
- package/src/components/Breadcrumb/Breadcrumb.tsx +90 -77
- package/src/components/Breadcrumb/index.ts +2 -2
- package/src/components/Button/Button.stories.tsx +1 -1
- package/src/components/Button/Button.tsx +2 -1
- package/src/components/Button/README.md +2 -2
- package/src/components/Callout/Callout.test.tsx +3 -3
- package/src/components/Callout/Callout.tsx +2 -2
- package/src/components/Callout/README.md +2 -2
- package/src/components/Card/Card.tsx +31 -11
- package/src/components/Chart/Chart.stories.tsx +1 -1
- package/src/components/Chart/Chart.tsx +5 -5
- package/src/components/Chart/TreemapChart.tsx +37 -29
- package/src/components/DatePicker/readme.md +3 -3
- package/src/components/Dropdown/Dropdown.stories.tsx +1 -1
- package/src/components/Dropdown/Dropdown.tsx +276 -273
- package/src/components/EdgePanel/EdgePanel.stories.tsx +7 -7
- package/src/components/Footer/FooterLink.tsx +2 -2
- package/src/components/Form/Checkbox.stories.tsx +1 -1
- package/src/components/Form/Checkbox.tsx +1 -1
- package/src/components/Form/Input.stories.tsx +1 -1
- package/src/components/Form/Input.tsx +1 -1
- package/src/components/Form/Radio.stories.tsx +1 -1
- package/src/components/Form/Radio.tsx +1 -1
- package/src/components/Form/Select.stories.tsx +1 -1
- package/src/components/Form/Select.tsx +1 -1
- package/src/components/Form/Textarea.stories.tsx +1 -1
- package/src/components/Form/Textarea.tsx +1 -1
- package/src/components/Hero/Hero.stories.tsx +2 -2
- package/src/components/Hero/Hero.tsx +2 -2
- package/src/components/Messages/Messages.stories.tsx +1 -1
- package/src/components/Messages/Messages.tsx +2 -2
- package/src/components/Modal/Modal.stories.tsx +1 -1
- package/src/components/Navigation/Nav/Nav.stories.tsx +2 -2
- package/src/components/Navigation/Nav/Nav.tsx +1 -1
- package/src/components/Navigation/Nav/NavItem.tsx +6 -3
- package/src/components/Navigation/Navbar/Navbar.stories.tsx +3 -3
- package/src/components/Navigation/Navbar/Navbar.tsx +1 -1
- package/src/components/Navigation/SideMenu/SideMenu.stories.tsx +2 -2
- package/src/components/Navigation/SideMenu/SideMenu.tsx +1 -1
- package/src/components/Pagination/Pagination.stories.tsx +1 -1
- package/src/components/Pagination/Pagination.tsx +1 -1
- package/src/components/Popover/Popover.stories.tsx +1 -1
- package/src/components/Popover/Popover.tsx +1 -1
- package/src/components/Progress/Progress.tsx +1 -1
- package/src/components/Rating/Rating.stories.tsx +1 -1
- package/src/components/Rating/Rating.test.tsx +73 -0
- package/src/components/Rating/Rating.tsx +25 -37
- package/src/components/Spinner/Spinner.tsx +1 -1
- package/src/components/Steps/Steps.stories.tsx +1 -1
- package/src/components/Steps/Steps.tsx +2 -2
- package/src/components/Tabs/Tabs.stories.tsx +1 -1
- package/src/components/Tabs/Tabs.tsx +1 -1
- package/src/components/Todo/Todo.tsx +0 -1
- package/src/components/Toggle/Toggle.stories.tsx +1 -1
- package/src/components/Toggle/Toggle.tsx +1 -1
- package/src/components/Tooltip/Tooltip.stories.tsx +1 -1
- package/src/components/VideoPlayer/VideoPlayer.stories.tsx +2 -2
- package/src/lib/composables/__tests__/useAtomixGlassPerf.test.tsx +88 -0
- package/src/lib/composables/__tests__/useChart.test.ts +50 -0
- package/src/lib/composables/__tests__/useChart.test.tsx +139 -0
- package/src/lib/composables/__tests__/useHeroBackgroundSlider.test.tsx +59 -0
- package/src/lib/composables/__tests__/useSliderAutoplay.test.tsx +68 -0
- package/src/lib/composables/atomix-glass/useGlassBackgroundDetection.ts +329 -0
- package/src/lib/composables/atomix-glass/useGlassCornerRadius.ts +82 -0
- package/src/lib/composables/atomix-glass/useGlassMouseTracking.ts +153 -0
- package/src/lib/composables/atomix-glass/useGlassOverLight.ts +198 -0
- package/src/lib/composables/atomix-glass/useGlassSize.ts +117 -0
- package/src/lib/composables/atomix-glass/useGlassState.ts +112 -0
- package/src/lib/composables/atomix-glass/useGlassTransforms.ts +160 -0
- package/src/lib/composables/glass-styles.ts +302 -0
- package/src/lib/composables/index.ts +0 -4
- package/src/lib/composables/useAtomixGlass.ts +331 -522
- package/src/lib/composables/useAtomixGlassStyles.ts +307 -0
- package/src/lib/composables/useBarChart.ts +1 -1
- package/src/lib/composables/useBreadcrumb.ts +6 -6
- package/src/lib/composables/useChart.ts +104 -21
- package/src/lib/composables/useHeroBackgroundSlider.ts +16 -7
- package/src/lib/composables/useSlider.ts +66 -34
- package/src/lib/theme/devtools/CLI.ts +1 -1
- package/src/lib/theme/utils/__tests__/themeUtils.test.ts +213 -0
- package/src/lib/types/components.ts +18 -21
- package/src/lib/utils/__tests__/dom.test.ts +100 -0
- package/src/lib/utils/__tests__/fontPreloader.test.ts +102 -0
- package/src/styles/02-tools/_tools.breakpoints.scss +1 -1
- package/src/styles/02-tools/_tools.utility-api.scss +6 -6
- package/src/styles/06-components/_components.accordion.scss +0 -2
- package/src/styles/06-components/_components.chart.scss +0 -1
- package/src/styles/06-components/_components.dropdown.scss +0 -1
- package/src/styles/06-components/_components.edge-panel.scss +0 -2
- package/src/styles/06-components/_components.photoviewer.scss +0 -1
- package/src/styles/06-components/_components.river.scss +0 -1
- package/src/styles/06-components/_components.slider.scss +0 -3
- package/src/styles/99-utilities/_utilities.glass-fixes.scss +0 -1
- package/src/styles/99-utilities/_utilities.text.scss +1 -0
|
@@ -2,7 +2,6 @@ import React, { useState, useEffect } from 'react';
|
|
|
2
2
|
import { TodoProps } from '../../lib/types/components';
|
|
3
3
|
import { useTodo } from '../../lib/composables/useTodo';
|
|
4
4
|
import { Icon } from '../Icon/Icon';
|
|
5
|
-
import { TODO } from '../../lib/constants/components';
|
|
6
5
|
import { generateUUID } from '../../lib/utils';
|
|
7
6
|
|
|
8
7
|
export const Todo: React.FC<TodoProps> = ({
|
|
@@ -813,7 +813,7 @@ export const GlassCustom: Story = {
|
|
|
813
813
|
saturation: 180,
|
|
814
814
|
aberrationIntensity: 2.5,
|
|
815
815
|
elasticity: 0.4,
|
|
816
|
-
|
|
816
|
+
borderRadius: 20,
|
|
817
817
|
mode: 'prominent',
|
|
818
818
|
overLight: false,
|
|
819
819
|
},
|
|
@@ -1071,7 +1071,7 @@ export const GlassWithInteractiveContent: Story = {
|
|
|
1071
1071
|
saturation: 170,
|
|
1072
1072
|
aberrationIntensity: 2,
|
|
1073
1073
|
elasticity: 0.3,
|
|
1074
|
-
|
|
1074
|
+
borderRadius: 15,
|
|
1075
1075
|
mode: 'standard',
|
|
1076
1076
|
},
|
|
1077
1077
|
glassOpacity: 0.6,
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import React, { useRef } from 'react';
|
|
2
|
+
import { render, act } from '@testing-library/react';
|
|
3
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
4
|
+
import { useAtomixGlass } from '../useAtomixGlass';
|
|
5
|
+
|
|
6
|
+
describe('useAtomixGlass Performance', () => {
|
|
7
|
+
let renderCount = 0;
|
|
8
|
+
let glassRefOut: React.RefObject<HTMLDivElement> | null = null;
|
|
9
|
+
|
|
10
|
+
const TestComponent = () => {
|
|
11
|
+
renderCount++;
|
|
12
|
+
const glassRef = useRef<HTMLDivElement>(null);
|
|
13
|
+
const contentRef = useRef<HTMLDivElement>(null);
|
|
14
|
+
glassRefOut = glassRef;
|
|
15
|
+
|
|
16
|
+
useAtomixGlass({
|
|
17
|
+
glassRef,
|
|
18
|
+
contentRef,
|
|
19
|
+
elasticity: 0.1, // Ensure elasticity so transform changes
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div ref={glassRef} style={{ width: 200, height: 100 }}>
|
|
24
|
+
<div ref={contentRef}>Content</div>
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
renderCount = 0;
|
|
31
|
+
glassRefOut = null;
|
|
32
|
+
// Mock getBoundingClientRect
|
|
33
|
+
Element.prototype.getBoundingClientRect = vi.fn(() => ({
|
|
34
|
+
width: 200,
|
|
35
|
+
height: 100,
|
|
36
|
+
top: 0,
|
|
37
|
+
left: 0,
|
|
38
|
+
bottom: 100,
|
|
39
|
+
right: 200,
|
|
40
|
+
x: 0,
|
|
41
|
+
y: 0,
|
|
42
|
+
toJSON: () => {},
|
|
43
|
+
}));
|
|
44
|
+
vi.useFakeTimers();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
afterEach(() => {
|
|
48
|
+
vi.useRealTimers();
|
|
49
|
+
vi.restoreAllMocks();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('does not re-render on mouse move but updates styles', async () => {
|
|
53
|
+
render(<TestComponent />);
|
|
54
|
+
|
|
55
|
+
// Run initial effects
|
|
56
|
+
await act(async () => {
|
|
57
|
+
vi.runAllTimers();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const countAfterSetup = renderCount;
|
|
61
|
+
const initialTransform = glassRefOut?.current?.style.transform;
|
|
62
|
+
console.log(`Initial transform: ${initialTransform}`);
|
|
63
|
+
|
|
64
|
+
// Simulate mouse move
|
|
65
|
+
const moveEvent = new MouseEvent('mousemove', {
|
|
66
|
+
clientX: 50,
|
|
67
|
+
clientY: 50,
|
|
68
|
+
bubbles: true,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
await act(async () => {
|
|
72
|
+
document.dispatchEvent(moveEvent);
|
|
73
|
+
vi.runAllTimers();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
console.log(`Render count: ${renderCount}`);
|
|
77
|
+
|
|
78
|
+
// Expect NO re-render
|
|
79
|
+
expect(renderCount).toBe(countAfterSetup);
|
|
80
|
+
|
|
81
|
+
// Expect style update
|
|
82
|
+
const finalTransform = glassRefOut?.current?.style.transform;
|
|
83
|
+
console.log(`Final transform: ${finalTransform}`);
|
|
84
|
+
|
|
85
|
+
expect(finalTransform).not.toBe(initialTransform);
|
|
86
|
+
expect(finalTransform).toContain('translate');
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { getDatasetBounds } from '../useChart';
|
|
3
|
+
import { ChartDataPoint } from '../../types/components';
|
|
4
|
+
|
|
5
|
+
describe('useChart', () => {
|
|
6
|
+
describe('getDatasetBounds', () => {
|
|
7
|
+
it('should correctly calculate min and max for valid numeric data', () => {
|
|
8
|
+
const data: ChartDataPoint[] = [
|
|
9
|
+
{ label: 'A', value: 10 },
|
|
10
|
+
{ label: 'B', value: 5 },
|
|
11
|
+
{ label: 'C', value: 20 },
|
|
12
|
+
];
|
|
13
|
+
const result = getDatasetBounds(data);
|
|
14
|
+
expect(result).toEqual({ min: 5, max: 20, hasValid: true });
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should handle empty data', () => {
|
|
18
|
+
const data: ChartDataPoint[] = [];
|
|
19
|
+
const result = getDatasetBounds(data);
|
|
20
|
+
expect(result).toEqual({ min: Infinity, max: -Infinity, hasValid: false });
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should handle undefined data', () => {
|
|
24
|
+
const result = getDatasetBounds(undefined);
|
|
25
|
+
expect(result).toEqual({ min: Infinity, max: -Infinity, hasValid: false });
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should ignore invalid values', () => {
|
|
29
|
+
const data: any[] = [
|
|
30
|
+
{ label: 'A', value: 10 },
|
|
31
|
+
{ label: 'B', value: 'invalid' },
|
|
32
|
+
{ label: 'C', value: null },
|
|
33
|
+
{ label: 'D', value: 30 },
|
|
34
|
+
];
|
|
35
|
+
const result = getDatasetBounds(data as ChartDataPoint[]);
|
|
36
|
+
expect(result).toEqual({ min: 10, max: 30, hasValid: true });
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should handle large datasets without stack overflow', () => {
|
|
40
|
+
const size = 150000;
|
|
41
|
+
const data: ChartDataPoint[] = Array.from({ length: size }, (_, i) => ({
|
|
42
|
+
label: `Point ${i}`,
|
|
43
|
+
value: i
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
const result = getDatasetBounds(data);
|
|
47
|
+
expect(result).toEqual({ min: 0, max: size - 1, hasValid: true });
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { renderHook, act } from '@testing-library/react';
|
|
2
|
+
import { useChartData } from '../useChart';
|
|
3
|
+
import { ChartDataset } from '../../types/components';
|
|
4
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
5
|
+
|
|
6
|
+
describe('useChartData - Real-time Optimization', () => {
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
vi.useFakeTimers();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
vi.useRealTimers();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should not update processedData if data has not changed', () => {
|
|
16
|
+
const datasets: ChartDataset[] = [
|
|
17
|
+
{
|
|
18
|
+
label: 'Dataset 1',
|
|
19
|
+
data: [
|
|
20
|
+
{ label: '1', value: 10 },
|
|
21
|
+
{ label: '2', value: 20 },
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const { result } = renderHook(() =>
|
|
27
|
+
useChartData(datasets, {
|
|
28
|
+
enableRealTime: true,
|
|
29
|
+
realTimeInterval: 1000,
|
|
30
|
+
enableDecimation: false,
|
|
31
|
+
})
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const initialData = result.current.processedData;
|
|
35
|
+
|
|
36
|
+
// Advance time
|
|
37
|
+
act(() => {
|
|
38
|
+
vi.advanceTimersByTime(1100);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
expect(result.current.processedData).toBe(initialData);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should update processedData if data length changes (mutation)', () => {
|
|
45
|
+
const datasets: ChartDataset[] = [
|
|
46
|
+
{
|
|
47
|
+
label: 'Dataset 1',
|
|
48
|
+
data: [
|
|
49
|
+
{ label: '1', value: 10 },
|
|
50
|
+
{ label: '2', value: 20 },
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
const { result } = renderHook(() =>
|
|
56
|
+
useChartData(datasets, {
|
|
57
|
+
enableRealTime: true,
|
|
58
|
+
realTimeInterval: 1000,
|
|
59
|
+
enableDecimation: false,
|
|
60
|
+
})
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const initialData = result.current.processedData;
|
|
64
|
+
|
|
65
|
+
datasets[0].data.push({ label: '3', value: 30 });
|
|
66
|
+
|
|
67
|
+
act(() => {
|
|
68
|
+
vi.advanceTimersByTime(1100);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
expect(result.current.processedData).not.toBe(initialData);
|
|
72
|
+
expect(result.current.processedData[0].data.length).toBe(3);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should update processedData if data value changes (mutation)', () => {
|
|
76
|
+
const datasets: ChartDataset[] = [
|
|
77
|
+
{
|
|
78
|
+
label: 'Dataset 1',
|
|
79
|
+
data: [
|
|
80
|
+
{ label: '1', value: 10 },
|
|
81
|
+
{ label: '2', value: 20 },
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
const { result } = renderHook(() =>
|
|
87
|
+
useChartData(datasets, {
|
|
88
|
+
enableRealTime: true,
|
|
89
|
+
realTimeInterval: 1000,
|
|
90
|
+
enableDecimation: false,
|
|
91
|
+
})
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const initialData = result.current.processedData;
|
|
95
|
+
|
|
96
|
+
datasets[0].data[1].value = 25;
|
|
97
|
+
|
|
98
|
+
act(() => {
|
|
99
|
+
vi.advanceTimersByTime(1100);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
expect(result.current.processedData).not.toBe(initialData);
|
|
103
|
+
expect(result.current.processedData[0].data[1].value).toBe(25);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should update processedData if historical data changes (mutation)', () => {
|
|
107
|
+
const datasets: ChartDataset[] = [
|
|
108
|
+
{
|
|
109
|
+
label: 'Dataset 1',
|
|
110
|
+
data: [
|
|
111
|
+
{ label: '1', value: 10 },
|
|
112
|
+
{ label: '2', value: 20 },
|
|
113
|
+
{ label: '3', value: 30 },
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
const { result } = renderHook(() =>
|
|
119
|
+
useChartData(datasets, {
|
|
120
|
+
enableRealTime: true,
|
|
121
|
+
realTimeInterval: 1000,
|
|
122
|
+
enableDecimation: false,
|
|
123
|
+
})
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
const initialData = result.current.processedData;
|
|
127
|
+
|
|
128
|
+
// Mutate historical data (index 0)
|
|
129
|
+
datasets[0].data[0].value = 15;
|
|
130
|
+
|
|
131
|
+
act(() => {
|
|
132
|
+
vi.advanceTimersByTime(1100);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Should update
|
|
136
|
+
expect(result.current.processedData).not.toBe(initialData);
|
|
137
|
+
expect(result.current.processedData[0].data[0].value).toBe(15);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { renderHook, act } from '@testing-library/react';
|
|
2
|
+
import { useHeroBackgroundSlider } from '../useHeroBackgroundSlider';
|
|
3
|
+
import { HeroBackgroundSliderConfig } from '../../types/components';
|
|
4
|
+
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
5
|
+
|
|
6
|
+
describe('useHeroBackgroundSlider Performance', () => {
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
vi.useFakeTimers();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
vi.useRealTimers();
|
|
13
|
+
vi.restoreAllMocks();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should not reset interval frequently during autoplay', () => {
|
|
17
|
+
const config: HeroBackgroundSliderConfig = {
|
|
18
|
+
slides: [
|
|
19
|
+
{ type: 'image', src: 'slide1.jpg' },
|
|
20
|
+
{ type: 'image', src: 'slide2.jpg' },
|
|
21
|
+
{ type: 'image', src: 'slide3.jpg' },
|
|
22
|
+
],
|
|
23
|
+
autoplay: {
|
|
24
|
+
delay: 3000,
|
|
25
|
+
},
|
|
26
|
+
transitionDuration: 1000,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const clearIntervalSpy = vi.spyOn(global, 'clearInterval');
|
|
30
|
+
|
|
31
|
+
const { result } = renderHook(() => useHeroBackgroundSlider(config));
|
|
32
|
+
|
|
33
|
+
// Reset call count after initial render
|
|
34
|
+
clearIntervalSpy.mockClear();
|
|
35
|
+
|
|
36
|
+
// 1st Transition
|
|
37
|
+
act(() => {
|
|
38
|
+
vi.advanceTimersByTime(3000);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
act(() => {
|
|
42
|
+
vi.advanceTimersByTime(1000);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// 2nd Transition
|
|
46
|
+
act(() => {
|
|
47
|
+
vi.advanceTimersByTime(3000);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
act(() => {
|
|
51
|
+
vi.advanceTimersByTime(1000);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
console.log(`clearInterval calls: ${clearIntervalSpy.mock.calls.length}`);
|
|
55
|
+
|
|
56
|
+
// With optimization, the interval should persist and not be cleared during transitions
|
|
57
|
+
expect(clearIntervalSpy).toHaveBeenCalledTimes(0);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { renderHook, act } from '@testing-library/react';
|
|
2
|
+
import { useSlider } from '../useSlider';
|
|
3
|
+
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
4
|
+
import { SliderSlide } from '../../types/components';
|
|
5
|
+
|
|
6
|
+
describe('useSlider Autoplay Optimization', () => {
|
|
7
|
+
const slides: SliderSlide[] = [
|
|
8
|
+
{ id: '1', content: 'Slide 1' },
|
|
9
|
+
{ id: '2', content: 'Slide 2' },
|
|
10
|
+
{ id: '3', content: 'Slide 3' },
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
vi.useFakeTimers();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
vi.useRealTimers();
|
|
19
|
+
vi.restoreAllMocks();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should not reset interval when transitioning state changes', () => {
|
|
23
|
+
const setIntervalSpy = vi.spyOn(global, 'setInterval');
|
|
24
|
+
const clearIntervalSpy = vi.spyOn(global, 'clearInterval');
|
|
25
|
+
|
|
26
|
+
const autoplayConfig = { delay: 1000 };
|
|
27
|
+
|
|
28
|
+
const { result } = renderHook(() =>
|
|
29
|
+
useSlider({
|
|
30
|
+
slides,
|
|
31
|
+
autoplay: autoplayConfig,
|
|
32
|
+
speed: 300,
|
|
33
|
+
slidesToShow: 1,
|
|
34
|
+
})
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
// Initial render should set interval.
|
|
38
|
+
// Note: It might be called more than once due to initial state updates (like internalIndex setting)
|
|
39
|
+
// causing re-renders if dependencies are unstable, though refs should be stable.
|
|
40
|
+
// However, strictly, we want to verify it doesn't reset DURING autoplay cycle.
|
|
41
|
+
|
|
42
|
+
// Let's clear mocks after initial render is done.
|
|
43
|
+
setIntervalSpy.mockClear();
|
|
44
|
+
clearIntervalSpy.mockClear();
|
|
45
|
+
|
|
46
|
+
// Fast-forward to trigger autoplay
|
|
47
|
+
act(() => {
|
|
48
|
+
vi.advanceTimersByTime(1000);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Check if transition started
|
|
52
|
+
expect(result.current.transitioning).toBe(true);
|
|
53
|
+
|
|
54
|
+
// At this point, in the buggy version, transitioning becoming true triggers the effect cleanup and re-setup.
|
|
55
|
+
// So we expect setInterval/clearInterval to have been called.
|
|
56
|
+
|
|
57
|
+
// Let's see what happens after transition ends
|
|
58
|
+
act(() => {
|
|
59
|
+
vi.advanceTimersByTime(300); // speed is 300
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
expect(result.current.transitioning).toBe(false);
|
|
63
|
+
|
|
64
|
+
// In the optimized version, these should be 0 because the interval persists.
|
|
65
|
+
expect(setIntervalSpy).toHaveBeenCalledTimes(0);
|
|
66
|
+
expect(clearIntervalSpy).toHaveBeenCalledTimes(0);
|
|
67
|
+
});
|
|
68
|
+
});
|