برنامه نویسی

توسعه مبتنی بر مؤلفه با Storybook React Native

Summarize this content to 400 words in Persian Lang

🚀 مقدمه

به این راهنمای جامع و مبتدی در مورد توسعه کامپوننت محور با استفاده از Storybook UI در React Native خوش آمدید. این آموزش از قدرت Expo، Tailwind و Storybook برای ارائه یک تجربه یادگیری قوی استفاده می کند. Repo GitHub: https://github.com/FastheDeveloper/RN_ComponentDrivenDevelopment_Storybook

🎯 آنچه یاد خواهید گرفت

اصول اصلی توسعه مولفه محور
از رابط کاربری Storybook برای بهبود گردش کار توسعه خود استفاده کنید
بوت استرپ پروژه React Native با Tailwind
ادغام یکپارچه رابط کاربری Storybook در پروژه Expo شما
ساخت و نمایش اجزای قابل استفاده مجدد
بهترین شیوه های صنعت برای توسعه مولفه محور

🛠 پیش نیازها

قبل از غواصی، مطمئن شوید که:

درک اولیه جاوا اسکریپت، تایپ اسکریپت و React Native
Node.js و npm روی دستگاه توسعه شما نصب شده است
مطمئن شوید که دستورالعمل های React Native – Environment Setup را تکمیل کرده اید
ویرایشگر کد دلخواه شما آماده کار است

📚 بخش های آموزشی

مقدمه ای بر توسعه مولفه محور
راه اندازی پروژه اکسپوی خود
یکپارچه سازی رابط کاربری کتاب داستان

ایجاد اولین کامپوننت شما

ساخت کامپوننت دکمه پویا
ایجاد یک داستان دکمه پیش فرض
ایجاد داستان های دکمه های مختلف
نوشتن تست های جست و جو برای کامپوننت دکمه

ایجاد کامپوننت دینامیک متن ورودی

ایجاد یک TextInput با Tailwind CSS
ایجاد یک داستان ورودی متن پیش فرض
ایجاد داستان های مختلف متن ورودی
نوشتن تست های Jest برای مولفه TextInput

تست کردن اجزای خود
نمونه های کامپوننت بیشتر
پیاده سازی TextInput and Button Component

مقدمه ای بر توسعه کامپوننت محور در React Native

Component-Driven Development (CDD) رویکردی برای ایجاد رابط های کاربری است که بر ایجاد برنامه های کاربردی از “پایین به بالا” با استفاده از اجزای قابل استفاده مجدد تاکید دارد. با توجه به معماری مبتنی بر کامپوننت چارچوب، این متدولوژی به ویژه برای توسعه React Native مناسب است.

توسعه مولفه محور چیست؟

CDD یک متدولوژی توسعه است که بر ایجاد رابط های کاربری با تجزیه آنها به اجزای کوچکتر و قابل استفاده مجدد تمرکز دارد. این اجزا قبل از مونتاژ شدن در ویژگی های بزرگتر و در نهایت صفحات یا صفحه های کامل به صورت مجزا توسعه می یابند.

اصول کلیدی CDD عبارتند از:

مدولار بودن: ایجاد رابط های کاربری از اجزای گسسته و قابل استفاده مجدد.

انزوا: توسعه و آزمایش اجزا به طور مستقل.

ترکیب: ترکیب اجزای کوچکتر برای ایجاد اجزای بزرگتر.

قابلیت استفاده مجدد: طراحی اجزا برای استفاده در بخش های مختلف یک برنامه.

مزایا برای توسعه دهندگان React Native

پذیرش CDD در توسعه React Native چندین مزیت دارد:

قابلیت نگهداری بهبود یافته: اجزای کوچکتر و مستقل برای درک، به روز رسانی و اشکال زدایی آسان تر هستند.

قابلیت استفاده مجدد پیشرفته: اجزای خوب طراحی شده را می توان در بخش های مختلف برنامه یا حتی در پروژه های مختلف مورد استفاده مجدد قرار داد.

همکاری آسان تر: تیم ها می توانند به طور همزمان روی اجزای مختلف بدون درگیری کار کنند.

طراحی منسجم: استفاده از کتابخانه ای از اجزای استاندارد، ثبات رابط کاربری را در سراسر برنامه تضمین می کند.

تست کارآمد: اجزا را می توان به صورت مجزا آزمایش کرد و نوشتن و نگهداری تست های واحد را آسان تر می کند.

پیاده سازی CDD در React Native

برای پیاده سازی CDD در پروژه React Native:

از کوچک شروع کنید: با شناسایی و ساختن کوچکترین و اساسی ترین عناصر رابط کاربری (دکمه ها، ورودی ها و غیره) شروع کنید.

یک کتابخانه کامپوننت بسازید: مجموعه ای از اجزای قابل استفاده مجدد ایجاد کنید که می توانند در برنامه شما به اشتراک گذاشته شوند.

از ابزارها استفاده کنید: از ابزارهایی مانند Storybook برای توسعه و نمایش اجزای خود به تنهایی استفاده کنید.

اجزای سند: برای هر مؤلفه مستندات واضح ارائه کنید، از جمله لوازم آن، مثال‌های استفاده و هرگونه محدودیت.

اجزای تست: تست های واحد را برای هر جزء بنویسید تا از عملکرد صحیح آنها به صورت مجزا مطمئن شوید.

با استفاده از CDD، توسعه دهندگان React Native می توانند برنامه های تلفن همراه قابل نگهداری، مقیاس پذیرتر و سازگارتر ایجاد کنند. این رویکرد به خوبی با فلسفه React Native مطابقت دارد و می‌تواند کارایی توسعه و کیفیت کد را به میزان قابل توجهی بهبود بخشد.

شروع به کار

استفاده از My Repository

برای شروع سریع با محیط از پیش پیکربندی شده، این مراحل را دنبال کنید:

کلون کردن مخزن با استفاده از:

git clone https://github.com/FastheDeveloper/RN_ComponentDrivenDevelopment_Storybook.git

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

راه اندازی از ابتدا

ایجاد برنامه بومی expo react:
npx create-expo-stack@latest

اجرای دستور بالا پس از پرسیدن چند سوال پیکربندی، یک برنامه boilerplate ایجاد می کند.

راه اندازی کتاب داستان در پروژه:
npx storybook@latest init

این یک پوشه جدید (.storybook) در فهرست پروژه شما اضافه می کند

پیکربندی اضافی:یک فایل app.config.js ایجاد کنیداین پیکربندی جابجایی آسان بین رابط کاربری Storybook برای آزمایش و برنامه واقعی React Native را تسهیل می‌کند.این فایل ثابت storybookEnabled را بر اساس متغیر محیطی STORYBOOK_ENABLED تعریف می کند. این به تعیین اینکه Storybook یا برنامه اصلی رندر شود کمک می کند.
export default ({ config }) => ({
…config,
name: ‘My_App_Name’,
slug: ‘My_App_Name’,
extra: {
storybookEnabled: process.env.STORYBOOK_ENABLED,
},
});

فایل package.json را به روز کنیددر فایل package.json، این اسکریپت های Storybook را اضافه کنید. ما از اینها برای انتقال آن متغیر محیطی به برنامه‌مان استفاده می‌کنیم، که با استفاده از cross-env نقطه ورودی را به رابط کاربری Storybook تعویض می‌کند تا مطمئن شود که این متغیر روی همه پلتفرم‌ها (ویندوز/macOS/لینوکس) کار می‌کند.

“`
{
“scripts”: {
“storybook”: “cross-env STORYBOOK_ENABLED=’true’ expo start”,
“storybook:ios”: “cross-env STORYBOOK_ENABLED=’true’ expo ios”,
“storybook:android”: “cross-env STORYBOOK_ENABLED=’true’ expo android”
}
}
“`

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

4. نقطه ورودی (/app/index.tsx) را با متغیر env تنظیم کنید:

“`javascript
import React from ‘react’
import { StyleSheet, Text, View } from ‘react-native’
import Constants from ‘expo-constants’

function Page() {
return (

This is the first page of your app.

)
}

let AppEntryPoint = Page

if (Constants?.expoConfig?.extra?.storybookEnabled === ‘true’) {
const StorybookUI = require(‘../.storybook’).default
AppEntryPoint = () => {
return (

)
}
}

export default AppEntryPoint

“`

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

5. برنامه را برای تست اجرا کنید:

To test the app, you can use one of the following commands depending on your target platform:

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

“`shell
# Run on iOS
yarn ios

# or

# Run on Android
yarn android
“`

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

6. کتاب داستان را برای آزمایش اجرا کنید:

“`shell
# Run on iOS
yarn storybook:ios

# or

# Run on Android
yarn storybook:android
“`

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ایجاد کامپوننت دکمه پویا

دکمه ایجاد با StyleSheet/Tailwind css، اضافه کردن Jest testID به قابل فشار دادن

import { ActivityIndicator, Pressable, StyleSheet, Text, View } from ‘react-native’
import React, { ComponentProps } from ‘react’
import { APP_COLOR } from ‘@constants/colorConstants’
import { FontAwesome } from ‘@expo/vector-icons’

type buttonProps = {
loading?: boolean
rightIcon?: keyof typeof FontAwesome.glyphMap
leftIcon?: keyof typeof FontAwesome.glyphMap
label: string
} & ComponentPropstypeof Pressable>

const AppButton = ({ loading, leftIcon, label, rightIcon, …pressableProps }: buttonProps) => {
const content = loading ? (

View

// style={styles.loaderWrapper}
className=”justify-center h-6″
>
ActivityIndicator size=”small” color={‘white’} animating={true} />
/View>
/>
) : (

{leftIcon && (
View
//style={styles.leftIcon}
className=”absolute left-5″
>
FontAwesome name={leftIcon} size={20} />
/View>
)}
Text
className=”text-APP_COLOR-MAIN_WHITE text-center text-lg font-bold ”
//style={styles.buttonText}
>
{label}
/Text>
{rightIcon && (
View
// style={styles.rightIcon}
className=”absolute right-5″
>
FontAwesome name={rightIcon} size={20} />
/View>
)}
/>
)
return (
Pressable
//style={styles.button}
className=”w-full flex justify-center item-center bg-APP_COLOR-MAIN_GREEN rounded-3xl p-4 shadow-lg”
{…pressableProps}
testID=”testClick”
>
{content}
/Pressable>
)
}

export default AppButton

const styles = StyleSheet.create({
button: {
alignItems: ‘center’,
backgroundColor: APP_COLOR.MAIN_GREEN,
borderRadius: 24,
elevation: 5,
flexDirection: ‘row’,
justifyContent: ‘center’,
padding: 16,
width: ‘100%’,
shadowColor: ‘#000’,
shadowOffset: {
height: 2,
width: 0,
},
shadowOpacity: 0.25,
shadowRadius: 3.84,
},
buttonText: {
color: APP_COLOR.MAIN_WHITE,
fontSize: 18,
fontWeight: ‘700’,
textAlign: ‘center’,
},
loaderWrapper: {
height: 24,
justifyContent: ‘center’,
},
rightIcon: {
position: ‘absolute’,
right: 20,
},
leftIcon: {
position: ‘absolute’,
left: 20,
},
})

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ایجاد داستان دکمه پیش فرض

در فایل های داستانی خود، از نحوی به نام Component Story Format (CSF) استفاده می کنیم. در این مورد، ما از CSF3 استفاده می کنیم که نسخه جدیدتر و به روز شده CSF است که توسط آخرین نسخه Storybook پشتیبانی می شود. این نسخه از CSF به میزان قابل توجهی دیگ بخار کمتری دارد و شروع را آسان تر می کند.در کتاب داستان، دو سطح اساسی از سازماندهی وجود دارد: جزء و داستان های فرزند آن. هر داستانی را می‌توان به‌عنوان جایگشت یک جزء در نظر گرفت. شما می توانید به تعداد مورد نیاز در هر جزء داستان داشته باشید.

* Component
* Story
* Story
* Story

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برای معرفی مؤلفه ای که در حال مستندسازی هستیم، یک صادرات پیش فرض ایجاد می کنیم که حاوی:

جزء – خود جزء
عنوان – نحوه مراجعه به مؤلفه در نوار کناری برنامه Storybook

argTypes – به ما امکان می دهد انواع args خود را مشخص کنیم، در اینجا ما از آن برای تعریف اقداماتی استفاده می کنیم که هر زمان که آن تعامل انجام شود، ثبت می شود.
import type { Meta, StoryObj } from ‘@storybook/react’

import AppButton from ‘./AppButton’

import React from ‘react’
import { View } from ‘react-native’

const AppButtonMeta: Metatypeof AppButton> = {
title: ‘Button’,
component: AppButton,
argTypes: {
onPress: { action: ‘pressed the button’ },
},
args: {
label: ‘Story Button’,
loading: false,
},
decorators: [
(Story) => (
View style={{ alignItems: ‘center’, justifyContent: ‘center’, flex: 1 }}>
Story />
/View>
),
],
}

export default AppButtonMeta

export const Default: StoryObjtypeof AppButton> = {}

در این مثال، ما یک داستان پیش فرض جدید ایجاد می کنیم که به Storybook می گوید:

نام در نوار کناری باید “دکمه” باشد
مؤلفه ای که باید به آن متصل شود AppButton است
برچسب پیش فرض باید “دکمه داستان” باشد
حالت بارگذاری پیش فرض باید نادرست باشد
عمل onPress باید عمل “فشرده دکمه” را اجرا کند

ویدیو را اینجا تماشا کنید

ایجاد داستان های دکمه های مختلف

export const TextOnlyButton: StoryObjtypeof AppButton> = {
args: {
label: ‘Text Button’,
},
argTypes: {
onPress: { action: ‘Yaay’ },
},
parameters: {
noBackground: true,
},
}

export const WithLeftIcon: StoryObjtypeof AppButton> = {
args: {
label: ‘With Left Icon’,
leftIcon: ‘paper-plane’,
},
argTypes: {
onPress: { action: ‘Lefty Pressed’ },
},
parameters: {
noBackground: true,
},
}

export const WithRightIcon: StoryObjtypeof AppButton> = {
args: {
label: ‘With Right Icon’,
rightIcon: ‘user-circle-o’,
},
argTypes: {
onPress: { action: ‘Righty Pressed’ },
},
parameters: {
noBackground: true,
},
}

export const WithBothIcons: StoryObjtypeof AppButton> = {
args: {
label: ‘With Both Icons’,
rightIcon: ‘user-circle-o’,
leftIcon: ‘paper-plane’,
},
argTypes: {
onPress: { action: ‘Bothy Pressed’ },
},
parameters: {
noBackground: true,
},
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ویدیو را اینجا تماشا کنید

ایجاد Jest Test

import React from ‘react’

import { render, fireEvent } from ‘@testing-library/react-native’
import AppButton from ‘@/components/Button/AppButton’

describe(‘MyButtons’, () => {
it(‘calls Unpressed when clicked’, () => {
const mockOnPress = jest.fn()
const { getByTestId } = render(AppButton label=”Test” onPress={mockOnPress} />)
const pressMeButton = getByTestId(‘testClick’)
fireEvent.press(pressMeButton)

expect(mockOnPress).toHaveBeenCalled()
})
})

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

راه اندازی مجموعه آزمایشی:

ما یک مجموعه آزمایشی به نام AppButton Component را با استفاده از توصیف تعریف می کنیم. این تست‌های مرتبط را برای مولفه AppButton گروه‌بندی می‌کند.

تعریف مورد آزمایشی:

در داخل مجموعه آزمایشی، یک مورد آزمایشی واحد وجود دارد که با استفاده از تابع it تعریف شده است. مورد آزمایشی با عنوان “تماس بدون فشار در هنگام کلیک کردن” است.

ایجاد تابع ساختگی:

ما یک تابع ساختگی mockOnPress را با استفاده از ()jest.fn ایجاد می کنیم. این تابع ساختگی پایه onPress مولفه AppButton را شبیه‌سازی می‌کند تا آزمایش کند که آیا با فشار دادن دکمه فراخوانی می‌شود یا خیر.

رندر کردن کامپوننت:

ما مولفه AppButton را با یک برچسب که روی “Test” تنظیم شده است و مولفه onPress prop را روی mockOnPress با استفاده از تابع render از @testing-library/react-native رندر می کنیم.

شبیه سازی تعامل کاربر:

ما عنصر دکمه را با استفاده از getByTestId با شناسه تست “testClick” بازیابی می کنیم. ما یک رویداد مطبوعاتی را روی عنصر دکمه با استفاده از fireEvent.press شبیه سازی می کنیم

بررسی رفتار:

ما ادعا می کنیم که تابع mockOnPress با استفاده از expect(mockOnPress).toHaveBeenCalled() فراخوانی شده است. این تأیید می‌کند که هنگام فشار دادن دکمه، عملکرد onPress prop فعال شده است.

اجرای تست جست

yarn test

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برای اجرای Test Case از دستور بالا استفاده کنید:

ایجاد کامپوننت دینامیک متن ورودی

Textinput را با Tailwind css ایجاد کنید و Jest testID را اضافه کنید

import { Pressable, Text, TextInput, View } from ‘react-native’
import React, { ComponentProps, useState } from ‘react’
import { FontAwesome } from ‘@expo/vector-icons’

type buttonProps = {
rightIcon?: keyof typeof FontAwesome.glyphMap
leftIcon?: keyof typeof FontAwesome.glyphMap
label?: string
} & ComponentPropstypeof TextInput>

const InputField = ({ leftIcon, label, rightIcon, …inputProps }: buttonProps) => {
const [hide, setHide] = useState(true)
return (
View className=”mx-2″>
Text className=”text-lg font-bold mb-2″>{label}/Text>

View className=”flex-row justify-between items-center bg-APP_COLOR-MAIN_WHITE w-full mt-2 rounded-xl shadow-xl border border-APP_COLOR-MAIN_GREY “>
{leftIcon && (
View className=”ml-2″ testID=”left-icon”>
FontAwesome name={leftIcon} size={20} />
/View>
)}

TextInput
className={`h-[52px] w-[85%] px-[2%] `}
{…inputProps}
secureTextEntry={inputProps.secureTextEntry && !rightIcon ? hide : undefined}
testID=”text-input”
/>

{(rightIcon || inputProps.secureTextEntry) && (
View className=”mr-2″ testID=”right-icon”>
Pressable onPress={() => (rightIcon ? null : setHide(!hide))} testID=”passwordTest”>
FontAwesome
name={rightIcon || (inputProps.secureTextEntry && (hide ? ‘eye’ : ‘eye-slash’)) || undefined}
size={20}
/>
/Pressable>
/View>
)}
/View>
/View>
)
}

export default InputField

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ایجاد داستان پیش فرض TextInput

import type { Meta, StoryObj } from ‘@storybook/react’

import React from ‘react’
import { View } from ‘react-native’
import InputField from ‘./InputField’

const InputFieldMeta: Metatypeof InputField> = {
title: ‘Input Field’,
component: InputField,
argTypes: {},
args: {
label: ‘Story Input’,
},
decorators: [
(Story) => (
View style={{ justifyContent: ‘center’, flex: 1 }}>
Story />
/View>
),
],
}

export default InputFieldMeta

export const Default: StoryObjtypeof InputField> = {}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در این مثال، ما یک داستان پیش فرض جدید ایجاد می کنیم که به Storybook می گوید:

نام در نوار کناری باید “فیلد ورودی” باشد
مؤلفه ای که باید به آن متصل شود InputField است
برچسب پیش فرض باید “ورودی داستان” باشد

ویدیو را اینجا تماشا کنید

ایجاد داستان های متنی مختلف

export const PasswordInput: StoryObjtypeof InputField> = {
args: {
secureTextEntry: true,
},
}

export const LeftIconInput: StoryObjtypeof InputField> = {
args: {
leftIcon: ‘user-circle’,
},
}

export const RightIconInput: StoryObjtypeof InputField> = {
args: {
rightIcon: ‘star’,
},
}

export const SafetyIconWithSecureEntry: StoryObjtypeof InputField> = {
args: {
rightIcon: ‘star’,
secureTextEntry: true,
},
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ویدیو را اینجا تماشا کنید

ایجاد Jest Test

import React from ‘react’
import { render, fireEvent } from ‘@testing-library/react-native’
import InputField from ‘@/components/InputField/InputField’

describe(‘InputField’, () => {
it(‘renders correctly with label’, () => {
const { getByText } = render(InputField label=”Test Label” />)
expect(getByText(‘Test Label’)).toBeTruthy()
})

it(‘renders left icon when provided’, () => {
const { getByTestId } = render(InputField leftIcon=”user” />)
expect(getByTestId(‘left-icon’)).toBeTruthy()
})

it(‘renders right icon when provided’, () => {
const { getByTestId } = render(InputField rightIcon=”lock” />)
expect(getByTestId(‘right-icon’)).toBeTruthy()
})

it(‘toggles password visibility when secureTextEntry is true’, () => {
const { getByTestId } = render(InputField secureTextEntry />)
const passwordToggle = getByTestId(‘passwordTest’)
const input = getByTestId(‘text-input’)

expect(input.props.secureTextEntry).toBe(true)
fireEvent.press(passwordToggle)
expect(input.props.secureTextEntry).toBe(false)
fireEvent.press(passwordToggle)
expect(input.props.secureTextEntry).toBe(true)
})

it(‘passes additional props to TextInput’, () => {
const { getByTestId } = render(InputField placeholder=”Enter text” />)
const input = getByTestId(‘text-input’)
expect(input.props.placeholder).toBe(‘Enter text’)
})
})

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تست کردن

این کامپوننت با استفاده از Jest and React Native Testing Library تست شده است. این تست‌ها جنبه‌های مختلف عملکرد مؤلفه InputField را پوشش می‌دهند:

رندر با لیبل: اطمینان حاصل می کند که جزء به درستی متن برچسب ارائه شده را ارائه می دهد.
نمایش نماد سمت چپ: بررسی می کند که نماد سمت چپ نمایش داده می شود leftIcon پایه ارائه شده است.
نمایش نماد سمت راست: بررسی می کند که آیا نماد سمت راست نمایش داده می شود rightIcon پایه داده می شود.

تغییر قابلیت مشاهده رمز عبور: عملکرد تغییر رویت رمز عبور را آزمایش می کند secureTextEntry درست است. تأیید می کند که:

ورودی در ابتدا امن است (رمز عبور پنهان)
با فشار دادن دکمه جابجایی، ورودی قابل مشاهده است
فشار دادن مجدد دکمه جابجایی ورودی را ایمن می کند

پاس دادن پروپ: تأیید می کند که لوازم اضافی (مانند placeholder) به درستی به مؤلفه TextInput ارسال می شوند.

تست ها استفاده می کنند render از React Native Testing Library برای رندر کامپوننت و fireEvent برای شبیه سازی تعاملات کاربر getByText و getByTestId برای پرس و جو از عناصر در کامپوننت رندر شده استفاده می شود.

اجرای تست جست

yarn test

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برای اجرای Test Case از دستور بالا استفاده کنید:

نمونه های کامپوننت

برای درک جامع تر از نحوه استفاده از این مؤلفه و کشف پیاده سازی های اضافی، لطفاً به بخش مراجعه کنید component پوشه در فهرست پروژه این پوشه حاوی مثال‌های مختلفی است که موارد استفاده و پیکربندی‌های مختلف Components و Storybook React Native را نشان می‌دهد.

این مثال ها می توانند به عنوان مرجع عملی عمل کنند،

با بررسی این مثال‌ها، بینش‌هایی در مورد ایجاد اجزای مختلف با استفاده از Storybook react native به دست خواهید آورد.

ساختن یک صفحه ثبت نام اولیه با اجزای ما

import { Text, View } from ‘react-native’
import React, { useState } from ‘react’
import InputField from ‘@/components/InputField/InputField’
import AppButton from ‘@/components/Button/AppButton’
import { validateEmail, passwordsMatch, allFieldsFilled } from ‘../utils/Validators’

export interface UserDetails {
name: string
email: string
password: string
confirmPassword: string
}

const initialUserDetails: UserDetails = {
name: ”,
email: ”,
password: ”,
confirmPassword: ”,
}

const SignupScreen = () => {
const [userDetails, setUserDetails] = useStateUserDetails>(initialUserDetails)

const handleChange = (name: keyof UserDetails, value: string) => {
setUserDetails((prevDetails) => ({
…prevDetails,
[name]: value,
}))
}

const isFormValid = () => {
return (
validateEmail(userDetails.email) &&
passwordsMatch(userDetails.password, userDetails.confirmPassword) &&
allFieldsFilled(userDetails)
)
}
return (
View className=” flex-1 mx-4″>
View className=” items-center “>
Text className=”text-lg text-[#212529] font-bold text-2xl”>Let’s Gets Signed Up
Create a new account

handleChange(‘name’, text)}
/>
‘check’ : undefined}
onChangeText={(text) => handleChange(’email’, text)}
/>

handleChange(‘password’, text)}
/>
handleChange(‘confirmPassword’, text)}
/>

)
}

