Commit 48b444dd authored by Dmitry Gozman's avatar Dmitry Gozman Committed by Commit Bot

[DevTools] Introduce UI.Fragment

Fragment is a piece of DOM generated from a template string,
to be used instead of manual dom construction.

- Allows functions ("binds") to be executed during instantiation:
    <div ${(fragment, node) => node._owner = fragment} />.

- Exposes selected elements via $:
    <div $=myName />.

- Toggling states which affect arbitrary attributes:
    <div s-selected-attr=value /> changes the value of "attr" to "value".

- Inserting elements into markup:
    <div>Some ${myDiv} text</div>.

- Inserting text into markup:
    <div>Some ${myText} text</div>.

- Composed attribute names:
    <div ${foo ? 'foo' : 'bar'}-name=value />.

- Transforms <x-shadow> to shadow root:
    <div><x-shadow>dark dom here</x-shadow></div>.

- Caching preprocessed template if needed.

Bug: none
Change-Id: Ib1eb7c10b0f3f4aaebf98c022697111f38d55f9f
Reviewed-on: https://chromium-review.googlesource.com/809952Reviewed-by: default avatarAndrey Lushnikov <lushnikov@chromium.org>
Commit-Queue: Dmitry Gozman <dgozman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#523680}
parent 53edf03e
Tests how fragment works.
f1.outerHTML:
<div-a attr="val" class=""><div-c class="my-class-1 my-class-2" foo="bar">Some text here And more text</div-c>
<div></div></div-a>
() => diva === f1.element()
true
() => diva.tagName === 'DIV-A'
true
() => divb.tagName === 'DIV-B'
true
() => divc.tagName === 'DIV-C'
true
() => shadow.nodeType === Node.DOCUMENT_FRAGMENT_NODE
true
() => shadow.parentElementOrShadowHost() === diva
true
() => divc.parentNode === diva
true
() => divb.parentElementOrShadowHost() === diva
true
() => diva.lastChild === inner
true
() => divb.getAttribute('foo1') === 'bar1'
true
() => divb.getAttribute('foo2') === 'bar2'
true
() => divb.divb === true
true
() => divc.textContent === 'Some text here And more text'
true
() => divc.classList.contains('my-class-1')
true
() => divc.classList.contains('my-class-2')
true
() => divc.getAttribute('foo') === 'bar'
true
() => diva.getAttribute('attr') === 'val'
true
() => divb.getAttribute('attr') === null
true
() => diva.getAttribute('attr') === 'val-state1'
true
() => divb.getAttribute('attr') === 'val-state1'
true
() => diva.getAttribute('attr') === 'val-state1'
true
() => divb.getAttribute('attr') === 'val-state1'
true
() => diva.getAttribute('attr') === 'val'
true
() => divb.getAttribute('attr') === null
true
() => diva.getAttribute('attr') === 'val-state2'
true
() => divb.getAttribute('attr') === null
true
f2.outerHTML:
<div><div-a attr="val-state2" class=""><div-c class="my-class-1 my-class-2" foo="bar">Some text here And more text</div-c>
<div></div></div-a></div>
() => f2.element().firstChild === f1.element()
true
f3.outerHTML:
<div><div><div-a attr="val-state2" class=""><div-c class="my-class-1 my-class-2" foo="bar">Some text here And more text</div-c>
<div></div></div-a></div></div>
() => f3.element().firstChild === f2.element()
true
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
(async function() {
function check(f) {
TestRunner.addResult(f.toString() + '\n ' + f());
}
TestRunner.addResult(`Tests how fragment works.\n`);
var inner = document.createElement('div');
var f1 = UI.Fragment.build`
<div-a $=name-a attr=val s-state1-attr=val-state1 s-state2-attr=val-state2>
<x-shadow $=name-shadow>
<div-b $=name-b foo1=bar1 foo${'2'}=${'b'}ar${'2'} ${''} ${element => element.divb = true} s-state1-attr=val-state1>
</div-b>
</x-shadow>
<div-c $=name-c class='${'my-class-1'} my-class-2' ${'foo'}=bar>${'Some text here'} ${'And more text'}</div-c>
${inner}
</div-a>
`;
TestRunner.addResult('f1.outerHTML:');
TestRunner.addResult(f1.element().outerHTML);
var diva = f1.$('name-a');
var divb = f1.$('name-b');
var divc = f1.$('name-c');
var shadow = f1.$('name-shadow');
check(() => diva === f1.element());
check(() => diva.tagName === 'DIV-A');
check(() => divb.tagName === 'DIV-B');
check(() => divc.tagName === 'DIV-C');
check(() => shadow.nodeType === Node.DOCUMENT_FRAGMENT_NODE);
check(() => shadow.parentElementOrShadowHost() === diva);
check(() => divc.parentNode === diva);
check(() => divb.parentElementOrShadowHost() === diva);
check(() => diva.lastChild === inner);
check(() => divb.getAttribute('foo1') === 'bar1');
check(() => divb.getAttribute('foo2') === 'bar2');
check(() => divb.divb === true);
check(() => divc.textContent === 'Some text here And more text');
check(() => divc.classList.contains('my-class-1'));
check(() => divc.classList.contains('my-class-2'));
check(() => divc.getAttribute('foo') === 'bar');
check(() => diva.getAttribute('attr') === 'val');
check(() => divb.getAttribute('attr') === null);
f1.setState('state1', true);
check(() => diva.getAttribute('attr') === 'val-state1');
check(() => divb.getAttribute('attr') === 'val-state1');
f1.setState('state1', true);
check(() => diva.getAttribute('attr') === 'val-state1');
check(() => divb.getAttribute('attr') === 'val-state1');
f1.setState('state1', false);
check(() => diva.getAttribute('attr') === 'val');
check(() => divb.getAttribute('attr') === null);
f1.setState('state2', true);
check(() => diva.getAttribute('attr') === 'val-state2');
check(() => divb.getAttribute('attr') === null);
TestRunner.addResult('');
function cached(child) {
return UI.Fragment.cached`
<div>${child}</div>
`;
}
var f2 = cached(f1);
TestRunner.addResult('f2.outerHTML:');
TestRunner.addResult(f2.element().outerHTML);
check(() => f2.element().firstChild === f1.element());
TestRunner.addResult('');
var f3 = cached(f2);
TestRunner.addResult('f3.outerHTML:');
TestRunner.addResult(f3.element().outerHTML);
check(() => f3.element().firstChild === f2.element());
TestRunner.addResult('');
TestRunner.completeTest();
})();
......@@ -728,6 +728,7 @@ all_devtools_files = [
"front_end/ui/FilterBar.js",
"front_end/ui/FilterSuggestionBuilder.js",
"front_end/ui/ForwardedInputEventHandler.js",
"front_end/ui/Fragment.js",
"front_end/ui/Geometry.js",
"front_end/ui/glassPane.css",
"front_end/ui/GlassPane.js",
......
......@@ -232,6 +232,8 @@ Node.prototype.enclosingNodeOrSelfWithClassList = function(classNames, stayWithi
* @return {?Element}
*/
Node.prototype.parentElementOrShadowHost = function() {
if (this.nodeType === Node.DOCUMENT_FRAGMENT_NODE && this.host)
return /** @type {!Element} */ (this.host);
var node = this.parentNode;
if (!node)
return null;
......
This diff is collapsed.
......@@ -22,6 +22,7 @@
"FilterBar.js",
"FilterSuggestionBuilder.js",
"ForwardedInputEventHandler.js",
"Fragment.js",
"HistoryInput.js",
"Icon.js",
"Infobar.js",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment