Integrate Prettier in to Stylelint and ESLint
This commit is contained in:
@@ -9,7 +9,7 @@ export default class Image {
|
||||
}
|
||||
|
||||
start() {
|
||||
if (this.dataset.loadTransition === 'false') {
|
||||
if (this.dataset.loadTransition === "false") {
|
||||
return;
|
||||
}
|
||||
this.image.style.opacity = 0;
|
||||
@@ -21,6 +21,6 @@ export default class Image {
|
||||
}
|
||||
|
||||
attachLoadedEvent() {
|
||||
this.image.addEventListener('load', () => this.fadeIn());
|
||||
this.image.addEventListener("load", () => this.fadeIn());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +1,46 @@
|
||||
import Image from './image';
|
||||
import Image from "./image";
|
||||
|
||||
const imageHtml = `
|
||||
<img class="image" src="foo.jpg"></img>
|
||||
<img class="disabled-image" data-load-transition="false" src="foo.jpg"></img>
|
||||
`;
|
||||
|
||||
describe('Image', () => {
|
||||
describe("Image", () => {
|
||||
let image;
|
||||
let disabledImage;
|
||||
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML = imageHtml;
|
||||
image = document.querySelector('.image');
|
||||
disabledImage = document.querySelector('.disabled-image');
|
||||
image = document.querySelector(".image");
|
||||
disabledImage = document.querySelector(".disabled-image");
|
||||
});
|
||||
|
||||
it('Should initialize', () => {
|
||||
it("Should initialize", () => {
|
||||
const img = new Image(image);
|
||||
expect(img).toBeInstanceOf(Image);
|
||||
});
|
||||
|
||||
it('Should store a reference to the image element', () => {
|
||||
it("Should store a reference to the image element", () => {
|
||||
const img = new Image(image);
|
||||
expect(img.image).toBe(image);
|
||||
});
|
||||
|
||||
test('fadeIn should set the element opacity to 1', () => {
|
||||
test("fadeIn should set the element opacity to 1", () => {
|
||||
const img = new Image(image);
|
||||
img.fadeIn();
|
||||
expect(img.image.style.opacity).toBe('1');
|
||||
expect(img.image.style.opacity).toBe("1");
|
||||
});
|
||||
|
||||
test('start should set the element opacity to 0', () => {
|
||||
test("start should set the element opacity to 0", () => {
|
||||
const img = new Image(image);
|
||||
img.start();
|
||||
expect(img.image.style.opacity).toBe('0');
|
||||
expect(img.image.style.opacity).toBe("0");
|
||||
});
|
||||
|
||||
test('start should call fadeIn when image has already loaded', () => {
|
||||
test("start should call fadeIn when image has already loaded", () => {
|
||||
const img = new Image(image);
|
||||
img.fadeIn = jest.fn();
|
||||
Object.defineProperty(img.image, 'complete', {
|
||||
Object.defineProperty(img.image, "complete", {
|
||||
value: true,
|
||||
writable: false,
|
||||
});
|
||||
@@ -48,10 +48,10 @@ describe('Image', () => {
|
||||
expect(img.fadeIn).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('start should call attachLoadedEvent if image has not yet loaded', () => {
|
||||
test("start should call attachLoadedEvent if image has not yet loaded", () => {
|
||||
const img = new Image(image);
|
||||
img.attachLoadedEvent = jest.fn();
|
||||
Object.defineProperty(img.image, 'complete', {
|
||||
Object.defineProperty(img.image, "complete", {
|
||||
value: false,
|
||||
writable: false,
|
||||
});
|
||||
@@ -59,7 +59,7 @@ describe('Image', () => {
|
||||
expect(img.attachLoadedEvent).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('start should do nothing if data-load-transition=false', () => {
|
||||
test("start should do nothing if data-load-transition=false", () => {
|
||||
const img = new Image(disabledImage);
|
||||
img.attachLoadedEvent = jest.fn();
|
||||
img.start();
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import windy from './windy';
|
||||
import windy from "./windy";
|
||||
|
||||
document.addEventListener('DOMContentLoaded', windy);
|
||||
document.addEventListener("DOMContentLoaded", windy);
|
||||
|
||||
29
js/menu.js
29
js/menu.js
@@ -1,20 +1,20 @@
|
||||
const STATES = {
|
||||
visible: 'is-visible',
|
||||
minimized: 'is-minimized',
|
||||
visible: "is-visible",
|
||||
minimized: "is-minimized",
|
||||
};
|
||||
|
||||
export default class Menu {
|
||||
constructor(cfg = {}) {
|
||||
if (!cfg.container) return false;
|
||||
this.container = cfg.container;
|
||||
this.state = 'visible';
|
||||
this.state = "visible";
|
||||
this.lastKnownScrollPosition = 0;
|
||||
}
|
||||
|
||||
init() {
|
||||
if (!this.state) return false;
|
||||
this.container.setAttribute('aria-haspopup', true);
|
||||
this.container.setAttribute('aria-expanded', false);
|
||||
this.container.setAttribute("aria-haspopup", true);
|
||||
this.container.setAttribute("aria-expanded", false);
|
||||
this.attachEvents();
|
||||
setTimeout(() => {
|
||||
this.showMenu();
|
||||
@@ -23,38 +23,37 @@ export default class Menu {
|
||||
}
|
||||
|
||||
attachEvents() {
|
||||
window.addEventListener('scroll', () => {
|
||||
window.addEventListener("scroll", () => {
|
||||
this.updateState(this.lastKnownScrollPosition, window.scrollY);
|
||||
this.lastKnownScrollPosition = window.scrollY;
|
||||
});
|
||||
}
|
||||
|
||||
updateState(lastY = 0, currentY) {
|
||||
if (this.state !== 'hidden') {
|
||||
if (this.state !== 'visible' && currentY < lastY) {
|
||||
if (this.state !== "hidden") {
|
||||
if (this.state !== "visible" && currentY < lastY) {
|
||||
this.showMenu();
|
||||
} else if (this.state !== 'minimized' && currentY
|
||||
> lastY) {
|
||||
} else if (this.state !== "minimized" && currentY > lastY) {
|
||||
this.minimizeMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showMenu() {
|
||||
this.state = 'visible';
|
||||
this.container.setAttribute('aria-expanded', false);
|
||||
this.state = "visible";
|
||||
this.container.setAttribute("aria-expanded", false);
|
||||
this.container.classList.add(STATES.visible);
|
||||
this.container.classList.remove(STATES.minimized);
|
||||
}
|
||||
|
||||
minimizeMenu() {
|
||||
this.state = 'minimized';
|
||||
this.state = "minimized";
|
||||
this.container.classList.add(STATES.minimized);
|
||||
}
|
||||
|
||||
hideMenu() {
|
||||
this.state = 'hidden';
|
||||
this.container.setAttribute('aria-expanded', true);
|
||||
this.state = "hidden";
|
||||
this.container.setAttribute("aria-expanded", true);
|
||||
this.container.classList.remove(STATES.visible);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Menu from './menu';
|
||||
import Menu from "./menu";
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
@@ -10,105 +10,105 @@ const menuHtml = `
|
||||
<span class="menu__text">Open menu</span>
|
||||
</button>`;
|
||||
|
||||
describe('Menu button', () => {
|
||||
describe("Menu button", () => {
|
||||
let menu;
|
||||
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML = menuHtml;
|
||||
menu = new Menu({
|
||||
container: document.querySelector('.menu'),
|
||||
container: document.querySelector(".menu"),
|
||||
});
|
||||
});
|
||||
|
||||
it('Should initialise', () => {
|
||||
it("Should initialise", () => {
|
||||
menu.init();
|
||||
expect(menu).toMatchObject({});
|
||||
});
|
||||
|
||||
it('Should fail to initialise if container is missing', () => {
|
||||
it("Should fail to initialise if container is missing", () => {
|
||||
menu.init();
|
||||
expect((new Menu()).state).toBeUndefined();
|
||||
expect(new Menu().state).toBeUndefined();
|
||||
});
|
||||
|
||||
it('Should default to visible state', () => {
|
||||
expect(menu.state).toBe('visible');
|
||||
it("Should default to visible state", () => {
|
||||
expect(menu.state).toBe("visible");
|
||||
});
|
||||
|
||||
it('Should be hidden initially', () => {
|
||||
it("Should be hidden initially", () => {
|
||||
menu.init();
|
||||
expect(menu.container.classList.contains('is-visible')).toBe(false);
|
||||
expect(menu.container.classList.contains("is-visible")).toBe(false);
|
||||
});
|
||||
|
||||
it('Should be visible after 1 second', () => {
|
||||
it("Should be visible after 1 second", () => {
|
||||
menu.init();
|
||||
jest.runAllTimers();
|
||||
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000);
|
||||
expect(menu.container.classList.contains('is-visible')).toBe(true);
|
||||
expect(menu.container.classList.contains("is-visible")).toBe(true);
|
||||
});
|
||||
|
||||
test('Menu can be shown', () => {
|
||||
test("Menu can be shown", () => {
|
||||
menu.init();
|
||||
menu.showMenu();
|
||||
expect(menu.state).toBe('visible');
|
||||
expect(menu.container.classList.contains('is-visible')).toBe(true);
|
||||
expect(menu.container.classList.contains('is-minimized')).toBe(false);
|
||||
expect(menu.state).toBe("visible");
|
||||
expect(menu.container.classList.contains("is-visible")).toBe(true);
|
||||
expect(menu.container.classList.contains("is-minimized")).toBe(false);
|
||||
});
|
||||
|
||||
test('Menu can be hidden', () => {
|
||||
test("Menu can be hidden", () => {
|
||||
menu.init();
|
||||
menu.hideMenu();
|
||||
expect(menu.state).toBe('hidden');
|
||||
expect(menu.container.classList.contains('is-visible')).toBe(false);
|
||||
expect(menu.container.classList.contains('is-minimized')).toBe(false);
|
||||
expect(menu.state).toBe("hidden");
|
||||
expect(menu.container.classList.contains("is-visible")).toBe(false);
|
||||
expect(menu.container.classList.contains("is-minimized")).toBe(false);
|
||||
});
|
||||
|
||||
test('Menu can be minimized', () => {
|
||||
test("Menu can be minimized", () => {
|
||||
menu.init();
|
||||
menu.minimizeMenu();
|
||||
expect(menu.state).toBe('minimized');
|
||||
expect(menu.container.classList.contains('is-visible')).toBe(false);
|
||||
expect(menu.container.classList.contains('is-minimized')).toBe(true);
|
||||
expect(menu.state).toBe("minimized");
|
||||
expect(menu.container.classList.contains("is-visible")).toBe(false);
|
||||
expect(menu.container.classList.contains("is-minimized")).toBe(true);
|
||||
});
|
||||
|
||||
test('The state should not change on scroll if it is hidden', () => {
|
||||
test("The state should not change on scroll if it is hidden", () => {
|
||||
menu.init();
|
||||
menu.hideMenu();
|
||||
menu.updateState(10, 0);
|
||||
expect(menu.state).toBe('hidden');
|
||||
expect(menu.state).toBe("hidden");
|
||||
menu.updateState(0, 10);
|
||||
expect(menu.state).toBe('hidden');
|
||||
expect(menu.state).toBe("hidden");
|
||||
});
|
||||
|
||||
test('The state should change to minimized when scrolling down', () => {
|
||||
test("The state should change to minimized when scrolling down", () => {
|
||||
menu.init();
|
||||
menu.showMenu();
|
||||
menu.updateState(0, 10);
|
||||
expect(menu.state).toBe('minimized');
|
||||
expect(menu.state).toBe("minimized");
|
||||
});
|
||||
|
||||
test('The state should change to visible when scrolling up', () => {
|
||||
test("The state should change to visible when scrolling up", () => {
|
||||
menu.init();
|
||||
menu.minimizeMenu();
|
||||
menu.updateState(10, 0);
|
||||
expect(menu.state).toBe('visible');
|
||||
expect(menu.state).toBe("visible");
|
||||
});
|
||||
|
||||
it('Should have initial ARIA attributes', () => {
|
||||
it("Should have initial ARIA attributes", () => {
|
||||
menu.init();
|
||||
expect(menu.container.getAttribute('aria-haspopup')).toBe('true');
|
||||
expect(menu.container.getAttribute('aria-expanded')).toBe('false');
|
||||
expect(menu.container.getAttribute("aria-haspopup")).toBe("true");
|
||||
expect(menu.container.getAttribute("aria-expanded")).toBe("false");
|
||||
});
|
||||
|
||||
it('Should have updated ARIA attribute when menu is opened', () => {
|
||||
it("Should have updated ARIA attribute when menu is opened", () => {
|
||||
menu.init();
|
||||
menu.showMenu();
|
||||
expect(menu.container.getAttribute('aria-expanded')).toBe('false');
|
||||
expect(menu.container.getAttribute("aria-expanded")).toBe("false");
|
||||
});
|
||||
|
||||
it('Should have updated ARIA attribute when menu is closed', () => {
|
||||
it("Should have updated ARIA attribute when menu is closed", () => {
|
||||
menu.init();
|
||||
menu.showMenu();
|
||||
menu.hideMenu();
|
||||
expect(menu.container.getAttribute('aria-expanded')).toBe('true');
|
||||
expect(menu.container.getAttribute("aria-expanded")).toBe("true");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,53 +1,55 @@
|
||||
const STATES = {
|
||||
open: 'is-open',
|
||||
open: "is-open",
|
||||
};
|
||||
|
||||
export default class Navigation {
|
||||
constructor(cfg = {}) {
|
||||
if (!cfg.container) return false;
|
||||
if (!cfg.menu) return false;
|
||||
this.state = 'hidden';
|
||||
this.state = "hidden";
|
||||
this.container = cfg.container;
|
||||
this.menu = cfg.menu;
|
||||
}
|
||||
|
||||
init() {
|
||||
if (!this.state) return false;
|
||||
this.container.setAttribute('aria-hidden', true);
|
||||
this.container.setAttribute("aria-hidden", true);
|
||||
this.attachClickEvents();
|
||||
this.attachKeyEvents();
|
||||
return true;
|
||||
}
|
||||
|
||||
attachClickEvents() {
|
||||
this.container.addEventListener('click', (e) => {
|
||||
this.container.addEventListener("click", e => {
|
||||
if (this.container === e.target) {
|
||||
this.closeNav();
|
||||
}
|
||||
});
|
||||
this.menu.container.addEventListener('click', () => {
|
||||
this.menu.container.addEventListener("click", () => {
|
||||
this.openNav();
|
||||
});
|
||||
this.container.querySelector('.nav__close').addEventListener('click', () => {
|
||||
this.closeNav();
|
||||
});
|
||||
this.container
|
||||
.querySelector(".nav__close")
|
||||
.addEventListener("click", () => {
|
||||
this.closeNav();
|
||||
});
|
||||
}
|
||||
|
||||
attachKeyEvents() {
|
||||
const first = this.container.querySelector('.nav__link');
|
||||
const last = this.container.querySelector('.nav__close');
|
||||
this.container.addEventListener('keydown', (e) => {
|
||||
const first = this.container.querySelector(".nav__link");
|
||||
const last = this.container.querySelector(".nav__close");
|
||||
this.container.addEventListener("keydown", e => {
|
||||
if (e.keyCode === 27) {
|
||||
this.closeNav();
|
||||
}
|
||||
});
|
||||
first.addEventListener('keydown', (e) => {
|
||||
first.addEventListener("keydown", e => {
|
||||
if (e.shiftKey && e.keyCode === 9) {
|
||||
e.preventDefault();
|
||||
last.focus();
|
||||
}
|
||||
});
|
||||
last.addEventListener('keydown', (e) => {
|
||||
last.addEventListener("keydown", e => {
|
||||
if (!e.shiftKey && e.keyCode === 9) {
|
||||
e.preventDefault();
|
||||
first.focus();
|
||||
@@ -57,18 +59,18 @@ export default class Navigation {
|
||||
|
||||
openNav() {
|
||||
this.lastActiveElement = document.activeElement;
|
||||
this.state = 'visible';
|
||||
this.state = "visible";
|
||||
this.menu.hideMenu();
|
||||
this.container.classList.add(STATES.open);
|
||||
this.container.setAttribute('aria-hidden', false);
|
||||
this.container.querySelector('.nav__title-link').focus();
|
||||
this.container.setAttribute("aria-hidden", false);
|
||||
this.container.querySelector(".nav__title-link").focus();
|
||||
}
|
||||
|
||||
closeNav() {
|
||||
this.state = 'hidden';
|
||||
this.state = "hidden";
|
||||
this.menu.showMenu();
|
||||
this.container.classList.remove(STATES.open);
|
||||
this.container.setAttribute('aria-hidden', true);
|
||||
this.container.setAttribute("aria-hidden", true);
|
||||
if (this.lastActiveElement) {
|
||||
this.lastActiveElement.focus();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Navigation from './navigation';
|
||||
import Navigation from "./navigation";
|
||||
|
||||
const navHtml = `<a href="/test">Test link</a>
|
||||
<nav class="nav">
|
||||
@@ -26,140 +26,140 @@ beforeEach(() => {
|
||||
document.body.innerHTML = navHtml;
|
||||
// Mock Menu
|
||||
const menu = {
|
||||
container: document.querySelector('.menu'),
|
||||
container: document.querySelector(".menu"),
|
||||
showMenu: jest.fn(),
|
||||
hideMenu: jest.fn(),
|
||||
};
|
||||
nav = new Navigation({
|
||||
container: document.querySelector('.nav'),
|
||||
container: document.querySelector(".nav"),
|
||||
menu,
|
||||
});
|
||||
});
|
||||
|
||||
describe('Navigation', () => {
|
||||
it('Should initialise', () => {
|
||||
describe("Navigation", () => {
|
||||
it("Should initialise", () => {
|
||||
nav.init();
|
||||
expect(nav).toMatchObject({});
|
||||
});
|
||||
|
||||
it('Should fail to initialise if container is missing', () => {
|
||||
it("Should fail to initialise if container is missing", () => {
|
||||
nav.init();
|
||||
expect((new Navigation()).state).toBeUndefined();
|
||||
expect(new Navigation().state).toBeUndefined();
|
||||
});
|
||||
|
||||
it('Should fail to initialise if the menu is not provided', () => {
|
||||
it("Should fail to initialise if the menu is not provided", () => {
|
||||
const noMenu = new Navigation({
|
||||
container: document.querySelector('.nav'),
|
||||
container: document.querySelector(".nav"),
|
||||
});
|
||||
noMenu.init();
|
||||
expect(noMenu.state).toBeUndefined();
|
||||
});
|
||||
|
||||
it('Should be hidden by default', () => {
|
||||
it("Should be hidden by default", () => {
|
||||
nav.init();
|
||||
expect(nav.container.classList.contains('is-open')).toBe(false);
|
||||
expect(nav.container.classList.contains("is-open")).toBe(false);
|
||||
});
|
||||
|
||||
it('Should close when the .nav element is clicked', () => {
|
||||
it("Should close when the .nav element is clicked", () => {
|
||||
nav.init();
|
||||
nav.openNav();
|
||||
const event = new Event('click');
|
||||
const event = new Event("click");
|
||||
nav.container.dispatchEvent(event);
|
||||
expect(nav.state).toBe('hidden');
|
||||
expect(nav.state).toBe("hidden");
|
||||
});
|
||||
|
||||
it('Should close when the user presses ESC', () => {
|
||||
it("Should close when the user presses ESC", () => {
|
||||
nav.init();
|
||||
nav.openNav();
|
||||
const event = new Event('keydown');
|
||||
const event = new Event("keydown");
|
||||
event.keyCode = 27;
|
||||
nav.container.dispatchEvent(event);
|
||||
expect(nav.state).toBe('hidden');
|
||||
expect(nav.state).toBe("hidden");
|
||||
});
|
||||
|
||||
it('Should not close when the .nav__content element is clicked', () => {
|
||||
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');
|
||||
const event = new Event("click");
|
||||
nav.container.querySelector(".nav__content").dispatchEvent(event);
|
||||
expect(nav.state).toBe("visible");
|
||||
});
|
||||
|
||||
it('Should have initial ARIA attributes', () => {
|
||||
it("Should have initial ARIA attributes", () => {
|
||||
nav.init();
|
||||
expect(nav.container.getAttribute('aria-hidden')).toBe('true');
|
||||
expect(nav.container.getAttribute("aria-hidden")).toBe("true");
|
||||
});
|
||||
|
||||
it('Should update the ARIA attributes when visible', () => {
|
||||
it("Should update the ARIA attributes when visible", () => {
|
||||
nav.init();
|
||||
nav.openNav();
|
||||
expect(nav.container.getAttribute('aria-hidden')).toBe('false');
|
||||
expect(nav.container.getAttribute("aria-hidden")).toBe("false");
|
||||
});
|
||||
|
||||
it('Should update the ARIA attributes when hidden', () => {
|
||||
it("Should update the ARIA attributes when hidden", () => {
|
||||
nav.init();
|
||||
nav.openNav();
|
||||
nav.closeNav();
|
||||
expect(nav.container.getAttribute('aria-hidden')).toBe('true');
|
||||
expect(nav.container.getAttribute("aria-hidden")).toBe("true");
|
||||
});
|
||||
});
|
||||
|
||||
describe('Navigation menu button', () => {
|
||||
it('Should open the Navigation when clicked', () => {
|
||||
describe("Navigation menu button", () => {
|
||||
it("Should open the Navigation when clicked", () => {
|
||||
nav.init();
|
||||
const event = new Event('click');
|
||||
const event = new Event("click");
|
||||
nav.menu.container.dispatchEvent(event);
|
||||
expect(nav.state).toBe('visible');
|
||||
expect(nav.state).toBe("visible");
|
||||
});
|
||||
});
|
||||
|
||||
describe('Navigation close button', () => {
|
||||
it('Should close the Navigation when clicked', () => {
|
||||
describe("Navigation close button", () => {
|
||||
it("Should close the Navigation when clicked", () => {
|
||||
nav.init();
|
||||
nav.openNav();
|
||||
const event = new Event('click');
|
||||
nav.container.querySelector('.nav__close').dispatchEvent(event);
|
||||
expect(nav.state).toBe('hidden');
|
||||
const event = new Event("click");
|
||||
nav.container.querySelector(".nav__close").dispatchEvent(event);
|
||||
expect(nav.state).toBe("hidden");
|
||||
});
|
||||
});
|
||||
|
||||
describe('Navigation focus behaviour', () => {
|
||||
describe("Navigation focus behaviour", () => {
|
||||
let first;
|
||||
let last;
|
||||
let menu;
|
||||
|
||||
beforeEach(() => {
|
||||
menu = nav.container.querySelector('.menu');
|
||||
first = nav.container.querySelector('.nav__link');
|
||||
last = nav.container.querySelector('.nav__close');
|
||||
menu = nav.container.querySelector(".menu");
|
||||
first = nav.container.querySelector(".nav__link");
|
||||
last = nav.container.querySelector(".nav__close");
|
||||
menu.focus();
|
||||
nav.init();
|
||||
nav.openNav();
|
||||
});
|
||||
|
||||
it('Should apply focus to .nav__title-link when opened', () => {
|
||||
const link = nav.container.querySelector('.nav__title-link');
|
||||
it("Should apply focus to .nav__title-link when opened", () => {
|
||||
const link = nav.container.querySelector(".nav__title-link");
|
||||
expect(document.activeElement).toBe(link);
|
||||
});
|
||||
|
||||
test('Focus moves to first element when TAB pressed on last', () => {
|
||||
test("Focus moves to first element when TAB pressed on last", () => {
|
||||
last.focus();
|
||||
const event = new Event('keydown');
|
||||
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', () => {
|
||||
test("Focus moves to last element when SHIFT + TAB pressed on first", () => {
|
||||
first.focus();
|
||||
const event = new Event('keydown');
|
||||
const event = new Event("keydown");
|
||||
event.shiftKey = true;
|
||||
event.keyCode = 9;
|
||||
first.dispatchEvent(event);
|
||||
expect(document.activeElement).toBe(last);
|
||||
});
|
||||
|
||||
test('Focus is restored to last active element when Navigation is closed', () => {
|
||||
test("Focus is restored to last active element when Navigation is closed", () => {
|
||||
first.focus();
|
||||
nav.closeNav();
|
||||
expect(document.activeElement).toBe(menu);
|
||||
|
||||
@@ -1,33 +1,37 @@
|
||||
export default class ReplyToComment {
|
||||
constructor(cfg = {}) {
|
||||
if (!cfg.form || cfg.form.className !== 'comments__foot') {
|
||||
if (!cfg.form || cfg.form.className !== "comments__foot") {
|
||||
return false;
|
||||
}
|
||||
if (!cfg.links || cfg.links.length === 0) {
|
||||
return false;
|
||||
}
|
||||
this.form = cfg.form;
|
||||
this.backupForm = cfg.form.querySelector('#respond').cloneNode(true);
|
||||
this.backupForm = cfg.form.querySelector("#respond").cloneNode(true);
|
||||
this.links = Array.prototype.slice.call(cfg.links);
|
||||
this.links.forEach((link) => {
|
||||
link.addEventListener('click', e => this.handleLinkClick(e, link));
|
||||
this.links.forEach(link => {
|
||||
link.addEventListener("click", e => this.handleLinkClick(e, link));
|
||||
});
|
||||
}
|
||||
|
||||
insertForm(container, id, author) {
|
||||
const form = this.backupForm.cloneNode(true);
|
||||
const cancel = form.querySelector('#cancel-comment-reply-link');
|
||||
form.querySelector('.comment-respond__title-text').innerHTML = `Reply to ${author}`;
|
||||
form.querySelector('#submit').value = 'Post reply';
|
||||
form.querySelector('#comment_parent').value = id;
|
||||
cancel.style.display = 'inline-block';
|
||||
cancel.addEventListener('click', e => this.handleCancelClick(e, container));
|
||||
const cancel = form.querySelector("#cancel-comment-reply-link");
|
||||
form.querySelector(
|
||||
".comment-respond__title-text",
|
||||
).innerHTML = `Reply to ${author}`;
|
||||
form.querySelector("#submit").value = "Post reply";
|
||||
form.querySelector("#comment_parent").value = id;
|
||||
cancel.style.display = "inline-block";
|
||||
cancel.addEventListener("click", e => this.handleCancelClick(e, container));
|
||||
container.appendChild(form);
|
||||
}
|
||||
|
||||
handleCancelClick(e, container) {
|
||||
e.preventDefault();
|
||||
container.parentNode.querySelector('.comment__reply').classList.remove('is-hidden');
|
||||
container.parentNode
|
||||
.querySelector(".comment__reply")
|
||||
.classList.remove("is-hidden");
|
||||
while (container.firstChild) {
|
||||
container.removeChild(container.firstChild);
|
||||
}
|
||||
@@ -36,11 +40,11 @@ export default class ReplyToComment {
|
||||
|
||||
handleLinkClick(e, link) {
|
||||
e.preventDefault();
|
||||
link.classList.add('is-hidden');
|
||||
link.classList.add("is-hidden");
|
||||
while (this.form.firstChild) {
|
||||
this.form.removeChild(this.form.firstChild);
|
||||
}
|
||||
const container = link.parentNode.querySelector('.comment__inline-reply');
|
||||
const container = link.parentNode.querySelector(".comment__inline-reply");
|
||||
const { commentId, commentAuthor } = link.dataset;
|
||||
this.insertForm(container, commentId, commentAuthor);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ReplyToComment from './reply-to-comment';
|
||||
import ReplyToComment from "./reply-to-comment";
|
||||
|
||||
const commentHtml = `
|
||||
<div class="comment">
|
||||
@@ -66,73 +66,77 @@ let inlineReply;
|
||||
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML = `${commentHtml}${formHtml}`;
|
||||
form = document.querySelector('.comments__foot');
|
||||
links = document.querySelectorAll('.comment__reply');
|
||||
inlineReply = document.querySelector('.comment__inline-reply');
|
||||
form = document.querySelector(".comments__foot");
|
||||
links = document.querySelectorAll(".comment__reply");
|
||||
inlineReply = document.querySelector(".comment__inline-reply");
|
||||
comments = new ReplyToComment({ form, links });
|
||||
});
|
||||
|
||||
describe('ReplyToComment', () => {
|
||||
it('Should initialise', () => {
|
||||
describe("ReplyToComment", () => {
|
||||
it("Should initialise", () => {
|
||||
expect(comments.form).toBeInstanceOf(HTMLDivElement);
|
||||
expect(comments.links).toBeInstanceOf(Array);
|
||||
});
|
||||
|
||||
it('Should fail to initialise if form is missing', () => {
|
||||
it("Should fail to initialise if form is missing", () => {
|
||||
expect(new ReplyToComment()).toEqual({});
|
||||
});
|
||||
|
||||
it('Should fail to initialise if there are no links', () => {
|
||||
expect(new ReplyToComment({
|
||||
form: document.querySelector('#respond'),
|
||||
})).toEqual({});
|
||||
expect(new ReplyToComment({
|
||||
form: document.querySelector('#respond'),
|
||||
links: document.querySelectorAll('.no-links-here'),
|
||||
})).toEqual({});
|
||||
it("Should fail to initialise if there are no links", () => {
|
||||
expect(
|
||||
new ReplyToComment({
|
||||
form: document.querySelector("#respond"),
|
||||
}),
|
||||
).toEqual({});
|
||||
expect(
|
||||
new ReplyToComment({
|
||||
form: document.querySelector("#respond"),
|
||||
links: document.querySelectorAll(".no-links-here"),
|
||||
}),
|
||||
).toEqual({});
|
||||
});
|
||||
|
||||
it('Should remove the form from the DOM when a reply link is clicked', () => {
|
||||
const event = new Event('click');
|
||||
it("Should remove the form from the DOM when a reply link is clicked", () => {
|
||||
const event = new Event("click");
|
||||
comments.handleLinkClick(event, links[0]);
|
||||
expect(form.innerHTML).toEqual('');
|
||||
expect(form.innerHTML).toEqual("");
|
||||
});
|
||||
|
||||
it('Should hide the link when it is clicked', () => {
|
||||
const event = new Event('click');
|
||||
it("Should hide the link when it is clicked", () => {
|
||||
const event = new Event("click");
|
||||
comments.handleLinkClick(event, links[0]);
|
||||
expect(links[0].classList.contains('is-hidden')).toBe(true);
|
||||
expect(links[0].classList.contains("is-hidden")).toBe(true);
|
||||
});
|
||||
|
||||
it('Should insert the form into the inline reply container', () => {
|
||||
it("Should insert the form into the inline reply container", () => {
|
||||
comments.insertForm(inlineReply);
|
||||
const inlineForm = inlineReply.querySelectorAll('form');
|
||||
const inlineForm = inlineReply.querySelectorAll("form");
|
||||
expect(inlineForm.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('Should update the comment_parent hidden element', () => {
|
||||
comments.insertForm(inlineReply, '123', 'Mr Smith');
|
||||
const { value } = inlineReply.querySelector('#comment_parent');
|
||||
expect(value).toEqual('123');
|
||||
it("Should update the comment_parent hidden element", () => {
|
||||
comments.insertForm(inlineReply, "123", "Mr Smith");
|
||||
const { value } = inlineReply.querySelector("#comment_parent");
|
||||
expect(value).toEqual("123");
|
||||
});
|
||||
|
||||
it('Should add a cancel link to the inline form', () => {
|
||||
it("Should add a cancel link to the inline form", () => {
|
||||
comments.insertForm(inlineReply);
|
||||
const el = inlineReply.querySelector('#cancel-comment-reply-link');
|
||||
expect(el.style.display).toEqual('inline-block');
|
||||
const el = inlineReply.querySelector("#cancel-comment-reply-link");
|
||||
expect(el.style.display).toEqual("inline-block");
|
||||
});
|
||||
|
||||
it('Should restore the reply link when the cancel link is clicked', () => {
|
||||
const event = new Event('click');
|
||||
it("Should restore the reply link when the cancel link is clicked", () => {
|
||||
const event = new Event("click");
|
||||
comments.insertForm(inlineReply);
|
||||
comments.handleCancelClick(event, inlineReply);
|
||||
expect(links[0].classList.contains('is-hidden')).toBe(false);
|
||||
expect(links[0].classList.contains("is-hidden")).toBe(false);
|
||||
});
|
||||
|
||||
it('Should remove the inline form when the cancel link is clicked', () => {
|
||||
const event = new Event('click');
|
||||
it("Should remove the inline form when the cancel link is clicked", () => {
|
||||
const event = new Event("click");
|
||||
comments.insertForm(inlineReply);
|
||||
comments.handleCancelClick(event, inlineReply);
|
||||
expect(inlineReply.innerHTML).toEqual('');
|
||||
expect(inlineReply.innerHTML).toEqual("");
|
||||
});
|
||||
});
|
||||
|
||||
21
js/windy.js
21
js/windy.js
@@ -1,30 +1,31 @@
|
||||
import Menu from './menu';
|
||||
import Navigation from './navigation';
|
||||
import Image from './image';
|
||||
import ReplyToComment from './reply-to-comment';
|
||||
import Menu from "./menu";
|
||||
import Navigation from "./navigation";
|
||||
import Image from "./image";
|
||||
import ReplyToComment from "./reply-to-comment";
|
||||
|
||||
const windy = {
|
||||
images: [],
|
||||
};
|
||||
|
||||
export default function init() {
|
||||
Array.prototype.slice.call(document.querySelectorAll('img'))
|
||||
.forEach((image) => {
|
||||
Array.prototype.slice
|
||||
.call(document.querySelectorAll("img"))
|
||||
.forEach(image => {
|
||||
const img = new Image(image);
|
||||
img.start();
|
||||
windy.images.push(img);
|
||||
});
|
||||
windy.menu = new Menu({
|
||||
container: document.querySelector('.menu'),
|
||||
container: document.querySelector(".menu"),
|
||||
});
|
||||
windy.navigation = new Navigation({
|
||||
container: document.querySelector('.nav'),
|
||||
container: document.querySelector(".nav"),
|
||||
menu: windy.menu,
|
||||
});
|
||||
windy.menu.init();
|
||||
windy.navigation.init();
|
||||
windy.comments = new ReplyToComment({
|
||||
form: document.querySelector('.comments__foot'),
|
||||
links: document.querySelectorAll('.comment__reply'),
|
||||
form: document.querySelector(".comments__foot"),
|
||||
links: document.querySelectorAll(".comment__reply"),
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user