UNPKG

@reactour/tour

Version:

<p align="center"> <a href="reactour.js.org"> <img alt="Reactour" title="Reactour" src="https://raw.githubusercontent.com/elrumordelaluz/reactour/main/logo.svg" width="250"></a> </p> <p align="center" style="margin-top: 40px;margin-bottom: 40px;"> <st

801 lines (577 loc) 23.1 kB
<p align="center"> <a href="reactour.js.org"> <img alt="Reactour" title="Reactour" src="https://raw.githubusercontent.com/elrumordelaluz/reactour/main/logo.svg" width="250"></a> </p> <p align="center" style="margin-top: 40px;margin-bottom: 40px;"> <strong>Tourist Guide</strong> into your React Components </p> ## Documentation [https://docs.react.tours](https://docs.react.tours/tour/quickstart) > This documentation is for the latest release, which uses [npm scoped package](https://docs.npmjs.com/cli/v7/using-npm/scope) `@reactour`. The original `reactour` is now on branch `v1` and its documentation can be found [here](https://github.com/elrumordelaluz/reactour/tree/v1). ## Install ```zsh 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_. ```js // ... 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. ```js 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](https://github.com/elrumordelaluz/reactour/tree/main/packages/playground) is the perfect place to play aroud with all `@reactour` _Components_. [Here](https://reactour.vercel.app) is an online version. #### Sandboxes - [Using React Router](https://codesandbox.io/s/reactour-tour-demo-using-react-router-dom-kujql) - [Using React Router with automatic route switching](https://codesandbox.io/s/tour-demo-using-react-router-dom-with-automatic-route-switching-fhdnxb?file=/src/App.js) - [Using React Modal](https://codesandbox.io/s/reactour-tour-demo-using-react-modal-8v0eo) - [Using Semantic UI Modal](https://codesandbox.io/s/reactour-tour-demo-using-semantic-ui-modal-xmqee) - [Using React Bootstrap Modal](https://codesandbox.io/s/reactour-tour-demo-using-react-bootstrap-modal-qjws4) - [Tour with data fetching](https://codesandbox.io/s/tour-with-data-fetching-dv2q0?file=/src/index.js) [![Edit @reactour/tour Demo Template](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/reactour-tour-demo-template-fglzv?fontsize=14&hidenavigation=1&theme=dark) > 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. <details> <summary><code>StepType</code></summary> #### `selector: string | Element` A string containing one [CSS Selector](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors) 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](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors) to be included (by union) in the highlighted region of the _Mask_. #### `mutationObservables?: string[]` Array of [CSS Selector](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors) that addition or removal will triggered a rerender of the _Mask_ shape. #### `resizeObservables?: string[]` Array of [CSS Selector](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors) 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. </details> ### `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` | <details> <summary>Example</summary> ```js 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> ) } ``` </details> ### `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](https://github.com/elrumordelaluz/reactour/tree/main/packages/mask) and [Popover docs](https://github.com/elrumordelaluz/reactour/tree/main/packages/popover) for its specific Components ##### Tour Components | key | props | | ---------- | ----------------------------------- | | `badge` | | | `controls` | | | `button` | `disabled` | | `arrow` | `disabled` | | `dot` | `current`, `disabled`, `showNumber` | | `close` | `disabled` | <details> <summary>Example</summary> ```js const styles = { maskWrapper: (base) => ({ ...base, color: 'red', }), highlightedArea: (base, { x, y }) => ({ ...base, x: x + 10, y: y + 10, }), badge: (base) => ({ ...base, color: 'blue' }), } ``` </details> ### `padding?: Padding` <details> <summary><small>Type details</small></summary> ```ts 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[] ``` </details> 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` <details> <summary><small>Type details</small></summary> ```ts 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 } ``` </details> 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`](#stepinteraction-boolean) 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`](#stepinteraction-boolean) prop. ### `disableFocusLock?: boolean` The _Tour_ uses [FocusScope](https://react-spectrum.adobe.com/react-aria/FocusScope.html) 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[]` <details> <summary><small>Type details</small></summary> ```ts type KeyboardParts = 'esc' | 'left' | 'right' ``` </details> Disable all keyboard navigation events when `true`, disable only selected keys when array. default: `false` ### `className?: string` [Class](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class) assigned to _Popover_. default: `reactour__popover` ### `maskClassName?: string` [Class](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class) assigned to _Mask_. default: `reactour__mask` ### `highlightedMaskClassName?: string` [Class](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class) assigned to highlighted part of _Mask_. Useful when using [`disableInteraction`](#disableinteraction-boolean). ### `nextButton?: (props: BtnFnProps) => void` ### `prevButton?: (props: BtnFnProps) => void` <details> <summary><small>Type details</small></summary> ```ts 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 } ``` </details> 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` <details> <summary><small>Type details</small></summary> ```ts 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 } ``` </details> Function that overrides the default close behavior of the _Mask_ click handler. Comes with useful parameters to play with. ### `onClickClose?: (clickProps: ClickProps) => void` <details> <summary><small>Type details</small></summary> ```ts 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 } ``` </details> 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. <details> <summary>Example</summary> ```jsx <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> ``` </details> <details> <summary><small>Type details</small></summary> ```ts 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 } ``` </details> ### `keyboardHandler?: KeyboardHandler` Function to handle keyboard events in a custom way. <details> <summary><small>Type details</small></summary> ```ts type KeyboardHandler = { keyboardHandler?: ( e: KeyboardEvent, clickProps?: ClickProps, status?: { isEscDisabled?: boolean isRightDisabled?: boolean isLeftDisabled?: boolean } ) => void } ``` </details> <details> <summary>Example</summary> ```jsx <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> ``` </details> ### `badgeContent?: (badgeProps: BadgeProps) => any` <details> <summary><small>Type details</small></summary> ```ts type BadgeProps = { totalSteps: number currentStep: number transition: boolean } ``` </details> 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` <details> <summary><small>Type details</small></summary> ```ts type A11yOptions = { ariaLabelledBy: string closeButtonAriaLabel: string showNavigationScreenReaders: boolean } ``` </details> Configure generic accessibility related attributes like [aria-labelledby](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-labelledby_attribute), [aria-label](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-label_attribute) 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](https://github.com/elrumordelaluz/reactour/tree/main/packages/mask#maskid-string) ### `clipId?: string` Clip ID to pass directly into the [Mask component](https://github.com/elrumordelaluz/reactour/tree/main/packages/mask#clipid-string) ### `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 <details> <summary><small>Type details</small></summary> ```ts type PositionType = ( postionsProps: PositionProps, prev: RectResult ) => 'top' | 'right' | 'bottom' | 'left' | 'center' | [number, number] ``` </details> ### `ContentComponent?: ComponentType<PopoverContentProps>` Completelly custom component to render inside the _Popover_. <details> <summary><small>Type details</small></summary> ```ts 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 } ``` </details> <details> <summary>Example</summary> ```js 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> ) } ``` </details> ### `Wrapper?: ComponentType` Element which wraps the Tour, useful in case is needed to port the Tour into a [Portal](https://reactjs.org/docs/portals.html). Defaults to `React.Fragment` ## `useTour` Later in any Component down in the tree of _TourProvider_ you can control the _Tour_ in many ways ```jsx 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. <!-- disabledActions: false, setDisabledActions: () => false, --> ## `withTour` In case you needed there is an enhancer that allows you to have all `useTour` functionalities through a Higher Order Component. ```jsx 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) ```