Skip to content

Commit bc179fc

Browse files
danilsomsikovDevtools-frontend LUCI CQ
authored andcommitted
Use Lit to render cells and default sorting in the CoverageListView data grid
Bug: 414630818 Change-Id: Ie464db1c3888ed14b7f808b7f1349d03205bf359 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6973513 Reviewed-by: Alex Rudenko <alexrudenko@chromium.org> Commit-Queue: Danil Somsikov <dsv@chromium.org>
1 parent f72a998 commit bc179fc

1 file changed

Lines changed: 83 additions & 110 deletions

File tree

front_end/panels/coverage/CoverageListView.ts

Lines changed: 83 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44
/* eslint-disable rulesdir/no-imperative-dom-api */
5+
/* eslint-disable rulesdir/no-lit-render-outside-of-view */
56

67
import * as Common from '../../core/common/common.js';
78
import * as i18n from '../../core/i18n/i18n.js';
@@ -10,6 +11,7 @@ import * as TextUtils from '../../models/text_utils/text_utils.js';
1011
import * as Workspace from '../../models/workspace/workspace.js';
1112
import * as DataGrid from '../../ui/legacy/components/data_grid/data_grid.js';
1213
import * as UI from '../../ui/legacy/legacy.js';
14+
import {Directives, html, nothing, render} from '../../ui/lit/lit.js';
1315

1416
import coverageListViewStyles from './coverageListView.css.js';
1517
import {
@@ -113,6 +115,7 @@ const UIStrings = {
113115
} as const;
114116
const str_ = i18n.i18n.registerUIStrings('panels/coverage/CoverageListView.ts', UIStrings);
115117
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
118+
const {styleMap} = Directives;
116119

117120
export function coverageTypeToString(type: CoverageType): string {
118121
const types = [];
@@ -179,17 +182,18 @@ export class CoverageListView extends UI.Widget.VBox {
179182
weight: 1,
180183
},
181184
] as DataGrid.DataGrid.ColumnDescriptor[];
182-
this.dataGrid = new DataGrid.SortableDataGrid.SortableDataGrid<GridNode>({
183-
displayName: i18nString(UIStrings.codeCoverage),
184-
columns,
185-
refreshCallback: undefined,
186-
deleteCallback: undefined,
187-
});
185+
this.dataGrid =
186+
DataGrid.SortableDataGrid.SortableDataGrid.create(['dummy'], [], i18nString(UIStrings.codeCoverage)) as
187+
DataGrid.SortableDataGrid.SortableDataGrid<GridNode>;
188+
this.dataGrid.removeColumn('dummy');
189+
for (const column of columns) {
190+
this.dataGrid.addColumn(column);
191+
}
192+
this.dataGrid.setColumnsVisibility(new Set(columns.map(column => column.id)));
188193
this.dataGrid.setResizeMethod(DataGrid.DataGrid.ResizeMethod.LAST);
189194
this.dataGrid.setStriped(true);
190195
this.dataGrid.element.classList.add('flex-auto');
191196
this.dataGrid.addEventListener(DataGrid.DataGrid.Events.OPENED_NODE, this.onOpenedNode, this);
192-
this.dataGrid.addEventListener(DataGrid.DataGrid.Events.SORTING_CHANGED, this.sortingChanged, this);
193197

194198
const dataGridWidget = this.dataGrid.asWidget();
195199
dataGridWidget.show(this.contentElement);
@@ -222,7 +226,7 @@ export class CoverageListView extends UI.Widget.VBox {
222226
}
223227
}
224228
if (hadUpdates) {
225-
this.sortingChanged();
229+
this.dataGrid.dispatchEventToListeners(DataGrid.DataGrid.Events.SORTING_CHANGED);
226230
}
227231
}
228232

@@ -279,7 +283,7 @@ export class CoverageListView extends UI.Widget.VBox {
279283
}
280284
}
281285
if (hadTreeUpdates) {
282-
this.sortingChanged();
286+
this.dataGrid.dispatchEventToListeners(DataGrid.DataGrid.Events.SORTING_CHANGED);
283287
}
284288
}
285289

@@ -322,20 +326,6 @@ export class CoverageListView extends UI.Widget.VBox {
322326
void Common.Revealer.reveal(sourceCode);
323327
}
324328