export default SignupScreen

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این استفاده اولیه نشان می‌دهد که چگونه توسعه مبتنی بر مؤلفه با Storybook، قابلیت استفاده مجدد مؤلفه‌هایی را که به‌صورت مجزا ایجاد شده و با Jest آزمایش می‌شوند، افزایش می‌دهد. این اعتماد به عملکرد و عملکرد آنها را فراهم می کند و مزایای استفاده از Storybook برای ساخت و اعتبار سنجی اجزا را نشان می دهد.

ویدیو را اینجا تماشا کنید

کد نویسی مبارک! 🎉🎉🎉

کتاب داستان

🚀 مقدمه

به این راهنمای جامع و مبتدی در مورد توسعه کامپوننت محور با استفاده از Storybook UI در React Native خوش آمدید. این آموزش از قدرت Expo، Tailwind و Storybook برای ارائه یک تجربه یادگیری قوی استفاده می کند. Repo GitHub: https://github.com/FastheDeveloper/RN_ComponentDrivenDevelopment_Storybook

🎯 آنچه یاد خواهید گرفت

  1. اصول اصلی توسعه مولفه محور
  2. از رابط کاربری Storybook برای بهبود گردش کار توسعه خود استفاده کنید
  3. بوت استرپ پروژه React Native با Tailwind
  4. ادغام یکپارچه رابط کاربری Storybook در پروژه Expo شما
  5. ساخت و نمایش اجزای قابل استفاده مجدد
  6. بهترین شیوه های صنعت برای توسعه مولفه محور

