Add keyboard interactions and focus trapping to Navigation
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
import Navigation from './navigation';
|
||||
|
||||
const navHtml = `<nav class="Nav">
|
||||
const navHtml = `<a href="/test">Test link</a>
|
||||
<nav class="Nav">
|
||||
<button class="Menu">Menu</button>
|
||||
<div class="Nav-content">
|
||||
<h2 class="Nav-title">WindyCOYS</h2>
|
||||
<h2 class="Nav-title">
|
||||
<a class="Nav-titleLink" href="/">WindyCOYS</a>
|
||||
</h2>
|
||||
<ul class="Nav-list">
|
||||
<li class="Nav-listItem">
|
||||
<a class="Nav-link" href="/">Home</a>
|
||||
@@ -14,7 +17,8 @@ const navHtml = `<nav class="Nav">
|
||||
</ul>
|
||||
</div>
|
||||
<button class="Nav-close">close</button>
|
||||
</nav>`;
|
||||
</nav>
|
||||
<a href="/another-test">Another test link</a>`;
|
||||
|
||||
let nav;
|
||||
|
||||
@@ -43,11 +47,44 @@ describe('Navigation', () => {
|
||||
expect((new Navigation()).state).toBeUndefined();
|
||||
});
|
||||
|
||||
it('Should fail to initialise if the menu is not provided', () => {
|
||||
const noMenu = new Navigation({
|
||||
container: document.querySelector('.Nav'),
|
||||
});
|
||||
noMenu.init();
|
||||
expect(noMenu.state).toBeUndefined();
|
||||
});
|
||||
|
||||
it('Should be hidden by default', () => {
|
||||
nav.init();
|
||||
expect(nav.container.classList.contains('is-open')).toBe(false);
|
||||
});
|
||||
|
||||
it('Should close when the .Nav element is clicked', () => {
|
||||
nav.init();
|
||||
nav.openNav();
|
||||
const event = new Event('click');
|
||||
nav.container.dispatchEvent(event);
|
||||
expect(nav.state).toBe('hidden');
|
||||
});
|
||||
|
||||
it('Should close when the user presses ESC', () => {
|
||||
nav.init();
|
||||
nav.openNav();
|
||||
const event = new Event('keydown');
|
||||
event.keyCode = 27;
|
||||
nav.container.dispatchEvent(event);
|
||||
expect(nav.state).toBe('hidden');
|
||||
});
|
||||
|
||||
it('Should not close when the .Nav-content element is clicked', () => {
|
||||
nav.init();
|
||||
nav.openNav();
|
||||
const event = new Event('click');
|
||||
nav.container.querySelector('.Nav-content').dispatchEvent(event);
|
||||
expect(nav.state).toBe('visible');
|
||||
});
|
||||
|
||||
it('Should have initial ARIA attributes', () => {
|
||||
nav.init();
|
||||
expect(nav.container.getAttribute('aria-hidden')).toBe('true');
|
||||
@@ -67,30 +104,55 @@ describe('Navigation', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Navigation list', () => {
|
||||
it('Should have initial ARIA attributes', () => {
|
||||
describe('Navigation menu button', () => {
|
||||
it('Should open the Navigation when clicked', () => {
|
||||
nav.init();
|
||||
const ul = nav.container.querySelector('.Nav-list');
|
||||
expect(ul.getAttribute('role')).toBe('menubar');
|
||||
const event = new Event('click');
|
||||
nav.menu.container.dispatchEvent(event);
|
||||
expect(nav.state).toBe('visible');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Navigation list items', () => {
|
||||
it('Should have initial ARIA attributes', () => {
|
||||
describe('Navigation close button', () => {
|
||||
it('Should close the Navigation when clicked', () => {
|
||||
nav.init();
|
||||
const lis = nav.container.querySelectorAll('.Nav-listItem');
|
||||
lis.forEach((li) => {
|
||||
expect(li.getAttribute('role')).toBe('none');
|
||||
});
|
||||
nav.openNav();
|
||||
const event = new Event('click');
|
||||
nav.container.querySelector('.Nav-close').dispatchEvent(event);
|
||||
expect(nav.state).toBe('hidden');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Navigation links', () => {
|
||||
it('Should have initial ARIA attributes', () => {
|
||||
describe('Navigation focus behaviour', () => {
|
||||
let first;
|
||||
let last;
|
||||
|
||||
beforeEach(() => {
|
||||
nav.init();
|
||||
const links = nav.container.querySelectorAll('.Nav-link');
|
||||
links.forEach((link) => {
|
||||
expect(link.getAttribute('role')).toBe('menuitem');
|
||||
});
|
||||
nav.openNav();
|
||||
first = nav.container.querySelector('.Nav-link');
|
||||
last = nav.container.querySelector('.Nav-close');
|
||||
});
|
||||
|
||||
it('Should apply focus to .Nav-titleLink when opened', () => {
|
||||
const link = nav.container.querySelector('.Nav-titleLink');
|
||||
expect(document.activeElement).toBe(link);
|
||||
});
|
||||
|
||||
test('Focus moves to first element when TAB pressed on last', () => {
|
||||
last.focus();
|
||||
const event = new Event('keydown');
|
||||
event.keyCode = 9;
|
||||
last.dispatchEvent(event);
|
||||
expect(document.activeElement).toBe(first);
|
||||
});
|
||||
|
||||
test('Focus moves to last element when SHIFT + TAB pressed on first', () => {
|
||||
first.focus();
|
||||
const event = new Event('keydown');
|
||||
event.shiftKey = true;
|
||||
event.keyCode = 9;
|
||||
first.dispatchEvent(event);
|
||||
expect(document.activeElement).toBe(last);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user