325-
private sortingChanged(): void {
326-
const columnId = this.dataGrid.sortColumnId();
327-
if (!columnId) {
328-
return;
329-
}
330-
const sortFunction = GridNode.sortFunctionForColumn(columnId) as (
331-
(arg0: DataGrid.SortableDataGrid.SortableDataGridNode<GridNode>,
332-
arg1: DataGrid.SortableDataGrid.SortableDataGridNode<GridNode>) => number) |
333-
null;
334-
if (!sortFunction) {
335-
return;
336-
}
337-
this.dataGrid.sortNodes(sortFunction, !this.dataGrid.isSortOrderAscending());
338-
}
339329
}
340330

341331
let percentageFormatter: Intl.NumberFormat|null = null;
@@ -372,6 +362,15 @@ export class GridNode extends DataGrid.SortableDataGrid.SortableDataGridNode<Gri
372362
this.url = coverageInfo.url();
373363
this.maxSize = maxSize;
374364
this.highlightRegExp = null;
365+
this.#updateData();
366+
}
367+
368+
#updateData(): void {
369+
this.data['url'] = this.url;
370+
this.data['type'] = coverageTypeToString(this.coverageInfo.type());
371+
this.data['size'] = this.coverageInfo.size();
372+
this.data['unused-size'] = this.coverageInfo.unusedSize();
373+
this.data['bars'] = this.coverageInfo.unusedSize();
375374
}
376375

377376
setHighlight(highlightRegExp: RegExp|null): void {
@@ -389,95 +388,91 @@ export class GridNode extends DataGrid.SortableDataGrid.SortableDataGridNode<Gri
389388
this.lastUsedSize = this.coverageInfo.usedSize();
390389
this.maxSize = maxSize;
391390
this.refresh();
391+
this.#updateData();
392392
return true;
393393
}
394394

395395
override createCell(columnId: string): HTMLElement {
396396
const cell = this.createTD(columnId);
397+
const info = this.coverageInfo;
398+
const formatBytes = (value: number|undefined): string => {
399+
return getBytesFormatter().format(value ?? 0);
400+
};
401+
const formatPercent = (value: number|undefined): string => {
402+
return getPercentageFormatter().format(value ?? 0);
403+
};
397404
switch (columnId) {
398405
case 'url': {
399406
UI.Tooltip.Tooltip.install(cell, this.url);
400-
const outer = cell.createChild('div', 'url-outer');
401-
const prefix = outer.createChild('div', 'url-prefix');
402-
const suffix = outer.createChild('div', 'url-suffix');
407+
this.setCellAccessibleName(this.url, cell, columnId);
403408
const splitURL = /^(.*)(\/[^/]*)$/.exec(this.url);
404-
prefix.textContent = splitURL ? splitURL[1] : this.url;
405-
suffix.textContent = splitURL ? splitURL[2] : '';
409+
render(
410+
html`
411+
<div class="url-outer">
412+
<div class="url-prefix">${splitURL ? splitURL[1] : this.url}</div>
413+
<div class="url-suffix">${splitURL ? splitURL[2] : ''}</div>
414+
</div>`,
415+
cell);
406416
if (this.highlightRegExp) {
407-
this.highlight(outer, this.url);
417+
this.highlight(cell, this.url);
408418
}
409-
this.setCellAccessibleName(this.url, cell, columnId);
410419
break;
411420
}
412421
case 'type': {
413-
cell.textContent = coverageTypeToString(this.coverageInfo.type());
414-
if (this.coverageInfo.type() & CoverageType.JAVA_SCRIPT_PER_FUNCTION) {
415-
UI.Tooltip.Tooltip.install(cell, i18nString(UIStrings.jsCoverageWithPerFunction));
416-
} else if (this.coverageInfo.type() & CoverageType.JAVA_SCRIPT) {
417-
UI.Tooltip.Tooltip.install(cell, i18nString(UIStrings.jsCoverageWithPerBlock));
418-
}
422+
UI.Tooltip.Tooltip.install(
423+
cell,
424+
info.type() & CoverageType.JAVA_SCRIPT_PER_FUNCTION ? i18nString(UIStrings.jsCoverageWithPerFunction) :
425+
info.type() & CoverageType.JAVA_SCRIPT ? i18nString(UIStrings.jsCoverageWithPerBlock) :
426+
'');
427+
render(coverageTypeToString(this.coverageInfo.type()), cell);
419428
break;
420429
}
421430
case 'size': {
422-
const size = this.coverageInfo.size() || 0;
423-
const sizeSpan = cell.createChild('span');
424-
const sizeFormatted = getBytesFormatter().format(size);
425-
sizeSpan.textContent = sizeFormatted;
426-
const sizeAccessibleName = i18nString(UIStrings.sBytes, {n: size});
427-
this.setCellAccessibleName(sizeAccessibleName, cell, columnId);
431+
this.setCellAccessibleName(i18nString(UIStrings.sBytes, {n: info.size() || 0}), cell, columnId);
432+
render(html`<span>${formatBytes(info.size())}</span>`, cell);
428433
break;
429434
}
430435
case 'unused-size': {
431-
const unusedSize = this.coverageInfo.unusedSize() || 0;
432-
const unusedSizeSpan = cell.createChild('span');
433-
const unusedPercentsSpan = cell.createChild('span', 'percent-value');
434-
const unusedSizeFormatted = getBytesFormatter().format(unusedSize);
435-
unusedSizeSpan.textContent = unusedSizeFormatted;
436-
const unusedPercentFormatted = getPercentageFormatter().format(this.coverageInfo.unusedPercentage());
437-
unusedPercentsSpan.textContent = unusedPercentFormatted;
438-
const unusedAccessibleName = i18nString(UIStrings.sBytesS, {n: unusedSize, percentage: unusedPercentFormatted});
439-
this.setCellAccessibleName(unusedAccessibleName, cell, columnId);
436+
this.setCellAccessibleName(
437+
i18nString(UIStrings.sBytesS, {n: info.unusedSize(), percentage: formatPercent(info.unusedPercentage())}),
438+
cell, columnId);
439+
// clang-format off
440+
render(html`
441+
<span>${formatBytes(info.unusedSize())}</span>
442+
<span class="percent-value">
443+
${formatPercent(info.unusedPercentage())}
444+
</span>`, cell);
445+
// clang-format on
440446
break;
441447
}
442448
case 'bars': {
443-
const barContainer = cell.createChild('div', 'bar-container');
444-
const unusedPercent = getPercentageFormatter().format(this.coverageInfo.unusedPercentage());
445-
const usedPercent = getPercentageFormatter().format(this.coverageInfo.usedPercentage());
446-
if (this.coverageInfo.unusedSize() > 0) {
447-
const unusedSizeBar = barContainer.createChild('div', 'bar bar-unused-size');
448-
unusedSizeBar.style.width = ((this.coverageInfo.unusedSize() / this.maxSize) * 100 || 0) + '%';
449-
if (this.coverageInfo.type() & CoverageType.JAVA_SCRIPT_PER_FUNCTION) {
450-
UI.Tooltip.Tooltip.install(
451-
unusedSizeBar,
452-
i18nString(
453-
UIStrings.sBytesSBelongToFunctionsThatHave,
454-
{PH1: this.coverageInfo.unusedSize(), PH2: unusedPercent}));
455-
} else if (this.coverageInfo.type() & CoverageType.JAVA_SCRIPT) {
456-
UI.Tooltip.Tooltip.install(
457-
unusedSizeBar,
458-
i18nString(
459-
UIStrings.sBytesSBelongToBlocksOf, {PH1: this.coverageInfo.unusedSize(), PH2: unusedPercent}));
460-
}
461-
}
462-
if (this.coverageInfo.usedSize() > 0) {
463-
const usedSizeBar = barContainer.createChild('div', 'bar bar-used-size');
464-
usedSizeBar.style.width = ((this.coverageInfo.usedSize() / this.maxSize) * 100 || 0) + '%';
465-
if (this.coverageInfo.type() & CoverageType.JAVA_SCRIPT_PER_FUNCTION) {
466-
UI.Tooltip.Tooltip.install(
467-
usedSizeBar,
468-
i18nString(
469-
UIStrings.sBytesSBelongToFunctionsThatHaveExecuted,
470-
{PH1: this.coverageInfo.usedSize(), PH2: usedPercent}));
471-
} else if (this.coverageInfo.type() & CoverageType.JAVA_SCRIPT) {
472-
UI.Tooltip.Tooltip.install(
473-
usedSizeBar,
474-
i18nString(
475-
UIStrings.sBytesSBelongToBlocksOfJavascript,
476-
{PH1: this.coverageInfo.usedSize(), PH2: usedPercent}));
477-
}
478-
}
479449
this.setCellAccessibleName(
480-
i18nString(UIStrings.sOfFileUnusedSOfFileUsed, {PH1: unusedPercent, PH2: usedPercent}), cell, columnId);
450+
i18nString(
451+
UIStrings.sOfFileUnusedSOfFileUsed,
452+
{PH1: formatPercent(info.unusedPercentage()), PH2: formatPercent(info.usedPercentage())}),
453+
cell, columnId);
454+
// clang-format off
455+
render(html`
456+
<div class="bar-container">
457+
${info.unusedSize() > 0 ? html`
458+
<div class="bar bar-unused-size"
459+
title=${
460+
info.type() & CoverageType.JAVA_SCRIPT_PER_FUNCTION ? i18nString(UIStrings.sBytesSBelongToFunctionsThatHave, {PH1: info.unusedSize(), PH2: formatPercent(info.unusedPercentage())}) :
461+
info.type() & CoverageType.JAVA_SCRIPT ? i18nString(UIStrings.sBytesSBelongToBlocksOf, {PH1: info.unusedSize(), PH2: formatPercent(info.unusedPercentage())}) :
462+
''}
463+
style=${styleMap({width: ((info.unusedSize() / this.maxSize) * 100 || 0) + '%'})}>
464+
</div>` : nothing}
465+
${info.usedSize() > 0 ? html`
466+
<div class="bar bar-used-size"
467+
title=${
468+
info.type() & CoverageType.JAVA_SCRIPT_PER_FUNCTION ? i18nString(UIStrings.sBytesSBelongToFunctionsThatHaveExecuted, {PH1: info.usedSize(), PH2: formatPercent(info.usedPercentage())}) :
469+
info.type() & CoverageType.JAVA_SCRIPT ? i18nString(UIStrings.sBytesSBelongToBlocksOfJavascript, {PH1: info.usedSize(), PH2: formatPercent(info.usedPercentage())}) :
470+
''}
471+
{ PH1: info.usedSize(), PH2: formatPercent(info.usedPercentage()) })}
472+
style=${styleMap({width:((info.usedSize() / this.maxSize) * 100 || 0) + '%'})}>
473+
</div>` : nothing}
474+
</div>`, cell);
475+
// clang-format on
481476
}
482477
}
483478
return cell;
@@ -495,26 +490,4 @@ export class GridNode extends DataGrid.SortableDataGrid.SortableDataGridNode<Gri
495490
UI.UIUtils.highlightRangesWithStyleClass(element, [range], 'filter-highlight');
496491
}
497492

498-
static sortFunctionForColumn(columnId: string): ((arg0: GridNode, arg1: GridNode) => number)|null {
499-
const compareURL = (a: GridNode, b: GridNode): number => a.url.localeCompare(b.url);
500-
switch (columnId) {
501-
case 'url':
502-
return compareURL;
503-
case 'type':
504-
return (a: GridNode, b: GridNode) => {
505-
const typeA = coverageTypeToString(a.coverageInfo.type());
506-
const typeB = coverageTypeToString(b.coverageInfo.type());
507-
return typeA.localeCompare(typeB) || compareURL(a, b);
508-
};
509-
case 'size':
510-
return (a: GridNode, b: GridNode) => a.coverageInfo.size() - b.coverageInfo.size() || compareURL(a, b);
511-
case 'bars':
512-
case 'unused-size':
513-
return (a: GridNode, b: GridNode) =>
514-
a.coverageInfo.unusedSize() - b.coverageInfo.unusedSize() || compareURL(a, b);
515-
default:
516-
console.assert(false, 'Unknown sort field: ' + columnId);
517-
return null;
518-
}
519-
}
520493
}

0 commit comments

Comments
 (0)