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.
- package/front_end/Images/src/spark.svg +10 -0
- package/front_end/core/sdk/ResourceTreeModel.ts +0 -1
- package/front_end/generated/SupportedCSSProperties.js +18 -0
- package/front_end/models/javascript_metadata/NativeFunctions.js +15 -23
- package/front_end/panels/application/BounceTrackingMitigationsTreeElement.ts +2 -6
- package/front_end/panels/application/components/BounceTrackingMitigationsView.ts +133 -118
- package/front_end/panels/security/CookieControlsView.ts +21 -10
- package/front_end/panels/timeline/CompatibilityTracksAppender.ts +0 -1
- package/front_end/panels/timeline/components/Sidebar.ts +16 -7
- package/front_end/panels/timeline/components/SidebarInsightsTab.ts +169 -129
- package/front_end/panels/timeline/components/insights/Checklist.ts +53 -43
- package/front_end/panels/timeline/components/insights/DocumentLatency.ts +6 -3
- package/front_end/panels/timeline/components/insights/LCPDiscovery.ts +7 -3
- package/front_end/panels/timeline/components/sidebarInsightsTab.css +50 -48
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/components/settings/SettingCheckbox.ts +4 -6
- package/front_end/ui/legacy/TabbedPane.ts +19 -12
- package/front_end/ui/legacy/Widget.ts +1 -3
- package/front_end/ui/legacy/tabbedPane.css +4 -7
- package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
- package/package.json +1 -1
|
@@ -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
|
-
|
|
21
|
-
|
|
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
|
|
154
|
+
* You can only have one of these open at any time.
|
|
30
155
|
*/
|
|
31
|
-
#
|
|
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.#
|
|
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.#
|
|
175
|
+
this.#selectedInsightSet = [...this.#parsedTrace.insights.values()].at(0) ?? null;
|
|
46
176
|
}
|
|
47
177
|
|
|
48
|
-
|
|
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
|
|
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.#
|
|
198
|
+
this.#selectedInsightSet = this.#parsedTrace?.insights?.get(this.#activeInsight.insightSetKey) ?? null;
|
|
68
199
|
}
|
|
69
|
-
|
|
200
|
+
this.requestUpdate();
|
|
70
201
|
}
|
|
71
202
|
|
|
72
|
-
#
|
|
73
|
-
this.#
|
|
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.#
|
|
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
|
-
|
|
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
|
-
#
|
|
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
|
-
#
|
|
91
|
-
|
|
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
|
-
#
|
|
100
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
50
|
-
|
|
86
|
+
export class Checklist extends UI.Widget.Widget {
|
|
87
|
+
#view: View;
|
|
51
88
|
#checklist?: GenericChecklist;
|
|
52
89
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
63
|
-
|
|
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
|
-
|
|
100
|
+
override performUpdate(): void {
|
|
77
101
|
if (!this.#checklist) {
|
|
78
102
|
return;
|
|
79
103
|
}
|
|
80
104
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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-
|
|
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-
|
|
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
|