-
Notifications
You must be signed in to change notification settings - Fork 455
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Create initial onboarding screens (#58)
* feat: create onboarding structure * feat: create onboarding carousel components * feat: create onboarding header component * feat: create onboarding container * feat: crate re-usable SafeButton component * feat: cover onboarding components with unit tests * feat: add storybook for carousel component * feat: fix unit tests * feat: remove hardcoded colors and test warnings * fix: remove unused testID * fix: remove magic numbers * feat: use tamagui variables
- Loading branch information
1 parent
951aae1
commit 013110a
Showing
28 changed files
with
678 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { Onboarding } from '@/src/features/Onboarding' | ||
import React from 'react' | ||
|
||
function OnboardingPage() { | ||
return <Onboarding /> | ||
} | ||
|
||
export default OnboardingPage |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions
18
apps/mobile/src/components/SafeButton/SafeButton.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import type { Meta, StoryObj } from '@storybook/react' | ||
import { SafeButton } from '@/src/components/SafeButton' | ||
import { action } from '@storybook/addon-actions' | ||
|
||
const meta: Meta<typeof SafeButton> = { | ||
title: 'SafeButton', | ||
component: SafeButton, | ||
args: { | ||
label: 'Get started', | ||
onPress: action('onPress'), | ||
}, | ||
} | ||
|
||
export default meta | ||
|
||
type Story = StoryObj<typeof SafeButton> | ||
|
||
export const Default: Story = {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import React from 'react' | ||
import { TouchableOpacity } from 'react-native' | ||
import { styled, Text, View } from 'tamagui' | ||
|
||
interface SafeButtonProps { | ||
onPress: () => void | ||
label: string | ||
} | ||
|
||
export const StyledButtonWrapper = styled(View, { | ||
height: 48, | ||
alignItems: 'center', | ||
justifyContent: 'center', | ||
borderRadius: 8, | ||
}) | ||
|
||
export function SafeButton({ onPress, label }: SafeButtonProps) { | ||
return ( | ||
<TouchableOpacity onPress={onPress}> | ||
<StyledButtonWrapper backgroundColor="$primary"> | ||
<Text fontSize="$4" fontWeight={600} color="$background"> | ||
{label} | ||
</Text> | ||
</StyledButtonWrapper> | ||
</TouchableOpacity> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
import { SafeButton } from './SafeButton' | ||
export { SafeButton } |
26 changes: 26 additions & 0 deletions
26
apps/mobile/src/features/Onboarding/Onboarding.container.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import React from 'react' | ||
import { Onboarding } from './Onboarding.container' | ||
import { fireEvent, render } from '@/src/tests/test-utils' | ||
|
||
const mockNavigate = jest.fn() | ||
|
||
jest.mock('expo-router', () => ({ | ||
useRouter: () => ({ | ||
navigate: mockNavigate, | ||
}), | ||
})) | ||
|
||
describe('Onboarding Component', () => { | ||
it('renders correctly', () => { | ||
const { getAllByText } = render(<Onboarding />) | ||
expect(getAllByText('Get started')).toHaveLength(1) | ||
}) | ||
|
||
it('navigates on button press', () => { | ||
const { getByText } = render(<Onboarding />) | ||
const button = getByText('Get started') | ||
|
||
fireEvent.press(button) | ||
expect(mockNavigate).toHaveBeenCalledWith('/(tabs)') | ||
}) | ||
}) |
19 changes: 19 additions & 0 deletions
19
apps/mobile/src/features/Onboarding/Onboarding.container.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import React from 'react' | ||
import { OnboardingCarousel } from './components/OnboardingCarousel' | ||
import { items } from './components/OnboardingCarousel/items' | ||
import { useRouter } from 'expo-router' | ||
import { SafeButton } from '@/src/components/SafeButton' | ||
|
||
export function Onboarding() { | ||
const router = useRouter() | ||
|
||
const onGetStartedPress = () => { | ||
router.navigate('/(tabs)') | ||
} | ||
|
||
return ( | ||
<OnboardingCarousel items={items}> | ||
<SafeButton onPress={onGetStartedPress} label="Get started" /> | ||
</OnboardingCarousel> | ||
) | ||
} |
21 changes: 21 additions & 0 deletions
21
apps/mobile/src/features/Onboarding/components/OnboardingCarousel/CarouselFeedback.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import React from 'react' | ||
import { CarouselFeedback } from './CarouselFeedback' | ||
import { render } from '@/src/tests/test-utils' | ||
import darkPalette from '@/src/theme/palettes/darkPalette' | ||
|
||
describe('CarouselFeedback', () => { | ||
it('renders with active state', () => { | ||
const { getByTestId } = render(<CarouselFeedback isActive={true} />) | ||
|
||
const carouselFeedback = getByTestId('carousel-feedback') | ||
|
||
expect(carouselFeedback.props.style.backgroundColor).toBe(darkPalette.background.default) | ||
}) | ||
|
||
it('renders with inactive state', () => { | ||
const { getByTestId } = render(<CarouselFeedback isActive={false} />) | ||
const carouselFeedback = getByTestId('carousel-feedback') | ||
|
||
expect(carouselFeedback.props.style.backgroundColor).toBe(darkPalette.primary.light) | ||
}) | ||
}) |
35 changes: 35 additions & 0 deletions
35
apps/mobile/src/features/Onboarding/components/OnboardingCarousel/CarouselFeedback.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import React, { useEffect } from 'react' | ||
import Animated, { useSharedValue, withSpring } from 'react-native-reanimated' | ||
import { useTheme } from 'tamagui' | ||
|
||
interface CarouselFeedbackProps { | ||
isActive: boolean | ||
} | ||
|
||
const UNACTIVE_WIDTH = 4 | ||
const ACTIVE_WIDTH = 14 | ||
|
||
export function CarouselFeedback({ isActive }: CarouselFeedbackProps) { | ||
const width = useSharedValue(UNACTIVE_WIDTH) | ||
const theme = useTheme() | ||
|
||
useEffect(() => { | ||
if (isActive) { | ||
width.value = withSpring(ACTIVE_WIDTH) | ||
} else { | ||
width.value = withSpring(UNACTIVE_WIDTH) | ||
} | ||
}, [isActive]) | ||
|
||
return ( | ||
<Animated.View | ||
testID="carousel-feedback" | ||
style={{ | ||
borderRadius: 50, | ||
backgroundColor: isActive ? theme.color.get() : theme.colorSecondary.get(), | ||
height: UNACTIVE_WIDTH, | ||
width, | ||
}} | ||
/> | ||
) | ||
} |
33 changes: 33 additions & 0 deletions
33
apps/mobile/src/features/Onboarding/components/OnboardingCarousel/CarouselItem.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import React from 'react' | ||
import { CarouselItem } from './CarouselItem' // adjust the import path as necessary | ||
import { Text } from 'tamagui' | ||
import { render } from '@/src/tests/test-utils' | ||
|
||
describe('CarouselItem Component', () => { | ||
it('renders correctly with all props', () => { | ||
const item = { | ||
title: <Text>Test Title</Text>, | ||
description: 'Test Description', | ||
image: <Text>Test Image</Text>, | ||
name: 'nevinha', | ||
} | ||
|
||
const { getByText } = render(<CarouselItem item={item} />) | ||
|
||
expect(getByText('Test Title')).toBeTruthy() | ||
expect(getByText('Test Description')).toBeTruthy() | ||
expect(getByText('Test Image')).toBeTruthy() | ||
}) | ||
|
||
it('renders correctly without optional props', () => { | ||
const item = { | ||
title: <Text>Test Title</Text>, | ||
name: 'Test Name', | ||
} | ||
|
||
const { getByText, queryByText } = render(<CarouselItem item={item} />) | ||
|
||
expect(getByText('Test Title')).toBeTruthy() | ||
expect(queryByText('Test Description')).toBeNull() // Description is optional and not provided | ||
}) | ||
}) |
28 changes: 28 additions & 0 deletions
28
apps/mobile/src/features/Onboarding/components/OnboardingCarousel/CarouselItem.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { Text, View, YStack } from 'tamagui' | ||
|
||
export type CarouselItem = { | ||
title: string | React.ReactNode | ||
name: string | ||
description?: string | ||
image?: React.ReactNode | ||
} | ||
|
||
interface CarouselItemProps { | ||
item: CarouselItem | ||
} | ||
|
||
export const CarouselItem = ({ item: { title, description, image } }: CarouselItemProps) => { | ||
return ( | ||
<View gap="$8" alignItems="center" justifyContent="center"> | ||
{image} | ||
|
||
<YStack gap="$8" paddingHorizontal="$5"> | ||
<YStack>{title}</YStack> | ||
|
||
<Text textAlign="center" fontSize={'$4'}> | ||
{description} | ||
</Text> | ||
</YStack> | ||
</View> | ||
) | ||
} |
25 changes: 25 additions & 0 deletions
25
...bile/src/features/Onboarding/components/OnboardingCarousel/OnboardingCarousel.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import type { Meta, StoryObj } from '@storybook/react' | ||
import React from 'react' | ||
import { OnboardingCarousel } from './OnboardingCarousel' | ||
import { items } from './items' | ||
import { SafeButton } from '@/src/components/SafeButton' | ||
import { action } from '@storybook/addon-actions' | ||
|
||
const meta: Meta<typeof OnboardingCarousel> = { | ||
title: 'Carousel', | ||
component: OnboardingCarousel, | ||
} | ||
|
||
export default meta | ||
|
||
type Story = StoryObj<typeof OnboardingCarousel> | ||
|
||
export const Default: Story = { | ||
render: function Render(args) { | ||
return ( | ||
<OnboardingCarousel {...args} items={items}> | ||
<SafeButton label="Get started" onPress={action('onPress')} /> | ||
</OnboardingCarousel> | ||
) | ||
}, | ||
} |
24 changes: 24 additions & 0 deletions
24
.../mobile/src/features/Onboarding/components/OnboardingCarousel/OnboardingCarousel.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import React from 'react' | ||
import { OnboardingCarousel } from './OnboardingCarousel' | ||
import { Text, View } from 'tamagui' | ||
import { render } from '@/src/tests/test-utils' | ||
|
||
describe('OnboardingCarousel', () => { | ||
const items = [ | ||
{ name: 'Item1', title: <Text>Item1 Title</Text> }, | ||
{ name: 'Item2', title: <Text>Item2 Title</Text> }, | ||
{ name: 'Item3', title: <Text>Item3 Title</Text> }, | ||
] | ||
|
||
// react-native-collapsible-tab-view does not returns any information about the tabs children | ||
// that is why we only test the children component here =/ | ||
it('renders without crashing', () => { | ||
const { getByTestId } = render( | ||
<OnboardingCarousel items={items}> | ||
<View testID="child-element">Child Element</View> | ||
</OnboardingCarousel>, | ||
) | ||
|
||
expect(getByTestId('child-element')).toBeTruthy() | ||
}) | ||
}) |
40 changes: 40 additions & 0 deletions
40
apps/mobile/src/features/Onboarding/components/OnboardingCarousel/OnboardingCarousel.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import React, { useState } from 'react' | ||
import { CarouselItem } from './CarouselItem' | ||
import { View } from 'tamagui' | ||
import { Tabs } from 'react-native-collapsible-tab-view' | ||
import { CarouselFeedback } from './CarouselFeedback' | ||
|
||
interface OnboardingCarouselProps { | ||
items: CarouselItem[] | ||
children: React.ReactNode | ||
} | ||
|
||
export function OnboardingCarousel({ items, children }: OnboardingCarouselProps) { | ||
const [activeTab, setActiveTab] = useState(items[0].name) | ||
|
||
return ( | ||
<View flex={1} justifyContent={'space-between'} position="relative" paddingVertical="$10"> | ||
<Tabs.Container | ||
onTabChange={(event) => setActiveTab(event.tabName)} | ||
initialTabName={items[0].name} | ||
renderTabBar={() => <></>} | ||
> | ||
{items.map((item, index) => ( | ||
<Tabs.Tab name={item.name} key={`${item.name}-${index}`}> | ||
<CarouselItem key={index} item={item} /> | ||
</Tabs.Tab> | ||
))} | ||
</Tabs.Container> | ||
|
||
<View paddingHorizontal={20}> | ||
<View gap="$1" flexDirection="row" alignItems="center" justifyContent="center" marginBottom="$6"> | ||
{items.map((item) => ( | ||
<CarouselFeedback key={item.name} isActive={activeTab === item.name} /> | ||
))} | ||
</View> | ||
|
||
{children} | ||
</View> | ||
</View> | ||
) | ||
} |
2 changes: 2 additions & 0 deletions
2
apps/mobile/src/features/Onboarding/components/OnboardingCarousel/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
import { OnboardingCarousel } from './OnboardingCarousel' | ||
export { OnboardingCarousel } |
Oops, something went wrong.