mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
add event tracking to navigation (#49800)
This commit is contained in:
parent
17d0133008
commit
309ad38fd6
@ -54,15 +54,14 @@ const NavBarItem = ({
|
||||
|
||||
const onNavigate = (item: NavModelItem) => {
|
||||
const { url, target, onClick } = item;
|
||||
if (!url) {
|
||||
onClick?.();
|
||||
return;
|
||||
}
|
||||
onClick?.();
|
||||
|
||||
if (!target && url.startsWith('/')) {
|
||||
locationService.push(locationUtil.stripBaseFromUrl(url));
|
||||
} else {
|
||||
window.open(url, target);
|
||||
if (url) {
|
||||
if (!target && url.startsWith('/')) {
|
||||
locationService.push(locationUtil.stripBaseFromUrl(url));
|
||||
} else {
|
||||
window.open(url, target);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -7,6 +7,7 @@ import CSSTransition from 'react-transition-group/CSSTransition';
|
||||
import { useLocalStorage } from 'react-use';
|
||||
|
||||
import { GrafanaTheme2, NavModelItem } from '@grafana/data';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { CollapsableSection, CustomScrollbar, Icon, IconButton, IconName, useStyles2, useTheme2 } from '@grafana/ui';
|
||||
|
||||
import { Branding } from '../../Branding/Branding';
|
||||
@ -64,7 +65,14 @@ export function NavBarMenu({ activeItem, isOpen, navItems, onClose, setMenuAnima
|
||||
variant="secondary"
|
||||
/>
|
||||
</div>
|
||||
<NavBarToggle className={styles.menuCollapseIcon} isExpanded={isOpen} onClick={onClose} />
|
||||
<NavBarToggle
|
||||
className={styles.menuCollapseIcon}
|
||||
isExpanded={isOpen}
|
||||
onClick={() => {
|
||||
reportInteraction('grafana_navigation_collapsed');
|
||||
onClose();
|
||||
}}
|
||||
/>
|
||||
<nav className={styles.content}>
|
||||
<CustomScrollbar hideHorizontalTrack>
|
||||
<ul className={styles.itemList}>
|
||||
|
@ -6,7 +6,7 @@ import { useSelector } from 'react-redux';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
import { GrafanaTheme2, NavModelItem, NavSection } from '@grafana/data';
|
||||
import { config, locationService } from '@grafana/runtime';
|
||||
import { config, locationService, reportInteraction } from '@grafana/runtime';
|
||||
import { Icon, IconName, useTheme2 } from '@grafana/ui';
|
||||
import { Branding } from 'app/core/components/Branding/Branding';
|
||||
import { getKioskMode } from 'app/core/navigation/kiosk';
|
||||
@ -14,7 +14,14 @@ import { KioskMode, StoreState } from 'app/types';
|
||||
|
||||
import { OrgSwitcher } from '../../OrgSwitcher';
|
||||
import { NavBarContext } from '../context';
|
||||
import { enrichConfigItems, getActiveItem, isMatchOrChildMatch, isSearchActive, SEARCH_ITEM_ID } from '../utils';
|
||||
import {
|
||||
enrichConfigItems,
|
||||
enrichWithInteractionTracking,
|
||||
getActiveItem,
|
||||
isMatchOrChildMatch,
|
||||
isSearchActive,
|
||||
SEARCH_ITEM_ID,
|
||||
} from '../utils';
|
||||
|
||||
import NavBarItem from './NavBarItem';
|
||||
import { NavBarItemWithoutMenu } from './NavBarItemWithoutMenu';
|
||||
@ -27,21 +34,6 @@ const onOpenSearch = () => {
|
||||
locationService.partial({ search: 'open' });
|
||||
};
|
||||
|
||||
const searchItem: NavModelItem = {
|
||||
id: SEARCH_ITEM_ID,
|
||||
onClick: onOpenSearch,
|
||||
text: 'Search dashboards',
|
||||
icon: 'search',
|
||||
};
|
||||
|
||||
// Here we need to hack in a "home" NavModelItem since this is constructed in the frontend
|
||||
const homeItem: NavModelItem = {
|
||||
id: 'home',
|
||||
text: 'Home',
|
||||
url: config.appSubUrl || '/',
|
||||
icon: 'grafana',
|
||||
};
|
||||
|
||||
export const NavBarNext = React.memo(() => {
|
||||
const navBarTree = useSelector((state: StoreState) => state.navBarTree);
|
||||
const theme = useTheme2();
|
||||
@ -49,23 +41,50 @@ export const NavBarNext = React.memo(() => {
|
||||
const location = useLocation();
|
||||
const kiosk = getKioskMode();
|
||||
const [showSwitcherModal, setShowSwitcherModal] = useState(false);
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const [menuAnimationInProgress, setMenuAnimationInProgress] = useState(false);
|
||||
const [menuIdOpen, setMenuIdOpen] = useState<string | undefined>(undefined);
|
||||
|
||||
const toggleSwitcherModal = () => {
|
||||
setShowSwitcherModal(!showSwitcherModal);
|
||||
};
|
||||
const navTree = cloneDeep(navBarTree);
|
||||
navTree.unshift(homeItem);
|
||||
|
||||
const coreItems = navTree.filter((item) => item.section === NavSection.Core);
|
||||
const pluginItems = navTree.filter((item) => item.section === NavSection.Plugin);
|
||||
// Here we need to hack in a "home" and "search" NavModelItem since this is constructed in the frontend
|
||||
const searchItem: NavModelItem = enrichWithInteractionTracking(
|
||||
{
|
||||
id: SEARCH_ITEM_ID,
|
||||
onClick: onOpenSearch,
|
||||
text: 'Search dashboards',
|
||||
icon: 'search',
|
||||
},
|
||||
menuOpen
|
||||
);
|
||||
|
||||
const homeItem: NavModelItem = enrichWithInteractionTracking(
|
||||
{
|
||||
id: 'home',
|
||||
text: 'Home',
|
||||
url: config.appSubUrl || '/',
|
||||
icon: 'grafana',
|
||||
},
|
||||
menuOpen
|
||||
);
|
||||
|
||||
const navTree = cloneDeep(navBarTree);
|
||||
|
||||
const coreItems = navTree
|
||||
.filter((item) => item.section === NavSection.Core)
|
||||
.map((item) => enrichWithInteractionTracking(item, menuOpen));
|
||||
const pluginItems = navTree
|
||||
.filter((item) => item.section === NavSection.Plugin)
|
||||
.map((item) => enrichWithInteractionTracking(item, menuOpen));
|
||||
const configItems = enrichConfigItems(
|
||||
navTree.filter((item) => item.section === NavSection.Config),
|
||||
location,
|
||||
toggleSwitcherModal
|
||||
);
|
||||
).map((item) => enrichWithInteractionTracking(item, menuOpen));
|
||||
|
||||
const activeItem = isSearchActive(location) ? searchItem : getActiveItem(navTree, location.pathname);
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const [menuAnimationInProgress, setMenuAnimationInProgress] = useState(false);
|
||||
const [menuIdOpen, setMenuIdOpen] = useState<string | undefined>(undefined);
|
||||
|
||||
if (kiosk !== KioskMode.Off) {
|
||||
return null;
|
||||
@ -87,16 +106,20 @@ export const NavBarNext = React.memo(() => {
|
||||
<NavBarToggle
|
||||
className={styles.menuExpandIcon}
|
||||
isExpanded={menuOpen}
|
||||
onClick={() => setMenuOpen(!menuOpen)}
|
||||
onClick={() => {
|
||||
reportInteraction('grafana_navigation_expanded');
|
||||
setMenuOpen(true);
|
||||
}}
|
||||
/>
|
||||
|
||||
<NavBarMenuPortalContainer />
|
||||
|
||||
<NavBarItemWithoutMenu
|
||||
elClassName={styles.grafanaLogoInner}
|
||||
label="Home"
|
||||
label={homeItem.text}
|
||||
className={styles.grafanaLogo}
|
||||
url={homeItem.url}
|
||||
onClick={homeItem.onClick}
|
||||
>
|
||||
<Branding.MenuLogo />
|
||||
</NavBarItemWithoutMenu>
|
||||
@ -111,7 +134,7 @@ export const NavBarNext = React.memo(() => {
|
||||
<NavBarItem
|
||||
key={`${link.id}-${index}`}
|
||||
isActive={isMatchOrChildMatch(link, activeItem)}
|
||||
link={{ ...link, subTitle: undefined, onClick: undefined }}
|
||||
link={{ ...link, subTitle: undefined }}
|
||||
>
|
||||
{link.icon && <Icon name={link.icon as IconName} size="xl" />}
|
||||
{link.img && <img src={link.img} alt={`${link.text} logo`} />}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Location } from 'history';
|
||||
|
||||
import { NavModelItem, NavSection } from '@grafana/data';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import { getConfig } from 'app/core/config';
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
|
||||
@ -59,6 +60,7 @@ export const enrichConfigItems = (
|
||||
link.children = [
|
||||
...getFooterLinks(),
|
||||
{
|
||||
id: 'keyboard-shortcuts',
|
||||
text: 'Keyboard shortcuts',
|
||||
icon: 'keyboard',
|
||||
onClick: onOpenShortcuts,
|
||||
@ -70,6 +72,7 @@ export const enrichConfigItems = (
|
||||
link.children = [
|
||||
...menuItems,
|
||||
{
|
||||
id: 'switch-organization',
|
||||
text: 'Switch organization',
|
||||
icon: 'arrow-random',
|
||||
onClick: toggleOrgSwitcher,
|
||||
@ -80,6 +83,21 @@ export const enrichConfigItems = (
|
||||
return items;
|
||||
};
|
||||
|
||||
export const enrichWithInteractionTracking = (item: NavModelItem, expandedState: boolean) => {
|
||||
const onClick = item.onClick;
|
||||
item.onClick = () => {
|
||||
reportInteraction('grafana_navigation_item_clicked', {
|
||||
path: item.url ?? item.id,
|
||||
state: expandedState ? 'expanded' : 'collapsed',
|
||||
});
|
||||
onClick?.();
|
||||
};
|
||||
if (item.children) {
|
||||
item.children = item.children.map((item) => enrichWithInteractionTracking(item, expandedState));
|
||||
}
|
||||
return item;
|
||||
};
|
||||
|
||||
export const isMatchOrChildMatch = (itemToCheck: NavModelItem, searchItem?: NavModelItem) => {
|
||||
return Boolean(itemToCheck === searchItem || itemToCheck.children?.some((child) => child === searchItem));
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user