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
5 changes: 5 additions & 0 deletions .changeset/tidy-unicode-tabs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'react-native-bottom-tabs': patch
---

Fix an iOS Fabric crash when selecting or long-pressing a tab whose key contains non-ASCII characters.
19 changes: 19 additions & 0 deletions packages/example-shared/e2e/unicode-tab-key.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
appId: ${APP_ID}
name: Press and long-press tabs with Unicode keys
---
- launchApp
- scrollUntilVisible:
element:
text: ^Native Bottom Tabs with Unicode keys$
direction: DOWN
timeout: 15000
- tapOn:
text: ^Native Bottom Tabs with Unicode keys$
- assertVisible: Press me
- tapOn:
text: ^アルバム$
- longPressOn:
text: ^기사$
- tapOn:
text: ^Чат$
- assertVisible: Press me
5 changes: 5 additions & 0 deletions packages/example-shared/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import SFSymbols from './Examples/SFSymbols';
import Roles from './Examples/Roles';
import LabeledTabs from './Examples/Labeled';
import NativeBottomTabs from './Examples/NativeBottomTabs';
import NativeBottomTabsUnicode from './Examples/NativeBottomTabsUnicode';
import TintColorsExample from './Examples/TintColors';
import NativeBottomTabsEmbeddedStacks from './Examples/NativeBottomTabsEmbeddedStacks';
import NativeBottomTabsSVGs from './Examples/NativeBottomTabsSVGs';
Expand Down Expand Up @@ -199,6 +200,10 @@ const examples = [
name: 'Native Bottom Tabs with original icons',
},
{ component: NativeBottomTabs, name: 'Native Bottom Tabs' },
{
component: NativeBottomTabsUnicode,
name: 'Native Bottom Tabs with Unicode keys',
},
{ component: JSBottomTabs, name: 'JS Bottom Tabs' },
{
component: JSBottomTabs,
Expand Down
100 changes: 100 additions & 0 deletions packages/example-shared/src/Examples/NativeBottomTabsUnicode.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { Article } from '../Screens/Article';
import { Albums } from '../Screens/Albums';
import { Contacts } from '../Screens/Contacts';
import { Chat } from '../Screens/Chat';
import { createNativeBottomTabNavigator } from '@bottom-tabs/react-navigation';
import { Platform } from 'react-native';
import { useState } from 'react';

const Tab = createNativeBottomTabNavigator();

function NativeBottomTabsUnicode() {
const [label, setLabel] = useState('기사');
return (
<Tab.Navigator
initialRouteName="Чат"
labeled={true}
hapticFeedbackEnabled={false}
layoutDirection="ltr"
tabBarInactiveTintColor="#C57B57"
tabBarActiveTintColor="#F7DBA7"
tabBarStyle={{
backgroundColor: '#1E2D2F',
}}
rippleColor="#F7DBA7"
tabLabelStyle={{
fontFamily: 'Avenir',
fontSize: 15,
}}
activeIndicatorColor="#041F1E"
screenListeners={{
tabLongPress: (data) => {
console.log(
`${Platform.OS}: Long press detected on tab with key ${data.target} at the navigator level.`
);
},
}}
>
<Tab.Screen
name="기사"
component={Article}
listeners={{
tabLongPress: (data) => {
console.log(
`${Platform.OS}: Long press detected on tab with key ${data.target} at the screen level.`
);
setLabel('새 기사');
},
}}
options={{
tabBarButtonTestID: 'articleTestID',
tabBarBadge: '10',
tabBarLabel: label,
tabBarIcon: ({ focused }) =>
focused
? require('../../assets/icons/person_dark.png')
: require('../../assets/icons/article_dark.png'),
}}
/>
<Tab.Screen
name="アルバム"
component={Albums}
options={{
tabBarButtonTestID: 'albumsTestID',
tabBarIcon: () => require('../../assets/icons/grid_dark.png'),
}}
/>
<Tab.Screen
name="Répertoire"
component={Contacts}
listeners={{
tabPress: () => {
console.log('Contacts tab press prevented');
},
}}
options={{
tabBarButtonTestID: 'contactsTestID',
tabBarIcon: () => require('../../assets/icons/person_dark.png'),
tabBarActiveTintColor: 'yellow',
preventsDefault: true,
}}
/>
<Tab.Screen
name="Чат"
component={Chat}
listeners={{
tabPress: () => {
console.log('Chat tab pressed');
},
}}
options={{
tabBarButtonTestID: 'chatTestID',
tabBarIcon: () => require('../../assets/icons/chat_dark.png'),
tabBarActiveTintColor: 'white',
}}
/>
</Tab.Navigator>
);
}

export default NativeBottomTabsUnicode;
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ - (void)onPageSelectedWithKey:(NSString *)key reactTag:(NSNumber *)reactTag {
auto eventEmitter = std::static_pointer_cast<const RNCTabViewEventEmitter>(_eventEmitter);
if (eventEmitter) {
eventEmitter->onPageSelected(RNCTabViewEventEmitter::OnPageSelected{
.key = [key cStringUsingEncoding:kCFStringEncodingUTF8]
.key = std::string([key UTF8String] ?: "")
});
}
}
Expand All @@ -274,7 +274,7 @@ - (void)onLongPressWithKey:(NSString *)key reactTag:(NSNumber *)reactTag {
auto eventEmitter = std::static_pointer_cast<const RNCTabViewEventEmitter>(_eventEmitter);
if (eventEmitter) {
eventEmitter->onTabLongPress(RNCTabViewEventEmitter::OnTabLongPress {
.key = [key cStringUsingEncoding:kCFStringEncodingUTF8]
.key = std::string([key UTF8String] ?: "")
});
}
}
Expand Down
Loading