diff --git a/admin/server/deployment.go b/admin/server/deployment.go index 2a7ca06fc66a..06dfb11dec69 100644 --- a/admin/server/deployment.go +++ b/admin/server/deployment.go @@ -702,6 +702,10 @@ func (s *Server) GetIFrame(ctx context.Context, req *adminv1.GetIFrameRequest) ( iframeQuery["navigation"] = "true" } + if req.HideNavigationBar { + iframeQuery["hide_navigation_bar"] = "true" + } + if req.State != "" { iframeQuery["state"] = req.State } diff --git a/cli/cmd/sudo/embed/embed_test.go b/cli/cmd/sudo/embed/embed_test.go index 15ab806bb082..9c277d34cce9 100644 --- a/cli/cmd/sudo/embed/embed_test.go +++ b/cli/cmd/sudo/embed/embed_test.go @@ -47,6 +47,12 @@ func TestEmbedOpen(t *testing.T) { require.Equal(t, 0, res.ExitCode, res.Output) require.Contains(t, res.Output, "Open browser at:") + // The navigation bar can be hidden without disabling navigation. + res = u1.Run(t, "sudo", "embed", "open", "embed-test", "embed-project", "--no-open", "--navigation", "--hide-navigation-bar") + require.Equal(t, 0, res.ExitCode, res.Output) + require.Contains(t, res.Output, "hide_navigation_bar=true") + require.Contains(t, res.Output, "navigation=true") + // Normal user cannot get an embed URL for the project. res = u3.Run(t, "sudo", "embed", "open", "embed-test", "embed-project", "--no-open", "--navigation") require.NotEqual(t, 0, res.ExitCode) diff --git a/cli/cmd/sudo/embed/open.go b/cli/cmd/sudo/embed/open.go index 4e5e310ebab2..f8b81d38803a 100644 --- a/cli/cmd/sudo/embed/open.go +++ b/cli/cmd/sudo/embed/open.go @@ -23,6 +23,7 @@ func OpenCmd(ch *cmdutil.Helper) *cobra.Command { var theme string var themeMode string var navigation bool + var hideNavigationBar bool var query map[string]string var noOpen bool @@ -45,6 +46,7 @@ func OpenCmd(ch *cmdutil.Helper) *cobra.Command { Theme: theme, ThemeMode: themeMode, Navigation: navigation, + HideNavigationBar: hideNavigationBar, Query: query, SuperuserForceAccess: true, } @@ -107,6 +109,7 @@ func OpenCmd(ch *cmdutil.Helper) *cobra.Command { openCmd.Flags().StringVar(&theme, "theme", "", "Theme for the embedded resource") openCmd.Flags().StringVar(&themeMode, "theme-mode", "", "Theme mode") openCmd.Flags().BoolVar(&navigation, "navigation", false, "Enable navigation between resources") + openCmd.Flags().BoolVar(&hideNavigationBar, "hide-navigation-bar", false, "Hide the navigation bar while keeping navigation enabled") openCmd.Flags().StringToStringVar(&query, "query", nil, "Additional query parameters (key=value)") openCmd.Flags().BoolVar(&noOpen, "no-open", false, "Print the URL without opening the browser") diff --git a/docs/docs/developers/embed/iframe.md b/docs/docs/developers/embed/iframe.md index 7c01c781609d..d4ce896bb20f 100644 --- a/docs/docs/developers/embed/iframe.md +++ b/docs/docs/developers/embed/iframe.md @@ -296,6 +296,7 @@ The API accepts the following parameters: | resource | The name of the dashboard to embed | No (if not specified, `navigation` should be set to `true`) | | type | The type of the dashboard identified by `resource` (options: `explore`, `canvas`) | No (defaults to `explore`) | | navigation | Boolean whether to enable navigation and allow users to navigate to other dashboards (`false` will hard embed and allow access to a single dashboard; `true` allows navigation) | No (defaults to `false`) | +| hide_navigation_bar | Boolean whether to hide the navigation bar (the home link and dashboard breadcrumbs) while keeping navigation enabled. Only meaningful when `navigation` is `true`; the bar is always hidden when `navigation` is `false`. In-dashboard navigation, such as the drill-through from a Canvas component to an Explore dashboard, remains available. | No (defaults to `false`) | | theme | If [themes](/developers/build/dashboards/customization#changing-themes--colors) are being used, the specific theme to pass to the embedded dashboard | No (set to the name of the theme) | | theme_mode | The theme mode to use for the embedded dashboard. Valid values: `light`, `dark`, `system`. When set to `system`, the dashboard will follow the user's system preference. | No (defaults to `light`) | | user_email | Email of the end user accessing the embed. Used to populate attributes for use in security policies. | No (only one of `user_email` or `attributes` should be passed in) | @@ -348,6 +349,18 @@ If you wish to still embed a dashboard _but allow navigation between dashboards_ } ``` +If you wish to allow navigation _but hide the navigation bar_ — for example to embed a Canvas dashboard whose components can drill through to an Explore dashboard, without showing Rill's own breadcrumbs — then add `hide_navigation_bar`: +```json +{ + "type": "canvas", + "resource": "dashboardName", + "navigation": true, + "hide_navigation_bar": true +} +``` + +Note that in this mode there is no in-iframe affordance for returning from a dashboard the user drilled into. Your application should listen for the [`navigation` notification](/developers/embed/postmessage) — still emitted when the navigation bar is hidden — and provide its own way back. + Finally, _if you wish to embed the project list view of dashboards instead (what you see when you first open a project in Rill Cloud)_, then you can simply omit the `resource` and appropriately set `navigation` in your payload: ```json { diff --git a/proto/gen/rill/admin/v1/admin.swagger.yaml b/proto/gen/rill/admin/v1/admin.swagger.yaml index c89fe22d8406..08ae89e880c3 100644 --- a/proto/gen/rill/admin/v1/admin.swagger.yaml +++ b/proto/gen/rill/admin/v1/admin.swagger.yaml @@ -1760,6 +1760,12 @@ paths: navigation: type: boolean description: Navigation denotes whether navigation between different resources should be enabled in the embed. + hideNavigationBar: + type: boolean + description: |- + HideNavigationBar hides the embed's top navigation bar (the home link and dashboard breadcrumbs) without disabling navigation itself. + It is only meaningful when `navigation` is true; the bar is always hidden when `navigation` is false. + In-dashboard navigation, such as the canvas drill-through to an explore dashboard, remains available. state: type: string description: Blob containing UI state for rendering the initial embed. Not currently supported. diff --git a/proto/gen/rill/admin/v1/api.pb.go b/proto/gen/rill/admin/v1/api.pb.go index 08e37dd78a01..61a8020918ab 100644 --- a/proto/gen/rill/admin/v1/api.pb.go +++ b/proto/gen/rill/admin/v1/api.pb.go @@ -3537,6 +3537,10 @@ type GetIFrameRequest struct { ThemeMode string `protobuf:"bytes,15,opt,name=theme_mode,json=themeMode,proto3" json:"theme_mode,omitempty"` // Navigation denotes whether navigation between different resources should be enabled in the embed. Navigation bool `protobuf:"varint,13,opt,name=navigation,proto3" json:"navigation,omitempty"` + // HideNavigationBar hides the embed's top navigation bar (the home link and dashboard breadcrumbs) without disabling navigation itself. + // It is only meaningful when `navigation` is true; the bar is always hidden when `navigation` is false. + // In-dashboard navigation, such as the canvas drill-through to an explore dashboard, remains available. + HideNavigationBar bool `protobuf:"varint,18,opt,name=hide_navigation_bar,json=hideNavigationBar,proto3" json:"hide_navigation_bar,omitempty"` // Blob containing UI state for rendering the initial embed. Not currently supported. State string `protobuf:"bytes,7,opt,name=state,proto3" json:"state,omitempty"` // DEPRECATED: Additional parameters to set outright in the generated URL query. @@ -3682,6 +3686,13 @@ func (x *GetIFrameRequest) GetNavigation() bool { return false } +func (x *GetIFrameRequest) GetHideNavigationBar() bool { + if x != nil { + return x.HideNavigationBar + } + return false +} + func (x *GetIFrameRequest) GetState() string { if x != nil { return x.State @@ -25654,7 +25665,7 @@ var file_rill_admin_v1_api_proto_rawDesc = []byte{ 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x74, 0x6c, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x74, 0x6c, 0x53, 0x65, 0x63, - 0x6f, 0x6e, 0x64, 0x73, 0x22, 0xa8, 0x05, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x49, 0x46, 0x72, 0x61, + 0x6f, 0x6e, 0x64, 0x73, 0x22, 0xd8, 0x05, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x49, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6f, 0x72, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6f, 0x72, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, @@ -25684,6 +25695,9 @@ var file_rill_admin_v1_api_proto_rawDesc = []byte{ 0x65, 0x6d, 0xd0, 0x01, 0x01, 0x52, 0x09, 0x74, 0x68, 0x65, 0x6d, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, 0x61, 0x76, 0x69, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x6e, 0x61, 0x76, 0x69, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x2e, 0x0a, 0x13, 0x68, 0x69, 0x64, 0x65, 0x5f, 0x6e, 0x61, 0x76, 0x69, 0x67, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x61, 0x72, 0x18, 0x12, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x68, + 0x69, 0x64, 0x65, 0x4e, 0x61, 0x76, 0x69, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x61, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x40, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x72, 0x69, 0x6c, 0x6c, 0x2e, 0x61, 0x64, 0x6d, diff --git a/proto/gen/rill/admin/v1/api.pb.validate.go b/proto/gen/rill/admin/v1/api.pb.validate.go index 3b8d43856cbd..40393b305d05 100644 --- a/proto/gen/rill/admin/v1/api.pb.validate.go +++ b/proto/gen/rill/admin/v1/api.pb.validate.go @@ -6628,6 +6628,8 @@ func (m *GetIFrameRequest) validate(all bool) error { // no validation rules for Navigation + // no validation rules for HideNavigationBar + // no validation rules for State // no validation rules for Query diff --git a/proto/gen/rill/admin/v1/openapi.yaml b/proto/gen/rill/admin/v1/openapi.yaml index 340183efc661..1c20b9a53273 100644 --- a/proto/gen/rill/admin/v1/openapi.yaml +++ b/proto/gen/rill/admin/v1/openapi.yaml @@ -4589,6 +4589,12 @@ paths: Optional ID for the external end user of the iframe. If set, the access token enables per-user state, such as AI chat history. Cannot be combined with `user_id`. If `user_email` matches a Rill Cloud user, their attributes are used, but this ID takes precedence for per-user state. type: string + hideNavigationBar: + description: |- + HideNavigationBar hides the embed's top navigation bar (the home link and dashboard breadcrumbs) without disabling navigation itself. + It is only meaningful when `navigation` is true; the bar is always hidden when `navigation` is false. + In-dashboard navigation, such as the canvas drill-through to an explore dashboard, remains available. + type: boolean kind: description: 'Deprecated: Alias for `type`.' type: string diff --git a/proto/rill/admin/v1/api.proto b/proto/rill/admin/v1/api.proto index e6c70d1ae450..e1b6522d3edd 100644 --- a/proto/rill/admin/v1/api.proto +++ b/proto/rill/admin/v1/api.proto @@ -1767,6 +1767,10 @@ message GetIFrameRequest { string theme_mode = 15 [(validate.rules).string = {ignore_empty: true, in: ["light", "dark", "system"]}]; // Navigation denotes whether navigation between different resources should be enabled in the embed. bool navigation = 13; + // HideNavigationBar hides the embed's top navigation bar (the home link and dashboard breadcrumbs) without disabling navigation itself. + // It is only meaningful when `navigation` is true; the bar is always hidden when `navigation` is false. + // In-dashboard navigation, such as the canvas drill-through to an explore dashboard, remains available. + bool hide_navigation_bar = 18; // Blob containing UI state for rendering the initial embed. Not currently supported. string state = 7; // DEPRECATED: Additional parameters to set outright in the generated URL query. diff --git a/web-admin/src/client/gen/index.schemas.ts b/web-admin/src/client/gen/index.schemas.ts index e66c609c5a62..9442ce6d9543 100644 --- a/web-admin/src/client/gen/index.schemas.ts +++ b/web-admin/src/client/gen/index.schemas.ts @@ -2136,6 +2136,10 @@ Cannot be combined with `user_id`. If `user_email` matches a Rill Cloud user, th themeMode?: string; /** Navigation denotes whether navigation between different resources should be enabled in the embed. */ navigation?: boolean; + /** HideNavigationBar hides the embed's top navigation bar (the home link and dashboard breadcrumbs) without disabling navigation itself. +It is only meaningful when `navigation` is true; the bar is always hidden when `navigation` is false. +In-dashboard navigation, such as the canvas drill-through to an explore dashboard, remains available. */ + hideNavigationBar?: boolean; /** Blob containing UI state for rendering the initial embed. Not currently supported. */ state?: string; /** DEPRECATED: Additional parameters to set outright in the generated URL query. */ diff --git a/web-admin/src/features/embeds/EmbedHeader.svelte b/web-admin/src/features/embeds/EmbedHeader.svelte index d86add076e16..b15601ca1a16 100644 --- a/web-admin/src/features/embeds/EmbedHeader.svelte +++ b/web-admin/src/features/embeds/EmbedHeader.svelte @@ -18,7 +18,7 @@ import { m } from "@rilldata/web-common/lib/i18n/gen/messages"; export let activeResource: V1ResourceName; - export let navigationEnabled: boolean = true; + export let navigationBarEnabled: boolean = true; const runtimeClient = useRuntimeClient(); const { twoTieredNavigation, dashboardChat } = featureFlags; @@ -26,7 +26,7 @@ $: onProjectPage = !activeResource; $: showDashboardChat = $dashboardChat && !onProjectPage; - $: shouldRender = navigationEnabled || showDashboardChat; + $: shouldRender = navigationBarEnabled || showDashboardChat; // Dashboard breadcrumb $: dashboardsQuery = useValidDashboards(runtimeClient); @@ -55,7 +55,7 @@ {#if $isErrorStoreEmpty && shouldRender}