Skip to content

Instantly share code, notes, and snippets.

@vigosan
Last active September 11, 2017 06:57
Show Gist options
  • Save vigosan/e4aea5a38891dee027d6e839c7d61e09 to your computer and use it in GitHub Desktop.
Save vigosan/e4aea5a38891dee027d6e839c7d61e09 to your computer and use it in GitHub Desktop.
Declarative react wizard component
import React, { Component } from 'react'
import PropTypes from 'react-proptypes'
import styles from './App.css'
class Wizard extends Component {
static childContextTypes = {
activeIndex: PropTypes.number.isRequired,
totalSteps: PropTypes.number.isRequired,
goToNextStep: PropTypes.func.isRequired,
goToPrevStep: PropTypes.func.isRequired
}
constructor(props) {
super(props)
let totalSteps = 0
React.Children.forEach(this.props.children, child => {
if (child.type.name === 'Steps') {
totalSteps = React.Children.count(child.props.children)
}
})
this.state = {
activeIndex: 0,
totalSteps
}
}
goToPrevStep = () => {
this.setState({ activeIndex: this.state.activeIndex - 1 })
}
goToNextStep = () => {
this.setState({ activeIndex: this.state.activeIndex + 1 })
}
getChildContext() {
const { activeIndex, totalSteps } = this.state
return {
activeIndex: activeIndex,
totalSteps: totalSteps,
goToPrevStep: this.goToPrevStep,
goToNextStep: this.goToNextStep
}
}
render() {
return <div className="Wizard">{this.props.children}</div>
}
}
class Steps extends Component {
render() {
const { children } = this.props;
const { activeIndex } = this.context;
const currentStep = React.Children.toArray(children)[activeIndex];
return <div className="Wizard-Steps">{currentStep}</div>;
}
}
class Step extends Component {
render() {
return <div>{this.props.children}</div>
}
}
class Contents extends Component {
static contextTypes = {
activeIndex: PropTypes.number.isRequired
}
render() {
const { children } = this.props
const { activeIndex } = this.context
return <div className="Wizard-step">{children[activeIndex]}</div>
}
}
class Content extends Component {
render() {
return <div>{this.props.children}</div>
}
}
class Buttons extends Component {
render() {
return <div>{this.props.children}</div>
}
}
class PreviousStepButton extends Component {
static contextTypes = {
activeIndex: PropTypes.number.isRequired,
goToPrevStep: PropTypes.func.isRequired
}
render() {
const { activeIndex, goToPrevStep } = this.context
const { children } = this.props
const isDisabled = activeIndex === 0
return (
<button disabled={isDisabled} onClick={goToPrevStep}>
{children || 'Prev'}
</button>
)
}
}
class NextStepButton extends Component {
static contextTypes = {
activeIndex: PropTypes.number.isRequired,
totalSteps: PropTypes.number.isRequired,
goToNextStep: PropTypes.func.isRequired
}
render() {
const { activeIndex, totalSteps, goToNextStep } = this.context
const isDisabled = activeIndex === totalSteps - 1
return (
<button disabled={isDisabled} onClick={goToNextStep}>
{this.props.children || 'Next'}
</button>
)
}
}
class ProgressBar extends Component {
static contextTypes = {
activeIndex: PropTypes.number.isRequired,
totalSteps: PropTypes.number.isRequired
}
render() {
const { activeIndex, totalSteps } = this.context
return <progress value={activeIndex + 1} max={totalSteps} />
}
}
const App = () => (
<Wizard>
<ProgressBar />
<Steps>
<Step>First</Step>
<Step>Second</Step>
</Steps>
<Contents>
<Content>This is the first step</Content>
<Content>This is the second step</Content>
</Contents>
<Buttons>
<PreviousStepButton />
<NextStepButton />
</Buttons>
</Wizard>
)
export default App
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment