Compare commits

...

14 Commits

Author SHA1 Message Date
f008ea821f 1.0.0-RC.5 2019-10-04 22:11:14 +01:00
590340e4da Add donate shortcode to banner 2019-10-04 22:08:32 +01:00
8144c99ceb 1.0.0-RC.4 2019-03-09 21:16:22 +00:00
4e0082b3f3 Integrate Prettier in to Stylelint and ESLint 2019-03-09 21:10:41 +00:00
2248cf1c8d Reduce citation font-size 2019-03-09 19:38:06 +00:00
1d819d6ea6 Change comments sub-list colour 2019-03-09 18:52:44 +00:00
a09b549219 Refactor list styles
Add blockquote styles
2019-03-09 18:50:40 +00:00
b321fb4698 Fix linting errors in build scripts 2019-03-09 18:35:00 +00:00
01c639ceca Add missing body and html tags 2019-03-09 17:12:12 +00:00
2598314c2b Update dependencies
Replace pre-commit hook with Husky and lint-staged
2019-03-03 23:26:24 +00:00
a81cb7cce8 1.0.0-RC.3 2019-03-03 22:31:35 +00:00
734860a334 Indent lists in content 2019-03-03 22:29:32 +00:00
5da894af13 1.0.0-RC.2 2019-02-02 23:32:51 +00:00
f5198270f2 Replace open graph tags with Jetpack implementation 2019-02-02 23:30:14 +00:00
29 changed files with 3365 additions and 6166 deletions

View File

@@ -1,7 +1,16 @@
{ {
"extends": "airbnb-base", "plugins": [
"prettier"
],
"extends": [
"airbnb-base",
"prettier"
],
"env": { "env": {
"browser": true, "browser": true,
"jest": true "jest": true
},
"rules": {
"prettier/prettier": "error"
} }
} }

3
.prettierrc Normal file
View File

@@ -0,0 +1,3 @@
{
"trailingComma": "all"
}

View File

@@ -1,6 +1,13 @@
{ {
"extends": [ "stylelint-config-standard" ], "plugins": [
"stylelint-prettier"
],
"extends": [
"stylelint-config-standard",
"stylelint-prettier/recommended"
],
"rules": { "rules": {
"prettier/prettier": true,
"selector-pseudo-element-colon-notation": "single" "selector-pseudo-element-colon-notation": "single"
} }
} }

View File

