Portal shell
Purpose
PortalShell is the product-page frame for modules mounted by bus-portal. It
is a Bus UI composition target, not a replacement for the portal host. The host
serves the outer application, assets, security headers, module launcher, and
runtime context; PortalShell renders the page-level title, local navigation,
and module body inside the mounted feature route.
Props
The shell uses typed Go props. It should not read global browser state or infer
deployment paths from literals. GX attributes use the lower-camel form of the
exported Go prop name, so Title is written as title and HostContext is
written as hostContext in markup.
type PortalShellProps struct {
Title string
HostContext HostContext
Nav []NavItem
Body gx.Node
}
type NavItem struct {
Label string
Path string
}
| Go prop | GX attribute | Required | Behavior |
|---|---|---|---|
Title string |
title |
yes | Public page title rendered inside the module frame. Empty titles fail validation. |
HostContext HostContext |
hostContext |
yes | Portal host context used for same-origin links and shared asset references. Missing module ID or base path fails validation. |
Nav []NavItem |
nav |
no | Ordered module-local navigation entries with public labels and host-resolved paths. Invalid entries fail validation. |
Body gx.Node |
child markup | yes | Child node or component body supplied by the product page. Nil bodies fail validation. |
Navigation paths are module-relative before rendering and become host-resolved
URLs through HostContext.ModuleURL. A valid path starts with /, does not
start with //, and does not contain backslashes, .., tabs, or newlines.
External URLs, path traversal, empty labels, and deployment-specific token
prefixes make ValidatePortalShellProps return an error instead of rendering a
partially active nav entry.
GX Example
GX authors use PortalShell as a normal typed component. Child markup fills the
body slot, and callback props remain Go function values.
package reportsui
import (
"github.com/busdk/bus-gx/pkg/gx"
. "github.com/busdk/bus-ui/pkg/uiportal"
)
type ReportsPageProps struct {
Host HostContext
Rows []ReportRow
Save func(ReportDraft)
}
func ReportsPage(props ReportsPageProps) gx.Node {
return (
<PortalShell
title="Reports"
hostContext={props.Host}
nav={[]NavItem}
>
<ReportForm rows={props.Rows} onSubmit={props.Save}></ReportForm>
</PortalShell>
)
}
The same page can be exposed through portal.UIFramework by wrapping the GX
function in a deterministic Go renderer. The renderer receives host context
from portal.UIRenderContext, so tests and live module dispatch use the same
base paths.
func renderReportsPage(ctx portal.UIRenderContext) gx.Node {
return ReportsPage(ReportsPageProps{
Host: HostContextFromPortal(ctx.HostContext),
Rows: reportRows(ctx),
Save: saveReportDraft,
})
}
Boundary
PortalShell may compose shared lower-level Bus UI pieces such as panels,
buttons, nav items, forms, and status tags. It should not perform provider API
calls, session validation, CSRF checks, logging transport, CSP construction, or
asset delivery. Those responsibilities belong to provider APIs, product module
handlers, or the portal host.
Browser actions originating inside the shell should be declared by the product
page as framework hooks. The module then projects observed Go/WASM events with
ProjectFrameworkEvent and only forwards the declared public fields.