Example:
<AutoLink linkStyle={ styles.textLink } text='this is a phone number 9999999999' />
| // @flow | |
| import React from 'react'; | |
| import { Linking, Text } from 'react-native'; | |
| import { parsePhoneNumber, type TextChild } from 'utils/strings'; | |
| type Props = { | |
| linkStyle: PassedStyle, | |
| text: string, | |
| }; | |
| class AutoLink extends React.PureComponent<Props> { | |
| render(): Text { | |
| const { text } = this.props; | |
| return ( | |
| <Text { ...this.props }> | |
| {typeof text !== 'string' ? text : this.getParsedText()} | |
| </Text> | |
| ); | |
| } | |
| getParsedText(): Array<Text> { | |
| const { linkStyle, text } = this.props; | |
| const parsed = parsePhoneNumber({ | |
| text, | |
| linkProps: { | |
| onPress: this.handlePress, | |
| style: linkStyle, | |
| }, | |
| }); | |
| return parsed.map((props: TextChild, index: number): Text => ( | |
| <Text key={ `parsedText-${index}` } { ...props } /> | |
| )); | |
| } | |
| handlePress = (number: string) => { | |
| Linking.openURL(`tel:${number}`); | |
| }; | |
| } | |
| export default AutoLink; |
Example:
<AutoLink linkStyle={ styles.textLink } text='this is a phone number 9999999999' />
| // @flow | |
| import { type TextProps } from 'react-native'; | |
| const textWithPhoneNumberRegex = /([\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,7})/; | |
| export type TextChild = { | |
| onPress?: Function, | |
| } & TextProps; | |
| export const parsePhoneNumber = ({ | |
| linkProps: { onPress, ...otherLinkProps } = {}, | |
| text, | |
| }: { | |
| linkProps: TextChild, | |
| text: string, | |
| }): Array<TextChild> => { | |
| const parseFraction = ( | |
| textLeft: string, | |
| previousFraction: ?Array<TextChild>, | |
| ): Array<TextChild> => { | |
| const matches = textWithPhoneNumberRegex.exec(textLeft); | |
| const fraction = previousFraction || []; | |
| if (!matches) { | |
| return [...fraction, { children: textLeft }].filter( | |
| (child: TextChild): boolean => !!child.children, | |
| ); | |
| } | |
| fraction.push({ children: textLeft.substr(0, matches.index) }); | |
| fraction.push({ | |
| children: matches[0], | |
| onPress: (): void => onPress && onPress(matches[0]), | |
| ...otherLinkProps, | |
| }); | |
| return parseFraction( | |
| textLeft.substr(matches.index + matches[0].length), | |
| fraction, | |
| ); | |
| }; | |
| return parseFraction(text); | |
| }; |
| import { parsePhoneNumber } from './strings'; | |
| describe('parsePhoneNumber', () => { | |
| const linkProps = { | |
| onPress: jest.fn(), | |
| otherProp: 'test', | |
| }; | |
| const receivedLinkProps = { | |
| ...linkProps, | |
| onPress: expect.any(Function), | |
| }; | |
| test('validates messages', () => { | |
| expect(parsePhoneNumber({ text: 'Lorem ipsum' })).toEqual([ | |
| { children: 'Lorem ipsum' }, | |
| ]); | |
| expect( | |
| parsePhoneNumber({ | |
| text: 'this is a normal number 99999', | |
| linkProps, | |
| }), | |
| ).toEqual([{ children: 'this is a normal number 99999' }]); | |
| expect( | |
| parsePhoneNumber({ | |
| text: 'but this is a phone number 9999999999', | |
| linkProps, | |
| }), | |
| ).toEqual([ | |
| { children: 'but this is a phone number ' }, | |
| { children: '9999999999', ...receivedLinkProps }, | |
| ]); | |
| expect( | |
| parsePhoneNumber({ | |
| text: '929-222-3456 92921346544 +556192334456', | |
| linkProps, | |
| }), | |
| ).toEqual([ | |
| { children: '929-222-3456', ...receivedLinkProps }, | |
| { children: ' ' }, | |
| { children: '92921346544', ...receivedLinkProps }, | |
| { children: ' ' }, | |
| { children: '+556192334456', ...receivedLinkProps }, | |
| ]); | |
| }); | |
| }); |