@slavikdenis/reactour

Tourist Guide into your React Components

Usage no npm install needed!

<script type="module">
  import slavikdenisReactour from 'https://cdn.skypack.dev/@slavikdenis/reactour';
</script>

README

Reactour

Tourist Guide into your React Components

Demo

Edit 6z56m8x18k


Install

npm i -S @slavikdenis/reactour
# or
yarn add @slavikdenis/reactour

From v1.9.1 react@^16.3 is required due to the use of createRef, so:

npm i -S @emotion/core @emotion/styled
# or
yarn add @emotion/core @emotion/styled

Usage

Add the Tour Component in your Application, passing the steps with the elements to highlight during the Tour.

import React from 'react'
import Tour from '@slavikdenis/reactour'

class App extends Component {
  // ...

  render  (
    <>
      { /* other stuff */}
      <Tour
        steps={steps}
        isOpen={this.state.isTourOpen}
        onRequestClose={this.closeTour} />
    </>
  )
}

const steps = [
  {
    selector: '.first-step',
    content: 'This is my first Step',
  },
  // ...
]

Tour Props

accentColor

Change --reactour-accent (defaults to accentColor on IE) css custom prop to apply color in Helper, number, dots, etc

Type: string

Default: #007aff

badgeContent

Customize Badge content using current and total steps values

Type: func

// example
<Tour badgeContent={(curr, tot) => `${curr} of ${tot}`} />

children

Content to be rendered inside the Helper

Type: node | elem

className

Custom class name to add to the Helper

Type: string

closeWithMask

Close the Tour by clicking the Mask

Type: bool

Default: true

disableDotsNavigation

Disable interactivity with Dots navigation in Helper

Type: bool

disableInteraction

Disable the ability to click or intercat in any way with the Highlighted element

Type: bool

disableKeyboardNavigation

Disable all keyboard navigation (next and prev step) when true, disable only selected keys when array

Type: bool | array(['esc', 'right', 'left'])

// example
<Tour disableKeyboardNavigation={['esc']} />

getCurrentStep

Function triggered each time current step change

Type: func

// example
<Tour
  getCurrentStep={(curr) => console.log(`The current step is ${curr + 1}`)}
/>

goToStep

Programmatically change current step after the first render, when the value changes

Type: number

highlightedMaskClassName

Custom class name to add to the element which is the overlay for the target element when disableInteraction

Type: string

inViewThreshold

Tolerance in pixels to add when calculating if an element is outside viewport to scroll into view

Type: number

isOpen

You know…

Type: bool

Required: true

lastStepNextButton

Change Next button in last step into a custom button to close the Tour

Type: node

