Skip to content

Commit

Permalink
feat(HeightAnimation): add new component
Browse files Browse the repository at this point in the history
  • Loading branch information
tujoworker committed Sep 17, 2022
1 parent 99629c4 commit 61db36d
Show file tree
Hide file tree
Showing 27 changed files with 342 additions and 162 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
title: 'HeightAnimation'
description: 'HeightAnimation is a helper component to animate from 0 to height:auto powered by CSS.'
status: 'new'
showTabs: true
---

import HeightAnimationInfo from 'Docs/uilib/components/height-animation/info'
import HeightAnimationDemos from 'Docs/uilib/components/height-animation/demos'

<HeightAnimationInfo />
<HeightAnimationDemos />
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* UI lib Component Example
*
*/

import ComponentBox from 'dnb-design-system-portal/src/shared/tags/ComponentBox'

export function HeightAnimationDefault() {
return (
<ComponentBox useRender>
{
/* jsx */ `
const Example = () => {
const [openState, setOpenState] = React.useState(false)
const onChangeHandler = ({ checked }) => {
setOpenState(checked)
}
return (
<>
<ToggleButton checked={openState} onChange={onChangeHandler}>
Toggle me
</ToggleButton>
<StyledSection style_type="lavender">
<HeightAnimation
top
open={openState}
element="div" // Optional
animate={true} // Optional
>
<P className="content-element" space={0}>
Your content
</P>
</HeightAnimation>
</StyledSection>
</>
)
}
const StyledSection = styled(Section)\`
.content-element {
transition: transform 400ms var(--easing-default);
transform: translateY(-2rem);
padding: 4rem 0;
}
.dnb-height-animation--parallax .content-element {
transform: translateY(0);
}
\`
render(<Example />)
`
}
</ComponentBox>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
showTabs: true
---

import {
HeightAnimationDefault,
} from 'Docs/uilib/components/height-animation/Examples'

## Demos

### HeightAnimation

<HeightAnimationDefault />
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
showTabs: true
---

## Events

No events are supported at the moment.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
showTabs: true
---

## Description

The HeightAnimation component is a helper component to animate from 0px to height:auto powered by CSS. It calculates the height on the fly.

When the animation is done, it sets the element's height to `auto`.

The component can be used as an opt-int replacement instead of vanilla HTML Elements.

The element animation is done with a CSS transition and a `400ms` duration:

## Accessibility

It is important to never animate from 0 to e.g. 64px – because the content may differ based on the viewport width (screen size), the content itself, or the user may even have a larger `font-size`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
showTabs: true
---

## Properties

| Properties | Description |
| ------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `open` | _(optional)_ Set to `true` when the view should animate from 0px to auto. Defaults to `false`. |
| `animate` | _(optional)_ Set to `false` to omit the animation. Defaults to `true`. |
| `element` | _(optional)_ Custom HTML element for the component. Defaults to `div` HTML Element. |
| [Space](/uilib/components/space/properties) | _(optional)_ spacing properties like `top` or `bottom` are supported. |
Original file line number Diff line number Diff line change
Expand Up @@ -5,110 +5,13 @@

import React from 'react'
import styled from '@emotion/styled'
import classnames from 'classnames'
import ComponentBox from 'dnb-design-system-portal/src/shared/tags/ComponentBox'
import { useHeightAnimation } from '@dnb/eufemia/src/shared/useHeightAnimation'

// have a limit because this page is used for screenshot tests
const Wrapper = styled.div`
max-width: 40rem;
`

export function HeightAnimationExample() {
return (
<ComponentBox useRender scope={{ useHeightAnimation, classnames }}>
{
/* jsx */ `
const AnimatedContent = ({
open = false,
noAnimation = false,
...rest
}) => {
const animationElement = React.useRef()
const { isOpen, isInDOM, isVisibleParallax } = useHeightAnimation(
animationElement,
{
open,
animate: !noAnimation,
}
)
// Optional: You can also entirely remove it from the DOM
// if (!isInDOM) {
// return null
// }
return (
<AnimatedDiv
className={classnames(
'wrapper-element',
// Optional: will toggle immediately
isOpen && 'is-open',
// Optional: is "true" while the element "should" be in the DOM (during animation)
isInDOM && 'is-in-dom',
// Optional: is "true" when completely opened, and "false" right after closing has started (usefull for additional CSS transitions/parallax effects)
isVisibleParallax && 'is-in-parallax'
)}
style_type="lavender"
{...rest}
>
{isInDOM /* <-- Optional */ && (
<div ref={animationElement} className="animation-element">
<P className="content-element" space={0}>Your content</P>
</div>
)}
</AnimatedDiv>
)
}
const HeightAnimation = ({ open = false, ...rest }) => {
const [openState, setOpenState] = React.useState(open)
const onChangeHandler = ({ checked }) => {
setOpenState(checked)
}
return (
<>
<ToggleButton checked={openState} onChange={onChangeHandler}>
Toggle me
</ToggleButton>
<AnimatedContent top open={openState} />
</>
)
}
const AnimatedDiv = styled(Section)\`
.animation-element {
overflow: hidden;
transition: height 1s var(--easing-default);
}
.content-element {
transition: transform 1s var(--easing-default);
transform: translateY(-2rem);
}
&.is-in-parallax .content-element {
transform: translateY(0);
}
.content-element {
padding: 4rem 0;
}
\`
render(<HeightAnimation />)
`
}
</ComponentBox>
)
}

export function CoreStyleExample() {
return (
<Wrapper className="dnb-spacing">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,4 @@
---
showTabs: true
draft: true
---

import {
HeightAnimationExample,
} from 'Docs/uilib/helpers/Examples'
import SkipLinkExample from 'Docs/uilib/usage/accessibility/examples/skip-link-example.js'

## Description

These React Hooks are internally used in the components, and are with that a good choice when it comes to save bandwidth in the final production bundle.

## `useHeightAnimation`

In many places we want to animate the content in and out. The challenge is to never define a fixed height, because of an unknown content size and users' different font sizes.

The `useHeightAnimation` hook takes an HTML Element, and animates it from 0 to the current content. When the animation is done, it sets the element's height to `auto`.

The element animation is done with a CSS transition, e.g.:

```css
.animation-element {
overflow: hidden;
transition: height 1s var(--easing-default);
}
```

<HeightAnimationExample />
14 changes: 14 additions & 0 deletions packages/dnb-eufemia/src/components/HeightAnimation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* ATTENTION: This file is auto generated by using "prepareTemplates".
* Do not change the content!
*
*/

/**
* Library Index height-animation to autogenerate all the components and extensions
* Used by "prepareHeightAnimations"
*/

import HeightAnimation from './height-animation/HeightAnimation'
export * from './height-animation/HeightAnimation'
export default HeightAnimation
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react'
import classnames from 'classnames'
import { DynamicElement } from '../../shared/interfaces'
import { useHeightAnimation } from './useHeightAnimation'
import Space from '../space/Space'

export type HeightAnimationProps = {
open: boolean
animate?: boolean
element?: DynamicElement
className?: React.ReactNode
children?: React.ReactNode | HTMLElement
}

export default function HeightAnimation({
open = false,
animate = true,
element,
className,
children,
...props
}: HeightAnimationProps) {
const innerRef = React.useRef()
const { isInDOM, isVisible, isVisibleParallax } = useHeightAnimation(
innerRef,
{
open,
animate,
}
)

if (!isInDOM) {
return null
}

return (
<Space
innerRef={innerRef}
element={element || 'div'}
className={classnames(
'dnb-height-animation',
isVisible && 'dnb-height-animation--is-visible',
isVisibleParallax && 'dnb-height-animation--parallax',
className
)}
{...props}
>
{children}
</Space>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
import React from 'react'
import classnames from 'classnames'
import { render, act, fireEvent } from '@testing-library/react'
import { useHeightAnimation } from '../useHeightAnimation'
import ToggleButton from '../../components/ToggleButton'
import ToggleButton from '../../ToggleButton'
import { wait } from '@testing-library/user-event/dist/utils'
import { useHeightAnimation } from '../useHeightAnimation'

beforeEach(() => {
global.IS_TEST = false
Expand Down
8 changes: 8 additions & 0 deletions packages/dnb-eufemia/src/components/height-animation/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Component Entry
*
*/

import HeightAnimation from './HeightAnimation'
export default HeightAnimation
export * from './HeightAnimation'
Loading

0 comments on commit 61db36d

Please sign in to comment.