Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
1b19a54
feat: add an accessible dismissal path for modal sheets
falcondpr Jul 8, 2026
0866fe4
fix(android): render the scrim as a real child view
DanyKrk Sep 14, 2026
64ecdf7
fix(android): route close requests to the visual top portal
DanyKrk Sep 18, 2026
a5cace7
test(android): cover dismissal from focused sheet content
DanyKrk Sep 18, 2026
d0b0155
fix(android): isolate portal modal accessibility
DanyKrk Sep 18, 2026
ee505e8
fix(android): preserve portal accessibility lifecycle
DanyKrk Sep 19, 2026
7cfb032
test(android): mount portal fixtures in React roots
DanyKrk Sep 19, 2026
a998561
fix(android): make stacked portal order provable
DanyKrk Sep 19, 2026
693617a
feat(example): add stacked portal accessibility demo
DanyKrk Sep 19, 2026
a687efb
test(ios): add native XCTest harness
DanyKrk Sep 20, 2026
d6ff725
fix(ci): declare Bun test types
DanyKrk Sep 20, 2026
efea656
feat(ios): coordinate presentation ownership per window
DanyKrk Sep 21, 2026
47aeb43
fix(ios): align accessible dismissal with closing state
DanyKrk Sep 21, 2026
a18c33a
fix(ios): route VoiceOver Escape to top presentation
DanyKrk Sep 21, 2026
bb97de8
fix(ios): isolate accessibility to top presentation
DanyKrk Sep 22, 2026
c015b90
fix(ios): complete presentation ownership lifecycle
DanyKrk Sep 22, 2026
f935346
Merge remote-tracking branch 'origin/main' into feat/accessible-sheet…
falcondpr Sep 24, 2026
50a95b9
fix(ios): isolate VoiceOver to top presentation path
DanyKrk Sep 24, 2026
8b26ac1
feat(example): expand portal accessibility validation
DanyKrk Sep 24, 2026
1827eb2
Merge remote-tracking branch 'pr-56/feat/accessible-sheet-dismissal' …
DanyKrk Sep 24, 2026
c331b10
chore(ios): update native test pod lockfile
DanyKrk Sep 25, 2026
b4c8e54
fix(android): bound accessible dismissal geometry
DanyKrk Sep 28, 2026
2415772
fix(android): target dismiss control in portal accessibility test
DanyKrk Sep 28, 2026
b8f3cd4
fix(android): complete accessible dismissal geometry
DanyKrk Sep 29, 2026
e3f29be
Merge remote-tracking branch 'refs/remotes/software-mansion-labs/main…
DanyKrk Sep 29, 2026
38e59e9
fix(android): retain applied window inset
DanyKrk Sep 29, 2026
a29350d
fix(ios): bound accessible dismissal geometry
DanyKrk Sep 29, 2026
9dc6c23
test(android): wait for overlay focus before Back
DanyKrk Sep 29, 2026
2f67ab9
fix(ios): treat zero-height targets as closing
DanyKrk Sep 30, 2026
24d3514
test: harden modal accessibility coverage
DanyKrk Sep 30, 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
34 changes: 34 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ jobs:
- run: bun install --frozen-lockfile
- run: bun run lint
- run: bun run typecheck
- run: bun run test:js
- run: bun run prepare
- uses: actions/setup-java@v5
with:
Expand Down Expand Up @@ -71,3 +72,36 @@ jobs:
arch: x86_64
enable-hw-keyboard: true
script: bun run test:android:instrumented
ios-native-tests:
name: iOS native tests
needs: check
runs-on: macos-15
timeout-minutes: 30
env:
DEVELOPER_DIR: /Applications/Xcode_16.4.app/Contents/Developer
IOS_TEST_RUNTIME_IDENTIFIER: com.apple.CoreSimulator.SimRuntime.iOS-18-5
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version-file: .nvmrc
- uses: oven-sh/setup-bun@v2
with:
bun-version-file: .bun-version
- run: bun install --frozen-lockfile
- uses: ruby/setup-ruby@v1
with:
ruby-version: .ruby-version
bundler-cache: true
- name: Run iOS native tests
run: bun run test:ios:native
- name: Upload iOS native failure artifacts
if: failure()
uses: actions/upload-artifact@v4
with:
name: ios-native-test-results
path: |
.bundle/ios-native-results/*.log
.bundle/ios-native-results/*.xcresult
include-hidden-files: true
if-no-files-found: warn
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,13 @@ android/app/libs
android/keystores/debug.keystore
.expo/
.turbo/
.bundle/
/vendor/bundle/
lib/
ios/generated
android/generated
nitrogen/
tests/ios-native/BottomSheetNativeTests.xcworkspace/
tests/ios-native/Pods/
tests/ios-native/build/
*.xcresult
1 change: 1 addition & 0 deletions .ruby-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
4.0.5
7 changes: 7 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
source "https://rubygems.org"

ruby "4.0.5"

gem "cocoapods", "1.16.2"
gem "nkf", "0.3.0"
gem "xcodeproj", "1.27.0"
196 changes: 196 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.8)
activesupport (7.2.3.2)
base64
benchmark (>= 0.3)
bigdecimal
concurrent-ruby (~> 1.0, >= 1.3.1)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2)
logger (>= 1.4.2)
minitest (>= 5.1, < 6)
securerandom (>= 0.3)
tzinfo (~> 2.0, >= 2.0.5)
addressable (2.9.0)
public_suffix (>= 2.0.2, < 8.0)
algoliasearch (1.27.5)
httpclient (~> 2.8, >= 2.8.3)
json (>= 1.5.1)
atomos (0.1.3)
base64 (0.3.0)
benchmark (0.5.0)
bigdecimal (4.1.3)
claide (1.1.0)
cocoapods (1.16.2)
addressable (~> 2.8)
claide (>= 1.0.2, < 2.0)
cocoapods-core (= 1.16.2)
cocoapods-deintegrate (>= 1.0.3, < 2.0)
cocoapods-downloader (>= 2.1, < 3.0)
cocoapods-plugins (>= 1.0.0, < 2.0)
cocoapods-search (>= 1.0.0, < 2.0)
cocoapods-trunk (>= 1.6.0, < 2.0)
cocoapods-try (>= 1.1.0, < 2.0)
colored2 (~> 3.1)
escape (~> 0.0.4)
fourflusher (>= 2.3.0, < 3.0)
gh_inspector (~> 1.0)
molinillo (~> 0.8.0)
nap (~> 1.0)
ruby-macho (>= 2.3.0, < 3.0)
xcodeproj (>= 1.27.0, < 2.0)
cocoapods-core (1.16.2)
activesupport (>= 5.0, < 8)
addressable (~> 2.8)
algoliasearch (~> 1.0)
concurrent-ruby (~> 1.1)
fuzzy_match (~> 2.0.4)
nap (~> 1.0)
netrc (~> 0.11)
public_suffix (~> 4.0)
typhoeus (~> 1.0)
cocoapods-deintegrate (1.0.5)
cocoapods-downloader (2.1)
cocoapods-plugins (1.0.0)
nap
cocoapods-search (1.0.1)
cocoapods-trunk (1.6.0)
nap (>= 0.8, < 2.0)
netrc (~> 0.11)
cocoapods-try (1.2.0)
colored2 (3.1.2)
concurrent-ruby (1.3.8)
connection_pool (3.0.2)
drb (2.2.3)
escape (0.0.4)
ethon (0.18.0)
ffi (>= 1.15.0)
logger
ffi (1.17.4)
ffi (1.17.4-aarch64-linux-gnu)
ffi (1.17.4-aarch64-linux-musl)
ffi (1.17.4-arm-linux-gnu)
ffi (1.17.4-arm-linux-musl)
ffi (1.17.4-arm64-darwin)
ffi (1.17.4-x86-linux-gnu)
ffi (1.17.4-x86-linux-musl)
ffi (1.17.4-x86_64-darwin)
ffi (1.17.4-x86_64-linux-gnu)
ffi (1.17.4-x86_64-linux-musl)
fourflusher (2.3.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
httpclient (2.9.0)
mutex_m
i18n (1.15.2)
concurrent-ruby (~> 1.0)
json (3.0.2)
logger (1.7.0)
minitest (5.27.0)
molinillo (0.8.0)
mutex_m (0.3.0)
nanaimo (0.4.0)
nap (1.1.0)
netrc (0.11.0)
nkf (0.3.0)
public_suffix (4.0.7)
rexml (3.4.4)
ruby-macho (2.5.1)
securerandom (0.4.1)
typhoeus (1.6.0)
ethon (>= 0.18.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
xcodeproj (1.27.0)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.4.0)
rexml (>= 3.3.6, < 4.0)

PLATFORMS
aarch64-linux-gnu
aarch64-linux-musl
arm-linux-gnu
arm-linux-musl
arm64-darwin
arm64-darwin-25
ruby
x86-linux-gnu
x86-linux-musl
x86_64-darwin
x86_64-linux-gnu
x86_64-linux-musl

DEPENDENCIES
cocoapods (= 1.16.2)
nkf (= 0.3.0)
xcodeproj (= 1.27.0)

CHECKSUMS
CFPropertyList (3.0.8) sha256=2c99d0d980536d3d7ab252f7bd59ac8be50fbdd1ff487c98c949bb66bb114261
activesupport (7.2.3.2) sha256=ddc90d11dd88086d78ace03407fe3c2a3f5c8853c3c3046313db5ed823ef3312
addressable (2.9.0) sha256=7fdf6ac3660f7f4e867a0838be3f6cf722ace541dd97767fa42bc6cfa980c7af
algoliasearch (1.27.5) sha256=26c1cddf3c2ec4bd60c148389e42702c98fdac862881dc6b07a4c0b89ffec853
atomos (0.1.3) sha256=7d43b22f2454a36bace5532d30785b06de3711399cb1c6bf932573eda536789f
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
benchmark (0.5.0) sha256=465df122341aedcb81a2a24b4d3bd19b6c67c1530713fd533f3ff034e419236c
bigdecimal (4.1.3) sha256=61ebe1e5e559bdc3cc6f2c0ee7f427321fc838f59611c294356eb04d6e21cf66
bundler (4.0.11) sha256=5bcec0fb78302e48d02ee46f10ee6e6942be647ba5b44a6d1ddfda9a240ce785
claide (1.1.0) sha256=6d3c5c089dde904d96aa30e73306d0d4bd444b1accb9b3125ce14a3c0183f82e
cocoapods (1.16.2) sha256=0ff1c860f32df3db8b16df09b58da1a6bb2a12fe55f6d5e8be994a74fadd1e5e
cocoapods-core (1.16.2) sha256=4bb1b5c420691e60cf36fa227dec6bc48c096c34c97bb7aa512ea7f3246fc12b
cocoapods-deintegrate (1.0.5) sha256=517c2a448ef563afe99b6e7668704c27f5de9e02715a88ee9de6974dc1b3f6a2
cocoapods-downloader (2.1) sha256=bb6ebe1b3966dc4055de54f7a28b773485ac724fdf575d9bee2212d235e7b6d1
cocoapods-plugins (1.0.0) sha256=725d17ce90b52f862e73476623fd91441b4430b742d8a071000831efb440ca9a
cocoapods-search (1.0.1) sha256=1b133b0e6719ed439bd840e84a1828cca46425ab73a11eff5e096c3b2df05589
cocoapods-trunk (1.6.0) sha256=5f5bda8c172afead48fa2d43a718cf534b1313c367ba1194cebdeb9bfee9ed31
cocoapods-try (1.2.0) sha256=145b946c6e7747ed0301d975165157951153d27469e6b2763c83e25c84b9defe
colored2 (3.1.2) sha256=b13c2bd7eeae2cf7356a62501d398e72fde78780bd26aec6a979578293c28b4a
concurrent-ruby (1.3.8) sha256=b2f1be836e968ccc78ccfce277ea79c72a88633f22306782c16ff23fb415d1e1
connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a
drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373
escape (0.0.4) sha256=e49f44ae2b4f47c6a3abd544ae77fe4157802794e32f19b8e773cbc4dcec4169
ethon (0.18.0) sha256=b598afc9f30448cb068b850714b7d6948e941476095d04f90a4ac65b8d6efcb2
ffi (1.17.4) sha256=bcd1642e06f0d16fc9e09ac6d49c3a7298b9789bcb58127302f934e437d60acf
ffi (1.17.4-aarch64-linux-gnu) sha256=b208f06f91ffd8f5e1193da3cae3d2ccfc27fc36fba577baf698d26d91c080df
ffi (1.17.4-aarch64-linux-musl) sha256=9286b7a615f2676245283aef0a0a3b475ae3aae2bb5448baace630bb77b91f39
ffi (1.17.4-arm-linux-gnu) sha256=d6dbddf7cb77bf955411af5f187a65b8cd378cb003c15c05697f5feee1cb1564
ffi (1.17.4-arm-linux-musl) sha256=9d4838ded0465bef6e2426935f6bcc93134b6616785a84ffd2a3d82bc3cf6f95
ffi (1.17.4-arm64-darwin) sha256=19071aaf1419251b0a46852abf960e77330a3b334d13a4ab51d58b31a937001b
ffi (1.17.4-x86-linux-gnu) sha256=38e150df5f4ca555e25beca4090823ae09657bceded154e3c52f8631c1ed72cf
ffi (1.17.4-x86-linux-musl) sha256=fbeec0fc7c795bcf86f623bb18d31ea1820f7bd580e1703a3d3740d527437809
ffi (1.17.4-x86_64-darwin) sha256=aa70390523cf3235096cf64962b709b4cfbd5c082a2cb2ae714eb0fe2ccda496
ffi (1.17.4-x86_64-linux-gnu) sha256=9d3db14c2eae074b382fa9c083fe95aec6e0a1451da249eab096c34002bc752d
ffi (1.17.4-x86_64-linux-musl) sha256=3fdf9888483de005f8ef8d1cf2d3b20d86626af206cbf780f6a6a12439a9c49e
fourflusher (2.3.1) sha256=1b3de61c7c791b6a4e64f31e3719eb25203d151746bb519a0292bff1065ccaa9
fuzzy_match (2.0.4) sha256=b5de4f95816589c5b5c3ad13770c0af539b75131c158135b3f3bbba75d0cfca5
gh_inspector (1.1.3) sha256=04cca7171b87164e053aa43147971d3b7f500fcb58177698886b48a9fc4a1939
httpclient (2.9.0) sha256=4b645958e494b2f86c2f8a2f304c959baa273a310e77a2931ddb986d83e498c8
i18n (1.15.2) sha256=00f9eb62412fe593b2a65a97daa75300d37abb8f7202ec748e94b6d46a9dd1b5
json (3.0.2) sha256=8e6d7e7b11384c21230430cef90b71f14849a34a1f4452796670f7c981bd19df
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
minitest (5.27.0) sha256=2d3b17f8a36fe7801c1adcffdbc38233b938eb0b4966e97a6739055a45fa77d5
molinillo (0.8.0) sha256=efbff2716324e2a30bccd3eba1ff3a735f4d5d53ffddbc6a2f32c0ca9433045d
mutex_m (0.3.0) sha256=cfcb04ac16b69c4813777022fdceda24e9f798e48092a2b817eb4c0a782b0751
nanaimo (0.4.0) sha256=faf069551bab17f15169c1f74a1c73c220657e71b6e900919897a10d991d0723
nap (1.1.0) sha256=949691660f9d041d75be611bb2a8d2fd559c467537deac241f4097d9b5eea576
netrc (0.11.0) sha256=de1ce33da8c99ab1d97871726cba75151113f117146becbe45aa85cb3dabee3f
nkf (0.3.0) sha256=357a8dbeba38b727b75930f665146546076a394a1c243faf634ff176e3588895
public_suffix (4.0.7) sha256=8be161e2421f8d45b0098c042c06486789731ea93dc3a896d30554ee38b573b8
rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142
ruby-macho (2.5.1) sha256=9075e52e0f9270b552a90b24fcc6219ad149b0d15eae1bc364ecd0ac8984f5c9
securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
typhoeus (1.6.0) sha256=bacc41c23e379547e29801dc235cd1699b70b955a1ba3d32b2b877aa844c331d
tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b
xcodeproj (1.27.0) sha256=8cc7a73b4505c227deab044dce118ede787041c702bc47636856a2e566f854d3

RUBY VERSION
ruby 4.0.5

BUNDLED WITH
4.0.11
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class CloseRequestAnimationInstrumentedTest {
)
setIndex(1)
}
activity.setContentView(sheet)
activity.setInstrumentedReactContentView(sheet)
}

val instrumentation = InstrumentationRegistry.getInstrumentation()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class EscapeRoutingInstrumentedTest {
val child = EscapeRecordingView(activity, childEventCount, consumesEscape = true)
child.isFocusableInTouchMode = true
sheet.addView(child, ViewGroup.LayoutParams(1, 1))
activity.setContentView(sheet)
activity.setInstrumentedReactContentView(sheet)
assertTrue(child.requestFocus())
}

Expand Down Expand Up @@ -82,7 +82,7 @@ class EscapeRoutingInstrumentedTest {
),
)
root.addView(sibling, FrameLayout.LayoutParams(100, 100))
activity.setContentView(root)
activity.setInstrumentedReactContentView(root)
assertTrue(sibling.requestFocus())
}

Expand Down Expand Up @@ -120,7 +120,7 @@ class EscapeRoutingInstrumentedTest {
FrameLayout.LayoutParams.MATCH_PARENT,
),
)
activity.setContentView(root)
activity.setInstrumentedReactContentView(root)
assertTrue(lowerChild.requestFocus())

upperContent = MutableContentHeightView(activity)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.swmansion.reactnativebottomsheet

import android.app.Activity
import android.content.Context
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import com.facebook.react.uimanager.RootView

internal class InstrumentedReactRoot(context: Context) : FrameLayout(context), RootView {
override fun onChildStartedNativeGesture(childView: View?, ev: MotionEvent) = Unit

override fun onChildEndedNativeGesture(childView: View, ev: MotionEvent) = Unit

override fun handleException(t: Throwable) = throw t
}

internal fun Activity.setInstrumentedReactContentView(content: View) {
setContentView(
InstrumentedReactRoot(this).apply {
addView(
content,
ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
),
)
}
)
}
Loading
Loading