@umituz/react-native-design-system 2.3.14 → 2.3.15

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.
Files changed (93) hide show
  1. package/package.json +19 -2
  2. package/src/index.ts +91 -0
  3. package/src/layouts/ScreenLayout/ScreenLayout.example.tsx +2 -2
  4. package/src/layouts/ScreenLayout/ScreenLayout.tsx +1 -1
  5. package/src/molecules/animation/core/AnimationCore.ts +29 -0
  6. package/src/molecules/animation/domain/entities/Animation.ts +81 -0
  7. package/src/molecules/animation/domain/entities/Fireworks.ts +44 -0
  8. package/src/molecules/animation/domain/entities/Theme.ts +76 -0
  9. package/src/molecules/animation/index.ts +146 -0
  10. package/src/molecules/animation/infrastructure/services/AnimationConfigService.ts +35 -0
  11. package/src/molecules/animation/infrastructure/services/SpringAnimationConfigService.ts +67 -0
  12. package/src/molecules/animation/infrastructure/services/TimingAnimationConfigService.ts +57 -0
  13. package/src/molecules/animation/infrastructure/services/__tests__/SpringAnimationConfigService.test.ts +114 -0
  14. package/src/molecules/animation/infrastructure/services/__tests__/TimingAnimationConfigService.test.ts +105 -0
  15. package/src/molecules/animation/presentation/components/Fireworks.tsx +126 -0
  16. package/src/molecules/animation/presentation/components/__tests__/Fireworks.test.tsx +189 -0
  17. package/src/molecules/animation/presentation/hooks/__tests__/useAnimation.integration.test.ts +216 -0
  18. package/src/molecules/animation/presentation/hooks/__tests__/useFireworks.test.ts +242 -0
  19. package/src/molecules/animation/presentation/hooks/__tests__/useGesture.test.ts +111 -0
  20. package/src/molecules/animation/presentation/hooks/__tests__/useSpringAnimation.test.ts +131 -0
  21. package/src/molecules/animation/presentation/hooks/__tests__/useTimingAnimation.test.ts +175 -0
  22. package/src/molecules/animation/presentation/hooks/__tests__/useTransformAnimation.test.ts +137 -0
  23. package/src/molecules/animation/presentation/hooks/useAnimation.ts +77 -0
  24. package/src/molecules/animation/presentation/hooks/useFireworks.ts +141 -0
  25. package/src/molecules/animation/presentation/hooks/useGesture.ts +61 -0
  26. package/src/molecules/animation/presentation/hooks/useGestureCreators.ts +163 -0
  27. package/src/molecules/animation/presentation/hooks/useGestureState.ts +53 -0
  28. package/src/molecules/animation/presentation/hooks/useIconAnimations.ts +119 -0
  29. package/src/molecules/animation/presentation/hooks/useModalAnimations.ts +124 -0
  30. package/src/molecules/animation/presentation/hooks/useReanimatedReady.ts +60 -0
  31. package/src/molecules/animation/presentation/hooks/useSpringAnimation.ts +69 -0
  32. package/src/molecules/animation/presentation/hooks/useTimingAnimation.ts +111 -0
  33. package/src/molecules/animation/presentation/hooks/useTransformAnimation.ts +57 -0
  34. package/src/molecules/animation/presentation/providers/AnimationThemeProvider.tsx +62 -0
  35. package/src/molecules/animation/presentation/providers/__tests__/AnimationThemeProvider.test.tsx +165 -0
  36. package/src/molecules/animation/types/global.d.ts +97 -0
  37. package/src/molecules/celebration/domain/entities/CelebrationConfig.ts +17 -0
  38. package/src/molecules/celebration/domain/entities/FireworksConfig.ts +32 -0
  39. package/src/molecules/celebration/index.ts +93 -0
  40. package/src/molecules/celebration/infrastructure/services/FireworksConfigService.ts +49 -0
  41. package/src/molecules/celebration/presentation/components/CelebrationFireworksOverlay.tsx +33 -0
  42. package/src/molecules/celebration/presentation/components/CelebrationModal.tsx +78 -0
  43. package/src/molecules/celebration/presentation/components/CelebrationModalContent.tsx +90 -0
  44. package/src/molecules/celebration/presentation/hooks/useCelebrationModalAnimation.ts +49 -0
  45. package/src/molecules/celebration/presentation/hooks/useCelebrationState.ts +45 -0
  46. package/src/molecules/celebration/presentation/styles/CelebrationModalStyles.ts +65 -0
  47. package/src/molecules/countdown/components/Countdown.tsx +128 -0
  48. package/src/molecules/countdown/components/CountdownHeader.tsx +84 -0
  49. package/src/molecules/countdown/components/TimeUnit.tsx +73 -0
  50. package/src/molecules/countdown/hooks/useCountdown.ts +107 -0
  51. package/src/molecules/countdown/index.ts +25 -0
  52. package/src/molecules/countdown/types/CountdownTypes.ts +31 -0
  53. package/src/molecules/countdown/utils/TimeCalculator.ts +46 -0
  54. package/src/molecules/emoji/domain/entities/Emoji.ts +129 -0
  55. package/src/molecules/emoji/index.ts +177 -0
  56. package/src/molecules/emoji/presentation/components/EmojiPicker.tsx +102 -0
  57. package/src/molecules/emoji/presentation/hooks/useEmojiPicker.ts +171 -0
  58. package/src/molecules/index.ts +21 -0
  59. package/src/molecules/long-press-menu/domain/entities/MenuAction.ts +37 -0
  60. package/src/molecules/long-press-menu/index.ts +16 -0
  61. package/src/molecules/navigation/StackNavigator.tsx +75 -0
  62. package/src/molecules/navigation/TabsNavigator.tsx +94 -0
  63. package/src/molecules/navigation/components/FabButton.tsx +45 -0
  64. package/src/molecules/navigation/components/TabLabel.tsx +47 -0
  65. package/src/molecules/navigation/createStackNavigator.ts +20 -0
  66. package/src/molecules/navigation/createTabNavigator.ts +20 -0
  67. package/src/molecules/navigation/hooks/useTabBarStyles.ts +54 -0
  68. package/src/molecules/navigation/index.ts +37 -0
  69. package/src/molecules/navigation/types.ts +118 -0
  70. package/src/molecules/navigation/utils/AppNavigation.ts +101 -0
  71. package/src/molecules/navigation/utils/IconRenderer.ts +50 -0
  72. package/src/molecules/navigation/utils/LabelProcessor.ts +70 -0
  73. package/src/molecules/navigation/utils/NavigationCleanup.ts +62 -0
  74. package/src/molecules/navigation/utils/NavigationTheme.ts +21 -0
  75. package/src/molecules/navigation/utils/NavigationValidator.ts +61 -0
  76. package/src/molecules/navigation/utils/ScreenFactory.ts +115 -0
  77. package/src/molecules/navigation/utils/__tests__/IconRenderer.getIconName.test.ts +109 -0
  78. package/src/molecules/navigation/utils/__tests__/IconRenderer.renderIcon.test.ts +116 -0
  79. package/src/molecules/navigation/utils/__tests__/LabelProcessor.processLabel.test.ts +116 -0
  80. package/src/molecules/navigation/utils/__tests__/LabelProcessor.processTitle.test.ts +59 -0
  81. package/src/molecules/navigation/utils/__tests__/NavigationCleanup.test.ts +271 -0
  82. package/src/molecules/navigation/utils/__tests__/NavigationValidator.test.ts +252 -0
  83. package/src/molecules/swipe-actions/domain/entities/SwipeAction.ts +194 -0
  84. package/src/molecules/swipe-actions/index.ts +6 -0
  85. package/src/molecules/swipe-actions/presentation/components/SwipeActionButton.tsx +131 -0
  86. package/src/theme/hooks/useResponsiveDesignTokens.ts +1 -1
  87. package/src/utilities/clipboard/ClipboardUtils.ts +71 -0
  88. package/src/utilities/clipboard/index.ts +5 -0
  89. package/src/utilities/index.ts +6 -0
  90. package/src/utilities/sharing/domain/entities/Share.ts +210 -0
  91. package/src/utilities/sharing/index.ts +205 -0
  92. package/src/utilities/sharing/infrastructure/services/SharingService.ts +165 -0
  93. package/src/utilities/sharing/presentation/hooks/useSharing.ts +154 -0
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Timing Animation Config Service
3
+ *
4
+ * Infrastructure service for timing animation configurations.
5
+ * Separates timing configuration logic from domain entities.
6
+ */
7
+
8
+ import type { AnimationTimingConfig } from '../../domain/entities/Animation';
9
+ import {
10
+ AnimationPreset,
11
+ ANIMATION_CONSTANTS,
12
+ } from '../../domain/entities/Animation';
13
+
14
+ /**
15
+ * Service for providing timing animation configurations
16
+ */
17
+ export class TimingAnimationConfigService {
18
+ /**
19
+ * Get preset timing config
20
+ */
21
+ static getTimingConfig(preset: AnimationPreset): AnimationTimingConfig {
22
+ const configs: Record<AnimationPreset, AnimationTimingConfig> = {
23
+ [AnimationPreset.FADE_IN]: {
24
+ duration: ANIMATION_CONSTANTS.DURATION.NORMAL,
25
+ },
26
+ [AnimationPreset.FADE_OUT]: {
27
+ duration: ANIMATION_CONSTANTS.DURATION.NORMAL,
28
+ },
29
+ [AnimationPreset.SLIDE_IN_UP]: {
30
+ duration: ANIMATION_CONSTANTS.DURATION.NORMAL,
31
+ },
32
+ [AnimationPreset.SLIDE_IN_DOWN]: {
33
+ duration: ANIMATION_CONSTANTS.DURATION.NORMAL,
34
+ },
35
+ [AnimationPreset.SLIDE_IN_LEFT]: {
36
+ duration: ANIMATION_CONSTANTS.DURATION.NORMAL,
37
+ },
38
+ [AnimationPreset.SLIDE_IN_RIGHT]: {
39
+ duration: ANIMATION_CONSTANTS.DURATION.NORMAL,
40
+ },
41
+ [AnimationPreset.SCALE_IN]: {
42
+ duration: ANIMATION_CONSTANTS.DURATION.FAST,
43
+ },
44
+ [AnimationPreset.SCALE_OUT]: {
45
+ duration: ANIMATION_CONSTANTS.DURATION.FAST,
46
+ },
47
+ [AnimationPreset.BOUNCE]: {
48
+ duration: ANIMATION_CONSTANTS.DURATION.SLOW,
49
+ },
50
+ [AnimationPreset.SHAKE]: {
51
+ duration: ANIMATION_CONSTANTS.DURATION.FAST,
52
+ },
53
+ };
54
+
55
+ return configs[preset];
56
+ }
57
+ }
@@ -0,0 +1,114 @@
1
+ /**
2
+ * SpringAnimationConfigService Tests
3
+ *
4
+ * Unit tests for spring animation configuration service.
5
+ */
6
+
7
+ import { SpringAnimationConfigService } from '../SpringAnimationConfigService';
8
+ import { AnimationPreset, ANIMATION_CONSTANTS } from '../../../domain/entities/Animation';
9
+
10
+ describe('SpringAnimationConfigService', () => {
11
+ describe('getSpringConfig', () => {
12
+ it('should return config for FADE_IN preset', () => {
13
+ const config = SpringAnimationConfigService.getSpringConfig(AnimationPreset.FADE_IN);
14
+
15
+ expect(config).toEqual({
16
+ damping: ANIMATION_CONSTANTS.SPRING.DAMPING,
17
+ stiffness: ANIMATION_CONSTANTS.SPRING.STIFFNESS,
18
+ });
19
+ });
20
+
21
+ it('should return config for FADE_OUT preset', () => {
22
+ const config = SpringAnimationConfigService.getSpringConfig(AnimationPreset.FADE_OUT);
23
+
24
+ expect(config).toEqual({
25
+ damping: ANIMATION_CONSTANTS.SPRING.DAMPING,
26
+ stiffness: ANIMATION_CONSTANTS.SPRING.STIFFNESS,
27
+ });
28
+ });
29
+
30
+ it('should return config for SCALE_IN preset', () => {
31
+ const config = SpringAnimationConfigService.getSpringConfig(AnimationPreset.SCALE_IN);
32
+
33
+ expect(config).toEqual({
34
+ damping: 15,
35
+ stiffness: 150,
36
+ });
37
+ });
38
+
39
+ it('should return config for SCALE_OUT preset', () => {
40
+ const config = SpringAnimationConfigService.getSpringConfig(AnimationPreset.SCALE_OUT);
41
+
42
+ expect(config).toEqual({
43
+ damping: 15,
44
+ stiffness: 150,
45
+ });
46
+ });
47
+
48
+ it('should return config for BOUNCE preset', () => {
49
+ const config = SpringAnimationConfigService.getSpringConfig(AnimationPreset.BOUNCE);
50
+
51
+ expect(config).toEqual({
52
+ damping: 5,
53
+ stiffness: 120,
54
+ });
55
+ });
56
+
57
+ it('should return config for SHAKE preset', () => {
58
+ const config = SpringAnimationConfigService.getSpringConfig(AnimationPreset.SHAKE);
59
+
60
+ expect(config).toEqual({
61
+ damping: 8,
62
+ stiffness: 200,
63
+ });
64
+ });
65
+
66
+ it('should return default spring config for slide presets', () => {
67
+ const slidePresets = [
68
+ AnimationPreset.SLIDE_IN_UP,
69
+ AnimationPreset.SLIDE_IN_DOWN,
70
+ AnimationPreset.SLIDE_IN_LEFT,
71
+ AnimationPreset.SLIDE_IN_RIGHT,
72
+ ];
73
+
74
+ slidePresets.forEach(preset => {
75
+ const config = SpringAnimationConfigService.getSpringConfig(preset);
76
+
77
+ expect(config).toEqual({
78
+ damping: ANIMATION_CONSTANTS.SPRING.DAMPING,
79
+ stiffness: ANIMATION_CONSTANTS.SPRING.STIFFNESS,
80
+ });
81
+ });
82
+ });
83
+
84
+ it('should handle all animation presets', () => {
85
+ const presets = Object.values(AnimationPreset);
86
+
87
+ presets.forEach(preset => {
88
+ const config = SpringAnimationConfigService.getSpringConfig(preset);
89
+
90
+ expect(config).toBeDefined();
91
+ expect(config).toHaveProperty('damping');
92
+ expect(config).toHaveProperty('stiffness');
93
+ expect(typeof config.damping).toBe('number');
94
+ expect(typeof config.stiffness).toBe('number');
95
+ expect(config.damping).toBeGreaterThan(0);
96
+ expect(config.stiffness).toBeGreaterThan(0);
97
+ });
98
+ });
99
+
100
+ it('should have appropriate values for different animation types', () => {
101
+ const bounceConfig = SpringAnimationConfigService.getSpringConfig(AnimationPreset.BOUNCE);
102
+ const shakeConfig = SpringAnimationConfigService.getSpringConfig(AnimationPreset.SHAKE);
103
+ const scaleConfig = SpringAnimationConfigService.getSpringConfig(AnimationPreset.SCALE_IN);
104
+
105
+ // Bounce should be more bouncy (lower damping, moderate stiffness)
106
+ expect(bounceConfig.damping).toBeLessThan(scaleConfig.damping);
107
+ expect(bounceConfig.stiffness).toBeLessThan(scaleConfig.stiffness);
108
+
109
+ // Shake should be quick and responsive (higher damping and stiffness)
110
+ expect(shakeConfig.damping).toBeGreaterThan(bounceConfig.damping);
111
+ expect(shakeConfig.stiffness).toBeGreaterThan(scaleConfig.stiffness);
112
+ });
113
+ });
114
+ });
@@ -0,0 +1,105 @@
1
+ /**
2
+ * TimingAnimationConfigService Tests
3
+ *
4
+ * Unit tests for timing animation configuration service.
5
+ */
6
+
7
+ import { TimingAnimationConfigService } from '../TimingAnimationConfigService';
8
+ import { AnimationPreset, ANIMATION_CONSTANTS } from '../../../domain/entities/Animation';
9
+
10
+ describe('TimingAnimationConfigService', () => {
11
+ describe('getTimingConfig', () => {
12
+ it('should return config for FADE_IN preset', () => {
13
+ const config = TimingAnimationConfigService.getTimingConfig(AnimationPreset.FADE_IN);
14
+
15
+ expect(config).toEqual({
16
+ duration: ANIMATION_CONSTANTS.DURATION.NORMAL,
17
+ });
18
+ });
19
+
20
+ it('should return config for FADE_OUT preset', () => {
21
+ const config = TimingAnimationConfigService.getTimingConfig(AnimationPreset.FADE_OUT);
22
+
23
+ expect(config).toEqual({
24
+ duration: ANIMATION_CONSTANTS.DURATION.NORMAL,
25
+ });
26
+ });
27
+
28
+ it('should return config for SLIDE_IN_UP preset', () => {
29
+ const config = TimingAnimationConfigService.getTimingConfig(AnimationPreset.SLIDE_IN_UP);
30
+
31
+ expect(config).toEqual({
32
+ duration: ANIMATION_CONSTANTS.DURATION.NORMAL,
33
+ });
34
+ });
35
+
36
+ it('should return config for SLIDE_IN_DOWN preset', () => {
37
+ const config = TimingAnimationConfigService.getTimingConfig(AnimationPreset.SLIDE_IN_DOWN);
38
+
39
+ expect(config).toEqual({
40
+ duration: ANIMATION_CONSTANTS.DURATION.NORMAL,
41
+ });
42
+ });
43
+
44
+ it('should return config for SLIDE_IN_LEFT preset', () => {
45
+ const config = TimingAnimationConfigService.getTimingConfig(AnimationPreset.SLIDE_IN_LEFT);
46
+
47
+ expect(config).toEqual({
48
+ duration: ANIMATION_CONSTANTS.DURATION.NORMAL,
49
+ });
50
+ });
51
+
52
+ it('should return config for SLIDE_IN_RIGHT preset', () => {
53
+ const config = TimingAnimationConfigService.getTimingConfig(AnimationPreset.SLIDE_IN_RIGHT);
54
+
55
+ expect(config).toEqual({
56
+ duration: ANIMATION_CONSTANTS.DURATION.NORMAL,
57
+ });
58
+ });
59
+
60
+ it('should return config for SCALE_IN preset', () => {
61
+ const config = TimingAnimationConfigService.getTimingConfig(AnimationPreset.SCALE_IN);
62
+
63
+ expect(config).toEqual({
64
+ duration: ANIMATION_CONSTANTS.DURATION.FAST,
65
+ });
66
+ });
67
+
68
+ it('should return config for SCALE_OUT preset', () => {
69
+ const config = TimingAnimationConfigService.getTimingConfig(AnimationPreset.SCALE_OUT);
70
+
71
+ expect(config).toEqual({
72
+ duration: ANIMATION_CONSTANTS.DURATION.FAST,
73
+ });
74
+ });
75
+
76
+ it('should return config for BOUNCE preset', () => {
77
+ const config = TimingAnimationConfigService.getTimingConfig(AnimationPreset.BOUNCE);
78
+
79
+ expect(config).toEqual({
80
+ duration: ANIMATION_CONSTANTS.DURATION.SLOW,
81
+ });
82
+ });
83
+
84
+ it('should return config for SHAKE preset', () => {
85
+ const config = TimingAnimationConfigService.getTimingConfig(AnimationPreset.SHAKE);
86
+
87
+ expect(config).toEqual({
88
+ duration: ANIMATION_CONSTANTS.DURATION.FAST,
89
+ });
90
+ });
91
+
92
+ it('should handle all animation presets', () => {
93
+ const presets = Object.values(AnimationPreset);
94
+
95
+ presets.forEach(preset => {
96
+ const config = TimingAnimationConfigService.getTimingConfig(preset);
97
+
98
+ expect(config).toBeDefined();
99
+ expect(config).toHaveProperty('duration');
100
+ expect(typeof config.duration).toBe('number');
101
+ expect(config.duration).toBeGreaterThan(0);
102
+ });
103
+ });
104
+ });
105
+ });
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Fireworks Component
3
+ *
4
+ * Particle-based fireworks animation component.
5
+ * Single Responsibility: Render fireworks particles.
6
+ */
7
+
8
+ import React from 'react';
9
+ import { View, StyleSheet } from 'react-native';
10
+ import Animated, { useAnimatedStyle, withTiming } from 'react-native-reanimated';
11
+ import { useFireworks } from '../hooks/useFireworks';
12
+ import type { FireworksConfig } from '../../domain/entities/Fireworks';
13
+
14
+ export interface FireworksProps extends FireworksConfig {
15
+ /**
16
+ * X position to trigger fireworks (0-1, relative to container width)
17
+ */
18
+ triggerX?: number;
19
+ /**
20
+ * Y position to trigger fireworks (0-1, relative to container height)
21
+ */
22
+ triggerY?: number;
23
+ /**
24
+ * Auto-trigger on mount
25
+ */
26
+ autoTrigger?: boolean;
27
+ /**
28
+ * Container style
29
+ */
30
+ style?: any;
31
+ /**
32
+ * Test ID
33
+ */
34
+ testID?: string;
35
+ }
36
+
37
+ /**
38
+ * Particle Component
39
+ */
40
+ const Particle: React.FC<{
41
+ x: number;
42
+ y: number;
43
+ color: string;
44
+ size: number;
45
+ opacity: number;
46
+ }> = ({ x, y, color, size, opacity }) => {
47
+ const animatedStyle = useAnimatedStyle(() => ({
48
+ position: 'absolute',
49
+ left: x,
50
+ top: y,
51
+ width: size,
52
+ height: size,
53
+ backgroundColor: color,
54
+ borderRadius: size / 2,
55
+ opacity: withTiming(opacity, { duration: 100 }),
56
+ }));
57
+
58
+ // @ts-ignore - Animated.View exists at runtime
59
+ return <Animated.View style={animatedStyle} />;
60
+ };
61
+
62
+ /**
63
+ * Fireworks Component
64
+ *
65
+ * @example
66
+ * <Fireworks
67
+ * autoTrigger
68
+ * particleCount={100}
69
+ * colors={['#FF6B6B', '#4ECDC4']}
70
+ * />
71
+ */
72
+ export const Fireworks: React.FC<FireworksProps> = ({
73
+ triggerX = 0.5,
74
+ triggerY = 0.5,
75
+ autoTrigger = false,
76
+ style,
77
+ testID = 'fireworks',
78
+ ...config
79
+ }) => {
80
+ const { particles, trigger } = useFireworks(config);
81
+ const [containerSize, setContainerSize] = React.useState({ width: 0, height: 0 });
82
+
83
+ React.useEffect(() => {
84
+ if (autoTrigger && containerSize.width > 0 && containerSize.height > 0) {
85
+ const x = containerSize.width * triggerX;
86
+ const y = containerSize.height * triggerY;
87
+ trigger(x, y);
88
+ }
89
+ }, [autoTrigger, containerSize, triggerX, triggerY, trigger]);
90
+
91
+ const handleLayout = (event: any) => {
92
+ const { width, height } = event.nativeEvent.layout;
93
+ setContainerSize({ width, height });
94
+ };
95
+
96
+ return (
97
+ <View
98
+ style={[styles.container, style]}
99
+ onLayout={handleLayout}
100
+ testID={testID}
101
+ pointerEvents="none"
102
+ >
103
+ {particles.map((particle, index) => (
104
+ <Particle
105
+ key={`particle-${index}`}
106
+ x={particle.x}
107
+ y={particle.y}
108
+ color={particle.color}
109
+ size={particle.size}
110
+ opacity={particle.life}
111
+ />
112
+ ))}
113
+ </View>
114
+ );
115
+ };
116
+
117
+ const styles = StyleSheet.create({
118
+ container: {
119
+ position: 'absolute',
120
+ top: 0,
121
+ left: 0,
122
+ right: 0,
123
+ bottom: 0,
124
+ overflow: 'hidden',
125
+ },
126
+ });
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Fireworks Component Tests
3
+ *
4
+ * Unit tests for fireworks particle system.
5
+ */
6
+
7
+ import React from 'react';
8
+ import { render, fireEvent } from '@testing-library/react-native';
9
+ import { Fireworks } from '../Fireworks';
10
+ import { FIREWORKS_CONSTANTS } from '../../../domain/entities/Fireworks';
11
+
12
+ // Mock react-native-reanimated
13
+ jest.mock('react-native-reanimated', () => ({
14
+ useAnimatedStyle: jest.fn((styleFactory) => styleFactory()),
15
+ withTiming: jest.fn((toValue, config) => ({ toValue, config })),
16
+ }));
17
+
18
+ // Mock useFireworks hook
19
+ jest.mock('../../hooks/useFireworks', () => ({
20
+ useFireworks: jest.fn(() => ({
21
+ particles: [
22
+ { x: 100, y: 100, color: '#FF0000', size: 4, life: 1 },
23
+ { x: 150, y: 150, color: '#00FF00', size: 6, life: 0.5 },
24
+ ],
25
+ trigger: jest.fn(),
26
+ isActive: true,
27
+ })),
28
+ }));
29
+
30
+ describe('Fireworks Component', () => {
31
+ const defaultProps = {
32
+ colors: ['#FF0000', '#00FF00', '#0000FF'],
33
+ testID: 'test-fireworks',
34
+ };
35
+
36
+ beforeEach(() => {
37
+ jest.clearAllMocks();
38
+ });
39
+
40
+ it('should render correctly', () => {
41
+ const { getByTestId } = render(<Fireworks {...defaultProps} />);
42
+
43
+ expect(getByTestId('test-fireworks')).toBeTruthy();
44
+ });
45
+
46
+ it('should render particles', () => {
47
+ const { getAllByTestId } = render(<Fireworks {...defaultProps} />);
48
+
49
+ const particles = getAllByTestId(/particle-/);
50
+ expect(particles).toHaveLength(2);
51
+ });
52
+
53
+ it('should use default props when not provided', () => {
54
+ const { getByTestId } = render(
55
+ <Fireworks colors={['#FF0000']} />
56
+ );
57
+
58
+ expect(getByTestId('fireworks')).toBeTruthy();
59
+ });
60
+
61
+ it('should apply custom styles', () => {
62
+ const customStyle = { backgroundColor: 'blue' };
63
+ const { getByTestId } = render(
64
+ <Fireworks
65
+ colors={['#FF0000']}
66
+ style={customStyle}
67
+ />
68
+ );
69
+
70
+ const container = getByTestId('fireworks');
71
+ expect(container.props.style).toEqual(
72
+ expect.arrayContaining([customStyle])
73
+ );
74
+ });
75
+
76
+ it('should handle layout changes', () => {
77
+ const { getByTestId } = render(<Fireworks {...defaultProps} />);
78
+ const container = getByTestId('test-fireworks');
79
+
80
+ fireEvent(container, 'layout', {
81
+ nativeEvent: {
82
+ layout: { width: 300, height: 400 }
83
+ }
84
+ });
85
+ });
86
+
87
+ it('should have pointerEvents disabled', () => {
88
+ const { getByTestId } = render(<Fireworks {...defaultProps} />);
89
+ const container = getByTestId('test-fireworks');
90
+
91
+ expect(container.props.pointerEvents).toBe('none');
92
+ });
93
+
94
+ describe('autoTrigger', () => {
95
+ it('should trigger fireworks when autoTrigger is true and container has size', () => {
96
+ const mockTrigger = jest.fn();
97
+ const { useFireworks } = require('../../hooks/useFireworks');
98
+ useFireworks.mockReturnValue({
99
+ particles: [],
100
+ trigger: mockTrigger,
101
+ isActive: false,
102
+ });
103
+
104
+ const { getByTestId } = render(
105
+ <Fireworks
106
+ colors={['#FF0000']}
107
+ autoTrigger={true}
108
+ />
109
+ );
110
+
111
+ const container = getByTestId('fireworks');
112
+ fireEvent(container, 'layout', {
113
+ nativeEvent: {
114
+ layout: { width: 300, height: 400 }
115
+ }
116
+ });
117
+
118
+ expect(mockTrigger).toHaveBeenCalledWith(150, 200); // center of container
119
+ });
120
+
121
+ it('should not trigger when autoTrigger is false', () => {
122
+ const mockTrigger = jest.fn();
123
+ const { useFireworks } = require('../../hooks/useFireworks');
124
+ useFireworks.mockReturnValue({
125
+ particles: [],
126
+ trigger: mockTrigger,
127
+ isActive: false,
128
+ });
129
+
130
+ render(
131
+ <Fireworks
132
+ colors={['#FF0000']}
133
+ autoTrigger={false}
134
+ />
135
+ );
136
+
137
+ expect(mockTrigger).not.toHaveBeenCalled();
138
+ });
139
+
140
+ it('should use custom trigger position', () => {
141
+ const mockTrigger = jest.fn();
142
+ const { useFireworks } = require('../../hooks/useFireworks');
143
+ useFireworks.mockReturnValue({
144
+ particles: [],
145
+ trigger: mockTrigger,
146
+ isActive: false,
147
+ });
148
+
149
+ const { getByTestId } = render(
150
+ <Fireworks
151
+ colors={['#FF0000']}
152
+ autoTrigger={true}
153
+ triggerX={0.25}
154
+ triggerY={0.75}
155
+ />
156
+ );
157
+
158
+ const container = getByTestId('fireworks');
159
+ fireEvent(container, 'layout', {
160
+ nativeEvent: {
161
+ layout: { width: 400, height: 200 }
162
+ }
163
+ });
164
+
165
+ expect(mockTrigger).toHaveBeenCalledWith(100, 150); // 25% of width, 75% of height
166
+ });
167
+ });
168
+
169
+ describe('Particle rendering', () => {
170
+ it('should render particle with correct properties', () => {
171
+ const particles = [
172
+ { x: 100, y: 100, color: '#FF0000', size: 4, life: 1 },
173
+ ];
174
+ const { useFireworks } = require('../../hooks/useFireworks');
175
+ useFireworks.mockReturnValue({
176
+ particles,
177
+ trigger: jest.fn(),
178
+ isActive: false,
179
+ });
180
+
181
+ render(<Fireworks colors={['#FF0000']} />);
182
+
183
+ // Particle should be rendered with correct properties
184
+ expect(useFireworks).toHaveBeenCalledWith({
185
+ colors: ['#FF0000'],
186
+ });
187
+ });
188
+ });
189
+ });