Skip to content

Commit 7f8852b

Browse files
authored
fix: add onTabLongPress for Bottom Navigation (#3758)
1 parent c6359f8 commit 7f8852b

3 files changed

Lines changed: 69 additions & 3 deletions

File tree

src/components/BottomNavigation/BottomNavigation.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,10 @@ export type Props = {
195195
* Function to execute on tab press. It receives the route for the pressed tab, useful for things like scroll to top.
196196
*/
197197
onTabPress?: (props: { route: Route } & TabPressEvent) => void;
198+
/**
199+
* Function to execute on tab long press. It receives the route for the pressed tab, useful for things like custom action when longed pressed.
200+
*/
201+
onTabLongPress?: (props: { route: Route } & TabPressEvent) => void;
198202
/**
199203
* Custom color for icon and label in the active tab.
200204
*/
@@ -335,6 +339,7 @@ const BottomNavigation = ({
335339
sceneAnimationType = 'opacity',
336340
sceneAnimationEasing,
337341
onTabPress,
342+
onTabLongPress,
338343
onIndexChange,
339344
shifting: shiftingProp,
340345
safeAreaInsets,
@@ -580,6 +585,7 @@ const BottomNavigation = ({
580585
labeled={labeled}
581586
animationEasing={sceneAnimationEasing}
582587
onTabPress={handleTabPress}
588+
onTabLongPress={onTabLongPress}
583589
shifting={shifting}
584590
safeAreaInsets={safeAreaInsets}
585591
labelMaxFontSizeMultiplier={labelMaxFontSizeMultiplier}

src/components/BottomNavigation/BottomNavigationBar.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,10 @@ export type Props = {
162162
* Function to execute on tab press. It receives the route for the pressed tab. Use this to update the navigation state.
163163
*/
164164
onTabPress: (props: { route: Route } & TabPressEvent) => void;
165+
/**
166+
* Function to execute on tab long press. It receives the route for the pressed tab
167+
*/
168+
onTabLongPress?: (props: { route: Route } & TabPressEvent) => void;
165169
/**
166170
* Custom color for icon and label in the active tab.
167171
*/
@@ -366,6 +370,7 @@ const BottomNavigationBar = ({
366370
labeled = true,
367371
animationEasing,
368372
onTabPress,
373+
onTabLongPress,
369374
shifting: shiftingProp,
370375
safeAreaInsets,
371376
labelMaxFontSizeMultiplier = 1,
@@ -498,7 +503,7 @@ const BottomNavigationBar = ({
498503
animateToIndex(navigationState.index);
499504
}, [navigationState.index, animateToIndex]);
500505

501-
const handleTabPress = (index: number) => {
506+
const eventForIndex = (index: number) => {
502507
const event = {
503508
route: navigationState.routes[index],
504509
defaultPrevented: false,
@@ -507,7 +512,7 @@ const BottomNavigationBar = ({
507512
},
508513
};
509514

510-
onTabPress(event);
515+
return event;
511516
};
512517

513518
const { routes } = navigationState;
@@ -745,7 +750,8 @@ const BottomNavigationBar = ({
745750
borderless: true,
746751
centered: true,
747752
rippleColor: isV3 ? 'transparent' : touchColor,
748-
onPress: () => handleTabPress(index),
753+
onPress: () => onTabPress(eventForIndex(index)),
754+
onLongPress: () => onTabLongPress?.(eventForIndex(index)),
749755
testID: getTestID({ route }),
750756
accessibilityLabel: getAccessibilityLabel({ route }),
751757
accessibilityRole: Platform.OS === 'ios' ? 'button' : 'tab',

src/components/__tests__/BottomNavigation.test.tsx

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,60 @@ it('calls onIndexChange', () => {
190190
expect(onIndexChange).toHaveBeenCalledTimes(1);
191191
});
192192

193+
it('calls onTabPress', () => {
194+
const onTabPress = jest.fn();
195+
const onIndexChange = jest.fn();
196+
197+
const tree = render(
198+
<BottomNavigation
199+
shifting
200+
onTabPress={onTabPress}
201+
onIndexChange={onIndexChange}
202+
navigationState={createState(0, 5)}
203+
renderScene={({ route }) => route.title}
204+
/>
205+
);
206+
fireEvent(tree.getByText('Route: 1'), 'onPress');
207+
expect(onTabPress).toHaveBeenCalled();
208+
expect(onTabPress).toHaveBeenCalledTimes(1);
209+
expect(onTabPress).toHaveBeenLastCalledWith(
210+
expect.objectContaining({
211+
route: expect.objectContaining({
212+
key: 'key-1',
213+
}),
214+
defaultPrevented: expect.any(Boolean),
215+
preventDefault: expect.any(Function),
216+
})
217+
);
218+
});
219+
220+
it('calls onTabLongPress', () => {
221+
const onTabLongPress = jest.fn();
222+
const onIndexChange = jest.fn();
223+
224+
const tree = render(
225+
<BottomNavigation
226+
shifting
227+
onIndexChange={onIndexChange}
228+
onTabLongPress={onTabLongPress}
229+
navigationState={createState(0, 5)}
230+
renderScene={({ route }) => route.title}
231+
/>
232+
);
233+
fireEvent(tree.getByText('Route: 2'), 'onLongPress');
234+
expect(onTabLongPress).toHaveBeenCalled();
235+
expect(onTabLongPress).toHaveBeenCalledTimes(1);
236+
expect(onTabLongPress).toHaveBeenLastCalledWith(
237+
expect.objectContaining({
238+
route: expect.objectContaining({
239+
key: 'key-2',
240+
}),
241+
defaultPrevented: expect.any(Boolean),
242+
preventDefault: expect.any(Function),
243+
})
244+
);
245+
});
246+
193247
it('renders non-shifting bottom navigation', () => {
194248
const tree = renderer
195249
.create(

0 commit comments

Comments
 (0)