🛠 پیش نیازها

قبل از غواصی، مطمئن شوید که:

  • درک اولیه جاوا اسکریپت، تایپ اسکریپت و React Native
  • Node.js و npm روی دستگاه توسعه شما نصب شده است
  • مطمئن شوید که دستورالعمل های React Native – Environment Setup را تکمیل کرده اید
  • ویرایشگر کد دلخواه شما آماده کار است

📚 بخش های آموزشی

  1. مقدمه ای بر توسعه مولفه محور
  2. راه اندازی پروژه اکسپوی خود
  3. یکپارچه سازی رابط کاربری کتاب داستان
  4. ایجاد اولین کامپوننت شما

    • ساخت کامپوننت دکمه پویا
    • ایجاد یک داستان دکمه پیش فرض
    • ایجاد داستان های دکمه های مختلف
    • نوشتن تست های جست و جو برای کامپوننت دکمه
  5. ایجاد کامپوننت دینامیک متن ورودی

    • ایجاد یک TextInput با Tailwind CSS
    • ایجاد یک داستان ورودی متن پیش فرض
    • ایجاد داستان های مختلف متن ورودی
    • نوشتن تست های Jest برای مولفه TextInput
  6. تست کردن اجزای خود
  7. نمونه های کامپوننت بیشتر
  8. پیاده سازی TextInput and Button Component

مقدمه ای بر توسعه کامپوننت محور در React Native

Component-Driven Development (CDD) رویکردی برای ایجاد رابط های کاربری است که بر ایجاد برنامه های کاربردی از “پایین به بالا” با استفاده از اجزای قابل استفاده مجدد تاکید دارد. با توجه به معماری مبتنی بر کامپوننت چارچوب، این متدولوژی به ویژه برای توسعه React Native مناسب است.

توسعه مولفه محور چیست؟

CDD یک متدولوژی توسعه است که بر ایجاد رابط های کاربری با تجزیه آنها به اجزای کوچکتر و قابل استفاده مجدد تمرکز دارد. این اجزا قبل از مونتاژ شدن در ویژگی های بزرگتر و در نهایت صفحات یا صفحه های کامل به صورت مجزا توسعه می یابند.

اصول کلیدی CDD عبارتند از:

  1. مدولار بودن: ایجاد رابط های کاربری از اجزای گسسته و قابل استفاده مجدد.
  2. انزوا: توسعه و آزمایش اجزا به طور مستقل.
  3. ترکیب: ترکیب اجزای کوچکتر برای ایجاد اجزای بزرگتر.
  4. قابلیت استفاده مجدد: طراحی اجزا برای استفاده در بخش های مختلف یک برنامه.

مزایا برای توسعه دهندگان React Native

پذیرش CDD در توسعه React Native چندین مزیت دارد:

  1. قابلیت نگهداری بهبود یافته: اجزای کوچکتر و مستقل برای درک، به روز رسانی و اشکال زدایی آسان تر هستند.
  2. قابلیت استفاده مجدد پیشرفته: اجزای خوب طراحی شده را می توان در بخش های مختلف برنامه یا حتی در پروژه های مختلف مورد استفاده مجدد قرار داد.
  3. همکاری آسان تر: تیم ها می توانند به طور همزمان روی اجزای مختلف بدون درگیری کار کنند.
  4. طراحی منسجم: استفاده از کتابخانه ای از اجزای استاندارد، ثبات رابط کاربری را در سراسر برنامه تضمین می کند.
  5. تست کارآمد: اجزا را می توان به صورت مجزا آزمایش کرد و نوشتن و نگهداری تست های واحد را آسان تر می کند.

پیاده سازی CDD در React Native

برای پیاده سازی CDD در پروژه React Native:

  1. از کوچک شروع کنید: با شناسایی و ساختن کوچکترین و اساسی ترین عناصر رابط کاربری (دکمه ها، ورودی ها و غیره) شروع کنید.
  2. یک کتابخانه کامپوننت بسازید: مجموعه ای از اجزای قابل استفاده مجدد ایجاد کنید که می توانند در برنامه شما به اشتراک گذاشته شوند.
  3. از ابزارها استفاده کنید: از ابزارهایی مانند Storybook برای توسعه و نمایش اجزای خود به تنهایی استفاده کنید.
  4. اجزای سند: برای هر مؤلفه مستندات واضح ارائه کنید، از جمله لوازم آن، مثال‌های استفاده و هرگونه محدودیت.
  5. اجزای تست: تست های واحد را برای هر جزء بنویسید تا از عملکرد صحیح آنها به صورت مجزا مطمئن شوید.

با استفاده از CDD، توسعه دهندگان React Native می توانند برنامه های تلفن همراه قابل نگهداری، مقیاس پذیرتر و سازگارتر ایجاد کنند. این رویکرد به خوبی با فلسفه React Native مطابقت دارد و می‌تواند کارایی توسعه و کیفیت کد را به میزان قابل توجهی بهبود بخشد.

شروع به کار

استفاده از My Repository

برای شروع سریع با محیط از پیش پیکربندی شده، این مراحل را دنبال کنید:

  • کلون کردن مخزن با استفاده از:
 git clone https://github.com/FastheDeveloper/RN_ComponentDrivenDevelopment_Storybook.git
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

راه اندازی از ابتدا

  1. ایجاد برنامه بومی expo react:

        npx create-expo-stack@latest
    

    اجرای دستور بالا پس از پرسیدن چند سوال پیکربندی، یک برنامه boilerplate ایجاد می کند.
    راه اندازی کنسول

  2. راه اندازی کتاب داستان در پروژه:

        npx storybook@latest init
    

    این یک پوشه جدید (.storybook) در فهرست پروژه شما اضافه می کند
    پوشه کتاب داستان

  3. پیکربندی اضافی:
    یک فایل app.config.js ایجاد کنید
    این پیکربندی جابجایی آسان بین رابط کاربری Storybook برای آزمایش و برنامه واقعی React Native را تسهیل می‌کند.
    این فایل ثابت storybookEnabled را بر اساس متغیر محیطی STORYBOOK_ENABLED تعریف می کند. این به تعیین اینکه Storybook یا برنامه اصلی رندر شود کمک می کند.

    export default ({ config }) => ({
        ...config,
        name: 'My_App_Name',  
        slug: 'My_App_Name', 
        extra: {
            storybookEnabled: process.env.STORYBOOK_ENABLED,
        },
    });
    

