Skip to content

feat: Add bottom toolbar positions to ToolbarUsage and render them in the standard layout#1542

Open
jason-crow wants to merge 1 commit intomasterfrom
features/bottom-toolbar-positions
Open

feat: Add bottom toolbar positions to ToolbarUsage and render them in the standard layout#1542
jason-crow wants to merge 1 commit intomasterfrom
features/bottom-toolbar-positions

Conversation

@jason-crow
Copy link
Copy Markdown
Contributor

@jason-crow jason-crow commented Apr 28, 2026

Summary

Adds BottomContentManipulation and BottomViewNavigation values to the ToolbarUsage enum, and extends the frontstage system to support bottom toolbar positions using the same FrontstageDef / WidgetDef / reactNode architecture as the existing top toolbars.

Closes #1541

What's Changed

  • ToolbarUsage enum — Added BottomContentManipulation = 2 and BottomViewNavigation = 3
  • ToolbarComposer — Updated toExpandsTo() and toPanelAlignment() helpers to handle the new usages (bottom toolbars expand upward; bottom-right aligns to end)
  • FrontstageConfig — Added optional bottomContentManipulation and bottomViewNavigation WidgetConfig properties, mirroring contentManipulation and viewNavigation
  • FrontstageDef — Added corresponding WidgetDef properties with initialization from config
  • BottomToolWidgetComposer — New component analogous to ToolWidgetComposer but for bottom toolbars (L-shaped grid, no corner item, proximity-based opacity, UI visibility)
  • BottomContentToolWidgetComposer — Pre-configured composer for BottomContentManipulation (analogous to ContentToolWidgetComposer)
  • BottomViewToolWidgetComposer — Pre-configured composer for BottomViewNavigation (analogous to ViewToolWidgetComposer)
  • WidgetPanelsToolbars — Renders bottom toolbars via frontstageDef?.bottomContentManipulation?.reactNode and frontstageDef?.bottomViewNavigation?.reactNode — same pattern as top toolbars
  • Toolbars.scss — Extended the CSS grid with a bottom row; bottom tool area uses an L-shaped grid ("vtools ." / "vtools htools") mirroring the top ToolsArea pattern but anchored to the bottom
  • Unit tests — Tests for empty state, bottom-left rendering, and bottom-right rendering
  • API extractappui-react.api.md updated with new exports
  • Changeset — Minor bump for @itwin/appui-react

Architecture

The bottom toolbars follow the exact same architecture as the top toolbars:

Top-Left:    FrontstageConfig.contentManipulation
             → FrontstageDef.contentManipulation → WidgetDef.reactNode
             → ContentToolWidgetComposer → ToolWidgetComposer → ToolsArea (CSS grid)

Bottom-Left: FrontstageConfig.bottomContentManipulation
             → FrontstageDef.bottomContentManipulation → WidgetDef.reactNode
             → BottomContentToolWidgetComposer → BottomToolWidgetComposer (CSS grid, no corner item)

Consumer Experience

// Option 1: Use the default composers in your frontstage config
const frontstage: Frontstage = {
  id: "my-frontstage",
  contentGroup: myContentGroup,
  version: 1,
  contentManipulation: { content: <ContentToolWidgetComposer cornerButton={<BackstageAppButton />} /> },
  bottomContentManipulation: { content: <BottomContentToolWidgetComposer /> },
  bottomViewNavigation: { content: <BottomViewToolWidgetComposer /> },
};

// Option 2: UiItemsProvider returns items with the new usage — they appear automatically
const provider: UiItemsProvider = {
  id: "MyProvider",
  getToolbarItems: () => [
    ToolbarItemUtilities.createActionItem({
      id: "map-layers-toggle",
      itemPriority: 10,
      icon: <SvgLayers />,
      label: "Map Layers",
      execute: () => { /* toggle */ },
      layouts: {
        standard: {
          usage: ToolbarUsage.BottomViewNavigation,
          orientation: ToolbarOrientation.Horizontal,
        },
      },
    }),
  ],
};

@jason-crow jason-crow requested a review from a team as a code owner April 28, 2026 11:01
@jason-crow jason-crow force-pushed the features/bottom-toolbar-positions branch from 35d2fe6 to 812ca11 Compare April 28, 2026 11:58
@jason-crow jason-crow force-pushed the features/bottom-toolbar-positions branch from 812ca11 to 7e4f092 Compare April 28, 2026 12:08
… the standard layout

Add BottomContentManipulation and BottomViewNavigation values to ToolbarUsage enum.
The standard layout now auto-renders bottom toolbars when UiItemsProviders return
items with these usages, removing the need for custom overlay components.

Closes #1541
@jason-crow jason-crow force-pushed the features/bottom-toolbar-positions branch from 7e4f092 to 5283160 Compare April 28, 2026 12:15
@GerardasB GerardasB requested a review from Copilot April 28, 2026 12:17
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends AppUI’s standard frontstage toolbar system to support bottom-left and bottom-right toolbars by adding new ToolbarUsage enum values and plumbing them through FrontstageConfig/FrontstageDef, composers, layout rendering, styling, and tests.

Changes:

  • Added BottomContentManipulation / BottomViewNavigation toolbar usages and updated ToolbarComposer placement helpers accordingly.
  • Extended frontstage configuration/definition to support bottom toolbar widget defs and render them in the standard layout.
  • Introduced bottom toolbar composer components + styling and added unit tests and API/changeset updates.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
ui/appui-react/src/test/widgets/BottomToolWidgetComposer.test.tsx Adds unit coverage for the new bottom toolbar composers.
ui/appui-react/src/test/widget-panels/Toolbars.test.tsx Extends layout tests to cover bottom toolbar rendering behavior.
ui/appui-react/src/appui-react/widgets/BottomViewToolWidgetComposer.tsx Adds a preconfigured bottom-right toolbar composer.
ui/appui-react/src/appui-react/widgets/BottomToolWidgetComposer.tsx Introduces the base bottom toolbar composer with opacity/visibility behavior.
ui/appui-react/src/appui-react/widgets/BottomContentToolWidgetComposer.tsx Adds a preconfigured bottom-left toolbar composer.
ui/appui-react/src/appui-react/widget-panels/Toolbars.tsx Renders bottom toolbars from the active FrontstageDef in the standard layout.
ui/appui-react/src/appui-react/widget-panels/Toolbars.scss Updates CSS grid/layout to add and position bottom toolbar areas.
ui/appui-react/src/appui-react/toolbar/ToolbarItem.ts Extends ToolbarUsage enum with bottom toolbar values.
ui/appui-react/src/appui-react/toolbar/ToolbarComposer.tsx Updates expansion direction and alignment logic for new bottom usages.
ui/appui-react/src/appui-react/frontstage/FrontstageDef.tsx Adds WidgetDef fields/getters and config initialization for bottom toolbars.
ui/appui-react/src/appui-react/frontstage/FrontstageConfig.ts Adds optional bottom toolbar widget config properties.
ui/appui-react/src/appui-react.ts Exports the new bottom toolbar composer components.
common/api/summary/appui-react.exports.csv Updates API summary exports for the new public symbols.
common/api/appui-react.api.md Updates API extractor output to include new exports/usages.
.changeset/bottom-toolbar-positions.md Adds a minor changeset documenting the feature.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +8 to +10

import "../widget-panels/Toolbars.scss";
import * as React from "react";
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BottomToolWidgetComposer imports ../widget-panels/Toolbars.scss, which also contains styling for WidgetPanelsToolbars and the overall center-content grid. This creates an unnecessary cross-module coupling and can pull unrelated layout CSS into bundles wherever the composer is used. Consider extracting the bottom toolbar area styles into a dedicated SCSS file (e.g., BottomToolWidgetComposer.scss) and importing that instead.

Copilot uses AI. Check for mistakes.
Comment on lines +25 to +27
/** Contains tools to Create Update and Delete content - rendered in bottom left of content area. */
BottomContentManipulation = 2,
/** Manipulate view/camera - rendered in bottom right of content area. */
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new enum doc comment reads awkwardly: "Create Update and Delete" should be lowercased and punctuated (e.g., "create, update, and delete") for clarity and consistency with the surrounding comments.

Copilot uses AI. Check for mistakes.
Comment on lines +38 to 67

.uifw-bottom-toolArea {
display: grid;
grid-gap: 6px;
grid-template-areas:
"vtools ."
"vtools htools";
grid-template-columns: auto 1fr;
grid-template-rows: 1fr auto;
align-items: end;
justify-items: start;
}

.uifw-bottom-toolArea_vertical {
grid-area: vtools;
display: inline-flex;
flex-direction: column;
}

.uifw-bottom-toolArea_horizontal {
grid-area: htools;
min-width: 0;
}

.uifw-bottom-toolArea_right {
margin-left: auto;

.uifw-bottom-toolArea {
justify-items: end;
}
Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The .uifw-bottom-toolArea* styles are nested under .uifw-widgetPanels-toolbars, so BottomToolWidgetComposer will only be styled when rendered as a descendant of WidgetPanelsToolbars. Since BottomToolWidgetComposer is exported as a public component, consider moving these rules to a top-level selector (or a dedicated SCSS file imported by the composer) so it renders correctly when used outside the standard layout.

Suggested change
.uifw-bottom-toolArea {
display: grid;
grid-gap: 6px;
grid-template-areas:
"vtools ."
"vtools htools";
grid-template-columns: auto 1fr;
grid-template-rows: 1fr auto;
align-items: end;
justify-items: start;
}
.uifw-bottom-toolArea_vertical {
grid-area: vtools;
display: inline-flex;
flex-direction: column;
}
.uifw-bottom-toolArea_horizontal {
grid-area: htools;
min-width: 0;
}
.uifw-bottom-toolArea_right {
margin-left: auto;
.uifw-bottom-toolArea {
justify-items: end;
}
}
.uifw-bottom-toolArea {
display: grid;
grid-gap: 6px;
grid-template-areas:
"vtools ."
"vtools htools";
grid-template-columns: auto 1fr;
grid-template-rows: 1fr auto;
align-items: end;
justify-items: start;
}
.uifw-bottom-toolArea_vertical {
grid-area: vtools;
display: inline-flex;
flex-direction: column;
}
.uifw-bottom-toolArea_horizontal {
grid-area: htools;
min-width: 0;
}
.uifw-bottom-toolArea_right {
margin-left: auto;
.uifw-bottom-toolArea {
justify-items: end;

Copilot uses AI. Check for mistakes.
Comment on lines 13 to 15
grid-template-columns: 1fr 1fr;
grid-template-rows: auto 1fr auto;

Copy link

Copilot AI Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

grid-template-rows: auto 1fr auto combined with putting the existing .nz-tools-widget instances in grid-row: 1 risks breaking layout: .nz-tools-widget (ToolsArea/NavigationArea) is styled with height: 100% and relies on spanning the full content height. In an auto first row this can either force the first row to consume the full height (pushing the bottom toolbars row out of view) or prevent the tools widget from spanning full height. Consider keeping the toolbars overlay in a single grid row and anchoring bottom toolbars via align-self: end (or similar), or otherwise ensure the top .nz-tools-widget elements still span the full available height while leaving room for the bottom toolbars.

Copilot uses AI. Check for mistakes.
@GerardasB
Copy link
Copy Markdown
Collaborator

GerardasB commented Apr 28, 2026

As discussed internally, I'm closing #1474 in favor of this PR. Closed PR implements the capability in a non-breaking manner and allows for easy extension/usage by custom layouts at the expense of being less discoverable. We've agreed to ensure that AppUI handles the ToolbarUsage enum in a non-exhaustive way and clearly document that new values can be added to the enum in the future.

/** Manipulate view/camera - in AppUI this is in top right of content area. */
ViewNavigation = 1,
/** Contains tools to Create Update and Delete content - rendered in bottom left of content area. */
BottomContentManipulation = 2,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we avoid placement keywords, like "Bottom"?
The "usage" should naturally help with grouping related toolbar items based on their behavior.
I went with "ViewSettings" in #1474, but other suggestions are welcome.

Alternatively, we could introduce an explicit "placement" property, however that is basically re-implementing #1474

public get viewNavigation(): WidgetDef | undefined {
return this._viewNavigation;
}
public get bottomContentManipulation(): WidgetDef | undefined {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if these APIs are useful on the FrontstageDef? E.g. is there a cuse-case for existing frontstageDef.viewNavigation?

ContentManipulation = 0,
/** Manipulate view/camera - in AppUI this is in top right of content area. */
ViewNavigation = 1,
/** Contains tools to Create Update and Delete content - rendered in bottom left of content area. */
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The enum itself should clearly be marked as non-exhaustive - new values might be added in the future.

const frontstageDef = useActiveFrontstageDef();
const tools = frontstageDef?.contentManipulation?.reactNode;
const navigation = frontstageDef?.viewNavigation?.reactNode;
const bottomTools = frontstageDef?.bottomContentManipulation?.reactNode;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

frontstageDef?.initialConfig can be used, if we get rid of bottomContentManipulation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants