@reactour/tour
TypeScript icon, indicating that this package has built-in type declarations

3.6.1 • Public • Published

Reactour

Tourist Guide into your React Components

This documentation is for the latest release, which uses npm scoped package @reactour. The original reactour is now on branch v1 and its documentation can be found here.

Install

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

Usage

Add the TourProvider at the root of your Application, passing the steps of the elements to highlight during the Tour.

// ...
import { TourProvider } from '@reactour/tour'

ReactDOM.render(
  <TourProvider steps={steps}>
    <App />
  </TourProvider>,
  document.getElementById('root')
)

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

Then somewhere down the Application tree, control the Tour using useTour hook.

import { useTour } from '@reactour/tour'

function App() {
  const { setIsOpen } = useTour()
  return (
    <>
      <p className="first-step">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent at
        finibus nulla, quis varius justo. Vestibulum lorem lorem, viverra porta
        metus nec, porta luctus orci
      </p>
      <button onClick={() => setIsOpen(true)}>Open Tour</button>
    </>
  )
}

Examples

Playground

The Playground is the perfect place to play aroud with all @reactour Components. Here is an online version.

Sandboxes

Edit @reactour/tour Demo Template

Feel free to make a PR proposing new sandboxes or demos to add in the playground.

TourProvider

steps?: StepType[]

Array of elements to highlight with special info and props.

StepType

selector: string | Element

A string containing one CSS Selector to match and highlight the element at the time of this step.

content: string | ({ setCurrentStep, transition, isHighlightingObserved, currentStep, setIsOpen }) => void

The content to show inside the Popover at the time of this step. Using a function have parameters to use inside content.

position?: 'top' | 'right' | 'bottom' | 'left' | 'center' | [number, number]

The preferred postion to position the Popover in relation with the highlighted element. Will be automatically calculated in case of unavailable space.

highlightedSelectors?: string[]

Array of CSS Selector to be included (by union) in the highlighted region of the Mask.

mutationObservables?: string[]

Array of CSS Selector that addition or removal will triggered a rerender of the Mask shape.

resizeObservables?: string[]

Array of CSS Selector that when resizeing each will triggered a rerender of the Mask shape.

navDotAriaLabel?: string

String to assign to aria-label attribute of the Dot of this step.

stepInteraction?: boolean

Allow to reenable the interaction for this specific step, when disableInteraction (from TourProvider) is true.

action?: (elem: Element | null) => void

Action fired when the Tour arrives in this step.

actionAfter?: (elem: Element | null) => void

Action fired when the Tour leaves this step.

disableActions?: boolean

Allow to disable all possible actions (interaction with Mask, Navigation Arrows, Navigation Dots, Close button and keyboard events) when the Tour is in this step.

padding?: Padding

Control padding spaces for this specific step.

bypassElem?: boolean

Excludes the main selector when calculating highlited area if present highlightedSelectors.

styles?: StylesObj & PopoverStylesObj & MaskStylesObj

Customize styles fro this specific step.

components?: PopoverComponentsType

Prop to customize granurally each Component inside the Popover.

Components available

key props
Badge styles
Close styles, onClick, disabled
Content content,setCurrentStep,transition, isHighlightingObserved,currentStep,setIsOpen
Navigation styles,setCurrentStep, steps, currentStep, disableDots, nextButton, prevButton, setIsOpen, hideButtons, hideDots, disableAll, rtl, Arrow,
Arrow styles, inverted, disabled
Example
import { components } from '@reactour/tour'

function Badge({ children }) {
  return (
    <components.Badge
      styles={{ badge: (base) => ({ ...base, backgroundColor: 'red' }) }}
    >
      👉 {children} 👈
    </components.Badge>
  )
}

function Close({ onClick }) {
  return (
    <button
      onClick={onClick}
      style={{ position: 'absolute', right: 0, top: 0 }}
    >
      x
    </button>
  )
}

const steps = [
  /* ... */
]

export default function App() {
  return (
    <TourProvider steps={steps} components={{ Badge, Close }}>
      {/* ... */}
    </TourProvider>
  )
}

styles?: StylesObj & PopoverStylesObj & MaskStylesObj

Prop to customize styles for the different parts of the Mask, Popover and Tour using a function that allows to extend the base styles an take advantage of some state props.

Style keys and props available

Refer to Mask docs and Popover docs for its specific Components

Tour Components
key props
badge
controls
button disabled
arrow disabled
dot current, disabled, showNumber
close disabled
Example
const styles = {
  maskWrapper: (base) => ({
    ...base,
    color: 'red',
  }),
  highlightedArea: (base, { x, y }) => ({
    ...base,
    x: x + 10,
    y: y + 10,
  }),
  badge: (base) => ({ ...base, color: 'blue' }),
}

padding?: Padding

Type details
type Padding =
  | number
  | {
      mask?: ComponentPadding
      popover?: ComponentPadding
      wrapper?: ComponentPadding
    }

// x and y same value or [x, y] or [top, x, bottom] or [top, right, bottom, left]
type ComponentPadding = number | number[]

Extra space to add between the Mask and the Popover and the highlighted element. A single number coordinates both spaces. Otherwise, passing an Object specifying the Component space.

position?: Position

Type details
type Position =
  | 'top'
  | 'right'
  | 'bottom'
  | 'left'
  | 'center'
  | [number, number]
  | ((postionsProps: PositionProps) => Position)

type PositionProps = {
  bottom: number
  height: number
  left: number
  right: number
  top: number
  width: number
  windowWidth: number
  windowHeight: number
}

Set a global position for the Popover in all steps, fixed in case of [number, number], calculated in case of position string

setCurrentStep: Dispatch<React.SetStateAction<number>>

Function to control the Tour current step state.

currentStep: number

Custom Tour current step state.

This option could be overrided on specific steps using stepInteraction prop.

disableInteraction?: boolean | ((clickProps: Pick<ClickProps, 'currentStep' | 'steps' | 'meta'>) => boolean)

Disables the ability to click or interact in any way with the Highlighted element on every step.

This option could be overrided on specific steps using stepInteraction prop.

disableFocusLock?: boolean

The Tour uses FocusScope in order to lock the focus iteration inside the Popover when Tour is active. This prop allows to disable this behaviour.

disableDotsNavigation?: boolean

Disable interactivity with Dot navigation inside Popover.

disableWhenSelectorFalsy?: boolean

If true, don't show tours when selector or document.getElementById(step.selector) is falsy.

disableKeyboardNavigation?: boolean | KeyboardParts[]

Type details
type KeyboardParts = 'esc' | 'left' | 'right'

Disable all keyboard navigation events when true, disable only selected keys when array.

default: false

className?: string

Class assigned to Popover.

default: reactour__popover

maskClassName?: string

Class assigned to Mask.

default: reactour__mask

highlightedMaskClassName?: string

Class assigned to highlighted part of Mask. Useful when using disableInteraction.

nextButton?: (props: BtnFnProps) => void

prevButton?: (props: BtnFnProps) => void

Type details
type BtnFnProps = {
  Button: React.FC<NavButtonProps>
  setCurrentStep: Dispatch<React.SetStateAction<number>>
  stepsLength: number
  currentStep: number
  setIsOpen: Dispatch<React.SetStateAction<Boolean>>
}

type NavButtonProps = {
  onClick?: () => void
  kind?: 'next' | 'prev'
  hideArrow?: boolean
}

Helper functions to customize the Next and Prev buttons inside Popover, with useful parameters. It is possible to use the base Button and customize the props.

afterOpen?: (target: Element | null) => void

Action fired just after the Tour is open.

beforeClose?: (target: Element | null) => void

Action fired just before the Tour is closed.

onClickMask?: (clickProps: ClickProps) => void

Type details
type ClickProps = {
  setIsOpen: Dispatch<React.SetStateAction<Boolean>>
  setCurrentStep: Dispatch<React.SetStateAction<number>>
  setSteps: Dispatch<React.SetStateAction<StepType[]>>
  setMeta: Dispatch<React.SetStateAction<string>>
  currentStep: number
  steps: StepType[]
  meta: string
}

Function that overrides the default close behavior of the Mask click handler. Comes with useful parameters to play with.

onClickClose?: (clickProps: ClickProps) => void

Type details
type ClickProps = {
  setIsOpen: Dispatch<React.SetStateAction<Boolean>>
  setCurrentStep: Dispatch<React.SetStateAction<number>>
  setSteps: Dispatch<React.SetStateAction<StepType[]>>
  setMeta: Dispatch<React.SetStateAction<string>>
  currentStep: number
  steps: StepType[]
  meta: string
}

Function that overrides the default close behavior of the Close icon click handler. Comes with useful parameters to play with.

onClickHighlighted?: (e: MouseEventHandler<SVGRectElement>, clickProps: ClickProps) => void

Click handler for highlighted area. Only works when disableInteraction is active. Useful in case is needed to avoid onClickMask when clicking the highlighted element.

Example
<TourProvider
  steps={steps}
  disableInteraction
  onClickHighlighted={(e, clickProps) => {
    console.log('No interaction at all')
    if (clickProps.currentStep < 2) {
      e.stopPropagation()
      event.preventDefault()
      clickProps.setCurrentStep(
        Math.min(clickProps.currentStep + 1, clickProps.steps.length - 1)
      )
    }
  }}
>
  {/* ... */}
</TourProvider>
Type details
type ClickProps = {
  setIsOpen: Dispatch<React.SetStateAction<Boolean>>
  setCurrentStep: Dispatch<React.SetStateAction<number>>
  setSteps: Dispatch<React.SetStateAction<StepType[]>>
  setMeta: Dispatch<React.SetStateAction<string>>
  currentStep: number
  steps: StepType[]
  meta: string
}

keyboardHandler?: KeyboardHandler

Function to handle keyboard events in a custom way.

Type details
type KeyboardHandler = {
  keyboardHandler?: (
    e: KeyboardEvent,
    clickProps?: ClickProps,
    status?: {
      isEscDisabled?: boolean
      isRightDisabled?: boolean
      isLeftDisabled?: boolean
    }
  ) => void
}
Example
<TourProvider
  steps={steps}
  disableInteraction
  keyboardHandler={(e, clickProps) => {
    if (e.key === 'ArrowRight') {
      clickProps.setCurrentStep(
        Math.min(clickProps.currentStep + 1, clickProps.steps.length - 1)
      )
    }
    if (e.key === 'ArrowLeft') {
      clickProps.setCurrentStep(Math.max(clickProps.currentStep - 1, 0))
    }
    if (e.key === 'Escape') {
      const nextStep = Math.floor(Math.random() * clickProps.steps.length)
      clickProps.setCurrentStep(nextStep)
    }
  }}
>
  {/* ... */}
</TourProvider>

badgeContent?: (badgeProps: BadgeProps) => any

Type details
type BadgeProps = {
  totalSteps: number
  currentStep: number
  transition: boolean
}

Function to customize the content of the Badge using helper parameters like the current and total steps and if the Tour is transitioning between steps.

showNavigation?: boolean

Show or hide the Navigation (Prev and Next buttons and Dots) inside Popover.

showPrevNextButtons?: boolean

Show or hide Prev and Next buttons inside Popover.

showCloseButton?: boolean

Show or hide the Close button inside Popover.

showBadge?: boolean

Show or hide the Badge inside Popover.

showDots?: boolean

Show or hide dots navigation inside Popover.

scrollSmooth?: boolean

Activate smooth scroll behavior when steps are outside viewport.

default: false

inViewThreshold?: { x?: number, y?: number } | number

Tolerance in pixels to add when calculating if the step element is outside viewport to scroll into view.

accessibilityOptions?: A11yOptions

Type details
type A11yOptions = {
  ariaLabelledBy: string
  closeButtonAriaLabel: string
  showNavigationScreenReaders: boolean
}

Configure generic accessibility related attributes like aria-labelledby, aria-label for Close button and if show or hide Dot navigation in screen readers.

rtl?: boolean

Option to navigate and show Navigation in right-to-left mode

maskId?: string

Mask ID to pass directly into the Mask component

clipId?: string

Clip ID to pass directly into the Mask component

onTransition?: PositionType

Function to control the behavior of Popover when is transitioning/scrolling from one step to another, calculating with Popover next position and previous one

Type details
type PositionType = (
  postionsProps: PositionProps,
  prev: RectResult
) => 'top' | 'right' | 'bottom' | 'left' | 'center' | [number, number]

ContentComponent?: ComponentType<PopoverContentProps>

Completelly custom component to render inside the Popover.

Type details
type PopoverContentProps = {
  styles?: StylesObj & PopoverStylesObj & MaskStylesObj
  badgeContent?: (badgeProps: BadgeProps) => any
  components?: PopoverComponentsType
  accessibilityOptions?: A11yOptions
  disabledActions?: boolean
  onClickClose?: (clickProps: ClickProps) => void
  setCurrentStep: Dispatch<React.SetStateAction<number>>
  currentStep: number
  transition?: boolean
  isHighlightingObserved?: boolean
  setIsOpen: Dispatch<React.SetStateAction<Boolean>>
  steps: StepType[]
  showNavigation?: boolean
  showPrevNextButtons?: boolean
  showCloseButton?: boolean
  showBadge?: boolean
  nextButton?: (props: BtnFnProps) => void
  prevButton?: (props: BtnFnProps) => void
  disableDotsNavigation?: boolean
  rtl?: boolean
}
Example
function ContentComponent(props) {
  const isLastStep = props.currentStep === props.steps.length - 1
  const content = props.steps[props.currentStep].content
  return (
    <div style={{ border: '5px solid red', padding: 10, background: 'white' }}>
      {/* Check if the step.content is a function or a string */}
      {typeof content === 'function'
        ? content({ ...props, someOtherStuff: 'Custom Text' })
        : content}
      <button
        onClick={() => {
          if (isLastStep) {
            props.setIsOpen(false)
          } else {
            props.setCurrentStep((s) => s + 1)
          }
        }}
      >
        {isLastStep ? 'x' : '>'}
      </button>
    </div>
  )
}

const steps = [
  /* ... */
]

function App() {
  return (
    <TourProvider
      steps={steps}
      ContentComponent={ContentComponent}
      styles={{ popover: (base) => ({ ...base, padding: 0 }) }}
    >
      {/* ... */}
    </TourProvider>
  )
}

Wrapper?: ComponentType

Element which wraps the Tour, useful in case is needed to port the Tour into a Portal. Defaults to React.Fragment

useTour

Later in any Component down in the tree of TourProvider you can control the Tour in many ways

import { useTour } from '@reactour/tour'

function MyComponent() {
  const { isOpen, currentStep, steps, setIsOpen, setCurrentStep, setSteps } = useTour()
  return (
    <>
      <h1>{isOpen ? 'Welcome to the tour!' : 'Thank you for participate!'}</h1>
      <p>
        Now you are visiting the place {currentStep + 1} of {steps.length}
      </p>
      <nav>
        <button onClick={() => setIsOpen(o => !o)}>Toggle Tour</button>
        <button onClick={() => setCurrentStep(3)}>
          Take a fast way to 4th place
        </button>
        <button
          onClick={() =>
            setSteps([
              { selector: '.new-place-1', content: 'New place 1' },
              { selector: '.new-place-2', content: 'New place 2' },
            ])
            setCurrentStep(1)
          }
        >
          Switch to a new set of places, starting from the last one!
        </button>
      </nav>
    </>
  )
}

isOpen: Boolean

Is the Tour open or close

currentStep: number

The current step. zero based

steps: StepType[]

The Array of steps set currently

setIsOpen: Dispatch<React.SetStateAction<Boolean>>

SetState function open or close Tour

setSteps: Dispatch<React.SetStateAction<StepType[]>>

SetState function to update the Array of steps.

meta: string

Global meta information that could be useful in complex Tour/s situtations

setMeta: Dispatch<React.SetStateAction<string>>

SetState function to update the global meta info.

Warning: Make sure you reset the currentStep value using the setCurrentStep function to ensure the tour will be opened to the correct step after update. Otherwise, in case where a person has already interacted with the tour steps and closed the tours on step 5 for example, they might open to the incorrect step, or similarly if the new set of steps only has 3 steps nothing will open.

withTour

In case you needed there is an enhancer that allows you to have all useTour functionalities through a Higher Order Component.

import { Component } from 'react'
import { withTour } from '@reactour/tour'

class MyComponent extends Component {
  render() {
    return (
      <>
        <button onClick={() => this.props.setIsOpen(true)}>Start Tour</button>
        <div>{/* ... */}</div>
      </>
    )
  }
}

export default withTour(MyCompnent)

Package Sidebar

Install

npm i @reactour/tour

Weekly Downloads

38,133

Version

3.6.1

License

MIT

Unpacked Size

98.3 kB

Total Files

8

Last publish

Collaborators

  • elrumordelaluz