-
-
Notifications
You must be signed in to change notification settings - Fork 661
feat(iOS, Tabs): integration with ScrollViewMarker #4191
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
da9650d
e5f2666
adc9727
1561d5c
93ed840
52b1039
43820f1
b2faa93
9cb43ba
108d473
d82e05d
3fc79b9
5375a8e
00e06b8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import type { ScenarioGroup } from '@apps/tests/shared/helpers'; | ||
| import TestSvmTabsScrollEdgeEffects from './test-svm-tabs-scroll-edge-effects'; | ||
| import TestStackSvmTabsSpecialEffects from './test-stack-svm-tabs-special-effects'; | ||
|
|
||
| const scenarios = { | ||
| TestSvmTabsScrollEdgeEffects, | ||
| TestStackSvmTabsSpecialEffects, | ||
| }; | ||
|
|
||
| const TestSvmScenarioGroup: ScenarioGroup<keyof typeof scenarios> = { | ||
| name: 'ScrollViewMarker Integration Tests', | ||
| details: 'Test interaction between ScrollViewMarker and various components', | ||
| scenarios, | ||
| }; | ||
|
|
||
| export default TestSvmScenarioGroup; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| import { createScenario } from '@apps/tests/shared/helpers'; | ||
| import React from 'react'; | ||
| import { ScrollView, StyleSheet, View } from 'react-native'; | ||
| import { scenarioDescription } from './scenario-description'; | ||
| import { | ||
| DEFAULT_TAB_ROUTE_OPTIONS, | ||
| type TabRouteConfig, | ||
| TabsContainer, | ||
| useTabsNavigationContext, | ||
| } from '@apps/shared/gamma/containers/tabs'; | ||
| import { Colors } from '@apps/shared/styling'; | ||
| import { Rectangle } from '@apps/shared/Rectangle'; | ||
| import { ScrollViewMarker } from 'react-native-screens/experimental'; | ||
| import { type ScrollEdgeEffect } from 'react-native-screens'; | ||
| import { | ||
| StackContainer, | ||
| type StackRouteConfig, | ||
| } from '@apps/shared/gamma/containers/stack'; | ||
|
|
||
| export function TestStackSvmTabsSpecialEffects() { | ||
| return <TabsNavigation />; | ||
| } | ||
|
|
||
| const TABS_ROUTE_CONFIGS: TabRouteConfig[] = [ | ||
| { | ||
| name: 'Home', | ||
| Component: TabContents, | ||
| options: { | ||
| ...DEFAULT_TAB_ROUTE_OPTIONS, | ||
| title: 'Home', | ||
| }, | ||
| }, | ||
| { | ||
| name: 'Stack', | ||
| Component: StackTabScreen, | ||
| options: { | ||
| ...DEFAULT_TAB_ROUTE_OPTIONS, | ||
| title: 'Stack', | ||
| }, | ||
| }, | ||
| ]; | ||
|
|
||
| const STACK_ROUTE_CONFIGS: StackRouteConfig[] = [ | ||
| { | ||
| name: 'First', | ||
| Component: StackContents, | ||
| options: {}, | ||
| }, | ||
| { | ||
| name: 'Second', | ||
| Component: StackContents, | ||
| options: {}, | ||
| }, | ||
| ]; | ||
|
|
||
| function TabContents() { | ||
| const edgeEffectStyle: ScrollEdgeEffect = | ||
| useTabsNavigationContext().routeKey === 'Home' ? 'hard' : 'soft'; | ||
|
|
||
| return ( | ||
| <View style={[{ backgroundColor: Colors.BlueLight20 }, styles.fillParent]}> | ||
| <ScrollViewMarker | ||
| style={styles.fillParent} | ||
| scrollEdgeEffects={{ top: 'hidden', bottom: edgeEffectStyle }}> | ||
| <ScrollView | ||
| contentInsetAdjustmentBehavior="automatic" | ||
| style={styles.fillParent}> | ||
| <ScrollViewContents /> | ||
| </ScrollView> | ||
| </ScrollViewMarker> | ||
| </View> | ||
| ); | ||
| } | ||
|
|
||
| function StackContents() { | ||
| return ( | ||
| <View style={[{ backgroundColor: Colors.BlueLight20 }, styles.fillParent]}> | ||
| <HeuristicBreakingView /> | ||
| <ScrollViewMarker | ||
| style={styles.fillParent} | ||
| scrollEdgeEffects={{ top: 'hidden', bottom: 'hard' }}> | ||
| <ScrollView | ||
| contentInsetAdjustmentBehavior="automatic" | ||
| style={styles.fillParent}> | ||
| <ScrollViewContents /> | ||
| </ScrollView> | ||
| </ScrollViewMarker> | ||
| </View> | ||
| ); | ||
| } | ||
|
|
||
| function TabsNavigation() { | ||
| return <TabsContainer routeConfigs={TABS_ROUTE_CONFIGS} />; | ||
| } | ||
|
|
||
| function StackTabScreen() { | ||
| return <StackContainer routeConfigs={STACK_ROUTE_CONFIGS} />; | ||
| } | ||
|
|
||
| function ScrollViewContents(props: { elementCount?: number }) { | ||
| const elementCount = props.elementCount ?? 48; | ||
| return ( | ||
| <> | ||
| {Array.from({ length: elementCount }).map((_, index) => { | ||
| return ( | ||
| <View key={index.toString()} style={[{ width: '100%' }]}> | ||
| <Rectangle | ||
| key={index.toString()} | ||
| color={Colors.RedDark100} | ||
| width={'100%'} | ||
| height={128} | ||
| /> | ||
| <View style={[{ width: '100%', height: 12 }]} /> | ||
| </View> | ||
| ); | ||
| })} | ||
| </> | ||
| ); | ||
| } | ||
|
|
||
| function HeuristicBreakingView() { | ||
| return <View collapsable={false} style={{ width: '100%', height: 0 }}></View>; | ||
| } | ||
|
|
||
| export default createScenario(TestStackSvmTabsSpecialEffects, scenarioDescription); | ||
|
|
||
| const styles = StyleSheet.create({ | ||
| fillParent: { | ||
| flex: 1, | ||
| width: '100%', | ||
| height: '100%', | ||
| }, | ||
| }); | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,12 @@ | ||||||
| import type { ScenarioDescription } from '@apps/tests/shared/helpers'; | ||||||
|
|
||||||
| export const scenarioDescription: ScenarioDescription = { | ||||||
| name: 'SVM in Stack & Tabs - tabs special effects', | ||||||
| key: 'test-stack-svm-tabs-special-effects', | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Im not sure if the name shouldn't be like this:
Suggested change
It's what I assumed from scenario name. If I'm right, please rename also test directory.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no, that would be against naming convention IIRC. |
||||||
| details: | ||||||
| 'Test whether special effects (on tab repetition) are performed correctly in nested container scenario ' + | ||||||
| '(stack in tabs)', | ||||||
| platforms: ['ios', 'android'], | ||||||
| e2eCoverage: 'tbd', | ||||||
| smokeTest: false, | ||||||
| }; | ||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,43 @@ | ||||||||||||||||
| # Test Scenario: SVM in Stack & Tabs - tabs special effects | ||||||||||||||||
|
|
||||||||||||||||
| ## Details | ||||||||||||||||
|
|
||||||||||||||||
| **Description:** | ||||||||||||||||
| This test verifies interaction between `ScrollViewMarker`, `Stack` and `Tabs` components. | ||||||||||||||||
| The primary goal is to verify that the tabs special effects (scroll-to-top, pop-to-top) do work in | ||||||||||||||||
| nested container scenario. | ||||||||||||||||
|
|
||||||||||||||||
| **OS test creation version:** | ||||||||||||||||
| iOS: 26.5, Android: API Level 36. | ||||||||||||||||
|
|
||||||||||||||||
| ## E2E test | ||||||||||||||||
|
|
||||||||||||||||
| TBD | ||||||||||||||||
|
|
||||||||||||||||
| ## Prerequisites | ||||||||||||||||
|
|
||||||||||||||||
| - iOS: simulator with iOS 15+ is enough, | ||||||||||||||||
| - Android: emulator | ||||||||||||||||
|
|
||||||||||||||||
| ## Note | ||||||||||||||||
|
|
||||||||||||||||
| Android part of the code is unimplemented yet, therefore it is expected not to work. | ||||||||||||||||
|
|
||||||||||||||||
| iOS implementation doesn't currently support nested container interaction yet. | ||||||||||||||||
| This test needs to be updated after such interaction is supported. | ||||||||||||||||
|
Comment on lines
+24
to
+27
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Formatting:
Suggested change
|
||||||||||||||||
|
|
||||||||||||||||
| ## Steps | ||||||||||||||||
|
|
||||||||||||||||
| 1. Launch the app and navigate to the **SVM in Stack & Tabs - tabs special effects** screen. | ||||||||||||||||
|
|
||||||||||||||||
| - [ ] There should be two tabs: `Home` and `Stack`. | ||||||||||||||||
| - [ ] `Home` tab should be selected. | ||||||||||||||||
|
|
||||||||||||||||
| 2. Scrolldown a bit. | ||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: scroll down |
||||||||||||||||
|
|
||||||||||||||||
| Doesn't really matter how much you scroll - the distance should be "noticeable". | ||||||||||||||||
|
LKuchno marked this conversation as resolved.
|
||||||||||||||||
|
|
||||||||||||||||
| 3. Press `Home` tab item (repeated tab selection) to trigger the special effect. | ||||||||||||||||
|
|
||||||||||||||||
| - [ ] *scroll-top-top* should be triggered and you should observe the scroll-view scrolling | ||||||||||||||||
| to it's top. | ||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| import { createScenario } from '@apps/tests/shared/helpers'; | ||
| import React from 'react'; | ||
| import { ScrollView, StyleSheet, View } from 'react-native'; | ||
| import { scenarioDescription } from './scenario-description'; | ||
| import { | ||
| DEFAULT_TAB_ROUTE_OPTIONS, | ||
| type TabRouteConfig, | ||
| TabsContainer, | ||
| useTabsNavigationContext, | ||
| } from '@apps/shared/gamma/containers/tabs'; | ||
| import { Colors } from '@apps/shared/styling'; | ||
| import { Rectangle } from '@apps/shared/Rectangle'; | ||
| import { ScrollViewMarker } from 'react-native-screens/experimental'; | ||
| import { type ScrollEdgeEffect } from 'react-native-screens'; | ||
|
|
||
| export function TestSvmTabsScrollEdgeEffects() { | ||
| return <TabsNavigation />; | ||
| } | ||
|
|
||
| const TABS_ROUTE_CONFIGS: TabRouteConfig[] = [ | ||
| { | ||
| name: 'Home', | ||
| Component: TabContents, | ||
| options: { | ||
| ...DEFAULT_TAB_ROUTE_OPTIONS, | ||
| title: 'Home', | ||
| }, | ||
| }, | ||
| { | ||
| name: 'Second', | ||
| Component: TabContents, | ||
| options: { | ||
| ...DEFAULT_TAB_ROUTE_OPTIONS, | ||
| title: 'Second', | ||
| }, | ||
| }, | ||
| ]; | ||
|
|
||
| function TabContents() { | ||
| const edgeEffectStyle: ScrollEdgeEffect = | ||
| useTabsNavigationContext().routeKey === 'Home' ? 'hard' : 'soft'; | ||
|
|
||
| return ( | ||
| <View style={[{ backgroundColor: Colors.BlueLight20 }, styles.fillParent]}> | ||
| <HeuristicBreakingView /> | ||
| <ScrollViewMarker | ||
| style={styles.fillParent} | ||
| scrollEdgeEffects={{ top: 'hidden', bottom: edgeEffectStyle }}> | ||
| <ScrollView | ||
| contentInsetAdjustmentBehavior="automatic" | ||
| style={styles.fillParent}> | ||
| <ScrollViewContents /> | ||
| </ScrollView> | ||
| </ScrollViewMarker> | ||
| </View> | ||
| ); | ||
| } | ||
|
|
||
| function TabsNavigation() { | ||
| return <TabsContainer routeConfigs={TABS_ROUTE_CONFIGS} />; | ||
| } | ||
|
|
||
| function ScrollViewContents(props: { elementCount?: number }) { | ||
| const elementCount = props.elementCount ?? 48; | ||
| return ( | ||
| <> | ||
| {Array.from({ length: elementCount }).map((_, index) => { | ||
| return ( | ||
| <View key={index.toString()} style={[{ width: '100%' }]}> | ||
| <Rectangle | ||
| key={index.toString()} | ||
| color={Colors.RedDark100} | ||
| width={'100%'} | ||
| height={128} | ||
| /> | ||
| <View style={[{ width: '100%', height: 12 }]} /> | ||
| </View> | ||
| ); | ||
| })} | ||
| </> | ||
| ); | ||
| } | ||
|
|
||
| function HeuristicBreakingView() { | ||
| return <View collapsable={false} style={{ width: '100%', height: 0 }}></View>; | ||
| } | ||
|
|
||
| export default createScenario(TestSvmTabsScrollEdgeEffects, scenarioDescription); | ||
|
|
||
| const styles = StyleSheet.create({ | ||
| fillParent: { | ||
| flex: 1, | ||
| width: '100%', | ||
| height: '100%', | ||
| }, | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| import type { ScenarioDescription } from '@apps/tests/shared/helpers'; | ||
|
|
||
| export const scenarioDescription: ScenarioDescription = { | ||
| name: 'SVM in Tabs - scroll edge effects', | ||
| key: 'test-svm-tabs-scroll-edge-effects', | ||
| details: | ||
| 'Test whether scroll edge effects are applied correctly when ScrollViewMarker ' + | ||
| 'is used inside Tabs', | ||
| platforms: ['ios'], | ||
| e2eCoverage: 'tbd', | ||
| smokeTest: false, | ||
| }; |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,43 @@ | ||||||
| # Test Scenario: Integration: SVM in Tabs - scroll edge effects | ||||||
|
|
||||||
| ## Details | ||||||
|
|
||||||
| **Description:** | ||||||
| This test verifies interaction between `ScrollViewMarker` and `Tabs` in scope of scroll-edge-effects. | ||||||
| It allows to test both whether the scroll-edge-effect is correctly applied AND whether it is correctly | ||||||
| updated between different tabs. | ||||||
|
|
||||||
| **OS test creation version:** | ||||||
| iOS 26.5 | ||||||
|
|
||||||
| ## E2E test | ||||||
|
|
||||||
| TBD | ||||||
|
|
||||||
| ## Prerequisites | ||||||
|
|
||||||
| iOS: simulator with iOS 26+ is enough, | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: , |
||||||
|
|
||||||
| ## Note | ||||||
|
|
||||||
| Seemingly the edge effect is applied correctly no matter the integration with the scrollview marker. | ||||||
| Likely UITabBarController uses some different logic to UIViewController.contentScrollView to detect scrollview, | ||||||
| because even when I have had returned nil from the method, the edge effect had still been applied. | ||||||
| Nevertheless, I decided to include this test case here, just to make sure it works. | ||||||
| Also this allows us to test that different ScrollViewMarkers from different tab update the edge effect correctly on tab change. | ||||||
|
|
||||||
| ## Steps | ||||||
|
|
||||||
| 1. Launch the app and navigate to the **SVM in Tabs - scroll edge effects** screen. | ||||||
|
|
||||||
| - [ ] There should be two tabs: `Home` and `Second`. | ||||||
| - [ ] `Home` tab should be selected. | ||||||
| - [ ] `Hard` scroll-edge-effect should be applied (opqaue background of tab bar). | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we can expand a bit on what is expected for
Suggested change
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Dividing line? I haven't noticed such. Will take another look later 😄
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's actually taken from 'hard' scroll-edge-effect documentation I interpret it as visible cutoff tab bar 😅 |
||||||
|
|
||||||
| 2. Change tab to `Second`. | ||||||
|
|
||||||
| - [ ] The scroll-edge-effect should be now changed to `soft` after/during the transition. | ||||||
|
|
||||||
| 3. Change tab back to the `Home`. | ||||||
|
|
||||||
| - [ ] The scroll-edge-effect should be back at `hard`. | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
aren't we missing
exportor is this intentional?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we are coming back to state where we will export only App() (now it should be renamed) and default export createScenario()
#4201