[GM3Restyling] Use <select> for Recorder replay options menu

Screenshot: https://imgur.com/a/nUiK1om

Bug: 325441856
Change-Id: I41396ff80c62dd745e6d73873f5239dfaadb6e0f
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6354790
Reviewed-by: Kim-Anh Tran <kimanh@chromium.org>
Auto-Submit: Kateryna Prokopenko <kprokopenko@chromium.org>
Commit-Queue: Kateryna Prokopenko <kprokopenko@chromium.org>
This commit is contained in:
Kateryna Prokopenko
2025-03-18 05:40:22 -07:00
committed by Devtools-frontend LUCI CQ
parent a0b49423bf
commit 7809edb4e4
3 changed files with 41 additions and 78 deletions
@@ -3,7 +3,6 @@
// found in the LICENSE file.
import {renderElementIntoDOM} from '../../../testing/DOMHelpers.js';
import * as Menus from '../../../ui/components/menus/menus.js';
import * as RenderCoordinator from '../../../ui/components/render_coordinator/render_coordinator.js';
import * as RecorderComponents from './components.js';
@@ -45,12 +44,11 @@ describe('SelectButton', () => {
await RenderCoordinator.done();
const dispatcherSpy = sinon.spy(component, 'dispatchEvent');
const selectMenu = component.shadowRoot?.querySelector(
'devtools-select-menu',
'select',
);
assert.exists(selectMenu);
selectMenu.dispatchEvent(
new Menus.SelectMenu.SelectMenuItemSelectedEvent('item1'),
);
selectMenu.value = 'item1';
selectMenu.dispatchEvent(new Event('change'));
dispatcherSpy.calledOnceWithExactly(
RecorderComponents.SelectButton.SelectMenuSelectedEvent as unknown as sinon.SinonMatcher);
@@ -7,9 +7,9 @@ import '../../../ui/components/menus/menus.js';
import * as Platform from '../../../core/platform/platform.js';
import type {LocalizedString} from '../../../core/platform/UIString.js';
import * as Buttons from '../../../ui/components/buttons/buttons.js';
import * as Dialogs from '../../../ui/components/dialogs/dialogs.js';
import * as ComponentHelpers from '../../../ui/components/helpers/helpers.js';
import type * as Menus from '../../../ui/components/menus/menus.js';
// eslint-disable-next-line rulesdir/es-modules-import
import inspectorCommonStylesRaw from '../../../ui/legacy/inspectorCommon.css.js';
import * as Lit from '../../../ui/lit/lit.js';
import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
import * as Models from '../models/models.js';
@@ -17,6 +17,10 @@ import type * as Actions from '../recorder-actions/recorder-actions.js';
import selectButtonStylesRaw from './selectButton.css.js';
// TODO(crbug.com/391381439): Fully migrate off of constructed style sheets.
const inspectorCommonStyles = new CSSStyleSheet();
inspectorCommonStyles.replaceSync(inspectorCommonStylesRaw.cssText);
// TODO(crbug.com/391381439): Fully migrate off of constructed style sheets.
const selectButtonStyles = new CSSStyleSheet();
selectButtonStyles.replaceSync(selectButtonStylesRaw.cssText);
@@ -114,7 +118,7 @@ export class SelectButton extends HTMLElement {
};
connectedCallback(): void {
this.#shadow.adoptedStyleSheets = [selectButtonStyles];
this.#shadow.adoptedStyleSheets = [inspectorCommonStyles, selectButtonStyles];
void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#render);
}
@@ -174,26 +178,29 @@ export class SelectButton extends HTMLElement {
}
#handleSelectMenuSelect(
evt: Menus.SelectMenu.SelectMenuItemSelectedEvent,
evt: Event,
): void {
this.dispatchEvent(new SelectMenuSelectedEvent(evt.itemValue as string));
void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#render);
if (evt.target instanceof HTMLSelectElement) {
this.dispatchEvent(new SelectMenuSelectedEvent(evt.target.value as string));
void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#render);
}
}
#renderSelectItem(
item: SelectButtonItem,
selectedItem: SelectButtonItem,
): Lit.TemplateResult {
const selected = item.value === selectedItem.value;
// clang-format off
return html`
<devtools-menu-item
<option
.title=${item.label()}
.value=${item.value}
.selected=${item.value === selectedItem.value}
value=${item.value}
?selected=${selected}
jslog=${VisualLogging.item(Platform.StringUtilities.toKebabCase(item.value)).track({click: true})}
>${
item.label()
}</devtools-menu-item>
(selected && item.buttonLabel) ? item.buttonLabel() : item.label()
}</option>
`;
// clang-format on
}
@@ -204,9 +211,9 @@ export class SelectButton extends HTMLElement {
): Lit.TemplateResult {
// clang-format off
return html`
<devtools-menu-group .name=${group.name}>
<optgroup label=${group.name}>
${group.items.map(item => this.#renderSelectItem(item, selectedItem))}
</devtools-menu-group>
</optgroup>
`;
// clang-format on
}
@@ -236,31 +243,21 @@ export class SelectButton extends HTMLElement {
Lit.render(
html`
<div class="select-button" title=${ifDefined(this.#getTitle(menuLabel))}>
<devtools-select-menu
title=""
class=${classMap(classes)}
@selectmenuselected=${this.#handleSelectMenuSelect}
?disabled=${this.#props.disabled}
.showArrow=${true}
.sideButton=${false}
.showSelectedItem=${true}
.disabled=${this.#props.disabled}
.buttonTitle=${() => html`${menuLabel}`}
.position=${Dialogs.Dialog.DialogVerticalPosition.BOTTOM}
.horizontalAlignment=${
Dialogs.Dialog.DialogHorizontalAlignment.RIGHT
}
>
${
hasGroups
? this.#props.groups.map(group =>
this.#renderSelectGroup(group, selectedItem),
)
: this.#props.items.map(item =>
this.#renderSelectItem(item, selectedItem),
)
}
</devtools-select-menu>
<select
class=${classMap(classes)}
?disabled=${this.#props.disabled}
jslog=${VisualLogging.dropDown('network-conditions').track({change: true})}
@change=${this.#handleSelectMenuSelect}>
${
hasGroups
? this.#props.groups.map(group =>
this.#renderSelectGroup(group, selectedItem),
)
: this.#props.items.map(item =>
this.#renderSelectItem(item, selectedItem),
)
}
</select>
${
selectedItem
? html`
+3 -35
View File
@@ -11,12 +11,11 @@ import {openPanelViaMoreTools} from '../../../test/e2e/helpers/settings-helpers.
import {
$,
click,
clickElement,
getBrowserAndPages,
getTestServerPort,
goToResource,
platform,
timeout,
selectOption,
waitFor,
waitForAria,
} from '../../../test/shared/helper.js';
@@ -234,46 +233,15 @@ async function setCode(flow: string) {
}, flow);
}
async function waitForDialogAnimationEnd(root?: ElementHandle) {
const ANIMATION_TIMEOUT = 2000;
const dialog = await waitFor('dialog[open]', root);
const animationPromise = dialog.evaluate((dialog: Element) => {
return new Promise<void>(resolve => {
dialog.addEventListener('animationend', () => resolve(), {once: true});
});
});
await Promise.race([animationPromise, timeout(ANIMATION_TIMEOUT)]);
}
export async function clickSelectButtonItem(itemLabel: string, root: string) {
const selectMenu = await waitFor(root);
const selectMenuButton = await waitFor(
'devtools-select-menu-button',
'select',
selectMenu,
);
const animationEndPromise = waitForDialogAnimationEnd();
await click('#arrow', {root: selectMenuButton});
await animationEndPromise;
const selectMenuItems = await selectMenu.$$('pierce/devtools-menu-item');
const selectMenuItemIndex =
await Promise
.all(
selectMenuItems.map(
selectMenuItem => selectMenuItem.evaluate(element => element.textContent?.trim()),
),
)
.then(
elements => elements.findIndex(elementText => elementText === itemLabel),
);
void selectOption(await selectMenuButton.toElement('select'), itemLabel);
if (selectMenuItemIndex === -1) {
throw new Error(
`Select menu item for label "${itemLabel}" is not found in "${root}"`,
);
}
await clickElement(selectMenuItems[selectMenuItemIndex]);
await click('devtools-button', {root: selectMenu});
}