mirror of
https://github.com/grafana/grafana.git
synced 2024-11-27 03:11:01 -06:00
376f4b0cc7
* feat(Navigation): add `pluginId` to NavLink and override sibling navlinks with the same URL * test replacing page from plugin * chore: fix go lint issues * fix(NavLink): change `PluginId` to `PluginID` Co-authored-by: Torkel Ödegaard <torkel@grafana.com> * fix(NavLink): make the `PluginId` -> `PluginID` change everywhere * chore(navModel.ts): update explanatory comment for `pluginId` Co-authored-by: Miklós Tolnai <miklos.tolnai@grafana.com> Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
195 lines
5.3 KiB
Go
195 lines
5.3 KiB
Go
package navtree
|
|
|
|
import (
|
|
"encoding/json"
|
|
"sort"
|
|
)
|
|
|
|
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.
|
|
|
|
WeightHome = (iota - 20) * 100
|
|
WeightSavedItems
|
|
WeightCreate
|
|
WeightDashboard
|
|
WeightQueryLibrary
|
|
WeightExplore
|
|
WeightAlerting
|
|
WeightDataConnections
|
|
WeightPlugin
|
|
WeightConfig
|
|
WeightAlertsAndIncidents
|
|
WeightMonitoring
|
|
WeightApps
|
|
WeightAdmin
|
|
WeightProfile
|
|
WeightHelp
|
|
)
|
|
|
|
const (
|
|
NavSectionCore string = "core"
|
|
NavSectionPlugin string = "plugin"
|
|
NavSectionConfig string = "config"
|
|
)
|
|
|
|
const (
|
|
NavIDDashboards = "dashboards"
|
|
NavIDDashboardsBrowse = "dashboards/browse"
|
|
NavIDCfg = "cfg" // NavIDCfg is the id for org configuration navigation node
|
|
NavIDAdmin = "admin"
|
|
NavIDAlertsAndIncidents = "alerts-and-incidents"
|
|
NavIDAlerting = "alerting"
|
|
NavIDMonitoring = "monitoring"
|
|
NavIDReporting = "reports"
|
|
NavIDApps = "apps"
|
|
)
|
|
|
|
type NavLink struct {
|
|
Id string `json:"id,omitempty"`
|
|
Text string `json:"text"`
|
|
Section string `json:"section,omitempty"`
|
|
SubTitle string `json:"subTitle,omitempty"`
|
|
Icon string `json:"icon,omitempty"` // Available icons can be browsed in Storybook: https://developers.grafana.com/ui/latest/index.html?path=/story/docs-overview-icon--icons-overview
|
|
Img string `json:"img,omitempty"`
|
|
Url string `json:"url,omitempty"`
|
|
Target string `json:"target,omitempty"`
|
|
SortWeight int64 `json:"sortWeight,omitempty"`
|
|
Divider bool `json:"divider,omitempty"`
|
|
HideFromMenu bool `json:"hideFromMenu,omitempty"`
|
|
HideFromTabs bool `json:"hideFromTabs,omitempty"`
|
|
ShowIconInNavbar bool `json:"showIconInNavbar,omitempty"`
|
|
RoundIcon bool `json:"roundIcon,omitempty"`
|
|
IsSection bool `json:"isSection,omitempty"`
|
|
Children []*NavLink `json:"children,omitempty"`
|
|
HighlightText string `json:"highlightText,omitempty"`
|
|
HighlightID string `json:"highlightId,omitempty"`
|
|
EmptyMessageId string `json:"emptyMessageId,omitempty"`
|
|
PluginID string `json:"pluginId,omitempty"` // (Optional) The ID of the plugin that registered nav link (e.g. as a standalone plugin page)
|
|
}
|
|
|
|
func (node *NavLink) Sort() {
|
|
Sort(node.Children)
|
|
}
|
|
|
|
type NavTreeRoot struct {
|
|
Children []*NavLink
|
|
}
|
|
|
|
func (root *NavTreeRoot) AddSection(node *NavLink) {
|
|
root.Children = append(root.Children, node)
|
|
}
|
|
|
|
func (root *NavTreeRoot) RemoveSection(node *NavLink) {
|
|
var result []*NavLink
|
|
|
|
for _, child := range root.Children {
|
|
if child != node {
|
|
result = append(result, child)
|
|
}
|
|
}
|
|
|
|
root.Children = result
|
|
}
|
|
|
|
func (root *NavTreeRoot) FindById(id string) *NavLink {
|
|
return FindById(root.Children, id)
|
|
}
|
|
|
|
func (root *NavTreeRoot) RemoveEmptySectionsAndApplyNewInformationArchitecture(topNavEnabled bool) {
|
|
// Remove server admin node if it has no children or set the url to first child
|
|
if node := root.FindById(NavIDAdmin); node != nil {
|
|
if len(node.Children) == 0 {
|
|
root.RemoveSection(node)
|
|
} else {
|
|
node.Url = node.Children[0].Url
|
|
}
|
|
}
|
|
|
|
if topNavEnabled {
|
|
orgAdminNode := root.FindById(NavIDCfg)
|
|
|
|
if orgAdminNode != nil {
|
|
orgAdminNode.Url = "/admin"
|
|
orgAdminNode.Text = "Administration"
|
|
}
|
|
|
|
if serverAdminNode := root.FindById(NavIDAdmin); serverAdminNode != nil {
|
|
serverAdminNode.Url = "/admin/server"
|
|
serverAdminNode.SortWeight = 0
|
|
|
|
if orgAdminNode != nil {
|
|
orgAdminNode.Children = append(orgAdminNode.Children, serverAdminNode)
|
|
root.RemoveSection(serverAdminNode)
|
|
}
|
|
}
|
|
|
|
// Move reports into dashboards
|
|
if reports := root.FindById(NavIDReporting); reports != nil {
|
|
if dashboards := root.FindById(NavIDDashboards); dashboards != nil {
|
|
reports.SortWeight = 0
|
|
dashboards.Children = append(dashboards.Children, reports)
|
|
root.RemoveSection(reports)
|
|
}
|
|
}
|
|
|
|
// Change id of dashboards
|
|
if dashboards := root.FindById(NavIDDashboards); dashboards != nil {
|
|
dashboards.Id = "dashboards/browse"
|
|
}
|
|
}
|
|
|
|
// Remove top level cfg / administration node if it has no children (needs to be after topnav new info archicture logic above that moves server admin into it)
|
|
// Remove server admin node if it has no children or set the url to first child
|
|
if node := root.FindById(NavIDCfg); node != nil {
|
|
if len(node.Children) == 0 {
|
|
root.RemoveSection(node)
|
|
} else if !topNavEnabled {
|
|
node.Url = node.Children[0].Url
|
|
}
|
|
}
|
|
}
|
|
|
|
func (root *NavTreeRoot) Sort() {
|
|
Sort(root.Children)
|
|
}
|
|
|
|
func (root *NavTreeRoot) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal(root.Children)
|
|
}
|
|
|
|
func Sort(nodes []*NavLink) {
|
|
sort.SliceStable(nodes, func(i, j int) bool {
|
|
iw := nodes[i].SortWeight
|
|
if iw == 0 {
|
|
iw = int64(i) + 1
|
|
}
|
|
jw := nodes[j].SortWeight
|
|
if jw == 0 {
|
|
jw = int64(j) + 1
|
|
}
|
|
|
|
return iw < jw
|
|
})
|
|
|
|
for _, child := range nodes {
|
|
child.Sort()
|
|
}
|
|
}
|
|
|
|
func FindById(nodes []*NavLink, id string) *NavLink {
|
|
for _, child := range nodes {
|
|
if child.Id == id {
|
|
return child
|
|
} else if len(child.Children) > 0 {
|
|
if found := FindById(child.Children, id); found != nil {
|
|
return found
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|