@@ -1,11 +1,11 @@
const { writeFile } = require('fs'); const { writeFile } = require("fs");
const { resolve } = require('path'); const { resolve } = require("path");
const imagemin = require('imagemin'); const imagemin = require("imagemin");
const imageminPngquant = require('imagemin-pngquant'); const imageminPngquant = require("imagemin-pngquant");
const imageminGifsicle = require('imagemin-gifsicle'); const imageminGifsicle = require("imagemin-gifsicle");
const imageminJpegtran = require('imagemin-jpegtran'); const imageminJpegtran = require("imagemin-jpegtran");
imagemin(['images/*.{png,jpg,gif}'], 'theme/images', { imagemin(["images/*.{png,jpg,gif}"], "theme/images", {
plugins: [ plugins: [
imageminPngquant({ imageminPngquant({
quality: [0.65, 0.8], quality: [0.65, 0.8],
@@ -15,18 +15,20 @@ imagemin(['images/*.{png,jpg,gif}'], 'theme/images', {
}), }),
imageminJpegtran(), imageminJpegtran(),
], ],
}).then((files) => { })
files.forEach((file) => { .then(files => {
const path = resolve(__dirname, '../', file.path); files.forEach(file => {
writeFile(path, file.data, (error) => { const path = resolve(__dirname, "../", file.path);
if (error) { writeFile(path, file.data, error => {
console.error(error); if (error) {
process.exit(1); process.stdout.write(`${error}\n`);
} process.exit(1);
console.log(`File written: ${path}`); }
process.stdout.write(`File written: ${path}\n`);
});
}); });
})
.catch(error => {
process.stderr.write(`${error}\n`);
process.exit(1);
}); });
}).catch((error) => {
console.error(error);
process.exit(1);
});

View File

@@ -1,10 +1,10 @@
const { resolve } = require('path'); const { resolve } = require("path");
const { execSync } = require('child_process'); const { execSync } = require("child_process");
const { exec, upAll, stop } = require('docker-compose'); const { upAll, stop } = require("docker-compose");
const browsersync = require('browser-sync'); const browsersync = require("browser-sync");
const mysql = require('mysql'); const mysql = require("mysql");
const name = 'windycoys'; const name = "windycoys";
const dockerOptions = { const dockerOptions = {
cwd: resolve(__dirname), cwd: resolve(__dirname),
@@ -12,24 +12,26 @@ const dockerOptions = {
}; };
const browserSyncOptions = { const browserSyncOptions = {
files: [ files: ["theme/**/*"],
'theme/**/*',
],
watchtask: true, watchtask: true,
open: false, open: false,
}; };
const mysqlConfig = { const mysqlConfig = {
user: 'root', user: "root",
password: 'password', password: "password",
database: 'wordpress', database: "wordpress",
}; };
process.env.COMPOSE_PROJECT_NAME = name; process.env.COMPOSE_PROJECT_NAME = name;
// Get IP address // Get IP address
const getIpAddr = function getIpAddr(id) { const getIpAddr = function getIpAddr(id) {
return execSync(`docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ${id}`).toString('utf8').trim(); return execSync(
`docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ${id}`,
)
.toString("utf8")
.trim();
}; };
// Update URIs in MYSQL database // Update URIs in MYSQL database
@@ -40,9 +42,9 @@ const updateUriData = function updateUriData(wordpress, db) {
const query = `UPDATE wp_options SET option_value='http://${wordpress}' WHERE option_name in ('siteurl', 'home')`; const query = `UPDATE wp_options SET option_value='http://${wordpress}' WHERE option_name in ('siteurl', 'home')`;
con.query(query, (error) => { con.query(query, error => {
if (error) { if (error) {
console.error('Error', error.sqlMessage); process.stderr.write(`Error: ${error.sqlMessage}\n`);
process.exit(1); process.exit(1);
} }
}); });
@@ -54,26 +56,28 @@ const updateUriData = function updateUriData(wordpress, db) {
const exitHandler = function exitHandler() { const exitHandler = function exitHandler() {
stop(dockerOptions).then( stop(dockerOptions).then(
() => { () => {
console.log('\n'); process.stdout.write("\n");
process.exit(); process.exit();
}, },
(err) => { err => {
console.error('Error:', err.message); process.stderr.write(`Error: ${err.message}\n`);
process.exit(1); process.exit(1);
}, },
); );
}; };
process.on('beforeExit', exitHandler.bind()); process.on("beforeExit", exitHandler.bind());
process.on('SIGINT', exitHandler.bind()); process.on("SIGINT", exitHandler.bind());
process.on('SIGUSR1', exitHandler.bind()); process.on("SIGUSR1", exitHandler.bind());
process.on('SIGUSR2', exitHandler.bind()); process.on("SIGUSR2", exitHandler.bind());
process.on('uncaughtException', exitHandler.bind()); process.on("uncaughtException", exitHandler.bind());
// Start Docker containers // Start Docker containers
upAll(dockerOptions).then( upAll(dockerOptions).then(
() => { () => {
console.log('Docker containers launched, starting browser-sync...'); process.stdout.write(
"Docker containers launched, starting browser-sync...",
);
const wordpress = getIpAddr(`${name}_wordpress_1`); const wordpress = getIpAddr(`${name}_wordpress_1`);
const db = getIpAddr(`${name}_db_1`); const db = getIpAddr(`${name}_db_1`);
if (wordpress && db) { if (wordpress && db) {
@@ -82,12 +86,14 @@ upAll(dockerOptions).then(
browserSyncOptions.proxy = wordpress; browserSyncOptions.proxy = wordpress;
bs.init(browserSyncOptions); bs.init(browserSyncOptions);
} else { } else {
console.error('Error', 'No IP address for Docker containers. Aborting.'); process.stderr.write(
"Error: No IP address for Docker containers. Aborting.\n",
);
process.exit(1); process.exit(1);
} }
}, },
(err) => { err => {
console.error('Error', err.message); process.strerr.write(`Error: ${err.message}\n`);
process.exit(1); process.exit(1);
}, },
); );

View File

@@ -12,7 +12,7 @@
} }
.comments__list--sub-list .comments__item { .comments__list--sub-list .comments__item {
border-left: 2px solid var(--color-light-grey); border-left: 2px solid var(--color-light-blue);
padding-left: var(--space-sm); padding-left: var(--space-sm);
} }

View File

@@ -39,35 +39,49 @@
.content ul, .content ul,
.content ol { .content ol {
counter-reset: li;
margin-bottom: var(--space-sm); margin-bottom: var(--space-sm);
} }
.content ul li, .content ul li,
.content ol li { .content ol li {
counter-increment: li;
position: relative; position: relative;
padding: 0 0 var(--space-xs) var(--space-sm); margin-bottom: var(--space-xs);
padding-left: var(--space-md);
}
.content ul li:before,
.content ol li:before {
position: absolute;
left: 0;
} }
.content ul li:before { .content ul li:before {
content: ''; content: "";
position: absolute;
left: calc(var(--space-xxs) - (var(--bullet-size) / 2));
top: calc(var(--base-line-height) / 2 * 1rem); top: calc(var(--base-line-height) / 2 * 1rem);
background-color: var(--color-dark-blue); background-color: var(--color-light-blue);
width: var(--bullet-size); width: var(--bullet-size);
height: var(--bullet-size); height: var(--bullet-size);
border-radius: 50%; border-radius: 50%;
transform: translateY(-50%); transform: translateY(-50%);
} }
.content ol li:before {
content: counter(li) ".";
top: 0;
color: var(--color-light-blue);
font-weight: 700;
font-size: 0.9rem;
}
.content a { .content a {
color: var(--color-light-blue); color: var(--color-light-blue);
transition: box-shadow var(--transition-md) ease-out; transition: box-shadow var(--transition-md) ease-out;
} }
.content a img { .content a img {
transition: transition: box-shadow var(--transition-md) ease-out,
box-shadow var(--transition-md) ease-out,
filter var(--transition-md) ease-out; filter var(--transition-md) ease-out;
} }
@@ -123,6 +137,16 @@
margin: var(--space-sm) 0 0; margin: var(--space-sm) 0 0;
} }
.content .wp-block-quote {
border-left: var(--space-xxs) solid var(--color-light-blue);
margin-bottom: var(--space-sm);
padding: var(--space-sm) var(--space-md);
}
.content .wp-block-quote cite {
font-size: 0.8rem;
}
.content table { .content table {
border-collapse: collapse; border-collapse: collapse;
table-layout: fixed; table-layout: fixed;

View File

@@ -64,11 +64,11 @@
--transition-fs: 0.15s; --transition-fs: 0.15s;
--transition-md: 0.3s; --transition-md: 0.3s;
--transition-sl: 0.6s; --transition-sl: 0.6s;
--heading-font-family: 'Raleway', sans-serif; --heading-font-family: "Raleway", sans-serif;
--body-font-family: 'Open Sans', sans-serif; --body-font-family: "Open Sans", sans-serif;
--link-focus: 0 0.1rem 0 0 var(--color-light-blue); --link-focus: 0 0.1rem 0 0 var(--color-light-blue);
--image-focus: 0 0 0 0.2rem #fff, 0 0 0 0.4rem var(--color-light-blue); --image-focus: 0 0 0 0.2rem #fff, 0 0 0 0.4rem var(--color-light-blue);
--bullet-size: 0.334rem; --bullet-size: 0.45rem;
} }
@custom-media --viewport-sm (min-width: 30rem); @custom-media --viewport-sm (min-width: 30rem);

View File

@@ -9,8 +9,7 @@
position: fixed; position: fixed;
top: var(--space-xxs); top: var(--space-xxs);
opacity: 0; opacity: 0;
transition: transition: filter var(--transition-md) ease-out,
filter var(--transition-md) ease-out,
opacity var(--transition-md) ease-out, opacity var(--transition-md) ease-out,
transform var(--transition-md) ease-out; transform var(--transition-md) ease-out;
width: var(--menu-size); width: var(--menu-size);

View File

@@ -79,8 +79,7 @@
} }
.metadata__tag-link { .metadata__tag-link {
transition: transition: box-shadow var(--transition-md) ease-out,
box-shadow var(--transition-md) ease-out,
color var(--transition-md) ease-out; color var(--transition-md) ease-out;
} }

View File

