Skip to content

Commit 0c19a5d

Browse files
Lightning00BladeDevtools-frontend LUCI CQ
authored andcommitted
Remove some of the Fragment.html
Bug: none Change-Id: I2ec822e462f2e5820a373a85ac5fc4580d8433c3 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/7526706 Reviewed-by: Danil Somsikov <dsv@chromium.org> Commit-Queue: Nikolay Vitkov <nvitkov@chromium.org>
1 parent 2a50e02 commit 0c19a5d

20 files changed

Lines changed: 472 additions & 147 deletions

front_end/panels/application/StorageView.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -510,8 +510,14 @@ export class StorageView extends UI.Widget.VBox {
510510
const quotaAsString = i18n.ByteUtilities.bytesToString(response.quota);
511511
const usageAsString = i18n.ByteUtilities.bytesToString(response.usage);
512512
const formattedQuotaAsString = i18nString(UIStrings.storageWithCustomMarker, {PH1: quotaAsString});
513-
const quota =
514-
quotaOverridden ? UI.Fragment.Fragment.build`<b>${formattedQuotaAsString}</b>`.element() : quotaAsString;
513+
514+
let quota: string|HTMLElement = quotaAsString;
515+
if (quotaOverridden) {
516+
const element = document.createElement('b');
517+
element.textContent = formattedQuotaAsString;
518+
quota = element;
519+
}
520+
515521
const element = uiI18n.getFormatLocalizedString(str_, UIStrings.storageQuotaUsed, {PH1: usageAsString, PH2: quota});
516522
this.quotaRow.appendChild(element);
517523
UI.Tooltip.Tooltip.install(

front_end/panels/lighthouse/BUILD.gn

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ devtools_ui_module("unittests") {
9393
"LighthousePanel.test.ts",
9494
"LighthouseProtocolService.test.ts",
9595
"LighthouseReportRenderer.test.ts",
96+
"LighthouseStartView.test.ts",
97+
"LighthouseTimespanView.test.ts",
98+
"RadioSetting.test.ts",
9699
]
97100

98101
deps = [
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 2026 The Chromium Authors
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import {assertScreenshot, renderElementIntoDOM} from '../../testing/DOMHelpers.js';
6+
import {describeWithEnvironment} from '../../testing/EnvironmentHelpers.js';
7+
8+
import type * as LighthouseModule from './lighthouse.js';
9+
10+
describeWithEnvironment('LighthouseStartView', () => {
11+
let lighthouse: typeof LighthouseModule;
12+
13+
beforeEach(async () => {
14+
lighthouse = await import('./lighthouse.js');
15+
});
16+
17+
it('renders correctly', async () => {
18+
const controller = {
19+
getFlags: () => ({mode: 'navigation'}),
20+
recomputePageAuditability: () => {},
21+
} as unknown as LighthouseModule.LighthouseController.LighthouseController;
22+
23+
const panel = {
24+
handleTimespanStart: () => {},
25+
handleCompleteRun: () => {},
26+
} as unknown as LighthouseModule.LighthousePanel.LighthousePanel;
27+
28+
const view = new lighthouse.LighthouseStartView.StartView(controller, panel);
29+
renderElementIntoDOM(view);
30+
31+
await assertScreenshot('lighthouse/LighthouseStartView.png');
32+
});
33+
});

front_end/panels/lighthouse/LighthouseStartView.ts

Lines changed: 99 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type * as Platform from '../../core/platform/platform.js';
1111
import * as Buttons from '../../ui/components/buttons/buttons.js';
1212
import {Link} from '../../ui/kit/kit.js';
1313
import * as UI from '../../ui/legacy/legacy.js';
14+
import {Directives, html, render} from '../../ui/lit/lit.js';
1415

1516
import {type LighthouseController, type Preset, Presets, RuntimeSettings} from './LighthouseController.js';
1617
import type {LighthousePanel} from './LighthousePanel.js';
@@ -55,6 +56,78 @@ const UIStrings = {
5556
const str_ = i18n.i18n.registerUIStrings('panels/lighthouse/LighthouseStartView.ts', UIStrings);
5657
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
5758

59+
const renderStartView = (
60+
_input: Record<string, never>,
61+
output: {
62+
helpText?: HTMLElement,
63+
warningText?: HTMLElement,
64+
modeFormElements?: HTMLElement,
65+
deviceTypeFormElements?: HTMLElement,
66+
categoriesFormElements?: HTMLElement,
67+
},
68+
target: HTMLElement,
69+
): void => {
70+
// clang-format off
71+
render(
72+
html`
73+
<form class="lighthouse-start-view">
74+
<header class="hbox">
75+
<div class="lighthouse-logo"></div>
76+
<div class="lighthouse-title">
77+
${i18nString(UIStrings.generateLighthouseReport)}
78+
</div>
79+
<div class="lighthouse-start-button-container"></div>
80+
</header>
81+
<div
82+
${Directives.ref(e => {
83+
output.helpText = e as HTMLElement;
84+
})}
85+
class="lighthouse-help-text hidden"
86+
></div>
87+
<div class="lighthouse-options hbox">
88+
<div class="lighthouse-form-section">
89+
<div
90+
class="lighthouse-form-elements"
91+
${Directives.ref(e => {
92+
output.modeFormElements = e as HTMLElement;
93+
})}
94+
></div>
95+
</div>
96+
<div class="lighthouse-form-section">
97+
<div
98+
class="lighthouse-form-elements"
99+
${Directives.ref(e => {
100+
output.deviceTypeFormElements = e as HTMLElement;
101+
})}
102+
></div>
103+
</div>
104+
<div class="lighthouse-form-categories">
105+
<fieldset class="lighthouse-form-section lighthouse-form-categories-fieldset">
106+
<legend class="lighthouse-form-section-label">
107+
${i18nString(UIStrings.categories)}
108+
</legend>
109+
<div
110+
class="lighthouse-form-elements"
111+
${Directives.ref(e => {
112+
output.categoriesFormElements = e as HTMLElement;
113+
})}
114+
></div>
115+
</fieldset>
116+
</div>
117+
</div>
118+
<div
119+
${Directives.ref(e => {
120+
output.warningText = e as HTMLElement;
121+
})}
122+
class="lighthouse-warning-text hidden"
123+
></div>
124+
</form>
125+
`,
126+
target,
127+
);
128+
// clang-format on
129+
};
130+
58131
export class StartView extends UI.Widget.Widget {
59132
private controller: LighthouseController;
60133
private panel: LighthousePanel;
@@ -145,13 +218,11 @@ export class StartView extends UI.Widget.Widget {
145218
}
146219
}
147220

148-
private populateFormControls(fragment: UI.Fragment.Fragment, mode?: string): void {
221+
private populateFormControls(deviceTypeFormElements: Element, categoryFormElements: Element, mode?: string): void {
149222
// Populate the device type
150-
const deviceTypeFormElements = fragment.$('device-type-form-elements');
151223
this.populateRuntimeSettingAsRadio('lighthouse.device-type', i18nString(UIStrings.device), deviceTypeFormElements);
152224

153225
// Populate the categories
154-
const categoryFormElements = fragment.$('categories-form-elements') as HTMLElement;
155226

156227
this.checkboxes = [];
157228
for (const preset of Presets) {
@@ -174,44 +245,34 @@ export class StartView extends UI.Widget.Widget {
174245
this.populateRuntimeSettingAsToolbarDropdown('lighthouse.throttling', this.#settingsToolbar);
175246

176247
const {mode} = this.controller.getFlags();
177-
this.populateStartButton(mode);
178248

179-
const fragment = UI.Fragment.Fragment.build`
180-
<form class="lighthouse-start-view">
181-
<header class="hbox">
182-
<div class="lighthouse-logo"></div>
183-
<h1 class="lighthouse-title">${i18nString(UIStrings.generateLighthouseReport)}</h1>
184-
<div class="lighthouse-start-button-container" $="start-button-container">${this.startButton}</div>
185-
</header>
186-
<div $="help-text" class="lighthouse-help-text hidden"></div>
187-
<div class="lighthouse-options hbox">
188-
<div class="lighthouse-form-section">
189-
<div class="lighthouse-form-elements" $="mode-form-elements"></div>
190-
</div>
191-
<div class="lighthouse-form-section">
192-
<div class="lighthouse-form-elements" $="device-type-form-elements"></div>
193-
</div>
194-
<div class="lighthouse-form-categories">
195-
<fieldset class="lighthouse-form-section lighthouse-form-categories-fieldset">
196-
<legend class="lighthouse-form-section-label">${i18nString(UIStrings.categories)}</legend>
197-
<div class="lighthouse-form-elements" $="categories-form-elements"></div>
198-
</fieldset>
199-
</div>
200-
</div>
201-
<div $="warning-text" class="lighthouse-warning-text hidden"></div>
202-
</form>
203-
`;
204-
205-
this.helpText = fragment.$('help-text');
206-
this.warningText = fragment.$('warning-text');
207-
208-
const modeFormElements = fragment.$('mode-form-elements');
209-
this.populateRuntimeSettingAsRadio('lighthouse.mode', i18nString(UIStrings.mode), modeFormElements);
249+
const output = {
250+
helpText: undefined as HTMLElement | undefined,
251+
warningText: undefined as HTMLElement | undefined,
252+
modeFormElements: undefined as HTMLElement | undefined,
253+
deviceTypeFormElements: undefined as HTMLElement | undefined,
254+
categoriesFormElements: undefined as HTMLElement | undefined,
255+
};
256+
257+
renderStartView(
258+
{},
259+
output,
260+
this.contentElement,
261+
);
210262

211-
this.populateFormControls(fragment, mode);
263+
this.helpText = output.helpText;
264+
this.warningText = output.warningText;
212265

213-
this.contentElement.textContent = '';
214-
this.contentElement.append(fragment.element());
266+
const modeFormElements = output.modeFormElements;
267+
const deviceTypeFormElements = output.deviceTypeFormElements;
268+
const categoriesFormElements = output.categoriesFormElements;
269+
270+
if (!modeFormElements || !deviceTypeFormElements || !categoriesFormElements) {
271+
throw new Error('Required elements not found in template');
272+
}
273+
274+
this.populateRuntimeSettingAsRadio('lighthouse.mode', i18nString(UIStrings.mode), modeFormElements);
275+
this.populateFormControls(deviceTypeFormElements, categoriesFormElements, mode);
215276

216277
this.refresh();
217278
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2026 The Chromium Authors
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import {assertScreenshot, TEST_CONTAINER_ID} from '../../testing/DOMHelpers.js';
6+
import {describeWithEnvironment} from '../../testing/EnvironmentHelpers.js';
7+
8+
import type * as LighthouseModule from './lighthouse.js';
9+
10+
describeWithEnvironment('LighthouseTimespanView', () => {
11+
let lighthouse: typeof LighthouseModule;
12+
13+
beforeEach(async () => {
14+
lighthouse = await import('./lighthouse.js');
15+
});
16+
17+
it('renders correctly', async () => {
18+
const panel = {
19+
handleTimespanEnd: () => {},
20+
handleRunCancel: () => {},
21+
} as unknown as LighthouseModule.LighthousePanel.LighthousePanel;
22+
23+
const view = new lighthouse.LighthouseTimespanView.TimespanView(panel);
24+
25+
const container = document.getElementById(TEST_CONTAINER_ID);
26+
assert(container);
27+
container.append(view.contentElement);
28+
29+
// Ensure contentElement has size
30+
view.contentElement.style.width = '800px';
31+
view.contentElement.style.height = '600px';
32+
view.contentElement.style.display = 'block';
33+
34+
await assertScreenshot('lighthouse/LighthouseTimespanView.png');
35+
});
36+
});

front_end/panels/lighthouse/LighthouseTimespanView.ts

Lines changed: 53 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import * as i18n from '../../core/i18n/i18n.js';
77
import * as Geometry from '../../models/geometry/geometry.js';
88
import * as Buttons from '../../ui/components/buttons/buttons.js';
99
import * as UI from '../../ui/legacy/legacy.js';
10+
import {Directives, html, render} from '../../ui/lit/lit.js';
1011

1112
import lighthouseDialogStyles from './lighthouseDialog.css.js';
1213
import type {LighthousePanel} from './LighthousePanel.js';
@@ -37,6 +38,43 @@ const UIStrings = {
3738
const str_ = i18n.i18n.registerUIStrings('panels/lighthouse/LighthouseTimespanView.ts', UIStrings);
3839
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
3940

41+
const renderTimespanView = (
42+
input: {
43+
cancelButton: Element,
44+
endButton: Element,
45+
},
46+
output: {
47+
statusHeader?: HTMLElement,
48+
contentContainer?: HTMLElement,
49+
},
50+
target: ShadowRoot,
51+
): void => {
52+
// clang-format off
53+
render(
54+
html`
55+
<div class="lighthouse-view vbox">
56+
<span
57+
${Directives.ref(e => {
58+
output.statusHeader = e as HTMLElement;
59+
})}
60+
class="header"
61+
></span>
62+
<span
63+
${Directives.ref(e => {
64+
output.contentContainer = e as HTMLElement;
65+
})}
66+
class="lighthouse-dialog-text"
67+
></span>
68+
<div class="lighthouse-action-buttons hbox">
69+
${input.cancelButton} ${input.endButton}
70+
</div>
71+
</div>
72+
`,
73+
target,
74+
);
75+
// clang-format on
76+
};
77+
4078
export class TimespanView extends UI.Dialog.Dialog {
4179
private panel: LighthousePanel;
4280
private statusHeader: Element|null;
@@ -90,20 +128,21 @@ export class TimespanView extends UI.Dialog.Dialog {
90128
className: 'cancel',
91129
jslogContext: 'lighthouse.cancel',
92130
});
93-
const fragment = UI.Fragment.Fragment.build`
94-
<div class="lighthouse-view vbox">
95-
<span $="status-header" class="header"></span>
96-
<span $="call-to-action" class="lighthouse-dialog-text"></span>
97-
<div class="lighthouse-action-buttons hbox">
98-
${cancelButton}
99-
${this.endButton}
100-
</div>
101-
</div>
102-
`;
103-
104-
this.statusHeader = fragment.$('status-header');
105-
this.contentContainer = fragment.$('call-to-action');
106-
dialogRoot.appendChild(fragment.element());
131+
132+
const output = {
133+
statusHeader: undefined as HTMLElement | undefined,
134+
contentContainer: undefined as HTMLElement | undefined,
135+
};
136+
137+
renderTimespanView(
138+
{
139+
cancelButton,
140+
endButton: this.endButton,
141+
},
142+
output, dialogRoot);
143+
144+
this.statusHeader = output.statusHeader ?? null;
145+
this.contentContainer = output.contentContainer ?? null;
107146

108147
this.setSizeBehavior(UI.GlassPane.SizeBehavior.SET_EXACT_WIDTH_MAX_HEIGHT);
109148
this.setMaxContentSize(new Geometry.Size(500, 400));
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2026 The Chromium Authors
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import * as Common from '../../core/common/common.js';
6+
import {assertScreenshot, renderElementIntoDOM} from '../../testing/DOMHelpers.js';
7+
import {describeWithEnvironment} from '../../testing/EnvironmentHelpers.js';
8+
9+
import type * as LighthouseModule from './lighthouse.js';
10+
11+
describeWithEnvironment('RadioSetting', () => {
12+
let lighthouse: typeof LighthouseModule;
13+
14+
beforeEach(async () => {
15+
lighthouse = await import('./lighthouse.js');
16+
});
17+
18+
it('renders correctly', async () => {
19+
const setting = Common.Settings.Settings.instance().createSetting(
20+
'test-radio-setting', 'b', Common.Settings.SettingStorageType.LOCAL);
21+
const options = [
22+
{value: 'a', label: () => 'Option A' as Common.UIString.LocalizedString},
23+
{value: 'b', label: () => 'Option B' as Common.UIString.LocalizedString},
24+
{value: 'c', label: () => 'Option C' as Common.UIString.LocalizedString},
25+
];
26+
27+
const radioSetting = new lighthouse.RadioSetting.RadioSetting(options, setting, 'Test Radio Setting');
28+
renderElementIntoDOM(radioSetting.element);
29+
30+
await assertScreenshot('lighthouse/RadioSetting.png');
31+
});
32+
});

0 commit comments

Comments
 (0)