This repository has been archived by the owner on Feb 13, 2019. It is now read-only.
<script is="lazy-template"> (ready for review) #165
Merged
Merged
Changes from 4 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
d4c1e35
spike: <script is="lazy-template">
collin 8d5287e
first critical test of lazy-template
collin 9e19880
clarified the reason of this bizarre stylesheet
collin 2a38061
fuller tests around lazy-template
collin 571766a
Remove noop teardown callback
collin 00b3ec3
fix detached callback syntax
collin File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
export default { | ||
element: 'lazy-template', | ||
examples: { | ||
'Load On Page Load': { | ||
render () { | ||
return ` | ||
<script type="text/html" is="lazy-template" load-on="page-load"> | ||
<h1>Damn Son, I Am Lazy Loaded</h1> | ||
<img src="http://www.fillmurray.com/g/200/300" alt="Billy Murray"> | ||
</script> | ||
`; | ||
}, | ||
}, | ||
'Load On In View': { | ||
render () { | ||
return ` | ||
<div | ||
style=" | ||
overflow: auto; | ||
position: absolute; | ||
top: 0; | ||
left: 0; | ||
bottom: 0; | ||
right: 0; | ||
" | ||
> | ||
<div style="height: 200%">SCROLL DOWN TO LOAD STUFF</div> | ||
<script type="text/html" is="lazy-template" load-on="in-view"> | ||
<h1>Damn Son, I Am Lazy Loaded</h1> | ||
<img src="http://www.fillmurray.com/g/200/300" alt="Billy Murray"> | ||
</script> | ||
</div> | ||
`; | ||
}, | ||
}, | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import { registerElement } from 'bulbs-elements/register'; | ||
import { InViewMonitor } from 'bulbs-elements/util'; | ||
import invariant from 'invariant'; | ||
import './lazy-template.scss'; | ||
|
||
function BulbsHTMLScriptElement () {} | ||
BulbsHTMLScriptElement.prototype = HTMLScriptElement.prototype; | ||
|
||
class LazyTemplate extends BulbsHTMLScriptElement { | ||
get loadOn () { | ||
return this.getAttribute('load-on'); | ||
} | ||
|
||
attachedCallback () { | ||
invariant(this.hasAttribute('load-on'), | ||
'<script is="lazy-template"> MUST specify a "load-on" attribute (either "page-load" or "in-view").'); | ||
|
||
invariant(this.getAttribute('type') === 'text/html', | ||
'<script is="lazy-template"> MUST set the attribute type="text/html".'); | ||
|
||
if (this.loadOn === 'page-load') { | ||
this.setupLoadOnPageLoad(); | ||
} | ||
else if (this.loadOn === 'in-view') { | ||
this.setUpLoadOnInView(); | ||
} | ||
|
||
this.replaceWithContents = this.replaceWithContents.bind(this); | ||
this.handleEnterViewport = this.handleEnterViewport.bind(this); | ||
} | ||
|
||
detachedCallback () { | ||
if (this.loadOn === 'page-load') { | ||
this.tearDownLoadOnPageLoad(); | ||
} | ||
else if (this.loadOn === 'in-view') { | ||
this.tearDownLoadOnInView(); | ||
} | ||
|
||
} | ||
|
||
setupLoadOnPageLoad () { | ||
if (document.readyState === 'complete') { | ||
this.replaceWithContents(); | ||
} | ||
else { | ||
window.addEventListener('load', () => this.replaceWithContents()); | ||
} | ||
} | ||
|
||
setUpLoadOnInView () { | ||
InViewMonitor.add(this); | ||
this.addEventListener('enterviewport', this.handleEnterViewport); | ||
} | ||
|
||
tearDownLoadOnPageLoad () { | ||
} | ||
|
||
handleEnterViewport () { | ||
InViewMonitor.remove(this); | ||
this.replaceWithContents(); | ||
} | ||
|
||
tearDownLoadOnInView () { | ||
InViewMonitor.remove(this); | ||
} | ||
|
||
replaceWithContents () { | ||
this.outerHTML = this.textContent; | ||
} | ||
} | ||
|
||
LazyTemplate.extends = 'script'; | ||
|
||
registerElement('lazy-template', LazyTemplate); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// we're using getBoundingClientRect to determine when our script | ||
// is in-view. Typically scripts are not part of the render tree | ||
// and their client rect is not meaningful. We can force them to be | ||
// an invisible 0x0 block element like this. Then we can meaninfully | ||
// track their position. | ||
script[is="lazy-template"] { | ||
display: block; | ||
width: 0px; | ||
height: 0px; | ||
overflow: hidden; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import './lazy-template'; | ||
|
||
describe.only('<script is="lazy-template">', () => { | ||
let sandbox; | ||
let subject; | ||
let container; | ||
|
||
beforeEach(() => { | ||
sandbox = sinon.sandbox.create(); | ||
subject = makeTemplate(` | ||
<h1>Cool Template</h1> | ||
<p>It is lazy!</p> | ||
`); | ||
container = document.createElement('div'); | ||
document.body.appendChild(container); | ||
}); | ||
|
||
afterEach(() => { | ||
sandbox.restore(); | ||
container.remove(); | ||
}); | ||
|
||
function makeTemplate (innerHTML) { | ||
let template = document.createElement('script', 'lazy-template'); | ||
template.setAttribute('type', 'text/html'); | ||
template.setAttribute('load-on', 'page-load'); | ||
template.innerHTML = innerHTML; | ||
return template; | ||
} | ||
|
||
it('requires a load-on attribute', () => { | ||
subject.removeAttribute('load-on'); | ||
expect(() => { | ||
subject.attachedCallback(); | ||
}).to.throw('<script is="lazy-template"> MUST specify a "load-on" attribute (either "page-load" or "in-view").'); | ||
}); | ||
|
||
it('requires a type set to "text/html"', () => { | ||
subject.setAttribute('type', 'text/whatever'); | ||
expect(() => { | ||
subject.attachedCallback(); | ||
}).to.throw('<script is="lazy-template"> MUST set the attribute type="text/html".'); | ||
}); | ||
|
||
it('requires a type attribute', () => { | ||
subject.removeAttribute('type'); | ||
expect(() => { | ||
subject.attachedCallback(); | ||
}).to.throw('<script is="lazy-template"> MUST set the attribute type="text/html".'); | ||
}); | ||
|
||
describe('replaceWithContents', () => { | ||
it('replaces itself with the content html', () => { | ||
container = document.createElement('div'); | ||
container.appendChild(subject); | ||
subject.replaceWithContents(); | ||
|
||
expect(container.children[0].outerHTML).to.eql('<h1>Cool Template</h1>'); | ||
expect(container.children[1].outerHTML).to.eql('<p>It is lazy!</p>'); | ||
}); | ||
}); | ||
|
||
context('load-on="page-load"', () => { | ||
beforeEach(() => subject.setAttribute('load-on', 'page-load')); | ||
|
||
xit('sets content when page load event fires', () => { | ||
// document.readyState is 'complete' by the time this test starts | ||
// and it can't be overwritten, not sure how to test this | ||
}); | ||
|
||
it('sets content immediately if page load event has fired', (done) => { | ||
container.appendChild(subject); | ||
|
||
setImmediate(() => { | ||
expect(container.children[0].outerHTML).to.eql('<h1>Cool Template</h1>'); | ||
expect(container.children[1].outerHTML).to.eql('<p>It is lazy!</p>'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
|
||
context('load-on="in-view"', () => { | ||
beforeEach(() => subject.setAttribute('load-on', 'in-view')); | ||
|
||
it('sets content when enterviewport event fires', (done) => { | ||
container.appendChild(subject); | ||
|
||
setImmediate(() => { | ||
expect(container.children[0].tagName).to.eql('SCRIPT'); | ||
subject.dispatchEvent(new CustomEvent('enterviewport')); | ||
expect(container.children[0].outerHTML).to.eql('<h1>Cool Template</h1>'); | ||
expect(container.children[1].outerHTML).to.eql('<p>It is lazy!</p>'); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to leave this here if it's a no-op?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch.