Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release/v0.4.2 #21

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/workflows/development.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Pull Request of development

on:
pull_request:
branches:
- 'dev'

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: yarn install --frozen-lockfile
- run: yarn test
- name: Check size limit
uses: andresz1/size-limit-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
label:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Split Action
id: prefix
uses: JungWinter/[email protected]
with:
msg: ${{ github.head_ref }}
separator: '/'

- name: Add label and update title
if: ${{ !startsWith(github.event.pull_request.title, steps.prefix.outputs._0) }}
run: |
gh pr edit ${{ github.event.pull_request.number }} --add-label ${{ steps.prefix.outputs._0 }} --title "${{ steps.prefix.outputs._0 }}: ${{ github.event.pull_request.title }}"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ yarn-error.log
.idea
dist/
*.tgz
coverage/

2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Have you ever wondered about creating a component like this? Or have you ever se

<img alt="Screenshot image" src="https://raw.githubusercontent.com/zoop-studio/react-native-animated-form-stack/main/docs/screenshot.gif" width='250' />

<img alt="Screenshot image" src="https://raw.githubusercontent.com/zoop-studio/react-native-animated-form-stack/main/docs/screenshot-example.gif" width='250' />

What if just wrapping your existing Form UI with our component could achieve this effect? No need for any additional external libraries; the ones you're already using in your project are sufficient.

Experience the magic of animated forms in a lightweight package of just under **3 KB**.
Expand Down
3 changes: 3 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
};
Binary file added docs/screenshot-example.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
153 changes: 97 additions & 56 deletions examples/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,16 @@
* @format
*/

