Skip to content

Commit 42efb14

Browse files
Bookmarks: Move building logic to the Frontend (grafana#91849)
1 parent d6e793b commit 42efb14

File tree

3 files changed

+88
-81
lines changed

3 files changed

+88
-81
lines changed

pkg/services/navtree/navtreeimpl/navtree.go

+1-36
Original file line numberDiff line numberDiff line change
@@ -169,14 +169,12 @@ func (s *ServiceImpl) GetNavTree(c *contextmodel.ReqContext, prefs *pref.Prefere
169169
}
170170

171171
if s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagPinNavItems) {
172-
bookmarks := s.buildBookmarksNavLinks(prefs, treeRoot)
173-
174172
treeRoot.AddSection(&navtree.NavLink{
175173
Text: "Bookmarks",
176174
Id: navtree.NavIDBookmarks,
177175
Icon: "bookmark",
178176
SortWeight: navtree.WeightBookmarks,
179-
Children: bookmarks,
177+
Children: []*navtree.NavLink{},
180178
EmptyMessageId: "bookmarks-empty",
181179
Url: s.cfg.AppSubURL + "/bookmarks",
182180
})
@@ -335,39 +333,6 @@ func (s *ServiceImpl) buildStarredItemsNavLinks(c *contextmodel.ReqContext) ([]*
335333

336334
return starredItemsChildNavs, nil
337335
}
338-
func (s *ServiceImpl) buildBookmarksNavLinks(prefs *pref.Preference, treeRoot *navtree.NavTreeRoot) []*navtree.NavLink {
339-
bookmarksChildNavs := []*navtree.NavLink{}
340-
341-
bookmarkUrls := prefs.JSONData.Navbar.BookmarkUrls
342-
343-
if len(bookmarkUrls) > 0 {
344-
for _, url := range bookmarkUrls {
345-
item := treeRoot.FindByURL(url)
346-
if item != nil {
347-
bookmarksChildNavs = append(bookmarksChildNavs, &navtree.NavLink{
348-
Id: item.Id,
349-
Text: item.Text,
350-
SubTitle: item.SubTitle,
351-
Icon: item.Icon,
352-
Img: item.Img,
353-
Url: item.Url,
354-
Target: item.Target,
355-
HideFromTabs: item.HideFromTabs,
356-
RoundIcon: item.RoundIcon,
357-
IsSection: item.IsSection,
358-
HighlightText: item.HighlightText,
359-
HighlightID: item.HighlightID,
360-
PluginID: item.PluginID,
361-
IsCreateAction: item.IsCreateAction,
362-
Keywords: item.Keywords,
363-
ParentItem: &navtree.NavLink{Id: navtree.NavIDBookmarks},
364-
})
365-
}
366-
}
367-
}
368-
369-
return bookmarksChildNavs
370-
}
371336

372337
func (s *ServiceImpl) buildDashboardNavLinks(c *contextmodel.ReqContext) []*navtree.NavLink {
373338
hasAccess := ac.HasAccess(s.accessControl, c)

public/app/core/components/AppChrome/MegaMenu/MegaMenu.tsx

+21-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { useDispatch, useSelector } from 'app/types';
1515

1616
import { MegaMenuItem } from './MegaMenuItem';
1717
import { usePinnedItems } from './hooks';
18-
import { enrichWithInteractionTracking, getActiveItem } from './utils';
18+
import { enrichWithInteractionTracking, findByUrl, getActiveItem } from './utils';
1919

2020
export const MENU_WIDTH = '300px';
2121

@@ -39,6 +39,26 @@ export const MegaMenu = memo(
3939
.filter((item) => item.id !== 'profile' && item.id !== 'help')
4040
.map((item) => enrichWithInteractionTracking(item, state.megaMenuDocked));
4141

42+
if (config.featureToggles.pinNavItems) {
43+
const bookmarksItem = findByUrl(navItems, '/bookmarks');
44+
if (bookmarksItem) {
45+
// Add children to the bookmarks section
46+
bookmarksItem.children = pinnedItems.reduce((acc: NavModelItem[], url) => {
47+
const item = findByUrl(navItems, url);
48+
if (!item) {
49+
return acc;
50+
}
51+
acc.push({
52+
id: item.id,
53+
text: item.text,
54+
url: item.url,
55+
parentItem: { id: 'bookmarks', text: 'Bookmarks' },
56+
});
57+
return acc;
58+
}, []);
59+
}
60+
}
61+
4262
const activeItem = getActiveItem(navItems, state.sectionNav.node, location.pathname);
4363

4464
const handleDockedMenu = () => {

public/app/core/components/AppChrome/MegaMenu/utils.test.ts

+66-44
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,50 @@
11
import { NavModelItem } from '@grafana/data';
22
import { ContextSrv, setContextSrv } from 'app/core/services/context_srv';
33

4-
import { enrichHelpItem, getActiveItem } from './utils';
4+
import { enrichHelpItem, getActiveItem, findByUrl } from './utils';
5+
6+
const starredDashboardUid = 'foo';
7+
const mockNavTree: NavModelItem[] = [
8+
{
9+
text: 'Item',
10+
url: '/item',
11+
id: 'item',
12+
},
13+
{
14+
text: 'Item with children',
15+
url: '/itemWithChildren',
16+
id: 'item-with-children',
17+
children: [
18+
{
19+
text: 'Child',
20+
url: '/child',
21+
id: 'child',
22+
},
23+
],
24+
},
25+
{
26+
text: 'Base',
27+
url: '/',
28+
id: 'home',
29+
},
30+
{
31+
text: 'Starred',
32+
url: '/dashboards?starred',
33+
id: 'starred',
34+
children: [
35+
{
36+
id: `starred/${starredDashboardUid}`,
37+
text: 'Lazy Loading',
38+
url: `/d/${starredDashboardUid}/some-name`,
39+
},
40+
],
41+
},
42+
{
43+
text: 'Dashboards',
44+
url: '/dashboards',
45+
id: 'dashboards',
46+
},
47+
];
548

649
jest.mock('../../../app_events', () => ({
750
publish: jest.fn(),
@@ -45,49 +88,6 @@ describe('enrichConfigItems', () => {
4588
});
4689

4790
describe('getActiveItem', () => {
48-
const starredDashboardUid = 'foo';
49-
const mockNavTree: NavModelItem[] = [
50-
{
51-
text: 'Item',
52-
url: '/item',
53-
id: 'item',
54-
},
55-
{
56-
text: 'Item with children',
57-
url: '/itemWithChildren',
58-
id: 'item-with-children',
59-
children: [
60-
{
61-
text: 'Child',
62-
url: '/child',
63-
id: 'child',
64-
},
65-
],
66-
},
67-
{
68-
text: 'Base',
69-
url: '/',
70-
id: 'home',
71-
},
72-
{
73-
text: 'Starred',
74-
url: '/dashboards?starred',
75-
id: 'starred',
76-
children: [
77-
{
78-
id: `starred/${starredDashboardUid}`,
79-
text: 'Lazy Loading',
80-
url: `/d/${starredDashboardUid}/some-name`,
81-
},
82-
],
83-
},
84-
{
85-
text: 'Dashboards',
86-
url: '/dashboards',
87-
id: 'dashboards',
88-
},
89-
];
90-
9191
it('returns an exact match at the top level', () => {
9292
const mockPage: NavModelItem = {
9393
text: 'Some current page',
@@ -124,3 +124,25 @@ describe('getActiveItem', () => {
124124
expect(getActiveItem(mockNavTree, mockPage, '/')?.id).toEqual('home');
125125
});
126126
});
127+
128+
describe('findByUrl', () => {
129+
it('returns the correct item at the top level', () => {
130+
expect(findByUrl(mockNavTree, '/item')).toEqual({
131+
text: 'Item',
132+
url: '/item',
133+
id: 'item',
134+
});
135+
});
136+
137+
it('returns the correct child item', () => {
138+
expect(findByUrl(mockNavTree, '/child')).toEqual({
139+
text: 'Child',
140+
url: '/child',
141+
id: 'child',
142+
});
143+
});
144+
145+
it('returns null if no item found', () => {
146+
expect(findByUrl(mockNavTree, '/no-item')).toBeNull();
147+
});
148+
});

0 commit comments

Comments
 (0)