enhancement
major
minor
major
minor
major
#29523
<sidebar> exposes two of the five sidebar item kinds ReactSidebarControl renders, and none of the badge
Affects 8.0.0-alpha8. Found while rebuilding an application's navigation rail on the React view UI.
tl-layout-react renders five kinds of sidebar item and a badge on each. tl-layout-view lets a view author declare two of the five and none of the badge.
| tl-layout-react | rendered by the client? | @TagName in tl-layout-view |
| NavigationItem | yes | nav-item |
| SeparatorItem | yes | separator |
| GroupItem — label, icon, children, initiallyExpanded | yes | none |
| HeaderItem — a group caption | yes | none |
| CommandItem — an item that runs a command | yes | none |
The client bundle carries all five — c.type === "command" ? … : c.type === "group" && … in script/tl-react-controls.js — and ReactSidebarControl already ships the group machinery: TOGGLE_GROUP_COMMAND, _groupStates, persisted per group id.
On NavigationItem itself, two further properties have no configuration:
- badge / setBadge — the client renders tlSidebar__badge and tlSidebar__badge--collapsed. NavItemConfig has only label, icon, route, children.
- withHidden(boolean) — an item can be hidden; nothing declares it.
withRoute is exposed as route and works.
The rail has no header or footer
ReactSidebarControl's constructor takes four content contributions for the rail itself — headerContent, headerCollapsedContent, footerContent, footerCollapsedContent. SidebarElement.createControl passes null for all four:
149: aconst_null 150: aconst_null 151: aconst_null 152: aconst_null 153: invokespecial // ReactSidebarControl."<init>":(…;ReactControl;ReactControl;ReactControl;ReactControl;)V
SidebarElement.Config has items, active-item, collapsed and drawer-open-slot-name — nothing for the four. So a rail cannot carry a header above its items or a footer below them, although the component it renders through supports both, collapsed variants included.
Note that <nav-item><children> is not the rail: NavigationItem takes the children as its _contentFactory, i.e. the item's content pane in the work area. There is no route from view XML into the rail's own chrome.
Why it matters
The rail in question has, top to bottom: a caption, a scope switcher, a group with a caption and one entry per project each carrying its document count, a second captioned group, and a footer with a button and a note. Of that, tl-layout-view can express the entries' labels, icons and routes. The captions, the counts, the switcher and the footer are all supported by ReactSidebarControl and unreachable from a view.
Not a gap, and the way out — worth documenting
SidebarElement.SidebarItemConfig is a PolymorphicConfiguration<SidebarItemElement>, and SidebarItemElement is a one-method interface:
SidebarItem createSidebarItem(ViewContext context);
So an application can register its own item element and return any SidebarItem subclass — including a GroupItem whose children it builds from a model query at createSidebarItem time, with a badge on each. That is how the application above gets its dynamic project group: one configuration element, server side only, no React component and no client build.
This is a good seam. It is documented nowhere, and it is the only reason the gaps above are survivable. What it does not solve is the header and footer, which are not items.
Suggested fix
- Register @TagName`s for `GroupItem, HeaderItem and CommandItem, mirroring nav-item.
- Add badge and hidden to NavItemConfig.
- Add header, header-collapsed, footer and footer-collapsed to SidebarElement.Config as PolymorphicConfiguration<UIElement> (or slot names, as with drawer-open-slot-name) and pass them through instead of null.
- Document SidebarItemElement as the extension point for computed items.
Items 1 and 2 are configuration plumbing over code that already exists; item 3 is four constructor arguments.