diff --git a/src/components/TouchableRipple/TouchableRipple.native.tsx b/src/components/TouchableRipple/TouchableRipple.native.tsx index 1cb17b4068..2e08ad2b2c 100644 --- a/src/components/TouchableRipple/TouchableRipple.native.tsx +++ b/src/components/TouchableRipple/TouchableRipple.native.tsx @@ -10,7 +10,7 @@ import { ColorValue, } from 'react-native'; -import type { PressableProps } from './Pressable'; +import type { PressableProps, PressableStateCallbackType } from './Pressable'; import { Pressable } from './Pressable'; import { getTouchableRippleColors } from './utils'; import { Settings, SettingsContext } from '../../core/settings'; @@ -33,7 +33,9 @@ export type Props = PressableProps & { onPressOut?: (e: GestureResponderEvent) => void; rippleColor?: ColorValue; underlayColor?: string; - children: React.ReactNode; + children: + | ((state: PressableStateCallbackType) => React.ReactNode) + | React.ReactNode; style?: StyleProp; theme?: ThemeProp; }; @@ -73,6 +75,18 @@ const TouchableRipple = ( underlayColor, }); + const getStyle = (state: unknown) => [ + borderless && styles.overflowHidden, + typeof style === 'function' + ? (style as (state: unknown) => StyleProp)(state) + : style, + ]; + + const getChildren = (state: unknown) => + typeof children === 'function' + ? (children as (state: unknown) => React.ReactNode)(state) + : children; + // A workaround for ripple on Android P is to use useForeground + overflow: 'hidden' // https://github.com/facebook/react-native/issues/6480 const useForeground = @@ -94,24 +108,19 @@ const TouchableRipple = ( {...rest} ref={ref} disabled={disabled} - style={[borderless && styles.overflowHidden, style]} + style={getStyle} android_ripple={androidRipple} > - {React.Children.only(children)} + {getChildren} ); } return ( - - {({ pressed }) => ( + + {(state) => ( <> - {pressed && rippleEffectEnabled && ( + {state.pressed && rippleEffectEnabled && ( )} - {React.Children.only(children)} + {getChildren(state)} )} diff --git a/src/components/TouchableRipple/TouchableRipple.tsx b/src/components/TouchableRipple/TouchableRipple.tsx index ecf848da32..95b8937085 100644 --- a/src/components/TouchableRipple/TouchableRipple.tsx +++ b/src/components/TouchableRipple/TouchableRipple.tsx @@ -286,11 +286,7 @@ const TouchableRipple = ( typeof style === 'function' ? style(state) : style, ]} > - {(state) => - React.Children.only( - typeof children === 'function' ? children(state) : children - ) - } + {(state) => (typeof children === 'function' ? children(state) : children)} ); }; diff --git a/src/components/__tests__/TouchableRipple.test.tsx b/src/components/__tests__/TouchableRipple.test.tsx index c578605b3a..85cdadee1d 100644 --- a/src/components/__tests__/TouchableRipple.test.tsx +++ b/src/components/__tests__/TouchableRipple.test.tsx @@ -42,6 +42,55 @@ describe('TouchableRipple', () => { expect(onPress).not.toHaveBeenCalled(); }); + it('supports children as a function', () => { + const children = ({ pressed }: { pressed: boolean }) => ( + {pressed ? 'Pressed' : 'Button'} + ); + + const { getByText } = render( + {children} + ); + + expect(getByText('Button')).toBeTruthy(); + }); + + it('supports children as an array of nodes', () => { + const { getByText } = render( + + {[Button A, Button B]} + + ); + + expect(getByText('Button A')).toBeTruthy(); + expect(getByText('Button B')).toBeTruthy(); + }); + + it('supports function children returning an array of nodes', () => { + const children = ({ pressed }: { pressed: boolean }) => [ + {pressed ? 'Pressed A' : 'Button A'}, + {pressed ? 'Pressed B' : 'Button B'}, + ]; + + const { getByText } = render( + {children} + ); + + expect(getByText('Button A')).toBeTruthy(); + expect(getByText('Button B')).toBeTruthy(); + }); + + it('supports style as a function', () => { + const style = jest.fn(() => ({ opacity: 0.5 })); + + render( + + Button + + ); + + expect(style).toHaveBeenCalled(); + }); + describe('on iOS', () => { Platform.OS = 'ios';