Chore: Remove pinNavItems feature toggle (#113855)
This commit is contained in:
@@ -53,7 +53,6 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general-
|
|||||||
| `cloudWatchNewLabelParsing` | Updates CloudWatch label parsing to be more accurate | Yes |
|
| `cloudWatchNewLabelParsing` | Updates CloudWatch label parsing to be more accurate | Yes |
|
||||||
| `pluginProxyPreserveTrailingSlash` | Preserve plugin proxy trailing slash. | |
|
| `pluginProxyPreserveTrailingSlash` | Preserve plugin proxy trailing slash. | |
|
||||||
| `azureMonitorPrometheusExemplars` | Allows configuration of Azure Monitor as a data source that can provide Prometheus exemplars | Yes |
|
| `azureMonitorPrometheusExemplars` | Allows configuration of Azure Monitor as a data source that can provide Prometheus exemplars | Yes |
|
||||||
| `pinNavItems` | Enables pinning of nav items | Yes |
|
|
||||||
| `ssoSettingsLDAP` | Use the new SSO Settings API to configure LDAP | Yes |
|
| `ssoSettingsLDAP` | Use the new SSO Settings API to configure LDAP | Yes |
|
||||||
| `cloudWatchRoundUpEndTime` | Round up end time for metric queries to the next minute to avoid missing data | Yes |
|
| `cloudWatchRoundUpEndTime` | Round up end time for metric queries to the next minute to avoid missing data | Yes |
|
||||||
| `newFiltersUI` | Enables new combobox style UI for the Ad hoc filters variable in scenes architecture | Yes |
|
| `newFiltersUI` | Enables new combobox style UI for the Ad hoc filters variable in scenes architecture | Yes |
|
||||||
|
|||||||
@@ -566,11 +566,6 @@ export interface FeatureToggles {
|
|||||||
*/
|
*/
|
||||||
azureMonitorPrometheusExemplars?: boolean;
|
azureMonitorPrometheusExemplars?: boolean;
|
||||||
/**
|
/**
|
||||||
* Enables pinning of nav items
|
|
||||||
* @default true
|
|
||||||
*/
|
|
||||||
pinNavItems?: boolean;
|
|
||||||
/**
|
|
||||||
* Enables the gRPC server for authorization
|
* Enables the gRPC server for authorization
|
||||||
*/
|
*/
|
||||||
authZGRPCServer?: boolean;
|
authZGRPCServer?: boolean;
|
||||||
|
|||||||
@@ -975,13 +975,6 @@ var (
|
|||||||
Owner: grafanaPartnerPluginsSquad,
|
Owner: grafanaPartnerPluginsSquad,
|
||||||
Expression: "true", // enabled by default
|
Expression: "true", // enabled by default
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Name: "pinNavItems",
|
|
||||||
Description: "Enables pinning of nav items",
|
|
||||||
Stage: FeatureStageGeneralAvailability,
|
|
||||||
Owner: grafanaFrontendSearchNavOrganise,
|
|
||||||
Expression: "true", // enabled by default
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Name: "authZGRPCServer",
|
Name: "authZGRPCServer",
|
||||||
Description: "Enables the gRPC server for authorization",
|
Description: "Enables the gRPC server for authorization",
|
||||||
|
|||||||
Generated
-1
@@ -127,7 +127,6 @@ preserveDashboardStateWhenNavigating,experimental,@grafana/dashboards-squad,fals
|
|||||||
alertingCentralAlertHistory,experimental,@grafana/alerting-squad,false,false,true
|
alertingCentralAlertHistory,experimental,@grafana/alerting-squad,false,false,true
|
||||||
pluginProxyPreserveTrailingSlash,GA,@grafana/plugins-platform-backend,false,false,false
|
pluginProxyPreserveTrailingSlash,GA,@grafana/plugins-platform-backend,false,false,false
|
||||||
azureMonitorPrometheusExemplars,GA,@grafana/partner-datasources,false,false,false
|
azureMonitorPrometheusExemplars,GA,@grafana/partner-datasources,false,false,false
|
||||||
pinNavItems,GA,@grafana/grafana-search-navigate-organise,false,false,false
|
|
||||||
authZGRPCServer,experimental,@grafana/identity-access-team,false,false,false
|
authZGRPCServer,experimental,@grafana/identity-access-team,false,false,false
|
||||||
ssoSettingsLDAP,GA,@grafana/identity-access-team,false,true,false
|
ssoSettingsLDAP,GA,@grafana/identity-access-team,false,true,false
|
||||||
zanzana,experimental,@grafana/identity-access-team,false,false,false
|
zanzana,experimental,@grafana/identity-access-team,false,false,false
|
||||||
|
|||||||
|
Generated
-4
@@ -519,10 +519,6 @@ const (
|
|||||||
// Allows configuration of Azure Monitor as a data source that can provide Prometheus exemplars
|
// Allows configuration of Azure Monitor as a data source that can provide Prometheus exemplars
|
||||||
FlagAzureMonitorPrometheusExemplars = "azureMonitorPrometheusExemplars"
|
FlagAzureMonitorPrometheusExemplars = "azureMonitorPrometheusExemplars"
|
||||||
|
|
||||||
// FlagPinNavItems
|
|
||||||
// Enables pinning of nav items
|
|
||||||
FlagPinNavItems = "pinNavItems"
|
|
||||||
|
|
||||||
// FlagAuthZGRPCServer
|
// FlagAuthZGRPCServer
|
||||||
// Enables the gRPC server for authorization
|
// Enables the gRPC server for authorization
|
||||||
FlagAuthZGRPCServer = "authZGRPCServer"
|
FlagAuthZGRPCServer = "authZGRPCServer"
|
||||||
|
|||||||
+1
@@ -3084,6 +3084,7 @@
|
|||||||
"name": "pinNavItems",
|
"name": "pinNavItems",
|
||||||
"resourceVersion": "1762958248290",
|
"resourceVersion": "1762958248290",
|
||||||
"creationTimestamp": "2024-06-10T11:40:03Z",
|
"creationTimestamp": "2024-06-10T11:40:03Z",
|
||||||
|
"deletionTimestamp": "2025-11-13T14:52:10Z",
|
||||||
"annotations": {
|
"annotations": {
|
||||||
"grafana.app/updatedTimestamp": "2025-11-12 14:37:28.29086 +0000 UTC"
|
"grafana.app/updatedTimestamp": "2025-11-12 14:37:28.29086 +0000 UTC"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,8 +24,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/star"
|
"github.com/grafana/grafana/pkg/services/star"
|
||||||
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlesimpl"
|
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlesimpl"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
|
|
||||||
"github.com/open-feature/go-sdk/openfeature"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServiceImpl struct {
|
type ServiceImpl struct {
|
||||||
@@ -86,7 +84,6 @@ func ProvideService(cfg *setting.Cfg, accessControl ac.AccessControl, pluginStor
|
|||||||
func (s *ServiceImpl) GetNavTree(c *contextmodel.ReqContext, prefs *pref.Preference) (*navtree.NavTreeRoot, error) {
|
func (s *ServiceImpl) GetNavTree(c *contextmodel.ReqContext, prefs *pref.Preference) (*navtree.NavTreeRoot, error) {
|
||||||
hasAccess := ac.HasAccess(s.accessControl, c)
|
hasAccess := ac.HasAccess(s.accessControl, c)
|
||||||
treeRoot := &navtree.NavTreeRoot{}
|
treeRoot := &navtree.NavTreeRoot{}
|
||||||
ctx := c.Req.Context()
|
|
||||||
|
|
||||||
treeRoot.AddSection(s.getHomeNode(c, prefs))
|
treeRoot.AddSection(s.getHomeNode(c, prefs))
|
||||||
|
|
||||||
@@ -188,9 +185,7 @@ func (s *ServiceImpl) GetNavTree(c *contextmodel.ReqContext, prefs *pref.Prefere
|
|||||||
treeRoot.RemoveSectionByID(navtree.NavIDCfg)
|
treeRoot.RemoveSectionByID(navtree.NavIDCfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
flagDetails, err := openfeature.NewDefaultClient().BooleanValueDetails(ctx, featuremgmt.FlagPinNavItems, true, openfeature.TransactionContext(ctx))
|
if c.IsSignedIn {
|
||||||
s.log.Debug("flag evaluation: ", "flagDetails", flagDetails, "err", err)
|
|
||||||
if flagDetails.Value && c.IsSignedIn {
|
|
||||||
treeRoot.AddSection(&navtree.NavLink{
|
treeRoot.AddSection(&navtree.NavLink{
|
||||||
Text: "Bookmarks",
|
Text: "Bookmarks",
|
||||||
Id: navtree.NavIDBookmarks,
|
Id: navtree.NavIDBookmarks,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { usePatchUserPreferencesMutation } from '@grafana/api-clients/rtkq/legac
|
|||||||
import { GrafanaTheme2, NavModelItem } from '@grafana/data';
|
import { GrafanaTheme2, NavModelItem } from '@grafana/data';
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { t } from '@grafana/i18n';
|
import { t } from '@grafana/i18n';
|
||||||
import { config, reportInteraction } from '@grafana/runtime';
|
import { reportInteraction } from '@grafana/runtime';
|
||||||
import { ScrollContainer, useStyles2 } from '@grafana/ui';
|
import { ScrollContainer, useStyles2 } from '@grafana/ui';
|
||||||
import { useGrafana } from 'app/core/context/GrafanaContext';
|
import { useGrafana } from 'app/core/context/GrafanaContext';
|
||||||
import { setBookmark } from 'app/core/reducers/navBarTree';
|
import { setBookmark } from 'app/core/reducers/navBarTree';
|
||||||
@@ -40,25 +40,23 @@ export const MegaMenu = memo(
|
|||||||
.filter((item) => item.id !== 'profile' && item.id !== 'help')
|
.filter((item) => item.id !== 'profile' && item.id !== 'help')
|
||||||
.map((item) => enrichWithInteractionTracking(item, state.megaMenuDocked));
|
.map((item) => enrichWithInteractionTracking(item, state.megaMenuDocked));
|
||||||
|
|
||||||
if (config.featureToggles.pinNavItems) {
|
const bookmarksItem = navItems.find((item) => item.id === 'bookmarks');
|
||||||
const bookmarksItem = navItems.find((item) => item.id === 'bookmarks');
|
if (bookmarksItem) {
|
||||||
if (bookmarksItem) {
|
// Add children to the bookmarks section
|
||||||
// Add children to the bookmarks section
|
bookmarksItem.children = pinnedItems.reduce((acc: NavModelItem[], url) => {
|
||||||
bookmarksItem.children = pinnedItems.reduce((acc: NavModelItem[], url) => {
|
const item = findByUrl(navItems, url);
|
||||||
const item = findByUrl(navItems, url);
|
if (!item) {
|
||||||
if (!item) {
|
|
||||||
return acc;
|
|
||||||
}
|
|
||||||
const newItem = {
|
|
||||||
id: item.id,
|
|
||||||
text: item.text,
|
|
||||||
url: item.url,
|
|
||||||
parentItem: { id: 'bookmarks', text: 'Bookmarks' },
|
|
||||||
};
|
|
||||||
acc.push(enrichWithInteractionTracking(newItem, state.megaMenuDocked));
|
|
||||||
return acc;
|
return acc;
|
||||||
}, []);
|
}
|
||||||
}
|
const newItem = {
|
||||||
|
id: item.id,
|
||||||
|
text: item.text,
|
||||||
|
url: item.url,
|
||||||
|
parentItem: { id: 'bookmarks', text: 'Bookmarks' },
|
||||||
|
};
|
||||||
|
acc.push(enrichWithInteractionTracking(newItem, state.megaMenuDocked));
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
const activeItem = getActiveItem(navItems, state.sectionNav.node, location.pathname);
|
const activeItem = getActiveItem(navItems, state.sectionNav.node, location.pathname);
|
||||||
@@ -85,8 +83,8 @@ export const MegaMenu = memo(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const onPinItem = (item: NavModelItem) => {
|
const onPinItem = (item: NavModelItem) => {
|
||||||
const url = item.url;
|
const { url } = item;
|
||||||
if (url && config.featureToggles.pinNavItems) {
|
if (url) {
|
||||||
const isSaved = isPinned(url);
|
const isSaved = isPinned(url);
|
||||||
const newItems = isSaved ? pinnedItems.filter((i) => url !== i) : [...pinnedItems, url];
|
const newItems = isSaved ? pinnedItems.filter((i) => url !== i) : [...pinnedItems, url];
|
||||||
const interactionName = isSaved ? 'grafana_nav_item_unpinned' : 'grafana_nav_item_pinned';
|
const interactionName = isSaved ? 'grafana_nav_item_unpinned' : 'grafana_nav_item_pinned';
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import * as React from 'react';
|
|||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { t } from '@grafana/i18n';
|
import { t } from '@grafana/i18n';
|
||||||
import { config } from '@grafana/runtime';
|
|
||||||
import { Icon, IconButton, Link, useTheme2 } from '@grafana/ui';
|
import { Icon, IconButton, Link, useTheme2 } from '@grafana/ui';
|
||||||
import { contextSrv } from 'app/core/services/context_srv';
|
import { contextSrv } from 'app/core/services/context_srv';
|
||||||
|
|
||||||
@@ -39,7 +38,6 @@ export function MegaMenuItemText({ children, isActive, onClick, target, url, onP
|
|||||||
<div
|
<div
|
||||||
className={cx(styles.wrapper, {
|
className={cx(styles.wrapper, {
|
||||||
[styles.wrapperActive]: isActive,
|
[styles.wrapperActive]: isActive,
|
||||||
[styles.wrapperBookmark]: config.featureToggles.pinNavItems,
|
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<LinkComponent
|
<LinkComponent
|
||||||
@@ -52,7 +50,7 @@ export function MegaMenuItemText({ children, isActive, onClick, target, url, onP
|
|||||||
>
|
>
|
||||||
{linkContent}
|
{linkContent}
|
||||||
</LinkComponent>
|
</LinkComponent>
|
||||||
{config.featureToggles.pinNavItems && contextSrv.isSignedIn && url && url !== '/bookmarks' && (
|
{contextSrv.isSignedIn && url && url !== '/bookmarks' && (
|
||||||
<IconButton
|
<IconButton
|
||||||
name="bookmark"
|
name="bookmark"
|
||||||
className={'pin-icon'}
|
className={'pin-icon'}
|
||||||
@@ -77,8 +75,6 @@ const getStyles = (theme: GrafanaTheme2, isActive: Props['isActive']) => ({
|
|||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
}),
|
|
||||||
wrapperBookmark: css({
|
|
||||||
'.pin-icon': {
|
'.pin-icon': {
|
||||||
visibility: 'hidden',
|
visibility: 'hidden',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import { useGetUserPreferencesQuery } from '@grafana/api-clients/rtkq/legacy/preferences';
|
import { useGetUserPreferencesQuery } from '@grafana/api-clients/rtkq/legacy/preferences';
|
||||||
import { config } from '@grafana/runtime';
|
|
||||||
|
|
||||||
import { contextSrv } from '../../../services/context_srv';
|
import { contextSrv } from '../../../services/context_srv';
|
||||||
|
|
||||||
@@ -9,8 +8,5 @@ export const usePinnedItems = () => {
|
|||||||
const preferences = useGetUserPreferencesQuery(undefined, { skip: !contextSrv.user.isSignedIn });
|
const preferences = useGetUserPreferencesQuery(undefined, { skip: !contextSrv.user.isSignedIn });
|
||||||
const pinnedItems = useMemo(() => preferences.data?.navbar?.bookmarkUrls || [], [preferences]);
|
const pinnedItems = useMemo(() => preferences.data?.navbar?.bookmarkUrls || [], [preferences]);
|
||||||
|
|
||||||
if (config.featureToggles.pinNavItems) {
|
return pinnedItems;
|
||||||
return pinnedItems;
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -61,8 +61,7 @@ export const enrichWithInteractionTracking = (
|
|||||||
reportInteraction('grafana_navigation_item_clicked', {
|
reportInteraction('grafana_navigation_item_clicked', {
|
||||||
path: newItem.url ?? newItem.id,
|
path: newItem.url ?? newItem.id,
|
||||||
menuIsDocked: megaMenuDockedState,
|
menuIsDocked: megaMenuDockedState,
|
||||||
itemIsBookmarked: Boolean(config.featureToggles.pinNavItems && newItem?.parentItem?.id === 'bookmarks'),
|
itemIsBookmarked: newItem?.parentItem?.id === 'bookmarks',
|
||||||
bookmarkToggleOn: Boolean(config.featureToggles.pinNavItems),
|
|
||||||
isNew,
|
isNew,
|
||||||
});
|
});
|
||||||
onClick?.();
|
onClick?.();
|
||||||
|
|||||||
@@ -52,9 +52,6 @@ const navTreeSlice = createSlice({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
setBookmark: (state, action: PayloadAction<{ item: NavModelItem; isSaved: boolean }>) => {
|
setBookmark: (state, action: PayloadAction<{ item: NavModelItem; isSaved: boolean }>) => {
|
||||||
if (!config.featureToggles.pinNavItems) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const bookmarks = state.find((navItem) => navItem.id === 'bookmarks');
|
const bookmarks = state.find((navItem) => navItem.id === 'bookmarks');
|
||||||
const { item, isSaved } = action.payload;
|
const { item, isSaved } = action.payload;
|
||||||
if (bookmarks) {
|
if (bookmarks) {
|
||||||
|
|||||||
Reference in New Issue
Block a user