Skip to content

Commit 7b4b6d6

Browse files
authored
chore(test): test-tabs-tab-bar-experimental-user-interface-style-ios with scenario (#3881)
## Description New screen for experimental_userInterfaceStyle prop (iOS only) for dark and light value with corresponding manual scenario. Closes: software-mansion/react-native-screens-labs#1116 ## Changes - Adding new screen and scenario in new directory to cover experimental_userInterfaceStyle prop set to dark and light - Screen added to tabs/index.ts to be displayed under SFT category
1 parent d556d71 commit 7b4b6d6

3 files changed

Lines changed: 264 additions & 0 deletions

File tree

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import {
2+
TabsContainerWithHostConfigContext,
3+
type TabRouteConfig,
4+
} from '@apps/shared/gamma/containers/tabs';
5+
import React from 'react';
6+
import { Button, View, Text, ScrollView, StyleSheet } from 'react-native';
7+
import { ThemedText } from '@apps/shared';
8+
9+
type InterfaceStyle = 'dark' | 'light';
10+
11+
export function DarkRootScreen({ onPush }: { onPush: () => void }) {
12+
return <RootScreen style="dark" onPush={onPush} />;
13+
}
14+
15+
export function LightRootScreen({ onPush }: { onPush: () => void }) {
16+
return <RootScreen style="light" onPush={onPush} />;
17+
}
18+
19+
function RootScreen({ style, onPush }: { style: InterfaceStyle; onPush: () => void }) {
20+
const isDark = style === 'dark';
21+
return (
22+
<View style={{ flex: 1 }}>
23+
<ScrollView style={{ padding: 40, backgroundColor: isDark ? 'black' : 'white' }}>
24+
<View>
25+
<Text style={styles.sectionHeader}>experimental_userInterfaceStyle</Text>
26+
{isDark ? (
27+
<Text style={styles.description}>
28+
Enable system light mode and observe the tab bar and back button on the pushed screen.
29+
</Text>
30+
) : (
31+
<ThemedText>
32+
Enable system dark mode and observe the tab bar and back button on the pushed screen.
33+
</ThemedText>
34+
)}
35+
<Button title={`Push screen with style: ${style}`} onPress={onPush} />
36+
</View>
37+
</ScrollView>
38+
</View>
39+
);
40+
}
41+
42+
function ThemedTabContent({ style }: { style: InterfaceStyle }) {
43+
const isDark = style === 'dark';
44+
const label = `experimental_userInterfaceStyle: ${style}`;
45+
const description = `This screen forces ${style} interface style regardless of system setting. Observe the tab bar and navigation bar appearance.`;
46+
47+
return (
48+
<View style={styles.centeredScreen}>
49+
{isDark ? (
50+
<>
51+
<Text style={styles.sectionHeader}>{label}</Text>
52+
<Text style={styles.description}>{description}</Text>
53+
</>
54+
) : (
55+
<>
56+
<ThemedText>{label}</ThemedText>
57+
<ThemedText style={{ textAlign: 'center' }}>{description}</ThemedText>
58+
</>
59+
)}
60+
</View>
61+
);
62+
}
63+
64+
export function DarkInterfaceStyleScreen() {
65+
return <InterfaceStyleScreen style="dark" />;
66+
}
67+
68+
export function LightInterfaceStyleScreen() {
69+
return <InterfaceStyleScreen style="light" />;
70+
}
71+
72+
function InterfaceStyleScreen({ style }: { style: InterfaceStyle }) {
73+
const isDark = style === 'dark';
74+
const backgroundColor = isDark ? 'black' : 'white';
75+
const icons = ['house', 'star'] as const;
76+
77+
const routeConfigs: TabRouteConfig[] = icons.map((icon, i) => ({
78+
name: `Tab${i + 1}`,
79+
Component: () => <ThemedTabContent style={style} />,
80+
options: {
81+
title: `Tab${i + 1}`,
82+
ios: {
83+
icon: { type: 'sfSymbol', name: icon },
84+
experimental_userInterfaceStyle: style,
85+
},
86+
style: { backgroundColor },
87+
},
88+
}));
89+
90+
return (
91+
<TabsContainerWithHostConfigContext
92+
routeConfigs={routeConfigs}
93+
nativeContainerStyle={{ backgroundColor }}
94+
/>
95+
);
96+
}
97+
98+
const styles = StyleSheet.create({
99+
sectionHeader: {
100+
fontSize: 13,
101+
fontWeight: '600',
102+
textTransform: 'uppercase',
103+
color: '#888',
104+
letterSpacing: 0.5,
105+
marginTop: 60,
106+
marginBottom: 4,
107+
},
108+
description: {
109+
fontSize: 13,
110+
color: '#555',
111+
marginBottom: 6,
112+
marginTop: 12,
113+
},
114+
centeredScreen: {
115+
flex: 1,
116+
justifyContent: 'center',
117+
alignItems: 'center',
118+
padding: 40,
119+
gap: 12,
120+
},
121+
});
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import React from 'react';
2+
import { Button, StyleSheet, Text, View } from 'react-native';
3+
import { StackContainer, StackRouteConfig, useStackNavigationContext } from '@apps/shared/gamma/containers/stack';
4+
import { LightRootScreen, LightInterfaceStyleScreen, DarkRootScreen, DarkInterfaceStyleScreen } from './ThemeScreen';
5+
import { Scenario } from '@apps/tests/shared/helpers';
6+
7+
const SCENARIO: Scenario = {
8+
name: 'Tab Bar Experimental UIStyle',
9+
key: 'test-tabs-tab-bar-experimental-user-interface-style-ios',
10+
platforms: ['ios'],
11+
AppComponent: App,
12+
};
13+
14+
export default SCENARIO;
15+
16+
function HomeScreen() {
17+
const navigation = useStackNavigationContext();
18+
return (
19+
<View style={styles.container}>
20+
<Text style={styles.title}>experimental_userInterfaceStyle</Text>
21+
<Text style={styles.description}>
22+
Select an interface style to preview how the tab bar and
23+
navigation bar respond.
24+
</Text>
25+
<Button title="Dark" onPress={() => navigation.push('darkRoot')} />
26+
<Button title="Light" onPress={() => navigation.push('lightRoot')} />
27+
</View>
28+
);
29+
}
30+
31+
function DarkRootScreenContent() {
32+
const navigation = useStackNavigationContext();
33+
return <DarkRootScreen onPush={() => navigation.push('darkPushed')} />;
34+
}
35+
36+
function LightRootScreenContent() {
37+
const navigation = useStackNavigationContext();
38+
return <LightRootScreen onPush={() => navigation.push('lightPushed')} />;
39+
}
40+
41+
const ROUTE_CONFIGS: StackRouteConfig[] = [
42+
{ name: 'home', Component: HomeScreen, options: {} },
43+
{ name: 'darkRoot', Component: DarkRootScreenContent, options: {} },
44+
{ name: 'darkPushed', Component: DarkInterfaceStyleScreen, options: {} },
45+
{ name: 'lightRoot', Component: LightRootScreenContent, options: {} },
46+
{ name: 'lightPushed', Component: LightInterfaceStyleScreen, options: {} },
47+
];
48+
49+
export function App() {
50+
return (
51+
<View style={{ flex: 1, width: '100%', height: '100%' }}>
52+
<StackContainer routeConfigs={ROUTE_CONFIGS} />
53+
</View>
54+
);
55+
}
56+
57+
const styles = StyleSheet.create({
58+
container: {
59+
flex: 1,
60+
justifyContent: 'center',
61+
alignItems: 'center',
62+
padding: 40,
63+
gap: 16,
64+
backgroundColor: 'white',
65+
},
66+
title: {
67+
fontSize: 13,
68+
fontWeight: '600',
69+
textTransform: 'uppercase',
70+
color: '#5d5b5b',
71+
letterSpacing: 0.5,
72+
marginBottom: 4,
73+
},
74+
description: {
75+
fontSize: 13,
76+
color: '#555',
77+
textAlign: 'center',
78+
marginBottom: 12,
79+
},
80+
});
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Test Scenario: experimental_userInterfaceStyle
2+
3+
## Details
4+
5+
**Description:** This test validates the experimental_userInterfaceStyle prop by ensuring screens correctly override the system's global appearance. It specifically monitors tab bar UI behavior during pushes, pops, and tab switches to ensure that forced styles (e.g., Dark mode on a Light system) remain stable. The goal is to eliminate visual glitches like flickering, flashing, or momentary reverts to the system theme.
6+
7+
**OS test creation version:** iOS 26.2
8+
9+
## E2E test
10+
11+
Not automated. Verification requires observing UI transitions (Tab Bar appearance and flicker detection) which are not reliably detectable by Detox snapshot or view-hierarchy testing.
12+
13+
## Prerequisites
14+
15+
- iOS device/simulator (use Cmd+Shift+A to toggle appearance on simulator)
16+
17+
## Note
18+
19+
Occasional flashes of the back button in the header are expected behavior and are unrelated to the experimental_userInterfaceStyle prop being tested in this scenario.
20+
21+
## Steps
22+
23+
### Baseline
24+
25+
1. Launch the app and set system appearance to **light**.
26+
27+
- [ ] Expected: App is displayed in light mode.
28+
29+
2. Navigate to the **Tab Bar Experimental UIStyle** screen.
30+
31+
- [ ] Expected: `Home` screen is shown with two buttons `Dark` and `Light`.
32+
33+
### Light system mode & dark screen style
34+
35+
3. On `Home` screen click `Dark` button.
36+
37+
- [ ] Expected: Screen is shown with a black background. A single button to push the screen with dark style is visible.
38+
39+
4. Tap **"Push screen with style: dark"** and observe tab bar.
40+
41+
- [ ] Expected: The dark-styled tab screen is pushed, showing **Tab1** and **Tab2**. The tab bar reflects a **dark** style and appears without flash, flicker, or reversion to light style.
42+
43+
5. Switch between **Tab1** and **Tab2** on the pushed screen.
44+
45+
- [ ] Expected: Both tabs maintain the dark interface style. No flash, flicker, or reversion to light style on tab switch.
46+
47+
1. Pop back to previous `Dark` screen and then pop back to the `Home` screen.
48+
49+
- [ ] Expected: `Home` screen is shown with two buttons `Dark` and `Light`.
50+
51+
### Dark system mode & light screen style
52+
53+
7. Set system appearance to **dark**. Click `Light` button.
54+
55+
- [ ] Expected: Screen is shown with a white background. A single button to push the screen with light style is visible.
56+
57+
8. Push **"Push screen with style: light"** and observe tab bar.
58+
59+
- [ ] Expected: `LightScreen` is pushed. The tab bar reflects a **light** style and appears without flash, flicker, or reversion to light style.
60+
61+
9. Switch between **Tab1** and **Tab2** on the pushed screen.
62+
63+
- [ ] Expected: Both tabs maintain the light interface style. No flash, flicker, or reversion to dark style on tab switch.

0 commit comments

Comments
 (0)