Deprecate Gerrit.css() and provide a replacement
Gerrit.css doesn't work with ShadowDom, because it adds styles to a document. This fix provides a replacement for Gerrit.css. The replacement allows to apply the same styles to any element inside document. Bug: Issue 11298 Change-Id: Ide325a889a69bd267382a9664ed6f8d25ed67a3b
This commit is contained in:
parent
54ca082f42
commit
941ee630d8
@ -713,10 +713,12 @@ accessed through this name.
|
|||||||
|
|
||||||
[[Gerrit_css]]
|
[[Gerrit_css]]
|
||||||
=== Gerrit.css()
|
=== Gerrit.css()
|
||||||
|
[WARNING]
|
||||||
|
This method is deprecated. It doesn't work with Shadow DOM and
|
||||||
|
will be removed in the future. Please, use link:pg-plugin-dev.html#plugin-styles[plugin.styles] instead.
|
||||||
|
|
||||||
Creates a new unique CSS class and injects it into the document.
|
Creates a new unique CSS class and injects it into the document.
|
||||||
The name of the class is returned and can be used by the plugin.
|
The name of the class is returned and can be used by the plugin.
|
||||||
See link:#Gerrit_html[`Gerrit.html()`] for an easy way to use
|
|
||||||
generated class names.
|
|
||||||
|
|
||||||
Classes created with this function should be created once at install
|
Classes created with this function should be created once at install
|
||||||
time and reused throughout the plugin. Repeatedly creating the same
|
time and reused throughout the plugin. Repeatedly creating the same
|
||||||
@ -814,112 +816,6 @@ If the URL passed matches `http://...`, `https://...`, or `//...`
|
|||||||
the current browser window will navigate to the non-Gerrit URL.
|
the current browser window will navigate to the non-Gerrit URL.
|
||||||
The user can return to Gerrit with the back button.
|
The user can return to Gerrit with the back button.
|
||||||
|
|
||||||
[[Gerrit_html]]
|
|
||||||
=== Gerrit.html()
|
|
||||||
Parses an HTML fragment after performing template replacements. If
|
|
||||||
the HTML has a single root element or node that node is returned,
|
|
||||||
otherwise it is wrapped inside a `<div>` and the div is returned.
|
|
||||||
|
|
||||||
.Signature
|
|
||||||
[source,javascript]
|
|
||||||
----
|
|
||||||
Gerrit.html(htmlText, options, wantElements);
|
|
||||||
----
|
|
||||||
|
|
||||||
* htmlText: string of HTML to be parsed. A new unattached `<div>` is
|
|
||||||
created in the browser's document and the innerHTML property is
|
|
||||||
assigned to the passed string, after performing replacements. If
|
|
||||||
the div has exactly one child, that child will be returned instead
|
|
||||||
of the div.
|
|
||||||
|
|
||||||
* options: optional object reference supplying replacements for any
|
|
||||||
`{name}` references in htmlText. Navigation through objects is
|
|
||||||
supported permitting `{style.bar}` to be replaced with `"foo"` if
|
|
||||||
options was `{style: {bar: "foo"}}`. Value replacements are HTML
|
|
||||||
escaped before being inserted into the document fragment.
|
|
||||||
|
|
||||||
* wantElements: if options is given and wantElements is also true
|
|
||||||
an object consisting of `{root: parsedElement, elements: {...}}` is
|
|
||||||
returned instead of the parsed element. The elements object contains
|
|
||||||
a property for each element using `id={name}` in htmlText.
|
|
||||||
|
|
||||||
.Example
|
|
||||||
[source,javascript]
|
|
||||||
----
|
|
||||||
var style = {bar: Gerrit.css('background: yellow')};
|
|
||||||
Gerrit.html(
|
|
||||||
'<span class="{style.bar}">Hello {name}!</span>',
|
|
||||||
{style: style, name: "World"});
|
|
||||||
----
|
|
||||||
|
|
||||||
Event handlers can be automatically attached to elements referenced
|
|
||||||
through an attribute id. Object navigation is not supported for ids,
|
|
||||||
and the parser strips the id attribute before returning the result.
|
|
||||||
Handler functions must begin with `on` and be a function to be
|
|
||||||
installed on the element. This approach is useful for onclick and
|
|
||||||
other handlers that do not want to create circular references that
|
|
||||||
will eventually leak browser memory.
|
|
||||||
|
|
||||||
.Example
|
|
||||||
[source,javascript]
|
|
||||||
----
|
|
||||||
var options = {
|
|
||||||
link: {
|
|
||||||
onclick: function(e) { window.close() },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
Gerrit.html('<a href="javascript:;" id="{link}">Close</a>', options);
|
|
||||||
----
|
|
||||||
|
|
||||||
When using options to install handlers care must be taken to not
|
|
||||||
accidentally include the returned element into the event handler's
|
|
||||||
closure. This is why options is built before calling `Gerrit.html()`
|
|
||||||
and not inline as a shown above with "Hello World".
|
|
||||||
|
|
||||||
DOM nodes can optionally be returned, allowing handlers to access the
|
|
||||||
elements identified by `id={name}` at a later point in time.
|
|
||||||
|
|
||||||
.Example
|
|
||||||
[source,javascript]
|
|
||||||
----
|
|
||||||
var w = Gerrit.html(
|
|
||||||
'<div>Name: <input type="text" id="{name}"></div>'
|
|
||||||
+ '<div>Age: <input type="text" id="{age}"></div>'
|
|
||||||
+ '<button id="{submit}"><div>Save</div></button>',
|
|
||||||
{
|
|
||||||
submit: {
|
|
||||||
onclick: function(s) {
|
|
||||||
var e = w.elements;
|
|
||||||
window.alert(e.name.value + " is " + e.age.value);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, true);
|
|
||||||
----
|
|
||||||
|
|
||||||
To prevent memory leaks `w.root` and `w.elements` should be set to
|
|
||||||
null when the elements are no longer necessary. Screens can use
|
|
||||||
link:#screen_onUnload[screen.onUnload()] to define a callback function
|
|
||||||
to perform this cleanup:
|
|
||||||
|
|
||||||
[source,javascript]
|
|
||||||
----
|
|
||||||
var w = Gerrit.html(...);
|
|
||||||
screen.body.appendElement(w.root);
|
|
||||||
screen.onUnload(function() { w.clear() });
|
|
||||||
----
|
|
||||||
|
|
||||||
[[Gerrit_injectCss]]
|
|
||||||
=== Gerrit.injectCss()
|
|
||||||
Injects CSS rules into the document by appending onto the end of the
|
|
||||||
existing rule list. CSS rules are global to the entire application
|
|
||||||
and must be manually scoped by each plugin. For an automatic scoping
|
|
||||||
alternative see link:#Gerrit_css[`css()`].
|
|
||||||
|
|
||||||
[source,javascript]
|
|
||||||
----
|
|
||||||
Gerrit.injectCss('.myplugin_bg {background: #000}');
|
|
||||||
----
|
|
||||||
|
|
||||||
[[Gerrit_install]]
|
[[Gerrit_install]]
|
||||||
=== Gerrit.install()
|
=== Gerrit.install()
|
||||||
Registers a new plugin by invoking the supplied initialization
|
Registers a new plugin by invoking the supplied initialization
|
||||||
|
@ -360,6 +360,16 @@ screen.
|
|||||||
|
|
||||||
Deprecated. Use link:#plugin-settings[`plugin.settings()`] instead.
|
Deprecated. Use link:#plugin-settings[`plugin.settings()`] instead.
|
||||||
|
|
||||||
|
[[plugin-styles]]
|
||||||
|
=== styles
|
||||||
|
`plugin.styles()`
|
||||||
|
|
||||||
|
.Params:
|
||||||
|
- none
|
||||||
|
|
||||||
|
.Returns:
|
||||||
|
- Instance of link:pg-plugin-styles-api.html[GrStylesApi]
|
||||||
|
|
||||||
=== changeMetadata
|
=== changeMetadata
|
||||||
`plugin.changeMetadata()`
|
`plugin.changeMetadata()`
|
||||||
|
|
||||||
@ -372,6 +382,7 @@ Deprecated. Use link:#plugin-settings[`plugin.settings()`] instead.
|
|||||||
=== theme
|
=== theme
|
||||||
`plugin.theme()`
|
`plugin.theme()`
|
||||||
|
|
||||||
|
|
||||||
Note: TODO
|
Note: TODO
|
||||||
|
|
||||||
=== url
|
=== url
|
||||||
|
33
Documentation/pg-plugin-style-object.txt
Normal file
33
Documentation/pg-plugin-style-object.txt
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
= Gerrit Code Review - GrStyleObject
|
||||||
|
|
||||||
|
Store information about css style properties. You can't create this object
|
||||||
|
directly. Instead you should use the link:pg-plugin-styles-api.html#css[css] method.
|
||||||
|
This object allows to apply style correctly to elements within different shadow
|
||||||
|
subtree.
|
||||||
|
|
||||||
|
[[get-class-name]]
|
||||||
|
== getClassName
|
||||||
|
`styleObject.getClassName(element)`
|
||||||
|
|
||||||
|
.Params
|
||||||
|
- `element` - an HTMLElement.
|
||||||
|
|
||||||
|
.Returns
|
||||||
|
- `string` - class name. The class name is valid only within the shadow root of `element`.
|
||||||
|
|
||||||
|
Creates a new unique CSS class and injects it into the appropriate place
|
||||||
|
in DOM (it can be document or shadow root for element). This class can be later
|
||||||
|
added to the element or to any other element in the same shadow root. It is guarantee,
|
||||||
|
that method adds CSS class only once for each shadow root.
|
||||||
|
|
||||||
|
== apply
|
||||||
|
`styleObject.apply(element)`
|
||||||
|
|
||||||
|
.Params
|
||||||
|
- `element` - element to apply style.
|
||||||
|
|
||||||
|
Create a new unique CSS class (see link:#get-class-name[getClassName]) and
|
||||||
|
adds class to the element.
|
||||||
|
|
||||||
|
|
||||||
|
|
29
Documentation/pg-plugin-styles-api.txt
Normal file
29
Documentation/pg-plugin-styles-api.txt
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
= Gerrit Code Review - Plugin styles API
|
||||||
|
|
||||||
|
This API is provided by link:pg-plugin-dev.html#plugin-styles[plugin.styles()]
|
||||||
|
and provides a way to apply dynamically created styles to elements in a
|
||||||
|
document.
|
||||||
|
|
||||||
|
[[css]]
|
||||||
|
== css
|
||||||
|
`styles.css(rulesStr)`
|
||||||
|
|
||||||
|
.Params
|
||||||
|
- `*string* rulesStr` string with CSS styling declarations.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
----
|
||||||
|
const styleObject = plugin.styles().css('background: black; color: white;');
|
||||||
|
...
|
||||||
|
const className = styleObject.getClassName(element)
|
||||||
|
...
|
||||||
|
element.classList.add(className);
|
||||||
|
...
|
||||||
|
styleObject.apply(someOtherElement);
|
||||||
|
----
|
||||||
|
|
||||||
|
.Returns
|
||||||
|
- Instance of link:pg-plugin-style-object.html[GrStyleObject].
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
|||||||
|
<!--
|
||||||
|
@license
|
||||||
|
Copyright (C) 2019 The Android Open Source Project
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<script src="gr-styles-api.js"></script>
|
@ -0,0 +1,77 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright (C) 2019 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
(function(window) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Prevent redefinition.
|
||||||
|
if (window.GrStylesApi) { return; }
|
||||||
|
|
||||||
|
let styleObjectCount = 0;
|
||||||
|
|
||||||
|
function GrStyleObject(rulesStr) {
|
||||||
|
this._rulesStr = rulesStr;
|
||||||
|
this._className = `__pg_js_api_class_${styleObjectCount}`;
|
||||||
|
styleObjectCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new unique CSS class and injects it in a root node of the element
|
||||||
|
* if it hasn't been added yet. A root node is an document or is the
|
||||||
|
* associated shadowRoot. This class can be added to any element with the same
|
||||||
|
* root node.
|
||||||
|
* @param {HTMLElement} element The element to get class name for.
|
||||||
|
* @return {string} Appropriate class name for the element is returned
|
||||||
|
*/
|
||||||
|
GrStyleObject.prototype.getClassName = function(element) {
|
||||||
|
const rootNode = Polymer.Settings.useShadow
|
||||||
|
? element.getRootNode() : document.body;
|
||||||
|
if (!rootNode.__pg_js_api_style_tags) {
|
||||||
|
rootNode.__pg_js_api_style_tags = {};
|
||||||
|
}
|
||||||
|
if (!rootNode.__pg_js_api_style_tags[this._className]) {
|
||||||
|
const styleTag = document.createElement('style');
|
||||||
|
styleTag.innerHTML = `.${this._className} { ${this._rulesStr} }`;
|
||||||
|
rootNode.appendChild(styleTag);
|
||||||
|
rootNode.__pg_js_api_style_tags[this._className] = true;
|
||||||
|
}
|
||||||
|
return this._className;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply shared style to the element.
|
||||||
|
* @param {HTMLElement} element The element to apply style for
|
||||||
|
*/
|
||||||
|
GrStyleObject.prototype.apply = function(element) {
|
||||||
|
element.classList.add(this.getClassName(element));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
function GrStylesApi() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new GrStyleObject with specified style properties.
|
||||||
|
* @param {string} String with style properties.
|
||||||
|
* @return {GrStyleObject}
|
||||||
|
*/
|
||||||
|
GrStylesApi.prototype.css = function(ruleStr) {
|
||||||
|
return new GrStyleObject(ruleStr);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
window.GrStylesApi = GrStylesApi;
|
||||||
|
})(window);
|
@ -0,0 +1,182 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<!--
|
||||||
|
@license
|
||||||
|
Copyright (C) 2019 The Android Open Source Project
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
|
||||||
|
<title>gr-admin-api</title>
|
||||||
|
<script src="/test/common-test-setup.js"></script>
|
||||||
|
<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
|
||||||
|
|
||||||
|
<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
|
||||||
|
<script src="/bower_components/web-component-tester/browser.js"></script>
|
||||||
|
<link rel="import" href="../../../test/common-test-setup.html"/>
|
||||||
|
<link rel="import" href="../../shared/gr-js-api-interface/gr-js-api-interface.html">
|
||||||
|
<link rel="import" href="gr-styles-api.html">
|
||||||
|
|
||||||
|
<script>void(0);</script>
|
||||||
|
|
||||||
|
<dom-module id="gr-style-test-element">
|
||||||
|
<template>
|
||||||
|
<div id="wrapper"></div>
|
||||||
|
</template>
|
||||||
|
<script>Polymer({is: 'gr-style-test-element'});</script>
|
||||||
|
</dom-module>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
suite('gr-styles-api tests', () => {
|
||||||
|
let sandbox;
|
||||||
|
let stylesApi;
|
||||||
|
|
||||||
|
setup(() => {
|
||||||
|
sandbox = sinon.sandbox.create();
|
||||||
|
let plugin;
|
||||||
|
Gerrit.install(p => { plugin = p; }, '0.1',
|
||||||
|
'http://test.com/plugins/testplugin/static/test.js');
|
||||||
|
sandbox.stub(Gerrit, '_arePluginsLoaded').returns(true);
|
||||||
|
stylesApi = plugin.styles();
|
||||||
|
});
|
||||||
|
|
||||||
|
teardown(() => {
|
||||||
|
stylesApi = null;
|
||||||
|
sandbox.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('exists', () => {
|
||||||
|
assert.isOk(stylesApi);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('css', () => {
|
||||||
|
const styleObject = stylesApi.css('background: red');
|
||||||
|
assert.isDefined(styleObject);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
suite('GrStyleObject tests', () => {
|
||||||
|
let sandbox;
|
||||||
|
let stylesApi;
|
||||||
|
let displayInlineStyle;
|
||||||
|
let displayNoneStyle;
|
||||||
|
|
||||||
|
setup(() => {
|
||||||
|
sandbox = sinon.sandbox.create();
|
||||||
|
let plugin;
|
||||||
|
Gerrit.install(p => { plugin = p; }, '0.1',
|
||||||
|
'http://test.com/plugins/testplugin/static/test.js');
|
||||||
|
sandbox.stub(Gerrit, '_arePluginsLoaded').returns(true);
|
||||||
|
stylesApi = plugin.styles();
|
||||||
|
displayInlineStyle = stylesApi.css('display: inline');
|
||||||
|
displayNoneStyle = stylesApi.css('display: none');
|
||||||
|
});
|
||||||
|
|
||||||
|
teardown(() => {
|
||||||
|
displayInlineStyle = null;
|
||||||
|
displayNoneStyle = null;
|
||||||
|
stylesApi = null;
|
||||||
|
sandbox.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
function createNestedElements(parentElement) {
|
||||||
|
/* parentElement
|
||||||
|
* |--- element1
|
||||||
|
* |--- element2
|
||||||
|
* |--- element3
|
||||||
|
**/
|
||||||
|
const element1 = document.createElement('div');
|
||||||
|
const element2 = document.createElement('div');
|
||||||
|
const element3 = document.createElement('div');
|
||||||
|
Polymer.dom(parentElement).appendChild(element1);
|
||||||
|
Polymer.dom(parentElement).appendChild(element2);
|
||||||
|
Polymer.dom(element2).appendChild(element3);
|
||||||
|
|
||||||
|
return [element1, element2, element3];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
test('getClassName - body level elements', () => {
|
||||||
|
const bodyLevelElements = createNestedElements(document.body);
|
||||||
|
|
||||||
|
testGetClassName(bodyLevelElements);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getClassName - elements inside polymer element', () => {
|
||||||
|
const polymerElement = document.createElement('gr-style-test-element');
|
||||||
|
Polymer.dom(document.body).appendChild(polymerElement);
|
||||||
|
const contentElements = createNestedElements(polymerElement.$.wrapper);
|
||||||
|
|
||||||
|
testGetClassName(contentElements);
|
||||||
|
});
|
||||||
|
|
||||||
|
function testGetClassName(elements) {
|
||||||
|
assertAllElementsHaveDefaultStyle(elements);
|
||||||
|
|
||||||
|
const className1 = displayInlineStyle.getClassName(elements[0]);
|
||||||
|
const className2 = displayNoneStyle.getClassName(elements[1]);
|
||||||
|
const className3 = displayInlineStyle.getClassName(elements[2]);
|
||||||
|
|
||||||
|
assert.notEqual(className2, className1);
|
||||||
|
assert.equal(className3, className1);
|
||||||
|
|
||||||
|
assertAllElementsHaveDefaultStyle(elements);
|
||||||
|
|
||||||
|
elements[0].classList.add(className1);
|
||||||
|
elements[1].classList.add(className2);
|
||||||
|
elements[2].classList.add(className1);
|
||||||
|
|
||||||
|
assertDisplayPropertyValues(elements, ['inline', 'none', 'inline']);
|
||||||
|
}
|
||||||
|
|
||||||
|
test('apply - body level elements', () => {
|
||||||
|
const bodyLevelElements = createNestedElements(document.body);
|
||||||
|
|
||||||
|
testApply(bodyLevelElements);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('apply - elements inside polymer element', () => {
|
||||||
|
const polymerElement = document.createElement('gr-style-test-element');
|
||||||
|
Polymer.dom(document.body).appendChild(polymerElement);
|
||||||
|
const contentElements = createNestedElements(polymerElement.$.wrapper);
|
||||||
|
|
||||||
|
testApply(contentElements);
|
||||||
|
});
|
||||||
|
|
||||||
|
function testApply(elements) {
|
||||||
|
assertAllElementsHaveDefaultStyle(elements);
|
||||||
|
displayInlineStyle.apply(elements[0]);
|
||||||
|
displayNoneStyle.apply(elements[1]);
|
||||||
|
displayInlineStyle.apply(elements[2]);
|
||||||
|
assertDisplayPropertyValues(elements, ['inline', 'none', 'inline']);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function assertAllElementsHaveDefaultStyle(elements) {
|
||||||
|
for (const element of elements) {
|
||||||
|
assert.equal(getComputedStyle(element).getPropertyValue('display'),
|
||||||
|
'block');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertDisplayPropertyValues(elements, expectedDisplayValues) {
|
||||||
|
for (const key in elements) {
|
||||||
|
if (elements.hasOwnProperty(key)) {
|
||||||
|
assert.equal(
|
||||||
|
getComputedStyle(elements[key]).getPropertyValue('display'),
|
||||||
|
expectedDisplayValues[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
@ -43,25 +43,26 @@
|
|||||||
* Method to add annotations to a content line.
|
* Method to add annotations to a content line.
|
||||||
* @param {number} offset The char offset where the update starts.
|
* @param {number} offset The char offset where the update starts.
|
||||||
* @param {number} length The number of chars that the update covers.
|
* @param {number} length The number of chars that the update covers.
|
||||||
* @param {string} cssClass The name of a CSS class created using Gerrit.css.
|
* @param {GrStyleObject} styleObject The style object for the range.
|
||||||
* @param {string} side The side of the update. ('left' or 'right')
|
* @param {string} side The side of the update. ('left' or 'right')
|
||||||
*/
|
*/
|
||||||
GrAnnotationActionsContext.prototype.annotateRange = function(
|
GrAnnotationActionsContext.prototype.annotateRange = function(
|
||||||
offset, length, cssClass, side) {
|
offset, length, styleObject, side) {
|
||||||
if (this._contentEl && this._contentEl.getAttribute('data-side') == side) {
|
if (this._contentEl && this._contentEl.getAttribute('data-side') == side) {
|
||||||
GrAnnotation.annotateElement(this._contentEl, offset, length, cssClass);
|
GrAnnotation.annotateElement(this._contentEl, offset, length,
|
||||||
|
styleObject.getClassName(this._contentEl));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to add a CSS class to the line number TD element.
|
* Method to add a CSS class to the line number TD element.
|
||||||
* @param {string} cssClass The name of a CSS class created using Gerrit.css.
|
* @param {GrStyleObject} styleObject The style object for the range.
|
||||||
* @param {string} side The side of the update. ('left' or 'right')
|
* @param {string} side The side of the update. ('left' or 'right')
|
||||||
*/
|
*/
|
||||||
GrAnnotationActionsContext.prototype.annotateLineNumber = function(
|
GrAnnotationActionsContext.prototype.annotateLineNumber = function(
|
||||||
cssClass, side) {
|
styleObject, side) {
|
||||||
if (this._lineNumberEl && this._lineNumberEl.classList.contains(side)) {
|
if (this._lineNumberEl && this._lineNumberEl.classList.contains(side)) {
|
||||||
this._lineNumberEl.classList.add(cssClass);
|
styleObject.apply(this._lineNumberEl);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -42,9 +42,13 @@ limitations under the License.
|
|||||||
let sandbox;
|
let sandbox;
|
||||||
let el;
|
let el;
|
||||||
let lineNumberEl;
|
let lineNumberEl;
|
||||||
|
let plugin;
|
||||||
|
|
||||||
setup(() => {
|
setup(() => {
|
||||||
sandbox = sinon.sandbox.create();
|
sandbox = sinon.sandbox.create();
|
||||||
|
Gerrit.install(p => { plugin = p; }, '0.1',
|
||||||
|
'http://test.com/plugins/testplugin/static/test.js');
|
||||||
|
|
||||||
const str = 'lorem ipsum blah blah';
|
const str = 'lorem ipsum blah blah';
|
||||||
const line = {text: str};
|
const line = {text: str};
|
||||||
el = document.createElement('div');
|
el = document.createElement('div');
|
||||||
@ -64,32 +68,34 @@ limitations under the License.
|
|||||||
annotateElementSpy = sandbox.spy(GrAnnotation, 'annotateElement');
|
annotateElementSpy = sandbox.spy(GrAnnotation, 'annotateElement');
|
||||||
const start = 0;
|
const start = 0;
|
||||||
const end = 100;
|
const end = 100;
|
||||||
const cssClass = Gerrit.css('background-color: #000000');
|
const cssStyleObject = plugin.styles().css('background-color: #000000');
|
||||||
|
|
||||||
// Assert annotateElement is not called when side is different.
|
// Assert annotateElement is not called when side is different.
|
||||||
instance.annotateRange(start, end, cssClass, 'left');
|
instance.annotateRange(start, end, cssStyleObject, 'left');
|
||||||
assert.equal(annotateElementSpy.callCount, 0);
|
assert.equal(annotateElementSpy.callCount, 0);
|
||||||
|
|
||||||
// Assert annotateElement is called once when side is the same.
|
// Assert annotateElement is called once when side is the same.
|
||||||
instance.annotateRange(start, end, cssClass, 'right');
|
instance.annotateRange(start, end, cssStyleObject, 'right');
|
||||||
assert.equal(annotateElementSpy.callCount, 1);
|
assert.equal(annotateElementSpy.callCount, 1);
|
||||||
const args = annotateElementSpy.getCalls()[0].args;
|
const args = annotateElementSpy.getCalls()[0].args;
|
||||||
assert.equal(args[0], el);
|
assert.equal(args[0], el);
|
||||||
assert.equal(args[1], start);
|
assert.equal(args[1], start);
|
||||||
assert.equal(args[2], end);
|
assert.equal(args[2], end);
|
||||||
assert.equal(args[3], cssClass);
|
assert.equal(args[3], cssStyleObject.getClassName(el));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test annotateLineNumber', () => {
|
test('test annotateLineNumber', () => {
|
||||||
const cssClass = Gerrit.css('background-color: #000000');
|
const cssStyleObject = plugin.styles().css('background-color: #000000');
|
||||||
|
|
||||||
|
const className = cssStyleObject.getClassName(lineNumberEl);
|
||||||
|
|
||||||
// Assert that css class is *not* applied when side is different.
|
// Assert that css class is *not* applied when side is different.
|
||||||
instance.annotateLineNumber(cssClass, 'left');
|
instance.annotateLineNumber(cssStyleObject, 'left');
|
||||||
assert.isFalse(lineNumberEl.classList.contains(cssClass));
|
assert.isFalse(lineNumberEl.classList.contains(className));
|
||||||
|
|
||||||
// Assert that css class is applied when side is the same.
|
// Assert that css class is applied when side is the same.
|
||||||
instance.annotateLineNumber(cssClass, 'right');
|
instance.annotateLineNumber(cssStyleObject, 'right');
|
||||||
assert.isTrue(lineNumberEl.classList.contains(cssClass));
|
assert.isTrue(lineNumberEl.classList.contains(className));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -26,6 +26,7 @@ limitations under the License.
|
|||||||
<link rel="import" href="../../plugins/gr-popup-interface/gr-popup-interface.html">
|
<link rel="import" href="../../plugins/gr-popup-interface/gr-popup-interface.html">
|
||||||
<link rel="import" href="../../plugins/gr-repo-api/gr-repo-api.html">
|
<link rel="import" href="../../plugins/gr-repo-api/gr-repo-api.html">
|
||||||
<link rel="import" href="../../plugins/gr-settings-api/gr-settings-api.html">
|
<link rel="import" href="../../plugins/gr-settings-api/gr-settings-api.html">
|
||||||
|
<link rel="import" href="../../plugins/gr-styles-api/gr-styles-api.html">
|
||||||
<link rel="import" href="../../plugins/gr-theme-api/gr-theme-api.html">
|
<link rel="import" href="../../plugins/gr-theme-api/gr-theme-api.html">
|
||||||
<link rel="import" href="../gr-rest-api-interface/gr-rest-api-interface.html">
|
<link rel="import" href="../gr-rest-api-interface/gr-rest-api-interface.html">
|
||||||
|
|
||||||
|
@ -319,6 +319,10 @@
|
|||||||
return new GrSettingsApi(this);
|
return new GrSettingsApi(this);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Plugin.prototype.styles = function() {
|
||||||
|
return new GrStylesApi();
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To make REST requests for plugin-provided endpoints, use
|
* To make REST requests for plugin-provided endpoints, use
|
||||||
* @example
|
* @example
|
||||||
@ -511,7 +515,13 @@
|
|||||||
'Please use plugin.getPluginName() instead.');
|
'Please use plugin.getPluginName() instead.');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use plugin.styles().css(rulesStr) instead. Please, consult
|
||||||
|
* the documentation how to replace it accordingly.
|
||||||
|
*/
|
||||||
Gerrit.css = function(rulesStr) {
|
Gerrit.css = function(rulesStr) {
|
||||||
|
console.warn('Gerrit.css(rulesStr) is deprecated!',
|
||||||
|
'Use plugin.styles().css(rulesStr)');
|
||||||
if (!Gerrit._customStyleSheet) {
|
if (!Gerrit._customStyleSheet) {
|
||||||
const styleEl = document.createElement('style');
|
const styleEl = document.createElement('style');
|
||||||
document.head.appendChild(styleEl);
|
document.head.appendChild(styleEl);
|
||||||
|
@ -32,6 +32,11 @@
|
|||||||
const coverageData = {};
|
const coverageData = {};
|
||||||
let displayCoverage = false;
|
let displayCoverage = false;
|
||||||
const annotationApi = plugin.annotationApi();
|
const annotationApi = plugin.annotationApi();
|
||||||
|
const styleApi = plugin.styles();
|
||||||
|
|
||||||
|
const coverageStyle = styleApi.css('background-color: #EF9B9B !important');
|
||||||
|
const emptyStyle = styleApi.css('');
|
||||||
|
|
||||||
annotationApi.addLayer(context => {
|
annotationApi.addLayer(context => {
|
||||||
if (Object.keys(coverageData).length === 0) {
|
if (Object.keys(coverageData).length === 0) {
|
||||||
// Coverage data is not ready yet.
|
// Coverage data is not ready yet.
|
||||||
@ -41,16 +46,16 @@
|
|||||||
const line = context.line;
|
const line = context.line;
|
||||||
// Highlight lines missing coverage with this background color if
|
// Highlight lines missing coverage with this background color if
|
||||||
// coverage should be displayed, else do nothing.
|
// coverage should be displayed, else do nothing.
|
||||||
const cssClass = displayCoverage
|
const annotationStyle = displayCoverage
|
||||||
? Gerrit.css('background-color: #EF9B9B')
|
? coverageStyle
|
||||||
: Gerrit.css('');
|
: emptyStyle;
|
||||||
if (coverageData[path] &&
|
if (coverageData[path] &&
|
||||||
coverageData[path].changeNum === context.changeNum &&
|
coverageData[path].changeNum === context.changeNum &&
|
||||||
coverageData[path].patchNum === context.patchNum) {
|
coverageData[path].patchNum === context.patchNum) {
|
||||||
const linesMissingCoverage = coverageData[path].linesMissingCoverage;
|
const linesMissingCoverage = coverageData[path].linesMissingCoverage;
|
||||||
if (linesMissingCoverage.includes(line.afterNumber)) {
|
if (linesMissingCoverage.includes(line.afterNumber)) {
|
||||||
context.annotateRange(0, line.text.length, cssClass, 'right');
|
context.annotateRange(0, line.text.length, annotationStyle, 'right');
|
||||||
context.annotateLineNumber(cssClass, 'right');
|
context.annotateLineNumber(annotationStyle, 'right');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).enableToggleCheckbox('Display Coverage', checkbox => {
|
}).enableToggleCheckbox('Display Coverage', checkbox => {
|
||||||
|
@ -125,6 +125,7 @@ limitations under the License.
|
|||||||
'edit/gr-edit-file-controls/gr-edit-file-controls_test.html',
|
'edit/gr-edit-file-controls/gr-edit-file-controls_test.html',
|
||||||
'edit/gr-editor-view/gr-editor-view_test.html',
|
'edit/gr-editor-view/gr-editor-view_test.html',
|
||||||
'plugins/gr-admin-api/gr-admin-api_test.html',
|
'plugins/gr-admin-api/gr-admin-api_test.html',
|
||||||
|
'plugins/gr-styles-api/gr-styles-api_test.html',
|
||||||
'plugins/gr-attribute-helper/gr-attribute-helper_test.html',
|
'plugins/gr-attribute-helper/gr-attribute-helper_test.html',
|
||||||
'plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.html',
|
'plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.html',
|
||||||
'plugins/gr-event-helper/gr-event-helper_test.html',
|
'plugins/gr-event-helper/gr-event-helper_test.html',
|
||||||
|
Loading…
Reference in New Issue
Block a user