Add Plus Button (2/3): Add declarative plus-button option to TabbedLocation [devtools/devtools-frontend : main]

1 view
Skip to first unread message

Guangyue Xu (Gerrit)

unread,
May 14, 2026, 2:32:43 PMMay 14
to Danil Somsikov, Piotr Paulski, Peter Müller, Sulekha Kulkarni, Leah Tu, devtools-fro...@luci-project-accounts.iam.gserviceaccount.com, devtools-rev...@chromium.org
Attention needed from Danil Somsikov and Piotr Paulski

Guangyue Xu added 1 comment

Patchset-level comments
File-level comment, Patchset 5 (Latest):
Guangyue Xu . resolved

Hi Danil and Piotr,

Could you take a look at this CL when you have a moment? It's the second of three planned CLs for the Plus Button feature, building on CL1's `TabbedPane` slot + overflow event. This one adds the populator/installer in a new `PlusButton.ts` and a declarative `plusButton` option on `createTabbedLocation`. No user-visible change yet — CL3 will opt the panel and drawer locations in.

For context:

Prototype CL: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/7792390

CL1 (landed): https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/7814020

Thanks,
Guangyue Xu

Open in Gerrit

Related details

Attention is currently required from:
  • Danil Somsikov
  • Piotr Paulski
Submit Requirements:
  • requirement is not satisfiedCode-Owners
  • requirement is not satisfiedCode-Review
  • requirement is not satisfiedReview-Enforcement
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: devtools/devtools-frontend
Gerrit-Branch: main
Gerrit-Change-Id: Iea908773522a43a2d640e74a9d31576690b5e3c4
Gerrit-Change-Number: 7846805
Gerrit-PatchSet: 5
Gerrit-Owner: Guangyue Xu <guang...@microsoft.com>
Gerrit-Reviewer: Danil Somsikov <d...@chromium.org>
Gerrit-Reviewer: Guangyue Xu <guang...@microsoft.com>
Gerrit-Reviewer: Piotr Paulski <piotrp...@chromium.org>
Gerrit-CC: Leah Tu <lea...@microsoft.com>
Gerrit-CC: Peter Müller <peterm...@chromium.org>
Gerrit-CC: Sulekha Kulkarni <Sulekha....@microsoft.com>
Gerrit-Attention: Piotr Paulski <piotrp...@chromium.org>
Gerrit-Attention: Danil Somsikov <d...@chromium.org>
Gerrit-Comment-Date: Thu, 14 May 2026 18:32:40 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
unsatisfied_requirement
open
diffy

Danil Somsikov (Gerrit)

unread,
May 15, 2026, 10:06:59 AMMay 15
to Guangyue Xu, Piotr Paulski, Peter Müller, Sulekha Kulkarni, Leah Tu, devtools-fro...@luci-project-accounts.iam.gserviceaccount.com, devtools-rev...@chromium.org
Attention needed from Guangyue Xu and Piotr Paulski

Danil Somsikov added 5 comments

File front_end/ui/legacy/PlusButton.test.ts
Line 432, Patchset 5 (Latest): // Compare against the same exported constant the implementation
// uses, not a hard-coded English string.
assert.strictEqual(button.title, UI.PlusButton.DEFAULT_TITLE());
Danil Somsikov . unresolved

This is not worth the complexity. You can test against the English string, test only run against the English locale

File front_end/ui/legacy/PlusButton.ts
Line 135, Patchset 5 (Latest):export function populatePlusButtonMenu(
Danil Somsikov . unresolved

This logic for gathering, filtering, and deduplicating views feels quite imperative. To follow our Model-View-Presenter (MVP) architecture, this business logic should be moved into a Presenter class. This separates the logic of 'what to show' from the legacy UI logic.

Line 165, Patchset 5 (Latest): if (view.viewId() === 'issues-pane') {
Danil Somsikov . unresolved

You can avoid the duplication here and correctly preserve `view.isPreviewFeature()` instead of hardcoding `false` by merging these two blocks:

```typescript
const isIssuesPane = view.viewId() === 'issues-pane';
addToolEntries.push({
title: view.title(),
jslogContext: view.viewId(),
isPreviewFeature: view.isPreviewFeature(),
action: () => {
if (isIssuesPane) {
Host.userMetrics.issuesPanelOpenedFrom(Host.UserMetrics.IssueOpener.HAMBURGER_MENU);
}
showView(view);
},
});
```

Also, a quick question: is `HAMBURGER_MENU` the correct metric enum to use here, or should the Plus Button get its own enum value (like `MORE_TOOLS_MENU` or similar) to avoid mixing this data with the main DevTools three-dot menu?

Line 286, Patchset 5 (Latest): context.tabbedPane.element.appendChild(button);
Danil Somsikov . unresolved

We should avoid using imperative APIs like `appendChild` to update the DOM. Instead, we should rely on orchestrated rendering of lit-html templates. The plus button should ideally be rendered via a declarative view function.

File front_end/ui/legacy/ViewManager.ts
Line 724, Patchset 5 (Latest): if (options?.plusButton) {
Danil Somsikov . unresolved

This new feature must be guarded by an experiment flag. Please ensure that the plus button is only installed if the corresponding experiment is enabled, for example: `if (options?.plusButton && Root.Runtime.experiments.isEnabled('plus-button'))`.

Open in Gerrit

Related details

Attention is currently required from:
  • Guangyue Xu
  • Piotr Paulski
Submit Requirements:
    • requirement is not satisfiedCode-Owners
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: devtools/devtools-frontend
    Gerrit-Branch: main
    Gerrit-Change-Id: Iea908773522a43a2d640e74a9d31576690b5e3c4
    Gerrit-Change-Number: 7846805
    Gerrit-PatchSet: 5
    Gerrit-Owner: Guangyue Xu <guang...@microsoft.com>
    Gerrit-Reviewer: Danil Somsikov <d...@chromium.org>
    Gerrit-Reviewer: Guangyue Xu <guang...@microsoft.com>
    Gerrit-Reviewer: Piotr Paulski <piotrp...@chromium.org>
    Gerrit-CC: Leah Tu <lea...@microsoft.com>
    Gerrit-CC: Peter Müller <peterm...@chromium.org>
    Gerrit-CC: Sulekha Kulkarni <Sulekha....@microsoft.com>
    Gerrit-Attention: Piotr Paulski <piotrp...@chromium.org>
    Gerrit-Attention: Guangyue Xu <guang...@microsoft.com>
    Gerrit-Comment-Date: Fri, 15 May 2026 14:06:55 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    unsatisfied_requirement
    open
    diffy

    Guangyue Xu (Gerrit)

    unread,
    May 15, 2026, 6:36:44 PMMay 15
    to Danil Somsikov, Piotr Paulski, Peter Müller, Sulekha Kulkarni, Leah Tu, devtools-fro...@luci-project-accounts.iam.gserviceaccount.com, devtools-rev...@chromium.org
    Attention needed from Danil Somsikov and Piotr Paulski

    Guangyue Xu added 6 comments

    File front_end/ui/legacy/PlusButton.test.ts
    Line 432, Patchset 5: // Compare against the same exported constant the implementation

    // uses, not a hard-coded English string.
    assert.strictEqual(button.title, UI.PlusButton.DEFAULT_TITLE());
    Danil Somsikov . unresolved

    This is not worth the complexity. You can test against the English string, test only run against the English locale

    Guangyue Xu

    Done. The default-tooltip assertion in the `installPlusButton` describe block now compares against the literal 'More tools' (no `i18nString` round-trip), matching the convention used elsewhere.

    File front_end/ui/legacy/PlusButton.ts
    Line 135, Patchset 5:export function populatePlusButtonMenu(
    Danil Somsikov . unresolved

    This logic for gathering, filtering, and deduplicating views feels quite imperative. To follow our Model-View-Presenter (MVP) architecture, this business logic should be moved into a Presenter class. This separates the logic of 'what to show' from the legacy UI logic.

    Guangyue Xu

    Done. Extracted `PlusButtonPresenter` with a `buildModel(): PlusButtonMenuModel` method (lines ~138-235). All gathering / filtering / dedup / sort lives there; `populatePlusButtonMenu` is now a thin "view" that takes the model and pushes it into `ContextMenu`. Presenter is exported and unit-tested directly (`describe('PlusButtonPresenter')`).

    Line 165, Patchset 5: if (view.viewId() === 'issues-pane') {
    Danil Somsikov . unresolved

    You can avoid the duplication here and correctly preserve `view.isPreviewFeature()` instead of hardcoding `false` by merging these two blocks:

    ```typescript
    const isIssuesPane = view.viewId() === 'issues-pane';
    addToolEntries.push({
    title: view.title(),
    jslogContext: view.viewId(),
    isPreviewFeature: view.isPreviewFeature(),
    action: () => {
    if (isIssuesPane) {
    Host.userMetrics.issuesPanelOpenedFrom(Host.UserMetrics.IssueOpener.HAMBURGER_MENU);
    }
    showView(view);
    },
    });
    ```

    Also, a quick question: is `HAMBURGER_MENU` the correct metric enum to use here, or should the Plus Button get its own enum value (like `MORE_TOOLS_MENU` or similar) to avoid mixing this data with the main DevTools three-dot menu?

    Guangyue Xu

    Done — merged the two branches; the issues-pane case now reuses `view.isPreviewFeature()` and only the UMA call is conditional on `isIssuesPane`.

    On the `enum:` agreed, added a new `IssueOpener.MORE_TOOLS_MENU = 6` in `UserMetrics.ts:749-758` (with a JSDoc comment pointing to the matching Chromium `enum DevToolsIssuesPanelOpenedFrom` in `tools/metrics/histograms/metadata/dev/enums.xml`). The plus-button issues entry now records `MORE_TOOLS_MENU` instead of `HAMBURGER_MENU` so the dashboard doesn't conflate the two surfaces. Filing the matching Chromium-side `enums.xml` CL as a follow-up.

    Line 206, Patchset 6: if (otherLocation) {
    Guangyue Xu . unresolved

    Removed the `includeOtherMainLocation` opt-in: cross-location is the design intent, not a per-caller knob. Per the design doc, the plus button replaces the "More tools" ≫ button to make the experience symmetric across the main panel and drawer — "the context menu for the '+' button in the main panel would show the tools not already available in the main panel, and the same holds for the drawer panel." The presenter still gates the loop on `location ∈ {PANEL, DRAWER_VIEW}`, so non-main tabbed locations are unaffected.

    Line 286, Patchset 5: context.tabbedPane.element.appendChild(button);
    Danil Somsikov . unresolved

    We should avoid using imperative APIs like `appendChild` to update the DOM. Instead, we should rely on orchestrated rendering of lit-html templates. The plus button should ideally be rendered via a declarative view function.

    Guangyue Xu

    Done. `installPlusButton` now uses a single `tabbedPane.element`). No more `document.createElement` / `setAttribute` / `appendChild`. The `Directives.ref` capture replaces the previous `querySelector` round-trip so we still hand the caller back the `MenuButton` instance.

    File front_end/ui/legacy/ViewManager.ts
    Line 724, Patchset 5: if (options?.plusButton) {
    Danil Somsikov . unresolved

    This new feature must be guarded by an experiment flag. Please ensure that the plus button is only installed if the corresponding experiment is enabled, for example: `if (options?.plusButton && Root.Runtime.experiments.isEnabled('plus-button'))`.

    Guangyue Xu

    Done. Registered a `plus-button` experiment in `ExperimentNames.ts` (`PLUS_BUTTON = 'plus-button'`) and `MainImpl.ts`, and added it to the `DevtoolsExperiments` UMA enum (value 112, MAX bumped to 113). The install site in `ViewManager.ts:724` now reads:

    ```
    if (options?.plusButton && Root.Runtime.experiments.isEnabled(Root.ExperimentNames.ExperimentName.PLUS_BUTTON)) {  PlusButton.installPlusButton(...);}
    ```

    Also added a regression test in `ViewManager.test.ts` that flips the experiment and asserts the button mounts before `appendApplicableItems`. Test-only experiment registration mirrored in `RuntimeHelpers.ts`.

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Danil Somsikov
    • Piotr Paulski
    Submit Requirements:
    • requirement is not satisfiedCode-Owners
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: devtools/devtools-frontend
    Gerrit-Branch: main
    Gerrit-Change-Id: Iea908773522a43a2d640e74a9d31576690b5e3c4
    Gerrit-Change-Number: 7846805
    Gerrit-PatchSet: 7
    Gerrit-Owner: Guangyue Xu <guang...@microsoft.com>
    Gerrit-Reviewer: Danil Somsikov <d...@chromium.org>
    Gerrit-Reviewer: Guangyue Xu <guang...@microsoft.com>
    Gerrit-Reviewer: Piotr Paulski <piotrp...@chromium.org>
    Gerrit-CC: Leah Tu <lea...@microsoft.com>
    Gerrit-CC: Peter Müller <peterm...@chromium.org>
    Gerrit-CC: Sulekha Kulkarni <Sulekha....@microsoft.com>
    Gerrit-Attention: Piotr Paulski <piotrp...@chromium.org>
    Gerrit-Attention: Danil Somsikov <d...@chromium.org>
    Gerrit-Comment-Date: Fri, 15 May 2026 22:36:41 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    Comment-In-Reply-To: Danil Somsikov <d...@chromium.org>
    unsatisfied_requirement
    open
    diffy

    Danil Somsikov (Gerrit)

    unread,
    May 18, 2026, 3:37:21 AM (12 days ago) May 18
    to Guangyue Xu, Piotr Paulski, Peter Müller, Sulekha Kulkarni, Leah Tu, devtools-fro...@luci-project-accounts.iam.gserviceaccount.com, devtools-rev...@chromium.org
    Attention needed from Guangyue Xu and Piotr Paulski

    Danil Somsikov added 2 comments

    File front_end/entrypoints/main/MainImpl.ts
    Line 387, Patchset 8 (Latest): Root.Runtime.experiments.register(
    Root.ExperimentNames.ExperimentName.PLUS_BUTTON, 'Show "+" button on the tab strip for adding tools');
    File front_end/ui/legacy/PlusButton.ts
    Line 169, Patchset 8 (Latest): // Skip views that are already shown as a visible tab — the user can
    Danil Somsikov . unresolved

    `hasTab` returns true for both visible and hidden tabs. You might want to update this comment to clarify that hidden tabs are skipped here because they are already handled in the `overflowTabs` section above.

    For example:
    ```typescript
    // Skip views that already have a tab. Hidden tabs are listed in the overflow section,
    // and visible tabs are accessible directly in the tab strip.
    ```

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Guangyue Xu
    • Piotr Paulski
    Submit Requirements:
    • requirement is not satisfiedCode-Owners
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: devtools/devtools-frontend
    Gerrit-Branch: main
    Gerrit-Change-Id: Iea908773522a43a2d640e74a9d31576690b5e3c4
    Gerrit-Change-Number: 7846805
    Gerrit-PatchSet: 8
    Gerrit-Owner: Guangyue Xu <guang...@microsoft.com>
    Gerrit-Reviewer: Danil Somsikov <d...@chromium.org>
    Gerrit-Reviewer: Guangyue Xu <guang...@microsoft.com>
    Gerrit-Reviewer: Piotr Paulski <piotrp...@chromium.org>
    Gerrit-CC: Leah Tu <lea...@microsoft.com>
    Gerrit-CC: Peter Müller <peterm...@chromium.org>
    Gerrit-CC: Sulekha Kulkarni <Sulekha....@microsoft.com>
    Gerrit-Attention: Piotr Paulski <piotrp...@chromium.org>
    Gerrit-Attention: Guangyue Xu <guang...@microsoft.com>
    Gerrit-Comment-Date: Mon, 18 May 2026 07:37:17 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    unsatisfied_requirement
    open
    diffy
    Reply all
    Reply to author
    Forward
    0 new messages