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
2 changes: 2 additions & 0 deletions apps/src/tests/single-feature-tests/tabs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -38,6 +39,7 @@ const scenarios = {
TestTabsTabBarExperimentalUserInterfaceStyle,
TestTabsLifecycleEvents,
TestTabsItemTitle,
TestTabsGeneralAppearance,
};

const TabsScenarioGroup: ScenarioGroup<keyof typeof scenarios> = {
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Comment on lines +3 to +8

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied as its used this way in other tests.

Comment on lines +3 to +8
Comment on lines +3 to +8
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<TabsScreenAppearanceAndroid['tabBarItemLabelVisibilityMode']>;

const LABEL_VISIBILITY_OPTIONS: LabelVisibilityMode[] = [
'auto',
'selected',
'labeled',
'unlabeled',
];

function DefaultTab() {
return (
<View style={styles.screen}>
<Text style={styles.label}>Default configuration</Text>
<Text style={styles.hint}>Tab bar renders in the system default configuration.</Text>
</View>
);
}

function LabelTab() {
const { routeKey, setRouteOptions } = useTabsNavigationContext();
const [labelVisibility, setLabelVisibility] = useState<LabelVisibilityMode>('auto');

const onLabelVisibilityChange = useCallback(
(value: LabelVisibilityMode) => {
setLabelVisibility(value);
setRouteOptions(routeKey, {
android: {
...DEFAULT_TAB_ROUTE_OPTIONS.android,
standardAppearance: {
tabBarItemLabelVisibilityMode: value,
},
},
});
},
[routeKey, setRouteOptions],
);

return (
<View style={styles.screen}>
<Text style={styles.label}>
Label Visibility Mode
</Text>
<Text style={styles.hint}>Only `tabBarItemLabelVisibilityMode` is defined.{'\n'} Labels follow the toggled value.</Text>
<SettingsPicker<LabelVisibilityMode>
label="tabBarItemLabelVisibilityMode"
value={labelVisibility}
onValueChange={onLabelVisibilityChange}
items={LABEL_VISIBILITY_OPTIONS}
/>
</View>
);
}

function RippleTab() {
return (
<View style={styles.screen}>
<Text style={styles.label}>
Ripple Effect
</Text>
<Text style={styles.hint}>
`tabBarItemLabelVisibilityMode`: 'labeled'
{'\n'}`tabBarBackgroundColor`:{' '}
<Text style={{ color: Colors.NavyDark100 }}>NavyDark100</Text>
{'\n'}`tabBarItemRippleColor`:{' '}
<Text style={{ color: Colors.YellowDark100 }}>YellowDark100</Text>
{'\n'}`tabBarItemActiveIndicatorEnabled`: `false`
{'\n'}`tabBarItemActiveIndicatorColor`:{' '}
<Text style={{ color: Colors.GreenLight100 }}>GreenLight100</Text>
{'\n'}
</Text>
</View>
);
}

function IndicatorTab() {
return (
<View style={styles.screen}>
<Text style={styles.label}>
Active Indicator Enabled
</Text>
<Text style={styles.hint}>`tabBarItemLabelVisibilityMode`: 'labeled'
{'\n'}`tabBarBackgroundColor`:{' '}
<Text style={{ color: Colors.PurpleDark100 }}>PurpleDark100</Text>
{'\n'}`tabBarItemRippleColor`:{' '}
<Text style={{ color: Colors.YellowDark100 }}>YellowDark100</Text>
{'\n'}`tabBarItemActiveIndicatorEnabled`: `true`
{'\n'}`tabBarItemActiveIndicatorColor`:{' '}
<Text style={{ color: Colors.GreenLight100 }}>GreenLight100</Text>
{'\n'}
</Text>
</View>
);
}

const ROUTE_CONFIGS: TabRouteConfig[] = [
{
name: 'Default',
Component: DefaultTab,
options: {
title: 'Default',
android: {
...DEFAULT_TAB_ROUTE_OPTIONS.android,
},
},
},
Comment on lines +109 to +119
Comment on lines +109 to +119
Comment on lines +109 to +119
{
name: 'Label',
Component: LabelTab,
options: {
title: 'Label',
android: {
...DEFAULT_TAB_ROUTE_OPTIONS.android,
standardAppearance: {
tabBarItemLabelVisibilityMode: 'auto',
},
},
},
Comment on lines +123 to +131
},
{
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',
},
},
},
Comment on lines +136 to +148
},
{
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',
},
},
},
Comment on lines +153 to +165
},
];

export function App() {
return (
<TabsContainer routeConfigs={ROUTE_CONFIGS} />
);
}

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);
Original file line number Diff line number Diff line change
@@ -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,
};
Comment on lines +3 to +10
Comment thread
LKuchno marked this conversation as resolved.
Comment thread
LKuchno marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -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.
Loading