Skip to content

Commit

Permalink
Merge pull request #64 from tttstudios/improvement/typescript-impleme…
Browse files Browse the repository at this point in the history
…ntation

Added typescript implementation and refined the npm package auto-release flow.
  • Loading branch information
BeckyWu220 committed Mar 27, 2020
2 parents 8693ae4 + f77aeb4 commit 006bcff
Show file tree
Hide file tree
Showing 11 changed files with 7,364 additions and 68 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/npmpublish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ jobs:
- uses: actions/setup-node@v1
with:
node-version: 12
- run: npm install
- run: npm test
- run: npm install --only=dev && npm test

publish-npm:
needs: build
Expand All @@ -27,7 +26,8 @@ jobs:
with:
node-version: 12
registry-url: https://registry.npmjs.org/
- run: npm install
- run: npm install --only=dev
- run: npm run build
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{secrets.npm_token}}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,6 @@ buck-out/
# Environment file
.env
ReactotronConfig.js

# ts build dir
dist
2 changes: 1 addition & 1 deletion example/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*/

import React from 'react'
import { View, Text, TextInput, TouchableOpacity, StyleSheet } from 'react-native'
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'

import OTPInputView from '@twotalltotems/react-native-otp-input'

Expand Down
3 changes: 3 additions & 0 deletions helpers/codeToArray.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const codeToArray = (code?: string): string[] => code?.split("") ?? [];

export default { codeToArray }
2 changes: 1 addition & 1 deletion helpers/device.js → helpers/device.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Platform } from 'react-native'

const majorVersionIOS = parseInt(Platform.Version, 10);
const majorVersionIOS = parseInt(String(Platform.Version), 10);
export const isAutoFillSupported = (Platform.OS === 'ios' && majorVersionIOS >= 12)

export default { isAutoFillSupported }
21 changes: 16 additions & 5 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
* created by: Eric Dao, Becky Wu from TTTStudios
*/


declare module '@twotalltotems/react-native-otp-input' {
import * as React from 'react'
import { TextStyle, ViewStyle } from 'react-native';

/**
* Define types of keyboard
Expand All @@ -18,13 +20,13 @@ declare module '@twotalltotems/react-native-otp-input' {
*/
pinCount: number;
/**
* Style of the inuput fields
* Style of the input fields
*/
codeInputFieldStyle?: object;
codeInputFieldStyle?: TextStyle;
/**
* Style of highlighted status for input fields
*/
codeInputHighlightStyle?: object;
codeInputHighlightStyle?: TextStyle;
/**
* Callback function
* Trigger when all fields of the OTP has been filled
Expand Down Expand Up @@ -66,14 +68,23 @@ declare module '@twotalltotems/react-native-otp-input' {
/**
* Style of the OTP container view
*/
style?: object;
style?: ViewStyle;
/**
* The highlight (and cursor on iOS) color of the text input.
*/
selectionColor?: string;
/**
* If inputs are automatically cleared.
*/
clearInputs?: boolean;
}

export interface OTPInputViewState {
digits: string[];
selectedIndex: number;
}

export default class OTPInputView extends React.Component<InputProps, any> {
export default class OTPInputView extends React.Component<InputProps, OTPInputViewState> {

}
}
Expand Down
84 changes: 33 additions & 51 deletions index.js → index.tsx
Original file line number Diff line number Diff line change
@@ -1,57 +1,41 @@
/// <reference path="index.d.ts" />
import { InputProps, OTPInputViewState } from '@twotalltotems/react-native-otp-input';
import React, { Component } from 'react'
import { View, TextInput, TouchableWithoutFeedback, Clipboard, Keyboard, Platform, I18nManager, } from 'react-native'
import PropTypes from 'prop-types'
import { View, TextInput, TouchableWithoutFeedback, Clipboard, Keyboard, Platform, I18nManager, EmitterSubscription, } from 'react-native'
import styles from './styles'
import { isAutoFillSupported } from './helpers/device'
import { codeToArray } from './helpers/codeToArray'

