Skip to content

Commit 0832829

Browse files
danilsomsikovDevtools-frontend LUCI CQ
authored andcommitted
Support ToolbarInput and Adorner in the preferTemplateLiterals
Bug: 400353541 Change-Id: Ibb13f752eb215b3a01b8d0ad16ced080b98a68ac Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6387797 Commit-Queue: Danil Somsikov <[email protected]> Reviewed-by: Philip Pfaffe <[email protected]>
1 parent 2638f1b commit 0832829

File tree

2 files changed

+144
-11
lines changed

2 files changed

+144
-11
lines changed

scripts/eslint_rules/lib/no-imperative-dom-api.js

Lines changed: 69 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,34 @@ module.exports = {
249249
const subpropertyValue = isSubpropertyAssignment ? /** @type {Node} */ (grandGrandParent.right) : null;
250250
if (isPropertyAssignment && isIdentifier(property, 'className')) {
251251
domFragment.classList.push(propertyValue);
252-
} else if (isPropertyAssignment && isIdentifier(property, 'textContent')) {
252+
} else if (isPropertyAssignment && isIdentifier(property, ['textContent', 'innerHTML'])) {
253253
domFragment.textContent = propertyValue;
254+
} else if (
255+
isPropertyAssignment && domFragment.tagName === 'devtools-adorner' && isIdentifier(property, 'data') &&
256+
propertyValue.type === 'ObjectExpression') {
257+
for (const property of propertyValue.properties) {
258+
if (property.type !== 'Property') {
259+
continue;
260+
}
261+
if (isIdentifier(property.key, 'name')) {
262+
domFragment.attributes.push({
263+
key: 'aria-label',
264+
value: /** @type {Node} */ (property.value),
265+
});
266+
}
267+
if (isIdentifier(property.key, 'jslogContext')) {
268+
domFragment.attributes.push({
269+
key: 'jslog',
270+
value: /** @type {Node} */ (
271+
{type: 'Literal', value: '${VisualLogging.adorner(' + sourceCode.getText(property.value) + ')}'})
272+
});
273+
}
274+
if (isIdentifier(property.key, 'content')) {
275+
const childFragment = getOrCreateDomFragment(/** @type {Node} */ (property.value));
276+
childFragment.parent = domFragment;
277+
domFragment.children.push(childFragment);
278+
}
279+
}
254280
} else if (isMethodCall && isIdentifier(property, 'setAttribute')) {
255281
const attribute = firstArg;
256282
const value = secondArg;
@@ -373,49 +399,84 @@ export const DEFAULT_VIEW = (input, _output, target) => {
373399
NewExpression(node) {
374400
if (isMemberExpression(
375401
node.callee, n => isMemberExpression(n, n => isIdentifier(n, 'UI'), n => isIdentifier(n, 'Toolbar')),
376-
n => isIdentifier(n, 'ToolbarFilter'))) {
402+
n => isIdentifier(n, ['ToolbarFilter', 'ToolbarInput']))) {
377403
const domFragment = getOrCreateDomFragment(node);
378404
domFragment.tagName = 'devtools-toolbar-input';
405+
const type = isIdentifier(node.callee.property, 'ToolbarFilter') ? 'filter' : 'text';
379406
domFragment.attributes.push({
380407
key: 'type',
381-
value: /** @type {Node} */ ({type: 'Literal', value: 'filter'}),
408+
value: /** @type {Node} */ ({type: 'Literal', value: type}),
382409
});
383-
const placeholder = node.arguments[0];
384-
const flexGrow = node.arguments[1];
385-
const flexShrink = node.arguments[2];
386-
const title = node.arguments[3];
387-
const jslogContext = node.arguments[6];
410+
const args = [...node.arguments];
411+
const placeholder = args.shift();
388412
if (placeholder && !isIdentifier(placeholder, 'undefined')) {
389413
domFragment.attributes.push({
390414
key: 'placeholder',
391415
value: placeholder,
392416
});
393417
}
418+
if (type === 'text') {
419+
const accesiblePlaceholder = args.shift();
420+
if (accesiblePlaceholder && !isIdentifier(accesiblePlaceholder, 'undefined')) {
421+
domFragment.attributes.push({
422+
key: 'aria-label',
423+
value: accesiblePlaceholder,
424+
});
425+
}
426+
}
427+
const flexGrow = args.shift();
394428
if (flexGrow && !isIdentifier(flexGrow, 'undefined')) {
395429
domFragment.style.push({
396430
key: 'flex-grow',
397431
value: flexGrow,
398432
});
399433
}
434+
const flexShrink = args.shift();
400435
if (flexShrink && !isIdentifier(flexShrink, 'undefined')) {
401436
domFragment.style.push({
402437
key: 'flex-shrink',
403438
value: flexShrink,
404439
});
405440
}
441+
const title = args.shift();
406442
if (title && !isIdentifier(title, 'undefined')) {
407443
domFragment.attributes.push({
408444
key: 'title',
409445
value: title,
410446
});
411447
}
448+
const completions = args.shift();
449+
if (completions && !isIdentifier(completions, 'undefined')) {
450+
domFragment.attributes.push({
451+
key: 'list',
452+
value: /** @type {Node} */ ({type: 'Literal', value: 'completions'}),
453+
});
454+
const dataList = getOrCreateDomFragment(completions);
455+
dataList.tagName = 'datalist';
456+
dataList.attributes.push({
457+
key: 'id',
458+
value: /** @type {Node} */ ({type: 'Literal', value: 'completions'}),
459+
});
460+
dataList.textContent = completions;
461+
domFragment.children.push(dataList);
462+
dataList.parent = domFragment;
463+
}
464+
args.shift(); // dynamicCompletions is not supported
465+
const jslogContext = args.shift();
412466
if (jslogContext && !isIdentifier(jslogContext, 'undefined')) {
413467
domFragment.attributes.push({
414468
key: 'id',
415469
value: jslogContext,
416470
});
417471
}
418472
}
473+
if (isMemberExpression(
474+
node.callee,
475+
n => isMemberExpression(n, n => isIdentifier(n, 'Adorners'), n => isIdentifier(n, 'Adorner')),
476+
n => isIdentifier(n, 'Adorner'))) {
477+
const domFragment = getOrCreateDomFragment(node);
478+
domFragment.tagName = 'devtools-adorner';
479+
}
419480
},
420481
'Program:exit'() {
421482
while (queue.length) {

scripts/eslint_rules/tests/no-imperative-dom-api.test.js

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ class SomeWidget extends UI.Widget.Widget {
183183
constructor() {
184184
super();
185185
const toolbar = this.contentElement.createChild('devtools-toolbar');
186-
const filterInput = new UI.Toolbar.ToolbarFilter('some-placeholder', 0.5, 1, undefined, undefined, false, 'some-filter');
186+
const filterInput = new UI.Toolbar.ToolbarFilter('some-placeholder', 0.5, 1, undefined, this.complete.bind(this), false, 'some-filter');
187187
filterInput.addEventListener(UI.Toolbar.ToolbarInput.Event.TEXT_CHANGED, this.onFilterChanged.bind(this));
188188
toolbar.appendToolbarItem(filterInput);
189189
}
@@ -194,13 +194,85 @@ export const DEFAULT_VIEW = (input, _output, target) => {
194194
render(html\`
195195
<div>
196196
<devtools-toolbar>
197-
<devtools-toolbar-input type="filter" placeholder="some-placeholder" id="some-filter"
198-
@change=\${this.onFilterChanged.bind(this)} style="flex-grow:0.5; flex-shrink:1"></devtools-toolbar-input>
197+
<devtools-toolbar-input type="filter" placeholder="some-placeholder" list="completions"
198+
id="some-filter" @change=\${this.onFilterChanged.bind(this)}
199+
style="flex-grow:0.5; flex-shrink:1">
200+
<datalist id="completions">\${this.complete.bind(this)}</datalist>
201+
</devtools-toolbar-input>
199202
</devtools-toolbar>
200203
</div>\`,
201204
target, {host: input});
202205
};
203206
207+
class SomeWidget extends UI.Widget.Widget {
208+
constructor() {
209+
super();
210+
}
211+
}`,
212+
errors: [{messageId: 'preferTemplateLiterals'}],
213+
},
214+
{
215+
filename: 'front_end/ui/components/component/file.ts',
216+
code: `
217+
class SomeWidget extends UI.Widget.Widget {
218+
constructor() {
219+
super();
220+
const toolbar = this.contentElement.createChild('devtools-toolbar');
221+
const filterInput = new UI.Toolbar.ToolbarInput('some-placeholder', 'accessible-placeholder', 0.5, 1);
222+
toolbar.appendToolbarItem(filterInput);
223+
}
224+
}`,
225+
output: `
226+
227+
export const DEFAULT_VIEW = (input, _output, target) => {
228+
render(html\`
229+
<div>
230+
<devtools-toolbar>
231+
<devtools-toolbar-input type="text" placeholder="some-placeholder"
232+
aria-label="accessible-placeholder" style="flex-grow:0.5; flex-shrink:1"></devtools-toolbar-input>
233+
</devtools-toolbar>
234+
</div>\`,
235+
target, {host: input});
236+
};
237+
238+
class SomeWidget extends UI.Widget.Widget {
239+
constructor() {
240+
super();
241+
}
242+
}`,
243+
errors: [{messageId: 'preferTemplateLiterals'}],
244+
},
245+
{
246+
filename: 'front_end/ui/components/component/file.ts',
247+
code: `
248+
class SomeWidget extends UI.Widget.Widget {
249+
constructor() {
250+
super();
251+
const adornerContent = document.createElement('span');
252+
adornerContent.innerHTML = '<div style="font-size: 12px;">💫</div>';
253+
const adorner = new Adorners.Adorner.Adorner();
254+
adorner.classList.add('fix-perf-icon');
255+
adorner.data = {
256+
name: i18nString(UIStrings.fixMe),
257+
content: adornerContent,
258+
jslogContext: 'fix-perf',
259+
};
260+
this.contentElement.appendChild(adorner);
261+
}
262+
}`,
263+
output: `
264+
265+
export const DEFAULT_VIEW = (input, _output, target) => {
266+
render(html\`
267+
<div>
268+
<devtools-adorner class="fix-perf-icon" aria-label=\${i18nString(UIStrings.fixMe)}
269+
jslog=\${VisualLogging.adorner('fix-perf')}>
270+
<span><div style="font-size: 12px;">💫</div></span>
271+
</devtools-adorner>
272+
</div>\`,
273+
target, {host: input});
274+
};
275+
204276
class SomeWidget extends UI.Widget.Widget {
205277
constructor() {
206278
super();

0 commit comments

Comments
 (0)