Repository: shipshapecode/shepherd
Branch: main
Commit: 850d298cf75a
Files: 190
Total size: 669.1 KB
Directory structure:
gitextract_xaiikd27/
├── .browserslistrc
├── .eslintignore
├── .eslintrc.js
├── .github/
│ ├── CODEOWNERS
│ ├── dependabot.yml
│ ├── stale.yml
│ └── workflows/
│ ├── plan-release.yml
│ ├── publish.yml
│ └── test.yml
├── .gitignore
├── .nojekyll
├── .prettierrc.js
├── .release-plan.json
├── .tool-versions
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── RELEASE.md
├── docs-src/
│ ├── .dockerignore
│ ├── .gitignore
│ ├── .vscode/
│ │ ├── extensions.json
│ │ └── launch.json
│ ├── Dockerfile
│ ├── README.md
│ ├── astro.config.mjs
│ ├── fly.toml
│ ├── package.json
│ ├── src/
│ │ ├── components/
│ │ │ ├── HeadWithPosthog.astro
│ │ │ └── Posthog.astro
│ │ ├── content/
│ │ │ ├── config.ts
│ │ │ └── docs/
│ │ │ ├── api/
│ │ │ │ └── README.md
│ │ │ ├── guides/
│ │ │ │ ├── analytics.mdx
│ │ │ │ ├── install.mdx
│ │ │ │ ├── license.mdx
│ │ │ │ ├── styling.md
│ │ │ │ └── usage.md
│ │ │ └── recipes/
│ │ │ ├── cookbook.md
│ │ │ └── react.md
│ │ ├── env.d.ts
│ │ └── pages/
│ │ └── index.astro
│ └── tsconfig.json
├── landing/
│ ├── .gitignore
│ ├── .vscode/
│ │ ├── extensions.json
│ │ └── launch.json
│ ├── README.md
│ ├── astro.config.mjs
│ ├── package.json
│ ├── public/
│ │ ├── CNAME
│ │ └── favicons/
│ │ └── site.webmanifest
│ ├── src/
│ │ ├── components/
│ │ │ ├── ArticleCard.astro
│ │ │ ├── BaseHead.astro
│ │ │ ├── Footer.astro
│ │ │ ├── FormattedDate.astro
│ │ │ ├── GithubButton.astro
│ │ │ ├── Header.astro
│ │ │ ├── HeaderLink.astro
│ │ │ ├── Icon.astro
│ │ │ └── Posthog.astro
│ │ ├── consts.ts
│ │ ├── content/
│ │ │ ├── blog/
│ │ │ │ ├── alpha-launch-pro.md
│ │ │ │ ├── secret-handshakes-hidden-passages.md
│ │ │ │ ├── shepherd-the-product.md
│ │ │ │ └── tale-of-frameworks.md
│ │ │ └── config.ts
│ │ ├── env.d.ts
│ │ ├── layouts/
│ │ │ ├── Base.astro
│ │ │ ├── BlogPost.astro
│ │ │ └── MainPage.astro
│ │ ├── lib/
│ │ │ └── github.ts
│ │ ├── pages/
│ │ │ ├── api/
│ │ │ │ └── checkout.ts
│ │ │ ├── blog/
│ │ │ │ ├── [...slug].astro
│ │ │ │ └── index.astro
│ │ │ ├── index.astro
│ │ │ ├── pricing.astro
│ │ │ └── rss.xml.js
│ │ └── styles/
│ │ ├── fonts.css
│ │ ├── prism.css
│ │ ├── shepherd.css
│ │ └── styles.css
│ └── tsconfig.json
├── package.json
├── packages/
│ └── react/
│ ├── .gitignore
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── package.json
│ ├── src/
│ │ └── index.tsx
│ ├── test/
│ │ ├── index.test.tsx
│ │ └── setup.ts
│ ├── tsconfig.json
│ ├── vite.config.ts
│ └── vitest.config.ts
├── pnpm-workspace.yaml
├── shepherd.js/
│ ├── .attw.json
│ ├── .eslintignore
│ ├── .eslintrc.cjs
│ ├── .gitignore
│ ├── .prettierignore
│ ├── babel.config.cjs
│ ├── cypress/
│ │ └── downloads/
│ │ └── downloads.html
│ ├── dev/
│ │ ├── assets/
│ │ │ └── favicons/
│ │ │ ├── browserconfig.xml
│ │ │ └── manifest.json
│ │ ├── css/
│ │ │ ├── fonts.css
│ │ │ ├── prism.css
│ │ │ └── welcome.css
│ │ ├── index.html
│ │ └── js/
│ │ └── prism.js
│ ├── dummy/
│ │ ├── assets/
│ │ │ └── favicons/
│ │ │ ├── browserconfig.xml
│ │ │ └── manifest.json
│ │ ├── css/
│ │ │ ├── fonts.css
│ │ │ ├── prism.css
│ │ │ └── welcome.css
│ │ ├── index.html
│ │ └── js/
│ │ └── prism.js
│ ├── eslint.config.js
│ ├── package.json
│ ├── rollup.config.mjs
│ ├── src/
│ │ ├── components/
│ │ │ ├── shepherd-button.css
│ │ │ ├── shepherd-button.ts
│ │ │ ├── shepherd-cancel-icon.css
│ │ │ ├── shepherd-cancel-icon.ts
│ │ │ ├── shepherd-content.css
│ │ │ ├── shepherd-content.ts
│ │ │ ├── shepherd-element.css
│ │ │ ├── shepherd-element.ts
│ │ │ ├── shepherd-footer.css
│ │ │ ├── shepherd-footer.ts
│ │ │ ├── shepherd-header.css
│ │ │ ├── shepherd-header.ts
│ │ │ ├── shepherd-modal.css
│ │ │ ├── shepherd-modal.ts
│ │ │ ├── shepherd-text.css
│ │ │ ├── shepherd-text.ts
│ │ │ ├── shepherd-title.css
│ │ │ └── shepherd-title.ts
│ │ ├── evented.ts
│ │ ├── shepherd.ts
│ │ ├── step.ts
│ │ ├── tour.ts
│ │ └── utils/
│ │ ├── auto-bind.ts
│ │ ├── bind.ts
│ │ ├── cleanup.ts
│ │ ├── dom.ts
│ │ ├── floating-ui.ts
│ │ ├── general.ts
│ │ ├── overlay-path.ts
│ │ └── type-check.ts
│ ├── test/
│ │ ├── cypress/
│ │ │ ├── .gitignore
│ │ │ ├── cypress.config.js
│ │ │ ├── dummy/
│ │ │ │ ├── assets/
│ │ │ │ │ └── favicons/
│ │ │ │ │ ├── browserconfig.xml
│ │ │ │ │ └── manifest.json
│ │ │ │ ├── css/
│ │ │ │ │ ├── fonts.css
│ │ │ │ │ ├── prism.css
│ │ │ │ │ └── welcome.css
│ │ │ │ ├── index.html
│ │ │ │ └── js/
│ │ │ │ └── prism.js
│ │ │ ├── examples/
│ │ │ │ ├── css/
│ │ │ │ │ ├── no-css-import.html
│ │ │ │ │ └── with-css-import.html
│ │ │ │ └── destroying-elements.html
│ │ │ ├── integration/
│ │ │ │ ├── a11y.cy.js
│ │ │ │ ├── css.cy.js
│ │ │ │ ├── destroying-elements.cy.js
│ │ │ │ ├── element-targeting.cy.js
│ │ │ │ ├── modal.cy.js
│ │ │ │ └── test.acceptance.cy.js
│ │ │ ├── support/
│ │ │ │ ├── commands.js
│ │ │ │ └── index.js
│ │ │ └── utils/
│ │ │ ├── default-buttons.js
│ │ │ ├── default-steps.js
│ │ │ └── setup-tour.js
│ │ └── unit/
│ │ ├── babel.config.cjs
│ │ ├── components/
│ │ │ ├── shepherd-button.spec.js
│ │ │ ├── shepherd-content.spec.js
│ │ │ ├── shepherd-element.spec.js
│ │ │ ├── shepherd-footer.spec.js
│ │ │ ├── shepherd-header.spec.js
│ │ │ ├── shepherd-modal.spec.js
│ │ │ ├── shepherd-text.spec.js
│ │ │ └── shepherd-title.spec.js
│ │ ├── evented.spec.js
│ │ ├── server.spec.js
│ │ ├── setupTests.js
│ │ ├── step.spec.js
│ │ ├── tour.spec.js
│ │ └── utils/
│ │ ├── bind.spec.js
│ │ ├── cleanup.spec.js
│ │ └── general.spec.js
│ ├── tsconfig.json
│ └── vitest.config.ts
└── stderr.log
================================================
FILE CONTENTS
================================================
================================================
FILE: .browserslistrc
================================================
# Browsers that we support
last 2 Chrome versions
last 2 Firefox versions
last 2 Safari versions
last 2 Edge versions
Edge 18
================================================
FILE: .eslintignore
================================================
/coverage/
/cypress/dummy/js/prism.js
/dist/
/docs/
/dummy/
/landing/
/site/
/test/cypress/dummy/
================================================
FILE: .eslintrc.js
================================================
module.exports = {
root: true,
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module'
},
extends: ['eslint:recommended', 'plugin:svelte/recommended'],
env: {
browser: true
},
rules: {
'no-console': 'off',
'prefer-const': 'off'
},
overrides: [
// svelte files
{
files: ['**/*.svelte'],
processor: 'svelte/svelte'
},
// Typescript files
{
parser: '@typescript-eslint/parser',
files: ['**/*.ts'],
plugins: ['@typescript-eslint'],
extends: ['plugin:@typescript-eslint/recommended'],
rules: {
'@typescript-eslint/no-unused-vars': [
'error',
{ argsIgnorePattern: '^_' }
],
'prefer-rest-params': 'off'
}
},
// node files
{
files: [
'.eslintrc.js',
'.prettierrc.js',
'babel.config.js',
'svelte.config.js',
'tailwind.config.js'
],
parserOptions: {
sourceType: 'module',
ecmaVersion: 2020
},
env: {
node: true
}
}
]
};
================================================
FILE: .github/CODEOWNERS
================================================
@chuckcarpenter
@RobbieTheWagner
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: daily
time: "10:00"
open-pull-requests-limit: 10
labels:
- dependencies
versioning-strategy: increase
================================================
FILE: .github/stale.yml
================================================
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 30
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- security
# Label to use when marking an issue as stale
staleLabel: wontfix
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
================================================
FILE: .github/workflows/plan-release.yml
================================================
name: Plan Release
on:
workflow_dispatch:
push:
branches:
- main
- master
pull_request_target: # This workflow has permissions on the repo, do NOT run code from PRs in this workflow. See https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
types:
- labeled
- unlabeled
concurrency:
group: plan-release # only the latest one of these should ever be running
cancel-in-progress: true
jobs:
should-run-release-plan-prepare:
name: Should we run release-plan prepare?
runs-on: ubuntu-latest
outputs:
should-prepare: ${{ steps.should-prepare.outputs.should-prepare }}
steps:
- uses: release-plan/actions/should-prepare-release@v1
with:
ref: 'main'
id: should-prepare
create-prepare-release-pr:
name: Create Prepare Release PR
runs-on: ubuntu-latest
timeout-minutes: 5
needs: should-run-release-plan-prepare
permissions:
contents: write
issues: read
pull-requests: write
if: needs.should-run-release-plan-prepare.outputs.should-prepare == 'true'
steps:
- uses: release-plan/actions/prepare@v1
name: Run release-plan prepare
with:
ref: 'main'
env:
GITHUB_AUTH: ${{ secrets.GITHUB_TOKEN }}
id: explanation
- uses: peter-evans/create-pull-request@v8
name: Create Prepare Release PR
with:
commit-message: "Prepare Release ${{ steps.explanation.outputs.new-version}} using 'release-plan'"
labels: "internal"
sign-commits: true
branch: release-preview
title: Prepare Release ${{ steps.explanation.outputs.new-version }}
body: |
This PR is a preview of the release that [release-plan](https://github.com/embroider-build/release-plan) has prepared. To release you should just merge this PR 👍
-----------------------------------------
${{ steps.explanation.outputs.text }}
================================================
FILE: .github/workflows/publish.yml
================================================
# For every push to the primary branch with .release-plan.json modified,
# runs release-plan.
name: Publish Stable
on:
workflow_dispatch:
push:
branches:
- main
- master
paths:
- '.release-plan.json'
concurrency:
group: publish-${{ github.head_ref || github.ref }}
cancel-in-progress: true
jobs:
publish:
name: "NPM Publish"
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
attestations: write
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v6
with:
node-version: 22
registry-url: 'https://registry.npmjs.org'
cache: pnpm
- run: npm install -g npm@latest # ensure that the globally installed npm is new enough to support OIDC
- run: pnpm install --frozen-lockfile
- name: Publish to NPM
run: NPM_CONFIG_PROVENANCE=true pnpm release-plan publish
env:
GITHUB_AUTH: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .github/workflows/test.yml
================================================
name: CI
on:
push:
branches:
- main
paths-ignore:
- 'app/**'
pull_request:
paths-ignore:
- 'app/**'
jobs:
lint:
name: Linting
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v4
- name: Setup pnpm
uses: wyvox/action-setup-pnpm@v3
- name: Install dependencies
run: pnpm install
- name: Run linting
run: pnpm lint
typecheck:
name: Type Check
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v4
- name: Setup pnpm
uses: wyvox/action-setup-pnpm@v3
- name: Install dependencies
run: pnpm install
- name: Check types
run: pnpm types:check
test:
name: Tests
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v4
- name: Setup pnpm
uses: wyvox/action-setup-pnpm@v3
- name: Install Dependencies
run: pnpm install
- name: Install Cypress Binary
run: pnpm cypress:install
- name: Run Jest/Vitest and Cypress Tests
uses: cypress-io/github-action@v6
with:
command: pnpm test:ci
install: false
- name: Upload Coverage to Qlty
uses: qltysh/qlty-action/coverage@v2
with:
token: ${{ secrets.QLTY_COVERAGE_TOKEN }}
files: shepherd.js/test/coverage/lcov.info
add-prefix: shepherd.js/
================================================
FILE: .gitignore
================================================
# Editors
/.idea/
/.vscode/
.eslintcache
.nyc_output/
coverage/
dist/
node_modules/
/.log/
/cypress/
/docs/
/site/
/test/unit/dist
*.DS_Store
/.sass-cache
/npm-debug.log*
/stats.html
/yarn-error.log
# app specific
.DS_Store
.env
.env.local
fly-*.toml
/app/.redwood/*
!.redwood/README.md
dev.db*
dist
dist-babel
node_modules
yarn-error.log
web/public/mockServiceWorker.js
web/types/graphql.d.ts
api/types/graphql.d.ts
api/src/lib/generateGraphiQLHeader.*
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
sendgrid.env
postgres
================================================
FILE: .nojekyll
================================================
================================================
FILE: .prettierrc.js
================================================
'use strict';
module.exports = {
arrowParens: 'always',
proseWrap: 'always',
trailingComma: 'none',
singleQuote: true,
plugins: ['prettier-plugin-astro'],
overrides: [
{
files: '*.astro',
options: {
parser: 'astro'
}
},
{ files: '*.svelte', options: { parser: 'svelte' } }
]
};
================================================
FILE: .release-plan.json
================================================
{
"solution": {
"react-shepherd": {
"impact": "patch",
"oldVersion": "7.0.3",
"newVersion": "7.0.4",
"tagName": "latest",
"constraints": [
{
"impact": "patch",
"reason": "Has dependency `workspace:*` on shepherd.js"
}
],
"pkgJSONPath": "./packages/react/package.json"
},
"shepherd.js": {
"impact": "patch",
"oldVersion": "15.2.1",
"newVersion": "15.2.2",
"tagName": "latest",
"constraints": [
{
"impact": "patch",
"reason": "Appears in changelog section :bug: Bug Fix"
}
],
"pkgJSONPath": "./shepherd.js/package.json"
}
},
"description": "## Release (2026-03-11)\n\n* react-shepherd 7.0.4 (patch)\n* shepherd.js 15.2.2 (patch)\n\n#### :bug: Bug Fix\n* `shepherd.js`\n * [#3376](https://github.com/shipshapecode/shepherd/pull/3376) Fix missing cancel event when navigating back past a hidden first step ([@Copilot](https://github.com/apps/copilot-swe-agent))\n\n#### Committers: 1\n- Copilot [Bot] ([@copilot-swe-agent](https://github.com/apps/copilot-swe-agent))\n"
}
================================================
FILE: .tool-versions
================================================
nodejs 22.22.0
pnpm 10.28.2
================================================
FILE: CHANGELOG.md
================================================
# Changelog
## Release (2026-03-11)
* react-shepherd 7.0.4 (patch)
* shepherd.js 15.2.2 (patch)
#### :bug: Bug Fix
* `shepherd.js`
* [#3376](https://github.com/shipshapecode/shepherd/pull/3376) Fix missing cancel event when navigating back past a hidden first step ([@Copilot](https://github.com/apps/copilot-swe-agent))
#### Committers: 1
- Copilot [Bot] ([@copilot-swe-agent](https://github.com/apps/copilot-swe-agent))
## Release (2026-02-23)
* react-shepherd 7.0.3 (patch)
* shepherd.js 15.2.1 (patch)
#### :bug: Bug Fix
* `shepherd.js`
* [#3373](https://github.com/shipshapecode/shepherd/pull/3373) Use files to reduce what is published to npm ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### Committers: 1
- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
## Release (2026-02-19)
* react-shepherd 7.0.2 (patch)
* shepherd.js 15.2.0 (minor)
#### :rocket: Enhancement
* `shepherd.js`
* [#3370](https://github.com/shipshapecode/shepherd/pull/3370) Add support for HTML attributes on buttons and cancelIcon ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### Committers: 1
- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
## Release (2026-02-18)
* react-shepherd 7.0.1 (patch)
* shepherd.js 15.1.0 (minor)
#### :rocket: Enhancement
* `shepherd.js`
* [#3362](https://github.com/shipshapecode/shepherd/pull/3362) Remove string continuations ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### :house: Internal
* [#3361](https://github.com/shipshapecode/shepherd/pull/3361) Switch to polar astro package ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### Committers: 1
- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
## Release (2026-02-08)
* react-shepherd 7.0.0 (major)
* shepherd.js 15.0.0 (major)
#### :boom: Breaking Change
* `shepherd.js`
* [#3352](https://github.com/shipshapecode/shepherd/pull/3352) Remove svelte and use vanilla TS ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
* [#3342](https://github.com/shipshapecode/shepherd/pull/3342) Drop support for node 18, move tests ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
* `react-shepherd`, `shepherd.js`
* [#3144](https://github.com/shipshapecode/shepherd/pull/3144) Update to Svelte 5 ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### :rocket: Enhancement
* `react-shepherd`
* [#3339](https://github.com/shipshapecode/shepherd/pull/3339) Support React 18+ in peerDeps ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### :bug: Bug Fix
* `shepherd.js`
* [#3351](https://github.com/shipshapecode/shepherd/pull/3351) Restore attachTo tabindex when tour is hidden ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### :memo: Documentation
* `react-shepherd`, `shepherd.js`
* [#3341](https://github.com/shipshapecode/shepherd/pull/3341) More license updates ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
* Other
* [#3340](https://github.com/shipshapecode/shepherd/pull/3340) Update LICENSE.md ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
* `shepherd.js`
* [#3323](https://github.com/shipshapecode/shepherd/pull/3323) Redirect to root when starting demo ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
* `react-shepherd`
* [#3315](https://github.com/shipshapecode/shepherd/pull/3315) Update README example path ([@Poylar](https://github.com/Poylar))
#### :house: Internal
* Other
* [#3343](https://github.com/shipshapecode/shepherd/pull/3343) Update landing to tailwind 4 ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
* `react-shepherd`, `shepherd.js`
* [#3324](https://github.com/shipshapecode/shepherd/pull/3324) pnpm update ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
* [#3302](https://github.com/shipshapecode/shepherd/pull/3302) Remove scarf, bump vitest ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### Committers: 2
- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
- [@Poylar](https://github.com/Poylar)
## Release (2025-07-23)
* react-shepherd 6.1.9 (patch)
* shepherd.js 14.5.1 (patch)
#### :bug: Bug Fix
* `shepherd.js`
* [#3230](https://github.com/shipshapecode/shepherd/pull/3230) Add attachTo elements to the keyboard focus flow ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### :house: Internal
* `shepherd.js`
* [#3229](https://github.com/shipshapecode/shepherd/pull/3229) Add dummy app back for development ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### Committers: 1
- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
## Release (2025-02-20)
react-shepherd 6.1.8 (patch)
shepherd.js 14.5.0 (minor)
#### :rocket: Enhancement
* `shepherd-docs`, `shepherd.js`, `cypress-tests`, `unit-tests`
* [#3051](https://github.com/shipshapecode/shepherd/pull/3051) Allow arrow padding to be configured for a step. ([@JakeThurman](https://github.com/JakeThurman))
#### :house: Internal
* `shepherd-docs`, `landing`, `shepherd.js`, `cypress-tests`, `unit-tests`
* [#3111](https://github.com/shipshapecode/shepherd/pull/3111) Update to Astro 5.x ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### Committers: 2
- Jake Thurman ([@JakeThurman](https://github.com/JakeThurman))
- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
## Release (2025-01-13)
react-shepherd 6.1.7 (patch)
shepherd.js 14.4.0 (minor)
#### :rocket: Enhancement
* `shepherd.js`
* [#3068](https://github.com/shipshapecode/shepherd/pull/3068) 💄 Add reset for dialog element ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### :bug: Bug Fix
* `shepherd.js`, `cypress-tests`
* [#3083](https://github.com/shipshapecode/shepherd/pull/3083) ♿ Remove tabindex to reduce tabs for modal trap ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### :house: Internal
* `landing`
* [#3089](https://github.com/shipshapecode/shepherd/pull/3089) ⬆ Update Polar SDK and use new API ([@chuckcarpenter](https://github.com/chuckcarpenter))
* Other
* [#3066](https://github.com/shipshapecode/shepherd/pull/3066) Prepare Release ([@github-actions[bot]](https://github.com/apps/github-actions))
#### Committers: 2
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
- [@github-actions[bot]](https://github.com/apps/github-actions)
## Release (2024-11-19)
react-shepherd 6.1.6 (patch)
shepherd.js 14.3.0 (minor)
#### :rocket: Enhancement
* `shepherd-docs`, `shepherd.js`, `unit-tests`
* [#3009](https://github.com/shipshapecode/shepherd/pull/3009) Add 'auto' as placement option ([@patrikholcak](https://github.com/patrikholcak))
#### :house: Internal
* `react-shepherd`, `shepherd.js`
* [#3035](https://github.com/shipshapecode/shepherd/pull/3035) Prepare Release ([@github-actions[bot]](https://github.com/apps/github-actions))
#### Committers: 2
- Patrik Holčák ([@patrikholcak](https://github.com/patrikholcak))
- [@github-actions[bot]](https://github.com/apps/github-actions)
## Release (2024-11-19)
react-shepherd 6.1.5 (patch)
shepherd.js 14.2.0 (minor)
#### :rocket: Enhancement
* `shepherd-docs`, `shepherd.js`, `unit-tests`
* [#3009](https://github.com/shipshapecode/shepherd/pull/3009) Add 'auto' as placement option ([@patrikholcak](https://github.com/patrikholcak))
#### Committers: 1
- Patrik Holčák ([@patrikholcak](https://github.com/patrikholcak))
## Release (2024-10-21)
react-shepherd 6.1.4 (patch)
shepherd.js 14.1.0 (minor)
#### :rocket: Enhancement
* `shepherd-docs`, `landing`, `shepherd.js`, `unit-tests`
* [#2995](https://github.com/shipshapecode/shepherd/pull/2995) Implement multiple element highlighting feature ([@YuunsGit](https://github.com/YuunsGit))
#### :bug: Bug Fix
* `shepherd.js`
* [#3011](https://github.com/shipshapecode/shepherd/pull/3011) Fix options type for `options.floatingUIOptions` ([@patrikholcak](https://github.com/patrikholcak))
#### :memo: Documentation
* `landing`
* [#3025](https://github.com/shipshapecode/shepherd/pull/3025) Fix loading on button, add link to root page on logo ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### :house: Internal
* `landing`
* [#3025](https://github.com/shipshapecode/shepherd/pull/3025) Fix loading on button, add link to root page on logo ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
* [#3015](https://github.com/shipshapecode/shepherd/pull/3015) State Fix for Pricing ([@Naman-B-Parlecha](https://github.com/Naman-B-Parlecha))
* [#3013](https://github.com/shipshapecode/shepherd/pull/3013) 🐛 Change check for DOMContentLoaded to astro:page-load ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### Committers: 5
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
- Naman-Parlecha ([@Naman-B-Parlecha](https://github.com/Naman-B-Parlecha))
- Patrik Holčák ([@patrikholcak](https://github.com/patrikholcak))
- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
- Yunus Emre ([@YuunsGit](https://github.com/YuunsGit))
## Release (2024-10-10)
react-shepherd 6.1.3 (patch)
shepherd.js 14.0.1 (patch)
#### :bug: Bug Fix
* `shepherd.js`, `unit-tests`
* [#3005](https://github.com/shipshapecode/shepherd/pull/3005) 🐛 Fix issue where passed in custom middleware is overridden ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### :house: Internal
* [#3003](https://github.com/shipshapecode/shepherd/pull/3003) Prepare Release ([@github-actions[bot]](https://github.com/apps/github-actions))
#### Committers: 2
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
- [@github-actions[bot]](https://github.com/apps/github-actions)
## Release (2024-10-08)
react-shepherd 6.1.2 (patch)
shepherd.js 14.0.0 (major)
#### :boom: Breaking Change
* `shepherd.js`
* [#2976](https://github.com/shipshapecode/shepherd/pull/2976) Update license to AGPL-3.0 ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
* Other
* [#2969](https://github.com/shipshapecode/shepherd/pull/2969) 📄 Update license for commercial use cases ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### :rocket: Enhancement
* `landing`, `shepherd.js`, `cypress-tests`
* [#2982](https://github.com/shipshapecode/shepherd/pull/2982) ✨ Update shepherd element to use html dialog ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### :bug: Bug Fix
* `landing`, `react-shepherd`
* [#2988](https://github.com/shipshapecode/shepherd/pull/2988) 🐛 Landing: Fix checkout issue with correct PAT ([@chuckcarpenter](https://github.com/chuckcarpenter))
* `shepherd.js`
* [#2979](https://github.com/shipshapecode/shepherd/pull/2979) Fix handler type for Evented.off() ([@joeldomke](https://github.com/joeldomke))
* `shepherd-docs`, `landing`, `shepherd.js`
* [#2980](https://github.com/shipshapecode/shepherd/pull/2980) 🐛 Fix event timing issue and add button for demo start ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### :memo: Documentation
* `landing`
* [#2974](https://github.com/shipshapecode/shepherd/pull/2974) Update some various styles ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
* [#2972](https://github.com/shipshapecode/shepherd/pull/2972) ✨ Add updated pricing page and commerical benefits ([@chuckcarpenter](https://github.com/chuckcarpenter))
* `landing`, `react-shepherd`, `shepherd.js`
* [#2973](https://github.com/shipshapecode/shepherd/pull/2973) 📝 Update documentation for correct url ([@chuckcarpenter](https://github.com/chuckcarpenter))
* `shepherd-docs`
* [#2967](https://github.com/shipshapecode/shepherd/pull/2967) 📝 Add docs for analytics and react usage ([@chuckcarpenter](https://github.com/chuckcarpenter))
* Other
* [#2960](https://github.com/shipshapecode/shepherd/pull/2960) 📝 Remove docker info on README ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### :house: Internal
* Other
* [#2981](https://github.com/shipshapecode/shepherd/pull/2981) 🔥 Pro: Remove deprecated pro package ([@chuckcarpenter](https://github.com/chuckcarpenter))
* `landing`
* [#2975](https://github.com/shipshapecode/shepherd/pull/2975) 🐛 Landing: Remove import string from frontmatter ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2968](https://github.com/shipshapecode/shepherd/pull/2968) Landing: 🔥 Remove and upgrade dependencies ([@chuckcarpenter](https://github.com/chuckcarpenter))
* `landing`, `react-shepherd`, `shepherd.js`
* [#2973](https://github.com/shipshapecode/shepherd/pull/2973) 📝 Update documentation for correct url ([@chuckcarpenter](https://github.com/chuckcarpenter))
* `shepherd-docs`
* [#2967](https://github.com/shipshapecode/shepherd/pull/2967) 📝 Add docs for analytics and react usage ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2966](https://github.com/shipshapecode/shepherd/pull/2966) ⬆ Update Astro to v14.15 ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### Committers: 3
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
- Joel Domke ([@joeldomke](https://github.com/joeldomke))
- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
## Release (2024-08-05)
@shepherdpro/pro-js 1.3.1 (patch)
react-shepherd 6.1.1 (patch)
shepherd.js 13.0.3 (patch)
#### :house: Internal
* `shepherd-docs`, `landing`
* [#2944](https://github.com/shipshapecode/shepherd/pull/2944) 🐛 Fix pnpm version in Dockerfiles ([@chuckcarpenter](https://github.com/chuckcarpenter))
* `landing`, `shepherd.js`, `cypress-tests`, `unit-tests`
* [#2938](https://github.com/shipshapecode/shepherd/pull/2938) ✨ Landing: Add storyblok CMS ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### Committers: 1
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
## Release (2024-07-25)
@shepherdpro/pro-js 1.3.0 (minor)
react-shepherd 6.1.0 (minor)
#### :rocket: Enhancement
* `@shepherdpro/pro-js`, `react-shepherd`
* [#2929](https://github.com/shipshapecode/shepherd/pull/2929) ➖ React: Remove extra pro specific setup ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### :memo: Documentation
* `shepherd-docs`
* [#2928](https://github.com/shipshapecode/shepherd/pull/2928) 📝 Docs updates for pro beta setup/usage ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### :house: Internal
* `shepherd-docs`
* [#2928](https://github.com/shipshapecode/shepherd/pull/2928) 📝 Docs updates for pro beta setup/usage ([@chuckcarpenter](https://github.com/chuckcarpenter))
* Other
* [#2925](https://github.com/shipshapecode/shepherd/pull/2925) Prepare Release ([@github-actions[bot]](https://github.com/apps/github-actions))
* [#2924](https://github.com/shipshapecode/shepherd/pull/2924) Update publish.yml to have node-registry-url ([@NullVoxPopuli](https://github.com/NullVoxPopuli))
#### Committers: 3
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
- [@NullVoxPopuli](https://github.com/NullVoxPopuli)
- [@github-actions[bot]](https://github.com/apps/github-actions)
## Release (2024-07-24)
#### :house: Internal
* [#2924](https://github.com/shipshapecode/shepherd/pull/2924) Update publish.yml to have node-registry-url ([@NullVoxPopuli](https://github.com/NullVoxPopuli))
#### Committers: 1
- [@NullVoxPopuli](https://github.com/NullVoxPopuli)
## Release (2024-07-24)
@shepherdpro/pro-js 1.2.2 (patch)
react-shepherd 6.0.8 (patch)
shepherd.js 13.0.2 (patch)
#### :house: Internal
* `@shepherdpro/pro-js`, `react-shepherd`, `shepherd.js`
* [#2922](https://github.com/shipshapecode/shepherd/pull/2922) 👷 Add publish config and scarf paackage ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### Committers: 1
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
## Release (2024-07-22)
@shepherdpro/pro-js 1.2.1 (patch)
react-shepherd 6.0.7 (patch)
#### :bug: Bug Fix
* `@shepherdpro/pro-js`, `react-shepherd`
* [#2920](https://github.com/shipshapecode/shepherd/pull/2920) 👷 Fix package for correct release plan build ordering ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### Committers: 1
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
## Release (2024-07-18)
@shepherdpro/pro-js 1.2.0 (minor)
react-shepherd 6.0.6 (patch)
#### :rocket: Enhancement
* `@shepherdpro/pro-js`
* [#2911](https://github.com/shipshapecode/shepherd/pull/2911) ✨ Pro: Add functions to pro for taking config from API ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### :house: Internal
* `shepherd-docs`, `landing`
* [#2912](https://github.com/shipshapecode/shepherd/pull/2912) ⬆ Update of Astro to v4.12 and related deps upgrades ([@chuckcarpenter](https://github.com/chuckcarpenter))
* `react-shepherd`
* [#2882](https://github.com/shipshapecode/shepherd/pull/2882) ➖ Remove release-it package and configs ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### Committers: 1
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
## Release (2024-06-28)
@shepherdpro/pro-js 1.1.0 (minor)
react-shepherd 6.0.5 (patch)
shepherd.js 13.0.1 (patch)
#### :rocket: Enhancement
* `@shepherdpro/pro-js`
* [#2895](https://github.com/shipshapecode/shepherd/pull/2895) ✨ Add reading for isAutoStart value sent from app ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### :bug: Bug Fix
* `shepherd.js`
* [#2900](https://github.com/shipshapecode/shepherd/pull/2900) 🐛 Add small padding to arrow if edge alignment is used ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### :house: Internal
* [#2898](https://github.com/shipshapecode/shepherd/pull/2898) 👷 Fix changelog error on merges ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### Committers: 1
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
## Release (2024-06-22)
@shepherdpro/pro-js 1.0.4 (patch)
react-shepherd 6.0.4 (patch)
#### :bug: Bug Fix
* `@shepherdpro/pro-js`
* [#2887](https://github.com/shipshapecode/shepherd/pull/2887) 🐛 Fix issue with object merge to push events to pro app ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### Committers: 1
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
## Release (2024-06-21)
@shepherdpro/pro-js 1.0.3 (patch)
react-shepherd 6.0.3 (patch)
#### :bug: Bug Fix
* `@shepherdpro/pro-js`, `react-shepherd`
* [#2885](https://github.com/shipshapecode/shepherd/pull/2885) 🐛 Fix missing tour active check ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### Committers: 1
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
## Release (2024-06-21)
@shepherdpro/pro-js 1.0.2 (patch)
react-shepherd 6.0.2 (patch)
#### :bug: Bug Fix
* `@shepherdpro/pro-js`
* [#2883](https://github.com/shipshapecode/shepherd/pull/2883) 🐛 Fix bug where events fail to fire due to naming ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### :memo: Documentation
* `shepherd-docs`, `landing`
* [#2880](https://github.com/shipshapecode/shepherd/pull/2880) 📝 Add updated documentation for pro package and updated CDN links ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### :house: Internal
* [#2881](https://github.com/shipshapecode/shepherd/pull/2881) Prepare Release ([@github-actions[bot]](https://github.com/apps/github-actions))
#### Committers: 2
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
- [@github-actions[bot]](https://github.com/apps/github-actions)
## Release (2024-06-21)
#### :memo: Documentation
* `shepherd-docs`, `landing`
* [#2880](https://github.com/shipshapecode/shepherd/pull/2880) 📝 Add updated documentation for pro package and updated CDN links ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### Committers: 1
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
## Release (2024-06-20)
@shepherdpro/pro-js 1.0.1 (patch)
react-shepherd 6.0.1 (patch)
#### :bug: Bug Fix
* `@shepherdpro/pro-js`
* [#2878](https://github.com/shipshapecode/shepherd/pull/2878) 👷 Add prepack script to pro-js ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### Committers: 1
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
## Release (2024-06-19)
@shepherdpro/pro-js 1.0.0 (major)
react-shepherd 6.0.0 (major)
shepherd.js 13.0.0 (major)
#### :boom: Breaking Change
* `shepherd-docs`, `@shepherdpro/pro-js`, `react-shepherd`, `shepherd.js`, `cypress-tests`, `unit-tests`
* [#2870](https://github.com/shipshapecode/shepherd/pull/2870) ✨ Add new package specific to Pro features ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### :bug: Bug Fix
* `shepherd-docs`, `react-shepherd`, `shepherd.js`
* [#2871](https://github.com/shipshapecode/shepherd/pull/2871) Simplify dist to only include one TS file ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### :house: Internal
* `landing`
* [#2877](https://github.com/shipshapecode/shepherd/pull/2877) 👷 Add correct token to publish all packages ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2838](https://github.com/shipshapecode/shepherd/pull/2838) tweak: prevents header links overflow in small screens ([@ArnavK-09](https://github.com/ArnavK-09))
#### Committers: 3
- Arnav K ([@ArnavK-09](https://github.com/ArnavK-09))
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
## Release (2024-06-07)
react-shepherd 5.0.6 (patch)
shepherd.js 12.0.6 (patch)
#### :bug: Bug Fix
* `shepherd.js`, `cypress-tests`
* [#2853](https://github.com/shipshapecode/shepherd/pull/2853) fix: reconfigure CSS bundling ([@Kenneth-Sills](https://github.com/Kenneth-Sills))
#### :house: Internal
* `shepherd.js`
* [#2848](https://github.com/shipshapecode/shepherd/pull/2848) Bump @floating-ui/dom from 1.6.3 to 1.6.5 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2851](https://github.com/shipshapecode/shepherd/pull/2851) Bump rollup-plugin-license from 3.3.1 to 3.4.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* `shepherd.js`, `cypress-tests`, `unit-tests`
* [#2849](https://github.com/shipshapecode/shepherd/pull/2849) Bump @babel/core from 7.24.5 to 7.24.6 ([@dependabot[bot]](https://github.com/apps/dependabot))
* Other
* [#2850](https://github.com/shipshapecode/shepherd/pull/2850) Bump release-it from 17.2.1 to 17.3.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2846](https://github.com/shipshapecode/shepherd/pull/2846) App: 🔥 Remove app as it is not part of a workspace ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2844](https://github.com/shipshapecode/shepherd/pull/2844) App: ⬆ Upgrade Shepherd lib in the app to v5 ([@chuckcarpenter](https://github.com/chuckcarpenter))
* `shepherd-docs`
* [#2852](https://github.com/shipshapecode/shepherd/pull/2852) Bump @astrojs/starlight from 0.22.4 to 0.23.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2799](https://github.com/shipshapecode/shepherd/pull/2799) Bump @flydotio/dockerfile from 0.5.6 to 0.5.7 ([@dependabot[bot]](https://github.com/apps/dependabot))
* `unit-tests`
* [#2816](https://github.com/shipshapecode/shepherd/pull/2816) Bump core-js from 3.37.0 to 3.37.1 ([@dependabot[bot]](https://github.com/apps/dependabot))
#### Committers: 2
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
- Kenneth Sills ([@Kenneth-Sills](https://github.com/Kenneth-Sills))
## Release (2024-05-29)
react-shepherd 5.0.5 (patch)
shepherd.js 12.0.5 (patch)
#### :bug: Bug Fix
* `shepherd-docs`, `shepherd.js`, `cypress-tests`
* [#2839](https://github.com/shipshapecode/shepherd/pull/2839) Export CSS and just create one file ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### :house: Internal
* `shepherd-docs`, `landing`
* [#2840](https://github.com/shipshapecode/shepherd/pull/2840) Bump @astrojs/check from 0.5.10 to 0.7.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* `shepherd.js`
* [#2818](https://github.com/shipshapecode/shepherd/pull/2818) Bump eslint-plugin-svelte from 2.38.0 to 2.39.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2835](https://github.com/shipshapecode/shepherd/pull/2835) Landing: ➕ Update node version for deployment action ([@chuckcarpenter](https://github.com/chuckcarpenter))
* `cypress-tests`
* [#2841](https://github.com/shipshapecode/shepherd/pull/2841) Bump chai from 4.4.1 to 5.1.1 ([@dependabot[bot]](https://github.com/apps/dependabot))
* Other
* [#2836](https://github.com/shipshapecode/shepherd/pull/2836) App: ⬆ Upgrade RW version to v7.6 ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### Committers: 2
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
## Release (2024-05-24)
shepherd-docs 0.0.6 (patch)
react-shepherd 5.0.4 (patch)
shepherd.js 12.0.4 (patch)
#### :bug: Bug Fix
* `shepherd-docs`, `landing`, `shepherd.js`, `cypress-tests`
* [#2820](https://github.com/shipshapecode/shepherd/pull/2820) Fix TS issues, more correctly ship CJS and ESM ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### :house: Internal
* `unit-tests`
* [#2814](https://github.com/shipshapecode/shepherd/pull/2814) Bump eslint-plugin-jest from 28.4.0 to 28.5.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* `react-shepherd`
* [#2819](https://github.com/shipshapecode/shepherd/pull/2819) Bump happy-dom from 14.7.1 to 14.11.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
#### Committers: 1
- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
## Release (2024-05-24)
shepherd-docs 0.0.5 (patch)
react-shepherd 5.0.3 (patch)
shepherd.js 12.0.3 (patch)
#### :bug: Bug Fix
* `react-shepherd`
* [#2828](https://github.com/shipshapecode/shepherd/pull/2828) React: 🐛 Fix init to be optional ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2809](https://github.com/shipshapecode/shepherd/pull/2809) React: 👷 Update tsc target to match lowest compatibility ([@chuckcarpenter](https://github.com/chuckcarpenter))
* `landing`
* [#2821](https://github.com/shipshapecode/shepherd/pull/2821) Landing: 🐛 Fix config for canonicalURL ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### :memo: Documentation
* [#2824](https://github.com/shipshapecode/shepherd/pull/2824) Update README.md for incorrect URL ([@pranshugupta54](https://github.com/pranshugupta54))
#### :house: Internal
* `landing`
* [#2827](https://github.com/shipshapecode/shepherd/pull/2827) Landing: 🐛 Update action to build js lib before docker copys it ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2826](https://github.com/shipshapecode/shepherd/pull/2826) Landing: 🐛 Update Dockerfile to build JS lib before copy ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2804](https://github.com/shipshapecode/shepherd/pull/2804) Bump @tailwindcss/typography from 0.5.12 to 0.5.13 ([@dependabot[bot]](https://github.com/apps/dependabot))
* `shepherd-docs`, `landing`, `react-shepherd`, `shepherd.js`
* [#2825](https://github.com/shipshapecode/shepherd/pull/2825) Landing: 🚚 Move blog to index page and update URLs ([@chuckcarpenter](https://github.com/chuckcarpenter))
* Other
* [#2822](https://github.com/shipshapecode/shepherd/pull/2822) App: 🐛 Fix IP address check for webhook ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2811](https://github.com/shipshapecode/shepherd/pull/2811) App: ✨ Remove FF for pricing and edit signup flow ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2810](https://github.com/shipshapecode/shepherd/pull/2810) App: 🗃️ Add created at for users and subscription defaults ([@chuckcarpenter](https://github.com/chuckcarpenter))
* `landing`, `shepherd.js`
* [#2812](https://github.com/shipshapecode/shepherd/pull/2812) Landing: 👷 Add fly.io deployment ([@chuckcarpenter](https://github.com/chuckcarpenter))
* `shepherd-docs`
* [#2808](https://github.com/shipshapecode/shepherd/pull/2808) Bump @astrojs/starlight from 0.21.5 to 0.22.4 ([@dependabot[bot]](https://github.com/apps/dependabot))
* `react-shepherd`
* [#2798](https://github.com/shipshapecode/shepherd/pull/2798) Bump @testing-library/react from 15.0.6 to 15.0.7 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2797](https://github.com/shipshapecode/shepherd/pull/2797) Bump @types/react from 18.3.1 to 18.3.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
* `shepherd-docs`, `landing`
* [#2806](https://github.com/shipshapecode/shepherd/pull/2806) Bump astro from 4.6.3 to 4.8.4 ([@dependabot[bot]](https://github.com/apps/dependabot))
#### Committers: 2
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
- Pranshu Gupta ([@pranshugupta54](https://github.com/pranshugupta54))
## Release (2024-05-14)
shepherd-docs 0.0.4 (patch)
react-shepherd 5.0.2 (patch)
shepherd.js 12.0.2 (patch)
#### :bug: Bug Fix
* `landing`, `shepherd.js`
* [#2805](https://github.com/shipshapecode/shepherd/pull/2805) 🐛 Fixes import issues reported in 2785 ([@chuckcarpenter](https://github.com/chuckcarpenter))
* `shepherd.js`
* [#2794](https://github.com/shipshapecode/shepherd/pull/2794) 🐛 Add missing required type attribute for button ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2793](https://github.com/shipshapecode/shepherd/pull/2793) 🐛 Add fix for modal reshow from 2436 ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### :house: Internal
* Other
* [#2784](https://github.com/shipshapecode/shepherd/pull/2784) Bump release-it from 17.2.0 to 17.2.1 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2790](https://github.com/shipshapecode/shepherd/pull/2790) App: ✨ Add webhook for subscription management ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2788](https://github.com/shipshapecode/shepherd/pull/2788) App: 🗃️ Add migration to give all users a subscription ([@chuckcarpenter](https://github.com/chuckcarpenter))
* `shepherd.js`
* [#2782](https://github.com/shipshapecode/shepherd/pull/2782) Bump rollup from 4.14.3 to 4.17.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
* `react-shepherd`
* [#2778](https://github.com/shipshapecode/shepherd/pull/2778) Bump vite from 5.2.10 to 5.2.11 ([@dependabot[bot]](https://github.com/apps/dependabot))
#### Committers: 1
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
## Release (2024-05-10)
shepherd-docs 0.0.3 (patch)
react-shepherd 5.0.1 (patch)
shepherd.js 12.0.1 (patch)
#### :bug: Bug Fix
* `react-shepherd`, `shepherd.js`
* [#2789](https://github.com/shipshapecode/shepherd/pull/2789) Add prepack scripts ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### :house: Internal
* `react-shepherd`
* [#2781](https://github.com/shipshapecode/shepherd/pull/2781) Bump vite-plugin-dts from 3.9.0 to 3.9.1 ([@dependabot[bot]](https://github.com/apps/dependabot))
* `shepherd.js`, `cypress-tests`, `unit-tests`
* [#2783](https://github.com/shipshapecode/shepherd/pull/2783) Bump @babel/core from 7.24.4 to 7.24.5 ([@dependabot[bot]](https://github.com/apps/dependabot))
* Other
* [#2786](https://github.com/shipshapecode/shepherd/pull/2786) App: 🚑️ Fix to disable post for checkout url ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### Committers: 2
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
## Release (2024-05-06)
shepherd-docs 0.0.2 (patch)
react-shepherd 5.0.0 (major)
shepherd.js 12.0.0 (patch)
#### :boom: Breaking Change
* `react-shepherd`
* [#2707](https://github.com/shipshapecode/shepherd/pull/2707) React: ✨ Adding improved provider for v5 release ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### :memo: Documentation
* `shepherd-docs`
* [#2768](https://github.com/shipshapecode/shepherd/pull/2768) Docs: 📝 Add page for pro analytics ([@chuckcarpenter](https://github.com/chuckcarpenter))
* `shepherd-docs`, `shepherd.js`
* [#2717](https://github.com/shipshapecode/shepherd/pull/2717) Docs: 📝 Move docs generator to Starlight and typedoc ([@chuckcarpenter](https://github.com/chuckcarpenter))
* `shepherd.js`
* [#2735](https://github.com/shipshapecode/shepherd/pull/2735) 🚚 Move copy of main README to lib ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### :house: Internal
* `react-shepherd`
* [#2755](https://github.com/shipshapecode/shepherd/pull/2755) Bump vite-plugin-dts from 3.8.3 to 3.9.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2772](https://github.com/shipshapecode/shepherd/pull/2772) Bump @testing-library/react from 15.0.4 to 15.0.6 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2774](https://github.com/shipshapecode/shepherd/pull/2774) Bump @vitest/ui from 1.5.0 to 1.6.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2759](https://github.com/shipshapecode/shepherd/pull/2759) Bump @types/react from 18.2.79 to 18.3.1 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2753](https://github.com/shipshapecode/shepherd/pull/2753) Bump @testing-library/react from 14.2.2 to 15.0.4 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2754](https://github.com/shipshapecode/shepherd/pull/2754) Bump vitest from 1.4.0 to 1.5.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2742](https://github.com/shipshapecode/shepherd/pull/2742) Bump vite from 5.2.8 to 5.2.10 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2750](https://github.com/shipshapecode/shepherd/pull/2750) Bump @vitest/ui from 1.4.0 to 1.5.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2718](https://github.com/shipshapecode/shepherd/pull/2718) Bump vite-plugin-dts from 3.7.3 to 3.8.3 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2704](https://github.com/shipshapecode/shepherd/pull/2704) Bump happy-dom from 14.3.9 to 14.7.1 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2726](https://github.com/shipshapecode/shepherd/pull/2726) Bump @types/react from 18.2.70 to 18.2.79 ([@dependabot[bot]](https://github.com/apps/dependabot))
* Other
* [#2776](https://github.com/shipshapecode/shepherd/pull/2776) App: ✨ Add initial Chargebee integration, under feature flag ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2751](https://github.com/shipshapecode/shepherd/pull/2751) App: ⬆ Upgrade redwood to 7.4.3 ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2741](https://github.com/shipshapecode/shepherd/pull/2741) App: 💄 Fix benefits layout on mobile ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2740](https://github.com/shipshapecode/shepherd/pull/2740) App: 💄 Add mobile menu and styling for landing page ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2739](https://github.com/shipshapecode/shepherd/pull/2739) App: 🐛 Update footer links to not use component link ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2738](https://github.com/shipshapecode/shepherd/pull/2738) App: 📝 Update analytics text on landing page ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2737](https://github.com/shipshapecode/shepherd/pull/2737) App: 💄 Update main landing page ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2732](https://github.com/shipshapecode/shepherd/pull/2732) Update README.md ([@malthauser](https://github.com/malthauser))
* [#2733](https://github.com/shipshapecode/shepherd/pull/2733) App: 🐛 Add href to reset email link ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2730](https://github.com/shipshapecode/shepherd/pull/2730) App: ✨ Add email for forgot password flow ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2731](https://github.com/shipshapecode/shepherd/pull/2731) App: 🐛 Fix position of second step to allow click on smaller screens ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2728](https://github.com/shipshapecode/shepherd/pull/2728) App: ✨ Add user properties to tour setup ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2714](https://github.com/shipshapecode/shepherd/pull/2714) Bump jose from 4.15.4 to 4.15.5 in /app ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2727](https://github.com/shipshapecode/shepherd/pull/2727) App: 🐛 Update before show of tour to wait for element to render ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2724](https://github.com/shipshapecode/shepherd/pull/2724) App: 🐛 Fix some TS errors from emails fix and prerender landing page ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2723](https://github.com/shipshapecode/shepherd/pull/2723) App: ⬆ Update Redwood to v7.4.1 ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2716](https://github.com/shipshapecode/shepherd/pull/2716) App: ✨ Add description field to demo request form ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2713](https://github.com/shipshapecode/shepherd/pull/2713) App: ⬆ Update Redwood to v7.3.2 ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2706](https://github.com/shipshapecode/shepherd/pull/2706) 🚚 Move platform application files to main repo ([@chuckcarpenter](https://github.com/chuckcarpenter))
* `shepherd-docs`, `landing`
* [#2775](https://github.com/shipshapecode/shepherd/pull/2775) Docs: ➕ Add posthog for analytics to both docs and landing ([@chuckcarpenter](https://github.com/chuckcarpenter))
* `shepherd-docs`
* [#2763](https://github.com/shipshapecode/shepherd/pull/2763) Bump sharp from 0.32.6 to 0.33.3 ([@dependabot[bot]](https://github.com/apps/dependabot))
* `shepherd.js`
* [#2764](https://github.com/shipshapecode/shepherd/pull/2764) Bump cssnano from 6.1.2 to 7.0.1 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2722](https://github.com/shipshapecode/shepherd/pull/2722) Bump rollup from 4.13.2 to 4.14.3 ([@dependabot[bot]](https://github.com/apps/dependabot))
* `unit-tests`
* [#2770](https://github.com/shipshapecode/shepherd/pull/2770) Bump eslint-plugin-jest from 28.2.0 to 28.4.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2703](https://github.com/shipshapecode/shepherd/pull/2703) Bump eslint-plugin-jest from 27.9.0 to 28.2.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2725](https://github.com/shipshapecode/shepherd/pull/2725) App: ✨ Add intro tour to admin portal ([@chuckcarpenter](https://github.com/chuckcarpenter))
* `shepherd.js`, `unit-tests`
* [#2765](https://github.com/shipshapecode/shepherd/pull/2765) Revert CI changes ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
* `landing`
* [#2736](https://github.com/shipshapecode/shepherd/pull/2736) Blog: 🐛 Add sharp for pnpm based md image processing ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2734](https://github.com/shipshapecode/shepherd/pull/2734) Blog: 📝 Add alpha launch blog post ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2687](https://github.com/shipshapecode/shepherd/pull/2687) Bump @astrojs/check from 0.5.9 to 0.5.10 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2698](https://github.com/shipshapecode/shepherd/pull/2698) Bump @astrojs/sitemap from 3.1.1 to 3.1.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2719](https://github.com/shipshapecode/shepherd/pull/2719) Bump astro from 4.5.12 to 4.6.1 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2715](https://github.com/shipshapecode/shepherd/pull/2715) Landing: 💬 Add codepen as OSS user ([@chuckcarpenter](https://github.com/chuckcarpenter))
* `landing`, `react-shepherd`, `shepherd.js`, `cypress-tests`, `unit-tests`
* [#2721](https://github.com/shipshapecode/shepherd/pull/2721) Bump typescript from 5.4.2 to 5.4.5 ([@dependabot[bot]](https://github.com/apps/dependabot))
#### Committers: 3
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
- Matt Althauser ([@malthauser](https://github.com/malthauser))
- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
## v12.0.0-alpha.13 (2024-04-05)
#### :rocket: Enhancement
* [#2684](https://github.com/shipshapecode/shepherd/pull/2684) Use IndexedDB to store tour data ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### :house: Internal
* `react`
* [#2690](https://github.com/shipshapecode/shepherd/pull/2690) Bump vite from 5.1.7 to 5.2.8 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2675](https://github.com/shipshapecode/shepherd/pull/2675) Bump happy-dom from 12.10.3 to 14.3.9 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2685](https://github.com/shipshapecode/shepherd/pull/2685) Bump vite from 5.1.4 to 5.1.7 ([@dependabot[bot]](https://github.com/apps/dependabot))
* Other
* [#2694](https://github.com/shipshapecode/shepherd/pull/2694) Bump cypress from 13.7.1 to 13.7.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2692](https://github.com/shipshapecode/shepherd/pull/2692) Bump @babel/core from 7.24.3 to 7.24.4 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2693](https://github.com/shipshapecode/shepherd/pull/2693) Bump @astrojs/mdx from 2.2.2 to 2.2.4 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2688](https://github.com/shipshapecode/shepherd/pull/2688) Bump @typescript-eslint/eslint-plugin from 7.4.0 to 7.5.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2686](https://github.com/shipshapecode/shepherd/pull/2686) Add tests for `Shepherd.isTourEnabled` ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
* [#2678](https://github.com/shipshapecode/shepherd/pull/2678) Bump astro from 4.5.9 to 4.5.12 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2676](https://github.com/shipshapecode/shepherd/pull/2676) Bump @tailwindcss/typography from 0.5.10 to 0.5.12 ([@dependabot[bot]](https://github.com/apps/dependabot))
#### Committers: 1
- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
## v12.0.0-alpha.12 (2024-04-02)
#### :boom: Breaking Change
* [#2683](https://github.com/shipshapecode/shepherd/pull/2683) Update to Svelte 4, run ESM in tests ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### :house: Internal
* [#2679](https://github.com/shipshapecode/shepherd/pull/2679) Bump @astrojs/mdx from 2.2.1 to 2.2.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2681](https://github.com/shipshapecode/shepherd/pull/2681) Bump @babel/preset-typescript from 7.23.3 to 7.24.1 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2680](https://github.com/shipshapecode/shepherd/pull/2680) Bump rollup from 4.13.0 to 4.13.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2677](https://github.com/shipshapecode/shepherd/pull/2677) Bump tailwindcss from 3.4.1 to 3.4.3 ([@dependabot[bot]](https://github.com/apps/dependabot))
#### Committers: 1
- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
## v12.0.0-alpha.11 (2024-04-01)
#### :bug: Bug Fix
* [#2682](https://github.com/shipshapecode/shepherd/pull/2682) Simplify rollup config ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### Committers: 1
- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
## v12.0.0-alpha.10 (2024-04-01)
#### :house: Internal
* [#2670](https://github.com/shipshapecode/shepherd/pull/2670) Remove NoOp type usage ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### Committers: 1
- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
## v12.0.0-alpha.9 (2024-04-01)
#### :rocket: Enhancement
* [#2669](https://github.com/shipshapecode/shepherd/pull/2669) Ship both cjs and esm ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### :memo: Documentation
* [#2668](https://github.com/shipshapecode/shepherd/pull/2668) ✨ Add dynamic button with stars count from github ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### Committers: 2
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
## v12.0.0-alpha.8 (2024-03-29)
#### :rocket: Enhancement
* `react`
* [#2649](https://github.com/shipshapecode/shepherd/pull/2649) ✨ Add initial React package from SDK ([@chuckcarpenter](https://github.com/chuckcarpenter))
* Other
* [#2667](https://github.com/shipshapecode/shepherd/pull/2667) Move `properties` under the `data` object ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### :bug: Bug Fix
* [#2665](https://github.com/shipshapecode/shepherd/pull/2665) Add async/await for ShepherdPro init ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### Committers: 2
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
## v12.0.0-alpha.7 (2024-03-28)
#### :rocket: Enhancement
* [#2664](https://github.com/shipshapecode/shepherd/pull/2664) Add extra properties bucket to send to Shepherd Pro ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
* [#2648](https://github.com/shipshapecode/shepherd/pull/2648) ✨ Add pro init and explicit ID ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### :bug: Bug Fix
* [#2659](https://github.com/shipshapecode/shepherd/pull/2659) 🐛 Update user ID ref to be direct from localstorage ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### :memo: Documentation
* [#2662](https://github.com/shipshapecode/shepherd/pull/2662) Document release process ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### :house: Internal
* [#2663](https://github.com/shipshapecode/shepherd/pull/2663) Update linting and prettier ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
* [#2656](https://github.com/shipshapecode/shepherd/pull/2656) Bump postcss from 8.4.37 to 8.4.38 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2653](https://github.com/shipshapecode/shepherd/pull/2653) Bump @babel/core from 7.24.0 to 7.24.3 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2661](https://github.com/shipshapecode/shepherd/pull/2661) Bump cssnano from 6.1.0 to 6.1.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2658](https://github.com/shipshapecode/shepherd/pull/2658) Bump release-plan from 0.8.0 to 0.9.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2650](https://github.com/shipshapecode/shepherd/pull/2650) Bump autoprefixer from 10.4.17 to 10.4.19 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2652](https://github.com/shipshapecode/shepherd/pull/2652) Bump cypress from 13.7.0 to 13.7.1 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2657](https://github.com/shipshapecode/shepherd/pull/2657) Bump @babel/preset-env from 7.24.0 to 7.24.3 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2654](https://github.com/shipshapecode/shepherd/pull/2654) Bump astro from 4.5.6 to 4.5.9 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2655](https://github.com/shipshapecode/shepherd/pull/2655) Bump @astrojs/mdx from 2.2.0 to 2.2.1 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2627](https://github.com/shipshapecode/shepherd/pull/2627) Bump cssnano from 6.0.5 to 6.1.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2629](https://github.com/shipshapecode/shepherd/pull/2629) Bump @astrojs/mdx from 2.1.1 to 2.2.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2634](https://github.com/shipshapecode/shepherd/pull/2634) Bump rollup-plugin-svelte from 7.1.6 to 7.2.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
#### Committers: 2
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
## v12.0.0-alpha.6 (2024-03-20)
#### :memo: Documentation
* [#2647](https://github.com/shipshapecode/shepherd/pull/2647) Remove webcomponents polyfill ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### :house: Internal
* [#2635](https://github.com/shipshapecode/shepherd/pull/2635) Bump rollup from 4.12.0 to 4.13.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
#### Committers: 1
- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
## v12.0.0-alpha.5 (2024-03-20)
#### :memo: Documentation
* [#2646](https://github.com/shipshapecode/shepherd/pull/2646) Fix docs build ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### :house: Internal
* [#2637](https://github.com/shipshapecode/shepherd/pull/2637) Bump @astrojs/check from 0.3.4 to 0.5.9 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2644](https://github.com/shipshapecode/shepherd/pull/2644) Bump postcss from 8.4.35 to 8.4.37 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2636](https://github.com/shipshapecode/shepherd/pull/2636) Bump cypress from 13.6.6 to 13.7.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
#### Committers: 1
- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
## v12.0.0-alpha.4 (2024-03-19)
#### :bug: Bug Fix
* [#2640](https://github.com/shipshapecode/shepherd/pull/2640) 🐛 Fix issue with full steps being circular dependency ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### :memo: Documentation
* [#2613](https://github.com/shipshapecode/shepherd/pull/2613) Add TS support for JSDoc ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### :house: Internal
* [#2642](https://github.com/shipshapecode/shepherd/pull/2642) Bump astro from 4.4.15 to 4.5.6 ([@dependabot[bot]](https://github.com/apps/dependabot))
#### Committers: 2
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
## v12.0.0-alpha.3 (2024-03-12)
#### :rocket: Enhancement
* [#2631](https://github.com/shipshapecode/shepherd/pull/2631) Add EventOptions interface, tweak data format ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2632](https://github.com/shipshapecode/shepherd/pull/2632) Move Step and Tour assignment ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### :house: Internal
* [#2630](https://github.com/shipshapecode/shepherd/pull/2630) Bump rollup-plugin-license from 3.2.0 to 3.3.1 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2628](https://github.com/shipshapecode/shepherd/pull/2628) Bump typescript from 5.3.3 to 5.4.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2625](https://github.com/shipshapecode/shepherd/pull/2625) Bump astro from 4.4.8 to 4.4.15 ([@dependabot[bot]](https://github.com/apps/dependabot))
#### Committers: 2
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
## v12.0.0-alpha.2 (2024-03-08)
#### :boom: Breaking Change
* [#2626](https://github.com/shipshapecode/shepherd/pull/2626) Convert to ESM ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### Committers: 1
- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
## v12.0.0-1 (2024-03-08)
## v12.0.0-0 (2024-03-08)
#### :boom: Breaking Change
* [#2610](https://github.com/shipshapecode/shepherd/pull/2610) Move files from src/js to src, convert to TS ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
* [#2572](https://github.com/shipshapecode/shepherd/pull/2572) Drop support for node < 18 ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
#### :rocket: Enhancement
* [#2620](https://github.com/shipshapecode/shepherd/pull/2620) ✨ Add Pro events sharing ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2562](https://github.com/shipshapecode/shepherd/pull/2562) Related to #2399, also stop keydown default behavior when navigation is enabled ([@karendolan](https://github.com/karendolan))
#### :bug: Bug Fix
* [#2551](https://github.com/shipshapecode/shepherd/pull/2551) fix: handle target elements within an iframe ([@rafiazman](https://github.com/rafiazman))
#### :memo: Documentation
* [#2604](https://github.com/shipshapecode/shepherd/pull/2604) 🎨 Add note in docs about importing styles ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2603](https://github.com/shipshapecode/shepherd/pull/2603) 📝 Add blog entry for Feb 2023 ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2582](https://github.com/shipshapecode/shepherd/pull/2582) 📝 Add blog post on Redwood.js ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2563](https://github.com/shipshapecode/shepherd/pull/2563) 🎨 Add blog layout and styling, plus first post! ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2557](https://github.com/shipshapecode/shepherd/pull/2557) 🔥 Remove extra options ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2556](https://github.com/shipshapecode/shepherd/pull/2556) ✨ Add new pricing page with options and contact link ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2549](https://github.com/shipshapecode/shepherd/pull/2549) 🚀 Add Astro and convert landing site to use framework ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2508](https://github.com/shipshapecode/shepherd/pull/2508) Added complete method on usage doc ([@abhayvershwal](https://github.com/abhayvershwal))
#### :house: Internal
* [#2624](https://github.com/shipshapecode/shepherd/pull/2624) Split library and tests into their own workspaces ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
* [#2621](https://github.com/shipshapecode/shepherd/pull/2621) Add release-plan ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
* [#2615](https://github.com/shipshapecode/shepherd/pull/2615) Adjust exports and rollup config ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
* [#2616](https://github.com/shipshapecode/shepherd/pull/2616) Update rollup packages ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
* [#2608](https://github.com/shipshapecode/shepherd/pull/2608) Fix cypress tests ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
* [#2601](https://github.com/shipshapecode/shepherd/pull/2601) 👷 Add typescript with minimal setup ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2605](https://github.com/shipshapecode/shepherd/pull/2605) 📝 Remove `polyfill.io` ([@SukkaW](https://github.com/SukkaW))
* [#2607](https://github.com/shipshapecode/shepherd/pull/2607) Switch from yarn to pnpm, initial monorepo setup ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
* [#2598](https://github.com/shipshapecode/shepherd/pull/2598) 👷 Add remaining yarn 4 files ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2594](https://github.com/shipshapecode/shepherd/pull/2594) ⬆ Upgrade to Yarn v4 ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2574](https://github.com/shipshapecode/shepherd/pull/2574) Bump deps, yarn upgrade ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
* [#2573](https://github.com/shipshapecode/shepherd/pull/2573) Update some actions versions ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
* [#2570](https://github.com/shipshapecode/shepherd/pull/2570) Update to release-it 16 ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
* [#2558](https://github.com/shipshapecode/shepherd/pull/2558) 📈 Add analytics and remove carbon ads ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#2545](https://github.com/shipshapecode/shepherd/pull/2545) Bump prettier from 3.1.0 to 3.1.1 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2544](https://github.com/shipshapecode/shepherd/pull/2544) Bump cypress from 13.6.0 to 13.6.1 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2543](https://github.com/shipshapecode/shepherd/pull/2543) Bump rollup-plugin-visualizer from 5.10.0 to 5.11.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2542](https://github.com/shipshapecode/shepherd/pull/2542) Bump @babel/preset-env from 7.23.3 to 7.23.5 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2541](https://github.com/shipshapecode/shepherd/pull/2541) Bump tailwindcss from 3.3.5 to 3.3.6 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2540](https://github.com/shipshapecode/shepherd/pull/2540) Bump @babel/core from 7.23.3 to 7.23.5 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2537](https://github.com/shipshapecode/shepherd/pull/2537) Bump postcss from 8.4.31 to 8.4.32 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2536](https://github.com/shipshapecode/shepherd/pull/2536) Bump eslint from 8.54.0 to 8.55.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2535](https://github.com/shipshapecode/shepherd/pull/2535) Bump rollup-plugin-visualizer from 5.9.3 to 5.10.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2534](https://github.com/shipshapecode/shepherd/pull/2534) Bump eslint-config-prettier from 9.0.0 to 9.1.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2532](https://github.com/shipshapecode/shepherd/pull/2532) Bump @adobe/css-tools from 4.3.1 to 4.3.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2531](https://github.com/shipshapecode/shepherd/pull/2531) Bump rollup-plugin-visualizer from 5.9.2 to 5.9.3 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2530](https://github.com/shipshapecode/shepherd/pull/2530) Bump svelte-preprocess from 5.1.0 to 5.1.1 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2529](https://github.com/shipshapecode/shepherd/pull/2529) Bump cypress from 13.5.1 to 13.6.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2528](https://github.com/shipshapecode/shepherd/pull/2528) Bump eslint from 8.53.0 to 8.54.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2527](https://github.com/shipshapecode/shepherd/pull/2527) Bump cypress from 13.5.0 to 13.5.1 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2526](https://github.com/shipshapecode/shepherd/pull/2526) Bump start-server-and-test from 2.0.2 to 2.0.3 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2525](https://github.com/shipshapecode/shepherd/pull/2525) Bump svelte-preprocess from 5.0.4 to 5.1.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2524](https://github.com/shipshapecode/shepherd/pull/2524) Bump @babel/core from 7.23.2 to 7.23.3 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2523](https://github.com/shipshapecode/shepherd/pull/2523) Bump prettier from 3.0.3 to 3.1.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2522](https://github.com/shipshapecode/shepherd/pull/2522) Bump cypress from 13.4.0 to 13.5.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2521](https://github.com/shipshapecode/shepherd/pull/2521) Bump @babel/preset-env from 7.23.2 to 7.23.3 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2518](https://github.com/shipshapecode/shepherd/pull/2518) Bump start-server-and-test from 2.0.1 to 2.0.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2519](https://github.com/shipshapecode/shepherd/pull/2519) Bump eslint from 8.52.0 to 8.53.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2517](https://github.com/shipshapecode/shepherd/pull/2517) Bump cypress from 13.3.3 to 13.4.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2515](https://github.com/shipshapecode/shepherd/pull/2515) Bump tailwindcss from 3.3.3 to 3.3.5 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2514](https://github.com/shipshapecode/shepherd/pull/2514) Bump eslint-plugin-jest from 27.4.3 to 27.6.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2513](https://github.com/shipshapecode/shepherd/pull/2513) Bump cypress from 13.3.2 to 13.3.3 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2510](https://github.com/shipshapecode/shepherd/pull/2510) Bump eslint-plugin-jest from 27.4.2 to 27.4.3 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2511](https://github.com/shipshapecode/shepherd/pull/2511) Bump cypress from 13.3.1 to 13.3.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2509](https://github.com/shipshapecode/shepherd/pull/2509) Bump eslint from 8.51.0 to 8.52.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2506](https://github.com/shipshapecode/shepherd/pull/2506) Bump @babel/preset-env from 7.22.20 to 7.23.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2504](https://github.com/shipshapecode/shepherd/pull/2504) Bump rollup-plugin-license from 3.1.0 to 3.2.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2502](https://github.com/shipshapecode/shepherd/pull/2502) Bump cypress from 13.3.0 to 13.3.1 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2503](https://github.com/shipshapecode/shepherd/pull/2503) Bump eslint-plugin-prettier from 5.0.0 to 5.0.1 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2501](https://github.com/shipshapecode/shepherd/pull/2501) Bump @babel/core from 7.23.0 to 7.23.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2497](https://github.com/shipshapecode/shepherd/pull/2497) Bump eslint from 8.50.0 to 8.51.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2494](https://github.com/shipshapecode/shepherd/pull/2494) Bump rimraf from 5.0.1 to 5.0.5 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2493](https://github.com/shipshapecode/shepherd/pull/2493) Bump glob from 10.3.7 to 10.3.10 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2492](https://github.com/shipshapecode/shepherd/pull/2492) Bump cypress from 13.1.0 to 13.3.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2491](https://github.com/shipshapecode/shepherd/pull/2491) Bump eslint from 8.49.0 to 8.50.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2489](https://github.com/shipshapecode/shepherd/pull/2489) Bump eslint-plugin-jest from 27.2.3 to 27.4.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2490](https://github.com/shipshapecode/shepherd/pull/2490) Bump postcss from 8.4.30 to 8.4.31 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2488](https://github.com/shipshapecode/shepherd/pull/2488) Bump chai from 4.3.8 to 4.3.10 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2486](https://github.com/shipshapecode/shepherd/pull/2486) Bump get-func-name from 2.0.0 to 2.0.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2480](https://github.com/shipshapecode/shepherd/pull/2480) Bump glob from 10.3.4 to 10.3.7 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2484](https://github.com/shipshapecode/shepherd/pull/2484) Bump @babel/core from 7.22.20 to 7.23.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2479](https://github.com/shipshapecode/shepherd/pull/2479) Bump start-server-and-test from 2.0.0 to 2.0.1 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2483](https://github.com/shipshapecode/shepherd/pull/2483) Bump autoprefixer from 10.4.15 to 10.4.16 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2482](https://github.com/shipshapecode/shepherd/pull/2482) Bump postcss from 8.4.29 to 8.4.30 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2481](https://github.com/shipshapecode/shepherd/pull/2481) Bump babel-jest from 29.6.4 to 29.7.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2476](https://github.com/shipshapecode/shepherd/pull/2476) Bump jest-environment-jsdom from 29.6.4 to 29.7.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2477](https://github.com/shipshapecode/shepherd/pull/2477) Bump @babel/core from 7.22.17 to 7.22.20 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2475](https://github.com/shipshapecode/shepherd/pull/2475) Bump jest from 29.6.4 to 29.7.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2473](https://github.com/shipshapecode/shepherd/pull/2473) Bump @floating-ui/dom from 1.5.2 to 1.5.3 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2474](https://github.com/shipshapecode/shepherd/pull/2474) Bump @babel/preset-env from 7.22.15 to 7.22.20 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2467](https://github.com/shipshapecode/shepherd/pull/2467) Bump @floating-ui/dom from 1.5.1 to 1.5.2 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2464](https://github.com/shipshapecode/shepherd/pull/2464) Bump @babel/core from 7.22.11 to 7.22.17 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2468](https://github.com/shipshapecode/shepherd/pull/2468) Bump @babel/preset-env from 7.22.14 to 7.22.15 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2465](https://github.com/shipshapecode/shepherd/pull/2465) Bump prettier from 3.0.2 to 3.0.3 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2466](https://github.com/shipshapecode/shepherd/pull/2466) Bump eslint from 8.48.0 to 8.49.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2462](https://github.com/shipshapecode/shepherd/pull/2462) Bump del from 7.0.0 to 7.1.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2463](https://github.com/shipshapecode/shepherd/pull/2463) Bump postcss from 8.4.28 to 8.4.29 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2461](https://github.com/shipshapecode/shepherd/pull/2461) Bump cypress from 12.17.4 to 13.1.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2460](https://github.com/shipshapecode/shepherd/pull/2460) Bump glob from 10.3.3 to 10.3.4 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2459](https://github.com/shipshapecode/shepherd/pull/2459) Bump @babel/preset-env from 7.22.10 to 7.22.14 ([@dependabot[bot]](https://github.com/apps/dependabot))
* [#2458](https://github.com/shipshapecode/shepherd/pull/2458) Bump rollup-plugin-license from 3.0.1 to 3.1.0 ([@dependabot[bot]](https://github.com/apps/dependabot))
#### Committers: 6
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
- Karen Dolan ([@karendolan](https://github.com/karendolan))
- Rafi ([@rafiazman](https://github.com/rafiazman))
- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
- Sukka ([@SukkaW](https://github.com/SukkaW))
- [@abhayvershwal](https://github.com/abhayvershwal)
## v11.2.0 (2023-09-02)
#### :rocket: Enhancement
* [#2399](https://github.com/shipshapecode/shepherd/pull/2399) Prevent ESC, KEY_RIGHT, KEY_LEFT propagation when keyboardNav is enabled ([@karendolan](https://github.com/karendolan))
#### Committers: 1
- Karen Dolan ([@karendolan](https://github.com/karendolan))
## v11.1.1 (2023-04-03)
## v11.1.0 (2023-04-03)
#### :rocket: Enhancement
* [#2168](https://github.com/shipshapecode/shepherd/pull/2168) add funtion support for confirmCancel ([@taozhiyu](https://github.com/taozhiyu))
#### :bug: Bug Fix
* [#2204](https://github.com/shipshapecode/shepherd/pull/2204) Bugfix: prevent to show spurious warnings in dev panel console ([@SamyCookie](https://github.com/SamyCookie))
#### :memo: Documentation
* [#2251](https://github.com/shipshapecode/shepherd/pull/2251) Use a valid event on code example ([@didaquis](https://github.com/didaquis))
* [#2224](https://github.com/shipshapecode/shepherd/pull/2224) Remove mention to Popper on docs ([@didaquis](https://github.com/didaquis))
* [#2205](https://github.com/shipshapecode/shepherd/pull/2205) Fix the typo in docs ([@de-don](https://github.com/de-don))
* [#2174](https://github.com/shipshapecode/shepherd/pull/2174) Adding an example use case of shepherd to Read Me ([@JayP718](https://github.com/JayP718))
#### Committers: 5
- Denis ([@de-don](https://github.com/de-don))
- Dídac García ([@didaquis](https://github.com/didaquis))
- [@JayP718](https://github.com/JayP718)
- [@SamyCookie](https://github.com/SamyCookie)
- 涛之雨 ([@taozhiyu](https://github.com/taozhiyu))
## v11.0.1 (2022-12-12)
#### :bug: Bug Fix
* [#2183](https://github.com/shipshapecode/shepherd/pull/2183) Fix centering steps with no attachTo.on ([@rwwagner90](https://github.com/RobbieTheWagner))
* [#2182](https://github.com/shipshapecode/shepherd/pull/2182) Fix arrow offset, add back flipping behavior ([@rwwagner90](https://github.com/RobbieTheWagner))
#### Committers: 1
- Robert Wagner ([@rwwagner90](https://github.com/RobbieTheWagner))
## v11.0.0 (2022-11-21)
#### :boom: Breaking Change
* [#2037](https://github.com/shipshapecode/shepherd/pull/2037) Replace popperJS with Floating UI ([@theodoreb](https://github.com/theodoreb))
#### :rocket: Enhancement
* [#2137](https://github.com/shipshapecode/shepherd/pull/2137) Exporting StepOptionsButton type to be able to add tour steps buttons dynamically with type checking ([@xhafan](https://github.com/xhafan))
* [#2116](https://github.com/shipshapecode/shepherd/pull/2116) feat: add ability to specify corner radii ([@simoneb](https://github.com/simoneb))
#### :bug: Bug Fix
* [#2068](https://github.com/shipshapecode/shepherd/pull/2068) Do nothing when running server side ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### :memo: Documentation
* [#2129](https://github.com/shipshapecode/shepherd/pull/2129) Replace references to popperjs ([@theodoreb](https://github.com/theodoreb))
* [#2022](https://github.com/shipshapecode/shepherd/pull/2022) remove all mentions of data-shepherd-active-tour ([@EmNicholson93](https://github.com/EmNicholson93))
#### :house: Internal
* [#2157](https://github.com/shipshapecode/shepherd/pull/2157) Remove firefox tests ([@rwwagner90](https://github.com/RobbieTheWagner))
* [#2047](https://github.com/shipshapecode/shepherd/pull/2047) chore: make yarn lint:js pass ([@theodoreb](https://github.com/theodoreb))
* [#2046](https://github.com/shipshapecode/shepherd/pull/2046) Fix calls to setTimeout in tests ([@theodoreb](https://github.com/theodoreb))
#### Committers: 6
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
- Eli Nicholson ([@EmNicholson93](https://github.com/EmNicholson93))
- Martin Havlišta ([@xhafan](https://github.com/xhafan))
- Robert Wagner ([@rwwagner90](https://github.com/RobbieTheWagner))
- Simone Busoli ([@simoneb](https://github.com/simoneb))
- Théodore Biadala ([@theodoreb](https://github.com/theodoreb))
## v10.0.1 (2022-08-01)
#### :bug: Bug Fix
* [#1997](https://github.com/shipshapecode/shepherd/pull/1997) Fix for smooth scrolling ([@hrypkema-amplify](https://github.com/hrypkema-amplify))
#### Committers: 1
- Henrik Rypkema ([@hrypkema-amplify](https://github.com/hrypkema-amplify))
## v10.0.0 (2022-06-07)
#### :boom: Breaking Change
* [#1930](https://github.com/shipshapecode/shepherd/pull/1930) Implement lazy evaluation of attachTo.element ([@monshan](https://github.com/monshan))
#### :bug: Bug Fix
* [#1942](https://github.com/shipshapecode/shepherd/pull/1942) Fix chrome opacity bug ([@monshan](https://github.com/monshan))
#### Committers: 1
- Marika Shanahan ([@monshan](https://github.com/monshan))
## v9.1.1 (2022-05-26)
#### :bug: Bug Fix
* [#1931](https://github.com/shipshapecode/shepherd/pull/1931) fixed showOn incorrectly skipping an index ([@liam-jones-lucout](https://github.com/liam-jones-lucout))
#### :house: Internal
* [#1920](https://github.com/shipshapecode/shepherd/pull/1920) Cleanup jest comments, update yarn.lock ([@monshan](https://github.com/monshan))
* [#1919](https://github.com/shipshapecode/shepherd/pull/1919) Write jest-environment-jsdom into package.json file ([@monshan](https://github.com/monshan))
#### Committers: 2
- Marika Shanahan ([@monshan](https://github.com/monshan))
- [@liam-jones-lucout](https://github.com/liam-jones-lucout)
## v9.1.0 (2022-04-09)
#### :rocket: Enhancement
* [#1815](https://github.com/shipshapecode/shepherd/pull/1815) Add dynamic text and label properties for buttons ([@radibit](https://github.com/radibit))
#### Committers: 1
- Radimir Bitsov ([@radibit](https://github.com/radibit))
## v9.0.0 (2022-01-11)
#### :boom: Breaking Change
* [#1682](https://github.com/shipshapecode/shepherd/pull/1682) Drop node 10 support ([@rwwagner90](https://github.com/RobbieTheWagner))
#### :rocket: Enhancement
* [#1758](https://github.com/shipshapecode/shepherd/pull/1758) Fix type signature for `Evented.off()` ([@ulken](https://github.com/ulken))
#### :bug: Bug Fix
* [#1544](https://github.com/shipshapecode/shepherd/pull/1544) 🐛 Add check to remove class if canClickTarget is true ([@chuckcarpenter](https://github.com/chuckcarpenter))
* [#1479](https://github.com/shipshapecode/shepherd/pull/1479) Fix issue #1353 : Complete the tour when skipStep is the lastStep ([@thomasguittonneau](https://github.com/thomasguittonneau))
#### :memo: Documentation
* [#1513](https://github.com/shipshapecode/shepherd/pull/1513) Add `useModalOverlay` to example ([@rodrigoaraujolima92trulogic](https://github.com/rodrigoaraujolima92trulogic))
* [#1651](https://github.com/shipshapecode/shepherd/pull/1651) Adds Drupal to the list of Projects Using Shepherd ([@thejimbirch](https://github.com/thejimbirch))
* [#1632](https://github.com/shipshapecode/shepherd/pull/1632) I faced a problem while was trying to install shepherdjs ([@loldalolwerollnroll](https://github.com/loldalolwerollnroll))
* [#1623](https://github.com/shipshapecode/shepherd/pull/1623) Add another progress indicator cookbook example ([@aripddev](https://github.com/aripddev))
#### :house: Internal
* [#1520](https://github.com/shipshapecode/shepherd/pull/1520) Update config for tailwind 2 ([@rwwagner90](https://github.com/RobbieTheWagner))
* [#1462](https://github.com/shipshapecode/shepherd/pull/1462) Add automerge back to dependabot ([@rwwagner90](https://github.com/RobbieTheWagner))
* [#1447](https://github.com/shipshapecode/shepherd/pull/1447) Add catalog-info.yaml config file ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### Committers: 8
- Aleksey Baranov ([@loldalolwerollnroll](https://github.com/loldalolwerollnroll))
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
- Jim Birch ([@thejimbirch](https://github.com/thejimbirch))
- Oskar Löfgren ([@ulken](https://github.com/ulken))
- Robert Wagner ([@rwwagner90](https://github.com/RobbieTheWagner))
- Rodrigo Lima ([@rodrigoaraujolima92trulogic](https://github.com/rodrigoaraujolima92trulogic))
- [@thomasguittonneau](https://github.com/thomasguittonneau)
- aripddev ([@aripddev](https://github.com/aripddev))
## v8.3.1 (2021-05-07)
#### :bug: Bug Fix
* [#1449](https://github.com/shipshapecode/shepherd/pull/1449) 🐛 Add preventOverflow option for tether to false ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### Committers: 2
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
- [@dependabot-preview[bot]](https://github.com/apps/dependabot-preview)
## v8.3.0 (2021-04-06)
#### :house: Internal
* [#1402](https://github.com/shipshapecode/shepherd/pull/1402) Update rollup-plugin-postcss ([@rwwagner90](https://github.com/RobbieTheWagner))
#### Committers: 2
- Robert Wagner ([@rwwagner90](https://github.com/RobbieTheWagner))
- [@dependabot-preview[bot]](https://github.com/apps/dependabot-preview)
## v8.2.3 (2021-03-25)
#### :house: Internal
* [#1379](https://github.com/shipshapecode/shepherd/pull/1379) Update npmignore, add assets to release ([@rwwagner90](https://github.com/RobbieTheWagner))
#### Committers: 1
- Robert Wagner ([@rwwagner90](https://github.com/RobbieTheWagner))
## v8.2.1 (2021-03-24)
#### :house: Internal
* [#1354](https://github.com/shipshapecode/shepherd/pull/1354) 👷 Add github token to action ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### Committers: 1
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
## v8.2.0 (2021-03-03)
#### :rocket: Enhancement
* [#1189](https://github.com/shipshapecode/shepherd/pull/1189) Remove `shepherd-target-click-disabled` class on step hide ([@zefj](https://github.com/zefj))
#### :bug: Bug Fix
* [#1332](https://github.com/shipshapecode/shepherd/pull/1332) properly handle centered modal ([@xiwcx](https://github.com/xiwcx))
#### :memo: Documentation
* [#1214](https://github.com/shipshapecode/shepherd/pull/1214) Add license scan report and status ([@fossabot](https://github.com/fossabot))
* [#1199](https://github.com/shipshapecode/shepherd/pull/1199) Add snapsure to Websites and Apps list in README.md ([@kkoppenhaver](https://github.com/kkoppenhaver))
#### :house: Internal
* [#1351](https://github.com/shipshapecode/shepherd/pull/1351) Bump cypress, fix cancel test ([@rwwagner90](https://github.com/RobbieTheWagner))
* [#1316](https://github.com/shipshapecode/shepherd/pull/1316) Remove browsersync, add rollup-serve ([@xiwcx](https://github.com/xiwcx))
* [#1262](https://github.com/shipshapecode/shepherd/pull/1262) 👷 Move CI/CD to actions ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### Committers: 7
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
- Filip Rec ([@zefj](https://github.com/zefj))
- Keanan Koppenhaver ([@kkoppenhaver](https://github.com/kkoppenhaver))
- Robert Wagner ([@rwwagner90](https://github.com/RobbieTheWagner))
- [@dependabot-preview[bot]](https://github.com/apps/dependabot-preview)
- fossabot ([@fossabot](https://github.com/fossabot))
- i. welch canavan ([@xiwcx](https://github.com/xiwcx))
## v8.1.0 (2020-10-05)
#### :rocket: Enhancement
* [#1176](https://github.com/shipshapecode/shepherd/pull/1176) Add `stepsContainer` option, allowing users to specify rendering target for step elements ([@zefj](https://github.com/zefj))
* [#1074](https://github.com/shipshapecode/shepherd/pull/1074) Add missing types for Step.options and Tour.steps ([@anakorn](https://github.com/anakorn))
#### :bug: Bug Fix
* [#1132](https://github.com/shipshapecode/shepherd/pull/1132) Added default fallback for target when step is hidden/destroyed ([@IWMTom](https://github.com/IWMTom))
* [#1119](https://github.com/shipshapecode/shepherd/pull/1119) shift + tab handled ([@faizanu94](https://github.com/faizanu94))
#### :memo: Documentation
* [#1133](https://github.com/shipshapecode/shepherd/pull/1133) docs: Fix simple typo, unminifed -> unminified ([@timgates42](https://github.com/timgates42))
* [#1098](https://github.com/shipshapecode/shepherd/pull/1098) Update demo image ([@shepmaster](https://github.com/shepmaster))
* [#1086](https://github.com/shipshapecode/shepherd/pull/1086) Add multiple events example to docs ([@ricobonfim](https://github.com/ricobonfim))
#### :house: Internal
* [#1044](https://github.com/shipshapecode/shepherd/pull/1044) add Step.id to typings, fix tour.getById() typing ([@cyremur](https://github.com/cyremur))
#### Committers: 9
- Alex Nakorn ([@anakorn](https://github.com/anakorn))
- Filip Rec ([@zefj](https://github.com/zefj))
- Jake Goulding ([@shepmaster](https://github.com/shepmaster))
- Muhammad Faizan Uddin ([@faizanu94](https://github.com/faizanu94))
- Ricardo Bonfim ([@ricobonfim](https://github.com/ricobonfim))
- Tim Gates ([@timgates42](https://github.com/timgates42))
- Tom Wilson ([@IWMTom](https://github.com/IWMTom))
- [@cyremur](https://github.com/cyremur)
- [@dependabot-preview[bot]](https://github.com/apps/dependabot-preview)
## v8.0.2 (2020-07-10)
#### :rocket: Enhancement
* [#1026](https://github.com/shipshapecode/shepherd/pull/1026) Add Edge 18 to browserslist ([@te1](https://github.com/te1))
* [#996](https://github.com/shipshapecode/shepherd/pull/996) Step Element Target Action ([@WORMSS](https://github.com/WORMSS))
* [#995](https://github.com/shipshapecode/shepherd/pull/995) Added the correct this context to StepOption 'when' functions ([@WORMSS](https://github.com/WORMSS))
#### :bug: Bug Fix
* [#1039](https://github.com/shipshapecode/shepherd/pull/1039) Use isElement fot svg scrollIntoView support ([@rwwagner90](https://github.com/RobbieTheWagner))
#### :house: Internal
* [#1040](https://github.com/shipshapecode/shepherd/pull/1040) Add secondary property on StepOptionsButton ([@linsolas](https://github.com/linsolas))
* [#1006](https://github.com/shipshapecode/shepherd/pull/1006) Switch to svelte-jester, test with node 12 ([@rwwagner90](https://github.com/RobbieTheWagner))
#### Committers: 5
- Robert Wagner ([@rwwagner90](https://github.com/RobbieTheWagner))
- Romain Linsolas ([@linsolas](https://github.com/linsolas))
- WORMSS ([@WORMSS](https://github.com/WORMSS))
- [@dependabot-preview[bot]](https://github.com/apps/dependabot-preview)
- te ([@te1](https://github.com/te1))
## v8.0.1 (2020-05-30)
## v8.0.0 (2020-05-25)
#### :boom: Breaking Change
* [#982](https://github.com/shipshapecode/shepherd/pull/982) Officially drop IE 11 and remove from browserslist ([@rwwagner90](https://github.com/RobbieTheWagner))
#### :bug: Bug Fix
* [#979](https://github.com/shipshapecode/shepherd/pull/979) Require attachTo.on to show arrow ([@rwwagner90](https://github.com/RobbieTheWagner))
#### :memo: Documentation
* [#967](https://github.com/shipshapecode/shepherd/pull/967) 📝 Add cookbook item for multi item highlighting ([@chuckcarpenter](https://github.com/chuckcarpenter))
#### :house: Internal
* [#981](https://github.com/shipshapecode/shepherd/pull/981) Remove eslint from rollup, tweak tailwind ([@rwwagner90](https://github.com/RobbieTheWagner))
#### Committers: 3
- Chuck Carpenter ([@chuckcarpenter](https://github.com/chuckcarpenter))
- Robert Wagner ([@rwwagner90](https://github.com/RobbieTheWagner))
- [@dependabot-preview[bot]](https://github.com/apps/dependabot-preview)
# Changelog
## [v7.0.2](https://github.com/shipshapecode/shepherd/tree/v7.0.2) (2020-02-25)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v7.0.1...v7.0.2)
**Fixed bugs:**
- Fix bug when modifiers was not defined [\#811](https://github.com/shipshapecode/shepherd/pull/811) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
**Closed issues:**
- Installing v7.0.0 fails [\#810](https://github.com/shipshapecode/shepherd/issues/810)
## [v7.0.1](https://github.com/shipshapecode/shepherd/tree/v7.0.1) (2020-02-24)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v7.0.0...v7.0.1)
## [v7.0.0](https://github.com/shipshapecode/shepherd/tree/v7.0.0) (2020-02-24)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v6.0.2...v7.0.0)
**Breaking changes:**
- \[WIP\] Popper v2 [\#752](https://github.com/shipshapecode/shepherd/pull/752) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
**Implemented enhancements:**
- Refactor back to Popper [\#744](https://github.com/shipshapecode/shepherd/issues/744)
**Fixed bugs:**
- Smooth scrolling not working? [\#788](https://github.com/shipshapecode/shepherd/issues/788)
- Overlay does not resize [\#751](https://github.com/shipshapecode/shepherd/issues/751)
- 🐛 Fix initial scroll to top before scrolling to target [\#801](https://github.com/shipshapecode/shepherd/pull/801) ([chuckcarpenter](https://github.com/chuckcarpenter))
**Closed issues:**
- .shepherd-arrow does not "flip" when scroll/resize causes a tour stop to switch sides [\#787](https://github.com/shipshapecode/shepherd/issues/787)
- Arrow position is incorrect for ion-fab element \#ionic [\#745](https://github.com/shipshapecode/shepherd/issues/745)
- shepherd in webview does not show buttons [\#721](https://github.com/shipshapecode/shepherd/issues/721)
**Merged pull requests:**
- 🔥 Remove demo directory and use landing site as default [\#802](https://github.com/shipshapecode/shepherd/pull/802) ([chuckcarpenter](https://github.com/chuckcarpenter))
- 🐛 Fix step positioning for viewport [\#797](https://github.com/shipshapecode/shepherd/pull/797) ([chuckcarpenter](https://github.com/chuckcarpenter))
## [v6.0.2](https://github.com/shipshapecode/shepherd/tree/v6.0.2) (2019-11-08)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v6.0.1...v6.0.2)
## [v6.0.1](https://github.com/shipshapecode/shepherd/tree/v6.0.1) (2019-11-06)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v6.0.0...v6.0.1)
## [v5.0.0](https://github.com/shipshapecode/shepherd/tree/v5.0.0) (2019-08-25)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v4.6.0...v5.0.0)
**Breaking changes:**
- Remove style vendor prefixing [\#519](https://github.com/shipshapecode/shepherd/pull/519) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- showCancelLink -\> cancelIcon [\#518](https://github.com/shipshapecode/shepherd/pull/518) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Remove link styles [\#509](https://github.com/shipshapecode/shepherd/pull/509) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Remove Shepherd.Evented [\#506](https://github.com/shipshapecode/shepherd/pull/506) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
**Implemented enhancements:**
- Vue wrapper [\#333](https://github.com/shipshapecode/shepherd/issues/333)
- Add includeStyles option [\#526](https://github.com/shipshapecode/shepherd/pull/526) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Tie modal z-index to shepherdElementZIndex [\#523](https://github.com/shipshapecode/shepherd/pull/523) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Add disableScroll to types [\#522](https://github.com/shipshapecode/shepherd/pull/522) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Types don't support activeTour or Evented properties. [\#504](https://github.com/shipshapecode/shepherd/issues/504)
- Remove object-assign-deep, refactor setting popper options [\#516](https://github.com/shipshapecode/shepherd/pull/516) ([genadis](https://github.com/genadis))
- Use requestAnimationFrame to position modal opening [\#514](https://github.com/shipshapecode/shepherd/pull/514) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Add overlayOpacity to styleVariables options [\#512](https://github.com/shipshapecode/shepherd/pull/512) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Add keyboardNav and exitOnEsc options [\#508](https://github.com/shipshapecode/shepherd/pull/508) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Move activeTour to namespace [\#507](https://github.com/shipshapecode/shepherd/pull/507) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
**Fixed bugs:**
- advanceOn click doesn't work on nested elements [\#511](https://github.com/shipshapecode/shepherd/issues/511)
- Use currentTarget for advanceOn [\#513](https://github.com/shipshapecode/shepherd/pull/513) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
**Closed issues:**
- Z-Index Issues [\#521](https://github.com/shipshapecode/shepherd/issues/521)
- ionic element - bubbles not pointing to right place due to clientHeight = 0 \(etc.\) [\#426](https://github.com/shipshapecode/shepherd/issues/426)
- Disable built in component styles [\#497](https://github.com/shipshapecode/shepherd/issues/497)
- Feature Request: I18n cancel link [\#499](https://github.com/shipshapecode/shepherd/issues/499)
**Merged pull requests:**
- Update rimraf to the latest version 🚀 [\#515](https://github.com/shipshapecode/shepherd/pull/515) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
- Update rollup-plugin-license to the latest version 🚀 [\#505](https://github.com/shipshapecode/shepherd/pull/505) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
- adding alt and role to img element [\#503](https://github.com/shipshapecode/shepherd/pull/503) ([MelSumner](https://github.com/MelSumner))
- fixing a11y issue by adding lang attribute to html element [\#501](https://github.com/shipshapecode/shepherd/pull/501) ([MelSumner](https://github.com/MelSumner))
## [v4.6.0](https://github.com/shipshapecode/shepherd/tree/v4.6.0) (2019-08-09)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v4.5.0...v4.6.0)
**Implemented enhancements:**
- Wrong type definition for scrollTo [\#490](https://github.com/shipshapecode/shepherd/issues/490)
- Fade in modal overlay [\#496](https://github.com/shipshapecode/shepherd/pull/496) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Fix for the wrong type definition of StepOptions.scrollTo [\#494](https://github.com/shipshapecode/shepherd/pull/494) ([moxival](https://github.com/moxival))
## [v4.5.0](https://github.com/shipshapecode/shepherd/tree/v4.5.0) (2019-08-09)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v4.4.1...v4.5.0)
**Fixed bugs:**
- Passing 'HTMLElement' to tour.addStep{ text: ... } doesn't work anymore [\#492](https://github.com/shipshapecode/shepherd/issues/492)
- Support passing elements for text [\#493](https://github.com/shipshapecode/shepherd/pull/493) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
## [v4.4.1](https://github.com/shipshapecode/shepherd/tree/v4.4.1) (2019-08-07)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v4.4.0...v4.4.1)
**Fixed bugs:**
- ShepherdClass missing after adding modifiers of popper [\#486](https://github.com/shipshapecode/shepherd/issues/486)
- Use objectAssignDeep to deeply merge tippyOptions [\#488](https://github.com/shipshapecode/shepherd/pull/488) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
## [v4.4.0](https://github.com/shipshapecode/shepherd/tree/v4.4.0) (2019-08-05)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v4.3.4...v4.4.0)
**Implemented enhancements:**
- Add addSteps method and allow passing steps to tour constructor [\#485](https://github.com/shipshapecode/shepherd/pull/485) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
**Fixed bugs:**
- Shepherd.Tour constructor definition of steps errors with showOn being undefined [\#114](https://github.com/shipshapecode/shepherd/issues/114)
## [v4.3.4](https://github.com/shipshapecode/shepherd/tree/v4.3.4) (2019-08-04)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v4.3.3...v4.3.4)
**Implemented enhancements:**
- styleVariables missing in 'TourOptions' declaration and beforeShowStep not implemented [\#483](https://github.com/shipshapecode/shepherd/issues/483)
- Fix some types and docs [\#484](https://github.com/shipshapecode/shepherd/pull/484) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
## [v4.3.3](https://github.com/shipshapecode/shepherd/tree/v4.3.3) (2019-08-02)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v4.3.2...v4.3.3)
**Implemented enhancements:**
- Fix some TypeScript issues [\#482](https://github.com/shipshapecode/shepherd/pull/482) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
## [v4.3.2](https://github.com/shipshapecode/shepherd/tree/v4.3.2) (2019-08-02)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v4.3.1...v4.3.2)
**Implemented enhancements:**
- Add confirmCancel and confirmCancelMessage to types [\#480](https://github.com/shipshapecode/shepherd/pull/480) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
## [v4.3.1](https://github.com/shipshapecode/shepherd/tree/v4.3.1) (2019-08-02)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v4.3.0...v4.3.1)
**Implemented enhancements:**
- Closing the tour should move the focus back to the element that opened it [\#473](https://github.com/shipshapecode/shepherd/issues/473)
- Return focus after closing the tour [\#479](https://github.com/shipshapecode/shepherd/pull/479) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
**Merged pull requests:**
- Update rollup-plugin-license to the latest version 🚀 [\#478](https://github.com/shipshapecode/shepherd/pull/478) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
## [v4.3.0](https://github.com/shipshapecode/shepherd/tree/v4.3.0) (2019-08-01)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v4.2.0...v4.3.0)
**Implemented enhancements:**
- Add option to specify container element for the modal [\#474](https://github.com/shipshapecode/shepherd/pull/474) ([genadis](https://github.com/genadis))
**Fixed bugs:**
- Fix cancel link color for when the header has dark background [\#477](https://github.com/shipshapecode/shepherd/pull/477) ([genadis](https://github.com/genadis))
- Fix content border radius [\#476](https://github.com/shipshapecode/shepherd/pull/476) ([genadis](https://github.com/genadis))
- Fix applying tippyOptions [\#475](https://github.com/shipshapecode/shepherd/pull/475) ([genadis](https://github.com/genadis))
## [v4.2.0](https://github.com/shipshapecode/shepherd/tree/v4.2.0) (2019-07-31)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v4.1.0...v4.2.0)
**Implemented enhancements:**
- Accessibility support [\#198](https://github.com/shipshapecode/shepherd/issues/198)
- Remove shepherdElementWidth option [\#471](https://github.com/shipshapecode/shepherd/pull/471) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
## [v4.1.0](https://github.com/shipshapecode/shepherd/tree/v4.1.0) (2019-07-30)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v4.0.0...v4.1.0)
**Implemented enhancements:**
- Make cancel link more accessible [\#469](https://github.com/shipshapecode/shepherd/pull/469) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- switched to default export in TS typing [\#468](https://github.com/shipshapecode/shepherd/pull/468) ([grycmat](https://github.com/grycmat))
## [v4.0.0](https://github.com/shipshapecode/shepherd/tree/v4.0.0) (2019-07-29)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v3.1.0...v4.0.0)
**Breaking changes:**
- Switch modals from ids to classes and prefix them [\#466](https://github.com/shipshapecode/shepherd/pull/466) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Simplify addStep API [\#464](https://github.com/shipshapecode/shepherd/pull/464) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Remove predefined themes [\#462](https://github.com/shipshapecode/shepherd/pull/462) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Switch to CSSinJS [\#450](https://github.com/shipshapecode/shepherd/pull/450) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Use autoBind, pass context rather than manually binding [\#440](https://github.com/shipshapecode/shepherd/pull/440) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Remove array support for `step.options.text` [\#429](https://github.com/shipshapecode/shepherd/pull/429) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- .shepherd-popper -\> .shepherd, move .shepherd-has-title [\#422](https://github.com/shipshapecode/shepherd/pull/422) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Tippy v5 [\#420](https://github.com/shipshapecode/shepherd/pull/420) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Remove remaining lodash, IE 11+ [\#419](https://github.com/shipshapecode/shepherd/pull/419) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Remove the string option for `advanceOn` in favor of object [\#418](https://github.com/shipshapecode/shepherd/pull/418) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Remove string option for `attachTo` in favor of object [\#417](https://github.com/shipshapecode/shepherd/pull/417) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
**Fixed bugs:**
- ommiting 'on' doesn't work [\#460](https://github.com/shipshapecode/shepherd/issues/460)
- Modal mask opening shows back up on scroll [\#444](https://github.com/shipshapecode/shepherd/issues/444)
- IE11 support is broken [\#437](https://github.com/shipshapecode/shepherd/issues/437)
- Incorrect path to typings files in package.json [\#435](https://github.com/shipshapecode/shepherd/issues/435)
- Start fixing IE11 support [\#438](https://github.com/shipshapecode/shepherd/pull/438) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- fix for incorrect types path in package.json [\#434](https://github.com/shipshapecode/shepherd/pull/434) ([cmcnicholas](https://github.com/cmcnicholas))
- Arrow navigation skips steps if you do back then next [\#423](https://github.com/shipshapecode/shepherd/issues/423)
- Only add keydown listeners once [\#424](https://github.com/shipshapecode/shepherd/pull/424) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- useModalOverlay does not play well with multiple instances on the page [\#370](https://github.com/shipshapecode/shepherd/issues/370)
**Implemented enhancements:**
- Make build smaller, while still supporting IE11 [\#467](https://github.com/shipshapecode/shepherd/pull/467) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Convert to Preact components [\#458](https://github.com/shipshapecode/shepherd/pull/458) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Add first class support for secondary button [\#457](https://github.com/shipshapecode/shepherd/pull/457) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Prefixes fixes [\#453](https://github.com/shipshapecode/shepherd/pull/453) ([genadis](https://github.com/genadis))
- Add prefix to data attributes [\#452](https://github.com/shipshapecode/shepherd/pull/452) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Import ES5 bodyScrollLock, use babel-transform-runtime [\#447](https://github.com/shipshapecode/shepherd/pull/447) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Remove drop util [\#436](https://github.com/shipshapecode/shepherd/pull/436) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Cleanup public/private API [\#430](https://github.com/shipshapecode/shepherd/pull/430) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
**Closed issues:**
- An in-range update of eslint-plugin-jest is breaking the build 🚨 [\#443](https://github.com/shipshapecode/shepherd/issues/443)
- Modal classes are not prefixed [\#456](https://github.com/shipshapecode/shepherd/issues/456)
- fix removing 'shepherd-modal-target' [\#455](https://github.com/shipshapecode/shepherd/issues/455)
- Document canClickTarget [\#461](https://github.com/shipshapecode/shepherd/issues/461)
**Merged pull requests:**
- Document canClickTarget [\#465](https://github.com/shipshapecode/shepherd/pull/465) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Add index to 'show' and 'cancel' events [\#454](https://github.com/shipshapecode/shepherd/pull/454) ([genadis](https://github.com/genadis))
- Remove Eager [\#451](https://github.com/shipshapecode/shepherd/pull/451) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Add StackShare badge [\#446](https://github.com/shipshapecode/shepherd/pull/446) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Transpile auto-bind [\#441](https://github.com/shipshapecode/shepherd/pull/441) ([RobbieTheWagner](https://github.com/RobbieTheWagner)
- Update del to the latest version 🚀 [\#425](https://github.com/shipshapecode/shepherd/pull/425) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
## [v3.1.0](https://github.com/shipshapecode/shepherd/tree/v3.1.0) (2019-06-25)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v3.0.0...v3.1.0)
**Fixed bugs:**
- Fix jumpy disableScroll [\#416](https://github.com/shipshapecode/shepherd/pull/416) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Reuse existing modal overlay [\#414](https://github.com/shipshapecode/shepherd/pull/414) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
**Merged pull requests:**
- Update rollup-plugin-eslint to the latest version 🚀 [\#415](https://github.com/shipshapecode/shepherd/pull/415) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
## [v3.0.0](https://github.com/shipshapecode/shepherd/tree/v3.0.0) (2019-06-23)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.10.0...v3.0.0)
**Breaking changes:**
- Remove ul, li button wrapper [\#409](https://github.com/shipshapecode/shepherd/pull/409) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
**Implemented enhancements:**
- Support to keyboard navigation [\#406](https://github.com/shipshapecode/shepherd/issues/406)
- Feature request --- Add Typescript Typings [\#359](https://github.com/shipshapecode/shepherd/issues/359)
- Add `disableScroll` option [\#413](https://github.com/shipshapecode/shepherd/pull/413) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Add aria-describedby and aria-labeledby [\#411](https://github.com/shipshapecode/shepherd/pull/411) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Arrow nav [\#410](https://github.com/shipshapecode/shepherd/pull/410) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Add focus trap, to disallow tabbing outside the modal [\#408](https://github.com/shipshapecode/shepherd/pull/408) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Support close with ESC, focus tooltip on `show` [\#407](https://github.com/shipshapecode/shepherd/pull/407) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
**Merged pull requests:**
- Update eslint to the latest version 🚀 [\#412](https://github.com/shipshapecode/shepherd/pull/412) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
- Greenkeeper/rollup plugin visualizer 2.1.1 [\#404](https://github.com/shipshapecode/shepherd/pull/404) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
## [v2.10.0](https://github.com/shipshapecode/shepherd/tree/v2.10.0) (2019-06-13)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.9.1...v2.10.0)
**Implemented enhancements:**
- Add scrollIntoView options and polyfill [\#402](https://github.com/shipshapecode/shepherd/pull/402) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Add TypeScript definitions [\#360](https://github.com/shipshapecode/shepherd/pull/360) ([superheri](https://github.com/superheri))
**Fixed bugs:**
- "TypeError: Property 'handleEvent' is not callable." in Firefox [\#393](https://github.com/shipshapecode/shepherd/issues/393)
- Remove addStepEventListeners call [\#396](https://github.com/shipshapecode/shepherd/pull/396) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
**Closed issues:**
- \[Proposal\] center elements for scrollTo [\#398](https://github.com/shipshapecode/shepherd/issues/398)
- An in-range update of rollup is breaking the build 🚨 [\#392](https://github.com/shipshapecode/shepherd/issues/392)
**Merged pull requests:**
- build support for windows \(rm does not exist\) [\#403](https://github.com/shipshapecode/shepherd/pull/403) ([hheexx](https://github.com/hheexx))
- Update stylelint-config-ship-shape to the latest version 🚀 [\#399](https://github.com/shipshapecode/shepherd/pull/399) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
## [v2.9.1](https://github.com/shipshapecode/shepherd/tree/v2.9.1) (2019-06-09)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.9.0...v2.9.1)
**Implemented enhancements:**
- Tippy 4.3.4, bump deps, fix lint [\#395](https://github.com/shipshapecode/shepherd/pull/395) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Fix modal padding test failures, add tests, docs [\#390](https://github.com/shipshapecode/shepherd/pull/390) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
**Closed issues:**
- An in-range update of autoprefixer is breaking the build 🚨 [\#388](https://github.com/shipshapecode/shepherd/issues/388)
- An in-range update of tippy.js is breaking the build 🚨 [\#387](https://github.com/shipshapecode/shepherd/issues/387)
- An in-range update of rollup-plugin-analyzer is breaking the build 🚨 [\#386](https://github.com/shipshapecode/shepherd/issues/386)
- An in-range update of rollup-plugin-node-resolve is breaking the build 🚨 [\#385](https://github.com/shipshapecode/shepherd/issues/385)
- An in-range update of rollup is breaking the build 🚨 [\#384](https://github.com/shipshapecode/shepherd/issues/384)
- Add space around attachedElement [\#379](https://github.com/shipshapecode/shepherd/issues/379)
**Merged pull requests:**
- Optional padding on modalOverlayOpening [\#383](https://github.com/shipshapecode/shepherd/pull/383) ([skmbr](https://github.com/skmbr))
## [v2.9.0](https://github.com/shipshapecode/shepherd/tree/v2.9.0) (2019-05-26)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.8.0...v2.9.0)
**Implemented enhancements:**
- Add back IE support [\#380](https://github.com/shipshapecode/shepherd/pull/380) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
**Fixed bugs:**
- Show event of tour does not pass the hash of `step` and `previous` [\#371](https://github.com/shipshapecode/shepherd/issues/371)
- Ensure arguments are passed down to trigger [\#381](https://github.com/shipshapecode/shepherd/pull/381) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
**Closed issues:**
- Is it possible to change fill color of the modal ? [\#374](https://github.com/shipshapecode/shepherd/issues/374)
- There will be blue edges after clicking [\#369](https://github.com/shipshapecode/shepherd/issues/369)
- \[Suggestion\] Add transition effects when the mask moving [\#304](https://github.com/shipshapecode/shepherd/issues/304)
**Merged pull requests:**
- Update rollup-plugin-eslint to the latest version 🚀 [\#378](https://github.com/shipshapecode/shepherd/pull/378) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
- Update rollup-plugin-terser to the latest version 🚀 [\#376](https://github.com/shipshapecode/shepherd/pull/376) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
- Update rollup-plugin-node-resolve to the latest version 🚀 [\#373](https://github.com/shipshapecode/shepherd/pull/373) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
## [v2.8.0](https://github.com/shipshapecode/shepherd/tree/v2.8.0) (2019-05-03)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.7.0...v2.8.0)
**Implemented enhancements:**
- Convert several lodash functions to internal utils [\#368](https://github.com/shipshapecode/shepherd/pull/368) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Use internal debounce function [\#367](https://github.com/shipshapecode/shepherd/pull/367) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Greenkeeper/stylelint 10.0.1 [\#362](https://github.com/shipshapecode/shepherd/pull/362) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
**Fixed bugs:**
- Shepherd popper-tippy CSS styles mixing up with non-shepherd tippy styles on the page [\#363](https://github.com/shipshapecode/shepherd/issues/363)
**Closed issues:**
- An in-range update of rollup is breaking the build 🚨 [\#350](https://github.com/shipshapecode/shepherd/issues/350)
**Merged pull requests:**
- Added 'shepherd-popper' css class [\#366](https://github.com/shipshapecode/shepherd/pull/366) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Add ESDoc, bump some deps [\#365](https://github.com/shipshapecode/shepherd/pull/365) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
## [v2.7.0](https://github.com/shipshapecode/shepherd/tree/v2.7.0) (2019-04-22)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.6.0...v2.7.0)
**Fixed bugs:**
- After tour is canceled/completed and started again, overlay is not present anymore [\#347](https://github.com/shipshapecode/shepherd/issues/347)
- Add createModalOverlay function [\#358](https://github.com/shipshapecode/shepherd/pull/358) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
**Merged pull requests:**
- \[BugFix\] Issue \#347 [\#357](https://github.com/shipshapecode/shepherd/pull/357) ([jayjfletcher](https://github.com/jayjfletcher))
## [v2.6.0](https://github.com/shipshapecode/shepherd/tree/v2.6.0) (2019-04-15)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.5.0...v2.6.0)
**Implemented enhancements:**
- React wrapper [\#331](https://github.com/shipshapecode/shepherd/issues/331)
**Fixed bugs:**
- Minified File Size [\#354](https://github.com/shipshapecode/shepherd/issues/354)
- Fix inflated build size, bump some deps [\#355](https://github.com/shipshapecode/shepherd/pull/355) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
## [v2.5.0](https://github.com/shipshapecode/shepherd/tree/v2.5.0) (2019-03-20)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.4.0...v2.5.0)
**Breaking changes:**
- Drop IE support, bump some deps [\#344](https://github.com/shipshapecode/shepherd/pull/344) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
**Closed issues:**
- Shepherd Doesn't Work Good On Mobile [\#339](https://github.com/shipshapecode/shepherd/issues/339)
- Fix demo app arrows to be title color [\#314](https://github.com/shipshapecode/shepherd/issues/314)
**Merged pull requests:**
- Add flipping tippy by default, scrollTo for demo [\#345](https://github.com/shipshapecode/shepherd/pull/345) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Update del to the latest version 🚀 [\#340](https://github.com/shipshapecode/shepherd/pull/340) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
## [v2.4.0](https://github.com/shipshapecode/shepherd/tree/v2.4.0) (2019-02-27)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.3.3...v2.4.0)
**Implemented enhancements:**
- Angular wrapper [\#332](https://github.com/shipshapecode/shepherd/issues/332)
- Add tabindex="0" to shepherd-button [\#337](https://github.com/shipshapecode/shepherd/pull/337) ([knoobie](https://github.com/knoobie))
**Fixed bugs:**
- Links in modal mode [\#328](https://github.com/shipshapecode/shepherd/issues/328)
- svg mask for the opening in browsers do not support getBoundingClientRect\(\).x|y [\#330](https://github.com/shipshapecode/shepherd/pull/330) ([yaxinr](https://github.com/yaxinr))
**Merged pull requests:**
- Tippy 4 [\#336](https://github.com/shipshapecode/shepherd/pull/336) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Fix inability to click things in shepherd-element [\#334](https://github.com/shipshapecode/shepherd/pull/334) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Update rollup-plugin-css-only to the latest version 🚀 [\#327](https://github.com/shipshapecode/shepherd/pull/327) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
## [v2.3.3](https://github.com/shipshapecode/shepherd/tree/v2.3.3) (2019-01-23)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.3.2...v2.3.3)
**Fixed bugs:**
- Adds missing 'hide' to binded methods list in Tour [\#326](https://github.com/shipshapecode/shepherd/pull/326) ([seppsepp](https://github.com/seppsepp))
**Merged pull requests:**
- Update rollup-plugin-babel-minify to the latest version 🚀 [\#325](https://github.com/shipshapecode/shepherd/pull/325) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
## [v2.3.2](https://github.com/shipshapecode/shepherd/tree/v2.3.2) (2019-01-16)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.3.1...v2.3.2)
**Fixed bugs:**
- Object.assign not transpiled [\#323](https://github.com/shipshapecode/shepherd/issues/323)
- Toggling developer tools breaks modal mask [\#320](https://github.com/shipshapecode/shepherd/issues/320)
- Scrolling to an element causes modal overlay to appear in wrong place [\#319](https://github.com/shipshapecode/shepherd/issues/319)
**Merged pull requests:**
- Use @babel/plugin-transform-object-assign [\#324](https://github.com/shipshapecode/shepherd/pull/324) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Apply scroll listener to all scroll events [\#322](https://github.com/shipshapecode/shepherd/pull/322) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Use vh and vw to ensure modal is always full screen [\#321](https://github.com/shipshapecode/shepherd/pull/321) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
## [v2.3.1](https://github.com/shipshapecode/shepherd/tree/v2.3.1) (2019-01-15)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.3.0...v2.3.1)
**Fixed bugs:**
- fix: move touchmove event listener cleanup out of if statement [\#317](https://github.com/shipshapecode/shepherd/pull/317) ([chuckcarpenter](https://github.com/chuckcarpenter))
**Merged pull requests:**
- Update rollup-plugin-license to the latest version 🚀 [\#318](https://github.com/shipshapecode/shepherd/pull/318) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
## [v2.3.0](https://github.com/shipshapecode/shepherd/tree/v2.3.0) (2019-01-14)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.2.0...v2.3.0)
**Implemented enhancements:**
- Inject tippy CSS to head [\#315](https://github.com/shipshapecode/shepherd/pull/315) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
## [v2.2.0](https://github.com/shipshapecode/shepherd/tree/v2.2.0) (2019-01-14)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.1.1...v2.2.0)
**Implemented enhancements:**
- setAttributeNS -\> setAttribute, add modal utils tests [\#312](https://github.com/shipshapecode/shepherd/pull/312) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Use rollup instead of webpack [\#309](https://github.com/shipshapecode/shepherd/pull/309) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
## [v2.1.1](https://github.com/shipshapecode/shepherd/tree/v2.1.1) (2019-01-11)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.1.0...v2.1.1)
**Fixed bugs:**
- Use correct cleanupStepEventListeners [\#311](https://github.com/shipshapecode/shepherd/pull/311) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
## [v2.1.0](https://github.com/shipshapecode/shepherd/tree/v2.1.0) (2019-01-06)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.2...v2.1.0)
**Fixed bugs:**
- fix: turn off passive events for touchmove on Safari \> 10 [\#307](https://github.com/shipshapecode/shepherd/pull/307) ([chuckcarpenter](https://github.com/chuckcarpenter))
**Merged pull requests:**
- Move modal to its own class [\#308](https://github.com/shipshapecode/shepherd/pull/308) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
## [v2.0.2](https://github.com/shipshapecode/shepherd/tree/v2.0.2) (2019-01-04)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.1...v2.0.2)
**Fixed bugs:**
- fix: remove class when modal hidden so elements are clickable [\#305](https://github.com/shipshapecode/shepherd/pull/305) ([chuckcarpenter](https://github.com/chuckcarpenter))
## [v2.0.1](https://github.com/shipshapecode/shepherd/tree/v2.0.1) (2018-12-31)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0...v2.0.1)
## [v2.0.0](https://github.com/shipshapecode/shepherd/tree/v2.0.0) (2018-12-31)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.35...v2.0.0)
**Implemented enhancements:**
- Add data attribute to the body for the currently active tour name and current step [\#284](https://github.com/shipshapecode/shepherd/issues/284)
- Use data attributes for element selection in demo/tests [\#273](https://github.com/shipshapecode/shepherd/issues/273)
**Closed issues:**
- An in-range update of autoprefixer is breaking the build 🚨 [\#298](https://github.com/shipshapecode/shepherd/issues/298)
- An in-range update of webpack is breaking the build 🚨 [\#297](https://github.com/shipshapecode/shepherd/issues/297)
**Merged pull requests:**
- Integrate modal functionality that was originally in Ember Shepherd [\#301](https://github.com/shipshapecode/shepherd/pull/301) ([BrianSipple](https://github.com/BrianSipple))
- Use data attributes for test selectors [\#299](https://github.com/shipshapecode/shepherd/pull/299) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Remove redundant `id` attribute on step tooltip containers. [\#295](https://github.com/shipshapecode/shepherd/pull/295) ([BrianSipple](https://github.com/BrianSipple))
- Link to Shepherd's Tippy defaults in docs [\#294](https://github.com/shipshapecode/shepherd/pull/294) ([BrianSipple](https://github.com/BrianSipple))
## [v2.0.0-beta.35](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.35) (2018-11-09)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.34...v2.0.0-beta.35)
**Implemented enhancements:**
- Document Tour.removeStep [\#278](https://github.com/shipshapecode/shepherd/issues/278)
- Update documentation to clarify arrow usage. [\#287](https://github.com/shipshapecode/shepherd/pull/287) ([BrianSipple](https://github.com/BrianSipple))
**Fixed bugs:**
- fix tooltip centering [\#288](https://github.com/shipshapecode/shepherd/pull/288) ([BrianSipple](https://github.com/BrianSipple))
**Closed issues:**
- An in-range update of start-server-and-test is breaking the build 🚨 [\#283](https://github.com/shipshapecode/shepherd/issues/283)
**Merged pull requests:**
- Bump deps [\#291](https://github.com/shipshapecode/shepherd/pull/291) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Add testing for `Tour.isActive`. [\#290](https://github.com/shipshapecode/shepherd/pull/290) ([BrianSipple](https://github.com/BrianSipple))
- Added documentation for tour.removeStep \(fixes \#278\) [\#289](https://github.com/shipshapecode/shepherd/pull/289) ([joeinnes](https://github.com/joeinnes))
- Improve CodeClimate and test coverage. [\#286](https://github.com/shipshapecode/shepherd/pull/286) ([BrianSipple](https://github.com/BrianSipple))
- Remove unnecessary management of `this.el.hidden` [\#285](https://github.com/shipshapecode/shepherd/pull/285) ([BrianSipple](https://github.com/BrianSipple))
## [v2.0.0-beta.34](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.34) (2018-10-23)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.33...v2.0.0-beta.34)
**Implemented enhancements:**
- Change `data-id` to `data-shepherd-step-id` on Step content elements. [\#282](https://github.com/shipshapecode/shepherd/pull/282) ([BrianSipple](https://github.com/BrianSipple))
**Closed issues:**
- An in-range update of webpack is breaking the build 🚨 [\#280](https://github.com/shipshapecode/shepherd/issues/280)
## [v2.0.0-beta.33](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.33) (2018-10-19)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.32...v2.0.0-beta.33)
**Implemented enhancements:**
- Add minified js + css to release package [\#258](https://github.com/shipshapecode/shepherd/issues/258)
- Bump a bunch of deps [\#281](https://github.com/shipshapecode/shepherd/pull/281) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
**Fixed bugs:**
- Exit animations don't play before step tooltip disappears. [\#277](https://github.com/shipshapecode/shepherd/issues/277)
- Allow exit animations to play before step tooltip disappears. [\#279](https://github.com/shipshapecode/shepherd/pull/279) ([BrianSipple](https://github.com/BrianSipple))
- Remove default arrow setting on centered tippy [\#275](https://github.com/shipshapecode/shepherd/pull/275) ([chuckcarpenter](https://github.com/chuckcarpenter))
**Merged pull requests:**
- Fix bug of classes not being added to targets on returned-to steps. [\#276](https://github.com/shipshapecode/shepherd/pull/276) ([BrianSipple](https://github.com/BrianSipple))
## [v2.0.0-beta.32](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.32) (2018-10-13)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.31...v2.0.0-beta.32)
## [v2.0.0-beta.31](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.31) (2018-10-13)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.30...v2.0.0-beta.31)
## [v2.0.0-beta.30](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.30) (2018-10-13)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.29...v2.0.0-beta.30)
**Implemented enhancements:**
- Hide buttons if none are passed to `Step.options.buttons` [\#243](https://github.com/shipshapecode/shepherd/issues/243)
- Constrain input for `Step.options.buttons` to an array of buttons. [\#271](https://github.com/shipshapecode/shepherd/pull/271) ([BrianSipple](https://github.com/BrianSipple))
**Merged pull requests:**
- fix docs typo [\#272](https://github.com/shipshapecode/shepherd/pull/272) ([BrianSipple](https://github.com/BrianSipple))
## [v2.0.0-beta.29](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.29) (2018-10-11)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.28...v2.0.0-beta.29)
**Implemented enhancements:**
- Bundle minified Tippy code with distribution [\#263](https://github.com/shipshapecode/shepherd/issues/263)
- Implement `Tour.hide` [\#265](https://github.com/shipshapecode/shepherd/pull/265) ([BrianSipple](https://github.com/BrianSipple))
**Fixed bugs:**
- Remove or restore hide method on Tour instance [\#249](https://github.com/shipshapecode/shepherd/issues/249)
**Closed issues:**
- An in-range update of tippy.js is breaking the build 🚨 [\#266](https://github.com/shipshapecode/shepherd/issues/266)
**Merged pull requests:**
- Bundle tippy with the main Shepherd distribution file. [\#270](https://github.com/shipshapecode/shepherd/pull/270) ([BrianSipple](https://github.com/BrianSipple))
- remove spm from package.json [\#269](https://github.com/shipshapecode/shepherd/pull/269) ([BrianSipple](https://github.com/BrianSipple))
- remove some popper arrow styles and target tippy-arrow [\#268](https://github.com/shipshapecode/shepherd/pull/268) ([chuckcarpenter](https://github.com/chuckcarpenter))
- Update screenshot of intro step in README [\#264](https://github.com/shipshapecode/shepherd/pull/264) ([BrianSipple](https://github.com/BrianSipple))
## [v2.0.0-beta.28](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.28) (2018-10-08)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.27...v2.0.0-beta.28)
**Implemented enhancements:**
- Not all of popper.js placement values are supported [\#259](https://github.com/shipshapecode/shepherd/issues/259)
- Integrate Tippy for step modal creation [\#255](https://github.com/shipshapecode/shepherd/issues/255)
- Tippy.js Integration [\#261](https://github.com/shipshapecode/shepherd/pull/261) ([BrianSipple](https://github.com/BrianSipple))
**Merged pull requests:**
- use uniqueId for step ID prop over idAttribute [\#262](https://github.com/shipshapecode/shepherd/pull/262) ([chuckcarpenter](https://github.com/chuckcarpenter))
- Support placement values and modifiers according to popper.js API \(\#259\) [\#260](https://github.com/shipshapecode/shepherd/pull/260) ([tedbeer](https://github.com/tedbeer))
- Change localhost port for cypress tests and document how its used [\#257](https://github.com/shipshapecode/shepherd/pull/257) ([BrianSipple](https://github.com/BrianSipple))
- replace hubspot favicons with shipshape favicons [\#256](https://github.com/shipshapecode/shepherd/pull/256) ([BrianSipple](https://github.com/BrianSipple))
- documentation update: add beforeShowPromise example [\#253](https://github.com/shipshapecode/shepherd/pull/253) ([jaffadog](https://github.com/jaffadog))
- Update uglifyjs-webpack-plugin to the latest version 🚀 [\#247](https://github.com/shipshapecode/shepherd/pull/247) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
- Update theming documentation. [\#242](https://github.com/shipshapecode/shepherd/pull/242) ([BrianSipple](https://github.com/BrianSipple))
## [v2.0.0-beta.27](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.27) (2018-09-13)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.26...v2.0.0-beta.27)
**Breaking changes:**
- Rename `Tour.options.defaults` to `Tour.options.defaultStepOptions`. [\#244](https://github.com/shipshapecode/shepherd/pull/244) ([BrianSipple](https://github.com/BrianSipple))
**Implemented enhancements:**
- Change `tour.options.defaults` to `tour.options.stepOptions` [\#240](https://github.com/shipshapecode/shepherd/issues/240)
**Merged pull requests:**
- Greenkeeper/babel plugin add module exports 1.0.0 [\#246](https://github.com/shipshapecode/shepherd/pull/246) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
## [v2.0.0-beta.26](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.26) (2018-09-07)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.25...v2.0.0-beta.26)
**Fixed bugs:**
- Build syntax errors and element.prepend function not supported \(Internet Explorer\) [\#238](https://github.com/shipshapecode/shepherd/issues/238)
- Syntax errors and ParentNode.prepend not supported in IE [\#239](https://github.com/shipshapecode/shepherd/pull/239) ([alexdaube](https://github.com/alexdaube))
## [v2.0.0-beta.25](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.25) (2018-09-06)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.24...v2.0.0-beta.25)
## [v2.0.0-beta.24](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.24) (2018-09-05)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.23...v2.0.0-beta.24)
**Implemented enhancements:**
- Way to get user to confirm quitting a tutorial [\#133](https://github.com/shipshapecode/shepherd/issues/133)
- Add step options to ESDoc [\#234](https://github.com/shipshapecode/shepherd/pull/234) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Add Confirm cancel [\#232](https://github.com/shipshapecode/shepherd/pull/232) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- improved theming [\#204](https://github.com/shipshapecode/shepherd/pull/204) ([bm2u](https://github.com/bm2u))
**Fixed bugs:**
- advanceOn doesn't complete tour [\#93](https://github.com/shipshapecode/shepherd/issues/93)
- advanceOn blur? [\#89](https://github.com/shipshapecode/shepherd/issues/89)
**Closed issues:**
- Action required: Greenkeeper could not be activated 🚨 [\#227](https://github.com/shipshapecode/shepherd/issues/227)
**Merged pull requests:**
- add `.vscode` directory to `.gitignore` [\#237](https://github.com/shipshapecode/shepherd/pull/237) ([BrianSipple](https://github.com/BrianSipple))
- Update extract-loader to the latest version 🚀 [\#236](https://github.com/shipshapecode/shepherd/pull/236) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
- \[Issue/89\]- add onCapture setting to bubble events such as blur for tour elements… [\#233](https://github.com/shipshapecode/shepherd/pull/233) ([chuckcarpenter](https://github.com/chuckcarpenter))
## [v2.0.0-beta.23](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.23) (2018-08-29)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.22...v2.0.0-beta.23)
**Implemented enhancements:**
- Update to Babel 7, use lodash-es [\#231](https://github.com/shipshapecode/shepherd/pull/231) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
**Merged pull requests:**
- add more tests for increased coverage [\#230](https://github.com/shipshapecode/shepherd/pull/230) ([chuckcarpenter](https://github.com/chuckcarpenter))
- Update dependencies to enable Greenkeeper 🌴 [\#229](https://github.com/shipshapecode/shepherd/pull/229) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
## [v2.0.0-beta.22](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.22) (2018-08-29)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.21...v2.0.0-beta.22)
**Implemented enhancements:**
- Implement ESDoc [\#226](https://github.com/shipshapecode/shepherd/pull/226) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
**Fixed bugs:**
- Fix cancel on any step with a title [\#228](https://github.com/shipshapecode/shepherd/pull/228) ([chuckcarpenter](https://github.com/chuckcarpenter))
## [v2.0.0-beta.21](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.21) (2018-08-27)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.20...v2.0.0-beta.21)
**Merged pull requests:**
- Merge coverage from cypress and unit tests [\#225](https://github.com/shipshapecode/shepherd/pull/225) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Renaming of scss source dir [\#219](https://github.com/shipshapecode/shepherd/pull/219) ([bm2u](https://github.com/bm2u))
## [v2.0.0-beta.20](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.20) (2018-08-26)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.19...v2.0.0-beta.20)
**Implemented enhancements:**
- Increase test coverage, refactor, and cleanup [\#224](https://github.com/shipshapecode/shepherd/pull/224) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
## [v2.0.0-beta.19](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.19) (2018-08-25)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.18...v2.0.0-beta.19)
## [v2.0.0-beta.18](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.18) (2018-08-25)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.17...v2.0.0-beta.18)
**Implemented enhancements:**
- Add some tour tests [\#216](https://github.com/shipshapecode/shepherd/pull/216) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Testing for Evented module [\#215](https://github.com/shipshapecode/shepherd/pull/215) ([chuckcarpenter](https://github.com/chuckcarpenter))
**Fixed bugs:**
- ES2015 imports not working [\#210](https://github.com/shipshapecode/shepherd/issues/210)
- Fix issue with cancel button [\#220](https://github.com/shipshapecode/shepherd/pull/220) ([chuckcarpenter](https://github.com/chuckcarpenter))
**Merged pull requests:**
- Move bind methods to their own file [\#222](https://github.com/shipshapecode/shepherd/pull/222) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Start some refactoring [\#221](https://github.com/shipshapecode/shepherd/pull/221) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Step.js coverage increase [\#218](https://github.com/shipshapecode/shepherd/pull/218) ([chuckcarpenter](https://github.com/chuckcarpenter))
- Reduce Evented complexity [\#217](https://github.com/shipshapecode/shepherd/pull/217) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- Start refactoring for Code Climate [\#214](https://github.com/shipshapecode/shepherd/pull/214) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
## [v2.0.0-beta.17](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.17) (2018-08-15)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.16...v2.0.0-beta.17)
**Merged pull requests:**
- Feature/webpack [\#212](https://github.com/shipshapecode/shepherd/pull/212) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
## [v2.0.0-beta.16](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.16) (2018-08-14)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.15...v2.0.0-beta.16)
**Implemented enhancements:**
- \[feature/builder\] - replace Gulp with module loader and npm [\#203](https://github.com/shipshapecode/shepherd/pull/203) ([chuckcarpenter](https://github.com/chuckcarpenter))
**Fixed bugs:**
- Uncaught TypeError: \_shepherd2.default.Tour is not a constructor [\#202](https://github.com/shipshapecode/shepherd/issues/202)
**Merged pull requests:**
- Start on cypress [\#209](https://github.com/shipshapecode/shepherd/pull/209) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
- increase test coverage [\#206](https://github.com/shipshapecode/shepherd/pull/206) ([chuckcarpenter](https://github.com/chuckcarpenter))
## [v2.0.0-beta.15](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.15) (2018-08-06)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.14...v2.0.0-beta.15)
**Fixed bugs:**
- Error: Cannot find module 'popper' from '\node\_modules\shepherd.js\dist\js' [\#201](https://github.com/shipshapecode/shepherd/issues/201)
## [v2.0.0-beta.14](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.14) (2018-08-02)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.13...v2.0.0-beta.14)
**Fixed bugs:**
- Error thrown if element not visible anymore [\#197](https://github.com/shipshapecode/shepherd/issues/197)
**Merged pull requests:**
- Basic testing framework [\#199](https://github.com/shipshapecode/shepherd/pull/199) ([chuckcarpenter](https://github.com/chuckcarpenter))
- Update documentation link in demo tour [\#195](https://github.com/shipshapecode/shepherd/pull/195) ([mikelkew](https://github.com/mikelkew))
## [v2.0.0-beta.13](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.13) (2018-07-16)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.12...v2.0.0-beta.13)
**Implemented enhancements:**
- Refactor css in shepherd-theme-arrows theme [\#52](https://github.com/shipshapecode/shepherd/issues/52)
- Automatically use theme if styles are included [\#1](https://github.com/shipshapecode/shepherd/issues/1)
## [v2.0.0-beta.12](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.12) (2018-07-12)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.11...v2.0.0-beta.12)
## [v2.0.0-beta.11](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.11) (2018-07-12)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.10...v2.0.0-beta.11)
**Implemented enhancements:**
- Attach shepherd-step to "custom" element / supporting dialog elements. [\#157](https://github.com/shipshapecode/shepherd/issues/157)
- Add renderLocation option [\#192](https://github.com/shipshapecode/shepherd/pull/192) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
**Fixed bugs:**
- Tours fail to load on Chrome 65 [\#180](https://github.com/shipshapecode/shepherd/issues/180)
- Step divs remain after tour has ended [\#66](https://github.com/shipshapecode/shepherd/issues/66)
**Closed issues:**
- Step class cleanup [\#36](https://github.com/shipshapecode/shepherd/issues/36)
## [v2.0.0-beta.10](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.10) (2018-07-11)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.9...v2.0.0-beta.10)
## [v2.0.0-beta.9](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.9) (2018-07-11)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.8...v2.0.0-beta.9)
**Implemented enhancements:**
- CSS should not be required to use this library [\#166](https://github.com/shipshapecode/shepherd/issues/166)
**Fixed bugs:**
- Arrows don't appear on some boxes randomly \(video\) [\#156](https://github.com/shipshapecode/shepherd/issues/156)
**Closed issues:**
- Undocumented `scrollToHandler` option [\#107](https://github.com/shipshapecode/shepherd/issues/107)
## [v2.0.0-beta.8](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.8) (2018-07-09)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.7...v2.0.0-beta.8)
## [v2.0.0-beta.7](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.7) (2018-07-07)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.6...v2.0.0-beta.7)
**Fixed bugs:**
- Use frame safe way for isArray\(\) & isObject\(\) [\#153](https://github.com/shipshapecode/shepherd/issues/153)
- remove shepherd-target class on tour.next\(\)/tour.back\(\) [\#109](https://github.com/shipshapecode/shepherd/issues/109)
**Closed issues:**
- Rethethering issue when target element is re-rendered. [\#112](https://github.com/shipshapecode/shepherd/issues/112)
## [v2.0.0-beta.6](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.6) (2018-07-07)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.5...v2.0.0-beta.6)
**Implemented enhancements:**
- Close button as HTML entity [\#148](https://github.com/shipshapecode/shepherd/pull/148) ([bm2u](https://github.com/bm2u))
**Fixed bugs:**
- Hide events being triggered twice when there is another step [\#167](https://github.com/shipshapecode/shepherd/issues/167)
- Removing duplicate call to hide step [\#168](https://github.com/shipshapecode/shepherd/pull/168) ([pedroceles](https://github.com/pedroceles))
**Closed issues:**
- The install doc is not working [\#179](https://github.com/shipshapecode/shepherd/issues/179)
- addStep not return step instance [\#165](https://github.com/shipshapecode/shepherd/issues/165)
- cancelling the tour when clicking outside the element [\#141](https://github.com/shipshapecode/shepherd/issues/141)
- showCancelLink yields weird characters [\#117](https://github.com/shipshapecode/shepherd/issues/117)
**Merged pull requests:**
- Added a demo tour at Simple Planner [\#155](https://github.com/shipshapecode/shepherd/pull/155) ([newscloud](https://github.com/newscloud))
## [v2.0.0-beta.5](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.5) (2018-07-03)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.4...v2.0.0-beta.5)
## [v2.0.0-beta.4](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.4) (2018-07-03)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.3...v2.0.0-beta.4)
## [v2.0.0-beta.3](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.3) (2018-07-03)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.2...v2.0.0-beta.3)
**Closed issues:**
- How can i use it with ionic 2 typescript? [\#174](https://github.com/shipshapecode/shepherd/issues/174)
## [v2.0.0-beta.2](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.2) (2018-07-02)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v2.0.0-beta.1...v2.0.0-beta.2)
## [v2.0.0-beta.1](https://github.com/shipshapecode/shepherd/tree/v2.0.0-beta.1) (2018-07-02)
[Full Changelog](https://github.com/shipshapecode/shepherd/compare/v1.8.1...v2.0.0-beta.1)
**Implemented enhancements:**
- Convert to popper [\#189](https://github.com/shipshapecode/shepherd/pull/189) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
**Fixed bugs:**
- Attempting to fix uncaught exception caused by non-existing element f… [\#190](https://github.com/shipshapecode/shepherd/pull/190) ([RobbieTheWagner](https://github.com/RobbieTheWagner))
**Closed issues:**
- href of the "View docs" button on demo site --\> 404 [\#187](https://github.com/shipshapecode/shepherd/issues/187)
- Demo site is down [\#185](https://github.com/shipshapecode/shepherd/issues/185)
- Maintainer/transfer [\#183](https://github.com/shipshapecode/shepherd/issues/183)
- cannot get node\_modules/tether-shepherd/dist/js/shepherd.min.js [\#173](https://github.com/shipshapecode/shepherd/issues/173)
- ES6 import from NPM failed. [\#171](https://github.com/shipshapecode/shepherd/issues/171)
- Display Shepherd only during first visit [\#164](https://github.com/shipshapecode/shepherd/issues/164)
- Tether EOL implications [\#163](https://github.com/shipshapecode/shepherd/issues/163)
- Get the AttachTo object [\#150](https://github.com/shipshapecode/shepherd/issues/150)
- Arrow problem [\#145](https://github.com/shipshapecode/shepherd/issues/145)
- `attachment: together` does not work as expected if attachment width greater than target width [\#142](https://github.com/shipshapecode/shepherd/issues/142)
- shepherd-step not placed in proper position [\#130](https://github.com/shipshapecode/shepherd/issues/130)
- attachTo is not working when passing a string [\#122](https://github.com/shipshapecode/shepherd/issues/122)
- Triggering click of an page element on tour step "show" [\#119](https://github.com/shipshapecode/shepherd/issues/119)
- Inherit animation styles from Drop [\#84](https://github.com/shipshapecode/shepherd/issues/84)
- Shepherd might need jQuery... [\#79](https://github.com/shipshapecode/shepherd/issues/79)
- Not accessibility friendly [\#26](https://github.com/shipshapecode/shepherd/issues/26)
**Merged pull requests:**
- RM Hubs Copyright [\#188](https://github.com/shipshapecode/shepherd/pull/188) ([FranDias](https://github.com/FranDias))
- Link correct demo site [\#186](https://github.com/shipshapecode/shepherd/pull/186) ([drucci](https://github.com/drucci))
- Add Repo to package.json [\#149](https://github.com/shipshapecode/shepherd/pull/149) ([bm2u](https://github.com/bm2u))
## v1.7.0
- Fixes bug where `buttons: false` resulted in the default Next button instead of showing the desired result of no buttons.
## v1.6.0 & v1.6.2
- Patches issue where Tether anchor reference was being cached instead of reset when step is shown.
## v1.5.2
- Adds functionality to pass an object `{element: el, on: tetherPositionString}` to tour step `attachTo` parameter.
## v1.5.1
- Exposes `scrollTo` option in Eager
## v1.5.0
- Positioning string parse improvements with regex
- Installation support for dynamically rendered pages in Eager
- Live updates for eager / creation of `tour.removeStep`
## v1.2.2
- Moves `Tether.js` out of `bower_components` and into `dist` for supoort in Eager
## v1.2.1
- Exposes tour object on eager install
## v1.2.0
- Adds `showOn` for conditionally showing tour steps
## v1.1.4
- Eager - Install helper now checks for the presence of first attach node before starting
## v1.1.2 & v1.1.3
- Fix stacking event listeners
## v1.1.1
- Pointer event none for arrows
## v1.1.0
- Update `Tether` to version 1
- Bump all dependencies
## v1.0.0
- Add proper UMD to `Shepherd`
- Convert from `Coffeescript` to `ES6 (Babel)`
- Fix `*.json` files to include `main`
- Remove bundled versions
- Restructure directory layout
- Update `gulp` builds
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing Guide
You will need:
- [pnpm](https://pnpm.io/)
## Getting started
1. Fork the project
2. Clone your forked project by running `git clone git@github.com:{
YOUR_USERNAME }/shepherd.git`
3. Run `pnpm i` to install node modules
4. Test that you can build the source by running `pnpm build` and ensure the `dist` directory appears.
## Writing code!
We use `rollup` to facilitate things like transpilation, minification, etc. so
you can focus on writing relevant code. If there is a fix or feature you would like
to contribute, we ask that you take the following steps:
1. Most of the _editable_ code lives in the `src` directory while built code
will end up in the `dist` directory upon running `pnpm build`.
2. The demo app is served out of the `landing` directory. Running `pnpm start` will open it in your browser and initiate a live-reloading session as you make changes.
## Opening Pull Requests
1. Please Provide a thoughtful commit message and push your changes to your fork using
`git push origin main` (assuming your forked project is using `origin` for
the remote name and you are on the `main` branch).
2. Open a Pull Request on GitHub with a description of your changes.
## Testing
All PRs that change code functionality are required to have accompanying tests.
### Acceptance Tests
Acceptance tests are run using [`cypress`](https://github.com/cypress-io/cypress). A number of different testing configurations can be found in [`package.json`](/package.json), but you can simply run `pnpm test:cy:watch` to build your latest changes and begin running the tests inside a Chrome browser instance.
⚠️ The acceptance tests are set up to run on `localhost` port `9002`. If you'd like to change this port, make sure to change the `baseUrl` option inside of [`cypress.json`](/cypress.json), and change any references to port `9002` in [`package.json`](/package.json) accordingly.
================================================
FILE: LICENSE.md
================================================
# Shepherd.js License
Shepherd.js is dual-licensed under **AGPL-3.0** (for open source and non-commercial use) and a **Commercial License** (for commercial use).
---
🆓 Free Use - AGPL-3.0 License
You may use Shepherd.js **FREE OF CHARGE** under the AGPL-3.0 license if you are:
✅ Open Source Projects
- Your project is open source and licensed under an AGPL-compatible license (GPL, AGPL, etc.)
- Your complete source code is publicly available
✅ Personal & Non-Commercial Use
- Personal projects, portfolios, and hobby websites
- Educational purposes (students, teachers, coursework)
- Academic research projects
✅ Evaluation & Testing
- Evaluating Shepherd.js for up to 30 days
- Development, testing, and staging environments during evaluation
- Proof-of-concept and demo projects
**Important:** If you use Shepherd.js under AGPL-3.0, you must:
- Make your complete source code available if you distribute or provide your software over a network
- License your code under AGPL-3.0 or a compatible license
- Comply with all AGPL-3.0 terms (see full license text below)
---
💳 Commercial License Required
You **must purchase a commercial license** at [shepherdjs.dev/pricing](https://shepherdjs.dev/pricing) if:
❌ Commercial Products & Services
- You're building a commercial product, application, SaaS, or website that generates revenue
- Your company generates revenue (even if the specific project using Shepherd.js does not)
- You're using Shepherd.js in any customer-facing commercial application
❌ Closed-Source Use
- You cannot or don't want to open-source your code under AGPL-3.0
- You want to keep your source code proprietary
- You want to avoid AGPL's source code disclosure requirements
❌ White-Label, Resale, or OEM Use
- You're embedding Shepherd.js in a product you sell or distribute
- You're offering Shepherd.js as part of a commercial service or hosting
- You're using Shepherd.js in a product sold to other businesses
❌ Internal Business Tools
- You're using Shepherd.js for internal tools, dashboards, or admin panels in a revenue-generating company
- Even if the tool is not customer-facing, commercial licenses are required for for-profit companies
**Why Commercial License?**
- ✅ No AGPL obligations - keep your code proprietary
- ✅ Legal protection and indemnification
- ✅ Priority support and updates
- ✅ Lifetime license with no recurring fees
[**Purchase Commercial License →**](https://shepherdjs.dev/pricing)
---
❓ Still Not Sure?
If you're unsure whether you need a commercial license:
- **Contact us:** [ahoy@shipshape.io](mailto:ahoy@shipshape.io)
- **View pricing:** [shepherdjs.dev/pricing](https://shepherdjs.dev/pricing)
**When in doubt:** If your organization generates revenue, you likely need a commercial license.
---
### GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc.
](http://godban.github.io/browsers-support-badges/)Edge | [
](http://godban.github.io/browsers-support-badges/)Firefox | [
](http://godban.github.io/browsers-support-badges/)Chrome | [
](http://godban.github.io/browsers-support-badges/)Safari |
| --------- | --------- | --------- | --------- |
| Edge| last 2 versions| last 2 versions| last 2 versions
# Shepherd
Shepherd makes it simple to create custom user on-boarding tours, trainings and announcements to drive user adoption.
Shepherd enables you to guide users through a custom tour or journey within your app or website. Highly customizable with minimal styles, Shepherd allows for powerful customization while being easy to use. Various frameworks supported including React, Ember, Angular, Vue.js, ES Modules, or plain JavaScript.
# Demo
See Shepherd Live on our website by clicking on the image:
# Using Shepherd
### Shepherd Open Source Library
The Shepherd Standalone Library has been open source since the very beginning. Check out our tutorials here:
#### [React Shepherd Wrapper](packages/react)
#### [Angular Shepherd Wrapper](https://github.com/shipshapecode/angular-shepherd)
#### [Vue Shepherd Wrapper](https://github.com/shipshapecode/vue-shepherd)
#### [Ember Shepherd Wrapper](https://github.com/RobbieTheWagner/ember-shepherd)
# White Glove Services
If you have an idea or project in mind and would like to engage our team to build a custom tour, training or on-boarding experience, [get in touch](mailto:ahoy@shipshape.io)!
## Resources
- [Website](https://shepherdjs.dev/)
- [Documentation](https://docs.shepherdjs.dev/)
- [Demo](https://shepherdjs.dev/)
- [Discord](https://discord.gg/EGcDW5NSud)
## Contributing
We encourage contributions of all kinds. If you would like to contribute in some way, please review our [guidelines for contributing](CONTRIBUTING.md).
Our release process is mostly automated. For more details, see [RELEASE.md](RELEASE.md).
## Projects Already Using Shepherd
### Rails gems
### [abraham](https://github.com/actmd/abraham)
Rails engine that generates and tracks Shepherd tours within an application
### Websites and Apps
### [SimplePlanner](https://simpleplanner.io)
[SimplePlanner](https://simpleplanner.io) uses Shepherd to help new users get familiar with its collaborative scheduling approach.
You do need to sign up via OAuth or email to see the scheduling tour.
Check out the [Envato Tuts+ Startup Series on its codebase](https://code.tutsplus.com/series/building-your-startup-with-php--cms-742) which describes how Simple Planner was built.
### [LogSeq](https://logseq.com/)
[LogSeq](https://logseq.com/) uses Shepherd to guide users through initial setup steps.
### [Snapsure](https://snapsure.app)
[Snapsure](https://snapsure.app) uses Shepherd to help photographers learn how to set up alerts for their desired picture-perfect weather conditions.
### [Drupal](https://www.drupal.org/docs/8/core/modules/tour/overview)
The [Drupal](https://www.drupal.org/docs/8/core/modules/tour/overview) CMS uses Shepherd to offer tours of it's core modules, and allows developers to add Tours to their custom and contributed modules.
### [Budibase Shepherd Tour Plugin](https://github.com/JayP718/tour_shepherd_bb_plugin)
[Budibase](https://budibase.com/) is an open source application which allows you develop low code applications rapidly and efficiently.This [Budibase](https://budibase.com/) Shepherd tour plugin allows you to create interative walkthroughs for your application.
### Your Project Here
If you have a cool open-source library built on Shepherd, PR this doc.
## License
Shepherd.js is dual-licensed under AGPL-3.0 and a Commercial License.
- **Free for open source and non-commercial use** under AGPL-3.0
- **Commercial license required** for commercial products and revenue-generating companies
See [LICENSE.md](LICENSE.md) for complete details or visit [shepherdjs.dev/pricing](https://shepherdjs.dev/pricing) to purchase a commercial license.
[](https://app.fossa.com/projects/git%2Bgithub.com%2Fshipshapecode%2Fshepherd?ref=badge_large)
================================================
FILE: RELEASE.md
================================================
# Release Process
Releases in this repo are mostly automated using [release-plan](https://github.com/embroider-build/release-plan/). Once you label all your PRs correctly (see below) you will have an automatically generated PR that updates your CHANGELOG.md file and a `.release-plan.json` that is used to prepare the release once the PR is merged.
## Preparation
Since the majority of the actual release process is automated, the remaining tasks before releasing are:
- correctly labeling **all** pull requests that have been merged since the last release
- updating pull request titles so they make sense to our users
Some great information on why this is important can be found at [keepachangelog.com](https://keepachangelog.com/en/1.1.0/), but the overall
guiding principle here is that changelogs are for humans, not machines.
When reviewing merged PR's the labels to be used are:
- breaking - Used when the PR is considered a breaking change.
- enhancement - Used when the PR adds a new feature or enhancement.
- bug - Used when the PR fixes a bug included in a previous release.
- documentation - Used when the PR adds or updates documentation.
- internal - Internal changes or things that don't fit in any other category.
**Note:** `release-plan` requires that **all** PRs are labeled. If a PR doesn't fit in a category it's fine to label it as `internal`
## Release
Once the prep work is completed, the actual release is straight forward: you just need to merge the open [Plan Release](https://github.com/shipshapecode/shepherd/pulls?q=is%3Apr+is%3Aopen+%22Prepare+Release%22+in%3Atitle) PR
================================================
FILE: docs-src/.dockerignore
================================================
# build output
dist/
# generated types
.astro/
# dependencies
node_modules/
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# environment variables
.env
.env.production
# macOS-specific files
.DS_Store
================================================
FILE: docs-src/.gitignore
================================================
# build output
dist/
# generated types
.astro/
# dependencies
node_modules/
# generated api docs
src/content/docs/api/*
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# environment variables
.env
.env.production
# macOS-specific files
.DS_Store
================================================
FILE: docs-src/.vscode/extensions.json
================================================
{
"recommendations": ["astro-build.astro-vscode"],
"unwantedRecommendations": []
}
================================================
FILE: docs-src/.vscode/launch.json
================================================
{
"version": "0.2.0",
"configurations": [
{
"command": "./node_modules/.bin/astro dev",
"name": "Development server",
"request": "launch",
"type": "node-terminal"
}
]
}
================================================
FILE: docs-src/Dockerfile
================================================
# syntax = docker/dockerfile:1
# Adjust NODE_VERSION as desired
ARG NODE_VERSION=20.10.0
FROM node:${NODE_VERSION}-slim as base
LABEL fly_launch_runtime="Astro"
# Astro app lives here
WORKDIR /app
# Set production environment
ENV NODE_ENV="production"
# Install pnpm
ARG PNPM_VERSION=9.2.0
RUN npm install -g pnpm@$PNPM_VERSION
# Throw-away build stage to reduce size of final image
FROM base as build
# Install packages needed to build node modules
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y build-essential node-gyp pkg-config python-is-python3
# Install node modules
COPY --link package.json pnpm-lock.yaml pnpm-workspace.yaml ./
# Copy library code
COPY --link ./shepherd.js ./shepherd.js
# Copy application code
COPY --link ./docs-src ./docs-src
RUN pnpm install --frozen-lockfile --prod=false
# Build application
RUN pnpm -F shepherd-docs run build
# Remove development dependencies
RUN pnpm prune --prod
# Final stage for app image
FROM nginx
# Copy built application
COPY --from=build /app/docs-src/dist /usr/share/nginx/html
# Start the server by default, this can be overwritten at runtime
EXPOSE 80
CMD [ "/usr/sbin/nginx", "-g", "daemon off;" ]
================================================
FILE: docs-src/README.md
================================================
# Starlight Starter Kit: Basics
[](https://starlight.astro.build)
```
npm create astro@latest -- --template starlight
```
[](https://stackblitz.com/github/withastro/starlight/tree/main/examples/basics)
[](https://codesandbox.io/p/sandbox/github/withastro/starlight/tree/main/examples/basics)
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fwithastro%2Fstarlight%2Ftree%2Fmain%2Fexamples%2Fbasics&project-name=my-starlight-docs&repository-name=my-starlight-docs)
> 🧑🚀 **Seasoned astronaut?** Delete this file. Have fun!
## 🚀 Project Structure
Inside of your Astro + Starlight project, you'll see the following folders and files:
```
.
├── public/
├── src/
│ ├── assets/
│ ├── content/
│ │ ├── docs/
│ │ └── config.ts
│ └── env.d.ts
├── astro.config.mjs
├── package.json
└── tsconfig.json
```
Starlight looks for `.md` or `.mdx` files in the `src/content/docs/` directory. Each file is exposed as a route based on its file name.
Images can be added to `src/assets/` and embedded in Markdown with a relative link.
Static assets, like favicons, can be placed in the `public/` directory.
## 🧞 Commands
All commands are run from the root of the project, from a terminal:
| Command | Action |
| :------------------------ | :----------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:4321` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
| `npm run astro -- --help` | Get help using the Astro CLI |
## 👀 Want to learn more?
Check out [Starlight’s docs](https://starlight.astro.build/), read [the Astro documentation](https://docs.astro.build), or jump into the [Astro Discord server](https://astro.build/chat).
================================================
FILE: docs-src/astro.config.mjs
================================================
import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';
import starlightTypeDoc, { typeDocSidebarGroup } from 'starlight-typedoc';
// https://astro.build/config
export default defineConfig({
integrations: [
starlight({
title: 'Documentation',
description:
'Shepherd is a way for guiding users through your app to that moment of "aha!".',
logo: {
src: './src/assets/Shepherd-Lamb.svg'
},
favicon: '/favicon.ico',
social: [
{
icon: 'github',
label: 'GitHub',
href: 'https://github.com/shipshapecode/shepherd'
},
{
icon: 'discord',
label: 'Discord',
href: 'https://discord.gg/EGcDW5NSud'
}
],
components: {
// Override the default `Head` component.
Head: './src/components/HeadWithPosthog.astro'
},
plugins: [
starlightTypeDoc({
entryPoints: ['./node_modules/shepherd.js/src/*.ts'],
tsconfig: './node_modules/shepherd.js/tsconfig.json',
typeDoc: {
entryPointStrategy: 'expand',
includeVersion: true
}
})
],
sidebar: [
{
label: 'Guides',
items: [
{
label: 'Install',
link: '/guides/install/'
},
{
label: 'Styling',
link: '/guides/styling/'
},
{
label: 'Usage',
link: '/guides/usage/'
},
{
label: 'License & Pricing',
link: '/guides/license/'
}
]
},
{
label: 'Recipes',
items: [
{
label: 'Cookbook',
link: '/recipes/cookbook/'
},
{
label: 'Analytics',
link: '/guides/analytics/'
},
{
label: 'React',
link: '/recipes/react/'
}
]
},
// {
// label: 'Reference',
// autogenerate: {
// directory: 'reference'
// }
// },
typeDocSidebarGroup
]
})
]
});
================================================
FILE: docs-src/fly.toml
================================================
# fly.toml app configuration file generated for shepherd-docs on 2024-04-23T14:17:39-07:00
#
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
#
app = 'shepherd-docs'
primary_region = 'dfw'
[build]
[http_service]
internal_port = 80
force_https = true
auto_stop_machines = true
auto_start_machines = true
min_machines_running = 0
processes = ['app']
[[vm]]
memory = '1gb'
cpu_kind = 'shared'
cpus = 1
================================================
FILE: docs-src/package.json
================================================
{
"name": "shepherd-docs",
"type": "module",
"version": "0.0.6",
"private": "true",
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro check && astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"@astrojs/starlight": "^0.37.6",
"astro": "^5.18.0",
"shepherd.js": "workspace:*",
"starlight-typedoc": "^0.21.5"
},
"devDependencies": {
"@astrojs/check": "^0.9.8",
"@flydotio/dockerfile": "^0.7.10",
"sharp": "^0.34.5",
"typedoc": "^0.28.17",
"typedoc-plugin-markdown": "^4.10.0",
"typescript": "^5.9.3"
}
}
================================================
FILE: docs-src/src/components/HeadWithPosthog.astro
================================================
---
import type { Props } from '@astrojs/starlight/props';
import Default from '@astrojs/starlight/components/Head.astro';
import PostHog from './Posthog.astro';
---
.example-css-selector element.',
attachTo: {
element: '.example-css-selector',
on: 'bottom'
},
classes: 'example-step-extra-class',
buttons: [
{
text: 'Next',
action: tour.next
}
]
});
```
Finally, to start the tour, just call `start` on your `Tour` instance:
```javascript
tour.start();
```
If you need to remove a step from your tour, call `removeStep` on your `Tour`
instance. If the step currently being displayed is the one you're removing, and
there are steps left in the tour, then the first one will be shown, otherwise,
the tour will be cancelled.
```javascript
tour.removeStep('example-step');
```
### API
#### Global Shepherd Object
Shepherd exposes a single object onto the window, `Shepherd`.
That global object fires several events to let you link up actions with events
occurring in _any_ tour:
##### Methods
- `Shepherd.on(eventName, handler, [context])`: Bind an event
- `Shepherd.off(eventName, [handler])`: Unbind an event
- `Shepherd.once(eventName, handler, [context])`: Bind just the next instance of
an event
##### Events
The global Shepherd fires the following events whenever a `Tour` instance fires
them. It adds to the object passed to the event handlers a `tour` key pointing
to the instance which fired the event:
- `complete`
- `cancel`
- `hide`
- `show`
- `start`
- `active`
- `inactive`
For multiple events, you can use something like:
```javascript
['complete', 'cancel'].forEach((event) =>
shepherd.on(event, () => {
// some code here
})
);
```
##### Current Tour
The global `Shepherd` includes a property which is always set to the currently
active tour, or null if there is no active tour:
- `Shepherd.activeTour`
#### Tour Instances
##### Creation
You create a `Tour` object for each tour you'd like to create.
Tour's constructor accepts a hash of options:
```javascript
const myTour = new Shepherd.Tour(options);
```
##### Tour Options
- `classPrefix`: The prefix to add to the `shepherd-enabled` and
`shepherd-target` class names as well as the `data-shepherd-step-id`.
- `confirmCancel`:
- If true, will issue a `window.confirm` before cancelling
- If it is a function(support Async Function), it will be called and wait for
the return value, and will only be cancelled if the value returned is true
- `confirmCancelMessage`: The message to display in the confirm dialog
- `defaultStepOptions`: Default options for Steps created through `addStep`
- `exitOnEsc`: Exiting the tour with the escape key will be enabled unless this
is explicitly set to `false`.
- `keyboardNavigation`: Navigating the tour via left and right arrow keys will
be enabled unless this is explicitly set to `false`.
- `stepsContainer` An optional container element for the steps. If not set, the
steps will be appended to `document.body`.
- `modalContainer` An optional container element for the modal. If not set, the
modal will be appended to `document.body`.
- `steps`: An array of step options objects or Step instances to initialize the
tour with.
- `tourName`: An optional "name" for the tour. This will be appended to the the
tour's dynamically generated `id` property.
- `useModalOverlay`: Whether or not steps should be placed above a darkened
modal overlay. If true, the overlay will create an opening around the target
element so that it can remain interactive.
##### Tour Methods
- `addStep(options)`: Creates a new Step object with options, and returns the
`Step` instance it created. If the options hash doesn't include an `id`, one
will be generated. You can also pass an existing `Step` instance rather than
`options`, but note that Shepherd does not support a Step being attached to
multiple Tours.
- `addSteps([Steps])`: Add multiple steps to the tour
- `getById(id)`: Return a step with a specific id
- `isActive()`: Check if the tour is active
- `next()`: Advance to the next step, in the order they were added
- `back()`: Show the previous step, in the order they were added
- `cancel()`: Trigger cancel on the current step, hiding it without advancing
- `complete()`: Calls \_done() triggering the `complete` event
- `hide()`: Hide the current step
- `show([id])`: Show the step specified by id (if it's a string), or index (if
it's a number) provided. Defaults to the first step.
- `start()`: Show the first step and begin the tour
- `getCurrentStep()`: Returns the currently shown step
- `removeStep(id)`: Removes the step from the tour
- `on(eventName, handler, [context])`: Bind an event
- `off(eventName, [handler])`: Unbind an event
- `once(eventName, handler, [context])`: Bind just the next instance of an event
##### Tour Events
- `complete`: Triggered when the last step is advanced
- `cancel`
- `hide`
- `show`: Triggered with a hash of the `step` and the `previous` step
- `start`
Steps are instances of the Step object. They are generally created by the
`Tour::addStep` method, which returns the `Step` instance it created.
#### Steps
##### Step Options
- `text`: The text in the body of the step. It can be one of three types:
- HTML string
- `HTMLElement` object
- `Function` to be executed when the step is built. It must return one the two
options above.
- `title`: The step's title. It becomes an `h3` at the top of the step.
- `attachTo`: The element the step should be attached to on the page. An object
with properties `element` and `on`.
- `element`: An element selector string, a DOM element, or a function
(returning a selector, a DOM element, `null` or `undefined`).
- `on`: The optional direction to place the Floating UI tooltip relative to
the element.
- Possible string values: 'auto', 'auto-start', 'auto-end', 'top',
'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'right',
'right-start', 'right-end', 'left', 'left-start', 'left-end'
```js
const new Step(tour, {
attachTo: { element: '.some .selector-path', on: 'left' },
...moreOptions
});
```
If you don’t specify an `attachTo` the element will appear in the middle of the
screen. The same will happen if your `attachTo.element` callback returns `null`,
`undefined`, or a selector that does not exist in the DOM.
If you omit the `on` portion of `attachTo`, the element will still be
highlighted, but the tooltip will appear in the middle of the screen, without an
arrow pointing to the target.
If the element to highlight does not yet exist while instantiating tour steps,
you may use lazy evaluation by supplying a function to `attachTo.element`. The
function will be called in the `before-show` phase.
- `beforeShowPromise`: A function that returns a promise. When the promise
resolves, the rest of the `show` code for the step will execute. For example:
```javascript
beforeShowPromise: function() {
return new Promise(function(resolve) {
$('#my-bootstrap-modal').on('shown.bs.modal', function () {
resolve();
});
});
},
```
- `canClickTarget` A boolean, that when set to false, will set
`pointer-events: none` on the target
- `cancelIcon` Options for the cancel icon
- `attrs` Additional HTML attributes to apply to the cancel icon button
element. This is useful for adding data attributes for testing or analytics.
Note: These attributes cannot override critical properties like `type`,
`onclick`, `class`, or `aria-label`.
```javascript
cancelIcon: {
enabled: true,
label: 'Close Tour',
attrs: {
'data-test': 'close-tour-button',
'data-analytics-id': 'tour-close'
}
}
```
- `enabled` Should a cancel "✕" be shown in the header of the step?
- `label` The label to add for `aria-label`
- `classes`: A string of extra classes to add to the step's content element.
- `buttons`: An array of buttons to add to the step. These will be rendered in a
footer below the main body text. Each button in the array is an object of the
format:
- `attrs`: Additional HTML attributes to apply to the button element. Useful
for adding data attributes for testing or analytics.
- `label`: The label to add for `aria-label`. It can also be a function that
returns a string (useful with i18n solutions).
- `disabled`: A boolean that controls the `disabled` attribute. It can also be
a function that returns a boolean.
- `classes`: Extra classes to apply to the ``
- `secondary`: A boolean, that when true, adds a `shepherd-button-secondary`
class to the button
- `text`: The HTML text of the button. It can also be a function that returns
a string (useful with i18n solutions).
- `action`: A function executed when the button is clicked on. It is
automatically bound to the `tour` the step is associated with, so things
like `this.next` will work inside the action. You can use action to skip
steps or navigate to specific steps, with something like:
```javascript
action() {
return this.show('some_step_name');
}
```
- `extraHighlights`: An array of extra element selectors to highlight when the
overlay is shown The tooltip won’t be fixed to these elements, but they will
be highlighted just like the attachTo element.
- `advanceOn`: An action on the page which should advance shepherd to the next
step. It should be an object with a string `selector` and an `event` name. For
example: `{selector: '.some-element', event: 'click'}`. It doesn't have to be
an event inside the tour, it can be any event fired on any element on the
page. You can also always manually advance the Tour by calling
`myTour.next()`.
- `highlightClass`: An extra class to apply to the `attachTo` element when it is
highlighted (that is, when its step is active). You can then target that
selector in your CSS.
- `id`: The string to use as the `id` for the step. If an id is not passed one
will be generated.
- `modalOverlayOpeningPadding`: An amount of padding to add around the modal
overlay opening
- `modalOverlayOpeningRadius`: An amount of border radius to add around the
modal overlay opening. It can be either a number or an object with properties
`{ topLeft, bottomLeft, bottomRight, topRight }`
- `floatingUIOptions`: Extra options to pass to
[Floating UI](https://floating-ui.com/docs/getting-started)
- `showOn`: A function that, when it returns true, will show the step. If it
returns false, the step will be skipped.
- `scrollTo`: Should the element be scrolled to when this step is shown? If
true, uses the default `scrollIntoView`, if an object, passes that object as
the params to `scrollIntoView` i.e. `{behavior: 'smooth', block: 'center'}`
- `scrollToHandler`: A function that lets you override the default `scrollTo`
behavior and define a custom action to do the scrolling, and possibly other
logic.
- `when`: You can define show, hide, etc events inside when. For example:
```javascript
when: {
show: function() {
window.scrollTo(0, 0);
}
}
```
##### Step Methods
- `show()`: Show this step
- `hide()`: Hide this step
- `cancel()`: Hide this step and trigger the `cancel` event
- `complete()`: Hide this step and trigger the `complete` event
- `scrollTo()`: Scroll to this step's element
- `isOpen()`: Returns true if the step is currently shown
- `destroy()`: Remove the element
- `on(eventName, handler, [context])`: Bind an event
- `off(eventName, [handler])`: Unbind an event
- `once(eventName, handler, [context])`: Bind just the next instance of an event
##### Step Events
- `before-show`
- `show`
- `before-hide`
- `hide`
- `complete`
- `cancel`
- `destroy`
Please note that `complete` and `cancel` are only ever triggered if you call the
associated methods in your code.
### Advancing on Actions
You can use the `advanceOn` option, or the Next button, to advance steps. If you
would like however to have a step advance on a complex user action, you can do
the following:
```javascript
const myStep = myTour.addStep(options);
yourApp.on('some-event', () => {
if (myStep.isOpen()) {
Shepherd.activeTour.next();
}
});
```
### 🔼 Displaying Arrows
By default, Shepherd will generate and position an "arrow" element that points
to the target of a step. This is done by setting the `arrow` option to `true` on
each ``Step.options` — but you can disable the arrow manually by setting
it to false:
```js
myTour.addStep({
id: 'Step 1',
arrow: false
});
```
You can also provide an options object, to configure the arrow's
[padding](https://floating-ui.com/docs/arrow#padding). The padding is the
closest the arrow will get to the edge of the step.
```js
myTour.addStep({
id: 'Step 1',
arrow: { padding: 10 }
});
```
Furthermore, while Shepherd provides some basic arrow styling, you can style it
as you wish by targeting the `.shepherd-arrow` element.
================================================
FILE: docs-src/src/content/docs/recipes/cookbook.md
================================================
---
title: Cookbook
---
### Disable Scroll
Previously, disabling scrolling was built into Shepherd, but it was buggy
and bulky, so we opted to remove [body-scroll-lock](https://github.com/willmcpo/body-scroll-lock)
as a dependency, in favor of users installing it directly in their apps. To disable scrolling,
you can install `body-scroll-lock` and run `bodyScrollLock.disableBodyScroll();` before
starting the tour, then `bodyScrollLock.clearAllBodyScrollLocks();` after stopping the tour.
### Highlighting multiple elements
Highlighting multiple elements is supported by Shepherd out of the box. You can pass an array of selectors to the `extraHighlights` option in the step configuration. This will highlight all the elements in the array as well as the target element defined in the `attachTo` option.
```javascript
const tour = new Shepherd.Tour({
steps: [
{
text: 'This is a step with multiple highlights',
attachTo: {
element: '.target-element',
on: 'bottom'
},
extraHighlights: ['.example-selector', '.example-selector-2']
}
]
});
```
If an element to be highlighted is contained by another element that is also being highlighted, the contained element will not be highlighted. This is to prevent the contained element from being obscured by the containing element.
### Offsets
By default, FloatingUI instances are placed directly next to their target. However, if you need to apply some margin
between them or if you need to fine tune the position according to some custom logic, you can use an offset middleware.
For example:
```js
import { offset } from '@floating-ui/dom';
const tour = new Shepherd.Tour({
steps: [
{
...
floatingUIOptions: {
middleware: [offset({ mainAxis: 0, crossAxis: 12 })]
}
...
}
]
});
```
### Progress Indicator
Using the already exposed API, you could add a progress indicator of your choosing
for each step to let your users know how far into a tour they may be.
The example below uses the [Step](https://docs.shepherdjs.dev/api/step/classes/step/) `options`
object and adds to `when` on the `show` event. Within that, we create an element
to render in the header with text of what step out of all potential steps is now
being show.
```javascript
when: {
show() {
const currentStep = Shepherd.activeTour?.getCurrentStep();
const currentStepElement = currentStep?.getElement();
const header = currentStepElement?.querySelector('.shepherd-header');
const progress = document.createElement('span');
progress.style['margin-right'] = '315px';
progress.innerText = `${Shepherd.activeTour?.steps.indexOf(currentStep) + 1}/${Shepherd.activeTour?.steps.length}`;
header?.insertBefore(progress, currentStepElement.querySelector('.shepherd-cancel-icon'));
}
}
```
Another example, for anyone who wants to add progress indicators to the footer. Add the `shepherd-progress` className and some extra styles.
```javascript
when: {
show() {
const currentStep = Shepherd.activeTour?.getCurrentStep();
const currentStepElement = currentStep?.getElement();
const footer = currentStepElement?.querySelector('.shepherd-footer');
const progress = document.createElement('span');
progress.className = 'shepherd-progress';
progress.innerText = `${Shepherd.activeTour?.steps.indexOf(currentStep) + 1} of ${Shepherd.activeTour?.steps.length}`;
footer?.insertBefore(progress, currentStepElement.querySelector('.shepherd-button:last-child'));
}
}
```
```scss
.shepherd-footer {
align-items: center;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
display: flex;
justify-content: space-between;
padding: 0 0.75rem 0.75rem;
.shepherd-button:last-child {
margin-right: 0;
}
.shepherd-progress {
font-size: 0.8rem;
}
}
```
================================================
FILE: docs-src/src/content/docs/recipes/react.md
================================================
---
title: React Usage
---
## Use in React
We've published a React package for years that really just exports the default Shepherd class and gives you some syntactical sugar for setting up Shepherd in a React app. This adds another layer from the root API and provides opinionated patterns. It's mainly a wrapper around the Shepherd library that exposes the tour object and methods to the context object that can be passed into props for dynamic interactivity. We think there's more than one way to create solutions and instead just prefer to provide a couple of examples on how you can do this.
### Custom hooks pattern
You can just create a hook that will let you setup and share a Tour instance and swap them out as needed.
```javascript
export const useShepherdTour = ({ tourOptions, steps }) => {
const tourObject = useMemo(() => {
const tourInstance = new Shepherd.Tour(tourOptions);
tourInstance.addSteps(steps);
return tourInstance;
}, [tourOptions, steps]);
return tourObject;
};
```
### Context/Provider pattern
```javascript
// App imports above, this is either in a root App.js/ts file or somewhere in your app
const ShepherdTourContext = React.createContext();
const ShepherdTourContextConsumer = ShepherdTourContext.Consumer;
const shepherdTourInstance = new Shepherd.Tour();
return (
Shepherd has full keyboard navigation support, focus trapping, and a11y compliance via aria attributes.
Shepherd's styles are kept minimal, allowing you to easily customize the look and feel, but still give you enough to drop in and be ready to go quickly.
Shepherd is ready to drop into your application using React, Ember, Angular, Vue.js, ES Modules, or plain Javascript!
`}
lang="js"
theme="nord"
wrap
/>
Shepherd remains open-source software, and this commitment is unwavering. Our library serves as a foundation for countless solo developers and esteemed organizations, empowering them to create their applications. With a thriving community of users, the ongoing enhancement and upkeep of the library, documentation, and community engagement demand significant resources. Your support is essential for us to continue this mission. So, if you want to use Shepherd.js in your commercial app, website or plugin, you would need to obtain a commercial license.
$50 /lifetime
Everything necessary to get started.
$300 /lifetime
Partner to use on all your products.
Request a Quote
Let us help you get the most out of Shepherd
const tour = new Shepherd.Tour({
defaultStepOptions: {
classes: 'shadow-md bg-purple-dark',
scrollTo: true
}
});
tour.addStep({
title: 'Example Shepherd',
text: 'Creating a Shepherd is easy too! Just create ...',
attachTo: {
element: '.hero-example',
on: 'bottom'
},
advanceOn: {
selector: '.docs-link',
event: 'click'
},
id: 'example'
});
tour.start();
const tour = new Shepherd.Tour({
defaultStepOptions: {
classes: 'shadow-md bg-purple-dark',
scrollTo: true
}
});
tour.addStep({
title: 'Example Shepherd',
text: 'Creating a Shepherd is easy too! Just create ...',
attachTo: {
element: '.hero-example',
on: 'bottom'
},
advanceOn: {
selector: '.docs-link',
event: 'click'
},
id: 'example'
});
tour.start();
const tour = new Shepherd.Tour({
defaultStepOptions: {
classes: 'shadow-md bg-purple-dark',
scrollTo: true
}
});
tour.addStep({
title: 'Example Shepherd',
text: 'Creating a Shepherd is easy too! Just create ...',
attachTo: {
element: '.hero-example',
on: 'bottom'
},
advanceOn: {
selector: '.docs-link',
event: 'click'
},
id: 'example'
});
tour.start();
Shepherd is a JavaScript library for guiding users through your app. It uses Floating UI, another open source library, to render dialogs for each tour "step".
Among many things, Floating UI makes sure your steps never end up off screen or cropped by an overflow. (Try resizing your browser to see what we mean.)
`, attachTo: { element: '.hero-welcome', on: 'bottom' }, classes: 'shepherd-step-element shepherd-transparent-text first-step', buttons: [ { action: shepherd.cancel, classes: 'shepherd-button-secondary cancel-button', text: 'Exit' }, { action: shepherd.next, classes: 'shepherd-button-example-primary next-button', text: 'Next' } ], id: 'welcome' }, { title: 'Including', text: 'Including Shepherd is easy! Just include shepherd.js. The styles are bundled with the JS.', attachTo: { element: '.hero-including', on: 'bottom' }, buttons: [ { action: shepherd.back, classes: 'shepherd-button-secondary back-button', text: 'Back' }, { action: shepherd.next, classes: 'shepherd-button-example-primary next-button', text: 'Next' } ], id: 'including', classes: 'shepherd-step-element second-step' }, { title: 'Example Shepherd', text: 'Creating a Shepherd is easy too! Just create Shepherd and add as many steps as you want. Check out the documentation to learn more.', attachTo: { element: '.hero-example', on: 'bottom' }, buttons: [ { action: shepherd.back, classes: 'shepherd-button-secondary back-button', text: 'Back' }, { action: shepherd.next, classes: 'shepherd-button-example-primary next-button', text: 'Next' } ], id: 'example', classes: 'shepherd-step-element third-step' }, { title: 'Learn more', text: 'Star Shepherd on Github so you remember it for your next project', attachTo: { element: '.hero-followup', on: 'left' }, buttons: [ { action: shepherd.back, classes: 'shepherd-button-secondary back-button', text: 'Back' }, { action: shepherd.next, classes: 'shepherd-button-example-primary next-button', text: 'Done' } ], id: 'followup', classes: 'shepherd-step-element fourth-step' } ]; } ================================================ FILE: shepherd.js/test/cypress/utils/setup-tour.js ================================================ import defaultSteps from './default-steps'; /** * Setup a tour * @param {Shepherd} Shepherd The Shepherd instance * @param {Object} globalDefaults A hash of the `defaultStepOptions` * @param {function} customSteps An array of the steps to add to the tour * @param {Object} otherOptions A hash of other options to pass to Shepherd */ export default function (Shepherd, globalDefaults, customSteps, otherOptions) { const defaultStepOptions = Object.assign( {}, { cancelIcon: { enabled: true } }, globalDefaults ); const shepherdOptions = Object.assign( {}, { defaultStepOptions }, otherOptions ); const shepherd = new Shepherd.Tour(shepherdOptions); const steps = typeof customSteps === 'function' ? customSteps(shepherd) : defaultSteps(shepherd); shepherd.addSteps(steps); return shepherd; } ================================================ FILE: shepherd.js/test/unit/babel.config.cjs ================================================ module.exports = function (api) { api.cache(true); return { env: { development: { presets: [ [ '@babel/preset-env', { loose: true } ] ] }, test: { presets: [ ['@babel/preset-env', { targets: { node: 'current' } }], ['@babel/preset-typescript', { allowDeclareFields: true }] ] } } }; }; ================================================ FILE: shepherd.js/test/unit/components/shepherd-button.spec.js ================================================ import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { createShepherdButton } from '../../../src/components/shepherd-button.ts'; describe('component/ShepherdButton', () => { let container; beforeEach(() => { container = document.createElement('div'); document.body.appendChild(container); }); afterEach(() => { container.remove(); }); describe('disabled', () => { it('should be enabled by default', () => { const button = createShepherdButton({}, undefined); container.appendChild(button); expect(button.disabled).toBeFalsy(); }); it('is enabled when false', () => { const button = createShepherdButton({ disabled: false }, undefined); container.appendChild(button); expect(button.disabled).toBeFalsy(); }); it('can be disabled with boolean', () => { const button = createShepherdButton({ disabled: true }, undefined); container.appendChild(button); expect(button.disabled).toBeTruthy(); }); it('can be disabled with function', () => { const button = createShepherdButton({ disabled: () => true }, undefined); container.appendChild(button); expect(button.disabled).toBeTruthy(); }); }); describe('label', () => { it('string', () => { const button = createShepherdButton({ label: 'Test' }, undefined); container.appendChild(button); expect(button).toHaveAttribute('aria-label', 'Test'); }); it('number', () => { const button = createShepherdButton({ label: 5 }, undefined); container.appendChild(button); expect(button).toHaveAttribute('aria-label', '5'); }); it('function', () => { const button = createShepherdButton({ label: () => 'Test' }, undefined); container.appendChild(button); expect(button).toHaveAttribute('aria-label', 'Test'); }); it('function re-creation uses updated value', () => { let label = 'Test'; const button = createShepherdButton({ label: () => label }, undefined); container.appendChild(button); expect(button).toHaveAttribute('aria-label', 'Test'); label = 'Test 2'; const buttonUpdated = createShepherdButton( { label: () => label }, undefined ); container.appendChild(buttonUpdated); expect(buttonUpdated).toHaveAttribute('aria-label', 'Test 2'); }); it('null', () => { const button = createShepherdButton({ label: null }, undefined); container.appendChild(button); expect(button).not.toHaveAttribute('aria-label'); }); it('undefined', () => { const button = createShepherdButton({}, undefined); container.appendChild(button); expect(button).not.toHaveAttribute('aria-label'); }); }); describe('text', () => { it('string', () => { const button = createShepherdButton({ text: 'Test' }, undefined); container.appendChild(button); expect(button).toHaveTextContent('Test'); }); it('function', () => { const button = createShepherdButton({ text: () => 'Test' }, undefined); container.appendChild(button); expect(button).toHaveTextContent('Test'); }); it('function re-creation uses updated value', () => { let text = 'Test'; const button = createShepherdButton({ text: () => text }, undefined); container.appendChild(button); expect(button).toHaveTextContent('Test'); text = 'Test 2'; const buttonUpdated = createShepherdButton( { text: () => text }, undefined ); container.appendChild(buttonUpdated); expect(buttonUpdated).toHaveTextContent('Test 2'); }); }); describe('attrs', () => { it('applies single data attribute', () => { const button = createShepherdButton( { attrs: { 'data-test': 'my-button' } }, undefined ); container.appendChild(button); expect(button).toHaveAttribute('data-test', 'my-button'); }); it('applies multiple custom attributes', () => { const button = createShepherdButton( { attrs: { 'data-test': 'my-button', 'data-step': '1', 'data-analytics': 'click-event', title: 'Click me' } }, undefined ); container.appendChild(button); expect(button).toHaveAttribute('data-test', 'my-button'); expect(button).toHaveAttribute('data-step', '1'); expect(button).toHaveAttribute('data-analytics', 'click-event'); expect(button).toHaveAttribute('title', 'Click me'); }); it('converts number and boolean values to strings', () => { const button = createShepherdButton( { attrs: { 'data-count': 42, 'data-enabled': true, 'data-rate': 3.14 } }, undefined ); container.appendChild(button); expect(button).toHaveAttribute('data-count', '42'); expect(button).toHaveAttribute('data-enabled', 'true'); expect(button).toHaveAttribute('data-rate', '3.14'); }); it('does not override core button attributes', () => { const mockAction = () => {}; const mockStep = { tour: {} }; const button = createShepherdButton( { text: 'Next', classes: 'my-class', disabled: true, label: 'Next Step', action: mockAction, attrs: { type: 'submit', // Should NOT override class: 'wrong-class', // Should NOT override disabled: false, // Should NOT override 'aria-label': 'Wrong Label', // Should NOT override tabindex: '5', // Should NOT override 'data-test': 'next-btn' // SHOULD apply } }, mockStep ); container.appendChild(button); // Core attributes should be protected expect(button).toHaveAttribute('type', 'button'); expect(button.className).toContain('shepherd-button'); expect(button.className).toContain('my-class'); expect(button.disabled).toBeTruthy(); expect(button).toHaveAttribute('aria-label', 'Next Step'); expect(button).toHaveAttribute('tabindex', '0'); // Non-conflicting custom attribute should work expect(button).toHaveAttribute('data-test', 'next-btn'); }); it('works with empty attrs object', () => { const button = createShepherdButton({ attrs: {} }, undefined); container.appendChild(button); expect(button).toBeInTheDocument(); expect(button).toHaveAttribute('type', 'button'); expect(button.className).toContain('shepherd-button'); }); it('works with undefined attrs', () => { const button = createShepherdButton({}, undefined); container.appendChild(button); expect(button).toBeInTheDocument(); expect(button).toHaveAttribute('type', 'button'); }); it('works alongside all other button properties', () => { const mockAction = () => {}; const mockStep = { tour: {} }; const button = createShepherdButton( { text: 'Next Step', label: 'Proceed forward', classes: 'custom-class', secondary: true, disabled: false, action: mockAction, attrs: { 'data-test': 'next-btn', 'data-step-id': '5', id: 'my-button-id' } }, mockStep ); container.appendChild(button); expect(button).toHaveTextContent('Next Step'); expect(button).toHaveAttribute('aria-label', 'Proceed forward'); expect(button.className).toContain('custom-class'); expect(button.className).toContain('shepherd-button-secondary'); expect(button.disabled).toBeFalsy(); expect(button).toHaveAttribute('data-test', 'next-btn'); expect(button).toHaveAttribute('data-step-id', '5'); expect(button).toHaveAttribute('id', 'my-button-id'); }); it('handles special characters in attribute values', () => { const button = createShepherdButton( { attrs: { 'data-text': 'Hello "World"', 'data-path': '/api/v1/test', 'data-symbol': '<>&' } }, undefined ); container.appendChild(button); expect(button).toHaveAttribute('data-text', 'Hello "World"'); expect(button).toHaveAttribute('data-path', '/api/v1/test'); expect(button).toHaveAttribute('data-symbol', '<>&'); }); }); }); ================================================ FILE: shepherd.js/test/unit/components/shepherd-content.spec.js ================================================ import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { createShepherdContent } from '../../../src/components/shepherd-content.ts'; describe('components/ShepherdContent', () => { let container; beforeEach(() => { container = document.createElement('div'); document.body.appendChild(container); }); afterEach(() => { container.remove(); }); describe('header', () => { it('is rendered when title is present and cancelIcon is enabled', () => { const step = { options: { title: 'I am some test title.', cancelIcon: { enabled: true } } }; const el = createShepherdContent('test-desc', 'test-label', step); container.appendChild(el); expect( container.querySelector('.shepherd-content .shepherd-header') ).toBeInTheDocument(); }); it('is rendered when title is present', () => { const step = { options: { title: 'I am some test title.' } }; const el = createShepherdContent('test-desc', 'test-label', step); container.appendChild(el); expect( container.querySelector('.shepherd-content .shepherd-header') ).toBeInTheDocument(); }); it('is rendered when cancelIcon is enabled', () => { const step = { options: { cancelIcon: { enabled: true } } }; const el = createShepherdContent('test-desc', 'test-label', step); container.appendChild(el); expect( container.querySelector('.shepherd-content .shepherd-header') ).toBeInTheDocument(); }); it('is not rendered when title is not present and cancelIcon is disabled', () => { const step = { options: { title: undefined } }; const el = createShepherdContent('test-desc', 'test-label', step); container.appendChild(el); expect( container.querySelector('.shepherd-header') ).not.toBeInTheDocument(); }); }); }); ================================================ FILE: shepherd.js/test/unit/components/shepherd-element.spec.js ================================================ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { createShepherdElement } from '../../../src/components/shepherd-element.ts'; import { Step } from '../../../src/step'; import { Tour } from '../../../src/tour'; function fireKeyDown(el, keyCode, opts = {}) { el.dispatchEvent( new KeyboardEvent('keydown', { keyCode, bubbles: true, ...opts }) ); } describe('components/ShepherdElement', () => { let container; beforeEach(() => { container = document.createElement('div'); document.body.appendChild(container); }); afterEach(() => { container.remove(); }); describe('arrow', () => { it('arrows shown by default', () => { const testElement = document.createElement('div'); const tour = new Tour(); const step = new Step(tour, { attachTo: { element: testElement, on: 'top' } }); const { element, cleanup } = createShepherdElement({ descriptionId: 'test-desc', labelId: 'test-label', step }); container.appendChild(element); expect( container.querySelectorAll('.shepherd-element .shepherd-arrow').length ).toBe(1); cleanup(); }); it('arrow: false hides arrows', () => { const testElement = document.createElement('div'); const tour = new Tour(); const step = new Step(tour, { arrow: false, attachTo: { element: testElement, on: 'top' } }); const { element, cleanup } = createShepherdElement({ descriptionId: 'test-desc', labelId: 'test-label', step }); container.appendChild(element); expect( container.querySelectorAll('.shepherd-element .shepherd-arrow').length ).toBe(0); cleanup(); }); it('arrow: object with padding shows arrow', () => { const testElement = document.createElement('div'); const tour = new Tour(); const step = new Step(tour, { arrow: { padding: 10 }, attachTo: { element: testElement, on: 'top' } }); const { element, cleanup } = createShepherdElement({ descriptionId: 'test-desc', labelId: 'test-label', step }); container.appendChild(element); expect( container.querySelectorAll('.shepherd-element .shepherd-arrow').length ).toBe(1); cleanup(); }); it('arrow: empty object shows arrow', () => { const testElement = document.createElement('div'); const tour = new Tour(); const step = new Step(tour, { arrow: {}, attachTo: { element: testElement, on: 'top' } }); const { element, cleanup } = createShepherdElement({ descriptionId: 'test-desc', labelId: 'test-label', step }); container.appendChild(element); expect( container.querySelectorAll('.shepherd-element .shepherd-arrow').length ).toBe(1); cleanup(); }); }); describe('handleKeyDown', () => { it('exitOnEsc: true - ESC cancels the tour', () => { const tour = new Tour(); const step = new Step(tour, {}); const stepCancelSpy = vi.spyOn(step, 'cancel'); const { element, cleanup } = createShepherdElement({ descriptionId: 'test-desc', labelId: 'test-label', step }); container.appendChild(element); fireKeyDown(element, 27); expect(stepCancelSpy).toHaveBeenCalled(); cleanup(); }); it('exitOnEsc: false - ESC does not cancel the tour', () => { const tour = new Tour({ exitOnEsc: false }); const step = new Step(tour, {}); const stepCancelSpy = vi.spyOn(step, 'cancel'); const { element, cleanup } = createShepherdElement({ descriptionId: 'test-desc', labelId: 'test-label', step }); container.appendChild(element); fireKeyDown(element, 27); expect(stepCancelSpy).not.toHaveBeenCalled(); cleanup(); }); it('keyboardNavigation: true - arrow keys move between steps', () => { const tour = new Tour(); const step = new Step(tour, {}); let propagateValue = 0; const tourBackStub = vi.spyOn(tour, 'back').mockImplementation(() => {}); const tourNextStub = vi.spyOn(tour, 'next').mockImplementation(() => {}); // Add a keystroke listener to a parent to test event propagation const propagateHandler = (event) => { if ([27, 37, 39].includes(event.keyCode)) { propagateValue += 1; } }; document.body.addEventListener('keydown', propagateHandler); expect(tourBackStub).not.toHaveBeenCalled(); expect(tourNextStub).not.toHaveBeenCalled(); const { element, cleanup } = createShepherdElement({ descriptionId: 'test-desc', labelId: 'test-label', step }); container.appendChild(element); fireKeyDown(element, 39); expect(tourNextStub).toHaveBeenCalled(); // There should be no event propagation expect(propagateValue).toBe(0); fireKeyDown(element, 37); expect(tourBackStub).toHaveBeenCalled(); // There should be no event propagation expect(propagateValue).toBe(0); tourBackStub.mockRestore(); tourNextStub.mockRestore(); document.body.removeEventListener('keydown', propagateHandler); cleanup(); }); it('keyboardNavigation: false - arrow keys do not move between steps', () => { const tour = new Tour({ keyboardNavigation: false }); const step = new Step(tour, {}); let propagateValue = 0; const tourBackStub = vi.spyOn(tour, 'back').mockImplementation(() => {}); const tourNextStub = vi.spyOn(tour, 'next').mockImplementation(() => {}); // Add a keystroke listener to a parent to test event propagation const propagateHandler = (event) => { if ([27, 37, 39].includes(event.keyCode)) { propagateValue += 1; } }; document.body.addEventListener('keydown', propagateHandler); expect(tourBackStub).not.toHaveBeenCalled(); expect(tourNextStub).not.toHaveBeenCalled(); const { element, cleanup } = createShepherdElement({ descriptionId: 'test-desc', labelId: 'test-label', step }); container.appendChild(element); fireKeyDown(element, 39); expect(tourNextStub).not.toHaveBeenCalled(); // There should be event propagation expect(propagateValue).toBe(1); fireKeyDown(element, 37); expect(tourBackStub).not.toHaveBeenCalled(); // There should be another event propagation expect(propagateValue).toBe(2); tourBackStub.mockRestore(); tourNextStub.mockRestore(); document.body.removeEventListener('keydown', propagateHandler); cleanup(); }); it('Tab key: prevents default when no focusable elements exist', () => { const tour = new Tour(); // Step with no buttons, no cancel icon — dialog has no focusable children const step = new Step(tour, {}); const { element, cleanup } = createShepherdElement({ descriptionId: 'test-desc', labelId: 'test-label', step }); container.appendChild(element); const prevented = []; element.addEventListener( 'keydown', (e) => { prevented.push(e.defaultPrevented); }, { capture: false } ); fireKeyDown(element, 9, { cancelable: true }); expect(prevented[0]).toBe(true); cleanup(); }); it('Shift+Tab from first dialog element focuses last attachTo element', () => { const tour = new Tour(); const attachToEl = document.createElement('div'); const attachToBtn = document.createElement('button'); attachToBtn.textContent = 'Attach Button'; attachToEl.appendChild(attachToBtn); container.appendChild(attachToEl); const step = new Step(tour, { attachTo: { element: attachToEl, on: 'bottom' }, buttons: [{ text: 'Next', action: () => {} }] }); const { element, cleanup } = createShepherdElement({ descriptionId: 'test-desc', labelId: 'test-label', step }); container.appendChild(element); // First focusable dialog element is the "Next" button const dialogBtn = element.querySelector('button.shepherd-button'); expect(dialogBtn).toBeTruthy(); dialogBtn.focus(); const focusSpy = vi.spyOn(attachToBtn, 'focus'); fireKeyDown(element, 9, { shiftKey: true }); // The last focusable attachTo element (attachToBtn) should be focused expect(focusSpy).toHaveBeenCalled(); focusSpy.mockRestore(); cleanup(); }); it('Shift+Tab from first attachTo element focuses last dialog element', () => { const tour = new Tour(); const attachToEl = document.createElement('button'); attachToEl.textContent = 'Target'; container.appendChild(attachToEl); const step = new Step(tour, { attachTo: { element: attachToEl, on: 'bottom' }, buttons: [{ text: 'Next', action: () => {} }] }); const { element, cleanup } = createShepherdElement({ descriptionId: 'test-desc', labelId: 'test-label', step }); container.appendChild(element); // Focus the attachTo element (first focusable attach-to element) attachToEl.focus(); const dialogBtn = element.querySelector('button.shepherd-button'); const focusSpy = vi.spyOn(dialogBtn, 'focus'); // Fire on the attachTo element since it also has the keydown listener fireKeyDown(attachToEl, 9, { shiftKey: true }); expect(focusSpy).toHaveBeenCalled(); focusSpy.mockRestore(); cleanup(); }); it('forward Tab from last dialog element focuses first attachTo element', () => { const tour = new Tour(); const attachToEl = document.createElement('button'); attachToEl.textContent = 'Target'; container.appendChild(attachToEl); const step = new Step(tour, { attachTo: { element: attachToEl, on: 'bottom' }, buttons: [{ text: 'Next', action: () => {} }] }); const { element, cleanup } = createShepherdElement({ descriptionId: 'test-desc', labelId: 'test-label', step }); container.appendChild(element); // Focus the last dialog element (the button) const dialogBtn = element.querySelector('button.shepherd-button'); dialogBtn.focus(); const focusSpy = vi.spyOn(attachToEl, 'focus'); fireKeyDown(element, 9); // First focusable attachTo element should receive focus expect(focusSpy).toHaveBeenCalled(); focusSpy.mockRestore(); cleanup(); }); it('forward Tab from last attachTo element focuses first dialog element', () => { const tour = new Tour(); const attachToEl = document.createElement('button'); attachToEl.textContent = 'Target'; container.appendChild(attachToEl); const step = new Step(tour, { attachTo: { element: attachToEl, on: 'bottom' }, buttons: [{ text: 'Next', action: () => {} }] }); const { element, cleanup } = createShepherdElement({ descriptionId: 'test-desc', labelId: 'test-label', step }); container.appendChild(element); // Focus the last attachTo element (the button itself, since it's the only one) attachToEl.focus(); const dialogBtn = element.querySelector('button.shepherd-button'); const focusSpy = vi.spyOn(dialogBtn, 'focus'); // Fire on the attachTo element fireKeyDown(attachToEl, 9); expect(focusSpy).toHaveBeenCalled(); focusSpy.mockRestore(); cleanup(); }); it('unhandled key falls through to default case', () => { const tour = new Tour(); const step = new Step(tour, {}); const { element, cleanup } = createShepherdElement({ descriptionId: 'test-desc', labelId: 'test-label', step }); container.appendChild(element); // Press an unrelated key (e.g. 'A' = keyCode 65) and ensure nothing breaks fireKeyDown(element, 65); // No error thrown, element is still in DOM expect(element.parentNode).toBe(container); cleanup(); }); }); }); ================================================ FILE: shepherd.js/test/unit/components/shepherd-footer.spec.js ================================================ import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { createShepherdFooter } from '../../../src/components/shepherd-footer.ts'; import defaultButtons from '../../cypress/utils/default-buttons.js'; describe('components/ShepherdFooter', () => { let container; beforeEach(() => { container = document.createElement('div'); document.body.appendChild(container); }); afterEach(() => { container.remove(); }); it('renders no buttons if an empty array is passed to `options.buttons`', () => { const step = { options: { buttons: [] } }; const el = createShepherdFooter(step); container.appendChild(el); const buttons = container.querySelectorAll( '.shepherd-footer .shepherd-button' ); expect(buttons.length).toBe(0); }); it('renders no buttons if nothing is passed to `options.buttons`', () => { const step = { options: {} }; const el = createShepherdFooter(step); container.appendChild(el); const buttons = container.querySelectorAll( '.shepherd-footer .shepherd-button' ); expect(buttons.length).toBe(0); }); it('renders buttons for each item passed to `options.buttons`', () => { const step = { options: { buttons: [defaultButtons.cancel, defaultButtons.next] } }; const el = createShepherdFooter(step); container.appendChild(el); const buttons = container.querySelectorAll( '.shepherd-footer .shepherd-button' ); expect(buttons.length).toBe(2); const cancelButton = container.querySelector('footer .cancel-button'); expect(cancelButton).toHaveAttribute('tabindex', '0'); expect(cancelButton).toHaveClass( 'shepherd-button-secondary cancel-button shepherd-button' ); expect(cancelButton).toHaveTextContent('Exit'); const nextButton = container.querySelector('footer .next-button'); expect(nextButton).toHaveAttribute('tabindex', '0'); expect(nextButton).toHaveClass( 'shepherd-button-primary next-button shepherd-button' ); expect(nextButton).toHaveTextContent('Next'); }); }); ================================================ FILE: shepherd.js/test/unit/components/shepherd-header.spec.js ================================================ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { createShepherdHeader } from '../../../src/components/shepherd-header.ts'; import { Tour } from '../../../src/tour'; import { Step } from '../../../src/step'; describe('components/ShepherdHeader', () => { let container; beforeEach(() => { container = document.createElement('div'); document.body.appendChild(container); }); afterEach(() => { container.remove(); }); it('cancel icon is added when cancelIcon.enabled === true', () => { const step = { options: { cancelIcon: { enabled: true } } }; const el = createShepherdHeader('test-label', step); container.appendChild(el); const cancelIcon = container.querySelector('.shepherd-cancel-icon'); expect(cancelIcon).toBeInTheDocument(); expect(cancelIcon).toHaveAttribute('aria-label', 'Close Tour'); expect(cancelIcon).toHaveAttribute('type', 'button'); }); it('cancel icon is not added when cancelIcon.enabled === false', () => { const step = { options: { cancelIcon: { enabled: false } } }; const el = createShepherdHeader('test-label', step); container.appendChild(el); const cancelIcon = container.querySelector('.shepherd-cancel-icon'); expect(cancelIcon).not.toBeInTheDocument(); }); it('cancel icon aria-label overridden when cancelIcon.label is set', () => { const step = { options: { cancelIcon: { enabled: true, label: 'Test' } } }; const el = createShepherdHeader('test-label', step); container.appendChild(el); expect(container.querySelector('.shepherd-cancel-icon')).toHaveAttribute( 'aria-label', 'Test' ); }); it('cancel icon cancels the tour', async () => { const tour = new Tour(); const step = new Step(tour, { cancelIcon: { enabled: true } }); const stepCancelSpy = vi.spyOn(step, 'cancel'); const el = createShepherdHeader('test-label', step); container.appendChild(el); container.querySelector('.shepherd-cancel-icon').click(); expect(stepCancelSpy).toHaveBeenCalled(); }); describe('cancel icon attrs', () => { it('applies custom attributes to cancel icon', () => { const step = { options: { cancelIcon: { enabled: true, attrs: { 'data-test': 'close-tour', 'data-analytics': 'tour-cancel' } } } }; const el = createShepherdHeader('test-label', step); container.appendChild(el); const cancelIcon = container.querySelector('.shepherd-cancel-icon'); expect(cancelIcon).toHaveAttribute('data-test', 'close-tour'); expect(cancelIcon).toHaveAttribute('data-analytics', 'tour-cancel'); }); it('cancel icon attrs work with custom label', () => { const step = { options: { cancelIcon: { enabled: true, label: 'Custom Close', attrs: { 'data-test': 'custom-close', id: 'tour-close-btn' } } } }; const el = createShepherdHeader('test-label', step); container.appendChild(el); const cancelIcon = container.querySelector('.shepherd-cancel-icon'); expect(cancelIcon).toHaveAttribute('aria-label', 'Custom Close'); expect(cancelIcon).toHaveAttribute('data-test', 'custom-close'); expect(cancelIcon).toHaveAttribute('id', 'tour-close-btn'); }); it('cancel icon does not override core attributes via attrs', () => { const step = { options: { cancelIcon: { enabled: true, label: 'Close', attrs: { type: 'submit', // Should NOT override class: 'wrong-class', // Should NOT override 'aria-label': 'Wrong Label', // Should NOT override 'data-test': 'close-btn' // SHOULD apply } } } }; const el = createShepherdHeader('test-label', step); container.appendChild(el); const cancelIcon = container.querySelector('.shepherd-cancel-icon'); expect(cancelIcon).toHaveAttribute('type', 'button'); expect(cancelIcon).toHaveClass('shepherd-cancel-icon'); expect(cancelIcon).toHaveAttribute('aria-label', 'Close'); expect(cancelIcon).toHaveAttribute('data-test', 'close-btn'); }); it('cancel icon works with empty attrs', () => { const step = { options: { cancelIcon: { enabled: true, attrs: {} } } }; const el = createShepherdHeader('test-label', step); container.appendChild(el); const cancelIcon = container.querySelector('.shepherd-cancel-icon'); expect(cancelIcon).toBeInTheDocument(); expect(cancelIcon).toHaveAttribute('type', 'button'); }); it('cancel icon handles numeric and boolean attrs values', () => { const step = { options: { cancelIcon: { enabled: true, attrs: { 'data-count': 5, 'data-active': true } } } }; const el = createShepherdHeader('test-label', step); container.appendChild(el); const cancelIcon = container.querySelector('.shepherd-cancel-icon'); expect(cancelIcon).toHaveAttribute('data-count', '5'); expect(cancelIcon).toHaveAttribute('data-active', 'true'); }); }); }); ================================================ FILE: shepherd.js/test/unit/components/shepherd-modal.spec.js ================================================ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { createShepherdModal } from '../../../src/components/shepherd-modal.ts'; import { Step } from '../../../src/step'; import { Tour } from '../../../src/tour'; describe('components/ShepherdModal', () => { let container; beforeEach(() => { container = document.createElement('div'); document.body.appendChild(container); }); afterEach(() => { container.remove(); }); describe('closeModalOpening()', function () { it('sets values back to 0', () => { const modal = createShepherdModal(container); modal.positionModal(0, 0, 0, 0, null, { getBoundingClientRect() { return { height: 250, x: 20, y: 20, width: 500 }; } }); let modalPath = modal.getElement().querySelector('path'); expect(modalPath).toHaveAttribute( 'd', 'M1024,768H0V0H1024V768ZM20,20a0,0,0,0,0-0,0V270a0,0,0,0,0,0,0H520a0,0,0,0,0,0-0V20a0,0,0,0,0-0-0Z' ); modal.closeModalOpening(); modalPath = modal.getElement().querySelector('path'); expect(modalPath).toHaveAttribute( 'd', 'M1024,768H0V0H1024V768ZM0,0a0,0,0,0,0-0,0V0a0,0,0,0,0,0,0H0a0,0,0,0,0,0-0V0a0,0,0,0,0-0-0Z' ); }); }); describe('positionModal()', function () { it('sets the correct attributes when positioning modal opening', () => { const modal = createShepherdModal(container); let modalPath = modal.getElement().querySelector('path'); expect(modalPath).toHaveAttribute( 'd', 'M1024,768H0V0H1024V768ZM0,0a0,0,0,0,0-0,0V0a0,0,0,0,0,0,0H0a0,0,0,0,0,0-0V0a0,0,0,0,0-0-0Z' ); modal.closeModalOpening(); modalPath = modal.getElement().querySelector('path'); expect(modalPath).toHaveAttribute( 'd', 'M1024,768H0V0H1024V768ZM0,0a0,0,0,0,0-0,0V0a0,0,0,0,0,0,0H0a0,0,0,0,0,0-0V0a0,0,0,0,0-0-0Z' ); modal.positionModal(0, 0, 0, 0, null, { getBoundingClientRect() { return { height: 250, x: 20, y: 20, width: 500 }; } }); modalPath = modal.getElement().querySelector('path'); expect(modalPath).toHaveAttribute( 'd', 'M1024,768H0V0H1024V768ZM20,20a0,0,0,0,0-0,0V270a0,0,0,0,0,0,0H520a0,0,0,0,0,0-0V20a0,0,0,0,0-0-0Z' ); }); it('sets the correct attributes with padding', () => { const modal = createShepherdModal(container); let modalPath = modal.getElement().querySelector('path'); expect(modalPath).toHaveAttribute( 'd', 'M1024,768H0V0H1024V768ZM0,0a0,0,0,0,0-0,0V0a0,0,0,0,0,0,0H0a0,0,0,0,0,0-0V0a0,0,0,0,0-0-0Z' ); modal.positionModal(10, 0, 0, 0, null, { getBoundingClientRect() { return { height: 250, x: 20, y: 20, width: 500 }; } }); modalPath = modal.getElement().querySelector('path'); expect(modalPath).toHaveAttribute( 'd', 'M1024,768H0V0H1024V768ZM10,10a0,0,0,0,0-0,0V280a0,0,0,0,0,0,0H530a0,0,0,0,0,0-0V10a0,0,0,0,0-0-0Z' ); }); it('sets the correct attributes when positioning modal opening with border radius as number', () => { const modal = createShepherdModal(container); let modalPath = modal.getElement().querySelector('path'); expect(modalPath).toHaveAttribute( 'd', 'M1024,768H0V0H1024V768ZM0,0a0,0,0,0,0-0,0V0a0,0,0,0,0,0,0H0a0,0,0,0,0,0-0V0a0,0,0,0,0-0-0Z' ); modal.closeModalOpening(); modalPath = modal.getElement().querySelector('path'); expect(modalPath).toHaveAttribute( 'd', 'M1024,768H0V0H1024V768ZM0,0a0,0,0,0,0-0,0V0a0,0,0,0,0,0,0H0a0,0,0,0,0,0-0V0a0,0,0,0,0-0-0Z' ); modal.positionModal(0, 10, 0, 0, null, { getBoundingClientRect() { return { height: 250, x: 20, y: 20, width: 500 }; } }); modalPath = modal.getElement().querySelector('path'); expect(modalPath).toHaveAttribute( 'd', 'M1024,768H0V0H1024V768ZM30,20a10,10,0,0,0-10,10V260a10,10,0,0,0,10,10H510a10,10,0,0,0,10-10V30a10,10,0,0,0-10-10Z' ); }); it('sets the correct attributes when positioning modal opening with border radius as object', () => { const modal = createShepherdModal(container); let modalPath = modal.getElement().querySelector('path'); expect(modalPath).toHaveAttribute( 'd', 'M1024,768H0V0H1024V768ZM0,0a0,0,0,0,0-0,0V0a0,0,0,0,0,0,0H0a0,0,0,0,0,0-0V0a0,0,0,0,0-0-0Z' ); modal.closeModalOpening(); modalPath = modal.getElement().querySelector('path'); expect(modalPath).toHaveAttribute( 'd', 'M1024,768H0V0H1024V768ZM0,0a0,0,0,0,0-0,0V0a0,0,0,0,0,0,0H0a0,0,0,0,0,0-0V0a0,0,0,0,0-0-0Z' ); modal.positionModal( 0, { topLeft: 1, bottomLeft: 2, bottomRight: 3 }, 0, 0, null, { getBoundingClientRect() { return { height: 250, x: 20, y: 20, width: 500 }; } } ); modalPath = modal.getElement().querySelector('path'); expect(modalPath).toHaveAttribute( 'd', 'M1024,768H0V0H1024V768ZM21,20a1,1,0,0,0-1,1V268a2,2,0,0,0,2,2H517a3,3,0,0,0,3-3V20a0,0,0,0,0-0-0Z' ); }); it('sets the correct attributes when target is overflowing from scroll parent', () => { const modal = createShepherdModal(container); modal.positionModal( 0, 0, 0, 0, { getBoundingClientRect() { return { height: 250, x: 10, y: 100, width: 500 }; } }, { getBoundingClientRect() { return { height: 500, x: 10, y: 10, width: 500 }; } } ); const modalPath = modal.getElement().querySelector('path'); expect(modalPath).toHaveAttribute( 'd', 'M1024,768H0V0H1024V768ZM10,100a0,0,0,0,0-0,0V350a0,0,0,0,0,0,0H510a0,0,0,0,0,0-0V100a0,0,0,0,0-0-0Z' ); }); it('sets the correct attributes when target fits inside scroll parent', () => { const modal = createShepherdModal(container); modal.positionModal( 0, 0, 0, 0, { getBoundingClientRect() { return { height: 500, x: 10, y: 10, width: 500 }; } }, { getBoundingClientRect() { return { height: 250, x: 10, y: 100, width: 500 }; } } ); const modalPath = modal.getElement().querySelector('path'); expect(modalPath).toHaveAttribute( 'd', 'M1024,768H0V0H1024V768ZM10,100a0,0,0,0,0-0,0V350a0,0,0,0,0,0,0H510a0,0,0,0,0,0-0V100a0,0,0,0,0-0-0Z' ); }); it('allows setting an x-axis offset', () => { const modal = createShepherdModal(container); modal.positionModal(0, 0, 50, 0, null, { getBoundingClientRect() { return { height: 250, x: 10, y: 10, width: 500 }; } }); let modalPath = modal.getElement().querySelector('path'); expect(modalPath).toHaveAttribute( 'd', 'M1024,768H0V0H1024V768ZM60,10a0,0,0,0,0-0,0V260a0,0,0,0,0,0,0H560a0,0,0,0,0,0-0V10a0,0,0,0,0-0-0Z' ); modal.positionModal(0, 0, 100, 0, null, { getBoundingClientRect() { return { height: 250, x: 10, y: 10, width: 500 }; } }); modalPath = modal.getElement().querySelector('path'); expect(modalPath).toHaveAttribute( 'd', 'M1024,768H0V0H1024V768ZM110,10a0,0,0,0,0-0,0V260a0,0,0,0,0,0,0H610a0,0,0,0,0,0-0V10a0,0,0,0,0-0-0Z' ); }); it('allows setting a y-axis offset', () => { const modal = createShepherdModal(container); modal.positionModal(0, 0, 0, 35, null, { getBoundingClientRect() { return { height: 250, x: 10, y: 10, width: 500 }; } }); let modalPath = modal.getElement().querySelector('path'); expect(modalPath).toHaveAttribute( 'd', 'M1024,768H0V0H1024V768ZM10,45a0,0,0,0,0-0,0V295a0,0,0,0,0,0,0H510a0,0,0,0,0,0-0V45a0,0,0,0,0-0-0Z' ); modal.positionModal(0, 0, 0, 75, null, { getBoundingClientRect() { return { height: 250, x: 10, y: 10, width: 500 }; } }); modalPath = modal.getElement().querySelector('path'); expect(modalPath).toHaveAttribute( 'd', 'M1024,768H0V0H1024V768ZM10,85a0,0,0,0,0-0,0V335a0,0,0,0,0,0,0H510a0,0,0,0,0,0-0V85a0,0,0,0,0-0-0Z' ); }); it('sets the correct attributes with extraHighlights', () => { const modal = createShepherdModal(container); modal.positionModal( 0, 0, 0, 0, null, { getBoundingClientRect() { return { height: 250, x: 20, y: 20, width: 500 }; } }, [ { getBoundingClientRect() { return { height: 100, x: 50, y: 50, width: 100 }; } } ] ); const modalPath = modal.getElement().querySelector('path'); expect(modalPath).toHaveAttribute( 'd', 'M1024,768H0V0H1024V768ZM20,20a0,0,0,0,0-0,0V270a0,0,0,0,0,0,0H520a0,0,0,0,0,0-0V20a0,0,0,0,0-0-0ZM50,50a0,0,0,0,0-0,0V150a0,0,0,0,0,0,0H150a0,0,0,0,0,0-0V50a0,0,0,0,0-0-0Z' ); }); it('sets the correct attributes with multiple extraHighlights', () => { const modal = createShepherdModal(container); modal.positionModal( 0, 0, 0, 0, null, { getBoundingClientRect() { return { height: 250, x: 20, y: 20, width: 500 }; } }, [ { getBoundingClientRect() { return { height: 100, x: 50, y: 50, width: 100 }; } }, { getBoundingClientRect() { return { height: 50, x: 200, y: 200, width: 50 }; } } ] ); const modalPath = modal.getElement().querySelector('path'); expect(modalPath).toHaveAttribute( 'd', 'M1024,768H0V0H1024V768ZM20,20a0,0,0,0,0-0,0V270a0,0,0,0,0,0,0H520a0,0,0,0,0,0-0V20a0,0,0,0,0-0-0ZM50,50a0,0,0,0,0-0,0V150a0,0,0,0,0,0,0H150a0,0,0,0,0,0-0V50a0,0,0,0,0-0-0ZM200,200a0,0,0,0,0-0,0V250a0,0,0,0,0,0,0H250a0,0,0,0,0,0-0V200a0,0,0,0,0-0-0Z' ); }); it('skips duplicate elements in extraHighlights', () => { const modal = createShepherdModal(container); const sharedElement = { getBoundingClientRect() { return { height: 100, x: 50, y: 50, width: 100, top: 50, bottom: 150, left: 50, right: 150 }; } }; modal.positionModal( 0, 0, 0, 0, null, { getBoundingClientRect() { return { height: 250, x: 20, y: 20, width: 500, top: 20, bottom: 270, left: 20, right: 520 }; } }, // Pass the same element twice — both duplicates are skipped [sharedElement, sharedElement] ); const modalPath = modal.getElement().querySelector('path'); const d = modalPath.getAttribute('d'); // Duplicate elements are both skipped, only the main target cutout remains // Outer path close + target cutout close = 2 Z's const cutouts = d.split('Z').length - 1; expect(cutouts).toBe(2); }); }); describe('setupForStep()', function () { it('useModalOverlay: false hides the modal', () => { const modal = createShepherdModal(container); modal.show(); expect(modal.getElement()).toHaveClass('shepherd-modal-is-visible'); const tour = new Tour({ useModalOverlay: false }); const step = new Step(tour, {}); modal.setupForStep(step); expect(modal.getElement()).not.toHaveClass('shepherd-modal-is-visible'); }); it('useModalOverlay: true shows the modal and calls _styleForStep', () => { const modal = createShepherdModal(container); const rafSpy = vi .spyOn(window, 'requestAnimationFrame') .mockImplementation(() => 1); const targetEl = document.createElement('div'); container.appendChild(targetEl); const tour = new Tour({ useModalOverlay: true }); const step = new Step(tour, { attachTo: { element: targetEl, on: 'bottom' } }); // Resolve attachTo so step.target is set step._resolveAttachToOptions(); step.target = targetEl; modal.setupForStep(step); expect(modal.getElement()).toHaveClass('shepherd-modal-is-visible'); // _styleForStep calls rafLoop which calls requestAnimationFrame expect(rafSpy).toHaveBeenCalled(); rafSpy.mockRestore(); }); }); describe('show/hide', function () { it('show adds classes', () => { const modal = createShepherdModal(container); modal.show(); expect(modal.getElement()).toHaveClass('shepherd-modal-is-visible'); }); it('hide removes classes', () => { const modal = createShepherdModal(container); modal.show(); modal.hide(); expect(modal.getElement()).not.toHaveClass('shepherd-modal-is-visible'); }); }); describe('destroy()', function () { it('removes the modal element from the DOM', () => { const modal = createShepherdModal(container); expect( container.querySelector('.shepherd-modal-overlay-container') ).toBeTruthy(); modal.destroy(); expect( container.querySelector('.shepherd-modal-overlay-container') ).toBeNull(); }); }); describe('_getScrollParent (via setupForStep)', function () { it('recurses to find a scrollable parent element', () => { const modal = createShepherdModal(container); const rafSpy = vi .spyOn(window, 'requestAnimationFrame') .mockImplementation(() => 1); // Create a scrollable parent const scrollParent = document.createElement('div'); Object.defineProperty(scrollParent, 'scrollHeight', { value: 500 }); Object.defineProperty(scrollParent, 'clientHeight', { value: 200 }); container.appendChild(scrollParent); const targetEl = document.createElement('div'); scrollParent.appendChild(targetEl); // Mock getComputedStyle so the target has 'visible' overflow (not scrollable) // and the scroll parent has 'auto' overflow (scrollable), forcing recursion const origGetComputedStyle = window.getComputedStyle; vi.spyOn(window, 'getComputedStyle').mockImplementation((el) => { if (el === targetEl) { return { overflowY: 'visible' }; } if (el === scrollParent) { return { overflowY: 'auto' }; } return origGetComputedStyle(el); }); const tour = new Tour({ useModalOverlay: true }); const step = new Step(tour, { attachTo: { element: targetEl, on: 'bottom' } }); step._resolveAttachToOptions(); step.target = targetEl; // setupForStep triggers _styleForStep -> _getScrollParent modal.setupForStep(step); expect(modal.getElement()).toHaveClass('shepherd-modal-is-visible'); rafSpy.mockRestore(); vi.mocked(window.getComputedStyle).mockRestore(); }); }); describe('_preventModalBodyTouch (via _addStepEventListeners)', function () { it('prevents default on window touchmove after setupForStep', () => { const modal = createShepherdModal(container); const rafSpy = vi .spyOn(window, 'requestAnimationFrame') .mockImplementation(() => 1); const targetEl = document.createElement('div'); container.appendChild(targetEl); const tour = new Tour({ useModalOverlay: true }); const step = new Step(tour, { attachTo: { element: targetEl, on: 'bottom' } }); step._resolveAttachToOptions(); step.target = targetEl; modal.setupForStep(step); // _addStepEventListeners was called, so window has a touchmove listener const touchEvent = new Event('touchmove', { bubbles: true, cancelable: true }); const preventSpy = vi.spyOn(touchEvent, 'preventDefault'); window.dispatchEvent(touchEvent); expect(preventSpy).toHaveBeenCalled(); // Clean up: hide triggers _cleanupStepEventListeners which removes the listener modal.hide(); rafSpy.mockRestore(); }); }); describe('_preventModalOverlayTouch', function () { it('stops propagation on touchmove events', () => { const modal = createShepherdModal(container); const svgEl = modal.getElement(); const touchEvent = new Event('touchmove', { bubbles: true, cancelable: true }); const stopSpy = vi.spyOn(touchEvent, 'stopPropagation'); svgEl.dispatchEvent(touchEvent); expect(stopSpy).toHaveBeenCalled(); }); }); describe('_getIframeOffset (via setupForStep)', function () { it('accumulates offset when element is inside an iframe', () => { const modal = createShepherdModal(container); const rafSpy = vi .spyOn(window, 'requestAnimationFrame') .mockImplementation(() => 1); const targetEl = document.createElement('div'); container.appendChild(targetEl); // Simulate the element being inside an iframe by mocking ownerDocument.defaultView const fakeIframe = document.createElement('iframe'); Object.defineProperty(fakeIframe, 'getBoundingClientRect', { value: () => ({ top: 10, left: 20, width: 100, height: 100, x: 20, y: 10 }) }); Object.defineProperty(fakeIframe, 'scrollTop', { value: 5 }); Object.defineProperty(fakeIframe, 'scrollLeft', { value: 3 }); const fakeChildWindow = { frameElement: fakeIframe, parent: window }; const origDescriptor = Object.getOwnPropertyDescriptor( targetEl.ownerDocument, 'defaultView' ); Object.defineProperty(targetEl.ownerDocument, 'defaultView', { value: fakeChildWindow, configurable: true }); const tour = new Tour({ useModalOverlay: true }); const step = new Step(tour, { attachTo: { element: targetEl, on: 'bottom' } }); step._resolveAttachToOptions(); step.target = targetEl; // This triggers _styleForStep -> _getIframeOffset, which should // walk up through fakeChildWindow and accumulate the iframe offset modal.setupForStep(step); // Restore defaultView before any assertions (jsdom needs it for instanceof checks) if (origDescriptor) { Object.defineProperty( targetEl.ownerDocument, 'defaultView', origDescriptor ); } else { Object.defineProperty(targetEl.ownerDocument, 'defaultView', { value: window, configurable: true }); } expect(modal.getElement()).toHaveClass('shepherd-modal-is-visible'); rafSpy.mockRestore(); }); it('handles cross-origin iframe SecurityError gracefully', () => { // Regression test for https://github.com/shipshapecode/shepherd/issues/3087 // When Shepherd is loaded in a nested cross-origin iframe, accessing // window.frameElement throws a SecurityError due to Same-Origin Policy. // This test ensures the error is caught and handled gracefully. const modal = createShepherdModal(container); const rafSpy = vi .spyOn(window, 'requestAnimationFrame') .mockImplementation(() => 1); const targetEl = document.createElement('div'); container.appendChild(targetEl); // Simulate a cross-origin iframe by making frameElement access throw SecurityError const fakeChildWindow = { get frameElement() { // Simulate browser's SecurityError when accessing cross-origin frameElement const error = new Error( 'Blocked a frame with origin "https://example.com" from accessing a cross-origin frame.' ); error.name = 'SecurityError'; throw error; }, parent: window }; const origDescriptor = Object.getOwnPropertyDescriptor( targetEl.ownerDocument, 'defaultView' ); Object.defineProperty(targetEl.ownerDocument, 'defaultView', { value: fakeChildWindow, configurable: true }); const tour = new Tour({ useModalOverlay: true }); const step = new Step(tour, { attachTo: { element: targetEl, on: 'bottom' } }); step._resolveAttachToOptions(); step.target = targetEl; // This should NOT throw an error, even though frameElement access throws SecurityError expect(() => { modal.setupForStep(step); }).not.toThrow(); // Restore defaultView before any assertions if (origDescriptor) { Object.defineProperty( targetEl.ownerDocument, 'defaultView', origDescriptor ); } else { Object.defineProperty(targetEl.ownerDocument, 'defaultView', { value: window, configurable: true }); } expect(modal.getElement()).toHaveClass('shepherd-modal-is-visible'); rafSpy.mockRestore(); }); }); }); ================================================ FILE: shepherd.js/test/unit/components/shepherd-text.spec.js ================================================ import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { createShepherdText } from '../../../src/components/shepherd-text.ts'; describe('components/ShepherdText', () => { let container; beforeEach(() => { container = document.createElement('div'); document.body.appendChild(container); }); afterEach(() => { container.remove(); }); it('adds plain text to the content', () => { const step = { options: { text: 'I am some test text.' } }; const el = createShepherdText('test-desc', step); container.appendChild(el); expect(container.querySelector('.shepherd-text')).toHaveTextContent( 'I am some test text.' ); }); it('applies HTML element directly to content', () => { const step = { options: { text: 'I am some test text.
' } }; const el = createShepherdText('test-desc', step); container.appendChild(el); expect(container.querySelector('.shepherd-text')).toContainHTML( 'I am some test text.
' ); }); it('applies the text from a function', () => { const step = { options: { text: () => 'I am some test text.' } }; const el = createShepherdText('test-desc', step); container.appendChild(el); expect(container.querySelector('.shepherd-text')).toHaveTextContent( 'I am some test text.' ); }); it('appends an HTMLElement when text is a DOM node', () => { const paragraph = document.createElement('p'); paragraph.textContent = 'I am a DOM node.'; const step = { options: { text: paragraph } }; const el = createShepherdText('test-desc', step); container.appendChild(el); const textEl = container.querySelector('.shepherd-text'); expect(textEl.contains(paragraph)).toBe(true); expect(textEl).toHaveTextContent('I am a DOM node.'); }); }); ================================================ FILE: shepherd.js/test/unit/components/shepherd-title.spec.js ================================================ import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { createShepherdTitle } from '../../../src/components/shepherd-title.ts'; describe('components/ShepherdTitle', () => { let container; beforeEach(() => { container = document.createElement('div'); document.body.appendChild(container); }); afterEach(() => { container.remove(); }); it('adds plain title to the content', () => { const el = createShepherdTitle('test-label', 'I am some test title.'); container.appendChild(el); expect(container.querySelector('.shepherd-title')).toHaveTextContent( 'I am some test title.' ); }); it('applies the title from a function', () => { const el = createShepherdTitle('test-label', () => 'I am some test title.'); container.appendChild(el); expect(container.querySelector('.shepherd-title')).toHaveTextContent( 'I am some test title.' ); }); }); ================================================ FILE: shepherd.js/test/unit/evented.spec.js ================================================ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { Evented } from '../../src/evented'; describe('Evented', () => { let testEvent, testOnTriggered; beforeEach(() => { testEvent = new Evented(); testEvent.on('testOn', () => (testOnTriggered = true)); testOnTriggered = false; }); describe('on()', () => { it('adds a new event binding', () => { expect(testEvent.bindings.testOn, 'custom event added').toBeTruthy(); }); }); describe('trigger()', () => { it('triggers a created event', () => { testEvent.trigger('testOn'); expect( testOnTriggered, 'true is returned from event trigger' ).toBeTruthy(); }); it('passes arguments to handler functions', () => { const handlerSpy = vi.fn(); testEvent.on('myEvent', handlerSpy); testEvent.trigger('myEvent', { step: { id: 'test', text: 'A step' }, previous: null }); expect(handlerSpy).toHaveBeenCalledWith({ previous: null, step: { id: 'test', text: 'A step' } }); }); }); describe('off()', () => { it('removes a generic event binding when no handler passed', () => { testEvent.off('testOn'); expect(testEvent.bindings.testOn, 'custom event removed').toBeUndefined(); }); it('removes a specific event binding when handler is passed', () => { const handler = () => {}; testEvent.on('testOn', handler); expect( testEvent.bindings.testOn.length, '2 event listeners for testOn' ).toBe(2); testEvent.off('testOn', handler); expect( testEvent.bindings.testOn.length, '1 event listener for testOn' ).toBe(1); }); it('does not remove uncreated events', () => { testEvent.off('testBlank'); expect( testEvent.bindings.testBlank, 'returns false for non created events' ).toBeFalsy(); }); }); describe('once()', () => { it('adds a new event binding that only triggers once', () => { testEvent.once('testOnce', () => true); testEvent.trigger('testOnce'); expect( testEvent.bindings.testOnce, 'custom event removed after one trigger' ).toBeTruthy(); }); }); }); ================================================ FILE: shepherd.js/test/unit/server.spec.js ================================================ /** * @jest-environment node */ import { describe, expect, it } from 'vitest'; import Shepherd from '../../src/shepherd'; describe('Server Side Render', function () { describe('Tour constructor', function () { it('does not start a tour when window is undefined', () => { const instance = new Shepherd.Tour(); expect(instance).toBeTruthy(); }); }); }); ================================================ FILE: shepherd.js/test/unit/setupTests.js ================================================ import { vi } from 'vitest'; import * as matchers from '@testing-library/jest-dom/matchers'; import { expect } from 'vitest'; expect.extend(matchers); // Set browser environment Object.defineProperty(globalThis, 'IS_BROWSER', { value: true, writable: false }); // Console errors are used for user information, do not display them during // tests. global.console = { ...console, error: vi.fn() }; global.sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); // Add custom matchers for DOM testing if (typeof window !== 'undefined') { Object.defineProperty(window, 'matchMedia', { writable: true, value: vi.fn().mockImplementation((query) => ({ matches: false, media: query, onchange: null, addListener: vi.fn(), // deprecated removeListener: vi.fn(), // deprecated addEventListener: vi.fn(), removeEventListener: vi.fn(), dispatchEvent: vi.fn() })) }); } ================================================ FILE: shepherd.js/test/unit/step.spec.js ================================================ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import Shepherd from '../../src/shepherd'; import { Step } from '../../src/step'; import { Tour } from '../../src/tour'; import ResizeObserver from 'resize-observer-polyfill'; import { offset } from '@floating-ui/dom'; // since importing non UMD, needs assignment window.Shepherd = Shepherd; window.ResizeObserver = ResizeObserver; const DEFAULT_STEP_CLASS = 'shepherd-step-tooltip'; describe('Tour | Step', () => { let tour; const showOn = () => { return true; }; const when = { show() {} }; beforeEach(() => { tour = new Tour(); }); describe('Shepherd.Step()', () => { const defaultOffsetMiddleware = offset({ mainAxis: 0, crossAxis: 32 }); const fooMiddleware = { name: 'foo', options: 'bar', fn: (args) => args }; const instance = new Shepherd.Tour({ defaultStepOptions: { classes: DEFAULT_STEP_CLASS, scrollTo: true, floatingUIOptions: { middleware: [defaultOffsetMiddleware] }, showOn, when } }); const testStep = instance.addStep({ attachTo: { element: 'body', on: 'top' }, highlightClass: 'highlight', text: 'This is a step for testing', buttons: [ { text: 'Next', action: instance.next } ], id: 'test', floatingUIOptions: { middleware: [fooMiddleware] } }); const showTestStep = instance.addStep({ buttons: [], id: 'test2', text: 'Another Step' }); // Add more steps for total _setupButtons coverage instance.addStep({ buttons: { text: 'Next', action: instance.next }, id: 'test3-id', text: 'Another Step part deux' }); const stepWithoutNameWithId = instance.addStep({ attachTo: { element: 'body' }, highlightClass: 'highlight', id: 'no-name', text: 'This is a step without a name, but with an id', buttons: [ { text: 'Next', action: instance.next } ] }); const stepWithoutNameWithoutIdOffsetMiddleware = offset({ mainAxis: 0, crossAxis: -32 }); const stepWithoutNameWithoutId = instance.addStep({ attachTo: { element: 'body' }, highlightClass: 'highlight', text: 'This is a step without a name, and without an id', buttons: [ { text: 'Next', action: instance.next } ], floatingUIOptions: { middleware: [stepWithoutNameWithoutIdOffsetMiddleware] } }); const beforeShowPromise = () => new Promise((resolve) => { setTimeout(() => { console.log('beforeShowPromise worked!'); resolve('beforeShowPromise worked!'); }, 1000); }); const beforeShowPromiseTestStep = instance.addStep({ text: 'Before Show Promise Step', id: 'test3', beforeShowPromise }); afterEach(() => { instance.complete(); }); it('has all the correct properties', () => { const values = [ 'arrow', 'classes', 'scrollTo', 'floatingUIOptions', 'showOn', 'when', 'attachTo', 'highlightClass', 'text', 'buttons', 'id' ]; expect(values).toEqual(Object.keys(testStep.options)); expect(testStep.id, 'passed name set as id').toBe('test'); expect(stepWithoutNameWithId.id, 'no name, id passed is set').toBe( 'no-name' ); expect( stepWithoutNameWithoutId.id, 'id is generated when no name or id passed' ).toMatch( /^step-[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/ ); }); it('deep clones defaultStepOptions and copies functions', () => { expect(testStep.options).toEqual({ arrow: true, attachTo: { element: 'body', on: 'top' }, buttons: [ { text: 'Next', action: instance.next } ], classes: DEFAULT_STEP_CLASS, highlightClass: 'highlight', id: 'test', scrollTo: true, text: 'This is a step for testing', floatingUIOptions: { middleware: [defaultOffsetMiddleware, fooMiddleware] }, showOn, when }); }); it('allows the step to override a previously defined modifier', () => { stepWithoutNameWithoutId.show(); const offsetMiddleware = stepWithoutNameWithoutId.options.floatingUIOptions.middleware.filter( ({ name }) => name === 'offset' ); const offsetResult = offsetMiddleware.reduce( (agg, current) => { agg.mainAxis += current.options.mainAxis; agg.crossAxis += current.options.crossAxis; return agg; }, { mainAxis: 0, crossAxis: 0 } ); expect(offsetResult).toEqual({ mainAxis: 0, crossAxis: 0 }); }); describe('.hide()', () => { it('detaches from the step target', () => { instance.start(); const targetElem = document.body; expect(targetElem.classList.contains('shepherd-enabled')).toBe(true); testStep.hide(); expect(targetElem.classList.contains('shepherd-enabled')).toBe(false); }); }); describe('.show()', () => { it('beforeShowPromise called before `show`', () => { console.log = vi.fn(); const promise = beforeShowPromiseTestStep.show(); return promise.then(() => { expect(console.log).toHaveBeenCalledWith('beforeShowPromise worked!'); }); }); it('shows step evoking method, regardless of order', () => { showTestStep.show(); expect( document.querySelector('[data-shepherd-step-id=test2]') ).toBeInTheDocument(); }); }); }); describe('cancel()', () => { it('triggers the cancel event and tour method', () => { let cancelCalled = false; let eventTriggered = false; const step = new Step( { cancel() { cancelCalled = true; } }, {} ); step.on('cancel', () => (eventTriggered = true)); step.cancel(); expect(cancelCalled, 'cancel method from tour called').toBeTruthy(); expect(eventTriggered, 'cancel event was triggered').toBeTruthy(); }); }); describe('complete()', () => { it('triggers the complete event and tour method', () => { let completeCalled = false; let eventTriggered = false; const step = new Step( { complete() { completeCalled = true; } }, {} ); step.on('complete', () => (eventTriggered = true)); step.complete(); expect(completeCalled, 'complete method from tour called').toBeTruthy(); expect(eventTriggered, 'complete event was triggered').toBeTruthy(); }); }); describe('destroy()', () => { it('triggers the destroy event', () => { const step = new Step(tour, {}); let eventTriggered = false; step.on('destroy', () => (eventTriggered = true)); step.destroy(); expect(eventTriggered, 'destroy event was triggered').toBeTruthy(); }); }); describe('hide()', () => { let beforeHideTriggered = false; let modalHideCalled = false; const step = new Step( { modal: { hide() { modalHideCalled = true; } } }, {} ); it('triggers the before-hide event', () => { step.on('before-hide', () => (beforeHideTriggered = true)); step.hide(); expect( beforeHideTriggered, 'before-hide event was triggered' ).toBeTruthy(); }); it('calls tour.modal.hide', () => { expect(modalHideCalled, 'tour.modal.hide called').toBeTruthy(); }); }); describe('updateStepOptions', () => { let step; beforeEach(() => { step = new Step(tour, { id: 'test-id', attachTo: { element: 'body', on: 'top' }, text: 'Lorem Ipsum', classes: 'classes-test', title: 'Test', scrollTo: false, buttons: [ { text: 'button one', disabled: false, classes: 'button1' }, { text: 'button two', disabled: true, classes: 'button2' } ] }); step.show(); }); afterEach(() => { step.destroy(); }); it('should update passed in properties', async () => { step.updateStepOptions({ text: 'updated', title: 'New title' }); expect(step.options.text).toBe('updated'); expect(step.options.title).toBe('New title'); await window.requestAnimationFrame( () => new Promise((resolve) => { return resolve(); }) ); expect(document.querySelector('.shepherd-text').textContent).toBe( 'updated' ); expect(document.querySelector('.shepherd-title').textContent).toBe( 'New title' ); }); it('should not affect other properties', async () => { step.updateStepOptions({ text: 'updated', title: 'New title' }); expect(step.options.id).toEqual('test-id'); expect(step.options.buttons).toEqual([ { text: 'button one', disabled: false, classes: 'button1' }, { text: 'button two', disabled: true, classes: 'button2' } ]); await window.requestAnimationFrame( () => new Promise((resolve) => { return resolve(); }) ); expect(document.querySelector('.button1').textContent).toBe('button one'); expect(document.querySelector('.button2').textContent).toBe('button two'); }); it('should update buttons', async () => { const buttons = [ { text: 'button one updated', disabled: true, classes: 'button1' }, { text: 'button two updated', disabled: false, classes: 'button2' } ]; step.updateStepOptions({ buttons }); expect(step.options.buttons).toEqual(buttons); await window.requestAnimationFrame( () => new Promise((resolve) => { return resolve(); }) ); const buttonOne = document.querySelector('.button1'); expect(buttonOne.textContent).toBe('button one updated'); expect(buttonOne.disabled).toBe(true); const buttonTwo = document.querySelector('.button2'); expect(buttonTwo.textContent).toBe('button two updated'); expect(buttonTwo.disabled).toBe(false); }); it('removing title should remove class', async () => { step.updateStepOptions({ title: '' }); expect(step.options.title).toEqual(''); await window.requestAnimationFrame( () => new Promise((resolve) => { return resolve(); }) ); const element = document.querySelector('.shepherd-element'); expect(element.classList.contains('shepherd-has-title')).toBeFalsy(); }); it('updating classes should update element classes', async () => { step.updateStepOptions({ classes: 'test-1 test-2' }); expect(step.options.classes).toEqual('test-1 test-2'); await window.requestAnimationFrame( () => new Promise((resolve) => { return resolve(); }) ); const element = document.querySelector('.shepherd-element'); expect(element.classList.contains('test-1')).toBeTruthy(); expect(element.classList.contains('test-2')).toBeTruthy(); expect(element.classList.contains('classes-test')).toBeFalsy(); }); }); describe('_setupElements()', () => { it('calls destroy on the step if the content element is already set', () => { const step = new Step(tour, {}); let destroyCalled = false; step.el = document.createElement('a'); step.destroy = () => (destroyCalled = true); step._setupElements(); expect( destroyCalled, '_setupElements method called destroy with element set' ).toBeTruthy(); }); it('calls destroy on the tooltip if it already exists', () => { const step = new Step(tour, {}); let destroyCalled = false; step.cleanup = () => { destroyCalled = true; }; step._setupElements(); expect( destroyCalled, '_setupElements method called destroy on the existing tooltip' ).toBe(true); }); }); describe('_scrollTo()', () => { it('calls the scroll native method', () => { const div = document.createElement('div'); let handlerCalled = false; div.classList.add('scroll-test'); document.body.appendChild(div); const step = new Step('test', { attachTo: { element: '.scroll-test', on: 'center' } }); div.scrollIntoView = () => (handlerCalled = true); step._scrollTo(); expect(handlerCalled).toBeTruthy(); }); it('calls the custom handler', () => { let handlerAdded = false; const step = new Step('test', { scrollToHandler: () => (handlerAdded = true) }); step._scrollTo(); expect(handlerAdded).toBeTruthy(); }); it('calls scroll native method after before-show promise resolution', () => { const resTester = document.createElement('div'); let resHandlerCalled = false; resTester.classList.add('post-res-scroll-test'); resTester.scrollIntoView = () => (resHandlerCalled = true); const resSpy = vi.spyOn(resTester, 'scrollIntoView'); document.body.appendChild(resTester); const beforeShowPromise = new Promise((resolve) => { return setTimeout(() => resolve('beforeShowPromise worked!'), 1000); }); const step = new Step('test', { attachTo: { element: '.post-res-scroll-test', on: 'center' }, beforeShowPromise: () => { return beforeShowPromise; } }); step.show(); step._scrollTo(); expect(resHandlerCalled).toBeTruthy(); expect(resSpy).toHaveBeenCalled(); }); it('calls the custom handler after before-show promise resolution', () => { let resHandlerAdded = false; const beforeShowPromise = new Promise((resolve) => { return setTimeout(() => resolve('beforeShowPromise worked!'), 1000); }); const step = new Step('test', { scrollToHandler: () => (resHandlerAdded = true), beforeShowPromise: () => { return beforeShowPromise; } }); const resSpy = vi.spyOn(step.options, 'scrollToHandler'); step.show(); step._scrollTo(); expect(resHandlerAdded).toBeTruthy(); expect(resSpy).toHaveBeenCalled(); }); }); describe('setOptions()', () => { it('calls event handlers passed in as properties to the `when` option', () => { let whenCalled = false; const step = new Step('test', { when: { destroy: () => (whenCalled = true) } }); step.destroy(); expect(whenCalled).toBeTruthy(); }); }); describe('getTour()', () => { it('returns the tour value', () => { const step = new Step(new Shepherd.Tour(), {}); expect(step.getTour() instanceof Shepherd.Tour).toBeTruthy(); }); }); describe('_createTooltipContent', () => { it('ARIA attributes set', () => { const step = new Step(tour, { id: 'test-step', text: 'Lorem Ipsum', title: 'Test' }); const element = step._createTooltipContent(); expect(element.getAttribute('aria-labelledby')).toBe('test-step-label'); expect(element.querySelector('.shepherd-title').id).toBe( 'test-step-label' ); expect(element.getAttribute('aria-describedby')).toBe( 'test-step-description' ); expect(element.querySelector('.shepherd-text').id).toBe( 'test-step-description' ); }); }); describe('correct operation of classes on body element when step not attached to an element', () => { const offsetMiddleware = offset({ crossAxis: 32 }); const defaultCallback = (args) => args; const instance = new Shepherd.Tour({ defaultStepOptions: { classes: DEFAULT_STEP_CLASS, scrollTo: true, floatingUIOptions: { middleware: [offsetMiddleware] }, showOn, when } }); const stepWithoutAttachment = instance.addStep({ highlightClass: 'highlight', text: 'This is a step for testing', buttons: [ { text: 'Next', action: instance.next } ], id: 'test', floatingUIOptions: { middleware: [{ name: 'foo', options: 'bar', fn: defaultCallback }] } }); afterEach(() => { instance.complete(); }); describe('.hide()', () => { it('detaches from the step target', () => { instance.start(); const targetElem = document.body; expect(targetElem.classList.contains('shepherd-enabled')).toBe(true); stepWithoutAttachment.hide(); expect(targetElem.classList.contains('shepherd-enabled')).toBe(false); }); }); describe('.destroy()', () => { it('detaches from the step target', () => { instance.start(); const targetElem = document.body; expect(targetElem.classList.contains('shepherd-enabled')).toBe(true); stepWithoutAttachment.destroy(); expect(targetElem.classList.contains('shepherd-enabled')).toBe(false); }); }); }); describe('lazy attachTo evaluation', () => { // We test this using attachTo.element callback. // Note that lazy evaluation largely relies on `parseAttachTo`, however this does // not it's implementation, only if the callback is called lazily. it('lazily evaluates attachTo.element callback', () => { const step1AttachToCallback = vi.fn(); const step2AttachToCallback = vi.fn(); const instance = new Shepherd.Tour({ steps: [ { text: 'step 1', attachTo: { element: step1AttachToCallback, on: 'auto' } }, { text: 'step 2', attachTo: { element: step2AttachToCallback, on: 'auto' } } ] }); instance.start(); expect(step1AttachToCallback).toHaveBeenCalled(); expect(step2AttachToCallback).not.toHaveBeenCalled(); instance.next(); expect(step2AttachToCallback).toHaveBeenCalled(); expect(step1AttachToCallback).toHaveBeenCalledTimes(1); expect(step2AttachToCallback).toHaveBeenCalledTimes(1); }); it('lazily evaluates attachTo.element selector', () => { const querySelectorSpy = vi.spyOn(document, 'querySelector'); const instance = new Shepherd.Tour({ steps: [ { text: 'step 1', attachTo: { element: '#step-1-attach-to-element', on: 'auto' } }, { text: 'step 2', attachTo: { element: '#step-2-attach-to-element', on: 'auto' } } ] }); instance.start(); expect(querySelectorSpy).toHaveBeenCalledWith( '#step-1-attach-to-element' ); expect(querySelectorSpy).not.toHaveBeenCalledWith( '#step-2-attach-to-element' ); instance.next(); expect(querySelectorSpy).toHaveBeenCalledWith( '#step-2-attach-to-element' ); }); it('evaluates attachTo on subsequent shows', () => { const step1AttachToCallback = vi.fn(); const step2AttachToCallback = vi.fn(); const instance = new Shepherd.Tour({ steps: [ { text: 'step 1', attachTo: { element: step1AttachToCallback, on: 'auto' } }, { text: 'step 2', attachTo: { element: step2AttachToCallback, on: 'auto' } } ] }); instance.start(); expect(step1AttachToCallback).toHaveBeenCalledTimes(1); instance.next(); instance.back(); expect(step1AttachToCallback).toHaveBeenCalledTimes(2); }); it('evaluates attachTo only once', () => { const instance = new Shepherd.Tour({ steps: [ { text: 'step 1', attachTo: { element: () => {}, on: 'auto' }, id: 'step1' }, { text: 'step 2', attachTo: { element: () => {}, on: 'auto' } } ] }); instance.start(); expect(instance.getCurrentStep().isOpen()).toBe(true); // Subsequent calls to the getter return the same object const result1 = instance.getCurrentStep()._getResolvedAttachToOptions(); expect(result1).not.toBeNull(); instance.next(); const result2 = instance.getById('step1')._getResolvedAttachToOptions(); expect(result1).toEqual(result2); }); it('can evaluate _getResolvedAttachToOptions before step before-show phase', () => { const instance = new Shepherd.Tour({ steps: [ { text: 'step 1', attachTo: { element: () => {}, on: 'auto' } }, { text: 'step 2', attachTo: { element: () => {}, on: 'auto' }, id: 'step2' } ] }); instance.start(); expect(instance.getById('step2')._getResolvedAttachToOptions()).toEqual({ element: undefined, on: 'auto' }); }); }); describe('tabIndex preservation', () => { let instance; let testElement; beforeEach(() => { // Create a test element testElement = document.createElement('div'); testElement.id = 'tabindex-test-element'; document.body.appendChild(testElement); instance = new Shepherd.Tour({ steps: [ { id: 'test-step', text: 'Test step', attachTo: { element: '#tabindex-test-element', on: 'top' } } ] }); }); afterEach(() => { instance.complete(); testElement?.remove(); }); it('stores and restores original tabIndex when element has no tabindex attribute', () => { // Initially, the element should have no tabindex attribute expect(testElement.hasAttribute('tabindex')).toBe(false); // Start the tour instance.start(); // During the tour, tabIndex should be set to 0 expect(testElement.tabIndex).toBe(0); expect(testElement.getAttribute('tabindex')).toBe('0'); // Hide the step instance.getCurrentStep().hide(); // After hiding, the tabindex attribute should be removed expect(testElement.hasAttribute('tabindex')).toBe(false); }); it('stores and restores original tabIndex when element has tabindex="-1"', () => { // Set tabindex to -1 initially testElement.setAttribute('tabindex', '-1'); expect(testElement.getAttribute('tabindex')).toBe('-1'); // Start the tour instance.start(); // During the tour, tabIndex should be set to 0 expect(testElement.tabIndex).toBe(0); expect(testElement.getAttribute('tabindex')).toBe('0'); // Hide the step instance.getCurrentStep().hide(); // After hiding, tabIndex should be restored to -1 expect(testElement.getAttribute('tabindex')).toBe('-1'); }); it('stores and restores original tabIndex when element has tabindex="5"', () => { // Set tabindex to 5 initially testElement.setAttribute('tabindex', '5'); expect(testElement.getAttribute('tabindex')).toBe('5'); // Start the tour instance.start(); // During the tour, tabIndex should be set to 0 expect(testElement.tabIndex).toBe(0); expect(testElement.getAttribute('tabindex')).toBe('0'); // Hide the step instance.getCurrentStep().hide(); // After hiding, tabIndex should be restored to 5 expect(testElement.getAttribute('tabindex')).toBe('5'); }); it('restores tabIndex when step is destroyed', () => { // Set tabindex to -1 initially testElement.setAttribute('tabindex', '-1'); // Start the tour instance.start(); // During the tour, tabIndex should be set to 0 expect(testElement.getAttribute('tabindex')).toBe('0'); // Destroy the step instance.getCurrentStep().destroy(); // After destroying, tabIndex should be restored to -1 expect(testElement.getAttribute('tabindex')).toBe('-1'); }); it('handles multiple show/hide cycles correctly', () => { // Set tabindex to 2 initially testElement.setAttribute('tabindex', '2'); // Start the tour (first show) instance.start(); expect(testElement.getAttribute('tabindex')).toBe('0'); // Hide the step instance.getCurrentStep().hide(); expect(testElement.getAttribute('tabindex')).toBe('2'); // Show again instance.getCurrentStep().show(); expect(testElement.getAttribute('tabindex')).toBe('0'); // Hide again instance.getCurrentStep().hide(); expect(testElement.getAttribute('tabindex')).toBe('2'); }); it('only stores the original value once, not intermediate values', () => { // Set tabindex to 3 initially testElement.setAttribute('tabindex', '3'); // Start the tour instance.start(); expect(testElement.getAttribute('tabindex')).toBe('0'); // Manually change tabIndex (simulating some other code changing it) testElement.setAttribute('tabindex', '7'); // Hide the step - should restore to original value (3), not intermediate (7) instance.getCurrentStep().hide(); expect(testElement.getAttribute('tabindex')).toBe('3'); }); }); }); ================================================ FILE: shepherd.js/test/unit/tour.spec.js ================================================ import _ from 'lodash'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import Shepherd from '../../src/shepherd'; import ResizeObserver from 'resize-observer-polyfill'; import { setupTooltip } from '../../src/utils/floating-ui'; import { offset } from '@floating-ui/dom'; const { Step } = Shepherd; // since importing non UMD, needs assignment window.Shepherd = Shepherd; window.ResizeObserver = ResizeObserver; const DEFAULT_STEP_CLASS = 'shepherd-step-tooltip'; describe('Tour | Top-Level Class', function () { let instance, shouldShowStep; const showOn = () => { return true; }; const when = { show() {} }; const offsetMiddleware = offset({ crossAxis: 32 }); const defaultStepOptions = { classes: DEFAULT_STEP_CLASS, scrollTo: true, floatingUIOptions: { middleware: [offsetMiddleware] }, showOn, when }; afterEach(() => { instance.complete(); }); describe('constructor', function () { it('creates a new tour instance', function () { instance = new Shepherd.Tour({ defaultStepOptions }); expect(instance instanceof Shepherd.Tour).toBe(true); }); it('returns the default options on the instance', function () { instance = new Shepherd.Tour({ defaultStepOptions, steps: [ { scrollTo: false } ] }); expect(instance.options.defaultStepOptions).toEqual({ classes: DEFAULT_STEP_CLASS, scrollTo: true, floatingUIOptions: { middleware: [offsetMiddleware] }, showOn, when }); }); it('sets the correct bindings', function () { instance = new Shepherd.Tour({ defaultStepOptions }); const bindings = Object.keys(instance.bindings); const tourEvents = [ 'complete', 'cancel', 'start', 'show', 'active', 'inactive' ]; // Check that all bindings are included const difference = _.difference(tourEvents, bindings); expect(difference.length, 'all tour events bound').toBe(0); }); it('generates a unique `id` property, optionally based upon the `tourName` option', function () { const instance1 = new Shepherd.Tour(); const instance2 = new Shepherd.Tour({ tourName: 'select-avatar' }); expect(instance1.id.startsWith('tour--')).toBe(true); expect(instance2.id.startsWith('select-avatar--')).toBe(true); const [uniqueId1] = instance1.id.split('--')[1]; const uniqueId2 = instance2.id.split('--')[1]; expect(uniqueId1).not.toBe(uniqueId2); }); }); describe('methods', () => { beforeEach(() => { shouldShowStep = false; instance = new Shepherd.Tour({ defaultStepOptions }); instance.addStep({ id: 'test', title: 'This is a test step for our tour' }); instance.addStep({ id: 'test2', title: 'Another Step' }); instance.addStep({ classes: 'skipped', id: 'skipped-step', title: 'This step should be skipped', showOn() { return shouldShowStep; } }); instance.addStep({ id: 'test3', title: 'Yet, another test step' }); }); describe('.addStep()', function () { it('adds tour steps', function () { expect(instance.steps.length).toBe(4); expect( instance.getById('test').options.classes, 'classes passed to step options' ).toBe(DEFAULT_STEP_CLASS); }); it('adds tour steps at specified index', function () { expect(instance.steps[1].options.id, 'original step at index 1').toBe( 'test2' ); instance.addStep( { id: 'index-test', title: 'Test index insertion' }, 1 ); expect(instance.steps.length).toBe(5); expect(instance.steps[1].options.id, 'step inserted at index 1').toBe( 'index-test' ); }); it('adds steps with only one arg', function () { const step = instance.addStep({ id: 'one-arg' }); expect(instance.steps.length).toBe(5); expect(step.id, 'id applied to step with just one arg').toBe('one-arg'); }); it('adds steps that are already Step instances', function () { const step = instance.addStep( new Step(instance, { id: 'already-a-step' }) ); expect(instance.steps.length).toBe(5); expect(step.id, 'id applied to step instance').toBe('already-a-step'); expect(step.tour, 'tour is set to `this`').toBe(instance); }); }); describe('.getById()', function () { it('returns the step by ID with the right title', function () { expect(instance.steps.length).toBe(4); expect(instance.getById('test3').options.title).toBe( 'Yet, another test step' ); }); }); describe('.start()', function () { it('starts a tour that is the current active', function () { instance.start(); expect(instance).toBe(Shepherd.activeTour); }); }); describe('.getCurrentStep()', function () { it('returns the currently shown step', function () { instance.start(); expect(instance.getCurrentStep().id).toBe('test'); }); }); describe('.hide()', function () { it('hides the current step', () => { const [firstStep] = instance.steps; const hideStepSpy = vi.spyOn(firstStep, 'hide'); expect(firstStep.isOpen()).toBe(false); instance.start(); expect(firstStep.isOpen()).toBe(true); instance.hide(); expect(firstStep.isOpen()).toBe(false); expect(hideStepSpy).toHaveBeenCalledTimes(1); }); }); describe('isActive', function () { it('computes whether or not `Shepherd.activeTour` equals the instance', function () { Shepherd.activeTour = ''; expect(instance.isActive()).toBe(false); Shepherd.activeTour = instance; expect(instance.isActive()).toBe(true); Shepherd.activeTour = ''; expect(instance.isActive()).toBe(false); }); }); describe('.next()/.back()', function () { it('goes to the next/previous steps', function () { instance.start(); instance.next(); expect(instance.getCurrentStep().id).toBe('test2'); instance.back(); expect(instance.getCurrentStep().id).toBe('test'); }); it('next completes tour when on last step', function () { let completeFired = false; instance.on('complete', () => { completeFired = true; }); instance.start(); instance.show('test3'); expect(instance.getCurrentStep().id).toBe('test3'); instance.next(); expect( completeFired, 'complete is called when next is clicked on last step' ).toBeTruthy(); }); }); describe('.cancel()', function () { it('shows confirm dialog when confirmCancel is true', function () { instance = new Shepherd.Tour({ defaultStepOptions, confirmCancel: true, confirmCancelMessage: 'Confirm cancel?' }); instance.addStep({ id: 'test', title: 'This is a test step for our tour' }); let inactiveFired = false; instance.on('inactive', () => { inactiveFired = true; }); window.confirm = vi.fn(); const windowConfirmStub = vi .spyOn(window, 'confirm') .mockImplementation(() => false); instance.start(); expect(instance, 'activeTour is set to our tour').toBe( Shepherd.activeTour ); instance.cancel(); expect(windowConfirmStub).toHaveBeenCalled(); expect( inactiveFired, 'tour still going, since confirm returned false' ).toBeFalsy(); windowConfirmStub.mockImplementation(() => true); instance.cancel(); expect(windowConfirmStub).toHaveBeenCalled(); expect( inactiveFired, 'tour inactive, since confirm returned true' ).toBeTruthy(); }); it('tears down tour on cancel', function () { let inactiveFired = false; instance.on('inactive', () => { inactiveFired = true; }); instance.start(); expect(instance, 'activeTour is set to our tour').toBe( Shepherd.activeTour ); instance.cancel(); expect(Shepherd.activeTour, 'activeTour is torn down').toBeFalsy(); expect(inactiveFired, 'inactive event fired').toBeTruthy(); }); it('triggers cancel event when cancel function is called', function () { let cancelFired = false; instance.on('cancel', () => { cancelFired = true; }); instance.start(); instance.cancel(); expect(cancelFired, 'cancel event fired').toBeTruthy(); }); [ { status: 'normal', title: 'whether cancel depends on function return value' }, { status: 'async', title: 'whether cancel will wait for the value returned from the function' } ].map(({ status, title }) => { it('when confirmCancel is function, ' + title, async function () { const myMock = vi.fn(); if (status === 'async') { myMock .mockImplementationOnce(() => Promise.resolve(false)) .mockImplementationOnce(() => Promise.resolve(true)); } else { myMock.mockReturnValueOnce(false).mockReturnValueOnce(true); } instance = new Shepherd.Tour({ defaultStepOptions, confirmCancel: myMock, confirmCancelMessage: 'Confirm cancel?' }); instance.addStep({ id: 'test', title: 'This is a test step for our tour' }); let inactiveFired = false; instance.on('cancel', () => { inactiveFired = true; }); instance.start(); expect(instance, 'activeTour is set to our tour').toBe( Shepherd.activeTour ); await instance.cancel(); expect( myMock.mock.calls.length, 'confirmCancel callback called once' ).toBe(1); expect( await myMock.mock.results[0].value, 'confirmCancel callback returns false' ).toBe(false); expect( inactiveFired, 'tour still going, since confirmCancel callback returned false' ).toBeFalsy(); await instance.cancel(); expect( myMock.mock.calls.length, 'confirmCancel callback called twice' ).toBe(2); expect( await myMock.mock.results[1].value, 'confirmCancel callback returns true' ).toBe(true); expect( inactiveFired, 'tour inactive, since confirm returned true' ).toBeTruthy(); }); }); }); describe('.complete()', function () { it('triggers complete event when complete function is called', function () { let completeFired = false; instance.on('complete', () => { completeFired = true; }); instance.start(); instance.complete(); expect(completeFired, 'complete event fired').toBeTruthy(); }); it('calls `done()`', () => { const doneSpy = vi.spyOn(instance, '_done'); expect(doneSpy).toHaveBeenCalledTimes(0); instance.start(); instance.complete(); expect(doneSpy).toHaveBeenCalledTimes(1); }); }); describe('._done()', function () { it('tears down the active tour', function () { instance.start(); expect(instance, 'activeTour is set to our tour').toBe( Shepherd.activeTour ); instance.complete(); expect( Shepherd.activeTour, '`activeTour` is torn down and removed from the `Shepherd` global' ).toBe(null); }); it('removes any of its `Step` tooltip elements from the DOM', function () { const testStep = { id: 'element-removal-test', classes: 'element-removal-test', title: 'This is a test step for our tour' }; instance.addStep(testStep); instance.start(); instance.show('element-removal-test'); expect( document.querySelector('.element-removal-test'), 'a step is rendered in the DOM after the tour starts' ).toBeInTheDocument(); instance.complete(); expect( document.querySelector('.element-removal-test'), 'steps are removed from the DOM after the tour completes' ).not.toBeInTheDocument(); }); it('fires the `inactive` event', function () { let inactiveFired = false; instance.on('inactive', () => { inactiveFired = true; }); instance.start(); expect( inactiveFired, 'inactive event does not fire before `complete()`' ).toBe(false); instance.complete(); expect(inactiveFired, 'inactive event fires after `complete()`').toBe( true ); }); }); describe('.removeStep()', function () { it('removes the step when passed the id', function () { instance.start(); expect(instance.steps.length).toBe(4); instance.removeStep('test2'); expect(instance.steps.length).toBe(3); }); it('hides the step before removing', function () { let hideFired = false; instance.start(); expect(instance.steps.length).toBe(4); const step = instance.getById('test'); step.on('hide', () => { hideFired = true; }); instance.removeStep('test'); expect(instance.steps.length).toBe(3); expect( hideFired, 'hide is fired before step is destroyed' ).toBeTruthy(); }); }); describe('.show()', function () { it('show short-circuits if next is not found', function () { let showFired = false; instance.start(); instance.on('show', () => { showFired = true; }); instance.show('not-a-real-key'); expect( showFired, 'showFired is false because show short circuits' ).toBeFalsy(); }); it('showOn determines which steps to skip', function () { instance.start(); expect(instance.getCurrentStep().id).toBe('test'); instance.next(); expect(instance.getCurrentStep().id).toBe('test2'); instance.next(); expect(instance.getCurrentStep().id).toBe('test3'); expect( instance.getCurrentStep().id, 'step skipped because `showOn` returns false' ).not.toBe('skipped-step'); instance.back(); shouldShowStep = true; instance.next(); expect( instance.getCurrentStep().id, 'step shown because `showOn` returns true' ).toBe('skipped-step'); }); it('fires cancel when going back past first step with showOn returning false', function () { let cancelFired = false; const tourInstance = new Shepherd.Tour(); tourInstance.addStep({ id: 'first-hidden', title: 'This step is always hidden', showOn() { return false; } }); tourInstance.addStep({ id: 'second', title: 'Second step' }); tourInstance.on('cancel', () => { cancelFired = true; }); tourInstance.start(); expect( tourInstance.getCurrentStep().id, 'first hidden step is skipped on start' ).toBe('second'); tourInstance.back(); expect( cancelFired, 'cancel is fired when going back past all shown steps' ).toBeTruthy(); expect( Shepherd.activeTour, 'activeTour is null after cancel' ).toBeNull(); }); it("sets the instance on `Shepherd.activeTour` if it's not already set", function () { const setupFuncSpy = vi.spyOn(instance, '_setupActiveTour'); Shepherd.activeTour = null; expect(setupFuncSpy).toHaveBeenCalledTimes(0); instance.start(); expect(setupFuncSpy).toHaveBeenCalledTimes(1); expect(Shepherd.activeTour).toBe(instance); }); }); }); describe('floatingUIOptions', () => { it('applies the default modifiers from defaultStepOptions', function () { instance = new Shepherd.Tour({ defaultStepOptions }); const step = instance.addStep({ id: 'test', title: 'This is a test step for our tour' }); instance.start(); const floatingUIOptions = setupTooltip(step); expect(floatingUIOptions.middleware.length).toBe(1); }); it('adds a step modifer to default modifiers', function () { instance = new Shepherd.Tour({ defaultStepOptions }); const step = instance.addStep({ id: 'test', title: 'This is a test step for our tour', floatingUIOptions: { middleware: [{ name: 'foo', options: 'bar', fn: (args) => args }] } }); instance.start(); const floatingUIOptions = setupTooltip(step); expect(floatingUIOptions.middleware.length).toBe(2); }); it('correctly changes modifiers when going from centered to attached', function () { const div = document.createElement('div'); div.classList.add('modifiers-test'); document.body.appendChild(div); instance = new Shepherd.Tour({ defaultStepOptions }); const centeredStep = instance.addStep({ id: 'centered', title: 'This is a centered step for our tour', floatingUIOptions: { middleware: [{ name: 'foo', options: 'bar', fn: (args) => args }] } }); const attachedStep = instance.addStep({ attachTo: { element: '.modifiers-test', on: 'top' }, id: 'attached', title: 'This is an attached step for our tour', floatingUIOptions: { middleware: [{ name: 'foo', options: 'bar', fn: (args) => args }] } }); instance.start(); const centeredOptions = setupTooltip(centeredStep); const centeredMiddlewareNames = centeredOptions.middleware.map( ({ name }) => name ); expect(centeredOptions.middleware.length).toBe(2); expect(centeredMiddlewareNames.includes('offset')).toBe(true); expect(centeredMiddlewareNames.includes('foo')).toBe(true); expect(centeredMiddlewareNames.includes('arrow')).toBe(false); instance.next(); const options = setupTooltip(attachedStep); const middlewareNames = options.middleware.map(({ name }) => name); expect(options.middleware.length).toBe(5); expect(middlewareNames.includes('offset')).toBe(true); expect(middlewareNames.includes('foo')).toBe(true); expect(middlewareNames.includes('shift')).toBe(true); expect(middlewareNames.includes('arrow')).toBe(true); document.body.removeChild(div); }); it('renders step in stepsContainer', () => { const stepsContainer = document.createElement('div'); stepsContainer.setAttribute('id', 'customStepTarget'); document.body.appendChild(stepsContainer); expect(stepsContainer).toBeInTheDocument(); instance = new Shepherd.Tour({ stepsContainer, defaultStepOptions }); const step = instance.addStep({ title: 'This is a test step for our tour' }); instance.start(); const stepElement = step.getElement(); expect(stepsContainer.contains(stepElement)).toBe(true); }); it('adds autoPlacement middleware when attachTo.on is set to auto', () => { const div = document.createElement('div'); div.classList.add('modifiers-test'); document.body.appendChild(div); instance = new Shepherd.Tour(); const step1 = instance.addStep({ id: 'test', title: 'This is a test step for our tour', attachTo: { element: '.modifiers-test', on: 'auto' } }); const step2 = instance.addStep({ id: 'test', title: 'This is a test step for our tour', attachTo: { element: '.modifiers-test', on: 'auto-start' } }); const step3 = instance.addStep({ id: 'test', title: 'This is a test step for our tour', attachTo: { element: '.modifiers-test', on: 'auto-end' } }); instance.start(); const step1FloatingUIOptions = setupTooltip(step1); const step1MiddlewareNames = step1FloatingUIOptions.middleware.map( ({ name }) => name ); const step1PlacementMiddleware = step1FloatingUIOptions.middleware.find( ({ name }) => name === 'autoPlacement' ); expect(step1MiddlewareNames.includes('autoPlacement')).toBe(true); expect(step1MiddlewareNames.includes('flip')).toBe(false); expect(step1PlacementMiddleware.options.alignment).toBe(null); instance.next(); const step2FloatingUIOptions = setupTooltip(step2); const step2PlacementMiddleware = step2FloatingUIOptions.middleware.find( ({ name }) => name === 'autoPlacement' ); expect(step2PlacementMiddleware.options.alignment).toBe('start'); instance.next(); const step3FloatingUIOptions = setupTooltip(step3); const step3PlacementMiddleware = step3FloatingUIOptions.middleware.find( ({ name }) => name === 'autoPlacement' ); expect(step3PlacementMiddleware.options.alignment).toBe('end'); }); }); describe('shepherdModalOverlayContainer', function () { beforeEach(() => { instance = new Shepherd.Tour({ useModalOverlay: true }); }); it('appends shepherdModalOverlayContainer to DOM when it does not exist', async () => { expect( document.querySelector('.shepherd-modal-overlay-container') ).not.toBeInTheDocument(); instance.start(); setTimeout(() => { expect( document.querySelector('.shepherd-modal-overlay-container') ).toBeInTheDocument(); }, 200); }); it('removes shepherdModalOverlayContainer from DOM when it is complete', () => { instance.start(); setTimeout(() => { expect( document.querySelector('.shepherd-modal-overlay-container') ).toBeInTheDocument(); }, 200); instance.complete(); expect( document.querySelector('.shepherd-modal-overlay-container') ).not.toBeInTheDocument(); }); it('renders modal in modalContainer', () => { const modalContainer = document.createElement('div'); modalContainer.setAttribute('id', 'customModalTarget'); document.body.appendChild(modalContainer); expect(modalContainer).toBeInTheDocument(); instance = new Shepherd.Tour({ modalContainer, useModalOverlay: true }); instance.addStep({ title: 'This is a test step for our tour' }); instance.start(); const modalElement = instance.modal.getElement(); expect(modalContainer.contains(modalElement)).toBe(true); }); }); }); ================================================ FILE: shepherd.js/test/unit/utils/bind.spec.js ================================================ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { bindAdvance } from '../../../src/utils/bind'; import { Step } from '../../../src/step'; describe('Bind Utils', function () { describe('bindAdvance()', () => { let event, link; let hasAdvanced = false; const advanceOnSelector = 'test-selector'; const advanceOnEventName = 'test-event'; const tourProto = { next() { hasAdvanced = true; } }; beforeEach(() => { event = new Event(advanceOnEventName); link = document.createElement('a'); link.classList.add(advanceOnSelector); link.textContent = 'Click Me 👋'; document.body.appendChild(link); }); afterEach(() => { link.remove(); }); it('triggers the `advanceOn` option via object', () => { const step = new Step(tourProto, { advanceOn: { selector: `.${advanceOnSelector}`, event: advanceOnEventName } }); step.isOpen = () => true; bindAdvance(step); link.dispatchEvent(event); expect(link.classList.contains(advanceOnSelector)).toBe(true); expect(hasAdvanced, '`next()` triggered for advanceOn').toBe(true); }); it('captures events attached to no element', () => { const step = new Step(tourProto, { advanceOn: { event: advanceOnEventName } }); step.isOpen = () => true; bindAdvance(step); document.body.dispatchEvent(event); expect(hasAdvanced, '`next()` triggered for advanceOn').toBeTruthy(); }); it('should support bubbling events for nodes that do not exist yet', () => { const event = new Event('blur'); const step = new Step(tourProto, { text: 'Lorem ipsum dolor: sit amet', advanceOn: { selector: 'a[href="https://example.com"]', event: 'blur' } }); step.isOpen = () => true; bindAdvance(step); document.body.dispatchEvent(event); expect(hasAdvanced, '`next()` triggered for advanceOn').toBeTruthy(); }); it('calls `removeEventListener` when destroyed', () => { return new Promise((done) => { const bodySpy = vi.spyOn(document.body, 'removeEventListener'); const step = new Step(tourProto, { advanceOn: { event: advanceOnEventName } }); step.isOpen = () => true; bindAdvance(step); step.trigger('destroy'); expect(bodySpy).toHaveBeenCalled(); bodySpy.mockRestore(); done(); }); }); }); }); ================================================ FILE: shepherd.js/test/unit/utils/cleanup.spec.js ================================================ import { describe, expect, it } from 'vitest'; import { cleanupSteps } from '../../../src/utils/cleanup'; describe('Cleanup Utils', function () { // Create some elements to use to attach to const firstAttachElement = document.createElement('div'); firstAttachElement.classList.add('first-attach-to'); firstAttachElement.classList.add('shepherd-target-click-disabled'); document.body.appendChild(firstAttachElement); const secondAttachElement = document.createElement('div'); secondAttachElement.classList.add('second-attach-to'); secondAttachElement.classList.add('shepherd-target-click-disabled'); document.body.appendChild(secondAttachElement); // Element with extra highlights const thirdAttachElement = document.createElement('div'); thirdAttachElement.classList.add('third-attach-to'); thirdAttachElement.classList.add('shepherd-target-click-disabled'); document.body.appendChild(thirdAttachElement); // Extra highlight elements for the third step const extraHighlightElement = document.createElement('div'); extraHighlightElement.classList.add('extra-highlight'); extraHighlightElement.classList.add('shepherd-target-click-disabled'); document.body.appendChild(extraHighlightElement); const extraHighlightElement2 = document.createElement('div'); extraHighlightElement2.classList.add('extra-highlight'); extraHighlightElement2.classList.add('shepherd-target-click-disabled'); document.body.appendChild(extraHighlightElement2); const mockedTour = { steps: [ { options: { attachTo: { element: '.first-attach-to', on: 'bottom' }, canClickTarget: false }, // Manually add the target. The tour would do this, if it were a real tour target: firstAttachElement }, { options: { attachTo: { element: '.second-attach-to', on: 'bottom' }, canClickTarget: false }, // Manually add the target. The tour would do this, if it were a real tour target: secondAttachElement }, { options: { attachTo: { element: '.third-attach-to', on: 'bottom' }, canClickTarget: false }, // Manually add the target. The tour would do this, if it were a real tour target: thirdAttachElement, // Add extra highlights _resolvedExtraHighlightElements: [ extraHighlightElement, extraHighlightElement2 ] } ] }; describe('cleanupSteps', function () { it('cleans up steps and removes shepherd-target-click-disabled class', () => { expect(firstAttachElement).toHaveClass('shepherd-target-click-disabled'); expect(secondAttachElement).toHaveClass('shepherd-target-click-disabled'); expect(thirdAttachElement).toHaveClass('shepherd-target-click-disabled'); expect(extraHighlightElement).toHaveClass( 'shepherd-target-click-disabled' ); expect(extraHighlightElement2).toHaveClass( 'shepherd-target-click-disabled' ); cleanupSteps(mockedTour); expect(firstAttachElement).not.toHaveClass( 'shepherd-target-click-disabled' ); expect(secondAttachElement).not.toHaveClass( 'shepherd-target-click-disabled' ); expect(thirdAttachElement).not.toHaveClass( 'shepherd-target-click-disabled' ); expect(extraHighlightElement).not.toHaveClass( 'shepherd-target-click-disabled' ); expect(extraHighlightElement2).not.toHaveClass( 'shepherd-target-click-disabled' ); }); }); }); ================================================ FILE: shepherd.js/test/unit/utils/general.spec.js ================================================ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { Step } from '../../../src/step'; import { parseAttachTo, shouldCenterStep, parseExtraHighlights } from '../../../src/utils/general'; import { getFloatingUIOptions } from '../../../src/utils/floating-ui'; describe('General Utils', function () { let optionsElement; beforeEach(() => { optionsElement = document.createElement('div'); optionsElement.classList.add('options-test'); document.body.appendChild(optionsElement); }); afterEach(() => { document.body.removeChild(optionsElement); }); describe('parseAttachTo()', function () { it('fails if element does not exist', function () { const step = new Step( {}, { attachTo: { element: '.element-does-not-exist', on: 'center' } } ); const { element } = parseAttachTo(step); expect(element).toBeFalsy(); }); it('accepts callback function as element', function () { const callback = vi.fn(); const step = new Step( {}, { attachTo: { element: callback, on: 'center' } } ); parseAttachTo(step); expect(callback).toHaveBeenCalled(); }); it('correctly resolves elements when given function that returns a selector', function () { const step = new Step( {}, { attachTo: { element: () => 'body', on: 'center' } } ); const { element } = parseAttachTo(step); expect(element).toBe(document.body); }); it('binds element callback to step', function () { const step = new Step( {}, { attachTo: { element() { expect(this).toBe(step); }, on: 'center' } } ); parseAttachTo(step); }); }); describe('parseExtraHighlights()', function () { it('returns empty array if extraHighlights is not defined', function () { const step = new Step({}, {}); const highlights = parseExtraHighlights(step); expect(highlights).toEqual([]); }); it('returns empty array if extraHighlights is an empty array', function () { const step = new Step({}, { extraHighlights: [] }); const highlights = parseExtraHighlights(step); expect(highlights).toEqual([]); }); it('resolves extraHighlights selectors to HTMLElements', function () { const step = new Step({}, { extraHighlights: ['.options-test'] }); const highlights = parseExtraHighlights(step); expect(highlights).toEqual([optionsElement]); }); it('returns empty array if no elements match the extraHighlights selectors', function () { const step = new Step({}, { extraHighlights: ['.non-existent-class'] }); const highlights = parseExtraHighlights(step); expect(highlights).toEqual([]); }); }); describe('floatingUIOptions', function () { it('middleware options are passed in last', function () { const step = new Step( {}, { attachTo: { element: '.options-test', on: 'right' }, floatingUIOptions: { middleware: [ { name: 'preventOverflow', options: { altAxis: false } } ] } } ); const floatingUIOptions = getFloatingUIOptions( step.options.attachTo, step ); // Shepherd pushes in flip and shift by default, so this is 3rd expect(floatingUIOptions.middleware[2].options.altAxis).toBe(false); }); it('positioning strategy is explicitly set', function () { const step = new Step( {}, { attachTo: { element: '.options-test', on: 'center' }, options: { floatingUIOptions: { strategy: 'absolute' } } } ); const floatingUIOptions = getFloatingUIOptions( step.options.attachTo, step ); expect(floatingUIOptions.strategy).toBe('absolute'); }); }); describe('shouldCenterStep()', () => { it('Returns true when resolved attachTo options are falsy', () => { const emptyObjAttachTo = {}; const emptyArrAttachTo = []; const nullAttachTo = null; // FAILS Cannot read properties of null (reading 'element') const undefAttachTo = undefined; // FAILS Cannot read properties of undefined (reading 'element') expect(shouldCenterStep(emptyObjAttachTo)).toBe(true); expect(shouldCenterStep(emptyArrAttachTo)).toBe(true); expect(shouldCenterStep(nullAttachTo)).toBe(true); expect(shouldCenterStep(undefAttachTo)).toBe(true); }); it('Returns false when element and on properties are truthy', () => { const testAttachTo = { element: '.pseudo', on: 'right' }; expect(shouldCenterStep(testAttachTo)).toBe(false); }); it('Returns true when element property is null', () => { const elementAttachTo = { element: null }; // FAILS expect(shouldCenterStep(elementAttachTo)).toBe(true); }); }); }); ================================================ FILE: shepherd.js/tsconfig.json ================================================ { "$schema": "https://json.schemastore.org/tsconfig", "compilerOptions": { "target": "es2021", "module": "esnext", "moduleResolution": "bundler", // We don't want to include types dependencies in our compiled output, so tell TypeScript // to enforce using `import type` instead of `import` for Types. "verbatimModuleSyntax": true, "allowJs": true, "strict": true, "noUncheckedIndexedAccess": true, "allowArbitraryExtensions": true, "allowSyntheticDefaultImports": false, "allowImportingTsExtensions": true, "esModuleInterop": true, // --- Lint-style rules // TypeScript also supplies some lint-style checks; nearly all of them are // better handled by ESLint with the `@typescript-eslint`. This one is more // like a safety check, though, so we leave it on. "noPropertyAccessFromIndexSignature": true, // --- Compilation/integration settings // Setting `noEmitOnError` here allows tools trying to respect the tsconfig // to still emit code without breaking on errors. // Errors are still reported in the CLI when running `tsc`, // but the errors won't prevent code from being emitted. // This helps hasten development by allowing devs to prototype before coming // to a decision on what they want their types to be. "noEmitOnError": false, // We use Babel for emitting runtime code, because it's very important that // we always and only use the same transpiler for non-stable features. // If you were to change this to `true`, it could lead to accidentally // generating code with `tsc` instead of Babel, and could thereby // result in broken code at runtime. "emitDeclarationOnly": true, "declaration": true, "declarationMap": true, "declarationDir": "dist", "inlineSourceMap": true, "inlineSources": true, "skipLibCheck": true, "paths": { "src/*": ["./src/*"] } }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"] } ================================================ FILE: shepherd.js/vitest.config.ts ================================================ import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { globals: true, environment: 'happy-dom', setupFiles: ['./test/unit/setupTests.js'], coverage: { provider: 'v8', reporter: ['text', 'lcov', 'html'], reportsDirectory: './test/coverage', include: ['src/**/*.ts'], exclude: [ '**/*.spec.{js,ts}', '**/*.test.{js,ts}', '**/node_modules/**', '**/dist/**', '**/tmp/**', '**/test/**' ] } }, resolve: { alias: { 'shepherd.js': './src' }, conditions: ['browser'] }, define: { 'import.meta.vitest': undefined, 'import.meta.env.SSR': false } }); ================================================ FILE: stderr.log ================================================ Warning: unknown package "shepherd-docs" Warning: unknown package "landing" Warning: unknown package "shepherd-docs" Warning: unknown package "shepherd-docs" Warning: unknown package "shepherd-docs" Warning: unknown package "landing" Warning: unknown package "cypress-tests" Warning: unknown package "unit-tests" Warning: unknown package "landing" Warning: unknown package "cypress-tests" Warning: unknown package "landing" Warning: unknown package "shepherd-docs" Warning: unknown package "landing" Warning: unknown package "shepherd-docs" Warning: unknown package "landing" Warning: unknown package "shepherd-docs" Warning: unknown package "unit-tests" Warning: unknown package "shepherd-docs" Warning: unknown package "landing" Warning: unknown package "unit-tests" Warning: unknown package "landing" Warning: unknown package "landing" (node:2262) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead. (Use `node --trace-deprecation ...` to show where the warning was created) Warning: unknown package "shepherd-docs" Warning: unknown package "unit-tests" (node:2337) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead. (Use `node --trace-deprecation ...` to show where the warning was created) Warning: unknown package "shepherd-docs" Warning: unknown package "unit-tests" (node:2278) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead. (Use `node --trace-deprecation ...` to show where the warning was created) (node:2403) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead. (Use `node --trace-deprecation ...` to show where the warning was created) Warning: unknown package "cypress-tests" Warning: unknown package "landing" (node:2504) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead. (Use `node --trace-deprecation ...` to show where the warning was created) Warning: unknown package "shepherd-docs" Warning: unknown package "cypress-tests" Warning: unknown package "unit-tests" Warning: unknown package "shepherd-docs" Warning: unknown package "landing" Warning: unknown package "cypress-tests" Warning: unknown package "unit-tests"