Skip to content

Commit 2e725f7

Browse files
danilsomsikovDevtools-frontend LUCI CQ
authored andcommitted
Use <devtools-new-data-grid> in the Autofill View
To make it highlight on hover work, added delegation for mouseenter and mouseleave events Bug: 390346490 Change-Id: I0275d9e41de80b2c5f93482d7a9268054f8c820d Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6214430 Reviewed-by: Wolfgang Beyer <[email protected]> Commit-Queue: Danil Somsikov <[email protected]>
1 parent 1c1db39 commit 2e725f7

File tree

7 files changed

+100
-130
lines changed

7 files changed

+100
-130
lines changed

front_end/panels/autofill/AutofillView.test.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import * as Root from '../../core/root/root.js';
66
import * as SDK from '../../core/sdk/sdk.js';
77
import * as Protocol from '../../generated/protocol.js';
88
import * as AutofillManager from '../../models/autofill_manager/autofill_manager.js';
9-
import {assertGridContents, getBodyRowByAriaIndex, getDataGrid} from '../../testing/DataGridHelpers.js';
9+
import {assertGridContents} from '../../testing/DataGridHelpers.js';
1010
import {renderElementIntoDOM} from '../../testing/DOMHelpers.js';
1111
import {createTarget, stubNoopSettings} from '../../testing/EnvironmentHelpers.js';
1212
import {describeWithMockConnection} from '../../testing/MockConnection.js';
@@ -110,9 +110,12 @@ describeWithMockConnection('AutofillView', () => {
110110

111111
const renderAutofillView = async () => {
112112
const view = new Autofill.AutofillView.AutofillView();
113+
view.style.display = 'block';
114+
view.style.width = '640px';
115+
view.style.height = '480px';
113116
renderElementIntoDOM(view);
114117
await view.render();
115-
await RenderCoordinator.done();
118+
await RenderCoordinator.done({waitForWork: true});
116119
return view;
117120
};
118121

@@ -121,12 +124,12 @@ describeWithMockConnection('AutofillView', () => {
121124
const addressText = [...addressSpans].map(div => div.textContent);
122125
assert.deepEqual(
123126
addressText, ['Crocodile', ' Middle ', 'Dundee', 'Uluru ToursOutback Road 1Bundaberg Queensland ', '12345']);
124-
const expectedHeaders = ['Form field', 'Predicted autofill value', 'Value', 'filledFieldIndex'];
127+
const expectedHeaders = ['Form field', 'Predicted autofill value', 'Value'];
125128
const expectedRows = [
126-
['#input1 (text)', 'First name \nheur', '"Crocodile"', ''],
127-
['input2 (text)', 'Last name \nheur', '"Dundee"', ''],
128-
['#input3 (text)', 'Country \nheur', '"Australia"', ''],
129-
['#input4 (text)', 'Zip code \nattr', '"12345"', ''],
129+
['#input1 (text)', 'First name \nheur', '"Crocodile"'],
130+
['input2 (text)', 'Last name \nheur', '"Dundee"'],
131+
['#input3 (text)', 'Country \nheur', '"Australia"'],
132+
['#input4 (text)', 'Zip code \nattr', '"12345"'],
130133
];
131134
assertGridContents(view, expectedHeaders, expectedRows);
132135
};
@@ -219,9 +222,9 @@ describeWithMockConnection('AutofillView', () => {
219222
const crocodileSpan = addressSpans[0];
220223
assert.strictEqual(crocodileSpan.textContent, 'Crocodile');
221224
assert.isFalse(crocodileSpan.classList.contains('highlighted'));
222-
const grid = getDataGrid(view);
225+
const grid = view.shadowRoot!.querySelector('devtools-new-data-grid')!;
223226
assert.isNotNull(grid.shadowRoot);
224-
const firstGridRow = getBodyRowByAriaIndex(grid.shadowRoot, 1);
227+
const firstGridRow = grid.shadowRoot!.querySelector('tbody tr[jslog]')!;
225228
let styles = firstGridRow.getAttribute('style') || '';
226229
assert.strictEqual(styles.replace(/\s/g, ''), monospaceStyles);
227230

@@ -265,9 +268,9 @@ describeWithMockConnection('AutofillView', () => {
265268
const zipCodeSpan = addressSpans[4];
266269
assert.strictEqual(zipCodeSpan.textContent, '12345');
267270
assert.isFalse(zipCodeSpan.classList.contains('highlighted'));
268-
const grid = getDataGrid(view);
271+
const grid = view.shadowRoot!.querySelector('devtools-new-data-grid')!;
269272
assert.isNotNull(grid.shadowRoot);
270-
const fourthGridRow = getBodyRowByAriaIndex(grid.shadowRoot, 4);
273+
const fourthGridRow = grid.shadowRoot!.querySelector('tbody tr[jslog]:nth-child(5)')!;
271274
fourthGridRow.dispatchEvent(new MouseEvent('mouseenter'));
272275
await RenderCoordinator.done({waitForWork: true});
273276
assert.isTrue(zipCodeSpan.classList.contains('highlighted'));

front_end/panels/autofill/AutofillView.ts

Lines changed: 43 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,14 @@
33
// found in the LICENSE file.
44

55
import '../../ui/components/adorners/adorners.js';
6-
import '../../ui/components/data_grid/data_grid.js';
6+
import '../../ui/legacy/components/data_grid/data_grid.js';
77

88
import * as Common from '../../core/common/common.js';
99
import * as i18n from '../../core/i18n/i18n.js';
1010
import type * as Platform from '../../core/platform/platform.js';
1111
import * as SDK from '../../core/sdk/sdk.js';
1212
import * as Protocol from '../../generated/protocol.js';
1313
import * as AutofillManager from '../../models/autofill_manager/autofill_manager.js';
14-
import type * as DataGrid from '../../ui/components/data_grid/data_grid.js';
1514
import * as ComponentHelpers from '../../ui/components/helpers/helpers.js';
1615
import * as Input from '../../ui/components/input/input.js';
1716
import * as LegacyWrapper from '../../ui/components/legacy_wrapper/legacy_wrapper.js';
@@ -20,7 +19,8 @@ import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
2019

2120
import autofillViewStyles from './autofillView.css.js';
2221

23-
const {html} = Lit;
22+
const {html, render, Directives: {styleMap}} = Lit;
23+
const {FillingStrategy} = Protocol.Autofill;
2424

2525
const UIStrings = {
2626
/**
@@ -166,7 +166,7 @@ export class AutofillView extends LegacyWrapper.LegacyWrapper.WrappableComponent
166166
if (!this.#address && !this.#filledFields.length) {
167167
// Disabled until https://crbug.com/1079231 is fixed.
168168
// clang-format off
169-
Lit.render(html`
169+
render(html`
170170
<main>
171171
<div class="top-left-corner">
172172
<label class="checkbox-label" title=${i18nString(UIStrings.showTestAddressesInAutofillMenu)}>
@@ -201,7 +201,7 @@ export class AutofillView extends LegacyWrapper.LegacyWrapper.WrappableComponent
201201

202202
// Disabled until https://crbug.com/1079231 is fixed.
203203
// clang-format off
204-
Lit.render(html`
204+
render(html`
205205
<main>
206206
<div class="content-container" jslog=${VisualLogging.pane('autofill')}>
207207
<div class="right-to-left" role="region" aria-label=${i18nString(UIStrings.addressPreview)}>
@@ -320,64 +320,54 @@ export class AutofillView extends LegacyWrapper.LegacyWrapper.WrappableComponent
320320
return Lit.nothing;
321321
}
322322

323-
const gridData: DataGrid.DataGridController.DataGridControllerData = {
324-
columns: [
325-
{
326-
id: 'name',
327-
title: i18nString(UIStrings.formField),
328-
widthWeighting: 50,
329-
hideable: false,
330-
visible: true,
331-
sortable: true,
332-
},
333-
{
334-
id: 'autofill-type',
335-
title: i18nString(UIStrings.predictedAutofillValue),
336-
widthWeighting: 50,
337-
hideable: false,
338-
visible: true,
339-
sortable: true,
340-
},
341-
{
342-
id: 'value',
343-
title: i18nString(UIStrings.value),
344-
widthWeighting: 50,
345-
hideable: false,
346-
visible: true,
347-
sortable: true,
348-
},
349-
{
350-
id: 'filled-field-index',
351-
title: 'filledFieldIndex',
352-
widthWeighting: 50,
353-
hideable: true,
354-
visible: false,
355-
},
356-
],
357-
rows: this.#buildReportRows(),
358-
striped: true,
359-
};
360-
323+
const highlightedGridRows = new Set(this.#highlightedMatches.map(match => match.filledFieldIndex));
361324
// Disabled until https://crbug.com/1079231 is fixed.
362325
// clang-format off
363326
return html`
364327
<div class="grid-wrapper" role="region" aria-label=${i18nString(UIStrings.formInspector)}>
365-
<devtools-data-grid-controller
366-
@rowmouseenter=${this.#onGridRowMouseEnter}
367-
@rowmouseleave=${this.#onGridRowMouseLeave}
328+
<devtools-new-data-grid
329+
striped
368330
class="filled-fields-grid"
369-
.data=${gridData}>
370-
</devtools-data-grid-controller>
331+
>
332+
<table>
333+
<tr>
334+
<th id="name" weight="50" sortable>${i18nString(UIStrings.formField)}</th>
335+
<th id="autofill-type" weight="50" sortable>${i18nString(UIStrings.predictedAutofillValue)}</th>
336+
<th id="value" weight="50" sortable>${i18nString(UIStrings.value)}</th>
337+
</tr>
338+
${this.#filledFields.map((field, index) => html`
339+
<tr style=${styleMap({
340+
'font-family': 'var(--monospace-font-family)',
341+
'font-size': 'var(--monospace-font-size)',
342+
'background-color': highlightedGridRows.has(index) ? 'var(--sys-color-state-hover-on-subtle)' : null,
343+
})}
344+
@mouseenter=${() => this.#onGridRowMouseEnter(index)}
345+
@mouseleave=${this.#onGridRowMouseLeave.bind(this)}
346+
>
347+
<td>${field.name || `#${field.id}`} (${field.htmlType})</td>
348+
<td>
349+
${field.autofillType}
350+
${field.fillingStrategy === FillingStrategy.AutocompleteAttribute ?
351+
html`<devtools-adorner title=${i18nString(UIStrings.autocompleteAttribute)} .data=${{name: field.fillingStrategy}}>
352+
<span slot="content">${i18nString(UIStrings.attr)}</span>
353+
</devtools-adorner>` :
354+
field.fillingStrategy === FillingStrategy.AutofillInferred ?
355+
html`<devtools-adorner title=${i18nString(UIStrings.inferredByHeuristics)} .data=${{name: field.fillingStrategy}}>
356+
<span slot="content">${i18nString(UIStrings.heur)}</span>
357+
</devtools-adorner>` :
358+
Lit.nothing}
359+
</td>
360+
<td>"${field.value}"</td>
361+
</tr>`
362+
)}
363+
</table>
364+
</devtools-new-data-grid>
371365
</div>
372366
`;
373367
// clang-format on
374368
}
375369

376-
#onGridRowMouseEnter(event: DataGrid.DataGridEvents.RowMouseEnterEvent): void {
377-
const rowIndex = event.data.row.cells[3].value;
378-
if (typeof rowIndex !== 'number') {
379-
return;
380-
}
370+
#onGridRowMouseEnter(rowIndex: number): void {
381371
this.#highlightedMatches = this.#matches.filter(match => match.filledFieldIndex === rowIndex);
382372
void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#renderBound);
383373

@@ -400,57 +390,6 @@ export class AutofillView extends LegacyWrapper.LegacyWrapper.WrappableComponent
400390
void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#renderBound);
401391
SDK.OverlayModel.OverlayModel.hideDOMNodeHighlight();
402392
}
403-
404-
#buildReportRows(): DataGrid.DataGridUtils.Row[] {
405-
const highlightedGridRows = new Set(this.#highlightedMatches.map(match => match.filledFieldIndex));
406-
407-
return this.#filledFields.map(
408-
(field, index) => {
409-
const fieldName = field.name || `#${field.id}`;
410-
return {
411-
cells: [
412-
{columnId: 'name', value: `${fieldName} (${field.htmlType})`},
413-
{
414-
columnId: 'autofill-type',
415-
value: field.autofillType,
416-
renderer: () => this.#autofillTypeRenderer(field.autofillType, field.fillingStrategy),
417-
},
418-
{columnId: 'value', value: `"${field.value}"`},
419-
{columnId: 'filled-field-index', value: index},
420-
],
421-
styles: {
422-
'font-family': 'var(--monospace-font-family)',
423-
'font-size': 'var(--monospace-font-size)',
424-
...(highlightedGridRows.has(index) && {'background-color': 'var(--sys-color-state-hover-on-subtle)'}),
425-
},
426-
};
427-
},
428-
);
429-
}
430-
431-
#autofillTypeRenderer(autofillType: string, fillingStrategy: Protocol.Autofill.FillingStrategy): Lit.TemplateResult {
432-
const adornerContent = document.createElement('span');
433-
let adornerTitle = '';
434-
switch (fillingStrategy) {
435-
case Protocol.Autofill.FillingStrategy.AutocompleteAttribute:
436-
adornerContent.textContent = i18nString(UIStrings.attr);
437-
adornerTitle = i18nString(UIStrings.autocompleteAttribute);
438-
break;
439-
case Protocol.Autofill.FillingStrategy.AutofillInferred:
440-
adornerContent.textContent = i18nString(UIStrings.heur);
441-
adornerTitle = i18nString(UIStrings.inferredByHeuristics);
442-
}
443-
444-
// Disabled until https://crbug.com/1079231 is fixed.
445-
// clang-format off
446-
return html`
447-
${autofillType}
448-
${adornerContent.textContent ? html`
449-
<devtools-adorner title=${adornerTitle} .data=${{name: fillingStrategy, content: adornerContent}}></devtools-adorner>
450-
`: Lit.nothing}
451-
`;
452-
// clang-format on
453-
}
454393
}
455394

456395
customElements.define('devtools-autofill-view', AutofillView);

front_end/panels/autofill/BUILD.gn

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ devtools_module("autofill") {
2020
"../../core/platform:bundle",
2121
"../../core/sdk:bundle",
2222
"../../models/autofill_manager:bundle",
23-
"../../ui/components/data_grid:bundle",
2423
"../../ui/components/legacy_wrapper:bundle",
24+
"../../ui/legacy/components/data_grid:bundle",
2525
]
2626
}
2727

front_end/panels/autofill/autofillView.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ main {
4646
flex-grow: 1;
4747
}
4848

49+
devtools-new-data-grid {
50+
border: none;
51+
height: 100%;
52+
}
53+
4954
.checkbox-label {
5055
display: flex;
5156
align-items: center;

front_end/ui/components/adorners/Adorner.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const {render, html} = Lit;
1212

1313
export interface AdornerData {
1414
name: string;
15-
content: HTMLElement;
15+
content?: HTMLElement;
1616
jslogContext?: string;
1717
}
1818

@@ -29,13 +29,21 @@ export class Adorner extends HTMLElement {
2929
set data(data: AdornerData) {
3030
this.name = data.name;
3131
this.#jslogContext = data.jslogContext;
32-
data.content.slot = 'content';
33-
this.#content?.remove();
34-
this.append(data.content);
35-
this.#content = data.content;
32+
if (data.content) {
33+
data.content.slot = 'content';
34+
this.#content?.remove();
35+
this.append(data.content);
36+
this.#content = data.content;
37+
}
3638
this.#render();
3739
}
3840

41+
override cloneNode(deep?: boolean): Node {
42+
const node = super.cloneNode(deep) as Adorner;
43+
node.data = {name: this.name, content: this.#content, jslogContext: this.#jslogContext};
44+
return node;
45+
}
46+
3947
connectedCallback(): void {
4048
if (!this.getAttribute('aria-label')) {
4149
this.setAttribute('aria-label', this.name);

front_end/ui/legacy/components/data_grid/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ devtools_entrypoint("bundle") {
4040
visibility = [
4141
":*",
4242
"../../../../panels/application/*",
43+
"../../../../panels/autofill/*",
4344
"../../../../panels/console/*",
4445
"../../../../panels/coverage/*",
4546
"../../../../panels/css_overview/*",

front_end/ui/legacy/components/data_grid/DataGridElement.ts

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -280,17 +280,32 @@ class DataGridElementNode extends SortableDataGridNode<DataGridElementNode> {
280280
const columnId = this.#dataGridElement.columnsOrder[i];
281281
this.data[columnId] = cell.dataset.value ?? cell.textContent ?? '';
282282
}
283+
if (this.#configElement.hasAttribute('selected')) {
284+
this.select();
285+
}
286+
}
287+
288+
override createElement(): HTMLElement {
289+
const element = super.createElement();
290+
element.addEventListener('click', this.#onRowMouseEvent.bind(this));
291+
element.addEventListener('mouseenter', this.#onRowMouseEvent.bind(this));
292+
element.addEventListener('mouseleave', this.#onRowMouseEvent.bind(this));
293+
if (this.#configElement.hasAttribute('style')) {
294+
element.setAttribute('style', this.#configElement.getAttribute('style') || '');
295+
}
296+
return element;
283297
}
284298

285299
override refresh(): void {
286300
this.#updateData();
287-
if (this.#configElement.hasAttribute('selected')) {
288-
this.select();
289-
}
290301
super.refresh();
302+
const existingElement = this.existingElement();
303+
if (existingElement && this.#configElement.hasAttribute('style')) {
304+
existingElement.setAttribute('style', this.#configElement.getAttribute('style') || '');
305+
}
291306
}
292307

293-
#onCellClick(configCell: HTMLElement, event: MouseEvent): void {
308+
#onRowMouseEvent(event: MouseEvent): void {
294309
let currentElement = event.target as HTMLElement;
295310
const childIndexesOnPathToRoot: number[] = [];
296311
while (currentElement?.parentElement && currentElement !== event.currentTarget) {
@@ -300,12 +315,12 @@ class DataGridElementNode extends SortableDataGridNode<DataGridElementNode> {
300315
if (!currentElement) {
301316
throw new Error('Cell click event target not found in the data grid');
302317
}
303-
let targetInConfigCell: Element = configCell;
318+
let targetInConfigRow = this.#configElement;
304319
for (const index of childIndexesOnPathToRoot.reverse()) {
305-
targetInConfigCell = targetInConfigCell.children[index];
320+
targetInConfigRow = targetInConfigRow.children[index];
306321
}
307-
if (targetInConfigCell instanceof HTMLElement) {
308-
targetInConfigCell?.click();
322+
if (targetInConfigRow instanceof HTMLElement) {
323+
targetInConfigRow?.dispatchEvent(new MouseEvent(event.type, {bubbles: true, composed: true}));
309324
}
310325
}
311326

@@ -316,7 +331,6 @@ class DataGridElementNode extends SortableDataGridNode<DataGridElementNode> {
316331
throw new Error(`Column ${columnId} not found in the data grid`);
317332
}
318333
const cell = this.createTD(columnId);
319-
cell.addEventListener('click', this.#onCellClick.bind(this, configCell));
320334
for (const child of configCell.childNodes) {
321335
cell.appendChild(child.cloneNode(true));
322336
}

0 commit comments

Comments
 (0)