Bookmarks: Move building logic to the Frontend (#91849)

This commit is contained in:
Joao Silva 2024-08-15 13:35:20 +01:00 committed by GitHub
parent d6e793b31e
commit 42efb14989
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 88 additions and 81 deletions

View File

@ -169,14 +169,12 @@ func (s *ServiceImpl) GetNavTree(c *contextmodel.ReqContext, prefs *pref.Prefere
}
if s.features.IsEnabled(c.Req.Context(), featuremgmt.FlagPinNavItems) {
bookmarks := s.buildBookmarksNavLinks(prefs, treeRoot)
treeRoot.AddSection(&navtree.NavLink{
Text: "Bookmarks",
Id: navtree.NavIDBookmarks,
Icon: "bookmark",
SortWeight: navtree.WeightBookmarks,
Children: bookmarks,
Children: []*navtree.NavLink{},
EmptyMessageId: "bookmarks-empty",
Url: s.cfg.AppSubURL + "/bookmarks",
})
@ -335,39 +333,6 @@ func (s *ServiceImpl) buildStarredItemsNavLinks(c *contextmodel.ReqContext) ([]*
return starredItemsChildNavs, nil
}
func (s *ServiceImpl) buildBookmarksNavLinks(prefs *pref.Preference, treeRoot *navtree.NavTreeRoot) []*navtree.NavLink {
bookmarksChildNavs := []*navtree.NavLink{}
bookmarkUrls := prefs.JSONData.Navbar.BookmarkUrls
if len(bookmarkUrls) > 0 {
for _, url := range bookmarkUrls {
item := treeRoot.FindByURL(url)
if item != nil {
bookmarksChildNavs = append(bookmarksChildNavs, &navtree.NavLink{
Id: item.Id,
Text: item.Text,
SubTitle: item.SubTitle,
Icon: item.Icon,
Img: item.Img,
Url: item.Url,
Target: item.Target,
HideFromTabs: item.HideFromTabs,
RoundIcon: item.RoundIcon,
IsSection: item.IsSection,
HighlightText: item.HighlightText,
HighlightID: item.HighlightID,
PluginID: item.PluginID,
IsCreateAction: item.IsCreateAction,
Keywords: item.Keywords,
ParentItem: &navtree.NavLink{Id: navtree.NavIDBookmarks},
})
}
}
}
return bookmarksChildNavs
}
func (s *ServiceImpl) buildDashboardNavLinks(c *contextmodel.ReqContext) []*navtree.NavLink {
hasAccess := ac.HasAccess(s.accessControl, c)

View File

@ -15,7 +15,7 @@ import { useDispatch, useSelector } from 'app/types';
import { MegaMenuItem } from './MegaMenuItem';
import { usePinnedItems } from './hooks';
import { enrichWithInteractionTracking, getActiveItem } from './utils';
import { enrichWithInteractionTracking, findByUrl, getActiveItem } from './utils';
export const MENU_WIDTH = '300px';
@ -39,6 +39,26 @@ export const MegaMenu = memo(
.filter((item) => item.id !== 'profile' && item.id !== 'help')
.map((item) => enrichWithInteractionTracking(item, state.megaMenuDocked));
if (config.featureToggles.pinNavItems) {
const bookmarksItem = findByUrl(navItems, '/bookmarks');
if (bookmarksItem) {
// Add children to the bookmarks section
bookmarksItem.children = pinnedItems.reduce((acc: NavModelItem[], url) => {
const item = findByUrl(navItems, url);
if (!item) {
return acc;
}
acc.push({
id: item.id,
text: item.text,
url: item.url,
parentItem: { id: 'bookmarks', text: 'Bookmarks' },
});
return acc;
}, []);
}
}
const activeItem = getActiveItem(navItems, state.sectionNav.node, location.pathname);
const handleDockedMenu = () => {

View File

@ -1,7 +1,50 @@
import { NavModelItem } from '@grafana/data';
import { ContextSrv, setContextSrv } from 'app/core/services/context_srv';
import { enrichHelpItem, getActiveItem } from './utils';
import { enrichHelpItem, getActiveItem, findByUrl } from './utils';
const starredDashboardUid = 'foo';
const mockNavTree: NavModelItem[] = [
{
text: 'Item',
url: '/item',
id: 'item',
},
{
text: 'Item with children',
url: '/itemWithChildren',
id: 'item-with-children',
children: [
{
text: 'Child',
url: '/child',
id: 'child',
},
],
},
{
text: 'Base',
url: '/',
id: 'home',
},
{
text: 'Starred',
url: '/dashboards?starred',
id: 'starred',
children: [
{
id: `starred/${starredDashboardUid}`,
text: 'Lazy Loading',
url: `/d/${starredDashboardUid}/some-name`,
},
],
},
{
text: 'Dashboards',
url: '/dashboards',
id: 'dashboards',
},
];
jest.mock('../../../app_events', () => ({
publish: jest.fn(),
@ -45,49 +88,6 @@ describe('enrichConfigItems', () => {
});
describe('getActiveItem', () => {
const starredDashboardUid = 'foo';
const mockNavTree: NavModelItem[] = [
{
text: 'Item',
url: '/item',
id: 'item',
},
{
text: 'Item with children',
url: '/itemWithChildren',
id: 'item-with-children',
children: [
{
text: 'Child',
url: '/child',
id: 'child',
},
],
},
{
text: 'Base',
url: '/',
id: 'home',
},
{
text: 'Starred',
url: '/dashboards?starred',
id: 'starred',
children: [
{
id: `starred/${starredDashboardUid}`,
text: 'Lazy Loading',
url: `/d/${starredDashboardUid}/some-name`,
},
],
},
{
text: 'Dashboards',
url: '/dashboards',
id: 'dashboards',
},
];
it('returns an exact match at the top level', () => {
const mockPage: NavModelItem = {
text: 'Some current page',
@ -124,3 +124,25 @@ describe('getActiveItem', () => {
expect(getActiveItem(mockNavTree, mockPage, '/')?.id).toEqual('home');
});
});
describe('findByUrl', () => {
it('returns the correct item at the top level', () => {
expect(findByUrl(mockNavTree, '/item')).toEqual({
text: 'Item',
url: '/item',
id: 'item',
});
});
it('returns the correct child item', () => {
expect(findByUrl(mockNavTree, '/child')).toEqual({
text: 'Child',
url: '/child',
id: 'child',
});
});
it('returns null if no item found', () => {
expect(findByUrl(mockNavTree, '/no-item')).toBeNull();
});
});