Skip to content

Commit

Permalink
Add keyboardNav and exitOnEsc options (#508)
Browse files Browse the repository at this point in the history
This allows for configurability to disabled keyboard navigation and/or exiting the tour on ESC.

Closes #491
  • Loading branch information
RobbieTheWagner committed Aug 13, 2019
1 parent 69219e5 commit 949e63e
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 6 deletions.
2 changes: 2 additions & 0 deletions docs-src/tutorials/02-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ const myTour = new Shepherd.Tour(options);
- `confirmCancel`: If true, will issue a `window.confirm` before cancelling
- `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`.
- `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
Expand Down
13 changes: 10 additions & 3 deletions src/js/components/shepherd-element/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export default class ShepherdElement extends Component {
* @private
*/
handleKeyDown(e) {
const { tour } = this.step;
switch (e.keyCode) {
case KEY_TAB:
if (this.focusableElements.length === 1) {
Expand All @@ -80,13 +81,19 @@ export default class ShepherdElement extends Component {
}
break;
case KEY_ESC:
this.step.cancel();
if (tour.options.exitOnEsc) {
this.step.cancel();
}
break;
case LEFT_ARROW:
this.step.tour.back();
if (tour.options.keyboardNavigation) {
tour.back();
}
break;
case RIGHT_ARROW:
this.step.tour.next();
if (tour.options.keyboardNavigation) {
tour.next();
}
break;
default:
break;
Expand Down
11 changes: 10 additions & 1 deletion src/js/tour.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ export class Tour extends Evented {
* @param {Object} options.defaultStepOptions Default options for Steps ({@link Step#constructor}), created through `addStep`
* @param {boolean} options.disableScroll When set to true, will keep the user from scrolling with the scrollbar,
* mousewheel, arrow keys, etc. You may want to use this to ensure you are driving the scroll position with the tour.
* @param {boolean} options.exitOnEsc Exiting the tour with the escape key will be enabled unless this is explicitly
* set to false.
* @param {boolean} options.keyboardNavigation Navigating the tour via left and right arrow keys will be enabled
* unless this is explicitly set to false.
* @param {HTMLElement} options.modalContainer An optional container element for the modal.
* If not set, the modal will be appended to `document.body`.
* @param {object[] | Step[]} options.steps An array of step options objects or Step instances to initialize the tour with
Expand All @@ -59,7 +63,12 @@ export class Tour extends Evented {

autoBind(this);

this.options = options;
const defaultTourOptions = {
exitOnEsc: true,
keyboardNavigation: true
};

this.options = Object.assign({}, defaultTourOptions, options);
this.classPrefix = this.options ? normalizePrefix(this.options.classPrefix) : '';
this.styles = generateStyles(options);
this.steps = [];
Expand Down
15 changes: 15 additions & 0 deletions src/types/tour.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,19 +94,34 @@ declare namespace Tour {
* The prefix to add to all the `shepherd-*` class names.
*/
classPrefix?: string;

/**
* If true, will issue a `window.confirm` before cancelling
*/
confirmCancel?: boolean;

/**
* The message to display in the confirm dialog
*/
confirmCancelMessage?: string;

/**
* Default options for Steps created through `addStep`
*/
defaultStepOptions?: Step.StepOptions;

/**
* Exiting the tour with the escape key will be enabled unless this is explicitly
* set to false.
*/
exitOnEsc?: boolean;

/**
* Navigating the tour via left and right arrow keys will be enabled
* unless this is explicitly set to false.
*/
keyboardNavigation?: boolean;

/**
* An array of step options objects or Step instances to initialize the tour with
*/
Expand Down
42 changes: 40 additions & 2 deletions test/unit/components/shepherd-element.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe('components/ShepherdElement', () => {
};

describe('handleKeyDown', () => {
it('ESC cancels the tour', async () => {
it('exitOnEsc: true - ESC cancels the tour', async () => {
const tour = new Tour();
const step = new Step(tour, {});
const stepCancelSpy = spy(step, 'cancel');
Expand All @@ -25,7 +25,20 @@ describe('components/ShepherdElement', () => {
expect(stepCancelSpy.called).to.be.true;
});

it('arrow keys move between steps', async () => {
it('exitOnEsc: false - ESC does not cancel the tour', async () => {
const tour = new Tour({ exitOnEsc: false });
const step = new Step(tour, {});
const stepCancelSpy = spy(step, 'cancel');

const element = shallow(<ShepherdElement
step={step}
styles={styles}
/>);
await element.find('[onKeyDown]').simulate('keyDown', { keyCode: 27, preventDefault() {} });
expect(stepCancelSpy.called).to.be.false;
});

it('keyboardNavigation: true - arrow keys move between steps', async () => {
const tour = new Tour();
const step = new Step(tour, {});

Expand All @@ -49,5 +62,30 @@ describe('components/ShepherdElement', () => {
tourBackStub.restore();
tourNextStub.restore();
});

it('keyboardNavigation: false - arrow keys do not move between steps', async () => {
const tour = new Tour({ keyboardNavigation: false });
const step = new Step(tour, {});

const tourBackStub = stub(tour, 'back');
const tourNextStub = stub(tour, 'next');

expect(tourBackStub.called).to.be.false;
expect(tourNextStub.called).to.be.false;

const element = shallow(<ShepherdElement
step={step}
styles={styles}
/>);

await element.find('[onKeyDown]').simulate('keyDown', { keyCode: 39, preventDefault() {} });
expect(tourNextStub.called).to.be.false;

await element.find('[onKeyDown]').simulate('keyDown', { keyCode: 37, preventDefault() {} });
expect(tourBackStub.called).to.be.false;

tourBackStub.restore();
tourNextStub.restore();
});
});
});

0 comments on commit 949e63e

Please sign in to comment.