@@ -65,6 +65,328 @@ test('auto-height wrapper around a feed ScrollView stays bounded by the viewport
6565 expect ( feedContentRect . height ) . toBe ( FEED_CONTENT_HEIGHT ) ;
6666} ) ;
6767
68+ test ( 'auto-height wrapper gives remaining ScrollView space to a flex-growing body' , ( ) => {
69+ const viewportHeight = 200 ;
70+ const headerHeight = 50 ;
71+ const root = Fantom . createRoot ( {
72+ viewportHeight,
73+ viewportWidth : 100 ,
74+ } ) ;
75+
76+ const wrapperRef = createRef < HostInstance > ( ) ;
77+ const growingBodyRef = createRef < HostInstance > ( ) ;
78+
79+ Fantom . runTask ( ( ) => {
80+ root . render (
81+ < ScrollView
82+ contentContainerStyle = { { alignItems : 'flex-start' , flexGrow : 1 } } >
83+ < View collapsable = { false } ref = { wrapperRef } >
84+ < View collapsable = { false } style = { { height : headerHeight , width : 50 } } />
85+ < View
86+ collapsable = { false }
87+ ref = { growingBodyRef }
88+ style = { { flexGrow : 1 , height : 40 , width : 50 } }
89+ />
90+ </ View >
91+ </ ScrollView > ,
92+ ) ;
93+ } ) ;
94+
95+ const wrapperRect = nullthrows ( wrapperRef . current ) . getBoundingClientRect ( ) ;
96+ const growingBodyRect = nullthrows (
97+ growingBodyRef . current ,
98+ ) . getBoundingClientRect ( ) ;
99+
100+ const layout = {
101+ growingBodyHeight : growingBodyRect . height ,
102+ wrapperHeight : wrapperRect . height ,
103+ } ;
104+
105+ expect ( layout ) . toEqual ( {
106+ growingBodyHeight : viewportHeight - headerHeight ,
107+ wrapperHeight : viewportHeight ,
108+ } ) ;
109+ } ) ;
110+
111+ test ( 'positive flex-basis preserves content height in an unbounded ScrollView axis' , ( ) => {
112+ const root = Fantom . createRoot ( {
113+ viewportHeight : 200 ,
114+ viewportWidth : 100 ,
115+ } ) ;
116+
117+ const wrapperRef = createRef < HostInstance > ( ) ;
118+ const contentRef = createRef < HostInstance > ( ) ;
119+
120+ Fantom . runTask ( ( ) => {
121+ root . render (
122+ < ScrollView >
123+ < View collapsable = { false } ref = { wrapperRef } style = { { flexBasis : 50 } } >
124+ < View collapsable = { false } ref = { contentRef } style = { { height : 100 } } />
125+ </ View >
126+ </ ScrollView > ,
127+ ) ;
128+ } ) ;
129+
130+ expect ( {
131+ contentHeight : nullthrows ( contentRef . current ) . getBoundingClientRect ( )
132+ . height ,
133+ wrapperHeight : nullthrows ( wrapperRef . current ) . getBoundingClientRect ( )
134+ . height ,
135+ } ) . toEqual ( {
136+ contentHeight : 100 ,
137+ wrapperHeight : 100 ,
138+ } ) ;
139+ } ) ;
140+
141+ test ( 'positive flex-basis larger than content preserves content height in an unbounded ScrollView axis' , ( ) => {
142+ const root = Fantom . createRoot ( {
143+ viewportHeight : 200 ,
144+ viewportWidth : 100 ,
145+ } ) ;
146+
147+ const wrapperRef = createRef < HostInstance > ( ) ;
148+ const contentRef = createRef < HostInstance > ( ) ;
149+
150+ Fantom . runTask ( ( ) => {
151+ root . render (
152+ < ScrollView >
153+ < View collapsable = { false } ref = { wrapperRef } style = { { flexBasis : 100 } } >
154+ < View collapsable = { false } ref = { contentRef } style = { { height : 40 } } />
155+ </ View >
156+ </ ScrollView > ,
157+ ) ;
158+ } ) ;
159+
160+ const layout = {
161+ contentHeight : nullthrows ( contentRef . current ) . getBoundingClientRect ( )
162+ . height ,
163+ wrapperHeight : nullthrows ( wrapperRef . current ) . getBoundingClientRect ( )
164+ . height ,
165+ } ;
166+
167+ expect ( layout ) . toEqual ( {
168+ contentHeight : 40 ,
169+ wrapperHeight : 40 ,
170+ } ) ;
171+ } ) ;
172+
173+ test ( 'nested padding respects an exhausted ScrollView height constraint' , ( ) => {
174+ const viewportHeight = 100 ;
175+ const root = Fantom . createRoot ( {
176+ viewportHeight,
177+ viewportWidth : 100 ,
178+ } ) ;
179+
180+ const wrapperRef = createRef < HostInstance > ( ) ;
181+ const nestedRef = createRef < HostInstance > ( ) ;
182+ const paddedRef = createRef < HostInstance > ( ) ;
183+
184+ Fantom . runTask ( ( ) => {
185+ root . render (
186+ < ScrollView contentContainerStyle = { { height : viewportHeight } } >
187+ < View
188+ collapsable = { false }
189+ ref = { wrapperRef }
190+ style = { { paddingBottom : 50 , paddingTop : 50 } } >
191+ < View collapsable = { false } ref = { nestedRef } >
192+ < View
193+ collapsable = { false }
194+ ref = { paddedRef }
195+ style = { { paddingBottom : 10 , paddingTop : 10 } }
196+ />
197+ </ View >
198+ </ View >
199+ </ ScrollView > ,
200+ ) ;
201+ } ) ;
202+
203+ expect ( {
204+ nestedHeight : nullthrows ( nestedRef . current ) . getBoundingClientRect ( ) . height ,
205+ paddedHeight : nullthrows ( paddedRef . current ) . getBoundingClientRect ( ) . height ,
206+ wrapperHeight : nullthrows ( wrapperRef . current ) . getBoundingClientRect ( )
207+ . height ,
208+ } ) . toEqual ( {
209+ nestedHeight : 0 ,
210+ paddedHeight : 20 ,
211+ wrapperHeight : viewportHeight ,
212+ } ) ;
213+ } ) ;
214+
215+ test ( 'clipped ScrollView inside a horizontal pager stays scrollable' , ( ) => {
216+ const root = Fantom . createRoot ( {
217+ viewportHeight : 150 ,
218+ viewportWidth : 320 ,
219+ } ) ;
220+
221+ const outerScrollRef = createRef < HostInstance > ( ) ;
222+ const boundedContainerRef = createRef < HostInstance > ( ) ;
223+ const autoHeightWrapperRef = createRef < HostInstance > ( ) ;
224+ const innerScrollRef = createRef < HostInstance > ( ) ;
225+
226+ Fantom . runTask ( ( ) => {
227+ root . render (
228+ < ScrollView horizontal pagingEnabled ref = { outerScrollRef } >
229+ < View
230+ collapsable = { false }
231+ ref = { boundedContainerRef }
232+ style = { { height : 150 , overflow : 'hidden' , width : 320 } } >
233+ < View collapsable = { false } ref = { autoHeightWrapperRef } >
234+ < ScrollView ref = { innerScrollRef } >
235+ < View style = { { height : 240 } } />
236+ </ ScrollView >
237+ </ View >
238+ </ View >
239+ < View style = { { height : 150 , width : 320 } } />
240+ </ ScrollView > ,
241+ ) ;
242+ } ) ;
243+
244+ const outerScroll = nullthrows ( outerScrollRef . current ) ;
245+ const innerScroll = nullthrows ( innerScrollRef . current ) ;
246+ const layout = {
247+ autoHeightWrapperHeight : nullthrows (
248+ autoHeightWrapperRef . current ,
249+ ) . getBoundingClientRect ( ) . height ,
250+ boundedContainerHeight : nullthrows (
251+ boundedContainerRef . current ,
252+ ) . getBoundingClientRect ( ) . height ,
253+ innerContentHeight : innerScroll . scrollHeight ,
254+ innerHeight : innerScroll . getBoundingClientRect ( ) . height ,
255+ innerScrollRange : innerScroll . scrollHeight - innerScroll . clientHeight ,
256+ outerHorizontalScrollRange :
257+ outerScroll . scrollWidth - outerScroll . clientWidth ,
258+ outerVerticalScrollRange :
259+ outerScroll . scrollHeight - outerScroll . clientHeight ,
260+ } ;
261+
262+ expect ( layout ) . toEqual ( {
263+ autoHeightWrapperHeight : 150 ,
264+ boundedContainerHeight : 150 ,
265+ innerContentHeight : 240 ,
266+ innerHeight : 150 ,
267+ innerScrollRange : 90 ,
268+ outerHorizontalScrollRange : 320 ,
269+ outerVerticalScrollRange : 0 ,
270+ } ) ;
271+ } ) ;
272+
273+ test ( 'percentage padding tracks an updated owner width' , ( ) => {
274+ const root = Fantom . createRoot ( {
275+ viewportHeight : 300 ,
276+ viewportWidth : 300 ,
277+ } ) ;
278+
279+ function render ( updated : boolean ) {
280+ Fantom . runTask ( ( ) => {
281+ root . render (
282+ < View
283+ collapsable = { false }
284+ style = { {
285+ height : 200 ,
286+ overflow : 'scroll' ,
287+ width : updated ? 200 : 100 ,
288+ } } >
289+ < View collapsable = { false } style = { updated ? { height : 150 } : undefined } >
290+ < View
291+ collapsable = { false }
292+ nativeID = "candidate"
293+ style = { { maxWidth : 50 , paddingLeft : '25%' } }
294+ />
295+ </ View >
296+ </ View > ,
297+ ) ;
298+ } ) ;
299+ }
300+
301+ render ( false ) ;
302+ render ( true ) ;
303+
304+ expect (
305+ root
306+ . getRenderedOutput ( {
307+ includeLayoutMetrics : true ,
308+ props : [ 'layoutMetrics-contentInsets' , 'nativeID' ] ,
309+ } )
310+ . toJSX ( ) ,
311+ ) . toEqual (
312+ < rn-view layoutMetrics-contentInsets = "{top:0,right:0,bottom:0,left:0}" >
313+ < rn-view layoutMetrics-contentInsets = "{top:0,right:0,bottom:0,left:0}" >
314+ < rn-view
315+ layoutMetrics-contentInsets = "{top:0,right:0,bottom:0,left:50}"
316+ nativeID = "candidate"
317+ />
318+ </ rn-view >
319+ </ rn-view > ,
320+ ) ;
321+ } ) ;
322+
323+ test ( 'native box sizing update invalidates cached intrinsic width' , ( ) => {
324+ const root = Fantom . createRoot ( {
325+ viewportHeight : 280 ,
326+ viewportWidth : 240 ,
327+ } ) ;
328+ const scrollRef = createRef < HostInstance > ( ) ;
329+ const leafRef = createRef < HostInstance > ( ) ;
330+
331+ const render = ( siblingHeight : number ) => {
332+ Fantom . runTask ( ( ) => {
333+ root . render (
334+ < View
335+ collapsable = { false }
336+ style = { {
337+ alignItems : 'flex-start' ,
338+ flexDirection : 'row' ,
339+ height : 240 ,
340+ width : 181 ,
341+ } } >
342+ < View
343+ collapsable = { false }
344+ style = { { alignItems : 'flex-start' , alignSelf : 'flex-start' } } >
345+ < ScrollView ref = { scrollRef } style = { { height : 200 } } >
346+ < View collapsable = { false } style = { { height : 100 } } >
347+ < View
348+ collapsable = { false }
349+ style = { { flexBasis : 100 , flexShrink : 1 } } >
350+ < View collapsable = { false } >
351+ < View
352+ collapsable = { false }
353+ ref = { leafRef }
354+ style = { {
355+ borderLeftWidth : 1 ,
356+ borderRightWidth : 2 ,
357+ paddingHorizontal : 3 ,
358+ width : 83 ,
359+ } }
360+ />
361+ </ View >
362+ </ View >
363+ < View style = { { height : siblingHeight , width : 17 } } />
364+ </ View >
365+ </ ScrollView >
366+ </ View >
367+ </ View > ,
368+ ) ;
369+ } ) ;
370+ } ;
371+
372+ render ( 27 ) ;
373+ Fantom . runTask ( ( ) => {
374+ nullthrows ( leafRef . current ) . setNativeProps ( {
375+ style : { boxSizing : 'content-box' } ,
376+ } ) ;
377+ } ) ;
378+ render ( 40 ) ;
379+ render ( 53 ) ;
380+
381+ expect ( {
382+ leafWidth : nullthrows ( leafRef . current ) . getBoundingClientRect ( ) . width ,
383+ scrollWidth : nullthrows ( scrollRef . current ) . getBoundingClientRect ( ) . width ,
384+ } ) . toEqual ( {
385+ leafWidth : 92 ,
386+ scrollWidth : 92 ,
387+ } ) ;
388+ } ) ;
389+
68390const styles = StyleSheet . create ( {
69391 feed : {
70392 width : VIEWPORT_WIDTH ,
0 commit comments