almost working some of the time

This commit is contained in:
M@
2025-12-04 15:03:49 -05:00
parent 03302046bf
commit 404316140f
4 changed files with 139 additions and 30 deletions
+6
View File
@@ -1991,6 +1991,12 @@ default_datasource_uid =
;feature1 = true
;feature2 = false
# default for the navigation sidebar docking behavior
# true = docked by default
# false = undocked by default
# When unspecified the current Grafana default remains (true).
;default_sidebar_docked = false
[date_formats]
# For information on what formatting patterns that are supported https://momentjs.com/docs/#/displaying/
+21
View File
@@ -220,6 +220,27 @@ func (hs *HTTPServer) setIndexViewData(c *contextmodel.ReqContext) (*dtos.IndexV
hs.HooksService.RunIndexDataHooks(&data, c)
if data.Settings.FeatureToggles == nil {
data.Settings.FeatureToggles = map[string]bool{}
}
// Use value populated by your loader (pkg/setting/feature_toggles.go). If not wired,
// you can temporarily hardcode here for testing or read env.
data.Settings.FeatureToggles["default_sidebar_docked"] = setting.FeatureToggleConfig.DefaultSidebarDocked
// Example temporary hardcode for quick verification:
// data.Settings.FeatureToggles["default_sidebar_docked"] = false
// Instrumentation: log the runtime value so you can see where it's coming from
if c != nil {
c.Logger.Info("feature toggle value", "default_sidebar_docked", setting.FeatureToggleConfig.DefaultSidebarDocked)
} else if hs != nil && hs.log != nil {
hs.log.Info("feature toggle value", "default_sidebar_docked", setting.FeatureToggleConfig.DefaultSidebarDocked)
} else {
// quick-and-dirty fallback (will print to stdout)
fmt.Printf("feature toggle default_sidebar_docked=%v\n", setting.FeatureToggleConfig.DefaultSidebarDocked)
}
data.NavTree.Sort()
return &data, nil
+4
View File
@@ -1019,6 +1019,10 @@ func (cfg *Cfg) loadConfiguration(args CommandLineArgs) (*ini.File, error) {
// apply command line overrides
cfg.applyCommandLineProperties(commandLineProps, parsedFile)
// --- ADD THIS LINE TO LOAD FEATURE TOGGLES FROM THE FINAL PARSED INI ---
loadFeatureToggles(parsedFile)
//
// evaluate config values containing environment variables
err = expandConfig(parsedFile)
if err != nil {
+108 -30
View File
@@ -1,43 +1,121 @@
// 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 */
// Early script to initialize grafana.navigation.docked from server bootdata.
// Behavior:
// - If the user already has a preference (no companion ".auto" key) we never override it.
// - If we set the value automatically, we set a companion key to mark it.
// - If later the authoritative bootdata promise resolves, we will override only if
// the current value was previously auto-set by us.
(function () {
try {
let key = 'grafana.navigation.docked';
let autoKey = key + '.auto';
// If already set (user choice exists), do nothing.
if (localStorage.getItem(key) !== null) {
return;
function isAutoSet() {
try {
return localStorage.getItem(autoKey) === '1';
} catch (e) {
return false;
}
}
// 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;
function setAuto(val) {
try {
localStorage.setItem(key, val ? 'true' : 'false');
localStorage.setItem(autoKey, '1');
} catch (e) {
// ignore
}
}
// 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');
function setIfAbsent(val) {
try {
if (localStorage.getItem(key) === null) {
setAuto(val);
}
} catch (e) {
// ignore
}
}
function setIfAutoOrAbsent(val) {
try {
let cur = localStorage.getItem(key);
if (cur === null || isAutoSet()) {
setAuto(val);
}
} catch (e) {
// ignore
}
}
function applyDefault(preferServer) {
try {
// If a real user preference exists (not marked as auto) and preferServer is true,
// we should NOT override it. setIfAutoOrAbsent will only override if auto or absent.
// preferServer indicates this call is from authoritative bootdata (true) or inline (false).
let serverDefault;
if (
typeof window !== 'undefined' &&
window.grafanaBootData &&
window.grafanaBootData.settings &&
window.grafanaBootData.settings.featureToggles &&
typeof window.grafanaBootData.settings.featureToggles.default_sidebar_docked !== 'undefined'
) {
serverDefault = window.grafanaBootData.settings.featureToggles.default_sidebar_docked;
}
let globalDefault = typeof window !== 'undefined' ? window.__defaultSidebarDocked : undefined;
let val =
typeof serverDefault !== 'undefined'
? serverDefault
: typeof globalDefault !== 'undefined'
? globalDefault
: undefined;
if (typeof val === 'undefined') {
return;
}
// If this is called for authoritative data (preferServer === true), allow override of previously
// auto-set values; if it's non-authoritative (inline), only set if absent.
if (preferServer) {
setIfAutoOrAbsent(val);
} else {
setIfAbsent(val);
}
} catch (e) {
// ignore
}
}
// Fast-path: try immediate apply from inline bootdata (non-authoritative)
applyDefault(false);
// If async bootdata is fetched, wait for the promise and apply authoritative value
try {
if (
typeof window !== 'undefined' &&
window.__grafana_boot_data_promise &&
typeof window.__grafana_boot_data_promise.then === 'function'
) {
window.__grafana_boot_data_promise
.then(function () {
applyDefault(true);
})
.catch(function () {
// ignore
});
}
} catch (e) {
// ignore
}
// Fallback: try again shortly after load as a final chance (authoritative)
setTimeout(function () {
applyDefault(true);
}, 1500);
} catch (e) {
// Keep page stable if something goes wrong
// Do not let this break the page
}
})();