Harden gr-formatted-text agains slow/failed project configs
Formerly, if a formatted text component tried to render without the project config (used by inner linked text components) it would temporarily fall-back to rendering the unformatted (and un-linkified) text via `.textContent` -- mirroring the behavior of gr-linked-text. The result is formatted text elements (when rendered without a project config) appear as one long line of text. Unlike linkification, however, text can be accurately formatted with or without the project config -- so this disruptive, poor UX is unnecessary. The formatted text component is updated to format text when the project config has not provided, and to re-render when the config has been provided. In order to propagate project config loads to the formatted text components hosted by diff comments, the REST calls must be made by the diff thread component. To make this call, the thread must have the project's name, so gr-diff-builder is updated to provide this name to thread components. Bug: Issue 6686 Change-Id: I8d09c740930500e99cb5f87b92f4d72f3f50a9ce
This commit is contained in:
@@ -338,6 +338,7 @@ limitations under the License.
|
||||
patch-range="[[patchRange]]"
|
||||
path="[[file.__path]]"
|
||||
prefs="[[diffPrefs]]"
|
||||
project-name="[[change.project]]"
|
||||
project-config="[[projectConfig]]"
|
||||
on-line-selected="_onLineSelected"
|
||||
no-render-on-prefs-change
|
||||
|
||||
@@ -19,9 +19,10 @@
|
||||
|
||||
const IMAGE_MIME_PATTERN = /^image\/(bmp|gif|jpeg|jpg|png|tiff|webp)$/;
|
||||
|
||||
function GrDiffBuilderImage(diff, comments, prefs, outputEl, baseImage,
|
||||
revisionImage) {
|
||||
GrDiffBuilderSideBySide.call(this, diff, comments, prefs, outputEl, []);
|
||||
function GrDiffBuilderImage(
|
||||
diff, comments, prefs, projectName, outputEl, baseImage, revisionImage) {
|
||||
GrDiffBuilderSideBySide.call(
|
||||
this, diff, comments, prefs, projectName, outputEl, []);
|
||||
this._baseImage = baseImage;
|
||||
this._revisionImage = revisionImage;
|
||||
}
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
// Prevent redefinition.
|
||||
if (window.GrDiffBuilderSideBySide) { return; }
|
||||
|
||||
function GrDiffBuilderSideBySide(diff, comments, prefs, outputEl, layers) {
|
||||
GrDiffBuilder.call(this, diff, comments, prefs, outputEl, layers);
|
||||
function GrDiffBuilderSideBySide(
|
||||
diff, comments, prefs, projectName, outputEl, layers) {
|
||||
GrDiffBuilder.call(
|
||||
this, diff, comments, prefs, projectName, outputEl, layers);
|
||||
}
|
||||
GrDiffBuilderSideBySide.prototype = Object.create(GrDiffBuilder.prototype);
|
||||
GrDiffBuilderSideBySide.prototype.constructor = GrDiffBuilderSideBySide;
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
// Prevent redefinition.
|
||||
if (window.GrDiffBuilderUnified) { return; }
|
||||
|
||||
function GrDiffBuilderUnified(diff, comments, prefs, outputEl, layers) {
|
||||
GrDiffBuilder.call(this, diff, comments, prefs, outputEl, layers);
|
||||
function GrDiffBuilderUnified(
|
||||
diff, comments, prefs, projectName, outputEl, layers) {
|
||||
GrDiffBuilder.call(
|
||||
this, diff, comments, prefs, projectName, outputEl, layers);
|
||||
}
|
||||
GrDiffBuilderUnified.prototype = Object.create(GrDiffBuilder.prototype);
|
||||
GrDiffBuilderUnified.prototype.constructor = GrDiffBuilderUnified;
|
||||
|
||||
@@ -94,6 +94,7 @@ limitations under the License.
|
||||
isImageDiff: Boolean,
|
||||
baseImage: Object,
|
||||
revisionImage: Object,
|
||||
projectName: String,
|
||||
_builder: Object,
|
||||
_groups: Array,
|
||||
_layers: Array,
|
||||
@@ -219,9 +220,9 @@ limitations under the License.
|
||||
},
|
||||
|
||||
createCommentThreadGroup(changeNum, patchNum, path,
|
||||
isOnParent, commentSide, projectConfig) {
|
||||
isOnParent, commentSide) {
|
||||
return this._builder.createCommentThreadGroup(changeNum, patchNum,
|
||||
path, isOnParent, commentSide, projectConfig);
|
||||
path, isOnParent, commentSide);
|
||||
},
|
||||
|
||||
emitGroup(group, sectionEl) {
|
||||
@@ -252,13 +253,14 @@ limitations under the License.
|
||||
_getDiffBuilder(diff, comments, prefs) {
|
||||
if (this.isImageDiff) {
|
||||
return new GrDiffBuilderImage(diff, comments, prefs,
|
||||
this.diffElement, this.baseImage, this.revisionImage);
|
||||
this.projectName, this.diffElement, this.baseImage,
|
||||
this.revisionImage);
|
||||
} else if (this.viewMode === DiffViewMode.SIDE_BY_SIDE) {
|
||||
return new GrDiffBuilderSideBySide(
|
||||
diff, comments, prefs, this.diffElement, this._layers);
|
||||
return new GrDiffBuilderSideBySide(diff, comments, prefs,
|
||||
this.projectName, this.diffElement, this._layers);
|
||||
} else if (this.viewMode === DiffViewMode.UNIFIED) {
|
||||
return new GrDiffBuilderUnified(
|
||||
diff, comments, prefs, this.diffElement, this._layers);
|
||||
return new GrDiffBuilderUnified(diff, comments, prefs,
|
||||
this.projectName, this.diffElement, this._layers);
|
||||
}
|
||||
throw Error('Unsupported diff view mode: ' + this.viewMode);
|
||||
},
|
||||
|
||||
@@ -30,10 +30,11 @@
|
||||
|
||||
const REGEX_ASTRAL_SYMBOL = /[\uD800-\uDBFF][\uDC00-\uDFFF]/;
|
||||
|
||||
function GrDiffBuilder(diff, comments, prefs, outputEl, layers) {
|
||||
function GrDiffBuilder(diff, comments, prefs, projectName, outputEl, layers) {
|
||||
this._diff = diff;
|
||||
this._comments = comments;
|
||||
this._prefs = prefs;
|
||||
this._projectName = projectName;
|
||||
this._outputEl = outputEl;
|
||||
this.groups = [];
|
||||
|
||||
@@ -336,14 +337,14 @@
|
||||
};
|
||||
|
||||
GrDiffBuilder.prototype.createCommentThreadGroup = function(changeNum,
|
||||
patchNum, path, isOnParent, projectConfig, range) {
|
||||
patchNum, path, isOnParent, range) {
|
||||
const threadGroupEl =
|
||||
document.createElement('gr-diff-comment-thread-group');
|
||||
threadGroupEl.changeNum = changeNum;
|
||||
threadGroupEl.patchForNewThreads = patchNum;
|
||||
threadGroupEl.path = path;
|
||||
threadGroupEl.isOnParent = isOnParent;
|
||||
threadGroupEl.projectConfig = projectConfig;
|
||||
threadGroupEl.projectName = this._projectName;
|
||||
threadGroupEl.range = range;
|
||||
return threadGroupEl;
|
||||
};
|
||||
@@ -370,8 +371,7 @@
|
||||
this._comments.meta.changeNum,
|
||||
patchNum,
|
||||
this._comments.meta.path,
|
||||
isOnParent,
|
||||
this._comments.meta.projectConfig);
|
||||
isOnParent);
|
||||
threadGroupEl.comments = comments;
|
||||
if (opt_side) {
|
||||
threadGroupEl.setAttribute('data-side', opt_side);
|
||||
|
||||
@@ -62,13 +62,16 @@ limitations under the License.
|
||||
setup(() => {
|
||||
stub('gr-rest-api-interface', {
|
||||
getLoggedIn() { return Promise.resolve(false); },
|
||||
getProjectConfig() { return Promise.resolve({}); },
|
||||
});
|
||||
const prefs = {
|
||||
line_length: 10,
|
||||
show_tabs: true,
|
||||
tab_size: 4,
|
||||
};
|
||||
builder = new GrDiffBuilder({content: []}, {left: [], right: []}, prefs);
|
||||
const projectName = 'my-project';
|
||||
builder = new GrDiffBuilder(
|
||||
{content: []}, {left: [], right: []}, prefs, projectName);
|
||||
});
|
||||
|
||||
test('context control buttons', () => {
|
||||
@@ -265,7 +268,7 @@ limitations under the License.
|
||||
assert.equal(threadGroupEl.patchForNewThreads, patchNum);
|
||||
assert.equal(threadGroupEl.path, '/path/to/foo');
|
||||
assert.equal(threadGroupEl.isOnParent, isOnParent);
|
||||
assert.deepEqual(threadGroupEl.projectConfig, {foo: 'bar'});
|
||||
assert.deepEqual(threadGroupEl.projectName, 'my-project');
|
||||
assert.deepEqual(threadGroupEl.comments, comments);
|
||||
}
|
||||
|
||||
@@ -761,7 +764,7 @@ limitations under the License.
|
||||
outputEl = element.queryEffectiveChildren('#diffTable');
|
||||
sandbox.stub(element, '_getDiffBuilder', () => {
|
||||
const builder = new GrDiffBuilder(
|
||||
{content}, {left: [], right: []}, prefs, outputEl);
|
||||
{content}, {left: [], right: []}, prefs, 'my-project', outputEl);
|
||||
sandbox.stub(builder, 'addColumns');
|
||||
builder.buildSectionElement = function(group) {
|
||||
const section = document.createElement('stub');
|
||||
|
||||
@@ -38,7 +38,7 @@ limitations under the License.
|
||||
location-range="[[thread.locationRange]]"
|
||||
patch-num="[[thread.patchNum]]"
|
||||
path="[[path]]"
|
||||
project-config="[[projectConfig]]"></gr-diff-comment-thread>
|
||||
project-name="[[projectName]]"></gr-diff-comment-thread>
|
||||
</template>
|
||||
</template>
|
||||
<script src="gr-diff-comment-thread-group.js"></script>
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
type: Array,
|
||||
value() { return []; },
|
||||
},
|
||||
projectName: String,
|
||||
patchForNewThreads: String,
|
||||
projectConfig: Object,
|
||||
range: Object,
|
||||
isOnParent: {
|
||||
type: Boolean,
|
||||
|
||||
@@ -61,7 +61,7 @@ limitations under the License.
|
||||
show-actions="[[_showActions]]"
|
||||
comment-side="[[comment.__commentSide]]"
|
||||
side="[[comment.side]]"
|
||||
project-config="[[projectConfig]]"
|
||||
project-config="[[_projectConfig]]"
|
||||
on-create-fix-comment="_handleCommentFix"
|
||||
on-comment-discard="_handleCommentDiscard"></gr-diff-comment>
|
||||
</template>
|
||||
|
||||
@@ -40,7 +40,10 @@
|
||||
commentSide: String,
|
||||
patchNum: String,
|
||||
path: String,
|
||||
projectConfig: Object,
|
||||
projectName: {
|
||||
type: String,
|
||||
observer: '_projectNameChanged',
|
||||
},
|
||||
isOnParent: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
@@ -53,6 +56,7 @@
|
||||
type: Boolean,
|
||||
notify: true,
|
||||
},
|
||||
_projectConfig: Object,
|
||||
},
|
||||
|
||||
behaviors: [
|
||||
@@ -352,5 +356,16 @@
|
||||
_computeHostClass(unresolved) {
|
||||
return unresolved ? 'unresolved' : '';
|
||||
},
|
||||
|
||||
/**
|
||||
* Load the project config when a project name has been provided.
|
||||
* @param {string} name The project name.
|
||||
*/
|
||||
_projectNameChanged(name) {
|
||||
if (!name) { return; }
|
||||
this.$.restAPI.getProjectConfig(name).then(config => {
|
||||
this._projectConfig = config;
|
||||
});
|
||||
},
|
||||
});
|
||||
})();
|
||||
|
||||
@@ -161,6 +161,17 @@ limitations under the License.
|
||||
lastComment.__draft = true;
|
||||
assert.equal(element._hideActions(showActions, lastComment), true);
|
||||
});
|
||||
|
||||
test('setting project name loads the project config', done => {
|
||||
const projectName = 'foo/bar/baz';
|
||||
const getProjectStub = sandbox.stub(element.$.restAPI, 'getProjectConfig')
|
||||
.returns(Promise.resolve({}));
|
||||
element.projectName = projectName;
|
||||
flush(() => {
|
||||
assert.isTrue(getProjectStub.calledWithExactly(projectName));
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('comment action tests', () => {
|
||||
|
||||
@@ -328,6 +328,7 @@ limitations under the License.
|
||||
path="[[_path]]"
|
||||
prefs="[[_prefs]]"
|
||||
project-config="[[_projectConfig]]"
|
||||
project-name="[[_change.project]]"
|
||||
view-mode="[[_diffMode]]"
|
||||
on-line-selected="_onLineSelected">
|
||||
</gr-diff>
|
||||
|
||||
@@ -229,6 +229,7 @@ limitations under the License.
|
||||
<gr-diff-builder
|
||||
id="diffBuilder"
|
||||
comments="[[comments]]"
|
||||
project-name="[[projectName]]"
|
||||
diff="[[_diff]]"
|
||||
diff-path="[[path]]"
|
||||
view-mode="[[viewMode]]"
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
type: Object,
|
||||
observer: '_projectConfigChanged',
|
||||
},
|
||||
projectName: String,
|
||||
displayLine: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
@@ -305,8 +306,7 @@
|
||||
let threadGroupEl = this._getThreadGroupForLine(contentEl);
|
||||
if (!threadGroupEl) {
|
||||
threadGroupEl = this.$.diffBuilder.createCommentThreadGroup(
|
||||
this.changeNum, patchNum, this.path, isOnParent,
|
||||
this.projectConfig);
|
||||
this.changeNum, patchNum, this.path, isOnParent);
|
||||
contentEl.appendChild(threadGroupEl);
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
// request for it still being in flight), set the content anyway to
|
||||
// prevent waiting on the config to display the text.
|
||||
if (this.config) { return; }
|
||||
this.$.container.textContent = content;
|
||||
this._contentOrConfigChanged(content);
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -363,12 +363,10 @@ limitations under the License.
|
||||
assert.equal(result, expected);
|
||||
});
|
||||
|
||||
test('_contentOrConfigChanged not called without config', () => {
|
||||
const contentStub = sandbox.stub(element, '_contentChanged');
|
||||
const contentConfigStub = sandbox.stub(element, '_contentOrConfigChanged');
|
||||
test('_computeNodes called without config', () => {
|
||||
const computeNodesSpy = sandbox.spy(element, '_computeNodes');
|
||||
element.content = 'some text';
|
||||
assert.isTrue(contentStub.called);
|
||||
assert.isFalse(contentConfigStub.called);
|
||||
assert.isTrue(computeNodesSpy.called);
|
||||
});
|
||||
|
||||
test('_contentOrConfigChanged called with config', () => {
|
||||
|
||||
Reference in New Issue
Block a user