Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
111 commits
Select commit Hold shift + click to select a range
85486eb
Prepare JS component and native spec
t0maboro Apr 24, 2026
ea2ece3
Prepare native component
t0maboro Apr 24, 2026
3eee957
Add yoga layout
t0maboro Apr 24, 2026
48ea740
Attach touch handler to modal hierarchy
t0maboro Apr 24, 2026
91820ae
Reset ShadowNode size, when dismissed
t0maboro Apr 24, 2026
22219a5
Reset modal wrapper size when controller disappears
t0maboro Apr 24, 2026
6f77e7d
Add native support for sheet detents
t0maboro Apr 24, 2026
3f8b6fa
Fix crash when detaching touch handler from modal
t0maboro Apr 24, 2026
8c32a9e
Remove workaround with content subviews layout
t0maboro Apr 24, 2026
6580cdd
Reset shadow node state AFTER completing dismissal from JS
t0maboro Apr 24, 2026
61d2983
Drop Modal prefix from names
t0maboro Apr 28, 2026
03f653c
Add modals directory
t0maboro Apr 28, 2026
36e4227
Move app to dedicated SFT dir
t0maboro Apr 28, 2026
72b381a
Cleanup
t0maboro Apr 28, 2026
3b65176
Cleanup cpp
t0maboro Apr 28, 2026
2640d32
Cleanup JS component
t0maboro Apr 28, 2026
05f350f
Add CIT for nesting stack v5
t0maboro Apr 28, 2026
7eccb93
Update test & add scenario
t0maboro Apr 29, 2026
a8c802e
Prepare SFT
t0maboro Apr 29, 2026
a60ea4f
Rename
t0maboro Apr 29, 2026
d18eea4
Align init logic with other components
t0maboro Apr 29, 2026
5d6a215
Add helper
t0maboro Apr 29, 2026
af178ab
Move EventEmitter to separate class
t0maboro Apr 29, 2026
eb92a0a
Move touchhandler setup to helper
t0maboro Apr 29, 2026
0365d35
Update detents logic
t0maboro Apr 29, 2026
f538805
Add fallback for detents on iOS 15
t0maboro Apr 29, 2026
7d8e1fe
Cleanup
t0maboro Apr 29, 2026
3e1fabc
Use clearColor
t0maboro Apr 29, 2026
26029bd
Optimize
t0maboro Apr 29, 2026
afa3a76
Check for sorting detents
t0maboro Apr 29, 2026
6c45a87
Exclude component from android, until we implement it
t0maboro Apr 29, 2026
84df508
Add fallbacks on Android and Web
t0maboro Apr 29, 2026
be9f198
Add nullable
t0maboro Apr 29, 2026
fca2881
Correction
t0maboro Apr 29, 2026
b303983
Add missing super call
t0maboro Apr 29, 2026
4c3f05a
Add range validation
t0maboro Apr 29, 2026
f0e6d37
Add logs
t0maboro Apr 29, 2026
ec4a983
Run formatter
t0maboro Apr 29, 2026
f8ca2ef
Update imports
t0maboro Apr 29, 2026
bc65aab
Add stubs
t0maboro Apr 29, 2026
1e47580
Update doc
t0maboro Apr 29, 2026
5edc396
Handle NaN and inf as invalid detents
t0maboro Apr 29, 2026
4a89038
Track frame changes instead of size changes
t0maboro Apr 29, 2026
15537a1
Drop readonly
t0maboro Apr 29, 2026
d365740
Drop non-negative size condition
t0maboro Apr 30, 2026
be99377
Don't use ifndef
t0maboro Apr 30, 2026
cc32fc3
Remove Shared alias
t0maboro Apr 30, 2026
2deff1f
Use new instead of alloc-init
t0maboro Apr 30, 2026
47c6319
Add Host to native part and spec
t0maboro Apr 30, 2026
dbdbeec
Move presentation source search to utils
t0maboro Apr 30, 2026
511dcda
Async update for cleanup
t0maboro Apr 30, 2026
376b194
Add comment
t0maboro Apr 30, 2026
bc152dd
Remove filtering out self from provider
t0maboro Apr 30, 2026
765c608
refactor sheetControllerViewDidLayoutSubviews
t0maboro Apr 30, 2026
1e1de08
Add ShadowStateProxy
t0maboro Apr 30, 2026
1e744fa
Remove reactSubviews
t0maboro Apr 30, 2026
e032571
Move section
t0maboro Apr 30, 2026
06ab334
Add assertion
t0maboro Apr 30, 2026
0719857
Preallocate array
t0maboro Apr 30, 2026
c8f8074
Add compile-time check
t0maboro Apr 30, 2026
7e0bfd9
Fallback to large detent when detens were not provided
t0maboro Apr 30, 2026
694cacc
Use iterator
t0maboro Apr 30, 2026
6fb3ad6
Remove isinf
t0maboro Apr 30, 2026
1dac8c8
Remove respondsToSelector calls
t0maboro Apr 30, 2026
3ff927a
Add section
t0maboro Apr 30, 2026
e853d1b
Add ContentView
t0maboro Apr 30, 2026
449740c
Use warnOnce
t0maboro Apr 30, 2026
6846aff
Drop ViewProps from types
t0maboro Apr 30, 2026
2321dac
Rephrase
t0maboro Apr 30, 2026
fa2beff
Improve docs
t0maboro Apr 30, 2026
b8bdb17
Add nullability qualifiers
t0maboro Apr 30, 2026
a960ffb
Add comment
t0maboro Apr 30, 2026
38a70f3
Move search only to presenting branch
t0maboro Apr 30, 2026
69d5b75
Temporary rename to onNativeDismiss
t0maboro Apr 30, 2026
e225d1e
rename
t0maboro Apr 30, 2026
eddce6c
Change key
t0maboro Apr 30, 2026
d4259d1
Move to ios dir
t0maboro Apr 30, 2026
2bf2717
Add step
t0maboro Apr 30, 2026
189b2e5
Rename key
t0maboro Apr 30, 2026
67eb5b6
Calculate contentOriginOffset as a vector from Host to ContentView
t0maboro Apr 30, 2026
2edda2d
Rename
t0maboro May 4, 2026
dcecbe0
Update comment
t0maboro May 5, 2026
df5ffca
Calculate coordinates of both views relatively to window, construct a…
t0maboro May 5, 2026
af8c8d8
Move TC
t0maboro May 5, 2026
7118ba4
Formatting
t0maboro May 5, 2026
bb8786c
Add contentView getter
t0maboro May 5, 2026
ed0fe49
Fix TVOS build
t0maboro May 6, 2026
1e916a1
Add iPadOS mention in scenarios
t0maboro May 6, 2026
110f4d8
Add doc
t0maboro May 6, 2026
5cb187a
Add error log
t0maboro May 6, 2026
5a88c2c
Add mention about the followup
t0maboro May 6, 2026
7fd4f0d
Cleanup
t0maboro May 6, 2026
6c4d190
Update doc
t0maboro May 6, 2026
875fe81
Update scenarios
t0maboro May 6, 2026
4c76849
Update scenario
t0maboro May 6, 2026
7e2e15c
Remove unused imports
t0maboro May 6, 2026
a9fb105
Minor cleanup
t0maboro May 6, 2026
a1ffbf3
Align CIT with naming conventions
t0maboro May 8, 2026
08c87a1
Export all components
t0maboro May 8, 2026
482c7cb
Refine the layout approach - disable touch handling on host & prevent…
t0maboro May 8, 2026
91cd3c8
Rename
t0maboro May 8, 2026
28eef5c
Rename
t0maboro May 8, 2026
909b060
static_cast
t0maboro May 8, 2026
b33ac73
Use contentView getter
t0maboro May 8, 2026
cb1325b
Split method into two
t0maboro May 8, 2026
c70dca6
static_cast
t0maboro May 8, 2026
faf0d58
Move delegate to init block
t0maboro May 8, 2026
ad0f281
Add comment
t0maboro May 8, 2026
358bd12
Revert "Move delegate to init block"
t0maboro May 8, 2026
84ac986
fix
t0maboro May 8, 2026
852af50
add comment
t0maboro May 8, 2026
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
12 changes: 12 additions & 0 deletions apps/src/tests/component-integration-tests/form-sheet/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { ScenarioGroup } from '@apps/tests/shared/helpers';
import TestFormSheetWithNestedStackV5 from './test-form-sheet-stack-v5-nesting-stack-v5-in-form-sheet-ios';

