diff --git a/apps/src/tests/single-feature-tests/stack-v5/index.ts b/apps/src/tests/single-feature-tests/stack-v5/index.ts index f8dd9a5e45..426bd4e3fe 100644 --- a/apps/src/tests/single-feature-tests/stack-v5/index.ts +++ b/apps/src/tests/single-feature-tests/stack-v5/index.ts @@ -9,6 +9,7 @@ import TestStackSimpleNav from './test-stack-simple-nav'; import TestStackSubviewsAndroid from './test-stack-subviews-android'; import TestStackSubviewsIOS from './test-stack-subviews-ios'; import TestStackHeaderMenuIOS from './test-stack-header-menu-ios'; +import TestStackHeaderIconIOS from './test-stack-header-icon-ios'; import TestStackBackButton from './test-stack-back-button-android'; import TestStackToolbarMenuCommands from './test-stack-toolbar-menu-commands-android'; import TestStackToolbarMenuDisabled from './test-stack-toolbar-menu-disabled-android'; @@ -29,6 +30,7 @@ export { default as TestStackSimpleNav } from './test-stack-simple-nav'; export { default as TestStackSubviewsAndroid } from './test-stack-subviews-android'; export { default as TestStackSubviewsIOS } from './test-stack-subviews-ios'; export { default as TestStackHeaderMenuIOS } from './test-stack-header-menu-ios'; +export { default as TestStackHeaderIconIOS } from './test-stack-header-icon-ios'; export { default as TestStackBackButton } from './test-stack-back-button-android'; export { default as TestStackToolbarMenuCommands } from './test-stack-toolbar-menu-commands-android'; export { default as TestStackToolbarMenuDisabled } from './test-stack-toolbar-menu-disabled-android'; @@ -46,6 +48,7 @@ const scenarios = { TestStackSubviewsAndroid, TestStackSubviewsIOS, TestStackHeaderMenuIOS, + TestStackHeaderIconIOS, TestStackHeaderSubviewOnPress, TestStackHeaderSelectiveUpdates, TestStackBackButton, diff --git a/apps/src/tests/single-feature-tests/stack-v5/test-stack-header-icon-ios/index.tsx b/apps/src/tests/single-feature-tests/stack-v5/test-stack-header-icon-ios/index.tsx new file mode 100644 index 0000000000..cb64850bdb --- /dev/null +++ b/apps/src/tests/single-feature-tests/stack-v5/test-stack-header-icon-ios/index.tsx @@ -0,0 +1,246 @@ +import React, { useCallback, useLayoutEffect, useMemo, useState } from 'react'; +import { createScenario } from '@apps/tests/shared/helpers'; +import { + StackContainer, + useStackNavigationContext, +} from '@apps/shared/gamma/containers/stack'; +import { StackHeaderConfigProps } from 'react-native-screens/components/gamma/stack/header'; +import { Button, ScrollView, Text, View, StyleSheet } from 'react-native'; +import { scenarioDescription } from './scenario-description'; +import { ToastProvider, useToast } from '@apps/shared'; +import { Colors } from '@apps/shared/styling'; +import { type PlatformIconIOS } from 'react-native-screens'; + +type IconVariant = 'sfSymbol' | 'xcasset' | 'imageSource' | 'templateSource'; + +const ICON_VARIANTS: IconVariant[] = [ + 'sfSymbol', + 'xcasset', + 'imageSource', + 'templateSource', +]; + +function iconForVariant(variant: IconVariant): PlatformIconIOS { + switch (variant) { + case 'sfSymbol': + return { type: 'sfSymbol', name: 'star.fill' }; + case 'xcasset': + return { type: 'xcasset', name: 'custom-icon-fill' }; + case 'imageSource': + return { + type: 'imageSource', + imageSource: require('@assets/search_black.png'), + }; + case 'templateSource': + return { + type: 'templateSource', + templateSource: require('@assets/variableIcons/icon.png'), + }; + } +} + +function nextVariant(current: IconVariant): IconVariant { + const idx = ICON_VARIANTS.indexOf(current); + return ICON_VARIANTS[(idx + 1) % ICON_VARIANTS.length]!; +} + +function buildHeaderConfig( + itemIconVariant: IconVariant, + menuIconVariant: IconVariant, + cycleMenuIcons: () => void, + showToast: (text: string) => void, +): StackHeaderConfigProps { + const itemIcon = iconForVariant(itemIconVariant); + const menuIcon = iconForVariant(menuIconVariant); + + return { + title: 'Header Icons', + ios: { + trailingItems: [ + { + type: 'item', + id: 'icon-item', + title: 'Actions', + icon: itemIcon, + onPress: () => showToast('Item pressed'), + menu: { + type: 'menu', + id: 'main-menu', + onSelectionChange: selection => + showToast('Selected: ' + selection.join(', ')), + children: [ + { + id: 'cycle-action', + type: 'menuItem', + itemType: 'action', + title: `Cycle icons (${menuIconVariant})`, + keepsMenuPresented: true, + onPress: cycleMenuIcons, + }, + { + id: 'toggle-1', + type: 'menuItem', + itemType: 'toggle', + title: 'Toggle 1', + icon: menuIcon, + keepsMenuPresented: true, + }, + { + id: 'toggle-2', + type: 'menuItem', + itemType: 'toggle', + title: 'Toggle 2', + icon: menuIcon, + keepsMenuPresented: true, + }, + { + id: 'toggle-3', + type: 'menuItem', + itemType: 'toggle', + title: 'Toggle 3', + icon: menuIcon, + keepsMenuPresented: true, + }, + { + id: 'submenu', + type: 'menu', + title: 'Submenu', + icon: menuIcon, + children: [ + { + id: 'sub-toggle-1', + type: 'menuItem', + itemType: 'toggle', + title: 'Sub Toggle 1', + icon: menuIcon, + keepsMenuPresented: true, + }, + { + id: 'sub-toggle-2', + type: 'menuItem', + itemType: 'toggle', + title: 'Sub Toggle 2', + icon: menuIcon, + keepsMenuPresented: true, + }, + { + id: 'sub-toggle-3', + type: 'menuItem', + itemType: 'toggle', + title: 'Sub Toggle 3', + icon: menuIcon, + keepsMenuPresented: true, + }, + ], + }, + ], + }, + }, + ], + }, + }; +} + +function ConfigScreen() { + const navigation = useStackNavigationContext(); + const toast = useToast(); + + const [itemIconVariant, setItemIconVariant] = + useState('sfSymbol'); + const [menuIconVariant, setMenuIconVariant] = + useState('sfSymbol'); + + const showToast = useCallback( + (text: string) => { + toast.push({ backgroundColor: Colors.GreenDark120, message: text }); + }, + [toast], + ); + + const cycleMenuIcons = useCallback(() => { + setMenuIconVariant(v => nextVariant(v)); + }, []); + + const { setRouteOptions, routeKey } = navigation; + const headerConfig = useMemo( + () => + buildHeaderConfig( + itemIconVariant, + menuIconVariant, + cycleMenuIcons, + showToast, + ), + [itemIconVariant, menuIconVariant, cycleMenuIcons, showToast], + ); + + useLayoutEffect(() => { + setRouteOptions(routeKey, { + headerConfig, + }); + }, [headerConfig, setRouteOptions, routeKey]); + + return ( + + + Bar Button Item Icon + {itemIconVariant} +