@@ -5,13 +5,12 @@
.nav:after { .nav:after {
background-color: rgba(0, 0, 0, 0.5); background-color: rgba(0, 0, 0, 0.5);
bottom: 0; bottom: 0;
content: ''; content: "";
left: 0; left: 0;
opacity: 0; opacity: 0;
position: fixed; position: fixed;
right: 0; right: 0;
transition: transition: opacity var(--nav-transition-duration) ease-out,
opacity var(--nav-transition-duration) ease-out,
visibility var(--nav-transition-duration) ease-out; visibility var(--nav-transition-duration) ease-out;
top: 0; top: 0;
visibility: hidden; visibility: hidden;
@@ -90,8 +89,7 @@
position: absolute; position: absolute;
right: var(--space-sm); right: var(--space-sm);
top: var(--space-sm); top: var(--space-sm);
transition: transition: box-shadow var(--nav-transition-duration) ease-out,
box-shadow var(--nav-transition-duration) ease-out,
filter var(--nav-transition-duration) ease-out; filter var(--nav-transition-duration) ease-out;
width: var(--icon-size-sm); width: var(--icon-size-sm);
} }
@@ -111,7 +109,8 @@
height: 8%; height: 8%;
position: absolute; position: absolute;
left: 0; left: 0;
transition: transform var(--nav-transition-duration) ease-out var(--nav-transition-duration); transition: transform var(--nav-transition-duration) ease-out
var(--nav-transition-duration);
transform: translateY(-50%); transform: translateY(-50%);
top: 50%; top: 50%;
width: 100%; width: 100%;
@@ -143,8 +142,7 @@
color: #fff; color: #fff;
display: inline-block; display: inline-block;
margin-bottom: var(--space-xs); margin-bottom: var(--space-xs);
transition: transition: box-shadow var(--nav-transition-duration) ease-out,
box-shadow var(--nav-transition-duration) ease-out,
text-shadow var(--nav-transition-duration) ease-out; text-shadow var(--nav-transition-duration) ease-out;
} }
@@ -171,8 +169,7 @@
.nav__social-link:visited { .nav__social-link:visited {
display: inline-block; display: inline-block;
color: #fff; color: #fff;
transition: transition: box-shadow var(--nav-transition-duration) ease-out,
box-shadow var(--nav-transition-duration) ease-out,
filter var(--nav-transition-duration) ease-out; filter var(--nav-transition-duration) ease-out;
} }

View File

