mirror of https://github.com/grafana/grafana.git
				
				
				
			NavLinks: Make ordering in navigation configurable (#20382)
The ordering of links in the navigation bar is currently based the order of the slice containing the navigation tree. Since Grafana supports adding more links to the navigation bar with `RunIndexDataHooks` which runs at the very end of the function this means that any link registered through a hook will be placed last in the slice and be displayed last in the menu. With this PR the ordering can be specified with a weight which allows for placing links created by extensions in a more intuitive place where applicable. Stable sorting is used to ensure that the current FIFO ordering is preserved when either no weight is set or two items shares the same weight.
This commit is contained in:
		
							parent
							
								
									d602da20f6
								
							
						
					
					
						commit
						d4e013fd44
					
				|  | @ -22,6 +22,23 @@ type PluginCss struct { | ||||||
| 	Dark  string `json:"dark"` | 	Dark  string `json:"dark"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | const ( | ||||||
|  | 	// These weights may be used by an extension to reliably place
 | ||||||
|  | 	// itself in relation to a particular item in the menu. The weights
 | ||||||
|  | 	// are negative to ensure that the default items are placed above
 | ||||||
|  | 	// any items with default weight.
 | ||||||
|  | 
 | ||||||
|  | 	WeightCreate = (iota - 20) * 100 | ||||||
|  | 	WeightDashboard | ||||||
|  | 	WeightExplore | ||||||
|  | 	WeightProfile | ||||||
|  | 	WeightAlerting | ||||||
|  | 	WeightPlugin | ||||||
|  | 	WeightConfig | ||||||
|  | 	WeightAdmin | ||||||
|  | 	WeightHelp | ||||||
|  | ) | ||||||
|  | 
 | ||||||
| type NavLink struct { | type NavLink struct { | ||||||
| 	Id           string     `json:"id,omitempty"` | 	Id           string     `json:"id,omitempty"` | ||||||
| 	Text         string     `json:"text,omitempty"` | 	Text         string     `json:"text,omitempty"` | ||||||
|  | @ -31,6 +48,7 @@ type NavLink struct { | ||||||
| 	Img          string     `json:"img,omitempty"` | 	Img          string     `json:"img,omitempty"` | ||||||
| 	Url          string     `json:"url,omitempty"` | 	Url          string     `json:"url,omitempty"` | ||||||
| 	Target       string     `json:"target,omitempty"` | 	Target       string     `json:"target,omitempty"` | ||||||
|  | 	SortWeight   int64      `json:"sortWeight,omitempty"` | ||||||
| 	Divider      bool       `json:"divider,omitempty"` | 	Divider      bool       `json:"divider,omitempty"` | ||||||
| 	HideFromMenu bool       `json:"hideFromMenu,omitempty"` | 	HideFromMenu bool       `json:"hideFromMenu,omitempty"` | ||||||
| 	HideFromTabs bool       `json:"hideFromTabs,omitempty"` | 	HideFromTabs bool       `json:"hideFromTabs,omitempty"` | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ package api | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"sort" | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"github.com/grafana/grafana/pkg/api/dtos" | 	"github.com/grafana/grafana/pkg/api/dtos" | ||||||
|  | @ -120,6 +121,7 @@ func (hs *HTTPServer) setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, er | ||||||
| 			Icon:       "fa fa-fw fa-plus", | 			Icon:       "fa fa-fw fa-plus", | ||||||
| 			Url:        setting.AppSubUrl + "/dashboard/new", | 			Url:        setting.AppSubUrl + "/dashboard/new", | ||||||
| 			Children:   children, | 			Children:   children, | ||||||
|  | 			SortWeight: dtos.WeightCreate, | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -137,6 +139,7 @@ func (hs *HTTPServer) setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, er | ||||||
| 		SubTitle:   "Manage dashboards & folders", | 		SubTitle:   "Manage dashboards & folders", | ||||||
| 		Icon:       "gicon gicon-dashboard", | 		Icon:       "gicon gicon-dashboard", | ||||||
| 		Url:        setting.AppSubUrl + "/", | 		Url:        setting.AppSubUrl + "/", | ||||||
|  | 		SortWeight: dtos.WeightDashboard, | ||||||
| 		Children:   dashboardChildNavs, | 		Children:   dashboardChildNavs, | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
|  | @ -146,6 +149,7 @@ func (hs *HTTPServer) setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, er | ||||||
| 			Id:         "explore", | 			Id:         "explore", | ||||||
| 			SubTitle:   "Explore your data", | 			SubTitle:   "Explore your data", | ||||||
| 			Icon:       "gicon gicon-explore", | 			Icon:       "gicon gicon-explore", | ||||||
|  | 			SortWeight: dtos.WeightExplore, | ||||||
| 			Url:        setting.AppSubUrl + "/explore", | 			Url:        setting.AppSubUrl + "/explore", | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
|  | @ -163,6 +167,7 @@ func (hs *HTTPServer) setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, er | ||||||
| 			Img:          data.User.GravatarUrl, | 			Img:          data.User.GravatarUrl, | ||||||
| 			Url:          setting.AppSubUrl + "/profile", | 			Url:          setting.AppSubUrl + "/profile", | ||||||
| 			HideFromMenu: true, | 			HideFromMenu: true, | ||||||
|  | 			SortWeight:   dtos.WeightProfile, | ||||||
| 			Children: []*dtos.NavLink{ | 			Children: []*dtos.NavLink{ | ||||||
| 				{Text: "Preferences", Id: "profile-settings", Url: setting.AppSubUrl + "/profile", Icon: "gicon gicon-preferences"}, | 				{Text: "Preferences", Id: "profile-settings", Url: setting.AppSubUrl + "/profile", Icon: "gicon gicon-preferences"}, | ||||||
| 				{Text: "Change Password", Id: "change-password", Url: setting.AppSubUrl + "/profile/password", Icon: "fa fa-fw fa-lock", HideFromMenu: true}, | 				{Text: "Change Password", Id: "change-password", Url: setting.AppSubUrl + "/profile/password", Icon: "fa fa-fw fa-lock", HideFromMenu: true}, | ||||||
|  | @ -192,6 +197,7 @@ func (hs *HTTPServer) setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, er | ||||||
| 			Icon:       "gicon gicon-alert", | 			Icon:       "gicon gicon-alert", | ||||||
| 			Url:        setting.AppSubUrl + "/alerting/list", | 			Url:        setting.AppSubUrl + "/alerting/list", | ||||||
| 			Children:   alertChildNavs, | 			Children:   alertChildNavs, | ||||||
|  | 			SortWeight: dtos.WeightAlerting, | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -207,6 +213,7 @@ func (hs *HTTPServer) setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, er | ||||||
| 				Id:         "plugin-page-" + plugin.Id, | 				Id:         "plugin-page-" + plugin.Id, | ||||||
| 				Url:        plugin.DefaultNavUrl, | 				Url:        plugin.DefaultNavUrl, | ||||||
| 				Img:        plugin.Info.Logos.Small, | 				Img:        plugin.Info.Logos.Small, | ||||||
|  | 				SortWeight: dtos.WeightPlugin, | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			for _, include := range plugin.Includes { | 			for _, include := range plugin.Includes { | ||||||
|  | @ -302,6 +309,7 @@ func (hs *HTTPServer) setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, er | ||||||
| 		SubTitle:   "Organization: " + c.OrgName, | 		SubTitle:   "Organization: " + c.OrgName, | ||||||
| 		Icon:       "gicon gicon-cog", | 		Icon:       "gicon gicon-cog", | ||||||
| 		Url:        configNodes[0].Url, | 		Url:        configNodes[0].Url, | ||||||
|  | 		SortWeight: dtos.WeightConfig, | ||||||
| 		Children:   configNodes, | 		Children:   configNodes, | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
|  | @ -326,6 +334,7 @@ func (hs *HTTPServer) setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, er | ||||||
| 			Id:           "admin", | 			Id:           "admin", | ||||||
| 			Icon:         "gicon gicon-shield", | 			Icon:         "gicon gicon-shield", | ||||||
| 			Url:          setting.AppSubUrl + "/admin/users", | 			Url:          setting.AppSubUrl + "/admin/users", | ||||||
|  | 			SortWeight:   dtos.WeightAdmin, | ||||||
| 			Children:     adminNavLinks, | 			Children:     adminNavLinks, | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
|  | @ -337,6 +346,7 @@ func (hs *HTTPServer) setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, er | ||||||
| 		Url:          "#", | 		Url:          "#", | ||||||
| 		Icon:         "gicon gicon-question", | 		Icon:         "gicon gicon-question", | ||||||
| 		HideFromMenu: true, | 		HideFromMenu: true, | ||||||
|  | 		SortWeight:   dtos.WeightHelp, | ||||||
| 		Children: []*dtos.NavLink{ | 		Children: []*dtos.NavLink{ | ||||||
| 			{Text: "Keyboard shortcuts", Url: "/shortcuts", Icon: "fa fa-fw fa-keyboard-o", Target: "_self"}, | 			{Text: "Keyboard shortcuts", Url: "/shortcuts", Icon: "fa fa-fw fa-keyboard-o", Target: "_self"}, | ||||||
| 			{Text: "Community site", Url: "http://community.grafana.com", Icon: "fa fa-fw fa-comment", Target: "_blank"}, | 			{Text: "Community site", Url: "http://community.grafana.com", Icon: "fa fa-fw fa-comment", Target: "_blank"}, | ||||||
|  | @ -345,6 +355,10 @@ func (hs *HTTPServer) setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, er | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	hs.HooksService.RunIndexDataHooks(&data) | 	hs.HooksService.RunIndexDataHooks(&data) | ||||||
|  | 
 | ||||||
|  | 	sort.SliceStable(data.NavTree, func(i, j int) bool { | ||||||
|  | 		return data.NavTree[i].SortWeight < data.NavTree[j].SortWeight | ||||||
|  | 	}) | ||||||
| 	return &data, nil | 	return &data, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue