Skip to content

Commit

Permalink
Support close with ESC, focus tooltip on show (#407)
Browse files Browse the repository at this point in the history
  • Loading branch information
RobbieTheWagner authored Jun 20, 2019
1 parent 79f6ab0 commit de53012
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 4 deletions.
33 changes: 30 additions & 3 deletions src/js/step.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { createFromHTML, setupTooltip, parseAttachTo } from './utils/general.js'
// Polyfills
import 'element-matches';
import smoothscroll from 'smoothscroll-polyfill';

smoothscroll.polyfill();

/**
Expand Down Expand Up @@ -152,9 +153,9 @@ export class Step extends Evented {
footer.classList.add('shepherd-footer');

this.options.buttons.map((cfg) => {
const button = createFromHTML(`<li><a class="shepherd-button ${cfg.classes || ''}" tabindex="0">${cfg.text}</a>`);
const button = createFromHTML(`<li><button class="shepherd-button ${cfg.classes || ''}" tabindex="0">${cfg.text}</button>`);
buttons.appendChild(button);
this.bindButtonEvents(cfg, button.querySelector('a'));
this.bindButtonEvents(cfg, button.querySelector('button'));
});

footer.appendChild(buttons);
Expand Down Expand Up @@ -207,6 +208,25 @@ export class Step extends Evented {
content.appendChild(text);
}

/**
* Setup keydown events to allow closing the modal with ESC
* @param {HTMLElement} element The element for the tooltip
* @private
*/
_addKeyDownHandler(element) {
const KEY_ESC = 27;

element.addEventListener('keydown', (e) => {
switch(e.keyCode) {
case KEY_ESC:
this.cancel();
break;
default:
break;
}
});
}

/**
* Creates Shepherd element for step based on options
*
Expand All @@ -216,7 +236,12 @@ export class Step extends Evented {
_createTooltipContent() {
const content = document.createElement('div');
const classes = this.options.classes || '';
const element = createFromHTML(`<div class="${classes}" data-shepherd-step-id="${this.id}">`);
const element = createFromHTML(
`<div class="${classes}"
data-shepherd-step-id="${this.id}"
role="dialog"
tabindex="0">`
);
const header = document.createElement('header');

if (this.options.title) {
Expand All @@ -238,6 +263,7 @@ export class Step extends Evented {

this._addButtons(content);
this._addCancelLink(element, header);
this._addKeyDownHandler(element);

return element;
}
Expand Down Expand Up @@ -414,6 +440,7 @@ export class Step extends Evented {

this.tooltip.show();
this.trigger('show');
this.el.focus();
}

/**
Expand Down
3 changes: 3 additions & 0 deletions src/scss/_base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
@include box-sizing();

.shepherd-element {
outline: none;

@if $use-drop-shadow {
filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.2));
}
Expand All @@ -19,6 +21,7 @@
.shepherd-content {
background: $shepherd-text-background;
font-size: inherit;
outline: none;
padding: 0;

header {
Expand Down
57 changes: 57 additions & 0 deletions test/cypress/integration/a11y.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import setupTour from '../utils/setup-tour';
import tippy from 'tippy.js';

describe('a11y', () => {
let Shepherd;

beforeEach(() => {
Shepherd = null;
tippy.setDefaults({ duration: 0, delay: 0 });

cy.visit('/test/dummy/', {
onLoad(contentWindow) {
if (contentWindow.Shepherd) {
return Shepherd = contentWindow.Shepherd;
}
}
});
});

describe('focus', () => {
let tour;

beforeEach(() => {
tour = setupTour(Shepherd);
});

afterEach(() => {
tour.complete();
});

it('shepherd-element should have focus on tour start', () => {
tour.start();

cy.get('.shepherd-element').should('have.focus');
});
});

describe('keydown events', () => {
let tour;

beforeEach(() => {
tour = setupTour(Shepherd);
});

afterEach(() => {
tour.complete();
});

it('ESC cancels the tour', () => {
tour.start();

cy.get('body').should('have.class', 'shepherd-active');
cy.get('.shepherd-element').trigger('keydown', { keyCode: 27 });
cy.get('body').should('not.have.class', 'shepherd-active');
});
});
});
19 changes: 18 additions & 1 deletion test/unit/step.spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { spy } from 'sinon';
import { spy, stub } from 'sinon';
import Shepherd from '../../src/js/shepherd.js';
import { Step } from '../../src/js/step.js';
import { Tour } from '../../src/js/tour.js';
Expand Down Expand Up @@ -564,4 +564,21 @@ describe('Tour | Step', () => {
expect(buttons.length).toBe(2);
});
});

describe('_addKeyDownHandler', () => {
it('ESC cancels the tour', () => {
const element = document.createElement('div');
const step = new Step();

const cancelStub = stub(step, 'cancel');

step._addKeyDownHandler(element);

const event = new KeyboardEvent('keydown', { keyCode: 27 });
element.dispatchEvent(event);

expect(cancelStub.called).toBe(true);
cancelStub.restore();
});
});
});

0 comments on commit de53012

Please sign in to comment.