diff --git a/pkg/api/frontend_settings.go b/pkg/api/frontend_settings.go new file mode 100644 index 00000000000..d23e6fbbfb8 --- /dev/null +++ b/pkg/api/frontend_settings.go @@ -0,0 +1,27 @@ +package api + +import ( + "encoding/json" + "net/http" + + "github.com/grafana/grafana/pkg/setting" +) + +// Note: This is a minimal example showing how to add feature toggles to the boot payload. +// Integrate with Grafana's actual frontend boot data code path (where the server renders JSON into the page). +func frontendSettingsHandler(w http.ResponseWriter, r *http.Request) { + // Example existing boot data: + boot := map[string]interface{}{ + "settings": map[string]interface{}{}, + } + + // Add our feature toggle into the front-end settings payload. + settings := boot["settings"].(map[string]interface{}) + + settings["featureToggles"] = map[string]interface{}{ + "default_sidebar_docked": setting.FeatureToggleConfig.DefaultSidebarDocked, + } + + w.Header().Set("Content-Type", "application/json") + _ = json.NewEncoder(w).Encode(boot) +} diff --git a/pkg/setting/feature_toggles.go b/pkg/setting/feature_toggles.go new file mode 100644 index 00000000000..1711f9681fc --- /dev/null +++ b/pkg/setting/feature_toggles.go @@ -0,0 +1,21 @@ +package setting + +import ( + "gopkg.in/ini.v1" +) + +// FeatureToggles holds lightweight feature flags that can be set via grafana.ini +type FeatureToggles struct { + // DefaultSidebarDocked determines the default state of the left navigation when no per-browser + // preference exists in localStorage (grafana.navigation.docked). + DefaultSidebarDocked bool `json:"default_sidebar_docked"` +} + +var FeatureToggleConfig = &FeatureToggles{DefaultSidebarDocked: true} + +// loadFeatureToggles reads [feature_toggles] section from the provided INI file. +// This is a minimal helper; integrate it into your existing settings loader. +func loadFeatureToggles(cfg *ini.File) { + sec := cfg.Section("feature_toggles") + FeatureToggleConfig.DefaultSidebarDocked = sec.Key("default_sidebar_docked").MustBool(true) +} diff --git a/public/scripts/set-default-sidebar.js b/public/scripts/set-default-sidebar.js new file mode 100644 index 00000000000..877c93393b7 --- /dev/null +++ b/public/scripts/set-default-sidebar.js @@ -0,0 +1,43 @@ +// This script runs very early (before frontend bundles) so we cannot import @grafana/data here. +// Disable the lint rule that forbids direct localStorage usage for this file. +/* eslint-disable no-restricted-syntax */ + +(function () { + try { + let key = 'grafana.navigation.docked'; + + // If already set (user choice exists), do nothing. + if (localStorage.getItem(key) !== null) { + return; + } + + // Try the server-provided boot data (common Grafana pattern) + let serverDefault; + if ( + typeof window !== 'undefined' && + window.grafanaBootData && + window.grafanaBootData.settings && + window.grafanaBootData.settings.featureToggles + ) { + serverDefault = window.grafanaBootData.settings.featureToggles.default_sidebar_docked; + } + + // Fallback: a page-global var you can set for quick testing in index.html + let globalDefault = typeof window !== 'undefined' ? window.__defaultSidebarDocked : undefined; + + // If neither is provided, leave the existing Grafana default (true/docked). + let val = + typeof serverDefault !== 'undefined' + ? serverDefault + : typeof globalDefault !== 'undefined' + ? globalDefault + : undefined; + + if (typeof val !== 'undefined') { + // localStorage stores strings; Grafana historically uses 'true'/'false' for this key. + localStorage.setItem(key, val ? 'true' : 'false'); + } + } catch (e) { + // Keep page stable if something goes wrong + } +})(); diff --git a/public/views/index.html b/public/views/index.html index 2ee208bbb78..838d1a1fec3 100644 --- a/public/views/index.html +++ b/public/views/index.html @@ -267,6 +267,13 @@
+ + + +