const scenarios = { TestFormSheetWithNestedStackV5 };

const FormSheetScenarioGroup: ScenarioGroup<keyof typeof scenarios> = {
name: 'FormSheet Integration Tests',
details: 'Test interaction between FormSheet and different components',
scenarios,
};

export default FormSheetScenarioGroup;
Comment thread
LKuchno marked this conversation as resolved.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Test name here does not conform with CIT naming convention introduced in https://github.com/software-mansion/react-native-screens-labs/commit/b73db82e771f5b287f09e860a855e7ca339d6414.

Please remember also to update the scenario key.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import React, { useState } from 'react';
import { Button, StyleSheet, Text, View } from 'react-native';
import { FormSheet } from 'react-native-screens/experimental';
import type { ScenarioDescription } from '@apps/tests/shared/helpers';
import { createScenario } from '@apps/tests/shared/helpers';
import { StackContainer } from '@apps/shared/gamma/containers/stack';
import { CenteredLayoutView } from '@apps/shared/CenteredLayoutView';
import { Colors } from '@apps/shared/styling';
import { StackNavigationButtons } from '@apps/tests/shared/components/stack-v5/StackNavigationButtons';

const scenarioDescription: ScenarioDescription = {
name: 'FormSheet with Nested Stack v5',
key: 'test-form-sheet-stack-v5-nesting-stack-v5-in-form-sheet-ios',
details: 'Test nesting Stack v5 inside a FormSheet',
platforms: ['ios'],
};

