Skip to content

Commit

Permalink
fix(HeightAnimation): enhance calculation of height
Browse files Browse the repository at this point in the history
  • Loading branch information
tujoworker committed Feb 27, 2024
1 parent 03c4f34 commit 943ab71
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,6 @@ export default class HeightAnimation {
}
firstPaintStyle() {
return {
position: 'absolute',
visibility: 'hidden',
opacity: '0', // prevents before/after elements to be visible
height: 'auto',
Expand All @@ -181,13 +180,16 @@ export default class HeightAnimation {
return this.__currentHeight
}

const width = this.elem.clientWidth
const clonedElem = this.elem.cloneNode(true) as HTMLElement
this.elem.parentNode?.insertBefore(clonedElem, this.elem.nextSibling)

for (const key in this.firstPaintStyle) {
clonedElem.style[key] = this.firstPaintStyle[key]
const styles = this.firstPaintStyle()
for (const key in styles) {
clonedElem.style[key] = styles[key]
}
clonedElem.style.height = 'auto'
clonedElem.style.width = width ? `${String(width)}px` : 'auto' // set width because of the "position: absolute"
clonedElem.style.position = 'absolute' // not a part of the "firstPaintStyle"

const height =
parseFloat(String(clonedElem.clientHeight)) ||
Expand Down Expand Up @@ -228,6 +230,7 @@ export default class HeightAnimation {
return
}

this.stop()
this.isAnimating = true

// make the animation
Expand Down Expand Up @@ -275,7 +278,7 @@ export default class HeightAnimation {
const toHeight = this.getUnknownHeight()

this.addEndEvent((e) => {
if (e.target === e.currentTarget) {
if (e.target === e.currentTarget || !e.currentTarget) {
this.setState('opened')
this.readjust()
}
Expand All @@ -299,7 +302,7 @@ export default class HeightAnimation {
const fromHeight = this.getHeight()

this.addEndEvent((e) => {
if (e.target === e.currentTarget) {
if (e.target === e.currentTarget || !e.currentTarget) {
if (this.elem) {
this.elem.style.visibility = 'hidden'
this.elem.style.overflowY = 'clip'
Expand Down Expand Up @@ -342,7 +345,10 @@ export default class HeightAnimation {
this.callAnimationStart()

this.addEndEvent((e) => {
if (this.state === 'adjusting' && e.target === e.currentTarget) {
if (
this.state === 'adjusting' &&
(e.target === e.currentTarget || !e.currentTarget)
) {
if (this.elem) {
this.elem.style.height = 'auto'
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { wait } from '../../../core/jest/jestSetup'
import HeightAnimationInstance from '../HeightAnimationInstance'
import {
simulateAnimationEnd,
Expand Down Expand Up @@ -37,6 +38,20 @@ describe('HeightAnimationInstance', () => {
expect(inst.elem).toBeUndefined()
})

it('firstPaintStyle should have these properties', () => {
const inst = new HeightAnimationInstance()
expect(inst.firstPaintStyle()).toEqual({
height: 'auto',
opacity: '0',
visibility: 'hidden',
})
expect(inst.firstPaintStyle()).not.toEqual(
expect.objectContaining({
position: 'absolute',
})
)
})

it('getHeight should return height', () => {
const inst = new HeightAnimationInstance()
inst.setElement(element)
Expand All @@ -55,6 +70,42 @@ describe('HeightAnimationInstance', () => {
expect(inst.getUnknownHeight()).toBe(100)
})

it('getUnknownHeight should create a cloned element', async () => {
const inst = new HeightAnimationInstance()
inst.setElement(element)

mockHeight(100, element)

const addedNodes = []
const removedNodes = []

const observer = new MutationObserver((mutationsList) => {
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
if (mutation.removedNodes?.length) {
removedNodes.push(mutation.removedNodes)
}
if (mutation.addedNodes?.length) {
addedNodes.push(mutation.addedNodes)
}
}
}
})

observer.observe(document.body, {
childList: true,
})

inst.getUnknownHeight()

await wait(1)

observer.disconnect()

expect(addedNodes).toHaveLength(1)
expect(removedNodes).toHaveLength(1)
})

it('open should call getUnknownHeight', () => {
const inst = new HeightAnimationInstance()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,66 @@ const StyledSection = styled(Section)`
transform: translateY(0);
}
`

export function HeightAnimationKeepInDOM() {
const Example = () => {
const [openState, setOpenState] = React.useState(true)
const [contentState, setContentState] = React.useState(false)

const onChangeHandler = ({ checked }) => {
setOpenState(checked)
}

// console.log('contentState', contentState)

return (
<>
<ToggleButton
checked={openState}
on_change={onChangeHandler}
right
>
Open/close
</ToggleButton>
<ToggleButton
checked={contentState || !openState}
disabled={!openState}
on_change={({ checked }) => {
setContentState(checked)
}}
space={{ top: true, bottom: true }}
>
Change height inside
</ToggleButton>

<StyledSection style_type="lavender" top>
<HeightAnimation
open={openState}
// keepInDOM={true}
duration={1000}
>
<Section spacing style_type="lavender">
<P space={0}>Your content</P>
</Section>
{contentState && <P space={0}>More content</P>}
</HeightAnimation>
</StyledSection>
</>
)
}

const StyledSection = styled(Section)`
.content-element {
transition: transform 1s var(--easing-default);
transform: translateY(-2rem);
padding: 4rem 0;
}
.dnb-height-animation--parallax .content-element {
transform: translateY(0);
}
`

return <Example />
}
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,11 @@ export function useHeightAnimation(
}

function useOpenClose({ open, instRef, isInitialRenderRef, targetRef }) {
const isTest =
typeof process !== 'undefined' &&
process.env.NODE_ENV === 'test' &&
typeof globalThis.IS_TEST === 'undefined'

useLayoutEffect(() => {
instRef.current.setElement(targetRef.current)

Expand All @@ -182,27 +187,20 @@ function useOpenClose({ open, instRef, isInitialRenderRef, targetRef }) {
} else {
instRef.current.setAsClosed()
}
}
}, [open, targetRef, instRef, isInitialRenderRef])

const isTest =
typeof process !== 'undefined' &&
process.env.NODE_ENV === 'test' &&
typeof globalThis.IS_TEST === 'undefined'

useLayoutEffect(() => {
if (open) {
instRef.current.open()
} else {
instRef.current.close()
if (open) {
instRef.current.open()
} else {
instRef.current.close()
}
}

// For testing purposes, we need to trigger the transitionend event
if (isTest) {
const event = new CustomEvent('transitionend')
targetRef.current?.dispatchEvent(event)
}
}, [open, instRef, isInitialRenderRef, targetRef, isTest])
}, [open, targetRef, instRef, isInitialRenderRef, isTest])

useLayoutEffect(() => {
const run = () => {
Expand Down

0 comments on commit 943ab71

Please sign in to comment.