// example
<Tour lastStepNextButton={<MyButton>Done! Let's start playing</MyButton>} />

maskClassName

Custom class name to add to the Mask

Type: string

maskSpace

Extra Space between in pixels between Highlighted element and Mask

Type: number

Default: 10

nextButton

Renders as next button navigation

Type: node

nextStep

Overrides default nextStep internal function

Type: func

onAfterOpen

Do something after Tour is opened

Type: func

// example
<Tour onAfterOpen={(target) => (document.body.style.overflowY = 'hidden')} />

onBeforeClose

Do something before Tour is closed

Type: func

// example
<Tour onBeforeClose={(target) => (document.body.style.overflowY = 'auto')} />

onRequestClose

Function to close the Tour

Type: func

Required: true

prevButton

Renders as prev button navigation

Type: node

prevStep

Overrides default prevStep internal function

Type: func

rounded

Beautify Helper and Mask with border-radius (in px)

Type: number

Default: 0

scrollDuration

Smooth scroll duration when positioning the target element (in ms)

Type: number

Default: 1

scrollOffset

Offset when positioning the target element after scroll to it

Type: number

Default: a calculation to the center of the viewport

showButtons

Show/Hide Helper Navigation buttons

Type: bool

Default: true

showCloseButton

Show/Hide Helper Close button

Type: bool

Default: true

showNavigation

Show/Hide Helper Navigation Dots

Type: bool

Default: true

showNavigationNumber

Show/Hide number when hovers on each Navigation Dot

Type: bool

Default: true

showNumber

Show/Hide Helper Number Badge

Type: bool

Default: true

startAt

Starting step when Tour is open the first time

Type: number

steps

Array of elements to highligt with special info and props

Type: shape

Required: true

Steps shape
steps: PropTypes.arrayOf(PropTypes.shape({
  'selector': PropTypes.string,
  'content': PropTypes.oneOfType([
    PropTypes.node,
    PropTypes.element,
    PropTypes.func,
  ]).isRequired,
  'position':PropTypes.oneOfType([
    PropTypes.arrayOf(PropTypes.number),
    PropTypes.oneOf(['top', 'right', 'bottom', 'left', 'center']),
  ]),
  'action': PropTypes.func,
  'style': PropTypes.object,
  'stepInteraction': PropTypes.bool,
  'navDotAriaLabel': PropTypes.string,
  'observe': PropTypes.string,
  'highlightedSelectors': PropTypes.array,
  'mutationObservables': PropTypes.array,
  'resizeObservables': PropTypes.array,
})),
Steps example
const steps = [
  {
    selector: '[data-tour="my-first-step"]',
    content: ({ goTo, inDOM }) => (
      <div>
        Lorem ipsum <button onClick={() => goTo(4)}>Go to Step 5</button>
        <br />
        {inDOM && '🎉 Look at your step!'}
      </div>
    ),
    position: 'top',
    // you could do something like:
    // position: [160, 250],
    action: (node) => {
      // by using this, focus trap is temporary disabled
      node.focus()
      console.log('yup, the target element is also focused!')
    },
    style: {
      backgroundColor: '#bada55',
    },
    // Disable interaction for this specific step.
    // Could be enabled passing `true`
    // when `disableInteraction` prop is present in Tour
    stepInteraction: false,
    // Text read to screen reader software for this step's navigation dot
    navDotAriaLabel: 'Go to step 4',
    // Observe direct children DOM mutations of this node
    // If a child is added: the highlighted region is redrawn focused on it
    // If a child is removed: the highlighted region is redrawn focused on the step selector node
    observe: '[data-tour="observable-parent"]',
    // Array of selectors, each selected node will be included (by union)
    // in the highlighted region of the mask. You don't need to add the
    // step selector here as the default highlighted region is focused on it
    highlightedSelectors: ['[data-tour="highlighted-element"]'],
    // Array of selectors, each selected node DOM addition/removal will triggered a rerender
    // of the mask shape. Useful in combinaison with highlightedSelectors when highlighted
    // region of mask should be redrawn after a user action
    mutationObservables: ['[data-tour="mutable-element"]'],
    // Array of selectors, each selected node resize will triggered a rerender of the mask shape.
    // Useful in combinaison with highlightedSelectors when highlighted region of mask should
    // be redrawn after a user action. You should also add the selector in mutationObservables
    // if you want to track DOM addition/removal too
    resizeObservables: ['[data-tour="resizable-parent"]'],
  },
  // ...
]

update

Value to listen if a forced update is needed

Type: string

updateDelay

Delay time when forcing update. Useful when there are known animation/transitions

Type: number

Default: 1

disableFocusLock

Disable FocusLock component.

Type: bool

Default: false

FAQ

How is implemented the scroll lock behaviour in the Demo?

To guarantee a cross browser behaviour we use body-scroll-lock.

Import the library

import { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock'

Create the event handlers

disableBody = target => disableBodyScroll(target)
enableBody = target => enableBodyScroll(target)

Then assign them into the Tour props

<Tour
  {...props}
  onAfterOpen={this.disableBody}
  onBeforeClose={this.enableBody}
/>