Skip to content
Open
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
6 changes: 6 additions & 0 deletions src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ export type Props = Omit<ViewProps, 'style'> & {
* The number of milliseconds a user must touch the element before executing `onLongPress`.
*/
delayLongPress?: number;
/**
* Delay in ms, from the start of the touch, before the press is engaged and ripple is triggered.
*/
unstable_pressDelay?: number;
/**
* Style of button's inner content.
* Use this prop to apply custom height and width, to set a custom padding or to set the icon on the right with `flexDirection: 'row-reverse'`.
Expand Down Expand Up @@ -179,6 +183,7 @@ const Button = ({
onPressOut,
onLongPress,
delayLongPress,
unstable_pressDelay,
style,
theme: themeOverrides,
uppercase: uppercaseProp,
Expand Down Expand Up @@ -327,6 +332,7 @@ const Button = ({
onPressIn={hasPassedTouchHandler ? handlePressIn : undefined}
onPressOut={hasPassedTouchHandler ? handlePressOut : undefined}
delayLongPress={delayLongPress}
unstable_pressDelay={unstable_pressDelay}
aria-label={ariaLabel}
accessibilityHint={accessibilityHint}
role={role}
Expand Down
5 changes: 4 additions & 1 deletion src/components/TouchableRipple/TouchableRipple.native.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ const TouchableRipple = ({
...rest
}: Props) => {
const theme = useInternalTheme(themeOverrides);
const { rippleEffectEnabled } = React.useContext<Settings>(SettingsContext);
const { rippleEffectEnabled, ripplePressDelay } =
React.useContext<Settings>(SettingsContext);

const { onPress, onLongPress, onPressIn, onPressOut } = rest;

Expand Down Expand Up @@ -90,6 +91,7 @@ const TouchableRipple = ({
return (
<Pressable
{...rest}
unstable_pressDelay={rest.unstable_pressDelay ?? ripplePressDelay}
ref={ref}
disabled={disabled}
style={[useForeground && styles.overflowHidden, style]}
Expand All @@ -103,6 +105,7 @@ const TouchableRipple = ({
return (
<Pressable
{...rest}
unstable_pressDelay={rest.unstable_pressDelay ?? ripplePressDelay}
ref={ref}
disabled={disabled}
style={[borderless && styles.overflowHidden, style]}
Expand Down
4 changes: 3 additions & 1 deletion src/components/TouchableRipple/TouchableRipple.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ const TouchableRipple = ({
typeof calculatedRippleColor === 'string'
? color(calculatedRippleColor).fade(0.5).rgb().string()
: calculatedRippleColor;
const { rippleEffectEnabled } = React.useContext<Settings>(SettingsContext);
const { rippleEffectEnabled, ripplePressDelay } =
React.useContext<Settings>(SettingsContext);

const { onPress, onLongPress, onPressIn, onPressOut } = rest;

Expand Down Expand Up @@ -276,6 +277,7 @@ const TouchableRipple = ({
return (
<Pressable
{...rest}
unstable_pressDelay={rest.unstable_pressDelay ?? ripplePressDelay}
ref={ref}
onPressIn={handlePressIn}
onPressOut={handlePressOut}
Expand Down
13 changes: 13 additions & 0 deletions src/components/__tests__/Button.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,19 @@ it('renders active button if only onLongPress handler is passed', async () => {
expect(screen.getByTestId('active-button')).toBeEnabled();
});

it('renders button with unstable_pressDelay and triggers onPress', async () => {
const onPress = jest.fn();

await render(
<Button testID="delay-button" unstable_pressDelay={100} onPress={onPress}>
Delay Button
</Button>
);

await userEvent.press(screen.getByTestId('delay-button'));
expect(onPress).toHaveBeenCalledTimes(1);
});

it('renders button with color', async () => {
const tree = (
await render(<Button textColor={pink500}>Custom Button</Button>)
Expand Down
31 changes: 31 additions & 0 deletions src/components/__tests__/TouchableRipple.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { GestureResponderEvent } from 'react-native';
import { describe, expect, it, jest } from '@jest/globals';
import { userEvent } from '@testing-library/react-native';

import { SettingsContext } from '../../core/settings';
import { render, screen } from '../../test-utils';
import TouchableRipple from '../TouchableRipple/TouchableRipple.native';

Expand Down Expand Up @@ -44,6 +45,36 @@ describe('TouchableRipple', () => {
expect(onPress).not.toHaveBeenCalled();
});

it('triggers onPress when unstable_pressDelay is provided', async () => {
const onPress = jest.fn<(event: GestureResponderEvent) => void>();
await render(
<TouchableRipple unstable_pressDelay={100} onPress={onPress}>
<Text>Button</Text>
</TouchableRipple>
);

await userEvent.press(screen.getByText('Button'));

expect(onPress).toHaveBeenCalledTimes(1);
});

it('triggers onPress when ripplePressDelay is provided in SettingsContext', async () => {
const onPress = jest.fn<(event: GestureResponderEvent) => void>();
await render(
<SettingsContext.Provider
value={{ rippleEffectEnabled: true, ripplePressDelay: 80 }}
>
<TouchableRipple onPress={onPress}>
<Text>Button</Text>
</TouchableRipple>
</SettingsContext.Provider>
);

await userEvent.press(screen.getByText('Button'));

expect(onPress).toHaveBeenCalledTimes(1);
});

describe('on iOS', () => {
Platform.OS = 'ios';

Expand Down
1 change: 1 addition & 0 deletions src/core/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export type Settings = {
testID,
}: IconProps) => React.ReactNode;
rippleEffectEnabled?: boolean;
ripplePressDelay?: number;
};

export const SettingsContext = React.createContext<Settings>({
Expand Down
Loading