Add Jest tests
Automate CSS and JS Banners Remove image fading for now Reference docker-compose.yml restored
This commit is contained in:
17
.babelrc
17
.babelrc
@@ -12,5 +12,22 @@
|
|||||||
"modules": false
|
"modules": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"test": {
|
||||||
|
"presets": [
|
||||||
|
[
|
||||||
|
"@babel/preset-env", {
|
||||||
|
"targets": {
|
||||||
|
"browsers": [
|
||||||
|
"last 2 versions",
|
||||||
|
"ie >= 11"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"loose": true
|
||||||
|
}
|
||||||
|
]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"extends": "airbnb-base",
|
"extends": "airbnb-base",
|
||||||
"env": {
|
"env": {
|
||||||
"browser": true
|
"browser": true,
|
||||||
|
"jest": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
40
.gitignore
vendored
40
.gitignore
vendored
@@ -1,4 +1,42 @@
|
|||||||
|
# Temporary
|
||||||
.tmp/
|
.tmp/
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
node_modules/
|
node_modules/
|
||||||
theme/style.css
|
jspm_packages/
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# Test
|
||||||
|
coverage/
|
||||||
|
|
||||||
|
# dotenv environment variables file
|
||||||
|
.env
|
||||||
|
|
||||||
|
# Distribution assets
|
||||||
theme/bundle.js
|
theme/bundle.js
|
||||||
|
theme/style.css
|
||||||
|
|
||||||
|
|||||||
20
build.sh
20
build.sh
@@ -1,20 +0,0 @@
|
|||||||
#!/usr/bin/env sh
|
|
||||||
|
|
||||||
# Build and copy theme files
|
|
||||||
npm run build
|
|
||||||
cp -vfr theme/* windycoys
|
|
||||||
|
|
||||||
# Purge Cloudflair cache
|
|
||||||
if [[ -z "${CLOUDFLARE_API_KEY}" ]] || [[ -z "${CLOUDFLARE_EMAIL}" ]] || [[ -z "${CLOUDFLARE_IDENTIFIER}" ]]; then
|
|
||||||
echo "Error: Cloudflare environment variables missing"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
curl -X DELETE \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-H "X-Auth-Key: $CLOUDFLARE_API_KEY" \
|
|
||||||
-H "X-Auth-Email: $CLOUDFLARE_EMAIL" \
|
|
||||||
--data '{"purge_everything":true}' \
|
|
||||||
"https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_IDENTIFIER/purge_cache"
|
|
||||||
|
|
||||||
exec tail -f /dev/null
|
|
||||||
@@ -1,14 +1,3 @@
|
|||||||
/*!
|
|
||||||
Theme Name: WindyCOYS
|
|
||||||
Theme URI: https://danielhead.com
|
|
||||||
Author: Dan Head
|
|
||||||
Author URI: https://danielhead.com
|
|
||||||
Description: Theme for windycoys.com
|
|
||||||
Version: 2.0
|
|
||||||
License: GNU General Public License v2 or later
|
|
||||||
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import "normalize.css";
|
@import "normalize.css";
|
||||||
@import "base";
|
@import "base";
|
||||||
@import "grid";
|
@import "grid";
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ hash rsync 2>/dev/null || { echo >&2 "Error: rsync is not installed. Aborting.";
|
|||||||
|
|
||||||
# Deploy theme with rsync
|
# Deploy theme with rsync
|
||||||
rsync -e "ssh -o StrictHostKeyChecking=no" -avzuh --progress \
|
rsync -e "ssh -o StrictHostKeyChecking=no" -avzuh --progress \
|
||||||
theme $SSH_USER@$SSH_IP:~/windycoys_theme
|
theme/* $SSH_USER@$SSH_IP:~/windycoys_theme
|
||||||
|
|
||||||
#Test curl is installed and Cloudflare ENV variables are set
|
#Test curl is installed and Cloudflare ENV variables are set
|
||||||
hash curl 2>/dev/null || { echo >&2 "Error: curl is not installed. Aborting."; exit 1; }
|
hash curl 2>/dev/null || { echo >&2 "Error: curl is not installed. Aborting."; exit 1; }
|
||||||
|
|||||||
22
docker-compose.yml
Normal file
22
docker-compose.yml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
wordpress:
|
||||||
|
environment:
|
||||||
|
- WORDPRESS_DB_HOST=db
|
||||||
|
- WORDPRESS_DB_PASSWORD=password
|
||||||
|
- WORDPRESS_DB_USER=windycoys
|
||||||
|
image: 'wordpress:4.9.5-php5.6-apache'
|
||||||
|
links:
|
||||||
|
- db
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
volumes:
|
||||||
|
- ./.tmp/wp-content:/var/www/html/wp-content
|
||||||
|
- ./theme:/var/www/html/wp-content/themes/windycoys
|
||||||
|
db:
|
||||||
|
environment:
|
||||||
|
- MYSQL_DATABASE=wordpress
|
||||||
|
- MYSQL_PASSWORD=password
|
||||||
|
- MYSQL_ROOT_PASSWORD=password
|
||||||
|
- MYSQL_USER=windycoys
|
||||||
|
image: 'mariadb:10.3.4'
|
||||||
|
volumes:
|
||||||
|
- ./.tmp/mysql:/var/lib/mysql
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
export default class Article {
|
|
||||||
constructor(cfg) {
|
|
||||||
this.article = cfg.article;
|
|
||||||
this.metadata = cfg.metadata;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
export default class ImageFade {
|
|
||||||
constructor() {
|
|
||||||
this.images = Array.prototype.slice.call(document.querySelectorAll('img'));
|
|
||||||
this.images.forEach((image) => {
|
|
||||||
this.prepareImage(image);
|
|
||||||
this.setEvent(image);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
prepareImage(image) {
|
|
||||||
image.style.opacity = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
setEvent(image) {
|
|
||||||
image.addEventListener('load', () => {
|
|
||||||
image.style.opacity = 1;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
23
js/index.js
23
js/index.js
@@ -1,22 +1,3 @@
|
|||||||
import Article from './article';
|
import windy from './windy';
|
||||||
import ImageFade from './imagefade';
|
|
||||||
import Menu from './menu';
|
|
||||||
import Nav from './nav';
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', windy);
|
||||||
const site = {};
|
|
||||||
site.menu = new Menu({
|
|
||||||
el: document.querySelector('.Menu'),
|
|
||||||
});
|
|
||||||
site.nav = new Nav({
|
|
||||||
el: document.querySelector('.Nav'),
|
|
||||||
menu: site.menu,
|
|
||||||
});
|
|
||||||
site.images = new ImageFade();
|
|
||||||
|
|
||||||
site.articles = [].slice.call(document.querySelectorAll('.Article'))
|
|
||||||
.map((article, index) => new Article({
|
|
||||||
article,
|
|
||||||
metadata: document.querySelectorAll('.Metadata')[index],
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|||||||
38
js/menu.js
38
js/menu.js
@@ -1,13 +1,19 @@
|
|||||||
const CLS = {
|
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 = {}) {
|
||||||
this.el = cfg.el;
|
if (!cfg.container) return false;
|
||||||
|
this.container = cfg.container;
|
||||||
this.state = 'visible';
|
this.state = 'visible';
|
||||||
|
this.lastKnownScrollPosition = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.container.setAttribute('aria-haspopup', true);
|
||||||
|
this.container.setAttribute('aria-expanded', false);
|
||||||
this.attachEvents();
|
this.attachEvents();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.showMenu();
|
this.showMenu();
|
||||||
@@ -15,33 +21,39 @@ export default class Menu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
attachEvents() {
|
attachEvents() {
|
||||||
let lastKnownPosition = 0;
|
|
||||||
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 !== 'hidden') {
|
||||||
if (this.state !== 'visible' && window.scrollY < lastKnownPosition) {
|
if (this.state !== 'visible' && currentY < lastY) {
|
||||||
this.showMenu();
|
this.showMenu();
|
||||||
} else if (this.state !== 'minimized' && window.scrollY >
|
} else if (this.state !== 'minimized' && currentY >
|
||||||
lastKnownPosition) {
|
lastY) {
|
||||||
this.minimizeMenu();
|
this.minimizeMenu();
|
||||||
}
|
}
|
||||||
lastKnownPosition = window.scrollY;
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
showMenu() {
|
showMenu() {
|
||||||
this.state = 'visible';
|
this.state = 'visible';
|
||||||
this.el.classList.add(CLS.visible);
|
this.container.setAttribute('aria-expanded', false);
|
||||||
this.el.classList.remove(CLS.minimized);
|
this.container.classList.add(STATES.visible);
|
||||||
|
this.container.classList.remove(STATES.minimized);
|
||||||
}
|
}
|
||||||
|
|
||||||
minimizeMenu() {
|
minimizeMenu() {
|
||||||
this.state = 'minimized';
|
this.state = 'minimized';
|
||||||
this.el.classList.add(CLS.minimized);
|
this.container.classList.add(STATES.minimized);
|
||||||
}
|
}
|
||||||
|
|
||||||
hideMenu() {
|
hideMenu() {
|
||||||
this.state = 'hidden';
|
this.state = 'hidden';
|
||||||
this.el.classList.remove(CLS.visible);
|
this.container.setAttribute('aria-expanded', true);
|
||||||
|
this.container.classList.remove(STATES.visible);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
114
js/menu.spec.js
Normal file
114
js/menu.spec.js
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import Menu from './menu';
|
||||||
|
|
||||||
|
jest.useFakeTimers();
|
||||||
|
|
||||||
|
const menuHtml = `
|
||||||
|
<button class="Menu">
|
||||||
|
<span class="Menu-line"></span>
|
||||||
|
<span class="Menu-line"></span>
|
||||||
|
<span class="Menu-line"></span>
|
||||||
|
<span class="Menu-text">Open menu</span>
|
||||||
|
</button>`;
|
||||||
|
|
||||||
|
describe('Menu button', () => {
|
||||||
|
let menu;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
document.body.innerHTML = menuHtml;
|
||||||
|
menu = new Menu({
|
||||||
|
container: document.querySelector('.Menu'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should initialise', () => {
|
||||||
|
menu.init();
|
||||||
|
expect(menu).toMatchObject({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should fail to initialise if container is missing', () => {
|
||||||
|
menu.init();
|
||||||
|
expect((new Menu()).state).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should default to visible state', () => {
|
||||||
|
expect(menu.state).toBe('visible');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should be hidden initially', () => {
|
||||||
|
menu.init();
|
||||||
|
expect(menu.container.classList.contains('is-visible')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
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');
|
||||||
|
menu.updateState(0, 10);
|
||||||
|
expect(menu.state).toBe('hidden');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('The state should change to minimized when scrolling down', () => {
|
||||||
|
menu.init();
|
||||||
|
menu.showMenu();
|
||||||
|
menu.updateState(0, 10);
|
||||||
|
expect(menu.state).toBe('minimized');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('The state should change to visible when scrolling up', () => {
|
||||||
|
menu.init();
|
||||||
|
menu.minimizeMenu();
|
||||||
|
menu.updateState(10, 0);
|
||||||
|
expect(menu.state).toBe('visible');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should have initial ARIA attributes', () => {
|
||||||
|
menu.init();
|
||||||
|
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', () => {
|
||||||
|
menu.init();
|
||||||
|
menu.showMenu();
|
||||||
|
expect(menu.container.getAttribute('aria-expanded')).toBe('false');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should have updated ARIA attribute when menu is closed', () => {
|
||||||
|
menu.init();
|
||||||
|
menu.showMenu();
|
||||||
|
menu.hideMenu();
|
||||||
|
expect(menu.container.getAttribute('aria-expanded')).toBe('true');
|
||||||
|
});
|
||||||
|
});
|
||||||
31
js/nav.js
31
js/nav.js
@@ -1,31 +0,0 @@
|
|||||||
const CLS = {
|
|
||||||
open: 'is-open',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class Nav {
|
|
||||||
constructor(cfg) {
|
|
||||||
this.el = cfg.el;
|
|
||||||
this.menu = cfg.menu;
|
|
||||||
|
|
||||||
this.attachEvents();
|
|
||||||
}
|
|
||||||
|
|
||||||
attachEvents() {
|
|
||||||
this.menu.el.addEventListener('click', () => {
|
|
||||||
this.menu.hideMenu();
|
|
||||||
this.openNav();
|
|
||||||
});
|
|
||||||
this.el.querySelector('.Nav-close').addEventListener('click', () => {
|
|
||||||
this.menu.showMenu();
|
|
||||||
this.closeNav();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
openNav() {
|
|
||||||
this.el.classList.add(CLS.open);
|
|
||||||
}
|
|
||||||
|
|
||||||
closeNav() {
|
|
||||||
this.el.classList.remove(CLS.open);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
49
js/navigation.js
Normal file
49
js/navigation.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
const STATES = {
|
||||||
|
open: 'is-open',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class Navigation {
|
||||||
|
constructor(cfg = {}) {
|
||||||
|
if (!cfg.container) return false;
|
||||||
|
if (!cfg.menu) return false;
|
||||||
|
this.state = 'hidden';
|
||||||
|
this.container = cfg.container;
|
||||||
|
this.menu = cfg.menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.container.setAttribute('aria-hidden', true);
|
||||||
|
this.container.querySelector('.Nav-list').setAttribute('role', 'menubar');
|
||||||
|
this.container.querySelectorAll('.Nav-listItem').forEach((item) => {
|
||||||
|
// Override implied listitem role
|
||||||
|
item.setAttribute('role', 'none');
|
||||||
|
});
|
||||||
|
this.container.querySelectorAll('.Nav-link').forEach((link) => {
|
||||||
|
link.setAttribute('role', 'menuitem');
|
||||||
|
});
|
||||||
|
this.attachEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
attachEvents() {
|
||||||
|
this.menu.container.addEventListener('click', () => {
|
||||||
|
this.openNav();
|
||||||
|
});
|
||||||
|
this.container.querySelector('.Nav-close').addEventListener('click', () => {
|
||||||
|
this.closeNav();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
openNav() {
|
||||||
|
this.state = 'visible';
|
||||||
|
this.menu.hideMenu();
|
||||||
|
this.container.classList.add(STATES.open);
|
||||||
|
this.container.setAttribute('aria-hidden', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
closeNav() {
|
||||||
|
this.state = 'hidden';
|
||||||
|
this.menu.showMenu();
|
||||||
|
this.container.classList.remove(STATES.open);
|
||||||
|
this.container.setAttribute('aria-hidden', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
96
js/navigation.spec.js
Normal file
96
js/navigation.spec.js
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
import Navigation from './navigation';
|
||||||
|
|
||||||
|
const navHtml = `<nav class="Nav">
|
||||||
|
<button class="Menu">Menu</button>
|
||||||
|
<div class="Nav-content">
|
||||||
|
<h2 class="Nav-title">WindyCOYS</h2>
|
||||||
|
<ul class="Nav-list">
|
||||||
|
<li class="Nav-listItem">
|
||||||
|
<a class="Nav-link" href="/">Home</a>
|
||||||
|
</li>
|
||||||
|
<li class="Nav-listItem">
|
||||||
|
<a class="Nav-link" href="/about">About</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<button class="Nav-close">close</button>
|
||||||
|
</nav>`;
|
||||||
|
|
||||||
|
let nav;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
document.body.innerHTML = navHtml;
|
||||||
|
// Mock Menu
|
||||||
|
const menu = {
|
||||||
|
container: document.querySelector('.Menu'),
|
||||||
|
showMenu: jest.fn(),
|
||||||
|
hideMenu: jest.fn(),
|
||||||
|
};
|
||||||
|
nav = new Navigation({
|
||||||
|
container: document.querySelector('.Nav'),
|
||||||
|
menu,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Navigation', () => {
|
||||||
|
it('Should initialise', () => {
|
||||||
|
nav.init();
|
||||||
|
expect(nav).toMatchObject({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should fail to initialise if container is missing', () => {
|
||||||
|
nav.init();
|
||||||
|
expect((new Navigation()).state).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should be hidden by default', () => {
|
||||||
|
nav.init();
|
||||||
|
expect(nav.container.classList.contains('is-open')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should have initial ARIA attributes', () => {
|
||||||
|
nav.init();
|
||||||
|
expect(nav.container.getAttribute('aria-hidden')).toBe('true');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should update the ARIA attributes when visible', () => {
|
||||||
|
nav.init();
|
||||||
|
nav.openNav();
|
||||||
|
expect(nav.container.getAttribute('aria-hidden')).toBe('false');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should update the ARIA attributes when hidden', () => {
|
||||||
|
nav.init();
|
||||||
|
nav.openNav();
|
||||||
|
nav.closeNav();
|
||||||
|
expect(nav.container.getAttribute('aria-hidden')).toBe('true');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Navigation list', () => {
|
||||||
|
it('Should have initial ARIA attributes', () => {
|
||||||
|
nav.init();
|
||||||
|
const ul = nav.container.querySelector('.Nav-list');
|
||||||
|
expect(ul.getAttribute('role')).toBe('menubar');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Navigation list items', () => {
|
||||||
|
it('Should have initial ARIA attributes', () => {
|
||||||
|
nav.init();
|
||||||
|
const lis = nav.container.querySelectorAll('.Nav-listItem');
|
||||||
|
lis.forEach((li) => {
|
||||||
|
expect(li.getAttribute('role')).toBe('none');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Navigation links', () => {
|
||||||
|
it('Should have initial ARIA attributes', () => {
|
||||||
|
nav.init();
|
||||||
|
const links = nav.container.querySelectorAll('.Nav-link');
|
||||||
|
links.forEach((link) => {
|
||||||
|
expect(link.getAttribute('role')).toBe('menuitem');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
16
js/windy.js
Normal file
16
js/windy.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import Menu from './menu';
|
||||||
|
import Navigation from './navigation';
|
||||||
|
|
||||||
|
const windy = {};
|
||||||
|
|
||||||
|
export default function init() {
|
||||||
|
windy.menu = new Menu({
|
||||||
|
container: document.querySelector('.Menu'),
|
||||||
|
});
|
||||||
|
windy.navigation = new Navigation({
|
||||||
|
container: document.querySelector('.Nav'),
|
||||||
|
menu: windy.menu,
|
||||||
|
});
|
||||||
|
windy.menu.init();
|
||||||
|
windy.navigation.init();
|
||||||
|
}
|
||||||
4600
package-lock.json
generated
4600
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
67
package.json
67
package.json
@@ -2,59 +2,76 @@
|
|||||||
"name": "windycoys",
|
"name": "windycoys",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "Wordpress theme for windycoys.com",
|
"description": "Wordpress theme for windycoys.com",
|
||||||
|
"author": "Dan Head <me@danielhead.com>",
|
||||||
|
"license": "MIT",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/danhead/windycoys"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npm run css:compile && npm run js:compile",
|
"build": "npm run css:compile && npm run js:compile",
|
||||||
"css": "run-p css:*",
|
"css": "run-p 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 --config .stylelintrc \"css/**/*.css\"",
|
||||||
|
"jest": "jest",
|
||||||
|
"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 js/",
|
||||||
"lint": "npm run css:lint && npm run js:lint",
|
|
||||||
"serve": "browser-sync start --config .browser-sync.js",
|
"serve": "browser-sync start --config .browser-sync.js",
|
||||||
"start": "run-p watch serve",
|
"start": "run-p watch serve",
|
||||||
"watch": "npm-watch"
|
"test": "npm run css:lint && npm run js:lint && npm run jest",
|
||||||
|
"watch": "run-p watch:*",
|
||||||
|
"watch:css": "nodemon -q --watch css -e css --exec npm run css",
|
||||||
|
"watch:js": "nodemon -q --watch js -e js --exec npm run js"
|
||||||
},
|
},
|
||||||
"author": "Dan Head <me@danielhead.com>",
|
|
||||||
"license": "MIT",
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"browser-sync": "^2.23.6",
|
"babel-core": "^7.0.0-bridge.0",
|
||||||
"eslint": "^4.17.0",
|
"babel-jest": "^22.4.3",
|
||||||
|
"browser-sync": "^2.23.7",
|
||||||
|
"eslint": "^4.19.1",
|
||||||
"eslint-config-airbnb-base": "^12.1.0",
|
"eslint-config-airbnb-base": "^12.1.0",
|
||||||
"eslint-plugin-import": "^2.8.0",
|
"eslint-plugin-import": "^2.11.0",
|
||||||
|
"jest": "^22.4.3",
|
||||||
|
"nodemon": "^1.17.3",
|
||||||
"npm-run-all": "^4.1.2",
|
"npm-run-all": "^4.1.2",
|
||||||
"npm-watch": "^0.3.0",
|
|
||||||
"stylelint": "^8.4.0",
|
"stylelint": "^8.4.0",
|
||||||
"stylelint-config-standard": "^18.0.0",
|
"stylelint-config-standard": "^18.0.0",
|
||||||
"stylelint-selector-bem-pattern": "^2.0.0"
|
"stylelint-selector-bem-pattern": "^2.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/cli": "^7.0.0-beta.40",
|
"@babel/cli": "^7.0.0-beta.44",
|
||||||
"@babel/core": "^7.0.0-beta.40",
|
"@babel/core": "^7.0.0-beta.44",
|
||||||
"@babel/preset-env": "^7.0.0-beta.40",
|
"@babel/preset-env": "^7.0.0-beta.44",
|
||||||
"autoprefixer": "^7.2.5",
|
"autoprefixer": "^7.2.5",
|
||||||
"cssnano": "^3.10.0",
|
"cssnano": "^3.10.0",
|
||||||
"normalize.css": "^7.0.0",
|
"normalize.css": "^8.0.0",
|
||||||
|
"postcss-banner": "^3.0.0",
|
||||||
"postcss-cli": "^4.1.1",
|
"postcss-cli": "^4.1.1",
|
||||||
"postcss-custom-media": "^6.0.0",
|
"postcss-custom-media": "^6.0.0",
|
||||||
"postcss-custom-properties": "^6.2.0",
|
"postcss-custom-properties": "^6.2.0",
|
||||||
"postcss-import": "^11.0.0",
|
"postcss-import": "^11.0.0",
|
||||||
"rollup": "^0.55.5",
|
"rollup": "^0.58.1",
|
||||||
"rollup-plugin-babel": "^4.0.0-beta.2",
|
"rollup-plugin-babel": "^4.0.0-beta.4",
|
||||||
"rollup-plugin-uglify": "^3.0.0"
|
"rollup-plugin-uglify": "^3.0.0"
|
||||||
},
|
},
|
||||||
"watch": {
|
"browserslist": [
|
||||||
"css": {
|
"Chrome >= 58",
|
||||||
"patterns": [
|
"Firefox >= 52",
|
||||||
"css"
|
"Edge >= 12",
|
||||||
|
"Explorer >= 11",
|
||||||
|
"iOS >= 10",
|
||||||
|
"Safari >= 10",
|
||||||
|
"Android >= 62",
|
||||||
|
"Opera >= 44"
|
||||||
],
|
],
|
||||||
"extensions": "css"
|
"jest": {
|
||||||
},
|
"collectCoverage": true,
|
||||||
"js": {
|
"collectCoverageFrom": [
|
||||||
"patterns": [
|
"js/**/*.js"
|
||||||
|
],
|
||||||
|
"roots": [
|
||||||
"js"
|
"js"
|
||||||
],
|
]
|
||||||
"extensions": "js"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,30 @@
|
|||||||
|
const { resolve } = require('path');
|
||||||
|
|
||||||
|
const pkg = require(resolve(__dirname, 'package.json'));
|
||||||
|
const year = new Date().getFullYear();
|
||||||
|
|
||||||
|
const banner = `Theme Name: WindyCOYS
|
||||||
|
Theme URI: https://windycoys.com/about
|
||||||
|
Author: ${pkg.author}
|
||||||
|
Author URI: https://danielhead.com
|
||||||
|
Description: Theme for windycoys.com
|
||||||
|
Version: ${pkg.version}
|
||||||
|
Copyright: (c) ${year} ${pkg.author}
|
||||||
|
License: MIT
|
||||||
|
License URI: https://choosealicense.com/licenses/mit/`;
|
||||||
|
|
||||||
|
|
||||||
module.exports = ctx => ({
|
module.exports = ctx => ({
|
||||||
map: ctx.env !== 'production',
|
map: ctx.env !== 'production',
|
||||||
plugins: {
|
plugins: {
|
||||||
'postcss-import': {},
|
'postcss-import': {},
|
||||||
'postcss-custom-media': {},
|
'postcss-custom-media': {},
|
||||||
'postcss-custom-properties': {},
|
'postcss-custom-properties': {},
|
||||||
'autoprefixer': {},
|
autoprefixer: {},
|
||||||
'cssnano': ctx.env === 'production' ? {} : false,
|
cssnano: ctx.env === 'production' ? {} : false,
|
||||||
|
'postcss-banner': {
|
||||||
|
banner,
|
||||||
|
important: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
const { resolve } = require('path');
|
const { resolve } = require('path');
|
||||||
const babel = require('rollup-plugin-babel');
|
const babel = require('rollup-plugin-babel');
|
||||||
const uglify = require('rollup-plugin-uglify');
|
const uglify = require('rollup-plugin-uglify');
|
||||||
|
const pkg = require('./package.json');
|
||||||
|
|
||||||
|
const year = new Date().getFullYear();
|
||||||
|
|
||||||
const production = process.env.NODE_ENV === 'production';
|
const production = process.env.NODE_ENV === 'production';
|
||||||
|
|
||||||
@@ -8,9 +11,20 @@ module.exports = {
|
|||||||
input: resolve(__dirname, 'js/index.js'),
|
input: resolve(__dirname, 'js/index.js'),
|
||||||
output: {
|
output: {
|
||||||
file: resolve(__dirname, 'theme/bundle.js'),
|
file: resolve(__dirname, 'theme/bundle.js'),
|
||||||
format: 'iife',
|
format: 'umd',
|
||||||
name: 'windycoys',
|
name: 'windy',
|
||||||
sourcemap: !production && 'inline',
|
sourcemap: !production && 'inline',
|
||||||
|
banner: `/**!
|
||||||
|
* Theme Name: WindyCOYS
|
||||||
|
* Theme URI: https://windycoys.com/about
|
||||||
|
* Author: ${pkg.author}
|
||||||
|
* Author URI: https://danielhead.com
|
||||||
|
* Description: Theme for windycoys.com
|
||||||
|
* Version: ${pkg.version}
|
||||||
|
* Copyright: (c) ${year} ${pkg.author}
|
||||||
|
* License: MIT
|
||||||
|
* License URI: https://choosealicense.com/licenses/mit/
|
||||||
|
*/`,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
babel({
|
babel({
|
||||||
|
|||||||
@@ -35,6 +35,5 @@
|
|||||||
<?php get_template_part('partials/footer') ?>
|
<?php get_template_part('partials/footer') ?>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
<?php get_template_part('partials/menu') ?>
|
<?php get_template_part('partials/navigation') ?>
|
||||||
<?php get_template_part('partials/nav') ?>
|
|
||||||
<?php get_footer() ?>
|
<?php get_footer() ?>
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
<button class="Menu">
|
|
||||||
<span class="Menu-line"></span>
|
|
||||||
<span class="Menu-line"></span>
|
|
||||||
<span class="Menu-line"></span>
|
|
||||||
<span class="Menu-text">Open menu</span>
|
|
||||||
</button>
|
|
||||||
@@ -1,4 +1,10 @@
|
|||||||
<nav class="Nav">
|
<nav class="Nav" aria-label="Main Navigation">
|
||||||
|
<button class="Menu">
|
||||||
|
<span class="Menu-line"></span>
|
||||||
|
<span class="Menu-line"></span>
|
||||||
|
<span class="Menu-line"></span>
|
||||||
|
<span class="Menu-text">Open menu</span>
|
||||||
|
</button>
|
||||||
<div class="Nav-content">
|
<div class="Nav-content">
|
||||||
<h2 class="Nav-title">WindyCOYS</h2>
|
<h2 class="Nav-title">WindyCOYS</h2>
|
||||||
<ul class="Nav-list">
|
<ul class="Nav-list">
|
||||||
Reference in New Issue
Block a user