فایل package.json را به روز کنید
در فایل package.json، این اسکریپت های Storybook را اضافه کنید. ما از اینها برای انتقال آن متغیر محیطی به برنامه‌مان استفاده می‌کنیم، که با استفاده از cross-env نقطه ورودی را به رابط کاربری Storybook تعویض می‌کند تا مطمئن شود که این متغیر روی همه پلتفرم‌ها (ویندوز/macOS/لینوکس) کار می‌کند.

```
{
    "scripts": {
        "storybook": "cross-env STORYBOOK_ENABLED='true' expo start",
        "storybook:ios": "cross-env STORYBOOK_ENABLED='true' expo ios",
        "storybook:android": "cross-env STORYBOOK_ENABLED='true' expo android"
    }
}
```
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

4. نقطه ورودی (/app/index.tsx) را با متغیر env تنظیم کنید:

```javascript
import React from 'react'
import { StyleSheet, Text, View } from 'react-native'
import Constants from 'expo-constants'


function Page() {
return (
    
    
        This is the first page of your app.
    
    
)
}

let AppEntryPoint = Page  

if (Constants?.expoConfig?.extra?.storybookEnabled === 'true') {
const StorybookUI = require('../.storybook').default
AppEntryPoint = () => {
    return (
    
        
    
    )
}
}

export default AppEntryPoint

```
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

5. برنامه را برای تست اجرا کنید:

To test the app, you can use one of the following commands depending on your target platform:
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

```shell 
    # Run on iOS
    yarn ios

# or 

    # Run on Android
    yarn android
```
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

6. کتاب داستان را برای آزمایش اجرا کنید:

```shell 
    # Run on iOS
    yarn storybook:ios

# or 

    # Run on Android
    yarn storybook:android
```
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ایجاد کامپوننت دکمه پویا

دکمه ایجاد با StyleSheet/Tailwind css، اضافه کردن Jest testID به قابل فشار دادن

        import { ActivityIndicator, Pressable, StyleSheet, Text, View } from 'react-native'
        import React, { ComponentProps } from 'react'
        import { APP_COLOR } from '@constants/colorConstants'
        import { FontAwesome } from '@expo/vector-icons'

        type buttonProps = {
        loading?: boolean
        rightIcon?: keyof typeof FontAwesome.glyphMap
        leftIcon?: keyof typeof FontAwesome.glyphMap
        label: string
        } & ComponentPropstypeof Pressable>

        const AppButton = ({  loading, leftIcon, label, rightIcon, ...pressableProps }: buttonProps) => {
        const content = loading ? (
            
            View

            // style={styles.loaderWrapper}
             className="justify-center h-6"
             >
                ActivityIndicator size="small" color={'white'} animating={true} />
            /View>
            />
        ) : (
            
            {leftIcon && (
                View 
                //style={styles.leftIcon}
                className="absolute left-5"
                >
                FontAwesome name={leftIcon} size={20} />
                /View>
            )}
            Text 
            className="text-APP_COLOR-MAIN_WHITE text-center text-lg font-bold "
            //style={styles.buttonText}
            >
                {label}
            /Text>
            {rightIcon && (
                View 
               // style={styles.rightIcon}
                className="absolute right-5"
                >
                FontAwesome name={rightIcon} size={20} />
                /View>
            )}
            />
        )
        return (
            Pressable
            //style={styles.button}
            className="w-full flex justify-center  item-center bg-APP_COLOR-MAIN_GREEN rounded-3xl p-4 shadow-lg"
            {...pressableProps}
            testID="testClick"
            >
            {content}
            /Pressable>
        )
        }

        export default AppButton

        const styles = StyleSheet.create({
            button: {
                alignItems: 'center',
                backgroundColor: APP_COLOR.MAIN_GREEN,
                borderRadius: 24,
                elevation: 5,
                flexDirection: 'row',
                justifyContent: 'center',
                padding: 16,
                width: '100%',
                shadowColor: '#000',
                shadowOffset: {
                height: 2,
                width: 0,
                },
                shadowOpacity: 0.25,
                shadowRadius: 3.84,
            },
            buttonText: {
                color: APP_COLOR.MAIN_WHITE,
                fontSize: 18,
                fontWeight: '700',
                textAlign: 'center',
            },
            loaderWrapper: {
                height: 24,
                justifyContent: 'center',
            },
            rightIcon: {
                position: 'absolute',
                right: 20,
            },
            leftIcon: {
                position: 'absolute',
                left: 20,
            },
        })

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ایجاد داستان دکمه پیش فرض

در فایل های داستانی خود، از نحوی به نام Component Story Format (CSF) استفاده می کنیم. در این مورد، ما از CSF3 استفاده می کنیم که نسخه جدیدتر و به روز شده CSF است که توسط آخرین نسخه Storybook پشتیبانی می شود. این نسخه از CSF به میزان قابل توجهی دیگ بخار کمتری دارد و شروع را آسان تر می کند.
در کتاب داستان، دو سطح اساسی از سازماندهی وجود دارد: جزء و داستان های فرزند آن. هر داستانی را می‌توان به‌عنوان جایگشت یک جزء در نظر گرفت. شما می توانید به تعداد مورد نیاز در هر جزء داستان داشته باشید.

    * Component
        * Story
        * Story
        * Story
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برای معرفی مؤلفه ای که در حال مستندسازی هستیم، یک صادرات پیش فرض ایجاد می کنیم که حاوی:

  • جزء – خود جزء
  • عنوان – نحوه مراجعه به مؤلفه در نوار کناری برنامه Storybook
  • argTypes – به ما امکان می دهد انواع args خود را مشخص کنیم، در اینجا ما از آن برای تعریف اقداماتی استفاده می کنیم که هر زمان که آن تعامل انجام شود، ثبت می شود.

        import type { Meta, StoryObj } from '@storybook/react'
    
        import AppButton from './AppButton'
    
        import React from 'react'
        import { View } from 'react-native'
    
        const AppButtonMeta: Metatypeof AppButton> = {
        title: 'Button',
        component: AppButton,
        argTypes: {
            onPress: { action: 'pressed the button' },
        },
        args: {
            label: 'Story Button',
            loading: false,
        },
        decorators: [
            (Story) => (
            View style={{ alignItems: 'center', justifyContent: 'center', flex: 1 }}>
                Story />
            /View>
            ),
        ],
        }
    
        export default AppButtonMeta
    
        export const Default: StoryObjtypeof AppButton> = {}
    

