Skip to content

Commit 5af3bd4

Browse files
Don't scroll lock when a Transition + Dialog is mounted but hidden (#1681)
* Refer to context for initial Transition Tree state * Update changelog
1 parent 6d13e79 commit 5af3bd4

File tree

4 files changed

+94
-2
lines changed

4 files changed

+94
-2
lines changed

packages/@headlessui-react/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2121
- Close `Menu` component when using `tab` key ([#1673](https://github.com/tailwindlabs/headlessui/pull/1673))
2222
- Resync input when display value changes ([#1679](https://github.com/tailwindlabs/headlessui/pull/1679))
2323
- Ensure controlled `Tabs` don't change automagically ([#1680](https://github.com/tailwindlabs/headlessui/pull/1680))
24+
- Don't scroll lock when a Transition + Dialog is mounted but hidden ([#1681](https://github.com/tailwindlabs/headlessui/pull/1681))
2425

2526
## [1.6.6] - 2022-07-07
2627

packages/@headlessui-react/src/components/dialog/dialog.test.tsx

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { createElement, useRef, useState } from 'react'
1+
import React, { createElement, useRef, useState, Fragment } from 'react'
22
import { render } from '@testing-library/react'
33

44
import { Dialog } from './dialog'
@@ -270,6 +270,44 @@ describe('Rendering', () => {
270270
expect(document.documentElement.style.overflow).toBe('hidden')
271271
})
272272
)
273+
274+
it(
275+
'should wait to add a scroll lock to the html tag when unmount is false in a Transition',
276+
suppressConsoleLogs(async () => {
277+
function Example() {
278+
let [isOpen, setIsOpen] = useState(false)
279+
280+
return (
281+
<>
282+
<button id="trigger" onClick={() => setIsOpen((v) => !v)}>
283+
Trigger
284+
</button>
285+
286+
<Transition as={Fragment} show={isOpen} unmount={false}>
287+
<Dialog onClose={() => setIsOpen(false)} unmount={false}>
288+
<input id="a" type="text" />
289+
<input id="b" type="text" />
290+
<input id="c" type="text" />
291+
</Dialog>
292+
</Transition>
293+
</>
294+
)
295+
}
296+
297+
render(<Example />)
298+
299+
// No overflow yet
300+
expect(document.documentElement.style.overflow).toBe('')
301+
302+
let btn = document.getElementById('trigger')
303+
304+
// Open the dialog
305+
await click(btn)
306+
307+
// Expect overflow
308+
expect(document.documentElement.style.overflow).toBe('hidden')
309+
})
310+
)
273311
})
274312

275313
describe('Dialog.Overlay', () => {

packages/@headlessui-react/src/components/transitions/transition.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,10 +209,12 @@ let TransitionChild = forwardRefWithAs(function TransitionChild<
209209
} = props as typeof props
210210
let container = useRef<HTMLElement | null>(null)
211211
let transitionRef = useSyncRefs(container, ref)
212-
let [state, setState] = useState(TreeStates.Visible)
213212
let strategy = rest.unmount ? RenderStrategy.Unmount : RenderStrategy.Hidden
214213

215214
let { show, appear, initial } = useTransitionContext()
215+
216+
let [state, setState] = useState(show ? TreeStates.Visible : TreeStates.Hidden)
217+
216218
let { register, unregister } = useParentNesting()
217219
let prevShow = useRef<boolean | null>(null)
218220

packages/@headlessui-vue/src/components/dialog/dialog.test.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,57 @@ describe('Rendering', () => {
391391
expect(document.documentElement.style.overflow).toBe('hidden')
392392
})
393393
)
394+
395+
it(
396+
'should wait to add a scroll lock to the html tag when unmount is false in a Transition',
397+
suppressConsoleLogs(async () => {
398+
renderTemplate({
399+
components: {
400+
Dialog,
401+
TransitionRoot,
402+
},
403+
404+
template: `
405+
<div>
406+
<button id="trigger" @click="toggleOpen">
407+
Trigger
408+
</button>
409+
410+
<TransitionRoot :show="isOpen" :unmount="false">
411+
<Dialog @close="setIsOpen" :unmount="false">
412+
<input id="a" type="text" />
413+
<input id="b" type="text" />
414+
<input id="c" type="text" />
415+
</Dialog>
416+
</TransitionRoot>
417+
</div>
418+
`,
419+
setup() {
420+
let isOpen = ref(false)
421+
return {
422+
isOpen,
423+
setIsOpen(value: boolean) {
424+
isOpen.value = value
425+
},
426+
toggleOpen() {
427+
isOpen.value = !isOpen.value
428+
},
429+
}
430+
},
431+
})
432+
433+
// No overflow yet
434+
expect(document.documentElement.style.overflow).toBe('')
435+
436+
let btn = document.getElementById('trigger')
437+
438+
// Open the dialog
439+
await click(btn)
440+
441+
// Expect overflow
442+
expect(document.documentElement.style.overflow).toBe('hidden')
443+
})
444+
)
394445
})
395446

396447
describe('DialogOverlay', () => {

0 commit comments

Comments
 (0)