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 Sheep
s) 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 Sheep
s are visible.
zIndex
?: increase the z-index if needed.id
?: if, under some weird circumstances, theFarmyard
'sid
clashes with some otherid
in your app, you can give it a custom one. However, doing so requires letting theShepherd
know. By default, it'sid
isshepherd-farmyard
.
import { Farmyard } from 'js-shepherd'
export const App = () => (
<>
<Farmyard />
{/** your app here, maybe */}
</>
)
The guy that coordinates the Sheep
s is... the Shepherd
(Provider)!
options
?:farmyardId
: in case you need to give a customid
to yourFarmyard
, match it here. Defaults toshepherd-farmyard
.shouldAutoScrollIntoView
: whetherSheep
s 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 Flock
s.
A Flock
s' single responsability is portalling their Sheep
s 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 Sheep
s.
number
?: by default,Sheep
s are ordered withing aFlock
from0
toN
in the order they are written, and that's the recommended way of usingSheep
s. However,number
allows you to specify your own order.- options?:
delay
: the miliseconds aSheep
should wait until being scrolled into view. Defaults to0
.
- spotRef: a
ref
to the spot (element) aSheep
points to. The spot is the element that determines the position of theSheep
.
useShepherd
allows you to consume:
flock
: the list ofSheep
s.setFlock
: programmatically/imperatively addSheep
s. Whether this is useful or not is hard to tell.shepherd
:activeSheep
: thenumber
of the current activeSheep
.options
: theShepherd
options.
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
'schildren
a good default position based on thespotRef
's.position
:x
: the horizontal position of theSheep
s spot (thex
result of callinggetClientBoundingRect
on the Sheep'sspotRef
).y
: the vertical position of theSheep
s spot (they
result of callinggetClientBoundingRect
on the Sheep'sspotRef
).height
: the height of theSheep
s spot (theheight
result of callinggetClientBoundingRect
on 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>
)