chrome-devtools-frontend 1.0.1553956 → 1.0.1555174

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.
@@ -1,11 +1,9 @@
1
1
  // Copyright 2024 The Chromium Authors
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
- /* eslint-disable @devtools/no-lit-render-outside-of-view */
5
4
 
6
5
  import * as Trace from '../../../models/trace/trace.js';
7
6
  import * as Buttons from '../../../ui/components/buttons/buttons.js';
8
- import * as ComponentHelpers from '../../../ui/components/helpers/helpers.js';
9
7
  import * as UI from '../../../ui/legacy/legacy.js';
10
8
  import * as Lit from '../../../ui/lit/lit.js';
11
9
  import * as Utils from '../utils/utils.js';
@@ -16,9 +14,136 @@ import sidebarInsightsTabStyles from './sidebarInsightsTab.css.js';
16
14
  import {SidebarSingleInsightSet, type SidebarSingleInsightSetData} from './SidebarSingleInsightSet.js';
17
15
 
18
16
  const {html} = Lit;
17
+ const {widgetConfig} = UI.Widget;
18
+
19
+ interface ViewInput {
20
+ parsedTrace: Trace.TraceModel.ParsedTrace;
21
+ labels: string[];
22
+ activeInsightSet: Trace.Insights.Types.InsightSet|null;
23
+ activeInsight: ActiveInsight|null;
24
+ selectedCategory: Trace.Insights.Types.InsightCategory;
25
+ onInsightSetToggled: (insightSet: Trace.Insights.Types.InsightSet) => void;
26
+ onInsightSetHovered: (insightSet: Trace.Insights.Types.InsightSet) => void;
27
+ onInsightSetUnhovered: () => void;
28
+ onZoomClick: (insightSet: Trace.Insights.Types.InsightSet) => void;
29
+ }
30
+
31
+ type View = (input: ViewInput, output: undefined, target: HTMLElement) => void;
32
+
33
+ export const DEFAULT_VIEW: View = (input, output, target) => {
34
+ const {
35
+ parsedTrace,
36
+ labels,
37
+ activeInsightSet,
38
+ activeInsight,
39
+ selectedCategory,
40
+ onInsightSetToggled,
41
+ onInsightSetHovered,
42
+ onInsightSetUnhovered,
43
+ onZoomClick,
44
+ } = input;
45
+
46
+ const insights = parsedTrace.insights;
47
+ if (!insights) {
48
+ return;
49
+ }
19
50
 
20
- export class SidebarInsightsTab extends HTMLElement {
21
- readonly #shadow = this.attachShadow({mode: 'open'});
51
+ const hasMultipleInsightSets = insights.size > 1;
52
+
53
+ // clang-format off
54
+ Lit.render(html`
55
+ <style>${sidebarInsightsTabStyles}</style>
56
+ <div class="insight-sets-wrapper">
57
+ ${[...insights.values()].map((insightSet, index) => {
58
+ const {id, url} = insightSet;
59
+ const data: SidebarSingleInsightSetData = {
60
+ insightSetKey: id,
61
+ activeCategory: selectedCategory,
62
+ activeInsight,
63
+ parsedTrace,
64
+ };
65
+ const selected = insightSet === activeInsightSet;
66
+
67
+ const contents = html`
68
+ <devtools-widget
69
+ data-insight-set-key=${id}
70
+ .widgetConfig=${widgetConfig(SidebarSingleInsightSet, {data})}
71
+ ></devtools-widget>
72
+ `;
73
+
74
+ if (hasMultipleInsightSets) {
75
+ return html`<details ?open=${selected}>
76
+ <summary
77
+ @click=${() => onInsightSetToggled(insightSet)}
78
+ @mouseenter=${() => onInsightSetHovered(insightSet)}
79
+ @mouseleave=${() => onInsightSetUnhovered()}
80
+ title=${url.href}>
81
+ ${renderDropdownIcon(selected)}
82
+ <span>${labels[index]}</span>
83
+ <span class='zoom-button'
84
+ @click=${(event: Event) => {
85
+ event.stopPropagation();
86
+ onZoomClick(insightSet);
87
+ }}
88
+ >
89
+ ${renderZoomButton(selected)}
90
+ </span>
91
+ </summary>
92
+ ${contents}
93
+ </details>`;
94
+ }
95
+
96
+ return contents;
97
+ })}
98
+ </div>
99
+ `, target);
100
+ // clang-format on
101
+ };
102
+
103
+ function renderZoomButton(insightSetToggled: boolean): Lit.TemplateResult {
104
+ const classes = Lit.Directives.classMap({
105
+ 'zoom-icon': true,
106
+ active: insightSetToggled,
107
+ });
108
+
109
+ // clang-format off
110
+ return html`
111
+ <div class=${classes}>
112
+ <devtools-button .data=${{
113
+ variant: Buttons.Button.Variant.ICON,
114
+ iconName: 'center-focus-weak',
115
+ size: Buttons.Button.Size.SMALL,
116
+ } as Buttons.Button.ButtonData}
117
+ ></devtools-button></div>`;
118
+ // clang-format on
119
+ }
120
+
121
+ function renderDropdownIcon(insightSetToggled: boolean): Lit.TemplateResult {
122
+ const containerClasses = Lit.Directives.classMap({
123
+ 'dropdown-icon': true,
124
+ active: insightSetToggled,
125
+ });
126
+
127
+ // clang-format off
128
+ return html`
129
+ <div class=${containerClasses}>
130
+ <devtools-button .data=${{
131
+ variant: Buttons.Button.Variant.ICON,
132
+ iconName: 'chevron-right',
133
+ size: Buttons.Button.Size.SMALL,
134
+ } as Buttons.Button.ButtonData}
135
+ ></devtools-button></div>
136
+ `;
137
+ // clang-format on
138
+ }
139
+
140
+ export class SidebarInsightsTab extends UI.Widget.Widget {
141
+ static createWidgetElement(): UI.Widget.WidgetElement<SidebarInsightsTab> {
142
+ const widgetElement = document.createElement('devtools-widget') as UI.Widget.WidgetElement<SidebarInsightsTab>;
143
+ return widgetElement;
144
+ }
145
+
146
+ #view: View;
22
147
  #parsedTrace: Trace.TraceModel.ParsedTrace|null = null;
23
148
  #activeInsight: ActiveInsight|null = null;
24
149
  #selectedCategory = Trace.Insights.Types.InsightCategory.ALL;
@@ -26,9 +151,14 @@ export class SidebarInsightsTab extends HTMLElement {
26
151
  * When a trace has sets of insights, we show an accordion with each
27
152
  * set within. A set can be specific to a single navigation, or include the
28
153
  * beginning of the trace up to the first navigation.
29
- * You can only have one of these open at any time, and we track it via this ID.
154
+ * You can only have one of these open at any time.
30
155
  */
31
- #selectedInsightSetKey: string|null = null;
156
+ #selectedInsightSet: Trace.Insights.Types.InsightSet|null = null;
157
+
158
+ constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
159
+ super(element, {useShadowDom: true});
160
+ this.#view = view;
161
+ }
32
162
 
33
163
  // TODO(paulirish): add back a disconnectedCallback() to avoid memory leaks that doesn't cause b/372943062
34
164
 
@@ -38,14 +168,14 @@ export class SidebarInsightsTab extends HTMLElement {
38
168
  }
39
169
 
40
170
  this.#parsedTrace = data;
41
- this.#selectedInsightSetKey = null;
171
+ this.#selectedInsightSet = null;
42
172
 
43
173
  if (this.#parsedTrace?.insights) {
44
174
  /** Select the first set. Filtering out trivial sets was done back in {@link Trace.Processor.#computeInsightsForInitialTracePeriod} */
45
- this.#selectedInsightSetKey = [...this.#parsedTrace.insights.keys()].at(0) ?? null;
175
+ this.#selectedInsightSet = [...this.#parsedTrace.insights.values()].at(0) ?? null;
46
176
  }
47
177
 
48
- void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#render);
178
+ this.requestUpdate();
49
179
  }
50
180
 
51
181
  get activeInsight(): ActiveInsight|null {
@@ -56,81 +186,39 @@ export class SidebarInsightsTab extends HTMLElement {
56
186
  if (active === this.#activeInsight) {
57
187
  return;
58
188
  }
189
+
59
190
  this.#activeInsight = active;
60
191
 
61
- // Only update the insightSetKey if there is an active insight. Otherwise, closing an insight
192
+ // Only update selectedInsightSet if there is an active insight. Otherwise, closing an insight
62
193
  // would also collapse the insight set. Usually the proper insight set is already set because
63
194
  // the user has it open already in order for this setter to be called, but insights can also
64
195
  // be activated by clicking on a insight chip in the Summary panel, which may require opening
65
196
  // a different insight set.
66
197
  if (this.#activeInsight) {
67
- this.#selectedInsightSetKey = this.#activeInsight.insightSetKey;
198
+ this.#selectedInsightSet = this.#parsedTrace?.insights?.get(this.#activeInsight.insightSetKey) ?? null;
68
199
  }
69
- void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#render);
200
+ this.requestUpdate();
70
201
  }
71
202
 
72
- #insightSetToggled(id: string): void {
73
- this.#selectedInsightSetKey = this.#selectedInsightSetKey === id ? null : id;
203
+ #onInsightSetToggled(insightSet: Trace.Insights.Types.InsightSet): void {
204
+ this.#selectedInsightSet = this.#selectedInsightSet === insightSet ? null : insightSet;
74
205
  // Update the active insight set.
75
- if (this.#selectedInsightSetKey !== this.#activeInsight?.insightSetKey) {
76
- this.dispatchEvent(new Insights.SidebarInsight.InsightDeactivated());
206
+ if (this.#selectedInsightSet?.id !== this.#activeInsight?.insightSetKey) {
207
+ this.element.dispatchEvent(new Insights.SidebarInsight.InsightDeactivated());
77
208
  }
78
- void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#render);
79
- }
80
-
81
- #insightSetHovered(id: string): void {
82
- const data = this.#parsedTrace?.insights?.get(id);
83
- data && this.dispatchEvent(new Insights.SidebarInsight.InsightSetHovered(data.bounds));
209
+ this.requestUpdate();
84
210
  }
85
211
 
86
- #insightSetUnhovered(): void {
87
- this.dispatchEvent(new Insights.SidebarInsight.InsightSetHovered());
212
+ #onInsightSetHovered(insightSet: Trace.Insights.Types.InsightSet): void {
213
+ this.element.dispatchEvent(new Insights.SidebarInsight.InsightSetHovered(insightSet.bounds));
88
214
  }