در این مثال، ما یک داستان پیش فرض جدید ایجاد می کنیم که به Storybook می گوید:

  • نام در نوار کناری باید “دکمه” باشد
  • مؤلفه ای که باید به آن متصل شود AppButton است
  • برچسب پیش فرض باید “دکمه داستان” باشد
  • حالت بارگذاری پیش فرض باید نادرست باشد
  • عمل onPress باید عمل “فشرده دکمه” را اجرا کند

ویدیو را اینجا تماشا کنید

ایجاد داستان های دکمه های مختلف

        export const TextOnlyButton: StoryObjtypeof AppButton> = {
        args: {
            label: 'Text Button',
        },
        argTypes: {
            onPress: { action: 'Yaay' },
        },
        parameters: {
            noBackground: true,
        },
        }

        export const WithLeftIcon: StoryObjtypeof AppButton> = {
        args: {
            label: 'With Left Icon',
            leftIcon: 'paper-plane',
        },
        argTypes: {
            onPress: { action: 'Lefty Pressed' },
        },
        parameters: {
            noBackground: true,
        },
        }

        export const WithRightIcon: StoryObjtypeof AppButton> = {
        args: {
            label: 'With Right Icon',
            rightIcon: 'user-circle-o',
        },
        argTypes: {
            onPress: { action: 'Righty Pressed' },
        },
        parameters: {
            noBackground: true,
        },
        }

        export const WithBothIcons: StoryObjtypeof AppButton> = {
        args: {
            label: 'With Both Icons',
            rightIcon: 'user-circle-o',
            leftIcon: 'paper-plane',
        },
        argTypes: {
            onPress: { action: 'Bothy Pressed' },
        },
        parameters: {
            noBackground: true,
        },
        }

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ویدیو را اینجا تماشا کنید

ایجاد Jest Test

    import React from 'react'

    import { render, fireEvent } from '@testing-library/react-native'
    import AppButton from '@/components/Button/AppButton'

    describe('MyButtons', () => {
    it('calls Unpressed when clicked', () => {
        const mockOnPress = jest.fn()
        const { getByTestId } = render(AppButton label="Test" onPress={mockOnPress} />)
        const pressMeButton = getByTestId('testClick')
        fireEvent.press(pressMeButton)

        expect(mockOnPress).toHaveBeenCalled()
    })
    })

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

  1. راه اندازی مجموعه آزمایشی:

    • ما یک مجموعه آزمایشی به نام AppButton Component را با استفاده از توصیف تعریف می کنیم. این تست‌های مرتبط را برای مولفه AppButton گروه‌بندی می‌کند.
  2. تعریف مورد آزمایشی:

    • در داخل مجموعه آزمایشی، یک مورد آزمایشی واحد وجود دارد که با استفاده از تابع it تعریف شده است. مورد آزمایشی با عنوان “تماس بدون فشار در هنگام کلیک کردن” است.
  3. ایجاد تابع ساختگی:

    • ما یک تابع ساختگی mockOnPress را با استفاده از ()jest.fn ایجاد می کنیم. این تابع ساختگی پایه onPress مولفه AppButton را شبیه‌سازی می‌کند تا آزمایش کند که آیا با فشار دادن دکمه فراخوانی می‌شود یا خیر.
  4. رندر کردن کامپوننت:

    • ما مولفه AppButton را با یک برچسب که روی “Test” تنظیم شده است و مولفه onPress prop را روی mockOnPress با استفاده از تابع render از @testing-library/react-native رندر می کنیم.
  5. شبیه سازی تعامل کاربر:

    • ما عنصر دکمه را با استفاده از getByTestId با شناسه تست “testClick” بازیابی می کنیم. ما یک رویداد مطبوعاتی را روی عنصر دکمه با استفاده از fireEvent.press شبیه سازی می کنیم
  6. بررسی رفتار:

    • ما ادعا می کنیم که تابع mockOnPress با استفاده از expect(mockOnPress).toHaveBeenCalled() فراخوانی شده است. این تأیید می‌کند که هنگام فشار دادن دکمه، عملکرد onPress prop فعال شده است.

اجرای تست جست

    yarn test
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برای اجرای Test Case از دستور بالا استفاده کنید:

کنسول تست وجود دارد

ایجاد کامپوننت دینامیک متن ورودی

Textinput را با Tailwind css ایجاد کنید و Jest testID را اضافه کنید

        import { Pressable, Text, TextInput, View } from 'react-native'
        import React, { ComponentProps, useState } from 'react'
        import { FontAwesome } from '@expo/vector-icons'

        type buttonProps = {
        rightIcon?: keyof typeof FontAwesome.glyphMap
        leftIcon?: keyof typeof FontAwesome.glyphMap
        label?: string
        } & ComponentPropstypeof TextInput>

        const InputField = ({ leftIcon, label, rightIcon, ...inputProps }: buttonProps) => {
        const [hide, setHide] = useState(true)
        return (
            View className="mx-2">
            Text className="text-lg font-bold mb-2">{label}/Text>

            View className="flex-row justify-between items-center bg-APP_COLOR-MAIN_WHITE w-full  mt-2 rounded-xl shadow-xl border border-APP_COLOR-MAIN_GREY ">
                {leftIcon && (
                View className="ml-2" testID="left-icon">
                    FontAwesome name={leftIcon} size={20} />
                /View>
                )}

                TextInput
                className={`h-[52px] w-[85%] px-[2%]  `}
                {...inputProps}
                secureTextEntry={inputProps.secureTextEntry && !rightIcon ? hide : undefined}
                testID="text-input"
                />

                {(rightIcon || inputProps.secureTextEntry) && (
                View className="mr-2" testID="right-icon">
                    Pressable onPress={() => (rightIcon ? null : setHide(!hide))} testID="passwordTest">
                    FontAwesome
                        name={rightIcon || (inputProps.secureTextEntry && (hide ? 'eye' : 'eye-slash')) || undefined}
                        size={20}
                    />
                    /Pressable>
                /View>
                )}
            /View>
            /View>
        )
        }

        export default InputField

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ایجاد داستان پیش فرض TextInput

        import type { Meta, StoryObj } from '@storybook/react'

        import React from 'react'
        import { View } from 'react-native'
        import InputField from './InputField'

        const InputFieldMeta: Metatypeof InputField> = {
        title: 'Input Field',
        component: InputField,
        argTypes: {},
        args: {
            label: 'Story Input',
        },
        decorators: [
            (Story) => (
            View style={{ justifyContent: 'center', flex: 1 }}>
                Story />
            /View>
            ),
        ],
        }

        export default InputFieldMeta

        export const Default: StoryObjtypeof InputField> = {}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در این مثال، ما یک داستان پیش فرض جدید ایجاد می کنیم که به Storybook می گوید:

  • نام در نوار کناری باید “فیلد ورودی” باشد
  • مؤلفه ای که باید به آن متصل شود InputField است
  • برچسب پیش فرض باید “ورودی داستان” باشد

ویدیو را اینجا تماشا کنید

ایجاد داستان های متنی مختلف

        export const PasswordInput: StoryObjtypeof InputField> = {
        args: {
            secureTextEntry: true,
        },
        }

        export const LeftIconInput: StoryObjtypeof InputField> = {
        args: {
            leftIcon: 'user-circle',
        },
        }

        export const RightIconInput: StoryObjtypeof InputField> = {
        args: {
            rightIcon: 'star',
        },
        }

        export const SafetyIconWithSecureEntry: StoryObjtypeof InputField> = {
        args: {
            rightIcon: 'star',
            secureTextEntry: true,
        },
        }

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ویدیو را اینجا تماشا کنید

ایجاد Jest Test

        import React from 'react'
        import { render, fireEvent } from '@testing-library/react-native'
        import InputField from '@/components/InputField/InputField'


        describe('InputField', () => {
        it('renders correctly with label', () => {
            const { getByText } = render(InputField label="Test Label" />)
            expect(getByText('Test Label')).toBeTruthy()
        })

        it('renders left icon when provided', () => {
            const { getByTestId } = render(InputField leftIcon="user" />)
            expect(getByTestId('left-icon')).toBeTruthy()
        })

        it('renders right icon when provided', () => {
            const { getByTestId } = render(InputField rightIcon="lock" />)
            expect(getByTestId('right-icon')).toBeTruthy()
        })

        it('toggles password visibility when secureTextEntry is true', () => {
            const { getByTestId } = render(InputField secureTextEntry />)
            const passwordToggle = getByTestId('passwordTest')
            const input = getByTestId('text-input')

            expect(input.props.secureTextEntry).toBe(true)
            fireEvent.press(passwordToggle)
            expect(input.props.secureTextEntry).toBe(false)
            fireEvent.press(passwordToggle)
            expect(input.props.secureTextEntry).toBe(true)
        })

        it('passes additional props to TextInput', () => {
            const { getByTestId } = render(InputField placeholder="Enter text" />)
            const input = getByTestId('text-input')
            expect(input.props.placeholder).toBe('Enter text')
        })
        })

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تست کردن

این کامپوننت با استفاده از Jest and React Native Testing Library تست شده است. این تست‌ها جنبه‌های مختلف عملکرد مؤلفه InputField را پوشش می‌دهند:

  1. رندر با لیبل:
    اطمینان حاصل می کند که جزء به درستی متن برچسب ارائه شده را ارائه می دهد.

  2. نمایش نماد سمت چپ:
    بررسی می کند که نماد سمت چپ نمایش داده می شود leftIcon پایه ارائه شده است.

  3. نمایش نماد سمت راست:
    بررسی می کند که آیا نماد سمت راست نمایش داده می شود rightIcon پایه داده می شود.

  4. تغییر قابلیت مشاهده رمز عبور:
    عملکرد تغییر رویت رمز عبور را آزمایش می کند secureTextEntry درست است. تأیید می کند که:

    • ورودی در ابتدا امن است (رمز عبور پنهان)
    • با فشار دادن دکمه جابجایی، ورودی قابل مشاهده است
    • فشار دادن مجدد دکمه جابجایی ورودی را ایمن می کند
  5. پاس دادن پروپ:
    تأیید می کند که لوازم اضافی (مانند placeholder) به درستی به مؤلفه TextInput ارسال می شوند.

تست ها استفاده می کنند render از React Native Testing Library برای رندر کامپوننت و fireEvent برای شبیه سازی تعاملات کاربر getByText و getByTestId برای پرس و جو از عناصر در کامپوننت رندر شده استفاده می شود.

اجرای تست جست

    yarn test
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

برای اجرای Test Case از دستور بالا استفاده کنید:

تست های ورودی

نمونه های کامپوننت

برای درک جامع تر از نحوه استفاده از این مؤلفه و کشف پیاده سازی های اضافی، لطفاً به بخش مراجعه کنید component پوشه در فهرست پروژه این پوشه حاوی مثال‌های مختلفی است که موارد استفاده و پیکربندی‌های مختلف Components و Storybook React Native را نشان می‌دهد.

این مثال ها می توانند به عنوان مرجع عملی عمل کنند،

با بررسی این مثال‌ها، بینش‌هایی در مورد ایجاد اجزای مختلف با استفاده از Storybook react native به دست خواهید آورد.

ساختن یک صفحه ثبت نام اولیه با اجزای ما

            import { Text, View } from 'react-native'
            import React, { useState } from 'react'
            import InputField from '@/components/InputField/InputField'
            import AppButton from '@/components/Button/AppButton'
            import { validateEmail, passwordsMatch, allFieldsFilled } from '../utils/Validators'

            export interface UserDetails {
            name: string
            email: string
            password: string
            confirmPassword: string
            }

            const initialUserDetails: UserDetails = {
            name: '',
            email: '',
            password: '',
            confirmPassword: '',
            }

            const SignupScreen = () => {
            const [userDetails, setUserDetails] = useStateUserDetails>(initialUserDetails)

            const handleChange = (name: keyof UserDetails, value: string) => {
                setUserDetails((prevDetails) => ({
                ...prevDetails,
                [name]: value,
                }))
            }

            const isFormValid = () => {
                return (
                validateEmail(userDetails.email) &&
                passwordsMatch(userDetails.password, userDetails.confirmPassword) &&
                allFieldsFilled(userDetails)
                )
            }
            return (
                View className=" flex-1 mx-4">
                View className=" items-center  ">
                    Text className="text-lg text-[#212529] font-bold text-2xl">Let's Gets Signed Up
                     Create a new account
                
                
                     handleChange('name', text)}
                    />
                    'check' : undefined}
                    onChangeText={(text) => handleChange('email', text)}
                    />

                     handleChange('password', text)}
                    />
                     handleChange('confirmPassword', text)}
                    />
                
                
                    
                
                
            )
            }

            export default SignupScreen

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این استفاده اولیه نشان می‌دهد که چگونه توسعه مبتنی بر مؤلفه با Storybook، قابلیت استفاده مجدد مؤلفه‌هایی را که به‌صورت مجزا ایجاد شده و با Jest آزمایش می‌شوند، افزایش می‌دهد. این اعتماد به عملکرد و عملکرد آنها را فراهم می کند و مزایای استفاده از Storybook برای ساخت و اعتبار سنجی اجزا را نشان می دهد.

ویدیو را اینجا تماشا کنید

کد نویسی مبارک! 🎉🎉🎉

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا