Skip to content
This repository has been archived by the owner on Feb 13, 2019. It is now read-only.

Custom elements v1 #223

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"Promise": false,
"sinon": false,
"xdescribe": false,
"xit": false
"xit": false,
"customElements": false
},
"ecmaFeatures": {
"arrowFunctions": true,
Expand Down
2 changes: 1 addition & 1 deletion elements/bulbs-carousel/bulbs-carousel-state.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe('<bulbs-carousel> BulbsCarouselState', () => {
firstItem.style.transition = 'none'; // Kill animations for test

document.body.appendChild(container);
// document.registerElement polyfill runs on next microtask in some browsers
// customElements.define polyfill runs on next microtask in some browsers
// MUST wait until end of queue for elements to be constructed
setImmediate(() => {
subject = carousel.state;
Expand Down
17 changes: 8 additions & 9 deletions elements/bulbs-carousel/bulbs-carousel.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import BulbsCarouselState from './bulbs-carousel-state';
import './bulbs-carousel.scss';

export default class BulbsCarousel extends BulbsHTMLElement {
createdCallback () {
connectedCallback () {
invariant(
this.slider = this.querySelector('bulbs-carousel-slider'),
'<bulbs-carousel> MUST contain a <bulbs-carousel-slider> element.'
Expand All @@ -30,14 +30,13 @@ export default class BulbsCarousel extends BulbsHTMLElement {
this.previousButtons = this.getElementsByTagName('bulbs-carousel-previous');
this.nextButtons = this.getElementsByTagName('bulbs-carousel-next');

this.state = new BulbsCarouselState({
carousel: this,
carouselItems: this.track.children,
currentIndex: 0,
});
}

attachedCallback () {
if (!this.state) {
this.state = new BulbsCarouselState({
carousel: this,
carouselItems: this.track.children,
currentIndex: 0,
});
}
this.state.pageToCarouselItem(this.state.getActiveCarouselItem());
this.applyState();
}
Expand Down
69 changes: 35 additions & 34 deletions elements/bulbs-carousel/bulbs-carousel.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ describe('<bulbs-carousel>', () => {
firstItem.style.transition = 'none'; // Kill animations for test

document.body.appendChild(container);
// document.registerElement polyfill runs on next microtask in some browsers
// customElements.define polyfill runs on next microtask in some browsers
// MUST wait until end of queue for elements to be constructed
setImmediate(() => {
track = carousel.track;
Expand All @@ -69,7 +69,38 @@ describe('<bulbs-carousel>', () => {
document.body.removeChild(container);
});

describe('createdCallback', () => {
describe('conn', () => {
});

describe('connectedCallback', () => {
context('there is an active carousel item', () => {
beforeEach(() => {
secondItem.setAttribute(
'href', window.location.pathname
);
});

it('pages to the active item in the carousel', () => {
subject.connectedCallback();
expect(subject.state.pageToCarouselItem).to.have.been.calledWith(
secondItem
);
});
});

it('applies state', () => {
subject.connectedCallback();
expect(subject.applyState).to.have.been.called;
});
});

describe('onClick', () => {
beforeEach(() => {
sinon.spy(subject.state,'slideToPrevious');
sinon.spy(subject.state,'slideToNext');
sinon.spy(subject, 'stateChanged');
});

it('wraps slider content in a <bulbs-carousel-track>', () => {
expect(slider.childNodes).to.have.length(1);
expect(
Expand All @@ -91,48 +122,18 @@ describe('<bulbs-carousel>', () => {
beforeEach(() => slider.remove());

it('throws an execption', () => {
expect(() => subject.createdCallback()).to.throw(
expect(() => subject.connectedCallback()).to.throw(
/MUST contain a <bulbs-carousel-slider>/
);
});
});

context('called twice', () => {
it('does not create a second track', () => {
subject.createdCallback();
subject.connectedCallback();
expect(subject.querySelector('bulbs-carousel-track bulbs-carousel-track')).to.be.null;
});
});
});

describe('attachedCallback', () => {
context('there is an active carousel item', () => {
beforeEach(() => {
secondItem.setAttribute(
'href', window.location.pathname
);
});

it('pages to the active item in the carousel', () => {
subject.attachedCallback();
expect(subject.state.pageToCarouselItem).to.have.been.calledWith(
secondItem
);
});
});

it('applies state', () => {
subject.attachedCallback();
expect(subject.applyState).to.have.been.called;
});
});

describe('onClick', () => {
beforeEach(() => {
sinon.spy(subject.state,'slideToPrevious');
sinon.spy(subject.state,'slideToNext');
sinon.spy(subject, 'stateChanged');
});

context('click on previous button', () => {
beforeEach(() => previousButton.click());
Expand Down
4 changes: 2 additions & 2 deletions elements/bulbs-carousel/buttons.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { BulbsHTMLElement, registerElement } from 'bulbs-elements/register';

export class NextButton extends BulbsHTMLElement {
createdCallback () {
connectedCallback () {
this.innerHTML = '<i class="fa fa-angle-right"></i>';
}
}

export class PreviousButton extends BulbsHTMLElement {
createdCallback () {
connectedCallback () {
this.innerHTML = '<i class="fa fa-angle-left"></i>';
}
}
Expand Down
14 changes: 12 additions & 2 deletions elements/bulbs-carousel/buttons.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@ let subject;
describe('<bulbs-carousel-next>', () => {
beforeEach((done) => {
subject = document.createElement('bulbs-carousel-next');
// document.registerElement polyfill runs on next microtask in some browsers
document.body.appendChild(subject);
// customElements.define polyfill runs on next microtask in some browsers
// MUST wait until end of queue for elements to be constructed
setImmediate(done);
});

afterEach(() => {
subject.remove();
});

it('renders a next icon', () => {
expect(subject.firstChild.matches('i.fa.fa-angle-right')).to.be.true;
});
Expand All @@ -18,11 +23,16 @@ describe('<bulbs-carousel-next>', () => {
describe('<bulbs-carousel-previous>', () => {
beforeEach((done) => {
subject = document.createElement('bulbs-carousel-previous');
// document.registerElement polyfill runs on next microtask in some browsers
document.body.appendChild(subject);
// customElements.define polyfill runs on next microtask in some browsers
// MUST wait until end of queue for elements to be constructed
setImmediate(done);
});

afterEach(() => {
subject.remove();
});

it('renders a previous icon', () => {
expect(subject.firstChild.matches('i.fa.fa-angle-left')).to.be.true;
});
Expand Down
2 changes: 1 addition & 1 deletion elements/bulbs-carousel/item.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { BulbsHTMLElement, registerElement } from 'bulbs-elements/register';
import { moveChildren, copyAttribute } from 'bulbs-elements/util';

export default class CarouselItem extends BulbsHTMLElement {
createdCallback () {
connectedCallback () {
if (this.getAttribute('href')) {
let anchor = document.createElement('a');

Expand Down
2 changes: 1 addition & 1 deletion elements/bulbs-carousel/item.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ describe('<bulbs-carousel-item>', () => {
</bulbs-carousel-item>
`;
document.body.appendChild(container);
// document.registerElement polyfill runs on next microtask in some browsers
// customElements.define polyfill runs on next microtask in some browsers
// MUST wait until end of queue for elements to be constructed
setImmediate(() => {
subject = container.querySelector('bulbs-carousel-item');
Expand Down
24 changes: 7 additions & 17 deletions elements/bulbs-cinemagraph/bulbs-cinemagraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,28 @@ function BulbsHTMLVideoElement () {}
BulbsHTMLVideoElement.prototype = HTMLVideoElement.prototype;

class BulbsCinemagraph extends BulbsHTMLVideoElement {
createdCallback () {
get duration () {
return parseFloat(this.getAttribute('cinemagraph-duration') || 0);
}

connectedCallback () {
if (!this.hasAttribute('cinemagraph-duration')) {
console.warn('is="bulbs-cinemagraph" elements should have a [cinemagraph-duration] attribute set');
}

// makeVideoPlayableInline is dumb and goes just a little bit too far in the
// video, this results in a quick flash of a black frame in the loop. If we
// override the duration we can get the loop to loop properly.
// For now, this must be determined by hand.
Object.defineProperty(this, 'duration', {
get () {
return parseFloat(this.getAttribute('cinemagraph-duration')) || 0;
},
});

this.setAttribute('loop', true);
this.setAttribute('webkit-playsinline', true);

this.addEventListener('enterviewport', () => this.play());
this.addEventListener('exitviewport', () => this.pause());
}

attachedCallback () {
iphoneInlineVideo.default(this, /* hasAudio */ false);
InViewMonitor.add(this);
}

detachedCallback () {
disconnectedCallback () {
InViewMonitor.remove(this);
}
}

BulbsCinemagraph.extends = 'video';

registerElement('bulbs-cinemagraph', BulbsCinemagraph);
registerElement('bulbs-cinemagraph', BulbsCinemagraph, { extends: 'video' });
13 changes: 7 additions & 6 deletions elements/bulbs-cinemagraph/bulbs-cinemagraph.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import * as iphoneInlineVideo from 'iphone-inline-video';
describe('<video is="bulbs-cinemagraph">', () => {
let subject;

beforeEach(() => {
beforeEach((done) => {
subject = document.createElement('video', 'bulbs-cinemagraph');
document.body.appendChild(subject);
sinon.spy(InViewMonitor, 'add');
sinon.spy(InViewMonitor, 'remove');
sinon.spy(subject, 'pause');
sinon.spy(subject, 'play');
setImmediate(() => done());
});

afterEach(() => {
Expand All @@ -33,24 +34,24 @@ describe('<video is="bulbs-cinemagraph">', () => {
expect(subject.duration).to.eql(4.55);
});

describe('attachedCallback', () => {
describe('connectedCallback', () => {
// Can't actually spy on the export like this.
// Any testing ideas welcome.
xit('calls makeVideoPlayableInline', () => {
let spy = sinon.spy(iphoneInlineVideo, 'default');
subject.attachedCallback();
subject.connectedCallback();
expect(spy).to.have.been.called;
});

it('registers with the InViewMonitor', () => {
subject.attachedCallback();
subject.connectedCallback();
expect(InViewMonitor.add).to.have.been.called.once;
});
});

describe('detachedCallback', () => {
describe('disconnectedCallback', () => {
it('removes selve from InViewMonitor', () => {
subject.detachedCallback();
subject.disconnectedCallback();
expect(InViewMonitor.remove).to.have.been.called.once;
});
});
Expand Down
2 changes: 1 addition & 1 deletion elements/bulbs-clickventure/bulbs-clickventure.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ function collapseResponsiveMenu () {
}

class BulbsClickventure extends BulbsHTMLElement {
attachedCallback () {
connectedCallback () {
let $element = $(this);
$element.data('clickventurePlugin', new Clickventure($element));
$element.on('clickventure-page-change', fireAnalytics);
Expand Down
2 changes: 1 addition & 1 deletion elements/bulbs-content/bulbs-content.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { BulbsHTMLElement, registerElement } from 'bulbs-elements/register';
import './bulbs-content.scss';

class BulbsContent extends BulbsHTMLElement {
attachedCallback () {
connectedCallback () {
if (!this.hasDispatchedReadyEvent) {
let event = new CustomEvent('bulbs-content-ready');
this.dispatchEvent(event);
Expand Down
6 changes: 3 additions & 3 deletions elements/bulbs-content/bulbs-content.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ describe('<bulbs-content>', () => {
let spy = sinon.spy();
let subject = document.createElement('bulbs-content');
subject.addEventListener('bulbs-content-ready', spy);
subject.attachedCallback();
subject.connectedCallback();

expect(spy).to.have.been.called.once;
});
Expand All @@ -14,8 +14,8 @@ describe('<bulbs-content>', () => {
let spy = sinon.spy();
let subject = document.createElement('bulbs-content');
subject.addEventListener('bulbs-content-ready', spy);
subject.attachedCallback();
subject.attachedCallback();
subject.connectedCallback();
subject.connectedCallback();

expect(spy).to.have.been.called.once;
});
Expand Down
2 changes: 1 addition & 1 deletion elements/bulbs-datamap/bulbs-datamap.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
import './bulbs-datamap.scss';

class BulbsDatamap extends BulbsHTMLElement {
createdCallback () {
connectedCallback () {
this.innerHTML = '<div class="bulbs-datamap"></div>';
let mapContainer = this.firstChild;
let that = this;
Expand Down
5 changes: 5 additions & 0 deletions elements/bulbs-datamap/bulbs-datamap.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@ describe('<bulbs-datamap>', function () {

beforeEach(function (done) {
element = document.createElement('bulbs-datamap');
document.body.appendChild(element);
setImmediate(done);
});

afterEach(() => {
element.remove();
});

it('renders an <bulbs-datamap>', function () {
assert.equal(element.tagName.toLowerCase(), 'bulbs-datamap');
});
Expand Down
8 changes: 3 additions & 5 deletions elements/bulbs-dfp/bulbs-dfp.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
} from 'bulbs-elements/register';

export default class BulbsDfp extends BulbsHTMLElement {
attachedCallback () {
connectedCallback () {

invariant(this.hasAttribute('viewport-threshold'),
'<div is="bulbs-dfp"> MUST specify a `viewport-threshold` property. ' +
Expand Down Expand Up @@ -41,7 +41,7 @@ export default class BulbsDfp extends BulbsHTMLElement {
}
}

detachedCallback () {
disconnectedCallback () {
util.InViewMonitor.remove(this);

if (this.refreshInterval) {
Expand Down Expand Up @@ -92,6 +92,4 @@ export default class BulbsDfp extends BulbsHTMLElement {
}
}

BulbsDfp.extends = 'div';

registerElement('bulbs-dfp', BulbsDfp);
registerElement('bulbs-dfp', BulbsDfp, { extends: 'div' });
Loading