Skip to content

Commit

Permalink
Release/v0.4.2 (#22)
Browse files Browse the repository at this point in the history
* Fix the animation delay when the first element is exposed (#12)

* Resolve expose issue (#16)

* Resolve expose issue

* Update example

* feature: Configure test env and add tests (#19)

* Configure test env and add tests

* Add an workflow for development

* Edit build pattern

* Edit example (#20)

* v0.4.2
  • Loading branch information
devethan authored Mar 5, 2024
1 parent 538b48f commit 91f3c28
Show file tree
Hide file tree
Showing 16 changed files with 1,256 additions and 162 deletions.
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

0 comments on commit 91f3c28

Please sign in to comment.