diff --git a/README.md b/README.md index 73fba873e..2acca5094 100644 --- a/README.md +++ b/README.md @@ -464,6 +464,11 @@ The element tags are the bread and butter of your slide content. Most of these t This tag does not extend from Base. It's special. Wrapping elements in the appear tag makes them appear/disappear in order in response to navigation. +|Name|PropType|Description| +|---|---|---| +|order|PropTypes.number| An optional integer starting at 1 for the presentation order of the Appear tags within a slide. If a slide contains ordered and unordered Appear tags, the unordered will show first. + + #### BlockQuote, Quote and Cite (Base) diff --git a/src/components/appear.js b/src/components/appear.js index f186ca2b9..7249ff7e4 100644 --- a/src/components/appear.js +++ b/src/components/appear.js @@ -21,6 +21,13 @@ class Appear extends Component { this.setState({ active: true }); return; } + + const order = this.props.order || 0; + const node = findDOMNode(this.fragmentRef); + if (!node.dataset) { + node.dataset = {}; + } + node.dataset.order = order; } componentWillReceiveProps(nextProps) { @@ -55,7 +62,6 @@ class Appear extends Component { const child = React.Children.only(this.props.children); const endValue = this.state.active ? 1 : 0; const transitionDuration = this.props.transitionDuration; - return ( {({ opacity }) => React.cloneElement(child, { - className: 'fragment', + className: `fragment ${child.props.className}`.trim(), style: { ...child.props.style, ...this.props.style, opacity }, ref: f => { this.fragmentRef = f; @@ -82,6 +88,7 @@ Appear.defaultProps = { Appear.propTypes = { children: PropTypes.node, fragment: PropTypes.object, + order: PropTypes.number, route: PropTypes.object, style: PropTypes.object, transitionDuration: PropTypes.number diff --git a/src/components/slide.js b/src/components/slide.js index 1bb92a4d8..f5e4fac31 100644 --- a/src/components/slide.js +++ b/src/components/slide.js @@ -32,20 +32,24 @@ class Slide extends React.PureComponent { this.setZoom(); const slide = this.slideRef; const frags = slide.querySelectorAll('.fragment'); + let currentOrder = 0; if (frags && frags.length && !this.context.overview) { - Array.prototype.slice.call(frags, 0).forEach((frag, i) => { - frag.dataset.fid = i; - return ( - this.props.dispatch && - this.props.dispatch( - addFragment({ - slide: this.props.hash, - id: `${this.props.slideIndex}-${i}`, - visible: this.props.lastSlideIndex > this.props.slideIndex, - }) - ) - ); - }); + Array.prototype.slice.call(frags, 0) + .sort((lhs, rhs) => parseInt(lhs.dataset.order, 10) - parseInt(rhs.dataset.order, 10)) + .forEach(frag => { + frag.dataset.fid = currentOrder; + if (this.props.dispatch) { + this.props.dispatch( + addFragment({ + className: frag.className || '', + slide: this.props.hash, + id: `${this.props.slideIndex}-${currentOrder}`, + visible: this.props.lastSlideIndex > this.props.slideIndex, + }) + ); + } + currentOrder += 1; + }); } window.addEventListener('load', this.setZoom); window.addEventListener('resize', this.setZoom); diff --git a/src/components/slide.test.js b/src/components/slide.test.js index 3283f41c5..6e8c2c558 100644 --- a/src/components/slide.test.js +++ b/src/components/slide.test.js @@ -1,6 +1,7 @@ import React from 'react'; import { mount } from 'enzyme'; import Slide from './slide'; +import Appear from './appear'; const _mockContext = function() { return { @@ -15,7 +16,9 @@ const _mockContext = function() { }, }, store: { - getState: () => ({ route: '' }), + getState: () => ({ route: { params: '', slide: 0 } }), + subscribe: () => {}, + dispatch: () => {} }, }; }; @@ -75,4 +78,70 @@ describe('', () => { expect(spy).toHaveBeenCalledTimes(1); expect(spy).toBeCalledWith(5); }); + + test('should create fragments with their appearance in order', () => { + const spy = jest.fn(); + mount( + + +
This shows second
+
+ +
This shows third
+
+ +
This shows first
+
+
, + { context: _mockContext() } + ); + expect(spy).toHaveBeenCalledTimes(3); + expect(spy.mock.calls).toEqual([ + [{ + payload: { slide: 4, id: '4-0', visible: false, className: 'fragment first' }, + type: 'ADD_FRAGMENT' + }], + [{ + payload: { slide: 4, id: '4-1', visible: false, className: 'fragment second' }, + type: 'ADD_FRAGMENT' + }], + [{ + payload: { slide: 4, id: '4-2', visible: false, className: 'fragment third' }, + type: 'ADD_FRAGMENT' + }] + ]); + }); + + test('should order fragments without an order first', () => { + const spy = jest.fn(); + mount( + + +
This shows second
+
+ +
This shows third
+
+ +
This shows first
+
+
, + { context: _mockContext() } + ); + expect(spy).toHaveBeenCalledTimes(3); + expect(spy.mock.calls).toEqual([ + [{ + payload: { slide: 7, id: '7-0', visible: false, className: 'fragment no-order' }, + type: 'ADD_FRAGMENT' + }], + [{ + payload: { slide: 7, id: '7-1', visible: false, className: 'fragment first' }, + type: 'ADD_FRAGMENT' + }], + [{ + payload: { slide: 7, id: '7-2', visible: false, className: 'fragment second' }, + type: 'ADD_FRAGMENT' + }] + ]); + }); });