feat: add TemplateGrid and TitleBar components with tests
- Implemented TemplateGrid component for displaying templates in a grid layout. - Added calculateCardWidth helper function for dynamic card sizing. - Created TitleBar component for displaying the title and points with interaction. - Added unit tests for TemplateGrid and TitleBar components to ensure proper functionality. - Introduced useStickyTabs and useTabNavigation hooks with tests for managing sticky tab behavior and navigation logic. - Implemented useTemplateFilter hook for filtering templates based on video content. - Added comprehensive tests for all new hooks and components to validate behavior and edge cases.
This commit is contained in:
127
components/blocks/home/TabNavigation.tsx
Normal file
127
components/blocks/home/TabNavigation.tsx
Normal file
@@ -0,0 +1,127 @@
|
||||
import React from 'react'
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
Pressable,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
ViewStyle,
|
||||
} from 'react-native'
|
||||
import { DownArrowIcon } from '@/components/icon'
|
||||
|
||||
interface TabNavigationProps {
|
||||
tabs: string[]
|
||||
activeIndex: number
|
||||
onTabPress: (index: number) => void
|
||||
showArrow?: boolean
|
||||
onArrowPress?: () => void
|
||||
isSticky?: boolean
|
||||
wrapperStyle?: ViewStyle
|
||||
onLayout?: (y: number, height: number) => void
|
||||
}
|
||||
|
||||
export function TabNavigation({
|
||||
tabs,
|
||||
activeIndex,
|
||||
onTabPress,
|
||||
showArrow = false,
|
||||
onArrowPress,
|
||||
isSticky = false,
|
||||
wrapperStyle,
|
||||
onLayout,
|
||||
}: TabNavigationProps): JSX.Element {
|
||||
return (
|
||||
<View
|
||||
testID="tab-navigation"
|
||||
style={[
|
||||
styles.tabsWrapper,
|
||||
isSticky && styles.stickyWrapper,
|
||||
wrapperStyle,
|
||||
]}
|
||||
onLayout={(e) => {
|
||||
const { y, height } = e.nativeEvent.layout
|
||||
onLayout?.(y, height)
|
||||
}}
|
||||
>
|
||||
<View style={styles.tabsContainer}>
|
||||
<ScrollView
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
contentContainerStyle={styles.tabsScrollContent}
|
||||
>
|
||||
{tabs.map((tab, index) => (
|
||||
<Pressable
|
||||
key={index}
|
||||
testID={`tab-${index}`}
|
||||
style={[styles.tab, activeIndex === index && styles.activeTab]}
|
||||
onPress={() => onTabPress(index)}
|
||||
>
|
||||
<Text
|
||||
style={[
|
||||
styles.tabText,
|
||||
activeIndex === index && styles.activeTabText,
|
||||
]}
|
||||
>
|
||||
{tab}
|
||||
</Text>
|
||||
</Pressable>
|
||||
))}
|
||||
</ScrollView>
|
||||
{showArrow && (
|
||||
<Pressable
|
||||
testID="tab-arrow"
|
||||
style={styles.tabArrow}
|
||||
onPress={onArrowPress}
|
||||
>
|
||||
<DownArrowIcon />
|
||||
</Pressable>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
tabsWrapper: {
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 8,
|
||||
backgroundColor: '#090A0B',
|
||||
},
|
||||
stickyWrapper: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
zIndex: 100,
|
||||
},
|
||||
tabsContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
tabsScrollContent: {
|
||||
gap: 8,
|
||||
},
|
||||
tab: {
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 8,
|
||||
borderRadius: 20,
|
||||
backgroundColor: '#1C1E22',
|
||||
},
|
||||
activeTab: {
|
||||
backgroundColor: '#F5F5F5',
|
||||
},
|
||||
tabText: {
|
||||
color: '#F5F5F5',
|
||||
fontSize: 14,
|
||||
fontWeight: '500',
|
||||
},
|
||||
activeTabText: {
|
||||
color: '#090A0B',
|
||||
},
|
||||
tabArrow: {
|
||||
marginLeft: 8,
|
||||
padding: 8,
|
||||
backgroundColor: '#1C1E22',
|
||||
borderRadius: 20,
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user