chrome-devtools-frontend 1.0.1518653 → 1.0.1520139
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/owner/COMMON_OWNERS +1 -2
- package/config/typescript/tsconfig.eslint.json +12 -1
- package/docs/ui_engineering.md +1011 -0
- package/eslint.config.mjs +1 -0
- package/front_end/core/host/GdpClient.ts +12 -3
- package/front_end/core/sdk/EnhancedTracesParser.ts +5 -5
- package/front_end/core/sdk/NetworkManager.ts +1 -0
- package/front_end/core/sdk/NetworkRequest.ts +10 -0
- package/front_end/core/sdk/RehydratingConnection.snapshot.txt +211 -0
- package/front_end/core/sdk/TargetManager.ts +4 -0
- package/front_end/entrypoints/main/MainImpl.ts +6 -1
- package/front_end/entrypoints/main/main-meta.ts +3 -3
- package/front_end/generated/SupportedCSSProperties.js +19 -4
- package/front_end/models/ai_assistance/agents/AiAgent.ts +57 -10
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +64 -87
- package/front_end/models/ai_assistance/agents/PerformanceAnnotationsAgent.ts +4 -4
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +0 -31
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +127 -29
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +106 -55
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +317 -640
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +23 -19
- package/front_end/models/ai_assistance/performance/AICallTree.snapshot.txt +75 -0
- package/front_end/models/ai_assistance/performance/AICallTree.ts +14 -6
- package/front_end/models/ai_assistance/performance/AIContext.ts +63 -8
- package/front_end/models/ai_code_completion/AiCodeCompletion.ts +5 -0
- package/front_end/models/badges/AiExplorerBadge.ts +19 -3
- package/front_end/models/badges/Badge.ts +8 -1
- package/front_end/models/badges/CodeWhispererBadge.ts +1 -0
- package/front_end/models/badges/DOMDetectiveBadge.ts +1 -0
- package/front_end/models/badges/SpeedsterBadge.ts +1 -0
- package/front_end/models/badges/StarterBadge.ts +6 -0
- package/front_end/models/badges/badges.ts +1 -0
- package/front_end/models/javascript_metadata/NativeFunctions.js +4 -0
- package/front_end/models/trace/EventsSerializer.ts +4 -3
- package/front_end/models/trace/handlers/UserInteractionsHandler.ts +101 -73
- package/front_end/models/trace/helpers/Timing.ts +1 -1
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +18 -8
- package/front_end/panels/ai_assistance/PatchWidget.ts +17 -55
- package/front_end/panels/ai_assistance/components/ChatView.ts +44 -68
- package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +63 -15
- package/front_end/panels/ai_assistance/components/chatView.css +12 -0
- package/front_end/panels/animation/AnimationTimeline.ts +1 -1
- package/front_end/panels/animation/animationTimeline.css +4 -0
- package/front_end/panels/application/components/BounceTrackingMitigationsView.ts +2 -2
- package/front_end/panels/application/preloading/components/PreloadingString.ts +2 -5
- package/front_end/panels/common/AiCodeCompletionTeaser.ts +5 -0
- package/front_end/panels/common/BadgeNotification.ts +3 -3
- package/front_end/panels/common/GdpSignUpDialog.ts +3 -4
- package/front_end/panels/common/aiCodeCompletionTeaser.css +6 -1
- package/front_end/panels/console/ConsolePrompt.ts +6 -0
- package/front_end/panels/console/ConsoleView.ts +4 -2
- package/front_end/panels/coverage/CoverageListView.ts +133 -158
- package/front_end/panels/coverage/CoverageView.ts +39 -16
- package/front_end/panels/css_overview/CSSOverviewCompletedView.ts +5 -5
- package/front_end/panels/mobile_throttling/NetworkThrottlingSelector.ts +2 -0
- package/front_end/panels/network/NetworkDataGridNode.ts +22 -0
- package/front_end/panels/network/NetworkLogViewColumns.ts +9 -0
- package/front_end/panels/recorder/components/CreateRecordingView.ts +2 -0
- package/front_end/panels/recorder/components/RecordingView.ts +2 -2
- package/front_end/panels/search/SearchResultsPane.ts +186 -134
- package/front_end/panels/search/SearchView.ts +42 -36
- package/front_end/panels/search/searchResultsPane.css +9 -0
- package/front_end/panels/search/searchView.css +0 -2
- package/front_end/panels/security/CookieControlsView.ts +2 -1
- package/front_end/panels/settings/AISettingsTab.ts +6 -3
- package/front_end/panels/settings/components/SyncSection.ts +26 -12
- package/front_end/panels/settings/emulation/components/UserAgentClientHintsForm.ts +1 -1
- package/front_end/panels/sources/AiCodeCompletionPlugin.ts +4 -4
- package/front_end/panels/sources/DebuggerPlugin.ts +4 -0
- package/front_end/panels/sources/SourcesPanel.ts +1 -1
- package/front_end/panels/sources/sourcesView.css +6 -1
- package/front_end/panels/timeline/ThirdPartyTreeView.ts +1 -1
- package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +0 -8
- package/front_end/panels/timeline/TimelineFlameChartView.ts +5 -5
- package/front_end/panels/timeline/TimelinePanel.ts +2 -0
- package/front_end/panels/timeline/TimelineTreeView.ts +1 -1
- package/front_end/panels/timeline/components/LayoutShiftDetails.ts +1 -1
- package/front_end/panels/timeline/components/NetworkRequestDetails.ts +1 -1
- package/front_end/panels/timeline/components/RelatedInsightChips.ts +1 -1
- package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +1 -1
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/puppeteer/README.chromium +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Realm.d.ts +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/injected.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/injected.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.js +1 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.js +15 -16
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +3 -3
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +3 -3
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.js +16 -25
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
- package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +19 -28
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/injected.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/injected.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.js +1 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.js +15 -16
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +3 -3
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +3 -3
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.js +16 -25
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.js.map +1 -1
- package/front_end/third_party/puppeteer/package/package.json +12 -4
- package/front_end/third_party/puppeteer/package/src/generated/injected.ts +1 -1
- package/front_end/third_party/puppeteer/package/src/generated/version.ts +1 -1
- package/front_end/third_party/puppeteer/package/src/node/BrowserLauncher.ts +1 -1
- package/front_end/third_party/puppeteer/package/src/node/ChromeLauncher.ts +1 -0
- package/front_end/third_party/puppeteer/package/src/node/PipeTransport.ts +15 -17
- package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
- package/front_end/third_party/puppeteer/package/src/util/Function.ts +22 -30
- package/front_end/tsconfig.json +12 -1
- package/front_end/ui/components/dialogs/Dialog.ts +1 -1
- package/front_end/ui/components/markdown_view/MarkdownImage.ts +4 -5
- package/front_end/ui/components/switch/SwitchImpl.ts +12 -1
- package/front_end/ui/components/text_editor/config.ts +16 -2
- package/front_end/ui/legacy/InspectorView.ts +86 -13
- package/front_end/ui/legacy/TabbedPane.ts +2 -1
- package/front_end/ui/legacy/Treeoutline.ts +3 -1
- package/front_end/ui/legacy/components/source_frame/XMLView.ts +12 -11
- package/front_end/ui/lit/i18n-template.ts +5 -2
- package/front_end/ui/visual_logging/KnownContextValues.ts +15 -5
- package/front_end/ui/visual_logging/LoggingEvents.ts +1 -1
- package/package.json +1 -1
package/docs/ui_engineering.md
CHANGED
@@ -212,3 +212,1014 @@ setMockConnectionResponseHandler('CSS.getHeaders', () => ({}));
|
|
212
212
|
const presenter = new Presenter();
|
213
213
|
presenter.doSomething();
|
214
214
|
```
|
215
|
+
|
216
|
+
# Migrating Widgets and other "legacy" components
|
217
|
+
|
218
|
+
This section provides a series of examples for migrating from imperative DOM manipulation to a declarative approach using lit-html templates.
|
219
|
+
|
220
|
+
## Setting `className` on `this.element`
|
221
|
+
|
222
|
+
Instead of setting `className` directly on `this.element`, define the component's structure declaratively using a lit-html template.
|
223
|
+
|
224
|
+
**Before:**
|
225
|
+
```typescript
|
226
|
+
class SomeWidget extends UI.Widget.Widget {
|
227
|
+
constructor() {
|
228
|
+
super();
|
229
|
+
this.element.className = 'some-class';
|
230
|
+
}
|
231
|
+
}
|
232
|
+
```
|
233
|
+
|
234
|
+
**After:**
|
235
|
+
```typescript
|
236
|
+
|
237
|
+
export const DEFAULT_VIEW = (input, _output, target) => {
|
238
|
+
render(html`
|
239
|
+
<div class="some-class"></div>`,
|
240
|
+
target, {host: input});
|
241
|
+
};
|
242
|
+
```
|
243
|
+
|
244
|
+
## Appending a new element
|
245
|
+
|
246
|
+
Instead of using `appendChild` with `document.createElement`, define the new element within a lit-html template.
|
247
|
+
|
248
|
+
**Before:**
|
249
|
+
```typescript
|
250
|
+
|
251
|
+
class SomeWidget extends UI.Widget.Widget {
|
252
|
+
constructor() {
|
253
|
+
super();
|
254
|
+
this.contentElement.appendChild(document.createElement('div'));
|
255
|
+
}
|
256
|
+
}
|
257
|
+
```
|
258
|
+
|
259
|
+
**After:**
|
260
|
+
```typescript
|
261
|
+
|
262
|
+
|
263
|
+
export const DEFAULT_VIEW = (input, _output, target) => {
|
264
|
+
render(html`
|
265
|
+
<div>
|
266
|
+
<div></div>
|
267
|
+
</div>`,
|
268
|
+
target, {host: input});
|
269
|
+
};
|
270
|
+
```
|
271
|
+
|
272
|
+
## Setting multiple attributes and `textContent`
|
273
|
+
|
274
|
+
Combine setting `className`, attributes, and `textContent` into a single declarative lit-html template.
|
275
|
+
|
276
|
+
**Before:**
|
277
|
+
```typescript
|
278
|
+
|
279
|
+
class SomeWidget extends UI.Widget.Widget {
|
280
|
+
constructor() {
|
281
|
+
super();
|
282
|
+
this.contentElement.className = 'some-class';
|
283
|
+
this.contentElement.setAttribute('aria-label', 'some-label');
|
284
|
+
this.contentElement.textContent = 'some-text';
|
285
|
+
}
|
286
|
+
}
|
287
|
+
```
|
288
|
+
|
289
|
+
**After:**
|
290
|
+
```typescript
|
291
|
+
|
292
|
+
|
293
|
+
export const DEFAULT_VIEW = (input, _output, target) => {
|
294
|
+
render(html`
|
295
|
+
<div class="some-class" aria-label="some-label">some-text</div>`,
|
296
|
+
target, {host: input});
|
297
|
+
};
|
298
|
+
```
|
299
|
+
|
300
|
+
## Creating a child, adding a class, and adding an event listener
|
301
|
+
|
302
|
+
Use a lit-html template to create the element, set multiple classes, and attach event listeners declaratively.
|
303
|
+
|
304
|
+
**Before:**
|
305
|
+
```typescript
|
306
|
+
|
307
|
+
class SomeWidget extends UI.Widget.Widget {
|
308
|
+
constructor() {
|
309
|
+
super();
|
310
|
+
this.container = this.contentElement.createChild('div', 'some-class');
|
311
|
+
this.container.classList.add('container');
|
312
|
+
this.container.addEventListener('click', this.onClick.bind(this));
|
313
|
+
}
|
314
|
+
}
|
315
|
+
```
|
316
|
+
|
317
|
+
**After:**
|
318
|
+
```typescript
|
319
|
+
|
320
|
+
|
321
|
+
export const DEFAULT_VIEW = (input, _output, target) => {
|
322
|
+
render(html`
|
323
|
+
<div>
|
324
|
+
<div class="some-class container" @click=${this.onClick.bind(this)}></div>
|
325
|
+
</div>`,
|
326
|
+
target, {host: input});
|
327
|
+
};
|
328
|
+
```
|
329
|
+
|
330
|
+
## Setting inline styles
|
331
|
+
|
332
|
+
Set inline styles directly within the lit-html template using the `style` attribute.
|
333
|
+
|
334
|
+
**Before:**
|
335
|
+
```typescript
|
336
|
+
|
337
|
+
class SomeWidget extends UI.Widget.Widget {
|
338
|
+
constructor() {
|
339
|
+
super();
|
340
|
+
this.contentElement.style.width = '100%';
|
341
|
+
this.contentElement.style.marginLeft = '10px';
|
342
|
+
}
|
343
|
+
}
|
344
|
+
```
|
345
|
+
|
346
|
+
**After:**
|
347
|
+
```typescript
|
348
|
+
|
349
|
+
|
350
|
+
export const DEFAULT_VIEW = (input, _output, target) => {
|
351
|
+
render(html`
|
352
|
+
<div style="width:100%; margin-left:10px"></div>`,
|
353
|
+
target, {host: input});
|
354
|
+
};
|
355
|
+
```
|
356
|
+
|
357
|
+
## Creating and appending a styled element
|
358
|
+
|
359
|
+
Replace `document.createElement`, setting `className`, and `appendChild` with a declarative lit-html template.
|
360
|
+
|
361
|
+
**Before:**
|
362
|
+
```typescript
|
363
|
+
|
364
|
+
class SomeWidget extends UI.Widget.Widget {
|
365
|
+
constructor() {
|
366
|
+
super();
|
367
|
+
const div = document.createElement('div');
|
368
|
+
div.className = 'some-class';
|
369
|
+
this.contentElement.appendChild(div);
|
370
|
+
}
|
371
|
+
}
|
372
|
+
```
|
373
|
+
|
374
|
+
**After:**
|
375
|
+
```typescript
|
376
|
+
|
377
|
+
|
378
|
+
export const DEFAULT_VIEW = (input, _output, target) => {
|
379
|
+
render(html`
|
380
|
+
<div>
|
381
|
+
<div class="some-class"></div>
|
382
|
+
</div>`,
|
383
|
+
target, {host: input});
|
384
|
+
};
|
385
|
+
```
|
386
|
+
|
387
|
+
## Creating a child with text content
|
388
|
+
|
389
|
+
Define the element and its text content directly within a lit-html template.
|
390
|
+
|
391
|
+
**Before:**
|
392
|
+
```typescript
|
393
|
+
|
394
|
+
class SomeWidget extends UI.Widget.Widget {
|
395
|
+
constructor() {
|
396
|
+
super();
|
397
|
+
this.contentElement.createChild('span', 'some-class').textContent = 'some-text';
|
398
|
+
}
|
399
|
+
}
|
400
|
+
```
|
401
|
+
|
402
|
+
**After:**
|
403
|
+
```typescript
|
404
|
+
|
405
|
+
|
406
|
+
export const DEFAULT_VIEW = (input, _output, target) => {
|
407
|
+
render(html`
|
408
|
+
<div>
|
409
|
+
<span class="some-class">some-text</span>
|
410
|
+
</div>`,
|
411
|
+
target, {host: input});
|
412
|
+
};
|
413
|
+
```
|
414
|
+
|
415
|
+
## Migrating `UI.Toolbar.ToolbarFilter`
|
416
|
+
|
417
|
+
Replace the imperative creation of a `ToolbarFilter` with the declarative `<devtools-toolbar-input>` component.
|
418
|
+
|
419
|
+
**Before:**
|
420
|
+
```typescript
|
421
|
+
|
422
|
+
class SomeWidget extends UI.Widget.Widget {
|
423
|
+
constructor() {
|
424
|
+
super();
|
425
|
+
const toolbar = this.contentElement.createChild('devtools-toolbar');
|
426
|
+
const filterInput = new UI.Toolbar.ToolbarFilter('some-placeholder', 0.5, 1, undefined, this.complete.bind(this), false, 'some-filter');
|
427
|
+
filterInput.addEventListener(UI.Toolbar.ToolbarInput.Event.TEXT_CHANGED, this.onFilterChanged.bind(this));
|
428
|
+
filterInput.element.classList.add('completions');
|
429
|
+
filterInput.element.setAttribute('aria-hidden', 'true');
|
430
|
+
toolbar.appendToolbarItem(filterInput);
|
431
|
+
}
|
432
|
+
}
|
433
|
+
```
|
434
|
+
|
435
|
+
**After:**
|
436
|
+
```typescript
|
437
|
+
|
438
|
+
|
439
|
+
export const DEFAULT_VIEW = (input, _output, target) => {
|
440
|
+
render(html`
|
441
|
+
<div>
|
442
|
+
<devtools-toolbar>
|
443
|
+
<devtools-toolbar-input class="completions" type="filter" placeholder="some-placeholder"
|
444
|
+
list="completions" id="some-filter" aria-hidden="true"
|
445
|
+
@change=${this.onFilterChanged.bind(this)} style="flex-grow:0.5; flex-shrink:1">
|
446
|
+
<datalist id="completions">${this.complete.bind(this)}</datalist>
|
447
|
+
</devtools-toolbar-input>
|
448
|
+
</devtools-toolbar>
|
449
|
+
</div>`,
|
450
|
+
target, {host: input});
|
451
|
+
};
|
452
|
+
```
|
453
|
+
|
454
|
+
## Migrating `UI.Toolbar.ToolbarInput`
|
455
|
+
|
456
|
+
Replace the imperative creation of a `ToolbarInput` with the declarative `<devtools-toolbar-input>` component.
|
457
|
+
|
458
|
+
**Before:**
|
459
|
+
```typescript
|
460
|
+
|
461
|
+
class SomeWidget extends UI.Widget.Widget {
|
462
|
+
constructor() {
|
463
|
+
super();
|
464
|
+
const toolbar = this.contentElement.createChild('devtools-toolbar');
|
465
|
+
const filterInput = new UI.Toolbar.ToolbarInput('some-placeholder', 'accessible-placeholder', 0.5, 1);
|
466
|
+
toolbar.appendToolbarItem(filterInput);
|
467
|
+
}
|
468
|
+
}
|
469
|
+
```
|
470
|
+
|
471
|
+
**After:**
|
472
|
+
```typescript
|
473
|
+
|
474
|
+
|
475
|
+
export const DEFAULT_VIEW = (input, _output, target) => {
|
476
|
+
render(html`
|
477
|
+
<div>
|
478
|
+
<devtools-toolbar>
|
479
|
+
<devtools-toolbar-input type="text" placeholder="some-placeholder"
|
480
|
+
aria-label="accessible-placeholder" style="flex-grow:0.5; flex-shrink:1"></devtools-toolbar-input>
|
481
|
+
</devtools-toolbar>
|
482
|
+
</div>`,
|
483
|
+
target, {host: input});
|
484
|
+
};
|
485
|
+
```
|
486
|
+
|
487
|
+
## Migrating `Adorners.Adorner.Adorner`
|
488
|
+
|
489
|
+
Replace the imperative creation of an `Adorner` with the declarative `<devtools-adorner>` component.
|
490
|
+
|
491
|
+
**Before:**
|
492
|
+
```typescript
|
493
|
+
|
494
|
+
class SomeWidget extends UI.Widget.Widget {
|
495
|
+
constructor() {
|
496
|
+
super();
|
497
|
+
const adornerContent = document.createElement('span');
|
498
|
+
adornerContent.innerHTML = '<div style="font-size: 12px;">💫</div>';
|
499
|
+
const adorner = new Adorners.Adorner.Adorner();
|
500
|
+
adorner.classList.add('fix-perf-icon');
|
501
|
+
adorner.data = {
|
502
|
+
name: i18nString(UIStrings.fixMe),
|
503
|
+
content: adornerContent,
|
504
|
+
jslogContext: 'fix-perf',
|
505
|
+
};
|
506
|
+
this.contentElement.appendChild(adorner);
|
507
|
+
}
|
508
|
+
}
|
509
|
+
```
|
510
|
+
|
511
|
+
**After:**
|
512
|
+
```typescript
|
513
|
+
|
514
|
+
|
515
|
+
export const DEFAULT_VIEW = (input, _output, target) => {
|
516
|
+
render(html`
|
517
|
+
<div>
|
518
|
+
<devtools-adorner class="fix-perf-icon" aria-label=${i18nString(UIStrings.fixMe)}
|
519
|
+
jslog=${VisualLogging.adorner('fix-perf')}>
|
520
|
+
<span><div style="font-size: 12px;">💫</div></span>
|
521
|
+
</devtools-adorner>
|
522
|
+
</div>`,
|
523
|
+
target, {host: input});
|
524
|
+
};
|
525
|
+
```
|
526
|
+
|
527
|
+
## Migrating `UI.Toolbar.ToolbarButton`
|
528
|
+
|
529
|
+
Replace the imperative creation of a `ToolbarButton` with the declarative `<devtools-button>` component.
|
530
|
+
|
531
|
+
**Before:**
|
532
|
+
```typescript
|
533
|
+
|
534
|
+
class SomeWidget extends UI.Widget.Widget {
|
535
|
+
constructor() {
|
536
|
+
super();
|
537
|
+
const toolbar = this.contentElement.createChild('devtools-toolbar');
|
538
|
+
const editButton = new UI.Toolbar.ToolbarButton(i18nString(UIStrings.editName), 'edit', undefined, 'edit-name');
|
539
|
+
editButton.addEventListener(UI.Toolbar.ToolbarButton.Events.CLICK, this.onClick.bind(this));
|
540
|
+
toolbar.appendToolbarItem(editButton);
|
541
|
+
}
|
542
|
+
}
|
543
|
+
```
|
544
|
+
|
545
|
+
**After:**
|
546
|
+
```typescript
|
547
|
+
|
548
|
+
|
549
|
+
export const DEFAULT_VIEW = (input, _output, target) => {
|
550
|
+
render(html`
|
551
|
+
<div>
|
552
|
+
<devtools-toolbar>
|
553
|
+
<devtools-button title=${i18nString(UIStrings.editName)} @click=${this.onClick.bind(this)}
|
554
|
+
.variant=${Buttons.Button.Variant.TOOLBAR} .iconName=${'edit'}
|
555
|
+
.jslogContext=${'edit-name'}></devtools-button>
|
556
|
+
</devtools-toolbar>
|
557
|
+
</div>`,
|
558
|
+
target, {host: input});
|
559
|
+
};
|
560
|
+
```
|
561
|
+
|
562
|
+
## Migrating various HTML elements with multiple properties
|
563
|
+
|
564
|
+
Replace imperative creation of standard HTML elements like `<a>`, `<img>`, and `<input>` with their declarative equivalents in a lit-html template, setting their properties as attributes.
|
565
|
+
|
566
|
+
**Before:**
|
567
|
+
```typescript
|
568
|
+
|
569
|
+
class SomeWidget extends UI.Widget.Widget {
|
570
|
+
constructor() {
|
571
|
+
super();
|
572
|
+
const input = document.createElement('input');
|
573
|
+
input.type = 'text';
|
574
|
+
input.placeholder = 'some-placeholder';
|
575
|
+
input.value = 'some-value';
|
576
|
+
input.disabled = !this.enabled;
|
577
|
+
input.checked = true
|
578
|
+
this.contentElement.append(input);
|
579
|
+
|
580
|
+
const anchor = document.createElement('a');
|
581
|
+
anchor.href = 'https://www.google.com';
|
582
|
+
anchor.innerText = 'some-text';
|
583
|
+
anchor.dataset.someKey = 'some-value';
|
584
|
+
anchor.role = 'some-role';
|
585
|
+
this.contentElement.insertBefore(anchor, input);
|
586
|
+
|
587
|
+
const img = document.createElement('img');
|
588
|
+
img.src = 'https://www.google.com/some-image.png';
|
589
|
+
img.alt = 'some-alt';
|
590
|
+
img.draggable = true;
|
591
|
+
img.height = 100;
|
592
|
+
img.hidden = 'hidden';
|
593
|
+
img.href = 'https://www.google.com';
|
594
|
+
img.id = 'some-id';
|
595
|
+
img.name = 'some-name';
|
596
|
+
img.rel = 'some-rel';
|
597
|
+
img.scope = 'some-scope';
|
598
|
+
|
599
|
+
input.insertAdjacentElement('beforebegin', img);
|
600
|
+
}
|
601
|
+
}
|
602
|
+
```
|
603
|
+
|
604
|
+
**After:**
|
605
|
+
```typescript
|
606
|
+
|
607
|
+
|
608
|
+
export const DEFAULT_VIEW = (input, _output, target) => {
|
609
|
+
render(html`
|
610
|
+
<div>
|
611
|
+
<a href="https://www.google.com" data-some-key="some-value" role="some-role">some-text</a>
|
612
|
+
<img src="https://www.google.com/some-image.png" alt="some-alt" draggable="true" height="100"
|
613
|
+
hidden="hidden" href="https://www.google.com" id="some-id" name="some-name" rel="some-rel"
|
614
|
+
scope="some-scope"></img>
|
615
|
+
<input type="text" placeholder="some-placeholder" value="some-value"
|
616
|
+
?disabled=${!this.enabled} checked>
|
617
|
+
</div>`,
|
618
|
+
target, {host: input});
|
619
|
+
};
|
620
|
+
```
|
621
|
+
|
622
|
+
## Migrating `UI.UIUtils` helpers
|
623
|
+
|
624
|
+
Replace `UI.UIUtils` helper functions like `createLabel` and `createTextButton` with their declarative component counterparts like `<label>` and `<devtools-button>`.
|
625
|
+
|
626
|
+
**Before:**
|
627
|
+
```typescript
|
628
|
+
|
629
|
+
class SomeWidget extends UI.Widget.Widget {
|
630
|
+
constructor() {
|
631
|
+
super();
|
632
|
+
const select = document.createElement('select');
|
633
|
+
select.add(UI.UIUtils.createOption('Option 1', '1', 'option-1'));
|
634
|
+
this.contentElement.appendChild(UI.UIUtils.createLabel('Some label:', 'some-label', select));
|
635
|
+
this.contentElement.appendChild(UI.UIUtils.createTextButton('Some button', onClick, {
|
636
|
+
className: 'some-class',
|
637
|
+
jslogContext: 'some-button',
|
638
|
+
variant: Buttons.Button.Variant.PRIMARY,
|
639
|
+
title: i18nString(UIStrings.someTitle),
|
640
|
+
iconName: 'some-icon'
|
641
|
+
}));
|
642
|
+
}
|
643
|
+
}
|
644
|
+
```
|
645
|
+
|
646
|
+
**After:**
|
647
|
+
```typescript
|
648
|
+
|
649
|
+
|
650
|
+
export const DEFAULT_VIEW = (input, _output, target) => {
|
651
|
+
render(html`
|
652
|
+
<div>
|
653
|
+
<label class="some-label">Some label:
|
654
|
+
<select>
|
655
|
+
<option value="1" jslog=${VisualLogging.dropDown('1').track({click: true})}>Option 1</option>
|
656
|
+
</select>
|
657
|
+
</label>
|
658
|
+
<devtools-button class="some-class" title=${i18nString(UIStrings.someTitle)} @click=${onClick}
|
659
|
+
.jslogContext=${'some-button'} .variant=${Buttons.Button.Variant.PRIMARY}>Some button</devtools-button>
|
660
|
+
</div>`,
|
661
|
+
target, {host: input});
|
662
|
+
};
|
663
|
+
```
|
664
|
+
|
665
|
+
## Migrating `UI.UIUtils.createTextChild`
|
666
|
+
|
667
|
+
Instead of using `createTextChild`, include the text directly inside the element in the lit-html template.
|
668
|
+
|
669
|
+
**Before:**
|
670
|
+
```typescript
|
671
|
+
|
672
|
+
class SomeWidget extends UI.Widget.Widget {
|
673
|
+
constructor() {
|
674
|
+
super();
|
675
|
+
UI.UIUtils.createTextChild(this.contentElement.createChild('div', 'some-class'), 'some-text');
|
676
|
+
}
|
677
|
+
}
|
678
|
+
```
|
679
|
+
|
680
|
+
**After:**
|
681
|
+
```typescript
|
682
|
+
|
683
|
+
|
684
|
+
export const DEFAULT_VIEW = (input, _output, target) => {
|
685
|
+
render(html`
|
686
|
+
<div>
|
687
|
+
<div class="some-class">some-text</div>
|
688
|
+
</div>`,
|
689
|
+
target, {host: input});
|
690
|
+
};
|
691
|
+
```
|
692
|
+
|
693
|
+
## Migrating `Buttons.Button.Button` with ARIA and Tooltip helpers
|
694
|
+
|
695
|
+
Replace the imperative creation of a `Button` and subsequent modifications with ARIA and Tooltip helpers with a single declarative `<devtools-button>` component, setting properties like `role` and `title`.
|
696
|
+
|
697
|
+
**Before:**
|
698
|
+
```typescript
|
699
|
+
|
700
|
+
class SomeWidget extends UI.Widget.Widget {
|
701
|
+
constructor() {
|
702
|
+
super();
|
703
|
+
this.button = new Buttons.Button.Button();
|
704
|
+
this.button.data = {
|
705
|
+
jslogContext: 'some-button',
|
706
|
+
variant: Buttons.Button.Variant.PRIMARY,
|
707
|
+
title: i18nString(UIStrings.someTitle),
|
708
|
+
};
|
709
|
+
UI.ARIAUtils.markAsPresentation(this.button);
|
710
|
+
UI.Tooltip.Tooltip.install(this.button, i18nString(UIStrings.someTooltip));
|
711
|
+
}
|
712
|
+
}
|
713
|
+
```
|
714
|
+
|
715
|
+
**After:**
|
716
|
+
```typescript
|
717
|
+
|
718
|
+
class SomeWidget extends UI.Widget.Widget {
|
719
|
+
constructor() {
|
720
|
+
super();
|
721
|
+
this.button = html`
|
722
|
+
<devtools-button role="presentation" title=${i18nString(UIStrings.someTooltip)}
|
723
|
+
.data=${{
|
724
|
+
jslogContext: 'some-button',
|
725
|
+
variant: Buttons.Button.Variant.PRIMARY,
|
726
|
+
title: i18nString(UIStrings.someTitle),
|
727
|
+
}}
|
728
|
+
></devtools-button>`;
|
729
|
+
}
|
730
|
+
}
|
731
|
+
```
|
732
|
+
|
733
|
+
## Migrating `IconButton.Icon.Icon`
|
734
|
+
|
735
|
+
Replace the imperative `Icon` creation with the declarative `<devtools-icon>` component.
|
736
|
+
|
737
|
+
**Before:**
|
738
|
+
```typescript
|
739
|
+
|
740
|
+
class SomeWidget extends UI.Widget.Widget {
|
741
|
+
constructor() {
|
742
|
+
super();
|
743
|
+
const icon = new IconButton.Icon.Icon();
|
744
|
+
icon.data = {iconName: 'checkmark', color: 'var(--icon-checkmark-green)', width: '14px', height: '14px'};
|
745
|
+
this.contentElement.appendChild(icon);
|
746
|
+
}
|
747
|
+
}
|
748
|
+
```
|
749
|
+
|
750
|
+
**After:**
|
751
|
+
```typescript
|
752
|
+
|
753
|
+
|
754
|
+
export const DEFAULT_VIEW = (input, _output, target) => {
|
755
|
+
render(html`
|
756
|
+
<div>
|
757
|
+
<devtools-icon name="checkmark"
|
758
|
+
style="color:var(--icon-checkmark-green); width:14px; height:14px"></devtools-icon>
|
759
|
+
</div>`,
|
760
|
+
target, {host: input});
|
761
|
+
};
|
762
|
+
```
|
763
|
+
|
764
|
+
## Migrating Checkboxes
|
765
|
+
|
766
|
+
Replace various imperative checkbox creation methods with the declarative `<devtools-checkbox>` component. For settings-backed checkboxes, use the `bindToSetting` directive.
|
767
|
+
|
768
|
+
**Before:**
|
769
|
+
```typescript
|
770
|
+
|
771
|
+
class SomeWidget extends UI.Widget.Widget {
|
772
|
+
constructor() {
|
773
|
+
super();
|
774
|
+
this.contentElement.appendChild(UI.UIUtils.CheckboxLabel.create(
|
775
|
+
i18nString(UIStrings.someTitle), true, i18nString(UIStrings.someTooltip),
|
776
|
+
undefined, 'some-checkbox', true));
|
777
|
+
this.contentElement.appendChild(UI.UIUtils.CheckboxLabel.create());
|
778
|
+
this.contentElement.appendChild(UI.UIUtils.CheckboxLabel.createWithStringLiteral(
|
779
|
+
':hover', undefined, undefined, 'some-other-checkbox'));
|
780
|
+
|
781
|
+
const toolbar = this.contentElement.createChild('devtools-toolbar');
|
782
|
+
toolbar.appendToolbarItem(new UI.Toolbar.ToolbarCheckbox(
|
783
|
+
i18nString(UIStrings.someToolbarTitle), i18nString(UIStrings.someToolbarTooltip),
|
784
|
+
this.someToolbarCheckboxClicked.bind(this), 'some-toolbar-checkbox'));
|
785
|
+
toolbar.appendToolbarItem(new UI.Toolbar.ToolbarSettingCheckbox(
|
786
|
+
this.someSetting, i18nString(UIStrings.someToolbarTooltip), i18nString(UIStrings.alternateToolbarTitle)));
|
787
|
+
toolbar.appendToolbarItem(new UI.Toolbar.ToolbarSettingCheckbox(this.someOtherSetting));
|
788
|
+
}
|
789
|
+
}
|
790
|
+
```
|
791
|
+
|
792
|
+
**After:**
|
793
|
+
```typescript
|
794
|
+
|
795
|
+
|
796
|
+
export const DEFAULT_VIEW = (input, _output, target) => {
|
797
|
+
render(html`
|
798
|
+
<div>
|
799
|
+
<devtools-checkbox class="small" checked>${i18nString(UIStrings.someTitle)}</devtools-checkbox>
|
800
|
+
<devtools-checkbox></devtools-checkbox>
|
801
|
+
<devtools-checkbox class="small">:hover</devtools-checkbox>
|
802
|
+
<devtools-toolbar>
|
803
|
+
<devtools-checkbox title=${i18nString(UIStrings.someToolbarTooltip)}
|
804
|
+
@click=${this.someToolbarCheckboxClicked.bind(this)}
|
805
|
+
.jslogContext=${'some-toolbar-checkbox'}>${i18nString(UIStrings.someToolbarTitle)}</devtools-checkbox>
|
806
|
+
<devtools-checkbox title=${i18nString(UIStrings.someToolbarTooltip)}
|
807
|
+
${bindToSetting(this.someSetting)}>${i18nString(UIStrings.alternateToolbarTitle)}</devtools-checkbox>
|
808
|
+
<devtools-checkbox ${bindToSetting(this.someOtherSetting)}>${this.someOtherSetting.title()}</devtools-checkbox>
|
809
|
+
</devtools-toolbar>
|
810
|
+
</div>`,
|
811
|
+
target, {host: input});
|
812
|
+
};
|
813
|
+
```
|
814
|
+
|
815
|
+
## Migrating `iframe` creation
|
816
|
+
|
817
|
+
Replace `document.createElement('iframe')` and `setAttribute` calls with a declarative `<iframe>` tag in a lit-html template.
|
818
|
+
|
819
|
+
**Before:**
|
820
|
+
```typescript
|
821
|
+
|
822
|
+
class SomeWidget extends UI.Widget.Widget {
|
823
|
+
constructor() {
|
824
|
+
super();
|
825
|
+
const iframe = document.createElement('iframe');
|
826
|
+
iframe.setAttribute('sandbox', '');
|
827
|
+
iframe.tabIndex = -1;
|
828
|
+
this.contentElement.appendChild(iframe);
|
829
|
+
}
|
830
|
+
}
|
831
|
+
```
|
832
|
+
|
833
|
+
**After:**
|
834
|
+
```typescript
|
835
|
+
|
836
|
+
|
837
|
+
export const DEFAULT_VIEW = (input, _output, target) => {
|
838
|
+
render(html`
|
839
|
+
<div>
|
840
|
+
<iframe sandbox tabindex="-1"></iframe>
|
841
|
+
</div>`,
|
842
|
+
target, {host: input});
|
843
|
+
};
|
844
|
+
```
|
845
|
+
|
846
|
+
## Migrating `UI.UIUtils.createInput`
|
847
|
+
|
848
|
+
Replace the `createInput` helper with a standard `<input>` element in a lit-html template.
|
849
|
+
|
850
|
+
**Before:**
|
851
|
+
```typescript
|
852
|
+
|
853
|
+
class SomeWidget extends UI.Widget.Widget {
|
854
|
+
constructor() {
|
855
|
+
super();
|
856
|
+
this.contentElement.appendChild(UI.UIUtils.createInput('add-source-map', 'text', 'url'));
|
857
|
+
}
|
858
|
+
}
|
859
|
+
```
|
860
|
+
|
861
|
+
**After:**
|
862
|
+
```typescript
|
863
|
+
|
864
|
+
|
865
|
+
export const DEFAULT_VIEW = (input, _output, target) => {
|
866
|
+
render(html`
|
867
|
+
<div>
|
868
|
+
<input class="harmony-input add-source-map" spellcheck="false" type="text"
|
869
|
+
jslog=${VisualLogging.textField('url').track({keydown: 'Enter', change: true})}>
|
870
|
+
</div>`,
|
871
|
+
target, {host: input});
|
872
|
+
};
|
873
|
+
```
|
874
|
+
|
875
|
+
## Migrating `SortableDataGrid`, `ViewportDataGrid`, `DataGridImpl`
|
876
|
+
|
877
|
+
Replace the imperative `SortableDataGrid`, `ViewportDataGrid`, or `DataGridImpl` with the declarative `<devtools-data-grid>` component. Columns are defined using `<th>` elements inside a `<table>`.
|
878
|
+
|
879
|
+
**Before:**
|
880
|
+
```typescript
|
881
|
+
|
882
|
+
class SomeWidget extends UI.Widget.Widget {
|
883
|
+
constructor() {
|
884
|
+
super();
|
885
|
+
this.#columns = [
|
886
|
+
{
|
887
|
+
id: 'node-id',
|
888
|
+
title: i18nString(UIStrings.element),
|
889
|
+
sortable: true,
|
890
|
+
weight: 50,
|
891
|
+
align: undefined,
|
892
|
+
},
|
893
|
+
{
|
894
|
+
id: 'declaration',
|
895
|
+
title: i18nString(UIStrings.declaration),
|
896
|
+
},
|
897
|
+
{
|
898
|
+
id: 'source-url',
|
899
|
+
title: i18nString(UIStrings.source),
|
900
|
+
sortable: false,
|
901
|
+
weight: 100,
|
902
|
+
align: DataGrid.DataGrid.Align.RIGHT,
|
903
|
+
},
|
904
|
+
];
|
905
|
+
|
906
|
+
this.#dataGrid = new DataGrid.SortableDataGrid.SortableDataGrid({
|
907
|
+
displayName: i18nString(UIStrings.someTitle),
|
908
|
+
columns: this.#columns,
|
909
|
+
deleteCallback: undefined,
|
910
|
+
refreshCallback: undefined,
|
911
|
+
});
|
912
|
+
this.#dataGrid.setStriped(true);
|
913
|
+
this.#dataGrid.addEventListener(
|
914
|
+
DataGrid.DataGrid.Events.SORTING_CHANGED, this.#sortMediaQueryDataGrid.bind(this));
|
915
|
+
|
916
|
+
this.#dataGrid.asWidget().show(this.element);
|
917
|
+
}
|
918
|
+
}
|
919
|
+
```
|
920
|
+
|
921
|
+
**After:**
|
922
|
+
```typescript
|
923
|
+
|
924
|
+
|
925
|
+
export const DEFAULT_VIEW = (input, _output, target) => {
|
926
|
+
render(html`
|
927
|
+
<div>
|
928
|
+
<devtools-data-grid name=${i18nString(UIStrings.someTitle)} striped
|
929
|
+
@sort=${this.#sortMediaQueryDataGrid.bind(this)}>
|
930
|
+
<table>
|
931
|
+
<tr>
|
932
|
+
<th id="node-id" weight="50" sortable>${i18nString(UIStrings.element)}</th>
|
933
|
+
<th id="declaration">${i18nString(UIStrings.declaration)}</th>
|
934
|
+
<th id="source-url" weight="100" align="right">${i18nString(UIStrings.source)}</th>
|
935
|
+
</tr>
|
936
|
+
</table>
|
937
|
+
</devtools-data-grid>
|
938
|
+
</div>`,
|
939
|
+
target, {host: input});
|
940
|
+
};
|
941
|
+
```
|
942
|
+
|
943
|
+
## Migrating `DataGridNode.createCell`
|
944
|
+
|
945
|
+
The `createCell` method in a `DataGridNode` can be refactored to return a lit-html template for the cell's content, making it declarative.
|
946
|
+
|
947
|
+
**Before:**
|
948
|
+
```typescript
|
949
|
+
|
950
|
+
class ElementNode extends DataGrid.SortableDataGrid.SortableDataGridNode<ElementNode> {
|
951
|
+
override createCell(columnId: string): HTMLElement {
|
952
|
+
const cell = this.createTD(columnId);
|
953
|
+
cell.classList.add('node-id');
|
954
|
+
cell.createChild('span', 'node-id-text').textContent = this.data.id;
|
955
|
+
return cell;
|
956
|
+
}
|
957
|
+
}
|
958
|
+
```
|
959
|
+
|
960
|
+
**After:**
|
961
|
+
```typescript
|
962
|
+
|
963
|
+
class ElementNode extends DataGrid.SortableDataGrid.SortableDataGridNode<ElementNode> {
|
964
|
+
override createCell(columnId: string): HTMLElement {
|
965
|
+
const cell = this.createTD(columnId);
|
966
|
+
render(html`
|
967
|
+
<td class="node-id">
|
968
|
+
<span class="node-id-text">${this.data.id}</span>
|
969
|
+
</td>`, cell);
|
970
|
+
return cell;
|
971
|
+
}
|
972
|
+
}
|
973
|
+
```
|
974
|
+
|
975
|
+
## Migrating `SplitWidget`
|
976
|
+
|
977
|
+
Replace the imperative `SplitWidget` with the declarative `<devtools-split-view>` component. Nested widgets can be rendered using `<devtools-widget>`.
|
978
|
+
|
979
|
+
**Before:**
|
980
|
+
```typescript
|
981
|
+
|
982
|
+
class SomeWidget extends UI.Widget.Widget {
|
983
|
+
constructor() {
|
984
|
+
super();
|
985
|
+
this.#splitWidget = new UI.SplitWidget.SplitWidget(this.vertical, false, undefined, 200);
|
986
|
+
this.#splitWidget.show(this.element);
|
987
|
+
|
988
|
+
this.#mainContainer = new UI.SplitWidget.SplitWidget(true, true);
|
989
|
+
this.#resultsContainer = new UI.Widget.EmptyWidget();
|
990
|
+
this.#elementContainer = new DetailsView();
|
991
|
+
|
992
|
+
this.#mainContainer.setMainWidget(this.#resultsContainer);
|
993
|
+
this.#mainContainer.setSidebarWidget(this.#elementContainer);
|
994
|
+
this.#mainContainer.setVertical(false);
|
995
|
+
this.#mainContainer.setSecondIsSidebar(this.dockedLeft);
|
996
|
+
|
997
|
+
this.#sideBar = new SidebarPanel();
|
998
|
+
this.#sideBar.setMinimumSize(100, 25);
|
999
|
+
this.#splitWidget.setSidebarWidget(this.#sideBar);
|
1000
|
+
this.#splitWidget.setMainWidget(this.#mainContainer);
|
1001
|
+
}
|
1002
|
+
}
|
1003
|
+
```
|
1004
|
+
|
1005
|
+
**After:**
|
1006
|
+
```typescript
|
1007
|
+
|
1008
|
+
|
1009
|
+
export const DEFAULT_VIEW = (input, _output, target) => {
|
1010
|
+
render(html`
|
1011
|
+
<div>
|
1012
|
+
<devtools-split-view direction=${this.vertical ? 'column' : 'row'} sidebar-position="first"
|
1013
|
+
sidebar-initial-size="200">
|
1014
|
+
<devtools-widget slot="sidebar" .widgetConfig=${widgetConfig(SidebarPanel,
|
1015
|
+
{minimumSize: {width: 100, height: 25}})}></devtools-widget>
|
1016
|
+
<devtools-split-view direction="column" sidebar-position="second" slot="main"
|
1017
|
+
direction="row" sidebar-position="$this.dockedLeft ? 'second' : 'first'}">
|
1018
|
+
<devtools-widget slot="main" .widgetConfig=${widgetConfig(UI.Widget.EmptyWidget)}></devtools-widget>
|
1019
|
+
<devtools-widget slot="sidebar" .widgetConfig=${widgetConfig(DetailsView)}></devtools-widget>
|
1020
|
+
</devtools-split-view>
|
1021
|
+
</devtools-split-view>
|
1022
|
+
</div>`,
|
1023
|
+
target, {host: input});
|
1024
|
+
};
|
1025
|
+
```
|
1026
|
+
|
1027
|
+
## Migrating `UI.Fragment`
|
1028
|
+
|
1029
|
+
Replace `UI.Fragment.Fragment.build` with a standard lit-html template. To get a reference to an element, use the `ref` directive.
|
1030
|
+
|
1031
|
+
**Before:**
|
1032
|
+
```typescript
|
1033
|
+
|
1034
|
+
class SomeWidget extends UI.Widget.Widget {
|
1035
|
+
constructor() {
|
1036
|
+
super();
|
1037
|
+
const contrastFragment = UI.Fragment.Fragment.build`
|
1038
|
+
<div class="contrast-container-in-grid" $="contrast-container-element">
|
1039
|
+
<span class="contrast-preview">Aa</span>
|
1040
|
+
<span>${contrastRatioString}</span>
|
1041
|
+
</div>`;
|
1042
|
+
this.contentElement.appendChild(contrastFragment.element());
|
1043
|
+
}
|
1044
|
+
}
|
1045
|
+
```
|
1046
|
+
|
1047
|
+
**After:**
|
1048
|
+
```typescript
|
1049
|
+
|
1050
|
+
|
1051
|
+
export const DEFAULT_VIEW = (input, output, target) => {
|
1052
|
+
render(html`
|
1053
|
+
<div>
|
1054
|
+
<div class="contrast-container-in-grid" ${ref(e => { output.contrastContainerElement = e; })}>
|
1055
|
+
<span class="contrast-preview">Aa</span>
|
1056
|
+
<span>${contrastRatioString}</span>
|
1057
|
+
</div>
|
1058
|
+
</div>`,
|
1059
|
+
target, {host: input});
|
1060
|
+
};
|
1061
|
+
```
|
1062
|
+
|
1063
|
+
## Migrating `UI.ARIAUtils` helpers
|
1064
|
+
|
1065
|
+
Replace calls to `UI.ARIAUtils` helper functions with the corresponding ARIA attributes directly in the lit-html template.
|
1066
|
+
|
1067
|
+
**Before:**
|
1068
|
+
```typescript
|
1069
|
+
|
1070
|
+
class SomeWidget extends UI.Widget.Widget {
|
1071
|
+
constructor() {
|
1072
|
+
super();
|
1073
|
+
this.button = this.contentElement.createChild('button');
|
1074
|
+
UI.ARIAUtils.markAsMenuButton(this.button);
|
1075
|
+
const tree = this.contentElement.createChild('ul');
|
1076
|
+
UI.ARIAUtils.markAsTree(tree);
|
1077
|
+
UI.ARIAUtils.markAsTreeitem(tree.createChild('li'));
|
1078
|
+
const alert = this.contentElement.createChild('span');
|
1079
|
+
alert.textContent = 'Alert';
|
1080
|
+
UI.ARIAUtils.markAsAlert(alert);
|
1081
|
+
const slider = this.contentElement.createChild('input');
|
1082
|
+
UI.ARIAUtils.markAsSlider(slider, 10);
|
1083
|
+
|
1084
|
+
UI.ARIAUtils.setDescription(this.button, 'Some button');
|
1085
|
+
UI.ARIAUtils.setInvalid(slider, this.valid);
|
1086
|
+
|
1087
|
+
const progress = this.contentElement.createChild('div');
|
1088
|
+
UI.ARIAUtils.markAsProgressBar(progress);
|
1089
|
+
UI.ARIAUtils.setProgressBarValue(progress, 0.5, '50% done');
|
1090
|
+
}
|
1091
|
+
}
|
1092
|
+
```
|
1093
|
+
|
1094
|
+
**After:**
|
1095
|
+
```typescript
|
1096
|
+
|
1097
|
+
|
1098
|
+
export const DEFAULT_VIEW = (input, _output, target) => {
|
1099
|
+
render(html`
|
1100
|
+
<div>
|
1101
|
+
<button role="button" aria-haspopup="true" aria-description="Some button"></button>
|
1102
|
+
<ul role="tree">
|
1103
|
+
<li role="treeitem"></li>
|
1104
|
+
</ul>
|
1105
|
+
<span role="alert" aria-live="polite">Alert</span>
|
1106
|
+
<input role="slider" aria-valuemin="10" aria-valuemax="100" aria-invalid=${this.valid}>
|
1107
|
+
<div role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0.5"
|
1108
|
+
aria-valuetext="50% done"></div>
|
1109
|
+
</div>`,
|
1110
|
+
target, {host: input});
|
1111
|
+
};
|
1112
|
+
```
|
1113
|
+
|
1114
|
+
## Migrating `EmptyWidget`
|
1115
|
+
|
1116
|
+
Replace the imperative `EmptyWidget` with a declarative `<devtools-widget>` and configure it with `widgetConfig` to render an `EmptyWidget`.
|
1117
|
+
|
1118
|
+
**Before:**
|
1119
|
+
```typescript
|
1120
|
+
|
1121
|
+
class SomeWidget extends UI.Widget.Widget {
|
1122
|
+
constructor() {
|
1123
|
+
super();
|
1124
|
+
const widget = new UI.EmptyWidget.EmptyWidget(i18nString(UIStrings.nothingToSeeHere), this.explanation);
|
1125
|
+
widget.link = 'http://www.google.com';
|
1126
|
+
widget.show(this.element);
|
1127
|
+
}
|
1128
|
+
}
|
1129
|
+
```
|
1130
|
+
|
1131
|
+
**After:**
|
1132
|
+
```typescript
|
1133
|
+
|
1134
|
+
|
1135
|
+
export const DEFAULT_VIEW = (input, _output, target) => {
|
1136
|
+
render(html`
|
1137
|
+
<div>
|
1138
|
+
<devtools-widget .widgetConfig=${widgetConfig(UI.EmptyWidget.EmptyWidget,{
|
1139
|
+
header: i18nString(UIStrings.nothingToSeeHere), text: this.explanation,
|
1140
|
+
link: 'http://www.google.com',})}></devtools-widget>
|
1141
|
+
</div>`,
|
1142
|
+
target, {host: input});
|
1143
|
+
};
|
1144
|
+
```
|
1145
|
+
|
1146
|
+
## Migrating `TextPrompt`
|
1147
|
+
|
1148
|
+
Replace imperative `TextPrompt` creation with the declarative `<devtools-prompt>` component, providing completions via `<datalist>`.
|
1149
|
+
|
1150
|
+
**Before:**
|
1151
|
+
```typescript
|
1152
|
+
class SomeWidget extends UI.Widget.Widget {
|
1153
|
+
constructor() {
|
1154
|
+
super();
|
1155
|
+
const prompt = new UI.TextPrompt.TextPrompt();
|
1156
|
+
prompt.initialize(async (expression, prefix) => {
|
1157
|
+
const options = ['completion1', 'completion2'];
|
1158
|
+
return options.filter(opt => opt.startsWith(prefix)).map(text => ({text}));
|
1159
|
+
});
|
1160
|
+
const promptElement = this.contentElement.createChild('span');
|
1161
|
+
prompt.attach(promptElement);
|
1162
|
+
}
|
1163
|
+
}
|
1164
|
+
```
|
1165
|
+
|
1166
|
+
**After:**
|
1167
|
+
```typescript
|
1168
|
+
export const DEFAULT_VIEW = (input, _output, target) => {
|
1169
|
+
render(html`
|
1170
|
+
<div>
|
1171
|
+
<datalist id="my-completions">
|
1172
|
+
<option>completion1</option>
|
1173
|
+
<option>completion2</option>
|
1174
|
+
</datalist>
|
1175
|
+
<devtools-prompt completions="my-completions" editing></devtools-prompt>
|
1176
|
+
</div>`,
|
1177
|
+
target, {host: input});
|
1178
|
+
};
|
1179
|
+
```
|
1180
|
+
|
1181
|
+
## Migrating `TreeOutline`
|
1182
|
+
|
1183
|
+
Replace imperative `TreeOutline` and `TreeElement` creation with the declarative `<devtools-tree>` component.
|
1184
|
+
|
1185
|
+
**Before:**
|
1186
|
+
```typescript
|
1187
|
+
class SomeWidget extends UI.Widget.Widget {
|
1188
|
+
constructor() {
|
1189
|
+
super();
|
1190
|
+
const tree = new UI.TreeOutline.TreeOutlineInShadow();
|
1191
|
+
this.contentElement.appendChild(tree.element);
|
1192
|
+
|
1193
|
+
const root = tree.rootElement();
|
1194
|
+
const node1 = new UI.TreeOutline.TreeElement('Node 1');
|
1195
|
+
root.appendChild(node1);
|
1196
|
+
|
1197
|
+
const node2 = new UI.TreeOutline.TreeElement('Node 2', true);
|
1198
|
+
root.appendChild(node2);
|
1199
|
+
|
1200
|
+
const child1 = new UI.TreeOutline.TreeElement('Child 1');
|
1201
|
+
node2.appendChild(child1);
|
1202
|
+
}
|
1203
|
+
}
|
1204
|
+
```
|
1205
|
+
|
1206
|
+
**After:**
|
1207
|
+
```typescript
|
1208
|
+
export const DEFAULT_VIEW = (input, _output, target) => {
|
1209
|
+
render(html`
|
1210
|
+
<div>
|
1211
|
+
<devtools-tree .template=${html`
|
1212
|
+
<ul role="tree">
|
1213
|
+
<li role="treeitem">Node 1</li>
|
1214
|
+
<li role="treeitem">
|
1215
|
+
Node 2
|
1216
|
+
<ul role="group" hidden>
|
1217
|
+
<li role="treeitem">Child 1</li>
|
1218
|
+
</ul>
|
1219
|
+
</li>
|
1220
|
+
</ul>
|
1221
|
+
`}></devtools-tree>
|
1222
|
+
</div>`,
|
1223
|
+
target, {host: input});
|
1224
|
+
};
|
1225
|
+
```
|