export function App() {
const [isOpen, setIsOpen] = useState(false);

return (
<View style={styles.container}>
<Text style={styles.title}>FormSheet with nested StackV5</Text>
<Button
title="Open FormSheet"
color={Colors.primary}
onPress={() => setIsOpen(true)}
/>
<FormSheet
isOpen={isOpen}
onNativeDismiss={() => setIsOpen(false)}
detents={[0.6, 1.0]}>
<View style={styles.sheetContent}>
<StackSetup />
</View>
</FormSheet>
</View>
);
}

export function StackSetup() {
return (
<StackContainer
routeConfigs={[
{
name: 'Home',
Component: HomeScreen,
options: {},
},
{
name: 'A',
Component: AScreen,
options: {
headerConfig: { title: 'A' },
},
},
]}
/>
);
}

export function HomeScreen() {
return (
<CenteredLayoutView style={{ backgroundColor: Colors.BlueLight40 }}>
<Text style={styles.screenText}>Home Screen</Text>
<StackNavigationButtons isPopEnabled={false} routeNames={['A']} />
</CenteredLayoutView>
);
}

export function AScreen() {
return (
<CenteredLayoutView style={{ backgroundColor: Colors.YellowLight40 }}>
<Text style={styles.screenText}>Screen A</Text>
<StackNavigationButtons isPopEnabled={true} routeNames={['A']} />
</CenteredLayoutView>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: Colors.offBackground,
},
title: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 20,
color: Colors.text,
},
sheetContent: {
flex: 1,
backgroundColor: Colors.background,
},
screenText: {
color: Colors.text,
fontSize: 20,
fontWeight: 'bold',
textAlign: 'center',
marginBottom: 10,
},
});

export default createScenario(App, scenarioDescription);
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Test Scenario: FormSheet with Nested Stack v5

## Details

**Description:** Verify the layout and state persistence of a `StackContainer` nested within a `FormSheet`. This test ensures that the Stack layout correctly fills the `FormSheet` container, that content remains properly centered, that the layout smoothly adapts when the FormSheet height changes, and that the Stack's navigation state is preserved when the sheet is dismissed and reopened.

**OS test creation version:** iOS: 18.6 and 26.4, iPadOS 26.4

## E2E test

Other: Planned, but will be implemented separately.

## Prerequisites

- iOS device or simulator: iPhone and iPad
- On iPad: Ensure the device is in full-screen mode, regular width, regular height size class

## Note

- On iPad: The FormSheet is presented as a **centered floating panel** with a fixed width, not as a full-width bottom sheet as on iPhone.

## Steps - iPhone

### Baseline

1. Launch the app and navigate to the **FormSheet with Nested Stack v5** screen.

- [ ] Expected: Content with the button "Open FormSheet" is shown

---

### Initialization & Layout Verification

2. Tap the "Open FormSheet" button.

- [ ] Expected: The FormSheet opens at the initial lower detent (0.6). The "Home Screen" text is visible and centered within the sheet. The light blue background completely covers the FormSheet content area.


3. Tap the "Push A" button to push Screen A.

- [ ] Expected: The stack navigates to "Screen A". The "Screen A" text is centered. The light yellow background completely covers the FormSheet content area.

---

### Detent Adaptation

4. Grab the top edge of the FormSheet and swipe up to expand it to the maximum detent (1.0).

- [ ] Expected: The FormSheet expands to take up the maximum available height (respecting the top inset). The layout adapts dynamically - the light yellow background stretches to cover the new full height, and the "Screen A" text dynamically re-centers itself within the newly expanded view area.

---

### State Persistence

5. Swipe down on the FormSheet to dismiss it, then tap the "Open FormSheet" button again.

- [ ] Expected: The FormSheet re-opens at the initial lower detent (0.6). The stack's navigation state has been kept - the sheet immediately displays "Screen A" (with the yellow background and centered text) rather than resetting back to the Home Screen.
Comment thread
LKuchno marked this conversation as resolved.

---

### Pop Action

6. Tap the "Pop" button (or native header back button) to pop Screen A.

- [ ] Expected: The stack correctly navigates back to the "Home Screen". The "Home Screen" text is visible and centered, and the light blue background completely covers the FormSheet content area.

