diff --git a/README.md b/README.md index 2355dd77..8ad8a085 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ var Sticky = require('react-stickynode'); - `innerZ {Number/String}` - z-index of the sticky - `enableTransforms {Boolean}` - Enable the use of CSS3 transforms (true by default). - `activeClass {String}` - Class name to be applied to the element when the sticky state is active (`active` by default). +- `releasedClass {String}` - Class name to be applied to the element when the sticky state is released (`released` by default). - `onStateChange {Function}` - Callback for when the sticky state changes. See below. - `shouldFreeze {Function}` - Callback to indicate when the sticky plugin should freeze position and ignore scroll/resize events. See below. diff --git a/src/Sticky.jsx b/src/Sticky.jsx index 5ce95eaa..a7069f45 100644 --- a/src/Sticky.jsx +++ b/src/Sticky.jsx @@ -315,7 +315,7 @@ class Sticky extends Component { } componentDidMount () { - // Only initialize the globals if this is the first + // Only initialize the globals if this is the first // time this component type has been mounted if (!win) { win = window; @@ -376,8 +376,13 @@ class Sticky extends Component { outerStyle.height = this.state.height + 'px'; } + var outerClasses = classNames('sticky-outer-wrapper', this.props.className, { + [this.props.activeClass]: this.state.status === STATUS_FIXED, + [this.props.releasedClass]: this.state.status === STATUS_RELEASED + }) + return ( -
+
{this.props.children}
@@ -395,6 +400,7 @@ Sticky.defaultProps = { bottomBoundary: 0, enableTransforms: true, activeClass: 'active', + releasedClass: 'released', onStateChange: null }; @@ -418,6 +424,7 @@ Sticky.propTypes = { ]), enableTransforms: PropTypes.bool, activeClass: PropTypes.string, + releasedClass: PropTypes.string, onStateChange: PropTypes.func, shouldFreeze: PropTypes.func, innerZ: PropTypes.oneOfType([ diff --git a/tests/unit/Sticky-test.js b/tests/unit/Sticky-test.js index f0601110..ba7da69e 100644 --- a/tests/unit/Sticky-test.js +++ b/tests/unit/Sticky-test.js @@ -139,11 +139,13 @@ describe('Sticky', function () { window.scrollTo(0, 10); shouldBeFixedAt(inner, 0); expect(outer.className).to.contain('active'); + expect(outer.className).to.not.contain('released'); // Scroll up to 0px, and Sticky should reset window.scrollTo(0, 0); shouldBeReset(inner); expect(outer.className).to.not.contain('active'); + expect(outer.className).to.not.contain('released'); // Increase coverage sticky.componentWillReceiveProps(); @@ -190,26 +192,31 @@ describe('Sticky', function () { window.scrollTo(0, 1500); shouldBeFixedAt(inner, -432); expect(outer.className).to.contain('active'); + expect(outer.className).to.not.contain('released'); // Scroll up to 1300px, and Sticky should release window.scrollTo(0, 1300); shouldBeReleasedAt(inner, 1068); expect(outer.className).to.not.contain('active'); + expect(outer.className).to.contain('released'); // Scroll down to 1350px, and Sticky should release as it was window.scrollTo(0, 1350); shouldBeReleasedAt(inner, 1068); expect(outer.className).to.not.contain('active'); + expect(outer.className).to.contain('released'); // Scroll up to 10px, and Sticky should fix window.scrollTo(0, 10); shouldBeFixedAt(inner, 0); expect(outer.className).to.contain('active'); + expect(outer.className).not.to.contain('released'); // Scroll down to 20px, and Sticky should release window.scrollTo(0, 20); shouldBeReleasedAt(inner, 10); expect(outer.className).to.not.contain('active'); + expect(outer.className).to.contain('released'); }); it('should work as expected with original postion 20px from top (short Sticky)', function () { @@ -228,11 +235,13 @@ describe('Sticky', function () { window.scrollTo(0, 10); shouldBeReset(inner); expect(outer.className).to.not.contain('active'); + expect(outer.className).to.not.contain('released'); // Scroll down to 50px, and Sticky should fix window.scrollTo(0, 50); shouldBeFixedAt(inner, 0); expect(outer.className).to.contain('active'); + expect(outer.className).to.not.contain('released'); }); it('should work as expected with original top 20px and 400px bottom boundary (short Sticky)', function () { @@ -253,16 +262,19 @@ describe('Sticky', function () { window.scrollTo(0, 10); shouldBeReset(inner); expect(outer.className).to.not.contain('active'); + expect(outer.className).to.not.contain('released'); // Scroll down to 50px, and Sticky should fix window.scrollTo(0, 50); shouldBeFixedAt(inner, 0); expect(outer.className).to.contain('active'); + expect(outer.className).to.not.contain('released'); // Scroll down to 150px, and Sticky should release window.scrollTo(0, 150); shouldBeReleasedAt(inner, 80); expect(outer.className).to.not.contain('active'); + expect(outer.className).to.contain('released'); }); it('should not be sticky if bottom boundary is shorter then its height (short Sticky)', function () { @@ -282,12 +294,14 @@ describe('Sticky', function () { window.scrollTo(0, 10); shouldBeReset(inner); expect(outer.className).to.not.contain('active'); + expect(outer.className).to.not.contain('released'); // Micic status was not 0 (STATUS_ORIGINAL), scroll down to 20px, and Sticky should stay sticky.state.status = 2; // STATUS_FIXED; window.scrollTo(0, 20); shouldBeReset(inner); expect(outer.className).to.not.contain('active'); + expect(outer.className).to.not.contain('released'); }); it('should work as expected with selector bottom boundary (short Sticky)', function () { @@ -308,16 +322,19 @@ describe('Sticky', function () { window.scrollTo(0, 10); shouldBeFixedAt(inner, 20); expect(outer.className).to.contain('active'); + expect(outer.className).to.not.contain('released'); // Scroll down to 50px, and Sticky should fix window.scrollTo(0, 50); shouldBeFixedAt(inner, 20); expect(outer.className).to.contain('active'); + expect(outer.className).to.not.contain('released'); // Scroll down to 150px, and Sticky should release window.scrollTo(0, 150); shouldBeReleasedAt(inner, 100); expect(outer.className).to.not.contain('active'); + expect(outer.className).to.contain('released'); }); it('should stick to the top when window resizes larger then Sticky (long Sticky)', function () { @@ -336,10 +353,12 @@ describe('Sticky', function () { window.scrollTo(0, 10); shouldBeReleasedAt(inner, 0); expect(outer.className).to.not.contain('active'); + expect(outer.className).to.contain('released'); window.resizeTo(0, 900); shouldBeFixedAt(inner, 0); expect(outer.className).to.contain('active'); + expect(outer.className).to.not.contain('released'); // Resize back window.resizeTo(0, 768); @@ -361,11 +380,13 @@ describe('Sticky', function () { window.scrollTo(0, 10); shouldBeReleasedAt(inner, 0); expect(outer.className).to.not.contain('active'); + expect(outer.className).to.contain('released'); // Scroll down to 1500px, and Sticky should fix to the bottom window.scrollTo(0, 1500); shouldBeFixedAt(inner, -432); expect(outer.className).to.contain('active'); + expect(outer.className).to.not.contain('released'); // Change Sticky's height STICKY_HEIGHT = 1300; @@ -374,11 +395,13 @@ describe('Sticky', function () { window.scrollTo(0, 1550); shouldBeReleasedAt(inner, 1068); expect(outer.className).to.not.contain('active'); + expect(outer.className).to.contain('released'); // Scroll down to 1650px, and Sticky should become fixed again window.scrollTo(0, 1650); shouldBeFixedAt(inner, -532); expect(outer.className).to.contain('active'); + expect(outer.className).to.not.contain('released'); }); it('should allow the sticky functionality to be toggled off', function () {