Lightweight, headless, in-app tutorial builder to guide your customers to better user experiences as shepherds guide sheeps to the farmyard.
The element that holds your tutorial is a Farmyard.
Your tutorial steps (a.k.a Sheeps) will be portaled to their Farmyard.
It's a container you can put anywhere in your app; it creates a stacking context to make sure your Sheeps are visible.
zIndex?: increase the z-index if needed.id?: if, under some weird circumstances, theFarmyard'sidclashes with some otheridin your app, you can give it a custom one. However, doing so requires letting theShepherdknow. By default, it'sidisshepherd-farmyard.
import { Farmyard } from 'js-shepherd'
export const App = () => (
<>
<Farmyard />
{/** your app here, maybe */}
</>
)The guy that coordinates the Sheeps is... the Shepherd(Provider)!
options?:farmyardId: in case you need to give a customidto yourFarmyard, match it here. Defaults toshepherd-farmyard.shouldAutoScrollIntoView: whetherSheeps should be auto scrolled into view or not.
import { Farmyard, ShepherdProvider } from 'js-shepherd'
export const App = () => (
<ShepherdProvider>
// Any subtree of your app which needs a tutorial!
</ShepherdProvider>
)A group of tutorial steps is a Flock. A tutorial may be composed by one or more Flocks.
A Flocks' single responsability is portalling their Sheeps to the Farmyard.
import * as React from 'react'
import { Flock, Sheep } from 'js-shepherd'
export const Home = () => {
const ref = React.useRef()
return (
<>
<div ref={ref}>I need a tutorial!</div>
<Flock>
<Sheep spotRef={ref}>
{/** `Sheep`s content will be unveiled later */}
</Sheep>
</Flock>
</>
)
}import { Farmyard, ShepherdProvider } from 'js-shepherd'
import { Home } from './Home'
export const App = () => (
<ShepherdProvider>
<Farmyard />
<Home />
</ShepherdProvider>
)A single tutorial step is a Sheep. A flock may be composed by one or more Sheeps.
number?: by default,Sheeps are ordered withing aFlockfrom0toNin the order they are written, and that's the recommended way of usingSheeps. However,numberallows you to specify your own order.- options?:
delay: the miliseconds aSheepshould wait until being scrolled into view. Defaults to0.
- spotRef: a
refto the spot (element) aSheeppoints to. The spot is the element that determines the position of theSheep.
useShepherd allows you to consume:
flock: the list ofSheeps.setFlock: programmatically/imperatively addSheeps. Whether this is useful or not is hard to tell.shepherd:activeSheep: thenumberof the current activeSheep.options: theShepherdoptions.
setShepherd: programmatically/imperatively updateShepherd's state. Whether this is useful or not is hard to tell.goNextSheep: function that goes to the next step.goPreviousSheep: function that goes to the previous step.openFarmyard: function that resets the tutorial to the first step.closeFarmyard: function that dismisses the tutorial.
import * as React from 'react'
import { Flock, Sheep } from 'js-shepherd'
export const Home = () => {
const greetRef = React.useRef()
const farewellRef = React.useRef()
const {
closeFarmyard,
flock,
goNextSheep,
goPreviousSheep,
openFarmyard,
} = useShepherd()
const { length: flockSize } = flock;
return (
<>
<div>
<p>
{activeSheep}/{flockSize}
</p>
<button onClick={openFarmyard}>Restart</button>
<button onClick={goPreviousSheep}>Previous</button>
<button onClick={goNextSheep}>Next</button>
<button onClick={closeFarmyard}>End</button>
</div>
<div ref={greetRef}>Hi!</div>
<div ref={farewellRef}>Bye!</div>
<Flock>
<Sheep spotRef={greetRef}>
Hi!
</Sheep>
<Sheep spotRef={farewellRef}>
Bye!
</Sheep>
</Flock>
</>
)
}import { Farmyard, ShepherdProvider } from 'js-shepherd'
import { Home } from './Home'
export const App = () => (
<ShepherdProvider>
<Farmyard />
<Home />
</ShepherdProvider>
)Passing a function as a Sheep children will be called with the same stuff as useShepherd returns, but, in addition:
getSheepProps: props getter whichs use is always recommended. It gives theSheep'schildrena good default position based on thespotRef's.position:x: the horizontal position of theSheeps spot (thexresult of callinggetClientBoundingRecton the Sheep'sspotRef).y: the vertical position of theSheeps spot (theyresult of callinggetClientBoundingRecton the Sheep'sspotRef).height: the height of theSheeps spot (theheightresult of callinggetClientBoundingRecton the Sheep'sspotRef).
flockLength: the length of theFlock. Will probably be deprecated or added touseShepherd.
import * as React from 'react'
import { Flock, Sheep } from 'js-shepherd'
export const Home = () => {
const greetRef = React.useRef()
const farewellRef = React.useRef()
return (
<>
<div ref={greetRef}>Hi!</div>
<div ref={farewellRef}>Bye!</div>
<Flock>
<Sheep spotRef={greetRef}>
{({ closeFarmyard, getSheepProps, goNextSheep, goPreviousSheep }) => (
<div
{...getSheepProps({
style: {
background: "#eee",
padding: "20px 10px"
}
})}
>
<p>Hi!</p>
<div>
<button onClick={goPreviousSheep}>Previous</button>
<button onClick={closeFarmyard}>Skip</button>
<button onClick={goNextSheep}>Next</button>
</div>
</div>
)}
</Sheep>
<Sheep spotRef={farewellRef}>
{({ closeFarmyard, getSheepProps, goPreviousSheep, goNextSheep }) => (
<div
{...getSheepProps({
style: {
background: "#eee",
padding: "10px 20px"
}
})}
>
<p>Bye!</p>
<div>
<button onClick={goPreviousSheep}>Previous</button>
<button onClick={closeFarmyard}>End</button>
<button onClick={goNextSheep}>Next</button>
</div>
</div>
)}
</Sheep>
</Flock>
</>
)
}import { Farmyard, ShepherdProvider } from 'js-shepherd'
import { Home } from './Home'
export const App = () => (
<ShepherdProvider>
<Farmyard />
<Home />
</ShepherdProvider>
)