export default class OTPInputView extends Component {
static propTypes = {
pinCount: PropTypes.number,
codeInputFieldStyle: PropTypes.object,
codeInputHighlightStyle: PropTypes.object,
onCodeFilled: PropTypes.func,
onCodeChanged: PropTypes.func,
autoFocusOnLoad: PropTypes.bool,
code: PropTypes.string,
secureTextEntry: PropTypes.bool,
keyboardType: PropTypes.string,
clearInputs: PropTypes.bool,
placeholderCharacter: PropTypes.string,
placeholderTextColor: PropTypes.string,
style: PropTypes.object,
selectionColor: PropTypes.string,
}

static defaultProps = {
export default class OTPInputView extends Component<InputProps, OTPInputViewState> {
static defaultProps: InputProps = {
pinCount: 6,
codeInputFieldStyle: null,
codeInputHighlightStyle: null,
onCodeFilled: null,
autoFocusOnLoad: true,
secureTextEntry: false,
keyboardType: "number-pad",
clearInputs: false,
placeholderCharacter: "",
placeholderTextColor: null,
style: null,
selectionColor: '#000',
}

fields = []
private fields: TextInput[] | null[] = []
private keyboardDidHideListener?: EmitterSubscription;
private timer?: NodeJS.Timeout;
private hasCheckedClipBoard?: boolean;
private clipBoardCode?: string;

constructor(props) {
constructor(props: InputProps) {
super(props)
const { code } = props
this.state = {
digits: (code === undefined ? [] : code.split("")),
digits: codeToArray(code),
selectedIndex: 0,
}
}

componentWillReceiveProps(nextProps) {
UNSAFE_componentWillReceiveProps(nextProps: InputProps) {
const { code } = this.props
if (nextProps.code !== code) {
this.setState({ digits: (nextProps.code === undefined ? [] : nextProps.code.split("")) })
this.setState({ digits: codeToArray(nextProps.code) })
}
}

Expand All @@ -62,18 +46,16 @@ export default class OTPInputView extends Component {
}

componentWillUnmount() {
if (this._timer) {
clearInterval(this._timer)
if (this.timer) {
clearInterval(this.timer)
}
this.keyboardDidHideListener.remove()
this.keyboardDidHideListener?.remove()
}

copyCodeFromClipBoardOnAndroid = () => {
private copyCodeFromClipBoardOnAndroid = () => {
if (Platform.OS === "android") {
this.checkPinCodeFromClipBoard()
this._timer = setInterval(() => {
this.checkPinCodeFromClipBoard()
}, 400)
this.timer = setInterval(this.checkPinCodeFromClipBoard, 400)
}
}

Expand All @@ -92,11 +74,11 @@ export default class OTPInputView extends Component {
return code === undefined ? innerDigits : code.split("")
}

handleKeyboardDidHide = () => {
private handleKeyboardDidHide = () => {
this.blurAllFields()
}

notifyCodeChanged = () => {
private notifyCodeChanged = () => {
const { digits } = this.state
const code = digits.join("")
const { onCodeChanged } = this.props
Expand All @@ -120,11 +102,11 @@ export default class OTPInputView extends Component {
}
this.clipBoardCode = code
this.hasCheckedClipBoard = true
}).catch(e => {
}).catch(() => {
})
}

handleChangeText = (index, text) => {
private handleChangeText = (index: number, text: string) => {
const { onCodeFilled, pinCount } = this.props
const digits = this.getDigits()
let newdigits = digits.slice()
Expand All @@ -140,10 +122,10 @@ export default class OTPInputView extends Component {
}
} else {
text.split("").forEach((value) => {
if(index < pinCount) {
newdigits[index] = value;
index += 1;
}
if(index < pinCount) {
newdigits[index] = value;
index += 1;
}
})
index -= 1
}
Expand All @@ -162,7 +144,7 @@ export default class OTPInputView extends Component {
}
}

handleKeyPressTextInput = (index, key) => {
private handleKeyPressTextInput = (index: number, key: string) => {
const digits = this.getDigits()
if (key === 'Backspace') {
if (!digits[index] && index > 0) {
Expand All @@ -172,17 +154,17 @@ export default class OTPInputView extends Component {
}
}

focusField = (index) => {
focusField = (index: number) => {
if (index < this.fields.length) {
this.fields[index].focus()
(this.fields[index] as TextInput).focus();
this.setState({
selectedIndex: index
})
}
}

blurAllFields = () => {
this.fields.forEach(field => field.blur())
this.fields.forEach((field: TextInput | null) => (field as TextInput).blur())
this.setState({
selectedIndex: -1,
})
Expand All @@ -196,7 +178,7 @@ export default class OTPInputView extends Component {
}
}

renderOneInputField = (_, index) => {
renderOneInputField = (_: TextInput, index: number) => {
const { codeInputFieldStyle, codeInputHighlightStyle, secureTextEntry, keyboardType, selectionColor } = this.props
const { defaultTextFieldStyle } = styles
const { selectedIndex, digits } = this.state
Expand Down
15 changes: 11 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
{
"name": "@twotalltotems/react-native-otp-input",
"version": "1.2.3",
"version": "1.3.0",
"description": "is a tiny JS library for one time passcode (OTP). Supports smart input suggestion on iOS and code autofill on Android (it will be filled when you press the copy button on the SMS notification bar)",
"main": "index.js",
"main": "./dist/index.js",
"types": "./index.d.ts",
"files": ["./dist"],
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"test": "jest"
"test": "jest",
"build": "tsc"
},
"jest": {
"preset": "react-native",
Expand Down Expand Up @@ -48,6 +51,9 @@
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.8.4",
"@types/jest": "^25.1.4",
"@types/react": "^16.9.25",
"@types/react-native": "^0.61.23",
"babel-jest": "^25.1.0",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.2",
Expand All @@ -57,6 +63,7 @@
"jsdom": "^16.1.0",
"react": "16",
"react-dom": "^16.12.0",
"react-native": "^0.61.5"
"react-native": "^0.61.5",
"typescript": "^3.8.3"
}
}
12 changes: 9 additions & 3 deletions styles.js → styles.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { StyleSheet } from 'react-native';
import { StyleSheet, TextStyle } from 'react-native';

export default styles = StyleSheet.create({
interface Styles {
defaultTextFieldStyle: TextStyle;
}

const styles = StyleSheet.create<Styles>({
defaultTextFieldStyle : {
width : 45,
height : 45,
Expand All @@ -10,4 +14,6 @@ export default styles = StyleSheet.create({
textAlign : 'center',
color: 'rgba(226, 226, 226, 1)',
},
})
});

export default styles;
42 changes: 42 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"compilerOptions": {
"declaration": true,
"outDir": "./dist",
"strict": true,
"jsx": "react-native",
"target": "es2019",
"module": "es6",
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"noImplicitAny": true,
"experimentalDecorators": true,
"preserveConstEnums": true,
"sourceMap": true,
"strictNullChecks": true,
"skipDefaultLibCheck": true,
"skipLibCheck": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"importHelpers": false,
"alwaysStrict": true,
"forceConsistentCasingInFileNames": true,
"strictFunctionTypes": true,
"resolveJsonModule": true,
"noFallthroughCasesInSwitch": true,
"strictPropertyInitialization": false,
"lib": ["es6"],
"typeRoots": ["./node_modules/@types"]
},
"exclude": [
"node_modules",
"babel.config.js",
"jest.config.js"
],
"include": [
"./index.tsx",
"./styles.ts",
"./index.d.ts"
],
}
Loading

0 comments on commit 006bcff

Please sign in to comment.