## Steps - iPad

### Baseline

1. Launch the app and navigate to the **FormSheet with Nested Stack v5** screen.

- [ ] Expected: Content with the button "Open FormSheet" is shown.

---

### Initialization & Layout Verification

2. Tap the "Open FormSheet" button.

- [ ] Expected: The FormSheet opens as a centered floating panel at the initial lower detent (0.6). The panel has a fixed width and is horizontally centered on screen. The "Home Screen" text is visible and centered within the panel. The light blue background completely covers the FormSheet content area.

3. Tap the "Push A" button to push Screen A.

- [ ] Expected: The stack navigates to "Screen A". The "Screen A" text is centered within the floating panel. The light yellow background completely covers the FormSheet content area.

---

### Detent Adaptation

4. Grab the top edge of the FormSheet and swipe up to expand it to the maximum detent (1.0).

- [ ] Expected: The FormSheet panel expands vertically to take up the maximum available height (respecting the top inset), while the width remains fixed. The layout adapts dynamically - the light yellow background stretches to cover the new full height, and the "Screen A" text dynamically re-centers itself within the newly expanded panel.

---

### State Persistence

5. Swipe down on the FormSheet to dismiss it, then tap the "Open FormSheet" button again.

- [ ] Expected: The FormSheet re-opens as a centered floating panel at the initial lower detent (0.6). The stack's navigation state has been kept - the panel immediately displays "Screen A" (with the yellow background and centered text) rather than resetting back to the Home Screen.

---

### Pop Action

6. Tap the "Pop" button (or native header back button) to pop Screen A.

- [ ] Expected: The stack correctly navigates back to the "Home Screen". The "Home Screen" text is visible and centered within the panel, and the light blue background completely covers the FormSheet content area.
2 changes: 2 additions & 0 deletions apps/src/tests/component-integration-tests/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ import { ScenarioButton } from '@apps/tests/shared/ScenarioButton';

import OrientationScenarioGroup from './orientation';
import ScrollViewScenarioGroup from './scroll-view';
import FormSheetScenarioGroup from './form-sheet';
import ScenarioSelectionScreen from '@apps/tests/shared/ScenarioScreen';

export const COMPONENT_SCENARIOS = {
Orientation: OrientationScenarioGroup,
ScrollView: ScrollViewScenarioGroup,
FormSheet: FormSheetScenarioGroup,
} as const;

type ParamsList = { [k: keyof typeof COMPONENT_SCENARIOS]: undefined } & {
Expand Down
14 changes: 14 additions & 0 deletions apps/src/tests/single-feature-tests/form-sheet/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { ScenarioGroup } from '@apps/tests/shared/helpers';
import TestFormSheetBase from './test-form-sheet-base-ios';

const scenarios = {
TestFormSheetBase,
};

const FormSheetScenarioGroup: ScenarioGroup<keyof typeof scenarios> = {
name: 'FormSheet',
details: 'Single feature tests for FormSheets',
scenarios,
};

export default FormSheetScenarioGroup;
Comment thread
LKuchno marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React, { useState } from 'react';
import { Button, StyleSheet, Text, View } from 'react-native';
import { FormSheet } from 'react-native-screens/experimental';
import type { ScenarioDescription } from '@apps/tests/shared/helpers';
import { createScenario } from '@apps/tests/shared/helpers';
import { Colors } from '@apps/shared/styling';

const scenarioDescription: ScenarioDescription = {
name: 'Basic functionality',
key: 'test-form-sheet-base-ios',
details: 'Allows to test the basic functionality of FormSheet component.',
platforms: ['ios'],
};

export function App() {
const [isOpen, setIsOpen] = useState(false);

return (
<View style={styles.container}>
<Text style={styles.title}>FormSheet Test</Text>
<Button
title="Open FormSheet"
color={Colors.primary}
onPress={() => setIsOpen(true)}
/>
<FormSheet
isOpen={isOpen}
onNativeDismiss={() => setIsOpen(false)}
detents={[0.6, 1.0]}>
<View style={styles.sheetContent}>
<Text style={styles.sheetTitle}>FormSheet content</Text>
<View style={styles.spacing} />
<Button
title="Dismiss from JS"
color={Colors.primary}
onPress={() => setIsOpen(false)}
/>
</View>
</FormSheet>
</View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: Colors.offBackground,
},
title: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 20,
color: Colors.text,
},
sheetContent: {
flex: 1,
backgroundColor: Colors.background,
padding: 24,
justifyContent: 'center',
alignItems: 'center',
},
sheetTitle: {
fontSize: 22,
fontWeight: '600',
marginBottom: 12,
color: Colors.text,
},
spacing: {
height: 32,
},
});

export default createScenario(App, scenarioDescription);
Loading
Loading