@@ -72,7 +72,9 @@
@media (--viewport-lg) { @media (--viewport-lg) {
.page { .page {
grid-template-columns: var(--logo-size-lg) 1fr; grid-template-columns: var(--logo-size-lg) 1fr;
grid-template-rows: var(--icon-size-md) calc(var(--logo-size-lg) - var(--space-md) - 32px) 1fr; grid-template-rows:
var(--icon-size-md) calc(var(--logo-size-lg) - var(--space-md) - 32px)
1fr;
} }
.page__logo { .page__logo {
@@ -93,7 +95,11 @@
@media (--viewport-xl) { @media (--viewport-xl) {
.page { .page {
grid-template-columns: var(--logo-size-xl) 1fr; grid-template-columns: var(--logo-size-xl) 1fr;
grid-template-rows: var(--icon-size-md) calc(var(--logo-size-xl) - var(--space-md) - var(--icon-size-md)) 1fr; grid-template-rows:
var(--icon-size-md) calc(
var(--logo-size-xl) - var(--space-md) - var(--icon-size-md)
)
1fr;
margin: var(--space-md) auto 0; margin: var(--space-md) auto 0;
} }

View File

@@ -1,11 +1,19 @@
.social { .social {
text-align: right; display: flex;
justify-content: flex-end;
align-items: center;
}
.social__item {
margin-left: var(--space-xs);
} }
.social__link { .social__link {
display: inline-block; display: block;
transition: background-color: transparent;
filter var(--transition-fs) ease-out, border: 0;
padding: 0;
transition: filter var(--transition-fs) ease-out,
transform var(--transition-fs) ease-out; transform var(--transition-fs) ease-out;
transform-origin: center bottom; transform-origin: center bottom;
} }
@@ -15,3 +23,17 @@
filter: saturate(1.5) contrast(1.5); filter: saturate(1.5) contrast(1.5);
transform: scale(1.1); transform: scale(1.1);
} }
/* Donate button */
.social__item input[type="image"] {
transition: filter var(--transition-fs) ease-out,
transform var(--transition-fs) ease-out;
transform-origin: center bottom;
}
.social__item input[type="image"]:focus,
.social__item input[type="image"]:hover {
filter: saturate(1.5) contrast(1.5);
transform: scale(1.1);
}

View File

@@ -9,7 +9,7 @@ export default class Image {
} }
start() { start() {
if (this.dataset.loadTransition === 'false') { if (this.dataset.loadTransition === "false") {
return; return;
} }
this.image.style.opacity = 0; this.image.style.opacity = 0;
@@ -21,6 +21,6 @@ export default class Image {
} }
attachLoadedEvent() { attachLoadedEvent() {
this.image.addEventListener('load', () => this.fadeIn()); this.image.addEventListener("load", () => this.fadeIn());
} }
} }

View File

@@ -1,46 +1,46 @@
import Image from './image'; import Image from "./image";
const imageHtml = ` const imageHtml = `
<img class="image" src="foo.jpg"></img> <img class="image" src="foo.jpg"></img>
<img class="disabled-image" data-load-transition="false" src="foo.jpg"></img> <img class="disabled-image" data-load-transition="false" src="foo.jpg"></img>
`; `;
describe('Image', () => { describe("Image", () => {
let image; let image;
let disabledImage; let disabledImage;
beforeEach(() => { beforeEach(() => {
document.body.innerHTML = imageHtml; document.body.innerHTML = imageHtml;
image = document.querySelector('.image'); image = document.querySelector(".image");
disabledImage = document.querySelector('.disabled-image'); disabledImage = document.querySelector(".disabled-image");
}); });
it('Should initialize', () => { it("Should initialize", () => {
const img = new Image(image); const img = new Image(image);
expect(img).toBeInstanceOf(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); const img = new Image(image);
expect(img.image).toBe(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); const img = new Image(image);
img.fadeIn(); 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); const img = new Image(image);
img.start(); 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); const img = new Image(image);
img.fadeIn = jest.fn(); img.fadeIn = jest.fn();
Object.defineProperty(img.image, 'complete', { Object.defineProperty(img.image, "complete", {
value: true, value: true,
writable: false, writable: false,
}); });
@@ -48,10 +48,10 @@ describe('Image', () => {
expect(img.fadeIn).toHaveBeenCalledTimes(1); 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); const img = new Image(image);
img.attachLoadedEvent = jest.fn(); img.attachLoadedEvent = jest.fn();
Object.defineProperty(img.image, 'complete', { Object.defineProperty(img.image, "complete", {
value: false, value: false,
writable: false, writable: false,
}); });
@@ -59,7 +59,7 @@ describe('Image', () => {
expect(img.attachLoadedEvent).toHaveBeenCalledTimes(1); 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); const img = new Image(disabledImage);
img.attachLoadedEvent = jest.fn(); img.attachLoadedEvent = jest.fn();
img.start(); img.start();

View File

@@ -1,3 +1,3 @@
import windy from './windy'; import windy from "./windy";
document.addEventListener('DOMContentLoaded', windy); document.addEventListener("DOMContentLoaded", windy);

View File

@@ -1,20 +1,20 @@
const STATES = { const STATES = {
visible: 'is-visible', visible: "is-visible",
minimized: 'is-minimized', minimized: "is-minimized",
}; };
export default class Menu { export default class Menu {
constructor(cfg = {}) { constructor(cfg = {}) {
if (!cfg.container) return false; if (!cfg.container) return false;
this.container = cfg.container; this.container = cfg.container;
this.state = 'visible'; this.state = "visible";
this.lastKnownScrollPosition = 0; this.lastKnownScrollPosition = 0;
} }
init() { init() {
if (!this.state) return false; if (!this.state) return false;
this.container.setAttribute('aria-haspopup', true); this.container.setAttribute("aria-haspopup", true);
this.container.setAttribute('aria-expanded', false); this.container.setAttribute("aria-expanded", false);
this.attachEvents(); this.attachEvents();
setTimeout(() => { setTimeout(() => {
this.showMenu(); this.showMenu();
@@ -23,38 +23,37 @@ export default class Menu {
} }
attachEvents() { attachEvents() {
window.addEventListener('scroll', () => { window.addEventListener("scroll", () => {
this.updateState(this.lastKnownScrollPosition, window.scrollY); this.updateState(this.lastKnownScrollPosition, window.scrollY);
this.lastKnownScrollPosition = window.scrollY; this.lastKnownScrollPosition = window.scrollY;
}); });
} }
updateState(lastY = 0, currentY) { updateState(lastY = 0, currentY) {
if (this.state !== 'hidden') { if (this.state !== "hidden") {
if (this.state !== 'visible' && currentY < lastY) { if (this.state !== "visible" && currentY < lastY) {
this.showMenu(); this.showMenu();
} else if (this.state !== 'minimized' && currentY } else if (this.state !== "minimized" && currentY > lastY) {
> lastY) {
this.minimizeMenu(); this.minimizeMenu();
} }
} }
} }
showMenu() { showMenu() {
this.state = 'visible'; this.state = "visible";
this.container.setAttribute('aria-expanded', false); this.container.setAttribute("aria-expanded", false);
this.container.classList.add(STATES.visible); this.container.classList.add(STATES.visible);
this.container.classList.remove(STATES.minimized); this.container.classList.remove(STATES.minimized);
} }
minimizeMenu() { minimizeMenu() {
this.state = 'minimized'; this.state = "minimized";
this.container.classList.add(STATES.minimized); this.container.classList.add(STATES.minimized);
} }
hideMenu() { hideMenu() {
this.state = 'hidden'; this.state = "hidden";
this.container.setAttribute('aria-expanded', true); this.container.setAttribute("aria-expanded", true);
this.container.classList.remove(STATES.visible); this.container.classList.remove(STATES.visible);
} }
} }

View File

@@ -1,4 +1,4 @@
import Menu from './menu'; import Menu from "./menu";
jest.useFakeTimers(); jest.useFakeTimers();
@@ -10,105 +10,105 @@ const menuHtml = `
<span class="menu__text">Open menu</span> <span class="menu__text">Open menu</span>
</button>`; </button>`;
describe('Menu button', () => { describe("Menu button", () => {
let menu; let menu;
beforeEach(() => { beforeEach(() => {
document.body.innerHTML = menuHtml; document.body.innerHTML = menuHtml;
menu = new Menu({ menu = new Menu({
container: document.querySelector('.menu'), container: document.querySelector(".menu"),
}); });
}); });
it('Should initialise', () => { it("Should initialise", () => {
menu.init(); menu.init();
expect(menu).toMatchObject({}); expect(menu).toMatchObject({});
}); });
it('Should fail to initialise if container is missing', () => { it("Should fail to initialise if container is missing", () => {
menu.init(); menu.init();
expect((new Menu()).state).toBeUndefined(); expect(new Menu().state).toBeUndefined();
}); });
it('Should default to visible state', () => { it("Should default to visible state", () => {
expect(menu.state).toBe('visible'); expect(menu.state).toBe("visible");
}); });
it('Should be hidden initially', () => { it("Should be hidden initially", () => {
menu.init(); 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(); menu.init();
jest.runAllTimers(); jest.runAllTimers();
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 1000); 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.init();
menu.showMenu(); menu.showMenu();
expect(menu.state).toBe('visible'); expect(menu.state).toBe("visible");
expect(menu.container.classList.contains('is-visible')).toBe(true); expect(menu.container.classList.contains("is-visible")).toBe(true);
expect(menu.container.classList.contains('is-minimized')).toBe(false); expect(menu.container.classList.contains("is-minimized")).toBe(false);
}); });
test('Menu can be hidden', () => { test("Menu can be hidden", () => {
menu.init(); menu.init();
menu.hideMenu(); menu.hideMenu();
expect(menu.state).toBe('hidden'); expect(menu.state).toBe("hidden");
expect(menu.container.classList.contains('is-visible')).toBe(false); expect(menu.container.classList.contains("is-visible")).toBe(false);
expect(menu.container.classList.contains('is-minimized')).toBe(false); expect(menu.container.classList.contains("is-minimized")).toBe(false);
}); });
test('Menu can be minimized', () => { test("Menu can be minimized", () => {
menu.init(); menu.init();
menu.minimizeMenu(); menu.minimizeMenu();
expect(menu.state).toBe('minimized'); expect(menu.state).toBe("minimized");
expect(menu.container.classList.contains('is-visible')).toBe(false); expect(menu.container.classList.contains("is-visible")).toBe(false);
expect(menu.container.classList.contains('is-minimized')).toBe(true); 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.init();
menu.hideMenu(); menu.hideMenu();
menu.updateState(10, 0); menu.updateState(10, 0);
expect(menu.state).toBe('hidden'); expect(menu.state).toBe("hidden");
menu.updateState(0, 10); 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.init();
menu.showMenu(); menu.showMenu();
menu.updateState(0, 10); 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.init();
menu.minimizeMenu(); menu.minimizeMenu();
menu.updateState(10, 0); 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(); menu.init();
expect(menu.container.getAttribute('aria-haspopup')).toBe('true'); expect(menu.container.getAttribute("aria-haspopup")).toBe("true");
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 opened', () => { it("Should have updated ARIA attribute when menu is opened", () => {
menu.init(); menu.init();
menu.showMenu(); 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.init();
menu.showMenu(); menu.showMenu();
menu.hideMenu(); menu.hideMenu();
expect(menu.container.getAttribute('aria-expanded')).toBe('true'); expect(menu.container.getAttribute("aria-expanded")).toBe("true");
}); });
}); });

View File

@@ -1,53 +1,55 @@
const STATES = { const STATES = {
open: 'is-open', open: "is-open",
}; };
export default class Navigation { export default class Navigation {
constructor(cfg = {}) { constructor(cfg = {}) {
if (!cfg.container) return false; if (!cfg.container) return false;
if (!cfg.menu) return false; if (!cfg.menu) return false;
this.state = 'hidden'; this.state = "hidden";
this.container = cfg.container; this.container = cfg.container;
this.menu = cfg.menu; this.menu = cfg.menu;
} }
init() { init() {
if (!this.state) return false; if (!this.state) return false;
this.container.setAttribute('aria-hidden', true); this.container.setAttribute("aria-hidden", true);
this.attachClickEvents(); this.attachClickEvents();
this.attachKeyEvents(); this.attachKeyEvents();
return true; return true;
} }
attachClickEvents() { attachClickEvents() {
this.container.addEventListener('click', (e) => { this.container.addEventListener("click", e => {
if (this.container === e.target) { if (this.container === e.target) {
this.closeNav(); this.closeNav();
} }
}); });
this.menu.container.addEventListener('click', () => { this.menu.container.addEventListener("click", () => {
this.openNav(); this.openNav();
}); });
this.container.querySelector('.nav__close').addEventListener('click', () => { this.container
this.closeNav(); .querySelector(".nav__close")
}); .addEventListener("click", () => {
this.closeNav();
});
} }
attachKeyEvents() { attachKeyEvents() {
const first = this.container.querySelector('.nav__link'); const first = this.container.querySelector(".nav__link");
const last = this.container.querySelector('.nav__close'); const last = this.container.querySelector(".nav__close");
this.container.addEventListener('keydown', (e) => { this.container.addEventListener("keydown", e => {
if (e.keyCode === 27) { if (e.keyCode === 27) {
this.closeNav(); this.closeNav();
} }
}); });
first.addEventListener('keydown', (e) => { first.addEventListener("keydown", e => {
if (e.shiftKey && e.keyCode === 9) { if (e.shiftKey && e.keyCode === 9) {
e.preventDefault(); e.preventDefault();
last.focus(); last.focus();
} }
}); });
last.addEventListener('keydown', (e) => { last.addEventListener("keydown", e => {
if (!e.shiftKey && e.keyCode === 9) { if (!e.shiftKey && e.keyCode === 9) {
e.preventDefault(); e.preventDefault();
first.focus(); first.focus();
@@ -57,18 +59,18 @@ export default class Navigation {
openNav() { openNav() {
this.lastActiveElement = document.activeElement; this.lastActiveElement = document.activeElement;
this.state = 'visible'; this.state = "visible";
this.menu.hideMenu(); this.menu.hideMenu();
this.container.classList.add(STATES.open); this.container.classList.add(STATES.open);
this.container.setAttribute('aria-hidden', false); this.container.setAttribute("aria-hidden", false);
this.container.querySelector('.nav__title-link').focus(); this.container.querySelector(".nav__title-link").focus();
} }
closeNav() { closeNav() {
this.state = 'hidden'; this.state = "hidden";
this.menu.showMenu(); this.menu.showMenu();
this.container.classList.remove(STATES.open); this.container.classList.remove(STATES.open);
this.container.setAttribute('aria-hidden', true); this.container.setAttribute("aria-hidden", true);
if (this.lastActiveElement) { if (this.lastActiveElement) {
this.lastActiveElement.focus(); this.lastActiveElement.focus();
} }

View File

@@ -1,4 +1,4 @@
import Navigation from './navigation'; import Navigation from "./navigation";
const navHtml = `<a href="/test">Test link</a> const navHtml = `<a href="/test">Test link</a>
<nav class="nav"> <nav class="nav">
@@ -26,140 +26,140 @@ beforeEach(() => {
document.body.innerHTML = navHtml; document.body.innerHTML = navHtml;
// Mock Menu // Mock Menu
const menu = { const menu = {
container: document.querySelector('.menu'), container: document.querySelector(".menu"),
showMenu: jest.fn(), showMenu: jest.fn(),
hideMenu: jest.fn(), hideMenu: jest.fn(),
}; };
nav = new Navigation({ nav = new Navigation({
container: document.querySelector('.nav'), container: document.querySelector(".nav"),
menu, menu,
}); });
}); });
describe('Navigation', () => { describe("Navigation", () => {
it('Should initialise', () => { it("Should initialise", () => {
nav.init(); nav.init();
expect(nav).toMatchObject({}); expect(nav).toMatchObject({});
}); });
it('Should fail to initialise if container is missing', () => { it("Should fail to initialise if container is missing", () => {
nav.init(); 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({ const noMenu = new Navigation({
container: document.querySelector('.nav'), container: document.querySelector(".nav"),
}); });
noMenu.init(); noMenu.init();
expect(noMenu.state).toBeUndefined(); expect(noMenu.state).toBeUndefined();
}); });
it('Should be hidden by default', () => { it("Should be hidden by default", () => {
nav.init(); 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.init();
nav.openNav(); nav.openNav();
const event = new Event('click'); const event = new Event("click");
nav.container.dispatchEvent(event); 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.init();
nav.openNav(); nav.openNav();
const event = new Event('keydown'); const event = new Event("keydown");
event.keyCode = 27; event.keyCode = 27;
nav.container.dispatchEvent(event); 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.init();
nav.openNav(); nav.openNav();
const event = new Event('click'); const event = new Event("click");
nav.container.querySelector('.nav__content').dispatchEvent(event); nav.container.querySelector(".nav__content").dispatchEvent(event);
expect(nav.state).toBe('visible'); expect(nav.state).toBe("visible");
}); });
it('Should have initial ARIA attributes', () => { it("Should have initial ARIA attributes", () => {
nav.init(); 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.init();
nav.openNav(); 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.init();
nav.openNav(); nav.openNav();
nav.closeNav(); nav.closeNav();
expect(nav.container.getAttribute('aria-hidden')).toBe('true'); expect(nav.container.getAttribute("aria-hidden")).toBe("true");
}); });
}); });
describe('Navigation menu button', () => { describe("Navigation menu button", () => {
it('Should open the Navigation when clicked', () => { it("Should open the Navigation when clicked", () => {
nav.init(); nav.init();
const event = new Event('click'); const event = new Event("click");
nav.menu.container.dispatchEvent(event); nav.menu.container.dispatchEvent(event);
expect(nav.state).toBe('visible'); expect(nav.state).toBe("visible");
}); });
}); });
describe('Navigation close button', () => { describe("Navigation close button", () => {
it('Should close the Navigation when clicked', () => { it("Should close the Navigation when clicked", () => {
nav.init(); nav.init();
nav.openNav(); nav.openNav();
const event = new Event('click'); const event = new Event("click");
nav.container.querySelector('.nav__close').dispatchEvent(event); nav.container.querySelector(".nav__close").dispatchEvent(event);
expect(nav.state).toBe('hidden'); expect(nav.state).toBe("hidden");
}); });
}); });
describe('Navigation focus behaviour', () => { describe("Navigation focus behaviour", () => {
let first; let first;
let last; let last;
let menu; let menu;
beforeEach(() => { beforeEach(() => {
menu = nav.container.querySelector('.menu'); menu = nav.container.querySelector(".menu");
first = nav.container.querySelector('.nav__link'); first = nav.container.querySelector(".nav__link");
last = nav.container.querySelector('.nav__close'); last = nav.container.querySelector(".nav__close");
menu.focus(); menu.focus();
nav.init(); nav.init();
nav.openNav(); nav.openNav();
}); });
it('Should apply focus to .nav__title-link when opened', () => { it("Should apply focus to .nav__title-link when opened", () => {
const link = nav.container.querySelector('.nav__title-link'); const link = nav.container.querySelector(".nav__title-link");
expect(document.activeElement).toBe(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(); last.focus();
const event = new Event('keydown'); const event = new Event("keydown");
event.keyCode = 9; event.keyCode = 9;
last.dispatchEvent(event); last.dispatchEvent(event);
expect(document.activeElement).toBe(first); 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(); first.focus();
const event = new Event('keydown'); const event = new Event("keydown");
event.shiftKey = true; event.shiftKey = true;
event.keyCode = 9; event.keyCode = 9;
first.dispatchEvent(event); first.dispatchEvent(event);
expect(document.activeElement).toBe(last); 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(); first.focus();
nav.closeNav(); nav.closeNav();
expect(document.activeElement).toBe(menu); expect(document.activeElement).toBe(menu);

View File

@@ -1,33 +1,37 @@
export default class ReplyToComment { export default class ReplyToComment {
constructor(cfg = {}) { constructor(cfg = {}) {
if (!cfg.form || cfg.form.className !== 'comments__foot') { if (!cfg.form || cfg.form.className !== "comments__foot") {
return false; return false;
} }
if (!cfg.links || cfg.links.length === 0) { if (!cfg.links || cfg.links.length === 0) {
return false; return false;
} }
this.form = cfg.form; 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 = Array.prototype.slice.call(cfg.links);
this.links.forEach((link) => { this.links.forEach(link => {
link.addEventListener('click', e => this.handleLinkClick(e, link)); link.addEventListener("click", e => this.handleLinkClick(e, link));
}); });
} }
insertForm(container, id, author) { insertForm(container, id, author) {
const form = this.backupForm.cloneNode(true); const form = this.backupForm.cloneNode(true);
const cancel = form.querySelector('#cancel-comment-reply-link'); const cancel = form.querySelector("#cancel-comment-reply-link");
form.querySelector('.comment-respond__title-text').innerHTML = `Reply to ${author}`; form.querySelector(
form.querySelector('#submit').value = 'Post reply'; ".comment-respond__title-text",
form.querySelector('#comment_parent').value = id; ).innerHTML = `Reply to ${author}`;
cancel.style.display = 'inline-block'; form.querySelector("#submit").value = "Post reply";
cancel.addEventListener('click', e => this.handleCancelClick(e, container)); form.querySelector("#comment_parent").value = id;
cancel.style.display = "inline-block";
cancel.addEventListener("click", e => this.handleCancelClick(e, container));
container.appendChild(form); container.appendChild(form);
} }
handleCancelClick(e, container) { handleCancelClick(e, container) {
e.preventDefault(); e.preventDefault();
container.parentNode.querySelector('.comment__reply').classList.remove('is-hidden'); container.parentNode
.querySelector(".comment__reply")
.classList.remove("is-hidden");
while (container.firstChild) { while (container.firstChild) {
container.removeChild(container.firstChild); container.removeChild(container.firstChild);
} }
@@ -36,11 +40,11 @@ export default class ReplyToComment {
handleLinkClick(e, link) { handleLinkClick(e, link) {
e.preventDefault(); e.preventDefault();
link.classList.add('is-hidden'); link.classList.add("is-hidden");
while (this.form.firstChild) { while (this.form.firstChild) {
this.form.removeChild(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; const { commentId, commentAuthor } = link.dataset;
this.insertForm(container, commentId, commentAuthor); this.insertForm(container, commentId, commentAuthor);
} }

View File

@@ -1,4 +1,4 @@
import ReplyToComment from './reply-to-comment'; import ReplyToComment from "./reply-to-comment";
const commentHtml = ` const commentHtml = `
<div class="comment"> <div class="comment">
@@ -66,73 +66,77 @@ let inlineReply;
beforeEach(() => { beforeEach(() => {
document.body.innerHTML = `${commentHtml}${formHtml}`; document.body.innerHTML = `${commentHtml}${formHtml}`;
form = document.querySelector('.comments__foot'); form = document.querySelector(".comments__foot");
links = document.querySelectorAll('.comment__reply'); links = document.querySelectorAll(".comment__reply");
inlineReply = document.querySelector('.comment__inline-reply'); inlineReply = document.querySelector(".comment__inline-reply");
comments = new ReplyToComment({ form, links }); comments = new ReplyToComment({ form, links });
}); });
describe('ReplyToComment', () => { describe("ReplyToComment", () => {
it('Should initialise', () => { it("Should initialise", () => {
expect(comments.form).toBeInstanceOf(HTMLDivElement); expect(comments.form).toBeInstanceOf(HTMLDivElement);
expect(comments.links).toBeInstanceOf(Array); 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({}); expect(new ReplyToComment()).toEqual({});
}); });
it('Should fail to initialise if there are no links', () => { it("Should fail to initialise if there are no links", () => {
expect(new ReplyToComment({ expect(
form: document.querySelector('#respond'), new ReplyToComment({
})).toEqual({}); form: document.querySelector("#respond"),
expect(new ReplyToComment({ }),
form: document.querySelector('#respond'), ).toEqual({});
links: document.querySelectorAll('.no-links-here'), expect(
})).toEqual({}); 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', () => { it("Should remove the form from the DOM when a reply link is clicked", () => {
const event = new Event('click'); const event = new Event("click");
comments.handleLinkClick(event, links[0]); comments.handleLinkClick(event, links[0]);
expect(form.innerHTML).toEqual(''); expect(form.innerHTML).toEqual("");
}); });
it('Should hide the link when it is clicked', () => { it("Should hide the link when it is clicked", () => {
const event = new Event('click'); const event = new Event("click");
comments.handleLinkClick(event, links[0]); 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); comments.insertForm(inlineReply);
const inlineForm = inlineReply.querySelectorAll('form'); const inlineForm = inlineReply.querySelectorAll("form");
expect(inlineForm.length).toEqual(1); expect(inlineForm.length).toEqual(1);
}); });
it('Should update the comment_parent hidden element', () => { it("Should update the comment_parent hidden element", () => {
comments.insertForm(inlineReply, '123', 'Mr Smith'); comments.insertForm(inlineReply, "123", "Mr Smith");
const { value } = inlineReply.querySelector('#comment_parent'); const { value } = inlineReply.querySelector("#comment_parent");
expect(value).toEqual('123'); 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); comments.insertForm(inlineReply);
const el = inlineReply.querySelector('#cancel-comment-reply-link'); const el = inlineReply.querySelector("#cancel-comment-reply-link");
expect(el.style.display).toEqual('inline-block'); expect(el.style.display).toEqual("inline-block");
}); });
it('Should restore the reply link when the cancel link is clicked', () => { it("Should restore the reply link when the cancel link is clicked", () => {
const event = new Event('click'); const event = new Event("click");
comments.insertForm(inlineReply); comments.insertForm(inlineReply);
comments.handleCancelClick(event, 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', () => { it("Should remove the inline form when the cancel link is clicked", () => {
const event = new Event('click'); const event = new Event("click");
comments.insertForm(inlineReply); comments.insertForm(inlineReply);
comments.handleCancelClick(event, inlineReply); comments.handleCancelClick(event, inlineReply);
expect(inlineReply.innerHTML).toEqual(''); expect(inlineReply.innerHTML).toEqual("");
}); });
}); });

View File

@@ -1,30 +1,31 @@
import Menu from './menu'; import Menu from "./menu";
import Navigation from './navigation'; import Navigation from "./navigation";
import Image from './image'; import Image from "./image";
import ReplyToComment from './reply-to-comment'; import ReplyToComment from "./reply-to-comment";
const windy = { const windy = {
images: [], images: [],
}; };
export default function init() { export default function init() {
Array.prototype.slice.call(document.querySelectorAll('img')) Array.prototype.slice
.forEach((image) => { .call(document.querySelectorAll("img"))
.forEach(image => {
const img = new Image(image); const img = new Image(image);
img.start(); img.start();
windy.images.push(img); windy.images.push(img);
}); });
windy.menu = new Menu({ windy.menu = new Menu({
container: document.querySelector('.menu'), container: document.querySelector(".menu"),
}); });
windy.navigation = new Navigation({ windy.navigation = new Navigation({
container: document.querySelector('.nav'), container: document.querySelector(".nav"),
menu: windy.menu, menu: windy.menu,
}); });
windy.menu.init(); windy.menu.init();
windy.navigation.init(); windy.navigation.init();
windy.comments = new ReplyToComment({ windy.comments = new ReplyToComment({
form: document.querySelector('.comments__foot'), form: document.querySelector(".comments__foot"),
links: document.querySelectorAll('.comment__reply'), links: document.querySelectorAll(".comment__reply"),
}); });
} }

8747
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "windycoys", "name": "windycoys",
"version": "1.0.0-RC.1", "version": "1.0.0-RC.5",
"description": "Wordpress theme for windycoys.com", "description": "Wordpress theme for windycoys.com",
"author": "Dan Head <me@danielhead.com>", "author": "Dan Head <me@danielhead.com>",
"license": "MIT", "license": "MIT",
@@ -12,13 +12,13 @@
"build": "run-s images css:compile js:compile", "build": "run-s images css:compile js:compile",
"css": "run-p -c css:*", "css": "run-p -c css:*",
"css:compile": "postcss -o theme/style.css css/index.css", "css:compile": "postcss -o theme/style.css css/index.css",
"css:lint": "stylelint --config .stylelintrc \"css/**/*.css\"", "css:lint": "stylelint css/*.css",
"images": "node build/imagemin.js", "images": "node build/imagemin.js",
"jest": "jest", "jest": "jest",
"jest:watch": "jest --watch", "jest:watch": "jest --watch",
"js": "run-s js:*", "js": "run-s js:*",
"js:compile": "rollup --config rollup.config.js", "js:compile": "rollup --config rollup.config.js",
"js:lint": "eslint js/", "js:lint": "eslint build js",
"serve": "node build/serve.js", "serve": "node build/serve.js",
"start": "run-p watch serve", "start": "run-p watch serve",
"test": "run-s css:lint js:lint jest", "test": "run-s css:lint js:lint jest",
@@ -27,43 +27,67 @@
"watch:images": "nodemon -q --watch images -e png,jpg,gif --exec npm run images", "watch:images": "nodemon -q --watch images -e png,jpg,gif --exec npm run images",
"watch:js": "nodemon -q --watch js -e js --exec npm run js" "watch:js": "nodemon -q --watch js -e js --exec npm run js"
}, },
"devDependencies": {
"browser-sync": "^2.26.3",
"docker-compose": "^0.9.3",
"mysql": "^2.16.0",
"nodemon": "^1.18.7",
"pre-commit": "^1.2.2"
},
"dependencies": { "dependencies": {
"@babel/cli": "^7.2.3", "@babel/cli": "^7.2.3",
"@babel/core": "^7.2.0", "@babel/core": "^7.3.4",
"@babel/preset-env": "^7.2.3", "@babel/preset-env": "^7.3.4",
"babel-core": "^7.0.0-bridge.0", "babel-core": "^7.0.0-bridge.0",
"babel-jest": "^23.6.0", "babel-jest": "^24.1.0",
"cssnano": "^4.1.8", "browser-sync": "^2.26.3",
"eslint": "^5.12.0", "cssnano": "^4.1.10",
"docker-compose": "^0.12.0",
"eslint": "^5.15.0",
"eslint-config-airbnb-base": "^13.1.0", "eslint-config-airbnb-base": "^13.1.0",
"eslint-plugin-import": "^2.14.0", "eslint-config-prettier": "^4.1.0",
"eslint-plugin-import": "^2.16.0",
"eslint-plugin-prettier": "^3.0.1",
"husky": "^1.3.1",
"imagemin": "^6.1.0", "imagemin": "^6.1.0",
"imagemin-gifsicle": "^6.0.1", "imagemin-gifsicle": "^6.0.1",
"imagemin-jpegtran": "^6.0.0", "imagemin-jpegtran": "^6.0.0",
"imagemin-pngquant": "^7.0.0", "imagemin-pngquant": "^7.0.0",
"jest": "^23.6.0", "jest": "^24.1.0",
"lint-staged": "^8.1.5",
"mysql": "^2.16.0",
"nodemon": "^1.18.10",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"postcss-banner": "^3.0.0", "postcss-banner": "^3.0.1",
"postcss-cli": "^6.1.1", "postcss-cli": "^6.1.2",
"postcss-import": "^12.0.1", "postcss-import": "^12.0.1",
"postcss-normalize": "^7.0.1", "postcss-normalize": "^7.0.1",
"postcss-preset-env": "^6.4.0", "postcss-preset-env": "^6.6.0",
"rollup": "^1.1.0", "prettier": "^1.16.4",
"rollup-plugin-babel": "^4.3.0", "rollup": "^1.4.0",
"rollup-plugin-uglify": "^6.0.1", "rollup-plugin-babel": "^4.3.2",
"stylelint": "^9.9.0", "rollup-plugin-uglify": "^6.0.2",
"stylelint-config-standard": "^18.0.0" "stylelint": "^9.10.1",
"stylelint-config-prettier": "^5.0.0",
"stylelint-config-standard": "^18.2.0",
"stylelint-prettier": "^1.0.6"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"build/*.js": [
"eslint --fix",
"prettier --write",
"git add"
],
"css/*.css": [
"stylelint --fix",
"prettier --write",
"git add"
],
"js/*.js": [
"eslint --fix",
"prettier --write",
"jest",
"git add"
]
}, },
"pre-commit": [
"test"
],
"browserslist": [ "browserslist": [
"last 1 version", "last 1 version",
"not dead", "not dead",

View File

@@ -31,3 +31,5 @@ window.twttr = (function(d, s, id) {
}; };
return t; return t;
}(document, "script", "twitter-wjs"));</script> }(document, "script", "twitter-wjs"));</script>
</body>
</html>

View File

@@ -150,3 +150,22 @@ function windycoys_is_ie() {
} }
return false; return false;
} }
function windycoys_jetpack_custom_image($media, $post_id, $args) {
if ($media) {
return $media;
} else {
$url = get_template_directory_uri() . '/images/icon.png';
$permalink = get_permalink($post_id);
return array(
array(
'type' => 'image',
'from' => 'custom_fallback',
'src' => esc_url($url),
'href' => $permalink,
)
);
}
}
add_filter('jetpack_images_get_images', 'windycoys_jetpack_custom_image', 10, 3);

View File

@@ -13,14 +13,6 @@ $manifest = get_template_directory_uri() . '/manifest.json';
<title><?php wp_title(''); ?><?php if(wp_title('', false)) { echo ' &laquo; '; } ?> <?php bloginfo('name'); ?></title> <title><?php wp_title(''); ?><?php if(wp_title('', false)) { echo ' &laquo; '; } ?> <?php bloginfo('name'); ?></title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#201f42"> <meta name="theme-color" content="#201f42">
<meta name="description" content="<?php bloginfo('description'); ?>">
<meta property="og:title" content="<?php if(is_single()) { single_post_title(); } else { bloginfo("name"); } ?>"/>
<meta property="og:type" content="blog"/>
<meta property="og:url" content="https://<?php echo $_SERVER["HTTP_HOST"].$_SERVER["REQUEST_URI"]; ?>"/>
<meta property="og:image" content="<?php echo get_template_directory_uri() ?>/images/icon.png"/>
<meta property="og:site_name" content="WindyCOYS"/>
<meta property="fb:admins" content="Windy84"/>
<meta property="og:description" content="<?php bloginfo('description'); ?>"/>
<?php wp_head(); ?> <?php wp_head(); ?>
<link rel="me" href="https://twitter.com/WindyCOYS"> <link rel="me" href="https://twitter.com/WindyCOYS">
<link rel="icon" type="image/png" href="<?php echo get_template_directory_uri() ?>/images/icon_48.png"> <link rel="icon" type="image/png" href="<?php echo get_template_directory_uri() ?>/images/icon_48.png">

View File

@@ -1,17 +1,26 @@
<div class="social"> <div class="social">
<a class="social__link" href="https://www.facebook.com/WindyCOYS/" aria-label="WindyCOYS on Facebook"> <div class="social__item">
<svg class="icon icon--facebook" aria-hidden="true"> <a class="social__link" href="https://www.facebook.com/WindyCOYS/" aria-label="WindyCOYS on Facebook">
<use xlink:href="#icon-facebook"></use> <svg class="icon icon--facebook" aria-hidden="true">
</svg> <use xlink:href="#icon-facebook"></use>
</a> </svg>
<a class="social__link" href="https://twitter.com/windycoys/" aria-label="WindyCOYS on Twitter"> </a>
<svg class="icon icon--twitter" aria-hidden="true"> </div>
<use xlink:href="#icon-twitter"></use> <div class="social__item">
</svg> <a class="social__link" href="https://twitter.com/windycoys/" aria-label="WindyCOYS on Twitter">
</a> <svg class="icon icon--twitter" aria-hidden="true">
<a class="social__link" href="/feed" aria-label="WindyCOYS RSS Feed"> <use xlink:href="#icon-twitter"></use>
<svg class="icon icon--rss" aria-hidden="true"> </svg>
<use xlink:href="#icon-rss"></use> </a>
</svg> </div>
</a> <div class="social__item">
<a class="social__link" href="/feed" aria-label="WindyCOYS RSS Feed">
<svg class="icon icon--rss" aria-hidden="true">
<use xlink:href="#icon-rss"></use>
</svg>
</a>
</div>
<div class="social__item">
<?php echo do_shortcode('[paypal-donation]'); ?>
</div>
</div> </div>