Frontend Routing: Always render standalone plugin pages using the <AppRootPage> (#57771)
* chore: fix go lint issues * feat(Routing): route standalone plugin pages to the `AppRoutePage` * feat(plugin.json): introduce a new field called `isCorePage` for `includes` * chore: add explanatory comments for types * refactor(AppRootPage): receive the `pluginId` and `pluginSection` through the props Now we are able to receive these as props as the pluginId is defined on navLinks that are registered by plugins. * chore: update teests for AppRootPage * fix: remove rebase issue * tests(applinks): add a test for checking isCorePage plugin page setting * refactor(applinks): update tests to use FindById() and be more resilient to changes * fix: Go lint issues * refactor(routes): use cleaner types when working with plugin nav nodes Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com> * chore: fix linting issues * t: remove `isCorePage` field from includes Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>
This commit is contained in:
co-authored by
Marcus Andersson
parent
f9c88e72ae
commit
eb3ee35e1c
@@ -86,7 +86,7 @@ func (s *ServiceImpl) processAppPlugin(plugin plugins.PluginDTO, c *models.ReqCo
|
||||
continue
|
||||
}
|
||||
|
||||
if include.Type == "page" && include.AddToNav {
|
||||
if include.Type == "page" {
|
||||
link := &navtree.NavLink{
|
||||
Text: include.Name,
|
||||
Icon: include.Icon,
|
||||
@@ -95,7 +95,7 @@ func (s *ServiceImpl) processAppPlugin(plugin plugins.PluginDTO, c *models.ReqCo
|
||||
|
||||
if len(include.Path) > 0 {
|
||||
link.Url = s.cfg.AppSubURL + include.Path
|
||||
if include.DefaultNav {
|
||||
if include.DefaultNav && include.AddToNav {
|
||||
appLink.Url = link.Url
|
||||
}
|
||||
} else {
|
||||
@@ -127,7 +127,9 @@ func (s *ServiceImpl) processAppPlugin(plugin plugins.PluginDTO, c *models.ReqCo
|
||||
sectionForPage.Children = append(sectionForPage.Children, link)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
// Register the page under the app
|
||||
} else if include.AddToNav {
|
||||
appLink.Children = append(appLink.Children, link)
|
||||
}
|
||||
}
|
||||
@@ -169,7 +171,7 @@ func (s *ServiceImpl) processAppPlugin(plugin plugins.PluginDTO, c *models.ReqCo
|
||||
|
||||
// Handle moving apps into specific navtree sections
|
||||
alertingNode := treeRoot.FindById(navtree.NavIDAlerting)
|
||||
sectionID := "apps"
|
||||
sectionID := navtree.NavIDApps
|
||||
|
||||
if navConfig, hasOverride := s.navigationAppConfig[plugin.ID]; hasOverride {
|
||||
appLink.SortWeight = navConfig.SortWeight
|
||||
|
||||
@@ -72,12 +72,24 @@ func TestAddAppLinks(t *testing.T) {
|
||||
Type: plugins.App,
|
||||
Includes: []*plugins.Includes{
|
||||
{
|
||||
Name: "Hello",
|
||||
Path: "/connections/connect-data",
|
||||
Name: "Default page",
|
||||
Path: "/a/test-app3/default",
|
||||
Type: "page",
|
||||
AddToNav: true,
|
||||
DefaultNav: true,
|
||||
},
|
||||
{
|
||||
Name: "Random page",
|
||||
Path: "/a/test-app3/random-page",
|
||||
Type: "page",
|
||||
AddToNav: true,
|
||||
},
|
||||
{
|
||||
Name: "Connect data",
|
||||
Path: "/connections/connect-data",
|
||||
Type: "page",
|
||||
AddToNav: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -113,20 +125,27 @@ func TestAddAppLinks(t *testing.T) {
|
||||
treeRoot := navtree.NavTreeRoot{}
|
||||
err := service.addAppLinks(&treeRoot, reqCtx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "Apps", treeRoot.Children[0].Text)
|
||||
require.Equal(t, "Test app1 name", treeRoot.Children[0].Children[0].Text)
|
||||
|
||||
appsNode := treeRoot.FindById(navtree.NavIDApps)
|
||||
require.NotNil(t, appsNode)
|
||||
require.Equal(t, "Apps", appsNode.Text)
|
||||
require.Len(t, appsNode.Children, 3)
|
||||
require.Equal(t, testApp1.Name, appsNode.Children[0].Text)
|
||||
})
|
||||
|
||||
t.Run("Should remove add default nav child when topnav is enabled", func(t *testing.T) {
|
||||
t.Run("Should remove the default nav child (DefaultNav=true) when topnav is enabled and should set its URL to the plugin nav root", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures(featuremgmt.FlagTopnav)
|
||||
treeRoot := navtree.NavTreeRoot{}
|
||||
err := service.addAppLinks(&treeRoot, reqCtx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "Apps", treeRoot.Children[0].Text)
|
||||
require.Equal(t, "Test app1 name", treeRoot.Children[0].Children[0].Text)
|
||||
require.Equal(t, "Page2", treeRoot.Children[0].Children[0].Children[0].Text)
|
||||
|
||||
app1Node := treeRoot.FindById("plugin-page-test-app1")
|
||||
require.Len(t, app1Node.Children, 1) // The page include with DefaultNav=true gets removed
|
||||
require.Equal(t, "/a/test-app1/catalog", app1Node.Url)
|
||||
require.Equal(t, "Page2", app1Node.Children[0].Text)
|
||||
})
|
||||
|
||||
// This can be done by using `[navigation.app_sections]` in the INI config
|
||||
t.Run("Should move apps that have specific nav id configured to correct section", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures(featuremgmt.FlagTopnav)
|
||||
service.navigationAppConfig = map[string]NavigationAppConfig{
|
||||
@@ -140,76 +159,158 @@ func TestAddAppLinks(t *testing.T) {
|
||||
|
||||
err := service.addAppLinks(&treeRoot, reqCtx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "plugin-page-test-app1", treeRoot.Children[0].Children[0].Id)
|
||||
|
||||
// Check if the plugin gets moved over to the "Admin" section
|
||||
adminNode := treeRoot.FindById(navtree.NavIDAdmin)
|
||||
require.NotNil(t, adminNode)
|
||||
require.Len(t, adminNode.Children, 1)
|
||||
require.Equal(t, "plugin-page-test-app1", adminNode.Children[0].Id)
|
||||
|
||||
// Check if it is not under the "Apps" section anymore
|
||||
appsNode := treeRoot.FindById(navtree.NavIDApps)
|
||||
require.NotNil(t, appsNode)
|
||||
require.Len(t, appsNode.Children, 2)
|
||||
require.Equal(t, "plugin-page-test-app2", appsNode.Children[0].Id)
|
||||
require.Equal(t, "plugin-page-test-app3", appsNode.Children[1].Id)
|
||||
})
|
||||
|
||||
t.Run("Should add monitoring section if plugin exists that wants to live there", func(t *testing.T) {
|
||||
t.Run("Should only add a 'Monitoring' section if a plugin exists that wants to live there", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures(featuremgmt.FlagTopnav)
|
||||
service.navigationAppConfig = map[string]NavigationAppConfig{}
|
||||
|
||||
// Check if the Monitoring section is not there if no apps try to register to it
|
||||
treeRoot := navtree.NavTreeRoot{}
|
||||
err := service.addAppLinks(&treeRoot, reqCtx)
|
||||
require.NoError(t, err)
|
||||
monitoringNode := treeRoot.FindById(navtree.NavIDMonitoring)
|
||||
require.Nil(t, monitoringNode)
|
||||
|
||||
// It should appear and once an app tries to register to it
|
||||
treeRoot = navtree.NavTreeRoot{}
|
||||
service.navigationAppConfig = map[string]NavigationAppConfig{
|
||||
"test-app1": {SectionID: navtree.NavIDMonitoring},
|
||||
}
|
||||
|
||||
treeRoot := navtree.NavTreeRoot{}
|
||||
|
||||
err := service.addAppLinks(&treeRoot, reqCtx)
|
||||
err = service.addAppLinks(&treeRoot, reqCtx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "Monitoring", treeRoot.Children[0].Text)
|
||||
require.Equal(t, "Test app1 name", treeRoot.Children[0].Children[0].Text)
|
||||
monitoringNode = treeRoot.FindById(navtree.NavIDMonitoring)
|
||||
require.NotNil(t, monitoringNode)
|
||||
require.Len(t, monitoringNode.Children, 1)
|
||||
require.Equal(t, "Test app1 name", monitoringNode.Children[0].Text)
|
||||
})
|
||||
|
||||
t.Run("Should add Alerts and incidents section if plugin exists that wants to live there", func(t *testing.T) {
|
||||
t.Run("Should add a 'Alerts and Incidents' section if a plugin exists that wants to live there", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures(featuremgmt.FlagTopnav)
|
||||
service.navigationAppConfig = map[string]NavigationAppConfig{}
|
||||
|
||||
// Check if the 'Alerts and Incidents' section is not there if no apps try to register to it
|
||||
treeRoot := navtree.NavTreeRoot{}
|
||||
err := service.addAppLinks(&treeRoot, reqCtx)
|
||||
require.NoError(t, err)
|
||||
alertsAndIncidentsNode := treeRoot.FindById(navtree.NavIDAlertsAndIncidents)
|
||||
require.Nil(t, alertsAndIncidentsNode)
|
||||
|
||||
// If there is no 'Alerting' node in the navigation (= alerting not enabled) then we don't auto-create the 'Alerts and Incidents' section
|
||||
treeRoot = navtree.NavTreeRoot{}
|
||||
service.navigationAppConfig = map[string]NavigationAppConfig{
|
||||
"test-app1": {SectionID: navtree.NavIDAlertsAndIncidents},
|
||||
}
|
||||
|
||||
treeRoot := navtree.NavTreeRoot{}
|
||||
treeRoot.AddSection(&navtree.NavLink{Id: navtree.NavIDAlerting, Text: "Alerting"})
|
||||
|
||||
err := service.addAppLinks(&treeRoot, reqCtx)
|
||||
err = service.addAppLinks(&treeRoot, reqCtx)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "Alerts & incidents", treeRoot.Children[0].Text)
|
||||
require.Equal(t, "Alerting", treeRoot.Children[0].Children[0].Text)
|
||||
require.Equal(t, "Test app1 name", treeRoot.Children[0].Children[1].Text)
|
||||
alertsAndIncidentsNode = treeRoot.FindById(navtree.NavIDAlertsAndIncidents)
|
||||
require.Nil(t, alertsAndIncidentsNode)
|
||||
|
||||
// It should appear and once an app tries to register to it and the `Alerting` nav node is present
|
||||
treeRoot = navtree.NavTreeRoot{}
|
||||
treeRoot.AddSection(&navtree.NavLink{Id: navtree.NavIDAlerting, Text: "Alerting"})
|
||||
service.navigationAppConfig = map[string]NavigationAppConfig{
|
||||
"test-app1": {SectionID: navtree.NavIDAlertsAndIncidents},
|
||||
}
|
||||
err = service.addAppLinks(&treeRoot, reqCtx)
|
||||
require.NoError(t, err)
|
||||
alertsAndIncidentsNode = treeRoot.FindById(navtree.NavIDAlertsAndIncidents)
|
||||
require.NotNil(t, alertsAndIncidentsNode)
|
||||
require.Len(t, alertsAndIncidentsNode.Children, 2)
|
||||
require.Equal(t, "Alerting", alertsAndIncidentsNode.Children[0].Text)
|
||||
require.Equal(t, "Test app1 name", alertsAndIncidentsNode.Children[1].Text)
|
||||
})
|
||||
|
||||
t.Run("Should be able to control app sort order with SortWeight", func(t *testing.T) {
|
||||
t.Run("Should be able to control app sort order with SortWeight (smaller SortWeight displayed first)", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures(featuremgmt.FlagTopnav)
|
||||
service.navigationAppConfig = map[string]NavigationAppConfig{
|
||||
"test-app2": {SectionID: navtree.NavIDMonitoring, SortWeight: 1},
|
||||
"test-app1": {SectionID: navtree.NavIDMonitoring, SortWeight: 2},
|
||||
"test-app2": {SectionID: navtree.NavIDMonitoring, SortWeight: 2},
|
||||
"test-app1": {SectionID: navtree.NavIDMonitoring, SortWeight: 3},
|
||||
"test-app3": {SectionID: navtree.NavIDMonitoring, SortWeight: 1},
|
||||
}
|
||||
|
||||
treeRoot := navtree.NavTreeRoot{}
|
||||
|
||||
err := service.addAppLinks(&treeRoot, reqCtx)
|
||||
|
||||
treeRoot.Sort()
|
||||
monitoringNode := treeRoot.FindById(navtree.NavIDMonitoring)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "Test app2 name", treeRoot.Children[0].Children[0].Text)
|
||||
require.Equal(t, "Test app1 name", treeRoot.Children[0].Children[1].Text)
|
||||
require.Equal(t, "Test app3 name", monitoringNode.Children[0].Text)
|
||||
require.Equal(t, "Test app2 name", monitoringNode.Children[1].Text)
|
||||
require.Equal(t, "Test app1 name", monitoringNode.Children[2].Text)
|
||||
})
|
||||
|
||||
t.Run("Should replace page from plugin", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures(featuremgmt.FlagTopnav, featuremgmt.FlagDataConnectionsConsole)
|
||||
service.navigationAppConfig = map[string]NavigationAppConfig{}
|
||||
service.navigationAppPathConfig = map[string]NavigationAppConfig{
|
||||
"/connections/connect-data": {SectionID: "connections"},
|
||||
}
|
||||
|
||||
treeRoot := navtree.NavTreeRoot{}
|
||||
treeRoot.AddSection(service.buildDataConnectionsNavLink(reqCtx))
|
||||
require.Equal(t, "Connections", treeRoot.Children[0].Text)
|
||||
require.Equal(t, "Connect Data", treeRoot.Children[0].Children[1].Text)
|
||||
require.Equal(t, "connections-connect-data", treeRoot.Children[0].Children[1].Id)
|
||||
require.Equal(t, "", treeRoot.Children[0].Children[1].PluginID)
|
||||
connectionsNode := treeRoot.FindById("connections")
|
||||
require.Equal(t, "Connections", connectionsNode.Text)
|
||||
require.Equal(t, "Connect Data", connectionsNode.Children[1].Text)
|
||||
require.Equal(t, "connections-connect-data", connectionsNode.Children[1].Id) // Original "Connect Data" page
|
||||
require.Equal(t, "", connectionsNode.Children[1].PluginID)
|
||||
|
||||
err := service.addAppLinks(&treeRoot, reqCtx)
|
||||
|
||||
// Check if the standalone plugin page appears under the section where we registered it
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "Connections", treeRoot.Children[0].Text)
|
||||
require.Equal(t, "Connect Data", treeRoot.Children[0].Children[1].Text)
|
||||
require.Equal(t, "standalone-plugin-page-/connections/connect-data", treeRoot.Children[0].Children[1].Id)
|
||||
require.Equal(t, "test-app3", treeRoot.Children[0].Children[1].PluginID)
|
||||
require.Equal(t, "Connections", connectionsNode.Text)
|
||||
require.Equal(t, "Connect Data", connectionsNode.Children[1].Text)
|
||||
require.Equal(t, "standalone-plugin-page-/connections/connect-data", connectionsNode.Children[1].Id) // Overridden "Connect Data" page
|
||||
require.Equal(t, "test-app3", connectionsNode.Children[1].PluginID)
|
||||
|
||||
// Check if the standalone plugin page does not appear under the app section anymore
|
||||
// (Also checking if the Default Page got removed)
|
||||
app3Node := treeRoot.FindById("plugin-page-test-app3")
|
||||
require.NotNil(t, app3Node)
|
||||
require.Len(t, app3Node.Children, 1)
|
||||
require.Equal(t, "Random page", app3Node.Children[0].Text)
|
||||
|
||||
// The plugin item should take the URL of the Default Nav
|
||||
require.Equal(t, "/a/test-app3/default", app3Node.Url)
|
||||
})
|
||||
|
||||
t.Run("Should not register pages under the app plugin section unless AddToNav=true", func(t *testing.T) {
|
||||
service.features = featuremgmt.WithFeatures(featuremgmt.FlagTopnav, featuremgmt.FlagDataConnectionsConsole)
|
||||
service.navigationAppPathConfig = map[string]NavigationAppConfig{} // We don't configure it as a standalone plugin page
|
||||
|
||||
treeRoot := navtree.NavTreeRoot{}
|
||||
treeRoot.AddSection(service.buildDataConnectionsNavLink(reqCtx))
|
||||
err := service.addAppLinks(&treeRoot, reqCtx)
|
||||
require.NoError(t, err)
|
||||
|
||||
// The original core page should exist under the section
|
||||
connectDataNode := treeRoot.FindById("connections-connect-data")
|
||||
require.Equal(t, "connections-connect-data", connectDataNode.Id)
|
||||
require.Equal(t, "", connectDataNode.PluginID)
|
||||
|
||||
// The standalone plugin page should not be found in the navtree at all (as we didn't configure it)
|
||||
standaloneConnectDataNode := treeRoot.FindById("standalone-plugin-page-/connections/connect-data")
|
||||
require.Nil(t, standaloneConnectDataNode)
|
||||
|
||||
// Only the pages that have `AddToNav=true` appear under the plugin navigation
|
||||
app3Node := treeRoot.FindById("plugin-page-test-app3")
|
||||
require.NotNil(t, app3Node)
|
||||
require.Len(t, app3Node.Children, 1) // It should only have a single child now
|
||||
require.Equal(t, "Random page", app3Node.Children[0].Text)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user