diff --git a/apps/src/tests/single-feature-tests/tabs/index.ts b/apps/src/tests/single-feature-tests/tabs/index.ts index 1b54e880ae..3e35629810 100644 --- a/apps/src/tests/single-feature-tests/tabs/index.ts +++ b/apps/src/tests/single-feature-tests/tabs/index.ts @@ -20,6 +20,7 @@ import TestTabsLifecycleEvents from './test-tabs-lifecycle-events'; import TestTabsItemTitle from './test-tabs-item-title'; import TestTabsSystemItem from './test-tabs-system-item-ios'; import TestTabsGeneralAppearanceNoLiquidGlass from './test-tabs-general-appearance-no-liquid-glass-ios'; +import TestTabsNativeContainerStyle from './test-tabs-native-container-style'; const scenarios = { TestTabBottomAccessory, @@ -42,6 +43,7 @@ const scenarios = { TestTabsItemTitle, TestTabsSystemItem, TestTabsGeneralAppearanceNoLiquidGlass, + TestTabsNativeContainerStyle, }; const TabsScenarioGroup: ScenarioGroup = { diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-native-container-style/index.tsx b/apps/src/tests/single-feature-tests/tabs/test-tabs-native-container-style/index.tsx new file mode 100644 index 0000000000..32fc18c79d --- /dev/null +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-native-container-style/index.tsx @@ -0,0 +1,196 @@ +import { + TabsContainerWithHostConfigContext, + type TabRouteConfig, + useTabsHostConfig, + DEFAULT_TAB_ROUTE_OPTIONS, +} from '@apps/shared/gamma/containers/tabs'; +import React from 'react'; +import { + View, + Text, + ScrollView, + StyleSheet, + type ColorValue, +} from 'react-native'; +import { scenarioDescription } from './scenario-description'; +import { createScenario } from '@apps/tests/shared/helpers'; +import { SettingsPicker } from '@apps/shared'; +import { Colors } from '@apps/shared/styling'; +type ContainerBackgroundOption = 'unset' | 'blue' | 'yellow' | 'purple'; + +function ConfigScreen() { + const { hostConfig, updateHostConfig } = useTabsHostConfig(); + + const getBackgroundColor = ( + option: ContainerBackgroundOption, + ): ColorValue | undefined => { + switch (option) { + case 'blue': + return Colors.BlueLight100; + case 'yellow': + return Colors.YellowLight100; + case 'purple': + return Colors.PurpleLight100; + case 'unset': + default: + return undefined; + } + }; + + const currentOption = (() => { + const bgColor = hostConfig.nativeContainerStyle?.backgroundColor; + if (bgColor === Colors.BlueLight100) return 'blue'; + if (bgColor === Colors.YellowLight100) return 'yellow'; + if (bgColor === Colors.PurpleLight100) return 'purple'; + return 'unset'; + })(); + + return ( + + + nativeContainerStyle + + Controls the background color of the native container. + + + + + + {'•'} On Android: Color is + applied to the FrameLayout that wraps the focused screen and + BottomNavigationView + + + + + + {'•'} On iOS: Color is + applied to the UITabBarController's view + + + + + Background Color + + label="backgroundColor" + value={currentOption} + onValueChange={option => { + updateHostConfig({ + nativeContainerStyle: { + backgroundColor: getBackgroundColor(option), + }, + }); + }} + items={['unset', 'blue', 'yellow', 'purple']} + /> + + + ); +} + +function TabScreen() { + return ( + + Transparent Tab + + Observe the container background color behind the tab content and within the tab bar area. + + + ); +} + +const ROUTE_CONFIGS: TabRouteConfig[] = [ + { + name: 'Config', + Component: ConfigScreen, + options: { + ...DEFAULT_TAB_ROUTE_OPTIONS, + title: 'Config', + ios: { + ...DEFAULT_TAB_ROUTE_OPTIONS.ios, + scrollEdgeAppearance: { + tabBarBackgroundColor: Colors.RedDark100, + }, + standardAppearance: { + tabBarBackgroundColor: Colors.RedDark100, + }, + }, + android: { + ...DEFAULT_TAB_ROUTE_OPTIONS.android, + standardAppearance: { + tabBarBackgroundColor: Colors.RedDark100, + tabBarItemActiveIndicatorEnabled: false, + }, + }, + }, + }, + { + name: 'Transparent', + Component: TabScreen, + options: { + ...DEFAULT_TAB_ROUTE_OPTIONS, + title: 'Transparent', + android: { + ...DEFAULT_TAB_ROUTE_OPTIONS.android, + standardAppearance: { + tabBarBackgroundColor: 'transparent', + tabBarItemActiveIndicatorEnabled: false, + }, + }, + }, + }, +]; + +export function App() { + return ; +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + paddingTop: 18, + }, + content: { + padding: 16, + }, + section: { + marginBottom: 16, + }, + heading: { + fontSize: 18, + fontWeight: '600', + color: Colors.OffNavy, + marginBottom: 8, + }, + description: { + fontSize: 14, + color: Colors.LightOffNavy, + marginBottom: 8, + }, + text: { + fontSize: 13, + color: Colors.LightOffNavy, + lineHeight: 20, + }, + centeredContent: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + paddingHorizontal: 24, + gap: 12, + }, + contentLabel: { + fontSize: 18, + fontWeight: '600', + color: Colors.OffNavy, + textAlign: 'center', + }, + contentHint: { + fontSize: 13, + color: Colors.LightOffNavy, + textAlign: 'center', + lineHeight: 20, + }, +}); + +export default createScenario(App, scenarioDescription); diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-native-container-style/scenario-description.ts b/apps/src/tests/single-feature-tests/tabs/test-tabs-native-container-style/scenario-description.ts new file mode 100644 index 0000000000..6cd382584b --- /dev/null +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-native-container-style/scenario-description.ts @@ -0,0 +1,11 @@ +import type { ScenarioDescription } from '@apps/tests/shared/helpers'; + +export const scenarioDescription: ScenarioDescription = { + name: 'Native Container Style', + key: 'test-tabs-native-container-style', + details: + 'Tests the nativeContainerStyle prop with backgroundColor variations on TabsHost.', + platforms: ['android', 'ios'], + e2eCoverage: 'incomplete', + smokeTest: false, +}; diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-native-container-style/scenario.md b/apps/src/tests/single-feature-tests/tabs/test-tabs-native-container-style/scenario.md new file mode 100644 index 0000000000..06d79b64ca --- /dev/null +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-native-container-style/scenario.md @@ -0,0 +1,110 @@ +# Test Scenario: nativeContainerStyle + +## Details + +**Description:** Verifies the `nativeContainerStyle` prop on `TabsHost`, +which allows customising the background color of the native container +wrapping the tabs. The test validates that setting `backgroundColor` +via the picker applies the color to the correct native surface on each +platform, that the color persists when switching between tabs, and that +restoring `unset` returns the container to the system default. The prop +is a host-level setting: it is not per-route. + +**OS test creation version:** iOS: 18.6 and 26.5, Android: API Level 36. + +## E2E test + +Incomplete: Not automated. Detox cannot assert native background-color +attributes on either platform, so visual verification must be done +manually. + +## Prerequisites + +- iOS device or simulator +- Android emulator or device + +## Note + +- On iOS the color fills the area behind the content screen and around + the tab bar (the full `UITabBarController` view). +- On Android the color fills the `FrameLayout` that wraps currently focused +screen and the `BottomNavigationView`. +- On Android and iOS 18 and earlier, the tab bar may obscure the native container + background color unless a transparent or semi-transparent tab bar background + color is configured. +- On iOS 26, while the "liquid glass" tab bar partially obscures the color, it + remains inherently visible through the material. + +## Steps + +### Baseline + +1. Launch the app and navigate to **Native Container Style**. + +- [ ] The **Config** tab is active. The `backgroundColor` + picker shows `unset`. +- [ ] The container background is the system default (no custom color visible). +- [ ] **Android/iOS 18:** Tab bar background is red. +- [ ] **iOS 26:** The system default color is visible through the liquid glass tab bar. + +--- + +### Setting backgroundColor to blue and yellow + +2. On the **Config** tab, set the `backgroundColor` picker to `blue`. + +- [ ] The picker displays `blue`. The native container's background changes to blue. +- [ ] The blue color is visible behind the tab content area. +- [ ] **Android/iOS 18:** The tab bar retains red color. +- [ ] **iOS 26:** The blue color is visible through the liquid glass tab bar. + +3. Tap the **Transparent** tab in the tab bar. + +- [ ] The **Transparent** screen content is displayed (featuring the "Transparent Tab" label and + hint text). +- [ ] The container background remains blue. +- [ ] **Android/iOS 18:** The tab bar is transparent, making the blue background visible behind the tabs. +- [ ] **iOS 26:** The appearance of the tab bar area is identical to the previous step. + +4. Tap the **Config** tab to switch back. + +- [ ] The **Config** tab is shown again. +- [ ] The blue container background persists. + +5. Set the `backgroundColor` picker to `yellow`. + +- [ ] The yellow color is visible behind the tab content area. +- [ ] **Android/iOS 18:** The tab bar retains red color. +- [ ] **iOS 26:** The yellow color is visible through the liquid glass tab bar. + +6. Tap the **Transparent** tab, observe the background, then return to **Config**. + +- [ ] The container background remains yellow. +- [ ] **Android/iOS 18:** The tab bar is transparent, making the yellow background visible behind the tabs. +- [ ] **iOS 26:** The appearance of the tab bar area is identical to the previous step. + +--- + +### Restoring to unset + +7. Return to **Config** and set the `backgroundColor` picker back to `unset`. + +- [ ] The container background returns to the system default. +- [ ] **Android/iOS 18:** The tab bar retains red color. +- [ ] **iOS 26:** The system default color is visible through the liquid glass tab bar. + +8. Tap the **Transparent** tab and observe the background. + +- [ ] The **Transparent** screen appears with the system-default container background. +- [ ] No color remnant from the previous `yellow` value is visible. + +--- + +### Rapid color cycling (edge case) + +9. From the **Config** tab, cycle the `backgroundColor` picker rapidly + through `blue` → `yellow` → `purple` → `unset` → `blue`. + +- [ ] The container background updates with each selection. +- [ ] No crash, no layout freeze, and no color bleed between selections. +- [ ] The final displayed color is blue.