diff --git a/apps/src/tests/single-feature-tests/tabs/index.ts b/apps/src/tests/single-feature-tests/tabs/index.ts index 223193ef06..5c028c5ec2 100644 --- a/apps/src/tests/single-feature-tests/tabs/index.ts +++ b/apps/src/tests/single-feature-tests/tabs/index.ts @@ -18,6 +18,7 @@ import TestTabsSpecialEffectsScrollToTop from './test-tabs-special-effects-scrol import TestTabsTabBarExperimentalUserInterfaceStyle from './test-tabs-tab-bar-experimental-user-interface-style-ios'; import TestTabsLifecycleEvents from './test-tabs-lifecycle-events'; import TestTabsItemTitle from './test-tabs-item-title'; +import TestTabsGeneralAppearance from './test-tabs-general-appearance-android'; const scenarios = { TestTabBottomAccessory, @@ -38,6 +39,7 @@ const scenarios = { TestTabsTabBarExperimentalUserInterfaceStyle, TestTabsLifecycleEvents, TestTabsItemTitle, + TestTabsGeneralAppearance, }; const TabsScenarioGroup: ScenarioGroup = { diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-general-appearance-android/index.tsx b/apps/src/tests/single-feature-tests/tabs/test-tabs-general-appearance-android/index.tsx new file mode 100644 index 0000000000..d655393eca --- /dev/null +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-general-appearance-android/index.tsx @@ -0,0 +1,196 @@ +import React, { useCallback, useState } from 'react'; +import { StyleSheet, Text, View } from 'react-native'; +import { + TabsContainer, + useTabsNavigationContext, + DEFAULT_TAB_ROUTE_OPTIONS, + type TabRouteConfig, +} from '@apps/shared/gamma/containers/tabs'; +import { createScenario } from '@apps/tests/shared/helpers'; +import { scenarioDescription } from './scenario-description'; +import { Colors } from '@apps/shared/styling'; +import { SettingsPicker } from '@apps/shared'; +import type { TabsScreenAppearanceAndroid } from 'react-native-screens'; + +type LabelVisibilityMode = NonNullable; + +const LABEL_VISIBILITY_OPTIONS: LabelVisibilityMode[] = [ + 'auto', + 'selected', + 'labeled', + 'unlabeled', +]; + +function DefaultTab() { + return ( + + Default configuration + Tab bar renders in the system default configuration. + + ); +} + +function LabelTab() { + const { routeKey, setRouteOptions } = useTabsNavigationContext(); + const [labelVisibility, setLabelVisibility] = useState('auto'); + + const onLabelVisibilityChange = useCallback( + (value: LabelVisibilityMode) => { + setLabelVisibility(value); + setRouteOptions(routeKey, { + android: { + ...DEFAULT_TAB_ROUTE_OPTIONS.android, + standardAppearance: { + tabBarItemLabelVisibilityMode: value, + }, + }, + }); + }, + [routeKey, setRouteOptions], + ); + + return ( + + + Label Visibility Mode + + Only `tabBarItemLabelVisibilityMode` is defined.{'\n'} Labels follow the toggled value. + + label="tabBarItemLabelVisibilityMode" + value={labelVisibility} + onValueChange={onLabelVisibilityChange} + items={LABEL_VISIBILITY_OPTIONS} + /> + + ); +} + +function RippleTab() { + return ( + + + Ripple Effect + + + `tabBarItemLabelVisibilityMode`: 'labeled' + {'\n'}`tabBarBackgroundColor`:{' '} + NavyDark100 + {'\n'}`tabBarItemRippleColor`:{' '} + YellowDark100 + {'\n'}`tabBarItemActiveIndicatorEnabled`: `false` + {'\n'}`tabBarItemActiveIndicatorColor`:{' '} + GreenLight100 + {'\n'} + + + ); +} + +function IndicatorTab() { + return ( + + + Active Indicator Enabled + + `tabBarItemLabelVisibilityMode`: 'labeled' + {'\n'}`tabBarBackgroundColor`:{' '} + PurpleDark100 + {'\n'}`tabBarItemRippleColor`:{' '} + YellowDark100 + {'\n'}`tabBarItemActiveIndicatorEnabled`: `true` + {'\n'}`tabBarItemActiveIndicatorColor`:{' '} + GreenLight100 + {'\n'} + + + ); +} + +const ROUTE_CONFIGS: TabRouteConfig[] = [ + { + name: 'Default', + Component: DefaultTab, + options: { + title: 'Default', + android: { + ...DEFAULT_TAB_ROUTE_OPTIONS.android, + }, + }, + }, + { + name: 'Label', + Component: LabelTab, + options: { + title: 'Label', + android: { + ...DEFAULT_TAB_ROUTE_OPTIONS.android, + standardAppearance: { + tabBarItemLabelVisibilityMode: 'auto', + }, + }, + }, + }, + { + name: 'Ripple', + Component: RippleTab, + options: { + title: 'Ripple', + android: { + ...DEFAULT_TAB_ROUTE_OPTIONS.android, + standardAppearance: { + tabBarBackgroundColor: Colors.NavyDark100, + tabBarItemRippleColor: Colors.YellowDark100, + tabBarItemActiveIndicatorEnabled: false, + tabBarItemActiveIndicatorColor: Colors.GreenLight100, + tabBarItemLabelVisibilityMode: 'labeled', + }, + }, + }, + }, + { + name: 'Indicator', + Component: IndicatorTab, + options: { + title: 'Indicator', + android: { + ...DEFAULT_TAB_ROUTE_OPTIONS.android, + standardAppearance: { + tabBarBackgroundColor: Colors.PurpleDark100, + tabBarItemRippleColor: Colors.YellowDark100, + tabBarItemActiveIndicatorEnabled: true, + tabBarItemActiveIndicatorColor: Colors.GreenLight100, + tabBarItemLabelVisibilityMode: 'labeled', + }, + }, + }, + }, +]; + +export function App() { + return ( + + ); +} + +const styles = StyleSheet.create({ + screen: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + padding: 24, + gap: 12, + }, + label: { + fontSize: 17, + fontWeight: '600', + textAlign: 'center', + }, + hint: { + 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-general-appearance-android/scenario-description.ts b/apps/src/tests/single-feature-tests/tabs/test-tabs-general-appearance-android/scenario-description.ts new file mode 100644 index 0000000000..273c564de8 --- /dev/null +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-general-appearance-android/scenario-description.ts @@ -0,0 +1,10 @@ +import type { ScenarioDescription } from '@apps/tests/shared/helpers'; + +export const scenarioDescription: ScenarioDescription = { + name: 'Tab Bar General Appearance', + key: 'test-tabs-general-appearance-android', + details: 'Tests Android tab bar appearance: default system rendering, label visibility, ripple effect, and active indicator.', + platforms: ['android'], + e2eCoverage: 'incomplete', + smokeTest: false, +}; diff --git a/apps/src/tests/single-feature-tests/tabs/test-tabs-general-appearance-android/scenario.md b/apps/src/tests/single-feature-tests/tabs/test-tabs-general-appearance-android/scenario.md new file mode 100644 index 0000000000..439f9d580f --- /dev/null +++ b/apps/src/tests/single-feature-tests/tabs/test-tabs-general-appearance-android/scenario.md @@ -0,0 +1,140 @@ +# Test Scenario: Tab Bar General Appearance (Android) + +## Details + +**Description:** Verifies Android tab bar appearance behaviors, +specifically checking native system defaults, dynamic text label visibility rules, +and the interaction between the touch-ripple animation and the persistent active +indicator shape. + +**OS test creation version:** Android: API Level 36. + +## E2E test + +Incomplete: Not automated. Detox does not have access to color or visibility +attributes on Android views, so it is not possible to programmatically assert whether +a label is hidden or a specific color value has been applied. + +## Prerequisites + +- Android emulator or physical device. + +## Note + +- This scenario is **Android-only**. All props under test are configured + only for the Android platform. +- The four tabs (Default, Label, Ripple, Indicator) are independent; + changes on one tab do not affect the others. +- The Label tab picker resets to `auto` on every fresh launch. +- The Ripple and Indicator tabs are static — they have no interactive + controls. Verification is purely visual. +- The ripple (`tabBarItemRippleColor`) is a **transient** touch-feedback + color seen only while pressing or holding a tab item. It is distinct + from the active indicator (`tabBarItemActiveIndicatorColor`), which + is a **persistent** pill visible behind the selected tab icon. + +## Steps + +### Default tab + +1. Launch the app and navigate to the **Tab Bar General Appearance** screen. + +- [ ] The **Default** tab is selected and its content ("Default configuration") +is visible. +- [ ] Four tabs are shown in the tab bar - selected tab has visible +title ("Default"), for other tabs only icons are shown in the tab bar. +- [ ] The tab bar renders in the system default colors with no appearance +overrides applied. + +--- + +### Label tab — tabBarItemLabelVisibilityMode + +2. Tap the second tab from the left side. + +- [ ] The Label tab content ("Label Visibility Mode") is visible. +- [ ] The `tabBarItemLabelVisibilityMode` picker shows `auto`. +- [ ] Tab bar renders labels using the system default behavior - only selected +tab title is visible. + +3. In the picker, select `labeled`. + +- [ ] Labels for all four tabs are shown. + +4. Tap a **Default** tab and observe the tab bar. + +- [ ] "Default" becomes selected and its label is now + shown. The labels for other tabs are no longer visible. + +5. Tap the second tab again. + +- [ ] "Label" tab is selected, label for all tabs reappears. + +6. In the picker, select `selected`. + +- [ ] Only the currently selected tab ("Label") shows + its label. Labels for the other three tabs are hidden. + +7. In the picker, select `unlabeled`. + +- [ ] Labels for all four tabs are hidden; only icons + are shown in the tab bar. + +8. Cycle through all four values (`auto` → `selected` → `labeled` + → `unlabeled`) in quick succession. + +- [ ] The tab bar updates immediately after each change with no crash or layout freeze. +- [ ] At the end, labels for all tabs are hidden (unlabeled). + +--- + +### Ripple tab — transient ripple color (indicator disabled) + +9. Tap the **Ripple** tab (third from left). + +- [ ] The Ripple tab content ("Ripple Effect") is visible. +- [ ] The tab bar background changes to dark navy. +- [ ] Labels are shown for all tabs. +- [ ] No persistent indicator pill is visible behind tab icon. + +10. Press and hold the **Default** tab item in the tab bar for + approximately one second, then release. + +- [ ] While pressing, a transient ripple expands from + the touch point in yellow. +- [ ] The ripple fades and disappears after release. + +11. Tap the **Label** tab, then the **Ripple** tab. + +- [ ] When Label tab is selected, labels for all tabs are hidden. +- [ ] While selecting Ripple tab, a ripple effect is visible as an +unconstrained yellow circle that expands and smoothly fades outward. + +--- + +### Indicator tab — persistent active indicator + ripple + +12. Tap the **Indicator** tab. + +- [ ] The Indicator tab content ("Active Indicator Enabled") is visible. +- [ ] The tab bar background changes to purple. +- [ ] Labels are shown for all tabs. +- [ ] A persistent green pill-shaped indicator is visible behind the "Indicator" +tab icon in the tab bar. + +13. Tap the **Default** tab, then the **Indicator** tab. + +- [ ] The yellow ripple effect is visible on the Indicator tab item, +contained within the pill-shaped active indicator frame. +- [ ] After the ripple animation ends, the indicator color remains green. + +14. Tap the **Indicator** tab. Then press and hold the **Default** + tab item for approximately one second, then release. + +- [ ] While pressing, a transient yellow ripple is visible. +- [ ] The ripple is contained within the pill-shaped indicator frame. + +15. Rapidly tap between **Default**, **Label**, and **Ripple** tabs + several times. + +- [ ] Per-tab tab bar custom appearance configurations are applied correctly while switching tabs.