89
215
 
90
- #onZoomClick(event: Event, id: string): void {
91
- event.stopPropagation();
92
- const data = this.#parsedTrace?.insights?.get(id);
93
- if (!data) {
94
- return;
95
- }
96
- this.dispatchEvent(new Insights.SidebarInsight.InsightSetZoom(data.bounds));
216
+ #onInsightSetUnhovered(): void {
217
+ this.element.dispatchEvent(new Insights.SidebarInsight.InsightSetHovered());
97
218
  }
98
219
 
99
- #renderZoomButton(insightSetToggled: boolean): Lit.TemplateResult {
100
- const classes = Lit.Directives.classMap({
101
- 'zoom-icon': true,
102
- active: insightSetToggled,
103
- });
104
-
105
- // clang-format off
106
- return html`
107
- <div class=${classes}>
108
- <devtools-button .data=${{
109
- variant: Buttons.Button.Variant.ICON,
110
- iconName: 'center-focus-weak',
111
- size: Buttons.Button.Size.SMALL,
112
- } as Buttons.Button.ButtonData}
113
- ></devtools-button></div>`;
114
- // clang-format on
115
- }
116
-
117
- #renderDropdownIcon(insightSetToggled: boolean): Lit.TemplateResult {
118
- const containerClasses = Lit.Directives.classMap({
119
- 'dropdown-icon': true,
120
- active: insightSetToggled,
121
- });
122
-
123
- // clang-format off
124
- return html`
125
- <div class=${containerClasses}>
126
- <devtools-button .data=${{
127
- variant: Buttons.Button.Variant.ICON,
128
- iconName: 'chevron-right',
129
- size: Buttons.Button.Size.SMALL,
130
- } as Buttons.Button.ButtonData}
131
- ></devtools-button></div>
132
- `;
133
- // clang-format on
220
+ #onZoomClick(insightSet: Trace.Insights.Types.InsightSet): void {
221
+ this.element.dispatchEvent(new Insights.SidebarInsight.InsightSetZoom(insightSet.bounds));
134
222
  }
135
223
 
136
224
  highlightActiveInsight(): void {
@@ -139,76 +227,28 @@ export class SidebarInsightsTab extends HTMLElement {
139
227
  }
140
228
 
141
229
  // Find the right set for this insight via the set key.
142
- const set = this.#shadow?.querySelector<UI.Widget.WidgetElement<SidebarSingleInsightSet>>(
230
+ const set = this.element.shadowRoot?.querySelector<UI.Widget.WidgetElement<SidebarSingleInsightSet>>(
143
231
  `[data-insight-set-key="${this.#activeInsight.insightSetKey}"]`);
144
232
  set?.getWidget()?.highlightActiveInsight();
145
233
  }
146
234
 
147
- #render(): void {
235
+ override performUpdate(): void {
148
236
  if (!this.#parsedTrace?.insights) {
149
- Lit.render(Lit.nothing, this.#shadow, {host: this});
150
237
  return;
151
238
  }
152
239
 
153
- const insights = this.#parsedTrace.insights;
154
- const hasMultipleInsightSets = insights.size > 1;
155
- const labels = Utils.Helpers.createUrlLabels([...insights.values()].map(({url}) => url));
156
-
157
- const contents =
158
- // clang-format off
159
- html`
160
- <style>${sidebarInsightsTabStyles}</style>
161
- <div class="insight-sets-wrapper">
162
- ${[...insights.values()].map(({id, url}, index) => {
163
- const data: SidebarSingleInsightSetData = {
164
- insightSetKey: id,
165
- activeCategory: this.#selectedCategory,
166
- activeInsight: this.#activeInsight,
167
- parsedTrace: this.#parsedTrace,
168
- };
169
-
170
- const contents = html`
171
- <devtools-widget
172
- data-insight-set-key=${id}
173
- .widgetConfig=${UI.Widget.widgetConfig(SidebarSingleInsightSet, {data})}
174
- ></devtools-widget>
175
- `;
176
-
177
- if (hasMultipleInsightSets) {
178
- return html`<details
179
- ?open=${id === this.#selectedInsightSetKey}
180
- >
181
- <summary
182
- @click=${() => this.#insightSetToggled(id)}
183
- @mouseenter=${() => this.#insightSetHovered(id)}
184
- @mouseleave=${() => this.#insightSetUnhovered()}
185
- title=${url.href}>
186
- ${this.#renderDropdownIcon(id === this.#selectedInsightSetKey)}
187
- <span>${labels[index]}</span>
188
- <span class='zoom-button' @click=${(event: Event) => this.#onZoomClick(event, id)}>${this.#renderZoomButton(id === this.#selectedInsightSetKey)}</span>
189
- </summary>
190
- ${contents}
191
- </details>`;
192
- }
193
-
194
- return contents;
195
- })}
196
- </div>
197
- `;
198
- // clang-format on
199
-
200
- // Insight components contain state, so to prevent insights from previous trace loads breaking things we use the parsedTrace
201
- // as a render key.
202
- // Note: newer Lit has `keyed`, but we don't have that, so we do it manually. https://lit.dev/docs/templates/directives/#keyed
203
- const result = Lit.Directives.repeat([contents], () => this.#parsedTrace, template => template);
204
- Lit.render(result, this.#shadow, {host: this});
205
- }
206
- }
207
-
208
- customElements.define('devtools-performance-sidebar-insights', SidebarInsightsTab);
209
-
210
- declare global {
211
- interface HTMLElementTagNameMap {
212
- 'devtools-performance-sidebar-insights': SidebarInsightsTab;
240
+ const insightSets = [...this.#parsedTrace.insights.values()];
241
+ const input: ViewInput = {
242
+ parsedTrace: this.#parsedTrace,
243
+ labels: Utils.Helpers.createUrlLabels(insightSets.map(({url}) => url)),
244
+ activeInsightSet: this.#selectedInsightSet,
245
+ activeInsight: this.#activeInsight,
246
+ selectedCategory: this.#selectedCategory,
247
+ onInsightSetToggled: this.#onInsightSetToggled.bind(this),
248
+ onInsightSetHovered: this.#onInsightSetHovered.bind(this),
249
+ onInsightSetUnhovered: this.#onInsightSetUnhovered.bind(this),
250
+ onZoomClick: this.#onZoomClick.bind(this),
251
+ };
252
+ this.#view(input, undefined, this.contentElement);
213
253
  }
214
254
  }
@@ -1,7 +1,6 @@
1
1
  // Copyright 2024 The Chromium Authors
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
- /* eslint-disable @devtools/no-lit-render-outside-of-view */
5
4
 
6
5
  /**
7
6
  * @file A list of pass/fail conditions for an insight.
@@ -11,11 +10,13 @@ import '../../../../ui/kit/kit.js';
11
10
 
12
11
  import * as i18n from '../../../../core/i18n/i18n.js';
13
12
  import type * as Trace from '../../../../models/trace/trace.js';
14
- import * as ComponentHelpers from '../../../../ui/components/helpers/helpers.js';
13
+ import * as UI from '../../../../ui/legacy/legacy.js';
15
14
  import * as Lit from '../../../../ui/lit/lit.js';
16
15
 
17
16
  import checklistStyles from './checklist.css.js';
18
17
 
18
+ const {html} = Lit;
19
+
19
20
  const UIStrings = {
20
21
  /**
21
22
  * @description Text for a screen-reader label to tell the user that the icon represents a successful insight check
@@ -32,7 +33,43 @@ const UIStrings = {
32
33
  const str_ = i18n.i18n.registerUIStrings('panels/timeline/components/insights/Checklist.ts', UIStrings);
33
34
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
34
35
 
35
- const {html} = Lit;
36
+ interface ViewInput {
37
+ checklist: GenericChecklist;
38
+ }
39
+
40
+ type View = (input: ViewInput, output: undefined, target: HTMLElement) => void;
41
+
42
+ export const DEFAULT_VIEW: View = (input, output, target) => {
43
+ const {
44
+ checklist,
45
+ } = input;
46
+
47
+ function getIcon(check: GenericChecklist['']): Lit.TemplateResult {
48
+ const icon = check.value ? 'check-circle' : 'clear';
49
+
50
+ const ariaLabel = check.value ? i18nString(UIStrings.successAriaLabel, {PH1: check.label}) :
51
+ i18nString(UIStrings.failedAriaLabel, {PH1: check.label});
52
+ return html`
53
+ <devtools-icon
54
+ aria-label=${ariaLabel}
55
+ name=${icon}
56
+ class=${check.value ? 'check-passed' : 'check-failed'}
57
+ ></devtools-icon>
58
+ `;
59
+ }
60
+
61
+ // clang-format off
62
+ Lit.render(html`
63
+ <style>${checklistStyles}</style>
64
+ <ul>
65
+ ${Object.values(checklist).map(check => html`<li>
66
+ ${getIcon(check)}
67
+ <span data-checklist-label>${check.label}</span>
68
+ </li>`)}
69
+ </ul>
70
+ `, target);
71
+ // clang-format on
72
+ };
36
73
 
37
74
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
75
  type GenericChecklist = Trace.Insights.Types.Checklist<any>;
@@ -46,55 +83,28 @@ export interface TableDataRow {
46
83
  overlays?: Trace.Types.Overlays.Overlay[];
47
84
  }
48
85
 
49
- export class Checklist extends HTMLElement {
50
- readonly #shadow = this.attachShadow({mode: 'open'});
86
+ export class Checklist extends UI.Widget.Widget {
87
+ #view: View;
51
88
  #checklist?: GenericChecklist;
52
89
 
53
- set checklist(checklist: GenericChecklist) {
54
- this.#checklist = checklist;
55
- void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#render);
56
- }
57
-
58
- connectedCallback(): void {
59
- void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#render);
90
+ constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
91
+ super(element, {useShadowDom: true});
92
+ this.#view = view;
60
93
  }
61
94
 
62
- #getIcon(check: GenericChecklist['']): Lit.TemplateResult {
63
- const icon = check.value ? 'check-circle' : 'clear';
64
-
65
- const ariaLabel = check.value ? i18nString(UIStrings.successAriaLabel, {PH1: check.label}) :
66
- i18nString(UIStrings.failedAriaLabel, {PH1: check.label});
67
- return html`
68
- <devtools-icon
69
- aria-label=${ariaLabel}
70
- name=${icon}
71
- class=${check.value ? 'check-passed' : 'check-failed'}
72
- ></devtools-icon>
73
- `;
95
+ set checklist(checklist: GenericChecklist) {
96
+ this.#checklist = checklist;
97
+ this.requestUpdate();
74
98
  }
75
99
 
76
- async #render(): Promise<void> {
100
+ override performUpdate(): void {
77
101
  if (!this.#checklist) {
78
102
  return;
79
103
  }
80
104
 
81
- Lit.render(
82
- html`
83
- <style>${checklistStyles}</style>
84
- <ul>
85
- ${Object.values(this.#checklist).map(check => html`<li>
86
- ${this.#getIcon(check)}
87
- <span data-checklist-label>${check.label}</span>
88
- </li>`)}
89
- </ul>`,
90
- this.#shadow, {host: this});
105
+ const input: ViewInput = {
106
+ checklist: this.#checklist,
107
+ };
108
+ this.#view(input, undefined, this.contentElement);
91
109
  }
92
110
  }
93
-
94
- declare global {
95
- interface HTMLElementTagNameMap {
96
- 'devtools-performance-checklist': Checklist;
97
- }
98
- }
99
-
100
- customElements.define('devtools-performance-checklist', Checklist);
@@ -2,15 +2,16 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
 
5
- import './Checklist.js';
6
-
7
5
  import type {DocumentLatencyInsightModel} from '../../../../models/trace/insights/DocumentLatency.js';
8
6
  import type * as Trace from '../../../../models/trace/trace.js';
7
+ import * as UI from '../../../../ui/legacy/legacy.js';
9
8
  import * as Lit from '../../../../ui/lit/lit.js';
10
9
 
11
10
  import {BaseInsightComponent} from './BaseInsightComponent.js';
11
+ import {Checklist} from './Checklist.js';
12
12
 
13
13
  const {html} = Lit;
14
+ const {widgetConfig} = UI.Widget;
14
15
 
15
16
  export class DocumentLatency extends BaseInsightComponent<DocumentLatencyInsightModel> {
16
17
  override internalName = 'document-latency';
@@ -29,7 +30,9 @@ export class DocumentLatency extends BaseInsightComponent<DocumentLatencyInsight
29
30
  }
30
31
 
31
32
  // clang-format off
32
- return html`<devtools-performance-checklist .checklist=${this.model.data.checklist}></devtools-performance-checklist>`;
33
+ return html`<devtools-widget .widgetConfig=${widgetConfig(Checklist, {
34
+ checklist: this.model.data.checklist,
35
+ })}></devtools-widget>`;
33
36
  // clang-format on
34
37
  }
35
38
  }
@@ -2,17 +2,19 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
 
5
- import './Checklist.js';
6
-
7
5
  import * as i18n from '../../../../core/i18n/i18n.js';
8
6
  import type {LCPDiscoveryInsightModel} from '../../../../models/trace/insights/LCPDiscovery.js';
9
7
  import * as Trace from '../../../../models/trace/trace.js';
10
8
  import * as uiI18n from '../../../../ui/i18n/i18n.js';
9
+ import * as UI from '../../../../ui/legacy/legacy.js';
11
10
  import * as Lit from '../../../../ui/lit/lit.js';
12
11
 
13
12
  import {BaseInsightComponent} from './BaseInsightComponent.js';
13
+ import {Checklist} from './Checklist.js';
14
14
  import {imageRef} from './ImageRef.js';
15
15
 
16
+ const {widgetConfig} = UI.Widget;
17
+
16
18
  const {UIStrings, i18nString, getImageData} = Trace.Insights.Models.LCPDiscovery;
17
19
 
18
20
  const {html} = Lit;
@@ -89,7 +91,9 @@ export class LCPDiscovery extends BaseInsightComponent<LCPDiscoveryInsightModel>
89
91
  // clang-format off
90
92
  return html`
91
93
  <div class="insight-section">
92
- <devtools-performance-checklist class="insight-section" .checklist=${imageData.checklist}></devtools-performance-checklist>
94
+ <devtools-widget .widgetConfig=${widgetConfig(Checklist, {
95
+ checklist: imageData.checklist,
96
+ })}></devtools-widget>
93
97
  <div class="insight-section">${imageRef(imageData.request)}${delayEl}</div>
94
98
  </div>`;
95
99
  // clang-format on