@@ -2897,6 +2897,89 @@ describe('ReactDOMServerPartialHydration', () => {
2897
2897
expect ( ref . current ) . not . toBe ( null ) ;
2898
2898
} ) ;
2899
2899
2900
+ // @gate experimental
2901
+ it ( 'regression test: does not overfire non-bubbling browser events' , async ( ) => {
2902
+ let suspend = false ;
2903
+ let resolve ;
2904
+ const promise = new Promise ( resolvePromise => ( resolve = resolvePromise ) ) ;
2905
+
2906
+ function Sibling ( { text} ) {
2907
+ if ( suspend ) {
2908
+ throw promise ;
2909
+ } else {
2910
+ return 'Hello' ;
2911
+ }
2912
+ }
2913
+
2914
+ let submits = 0 ;
2915
+
2916
+ function Form ( ) {
2917
+ const [ submitted , setSubmitted ] = React . useState ( false ) ;
2918
+ if ( submitted ) {
2919
+ return null ;
2920
+ }
2921
+ return (
2922
+ < form
2923
+ onSubmit = { ( ) => {
2924
+ setSubmitted ( true ) ;
2925
+ submits ++ ;
2926
+ } } >
2927
+ Click me
2928
+ </ form >
2929
+ ) ;
2930
+ }
2931
+
2932
+ function App ( ) {
2933
+ return (
2934
+ < div >
2935
+ < Suspense fallback = "Loading..." >
2936
+ < Form />
2937
+ < Sibling />
2938
+ </ Suspense >
2939
+ </ div >
2940
+ ) ;
2941
+ }
2942
+
2943
+ suspend = false ;
2944
+ const finalHTML = ReactDOMServer . renderToString ( < App /> ) ;
2945
+ const container = document . createElement ( 'div' ) ;
2946
+ container . innerHTML = finalHTML ;
2947
+
2948
+ // We need this to be in the document since we'll dispatch events on it.
2949
+ document . body . appendChild ( container ) ;
2950
+
2951
+ const form = container . getElementsByTagName ( 'form' ) [ 0 ] ;
2952
+
2953
+ // On the client we don't have all data yet but we want to start
2954
+ // hydrating anyway.
2955
+ suspend = true ;
2956
+ const root = ReactDOM . createRoot ( container , { hydrate : true } ) ;
2957
+ root . render ( < App /> ) ;
2958
+ Scheduler . unstable_flushAll ( ) ;
2959
+ jest . runAllTimers ( ) ;
2960
+
2961
+ expect ( container . textContent ) . toBe ( 'Click meHello' ) ;
2962
+
2963
+ // We're now partially hydrated.
2964
+ form . dispatchEvent (
2965
+ new Event ( 'submit' , {
2966
+ bubbles : true ,
2967
+ } ) ,
2968
+ ) ;
2969
+ expect ( submits ) . toBe ( 0 ) ;
2970
+
2971
+ // Resolving the promise so that rendering can complete.
2972
+ suspend = false ;
2973
+ resolve ( ) ;
2974
+ await promise ;
2975
+
2976
+ Scheduler . unstable_flushAll ( ) ;
2977
+ jest . runAllTimers ( ) ;
2978
+ expect ( submits ) . toBe ( 1 ) ;
2979
+ expect ( container . textContent ) . toBe ( 'Hello' ) ;
2980
+ document . body . removeChild ( container ) ;
2981
+ } ) ;
2982
+
2900
2983
// This test fails, in both forks. Without a boundary, the deferred tree won't
2901
2984
// re-enter hydration mode. It doesn't come up in practice because there's
2902
2985
// always a parent Suspense boundary. But it's still a bug. Leaving for a
0 commit comments