From 022f97d9088afa107c5c8c1e5c52c754ff429e12 Mon Sep 17 00:00:00 2001 From: Carlos Paelinck Date: Thu, 2 Nov 2017 18:02:46 -0500 Subject: [PATCH 1/3] Add GoToSlide button --- example/src/index.js | 36 +++++++++++- .../__snapshots__/go-to-action.test.js.snap | 7 +++ src/components/go-to-action.js | 55 +++++++++++++++++++ src/components/go-to-action.test.js | 50 +++++++++++++++++ src/components/manager.js | 27 ++++++--- src/index.js | 2 + src/themes/default/screen.js | 12 ++++ 7 files changed, 181 insertions(+), 8 deletions(-) create mode 100644 src/components/__snapshots__/go-to-action.test.js.snap create mode 100644 src/components/go-to-action.js create mode 100644 src/components/go-to-action.test.js diff --git a/example/src/index.js b/example/src/index.js index d2a5ecf39..0e066438b 100644 --- a/example/src/index.js +++ b/example/src/index.js @@ -3,7 +3,7 @@ import React from 'react'; import { Appear, BlockQuote, Cite, CodePane, ComponentPlayground, Deck, Fill, Heading, Image, Layout, Link, ListItem, List, Markdown, MarkdownSlides, Quote, Slide, SlideSet, - TableBody, TableHeader, TableHeaderItem, TableItem, TableRow, Table, Text, S + TableBody, TableHeader, TableHeaderItem, TableItem, TableRow, Table, Text, GoToAction } from '../../src'; import preloader from '../../src/utils/preloader'; @@ -28,6 +28,8 @@ const theme = createTheme({ primary: '#ff4081' }); +console.log(theme) + export default class Presentation extends React.Component { state = { steps: 0 @@ -116,6 +118,38 @@ export default class Presentation extends React.Component { + + + Mix it up! + + + You can even jump to different slides with a standard button or custom component! + + + Jump to Slide 8 + + ( + + )} + /> + diff --git a/src/components/__snapshots__/go-to-action.test.js.snap b/src/components/__snapshots__/go-to-action.test.js.snap new file mode 100644 index 000000000..a45efb734 --- /dev/null +++ b/src/components/__snapshots__/go-to-action.test.js.snap @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` should just render a div when no props are provided 1`] = ` + +
+ +`; diff --git a/src/components/go-to-action.js b/src/components/go-to-action.js new file mode 100644 index 000000000..58dc57e49 --- /dev/null +++ b/src/components/go-to-action.js @@ -0,0 +1,55 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { getStyles } from '../utils/base'; +import styled from 'react-emotion'; +import isFunction from 'lodash/isFunction'; + +const GoToActionButton = styled.button(({ styles }) => [ + styles.context, + styles.base, + styles.user +]); + +class GoToAction extends React.Component { + render() { + const { + props: { render, children, style, slide }, + context: { goToSlide } + } = this; + if (render && isFunction(render)) { + return render(goToSlide); + } else if (slide) { + return ( + goToSlide(slide)} + styles={{ + context: this.context.styles.components.goToAction, + base: getStyles.call(this), + user: style + }} + > + {children} + + ); + } + // eslint-disable-next-line no-console + console.warn(' must have a render or slide prop.'); + return
; + } +} + +GoToAction.propTypes = { + children: PropTypes.node, + render: PropTypes.func, + slide: PropTypes.oneOfType([ + PropTypes.number, PropTypes.string + ]), + style: PropTypes.object, +}; + +GoToAction.contextTypes = { + styles: PropTypes.object, + goToSlide: PropTypes.func, +}; + +export default GoToAction; diff --git a/src/components/go-to-action.test.js b/src/components/go-to-action.test.js new file mode 100644 index 000000000..1ffb1888d --- /dev/null +++ b/src/components/go-to-action.test.js @@ -0,0 +1,50 @@ +import React from 'react'; +import { mount } from 'enzyme'; +import GoToAction from './go-to-action'; + +describe('', () => { + test('should call the context function with the slide prop when it has a child', () => { + const stub = jest.fn(); + const context = { + styles: { components: { goToAction: {} } }, + goToSlide: stub + }; + const wrapper = mount( + Slide 2, { context } + ); + wrapper.simulate('click'); + expect(stub).toHaveBeenCalledTimes(1); + expect(stub).toHaveBeenCalledWith(2); + }); + + test('should call the context function when providing a custom component', () => { + const stub = jest.fn(); + const context = { + styles: { components: { goToAction: {} } }, + goToSlide: stub + }; + const wrapper = mount( + ( + + )} + />, { context } + ); + wrapper.find('button#inner-btn').simulate('click'); + expect(stub).toHaveBeenCalledTimes(1); + expect(stub).toHaveBeenCalledWith('wait-what'); + }); + + test('should just render a div when no props are provided', () => { + const context = { + styles: { components: { goToAction: {} } }, + goToSlide: () => {} + }; + const wrapper = mount( + , { context } + ); + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/src/components/manager.js b/src/components/manager.js index f1dd59b9b..24d4c41b1 100644 --- a/src/components/manager.js +++ b/src/components/manager.js @@ -96,6 +96,7 @@ export class Manager extends Component { static childContextTypes = { contentWidth: PropTypes.number, contentHeight: PropTypes.number, + goToSlide: PropTypes.func }; constructor(props) { @@ -119,6 +120,7 @@ export class Manager extends Component { return { contentWidth: this.props.contentWidth, contentHeight: this.props.contentHeight, + goToSlide: slide => this._goToSlide({ slide }) }; } @@ -273,15 +275,26 @@ export class Manager extends Component { } } _goToSlide(e) { + let data = null; + let canNavigate = true; if (e.key === 'spectacle-slide') { - const data = JSON.parse(e.newValue); - const slideIndex = this._getSlideIndex(); - this.setState({ - lastSlideIndex: slideIndex || 0, - }); - if (this._checkFragments(this.props.route.slide, data.forward)) { - this.context.history.replace(`/${data.slide}${this._getSuffix()}`); + canNavigate = this._checkFragments(this.props.route.slide, data.forward); + data = JSON.parse(e.newValue); + } else if (e.slide) { + data = e; + } else { + return; + } + const slideIndex = this._getSlideIndex(); + this.setState({ + lastSlideIndex: slideIndex || 0, + }); + if (canNavigate) { + let slide = data.slide; + if (typeof slide === 'number') { + slide -= 1; } + this.context.history.replace(`/${slide}${this._getSuffix()}`); } } _prevSlide() { diff --git a/src/index.js b/src/index.js index 42f2c204f..aebf026a7 100644 --- a/src/index.js +++ b/src/index.js @@ -9,6 +9,7 @@ import { Fill } from './components/fill'; import { Fit } from './components/fit'; import Heading from './components/heading'; import Image from './components/image'; +import GoToAction from './components/go-to-action'; import Layout from './components/layout'; import Link from './components/link'; import ListItem from './components/list-item'; @@ -48,6 +49,7 @@ export { Fit, Heading, Image, + GoToAction, Layout, Link, ListItem, diff --git a/src/themes/default/screen.js b/src/themes/default/screen.js index 93c2a7c68..d61899cb4 100644 --- a/src/themes/default/screen.js +++ b/src/themes/default/screen.js @@ -230,6 +230,18 @@ const screen = (colorArgs = defaultColors, fontArgs = defaultFonts) => { padding: '0 10px', borderRadius: 3, }, + goToAction: { + borderRadius: '6px', + fontFamily: fonts.primary, + padding: '0.25em 1em', + border: 'none', + background: '#000', + color: '#fff', + '&:hover': { + background: colors.tertiary, + color: '#000' + } + }, heading: { h1: { color: colors.tertiary, From a77e586ff4c6ee787f25e4300d2803c4095a6287 Mon Sep 17 00:00:00 2001 From: Carlos Paelinck Date: Thu, 2 Nov 2017 18:06:35 -0500 Subject: [PATCH 2/3] Removed errant console --- example/src/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/example/src/index.js b/example/src/index.js index 0e066438b..104b0c705 100644 --- a/example/src/index.js +++ b/example/src/index.js @@ -28,8 +28,6 @@ const theme = createTheme({ primary: '#ff4081' }); -console.log(theme) - export default class Presentation extends React.Component { state = { steps: 0 From 252735b3cfd576486b51a5419a9b540b1243ea04 Mon Sep 17 00:00:00 2001 From: Carlos Paelinck Date: Fri, 3 Nov 2017 08:40:25 -0500 Subject: [PATCH 3/3] Updated docs for GoToAction --- README.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/README.md b/README.md index d0a897f3e..ca4e85359 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ ReactJS based Presentation Library - [CodePane (Base)](#codepane-base) - [Code (Base)](#code-base) - [ComponentPlayground](#component-playground) + - [GoToAction (Base)](#go-to-action) - [Heading (Base)](#heading-base) - [Image (Base)](#image-base) - [Link (Base)](#link-base) @@ -523,6 +524,34 @@ class View extends React.Component { render(); ``` + +#### Go To Action (Base) + +The GoToAction tag lets you jump to another slide in your deck. The GoToAction can be used a simple button that supports `Base` styling or accept a render prop with a callback to support custom components. + +|Name|PropType|Description| +|---|---|---| +|slide|PropTypes.string or PropTypes.number|The string identifier or number of the side the button should jump to. Slide numbers start at `1`. This is only used in the simple button configuration. +|render|PropTypes.func|A function with a `goToSlide` param that should return a React element to render. This is only used in the custom component configuration. + +##### Simple Button Configuration Example +```jsx +Jump to 3 +``` + +##### Custom Component Configuration Example +```jsx + ( + goToSlide("wait-wut")}> + WAIT WUT!? + + )} +/> +``` + + + #### Heading (Base)