import {useRef, useState} from 'react';
import {Button, SafeAreaView, ScrollView, Text, View} from 'react-native';
import {ReactElement, useRef, useState} from 'react';
import {
Button,
SafeAreaView,
ScrollView,
StyleSheet,
Text,
TextInput,
View,
} from 'react-native';
import {
FormStack,
IFormStackRef,
Expand All @@ -16,81 +24,96 @@ import {
} from 'react-native-animated-form-stack';

const Example = () => {
const ref = useRef<IFormStackRef>(null);
const firstNameRef = useRef<TextInput>(null);
const lastNameRef = useRef<TextInput>(null);
const jobTitleRef = useRef<TextInput>(null);
const [step, setStep] = useState(0);
const handlePressPrev = () => {
ref.current?.prev();
};
const handlePressNext = () => {
ref.current?.next();
const {update} = useFormStackAction();
const handleStepUpdate = (_step: number) => {
setStep(_step);
switch (step) {
case 0:
firstNameRef.current?.focus();
lastNameRef.current?.clear();
jobTitleRef.current?.clear();
break;
case 1:
lastNameRef.current?.focus();
jobTitleRef.current?.clear();
break;
case 2:
console.debug(jobTitleRef.current?.props);
jobTitleRef.current?.focus();
case 3:
break;
default:
break;
}
};

return (
<View style={{flex: 1}}>
<ScrollView contentContainerStyle={{flexGrow: 1}}>
<Text
style={{
marginVertical: 12,
fontSize: 24,
textAlign: 'center',
}}>{`Your current step is ${step}`}</Text>
style={formStyle.headerTitle}>{`Your current step is ${step}`}</Text>
<FormStack
ref={ref}
onUpdate={setStep} // You can obtain the step without the hook
gap={20} // Size of gap between the elements. Default 0
duration={250} // Length of animation (milliseconds). Default 250
initialStep={0} // Initial exposed step. Default 0
onUpdate={handleStepUpdate} // You can obtain the step without the hook
>
<View style={{backgroundColor: 'red', height: 500}} />
<View style={{backgroundColor: 'yellow', height: 200}} />
<Children5 />
<View style={{backgroundColor: 'orange', height: 50}} />
<Form label={'About'}>
<View>
<Text>
{
'What if you have a different size view?\nNo problem!\nThis form will automatically calculate the offset based on the size of the view.'
}
</Text>
<View
style={{
flexDirection: 'row',
justifyContent: 'flex-end',
}}>
<Button title={'reset'} onPress={() => update(0)} />
</View>
</View>
</Form>
<Form label={'Job title'}>
<TextInput
ref={jobTitleRef}
placeholder={'Please input your job title'}
onSubmitEditing={() => update(3)}
/>
</Form>
{/* null cannot be included on the step */}
{null}
<View style={{backgroundColor: 'blue', height: 75}} />
<Children2 />
<View style={{backgroundColor: 'skyblue', height: 25}} />
<View style={{backgroundColor: 'black', height: 85}} />
<Form label={'Last name'}>
<TextInput
ref={lastNameRef}
placeholder={'Please input your last name'}
onSubmitEditing={() => update(2)}
/>
</Form>
<Form label={'First name'}>
<TextInput
ref={firstNameRef}
placeholder={'Please input your first name'}
onSubmitEditing={() => update(1)}
/>
</Form>
</FormStack>
</ScrollView>
<View style={{flexDirection: 'row', alignItems: 'center'}}>
<View style={{flex: 1}}>
<Button title={'Previous'} onPress={handlePressPrev} />
</View>
<View style={{flex: 1}}>
<Button title={'Next'} onPress={handlePressNext} />
</View>
</View>
</View>
);
};

const Children5 = () => {
const {step} = useFormStackValue();
return (
<View
style={{
backgroundColor: 'grey',
height: 250,
alignItems: 'center',
justifyContent: 'center',
}}>
<Text style={{fontSize: 24}}>{`This is step ${step}`}</Text>
</View>
);
};

const Children2 = () => {
const {update} = useFormStackAction();
const to = 5;
const Form = ({label, children}: {label: string; children: ReactElement}) => {
return (
<View
style={{
backgroundColor: '#ddd',
height: 250,
justifyContent: 'center',
}}>
<Button title={`Let's go to step ${to}`} onPress={() => update(to)} />
<View style={formStyle.container}>
<View style={formStyle.labelContainer}>
<Text style={formStyle.labelText}>{label}</Text>
</View>
<View>{children}</View>
</View>
);
};
Expand All @@ -105,4 +128,22 @@ const App = () => {
);
};

const formStyle = StyleSheet.create({
container: {
paddingHorizontal: 16,
},
headerTitle: {
marginVertical: 12,
fontSize: 24,
textAlign: 'center',
},
labelContainer: {
paddingVertical: 8,
},
labelText: {
fontWeight: 'bold',
fontSize: 16,
},
});

export default App;
17 changes: 0 additions & 17 deletions examples/__tests__/App.test.tsx

This file was deleted.

8 changes: 8 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
preset: 'react-native',
setupFilesAfterEnv: ['@testing-library/jest-native/extend-expect'],
transform: {
'^.+\\.tsx?$': 'babel-jest',
},
testMatch: ['**/?(*.)+(test).ts?(x)'],
};
19 changes: 17 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-native-animated-form-stack",
"version": "0.4.1",
"version": "0.4.2",
"description": "A lightweight react-native UI form for sequentially populating form fields with animated effects",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand All @@ -10,19 +10,34 @@
"private": false,
"scripts": {
"prebuild": "rm -rf dist/",
"build": "tsc"
"build": "tsc",
"test": "jest"
},
"devDependencies": {
"@babel/core": "^7.22.15",
"@babel/preset-env": "^7.22.15",
"@babel/preset-typescript": "^7.22.15",
"@size-limit/file": "^8.2.6",
"@testing-library/jest-native": "^5.4.3",
"@testing-library/react-native": "^12.2.2",
"@tsconfig/react-native": "^3.0.2",
"@types/jest": "^29.5.4",
"@types/react": "^18.2.21",
"@types/react-native": "^0.72.2",
"babel-jest": "^29.6.4",
"eslint": "^8.47.0",
"jest": "^29.6.4",
"metro-react-native-babel-preset": "^0.77.0",
"prettier": "^3.0.2",
"react": "^18.2.0",
"react-native": "^0.72.4",
"react-test-renderer": "^18.2.0",
"size-limit": "^8.2.6",
"typescript": "^5.2.2"
},
"peerDependencies": {
"react": "*",
"react-native": "*",
"typescript": "*"
}
}
63 changes: 63 additions & 0 deletions src/FormItemWrapper.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import {render, waitFor} from '@testing-library/react-native';
import {FormItemWrapper} from './FormItemWrapper';
import {View} from 'react-native';

const CHILD_HEIGHT = 20;
const children = <View testID={'children'} style={{height: CHILD_HEIGHT}} />;

describe('Render tests', function () {
test('it should be shown according to change its `visible` props', async function () {
const snapshot = render(
<FormItemWrapper visible={false} onLayout={() => {}}>
{children}
</FormItemWrapper>,
);

expect(snapshot.getByTestId(FormItemWrapper.name).props.style.opacity).toBe(
0,
);

snapshot.rerender(
<FormItemWrapper visible={true} onLayout={() => {}}>
{children}
</FormItemWrapper>,
);

await waitFor(
() => {
expect(
snapshot.getByTestId(FormItemWrapper.name).props.style.opacity,
).toBe(1);
},
{timeout: 500},
);
});
});
describe('handler', function () {
test('`onLayout` should be called with its height', async function () {
const onLayoutMock = jest.fn();
const snapshot = render(
<FormItemWrapper visible={false} onLayout={onLayoutMock}>
{children}
</FormItemWrapper>,
);

const event = {
nativeEvent: {
layout: {
height: CHILD_HEIGHT,
},
},
};

snapshot.getByTestId(FormItemWrapper.name).props.onLayout(event);

await waitFor(
function () {
expect(onLayoutMock).toHaveBeenCalledTimes(1);
expect(onLayoutMock).toHaveBeenCalledWith(event);
},
{timeout: 500},
);
});
});
Loading
Loading