This repository has been archived by the owner on Oct 20, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Animate): Added new Animate component (#230)
* New Animate component. For now it can only animate the height of the content, but in future maybe more alternatives. * test(Animate): SHEL-165 Added unit tests for Animate * refactor(Animate): Change inner workings of Animate * refactor(Animate): Move modifierHeight into Animate component folder * feat(Animate): Possibility to change easing functions as props to Animate * fix(Animate): Review comments fix
- Loading branch information
1 parent
11f5f41
commit 76eba45
Showing
12 changed files
with
297 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import PropTypes from 'prop-types'; | ||
import React from 'react'; | ||
import cn from 'classnames'; | ||
import { createStyleSheet } from 'jss-theme-reactor'; | ||
import CSSTransitionGroup from 'react-transition-group/CSSTransitionGroup'; | ||
import modifierHeight from './modifierHeight'; | ||
import easings from '../../styles/transitions/easings'; | ||
|
||
class Animate extends React.PureComponent { | ||
constructor(props, context) { | ||
super(props, context); | ||
this.idNbr = Math.round(Math.random() * 1000); | ||
this.animationName = `${this.props.type}-animation-${this.idNbr}`; | ||
this.classes = this.context.styleManager.render( | ||
createStyleSheet(`Animate_${this.animationName}`, () => { | ||
switch (this.props.type) { | ||
case 'height': | ||
return { | ||
root: { | ||
...modifierHeight({ | ||
classPrefixSpace: true, | ||
name: this.animationName, | ||
estimatedHeight: this.props.estimatedHeight, | ||
transitionEnterTimeout: this.props.enterTime, | ||
transitionLeaveTimeout: this.props.leaveTime, | ||
easingEnterFunction: this.props.easingEnterFunction, | ||
easingLeaveFunction: this.props.easingLeaveFunction, | ||
}), | ||
}, | ||
}; | ||
default: | ||
return { root: {} }; | ||
} | ||
}), | ||
); | ||
} | ||
|
||
render() { | ||
return ( | ||
<CSSTransitionGroup | ||
className={cn(this.classes.root, this.props.className)} | ||
transitionName={this.animationName} | ||
transitionEnterTimeout={this.props.enterTime} | ||
transitionLeaveTimeout={this.props.leaveTime} | ||
> | ||
{this.props.children} | ||
</CSSTransitionGroup> | ||
); | ||
} | ||
} | ||
|
||
Animate.propTypes = { | ||
children: PropTypes.node, | ||
className: PropTypes.string, | ||
type: PropTypes.oneOf(['height']).isRequired, | ||
enterTime: PropTypes.number.isRequired, | ||
leaveTime: PropTypes.number.isRequired, | ||
easingEnterFunction: PropTypes.string, | ||
easingLeaveFunction: PropTypes.string, | ||
estimatedHeight: PropTypes.number, | ||
}; | ||
|
||
Animate.defaultProps = { | ||
type: 'height', | ||
enterTime: 200, | ||
leaveTime: 200, | ||
easingEnterFunction: easings.easeIn, | ||
easingLeaveFunction: easings.easeOut, | ||
estimatedHeight: 500, | ||
}; | ||
|
||
Animate.contextTypes = { | ||
styleManager: PropTypes.object.isRequired, | ||
}; | ||
|
||
export default Animate; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
This is how you should use the Animate component. | ||
|
||
```html | ||
<Animate type="height" enterTime={300} leaveTime={200} estimatedHeight={200}> | ||
{ variableToDecideIfShown ? ( | ||
<div>Something inside the box in Animate component.</div> | ||
) : null } | ||
</Animate> | ||
``` | ||
|
||
Below is an example of how it behaves when animating. | ||
|
||
// This is a sample component to illustrate how to use the Animate | ||
// component. This was needed to have a toggle button with state. | ||
const React = require('react'); | ||
const PropTypes = require('prop-types'); | ||
const { createStyleSheet } = require('jss-theme-reactor'); | ||
|
||
const divStyle = { height: 50, padding: 10 }; | ||
const outerDivStyle = { backgroundColor: 'tomato', height: 100, padding: 10, color: 'white' }; | ||
|
||
class AnimateMarkdownSample extends React.PureComponent { | ||
constructor(props, context) { | ||
super(props, context); | ||
this.state = { show: true, toggleCount: 0 }; | ||
this.toggleShow = function() { this.setState({ show: !this.state.show, toggleCount: this.state.toggleCount += 1 }); }.bind(this); | ||
this.classes = this.context.styleManager.render(createStyleSheet('AnimateMarkdownSample', () => ({ | ||
box: { color: 'white', backgroundColor: '#07B' } | ||
}))); | ||
} | ||
|
||
render() { | ||
return ( | ||
<div> | ||
<button style={{marginBottom: 10}} onClick={this.toggleShow}>Toggle animation</button> | ||
<Animate enterTime={300} leaveTime={200} estimatedHeight={210}> | ||
{this.state.show && this.state.toggleCount % 3 === 2 ? ( | ||
<div style={outerDivStyle}> | ||
Even more content inside what will be animated. | ||
</div> | ||
) : null} | ||
{this.state.show ? ( | ||
<div className={this.classes.box}> | ||
<div style={divStyle}> | ||
Something inside the box in Animate component. | ||
</div> | ||
<div style={divStyle}> | ||
Some more content inside what will be animated. | ||
</div> | ||
{this.state.toggleCount % 3 === 1 ? ( | ||
<div style={divStyle}> | ||
Even more content inside what will be animated. | ||
</div> | ||
) : null} | ||
</div> | ||
) : null } | ||
{this.state.show && this.state.toggleCount % 3 === 0 ? ( | ||
<div style={outerDivStyle}> | ||
Even more content inside what will be animated. | ||
</div> | ||
) : null} | ||
</Animate> | ||
<div>Some content after the animated stuff.</div> | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
AnimateMarkdownSample.contextTypes = { | ||
styleManager: PropTypes.object.isRequired, | ||
}; | ||
|
||
<AnimateMarkdownSample /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default } from './animate'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import easings from '../../styles/transitions/easings'; | ||
import durations from '../../styles/transitions/durations'; | ||
|
||
export default ({ | ||
name, | ||
estimatedHeight = 500, | ||
classPrefixSpace = false, | ||
transitionEnterTimeout = durations.faster, | ||
transitionLeaveTimeout = durations.fastest, | ||
easingEnterFunction = easings.easeIn, | ||
easingLeaveFunction = easings.easeOut, | ||
}) => ({ | ||
overflow: 'hidden', | ||
maxHeight: 'auto', | ||
|
||
[`&${classPrefixSpace ? ' ' : ''}.${name}`]: { | ||
'&-enter': { | ||
maxHeight: 0, | ||
|
||
[`&.${name}-enter-active`]: { | ||
maxHeight: estimatedHeight, | ||
transitionTimingFunction: easingEnterFunction, | ||
transitionProperty: 'max-height', | ||
transitionDuration: transitionEnterTimeout, | ||
}, | ||
}, | ||
'&-leave': { | ||
maxHeight: estimatedHeight, | ||
transitionTimingFunction: easingLeaveFunction, | ||
transitionProperty: 'max-height', | ||
transitionDuration: transitionLeaveTimeout, | ||
|
||
[`&.${name}-leave-active`]: { | ||
maxHeight: 0, | ||
}, | ||
}, | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// Easings and durations from material design, see their docs for usage information: | ||
// https://material.google.com/motion/duration-easing.html#duration-easing-natural-easing-curves | ||
// https://material.io/guidelines/motion/duration-easing.html#duration-easing-common-durations | ||
export default { | ||
shortest: 150, | ||
shorter: 200, | ||
short: 250, | ||
|
||
standard: 300, | ||
complex: 400, | ||
|
||
enteringScreen: 225, | ||
leavingScreen: 195, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
// Easings and durations from material design, see their docs for usage information: | ||
// https://material.google.com/motion/duration-easing.html#duration-easing-natural-easing-curves | ||
// https://material.io/guidelines/motion/duration-easing.html#duration-easing-common-durations | ||
export default { | ||
easeInOut: 'cubic-bezier(.4, 0, .2, 1)', | ||
easeOut: 'cubic-bezier(0, 0, .2, 1)', | ||
easeIn: 'cubic-bezier(.4, 0, 1, 1)', | ||
sharp: 'cubic-bezier(.4, 0, .6, 1)', | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import easingInternal from './easings'; | ||
import durationInternal from './durations'; | ||
|
||
export default { | ||
easing: easingInternal, | ||
duration: durationInternal, | ||
|
||
create(props = ['all'], { easing = easingInternal.easeInOut, duration = durationInternal.standard, delay = 0 } = {}) { | ||
return props.map(prop => `${prop} ${duration}ms ${easing} ${delay}ms`).join(','); | ||
}, | ||
|
||
getAutoHeightDuration(height) { | ||
if (!height) { | ||
return 0; | ||
} | ||
|
||
const constant = height / 36; | ||
const duration = (4 + 15 * constant ** 0.25 + constant / 5) * 10; | ||
|
||
return Math.round(duration); | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import React from 'react'; | ||
import CSSTransitionGroup from 'react-transition-group/CSSTransitionGroup'; | ||
import { expect } from 'chai'; | ||
import { shallow as enzymeShallow } from 'enzyme'; | ||
import { createShallow } from '../../../src/test-utils'; | ||
import Animate from '../../../src/components/animate/animate'; | ||
|
||
describe('<Animate />', () => { | ||
const inputText = 'ISK'; | ||
const shallow = createShallow(enzymeShallow); | ||
let topWrapper; | ||
let wrapper; | ||
|
||
beforeEach(() => { | ||
topWrapper = shallow( | ||
<Animate> | ||
{inputText} | ||
</Animate>, | ||
); | ||
wrapper = topWrapper.childAt(0); | ||
}); | ||
|
||
it('should render a CSSTransitionGroup', () => { | ||
expect(topWrapper.type()).to.equal(CSSTransitionGroup); | ||
}); | ||
|
||
it(`should render the text ${inputText}`, () => { | ||
expect(wrapper.text()).to.equal(inputText); | ||
}); | ||
|
||
it('should give a unique identifier to each Animate instance', () => { | ||
const transitionName = topWrapper.prop('transitionName'); | ||
const otherWrapper = shallow( | ||
<Animate> | ||
{inputText} | ||
</Animate>, | ||
); | ||
|
||
const otherTransitionName = otherWrapper.prop('transitionName'); | ||
expect(transitionName).to.not.equal(otherTransitionName); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters