Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -523,6 +524,34 @@ class View extends React.Component {
render(<View />);
```

<a name="go-to-action"></a>
#### 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
<GoToAction slide={3}>Jump to 3</GoToAction>
```

##### Custom Component Configuration Example
```jsx
<GoToAction
render={goToSlide => (
<CustomComponent onClick={() => goToSlide("wait-wut")}>
WAIT WUT!?
</CustomComponent>
)}
/>
```



<a name="heading-base"></a>
#### Heading (Base)

Expand Down
34 changes: 33 additions & 1 deletion example/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -116,6 +116,38 @@ export default class Presentation extends React.Component {
</Heading>
</Appear>
</Slide>
<Slide>
<Heading size={2} textColor="secondary" margin="0.25em">
Mix it up!
</Heading>
<Heading size={6} textColor="tertiary">
You can even jump to different slides with a standard button or custom component!
</Heading>
<GoToAction
margin="1em"
slide={8}
>
Jump to Slide 8
</GoToAction>
<GoToAction
render={goToSlide => (
<select
defaultValue=""
style={{
background: '#000',
color: '#fff',
fontFamily: theme.print.fonts.primary,
fontSize: '1.1em'
}}
onChange={({ target }) => goToSlide(target.value)}
>
<option value="" disabled>Custom Slide Picker</option>
<option value="wait-what">Wait What!? Slide</option>
<option value={2}>Slide 2</option>
</select>
)}
/>
</Slide>
<Slide transition={['slide']} bgDarken={0.75} getAppearStep={this.updateSteps}>
<Appear>
<Heading size={1} caps textColor="tertiary">
Expand Down
7 changes: 7 additions & 0 deletions src/components/__snapshots__/go-to-action.test.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`<GoToAction /> should just render a div when no props are provided 1`] = `
<GoToAction>
<div />
</GoToAction>
`;
55 changes: 55 additions & 0 deletions src/components/go-to-action.js
Original file line number Diff line number Diff line change
@@ -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 (
<GoToActionButton
onClick={() => goToSlide(slide)}
styles={{
context: this.context.styles.components.goToAction,
base: getStyles.call(this),
user: style
}}
>
{children}
</GoToActionButton>
);
}
// eslint-disable-next-line no-console
console.warn('<GoToAction /> must have a render or slide prop.');
return <div />;
}
}

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;
50 changes: 50 additions & 0 deletions src/components/go-to-action.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from 'react';
import { mount } from 'enzyme';
import GoToAction from './go-to-action';

describe('<GoToAction />', () => {
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(
<GoToAction slide={2}>Slide 2</GoToAction>, { 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(
<GoToAction
render={goToSlide => (
<button id="inner-btn" onClick={() => goToSlide('wait-what')}>
WAIT WUT
</button>
)}
/>, { 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(
<GoToAction />, { context }
);
expect(wrapper).toMatchSnapshot();
});
});
27 changes: 20 additions & 7 deletions src/components/manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export class Manager extends Component {
static childContextTypes = {
contentWidth: PropTypes.number,
contentHeight: PropTypes.number,
goToSlide: PropTypes.func
};

constructor(props) {
Expand All @@ -119,6 +120,7 @@ export class Manager extends Component {
return {
contentWidth: this.props.contentWidth,
contentHeight: this.props.contentHeight,
goToSlide: slide => this._goToSlide({ slide })
};
}

Expand Down Expand Up @@ -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() {
Expand Down
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -48,6 +49,7 @@ export {
Fit,
Heading,
Image,
GoToAction,
Layout,
Link,
ListItem,
Expand Down
12 changes: 12 additions & 0 deletions src/themes/default/screen.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down