Navigation: Add help menu to top search bar (#55062)

* add help menu to top search bar

* fixes

* handle preventDefault in node graph specifically

* use icon prop of MenuItem

* undo changes to ContextMenuPlugin/DataLinksContextMenu

* remove unused component

* revert storybook changes

* Tweaks

* remove unused style

* stop propagation on the header so version can be highlighted

* make sure useContextMenu has the exact same logic as before

Co-authored-by: Ashley Harrison <ashley.harrison@grafana.com>
Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
This commit is contained in:
Leo
2022-09-17 18:17:00 +02:00
committed by GitHub
parent 17b2fb04e8
commit 1a0cbdeabe
7 changed files with 73 additions and 62 deletions

View File

@@ -16,59 +16,47 @@ export function TopNavBarMenu({ node }: TopNavBarMenuProps) {
if (!node) {
return null;
}
const onNavigate = (item: NavModelItem) => {
const { url, target, onClick } = item;
onClick?.();
if (url) {
window.open(url, target);
}
};
return (
<Menu>
<MenuItem url={node.url} label={node.text} className={styles.header} />
<Menu
header={
<div onClick={(e) => e.stopPropagation()} className={styles.header}>
<div>{node.text}</div>
{node.subTitle && <div className={styles.subTitle}>{node.subTitle}</div>}
</div>
}
>
{node.children?.map((item) => {
const translationKey = item.id && menuItemTranslations[item.id];
const itemText = translationKey ? i18n._(translationKey) : item.text;
return !item.target && item.url?.startsWith('/') ? (
<MenuItem url={item.url} label={itemText} key={item.id} />
const showExternalLinkIcon = /^https?:\/\//.test(item.url || '');
return item.url ? (
<MenuItem
url={item.url}
label={itemText}
icon={showExternalLinkIcon ? 'external-link-alt' : undefined}
target={item.target}
key={item.id}
/>
) : (
<MenuItem onClick={() => onNavigate(item)} label={itemText} key={item.id} />
<MenuItem icon={item.icon} onClick={item.onClick} label={itemText} key={item.id} />
);
})}
{node.subTitle && (
// Stopping the propagation of the event when clicking the subTitle so the menu
// does not close
<div onClick={(e) => e.stopPropagation()} className={styles.subtitle}>
{node.subTitle}
</div>
)}
</Menu>
);
}
const getStyles = (theme: GrafanaTheme2) => {
return {
subtitle: css`
background-color: transparent;
border-top: 1px solid ${theme.colors.border.weak};
color: ${theme.colors.text.secondary};
font-size: ${theme.typography.bodySmall.fontSize};
font-weight: ${theme.typography.bodySmall.fontWeight};
padding: ${theme.spacing(1)} ${theme.spacing(2)} ${theme.spacing(1)};
text-align: left;
white-space: nowrap;
`,
header: css({
height: `calc(${theme.spacing(6)} - 1px)`,
fontSize: theme.typography.h4.fontSize,
fontWeight: theme.typography.h4.fontWeight,
padding: `${theme.spacing(1)} ${theme.spacing(2)}`,
fontSize: theme.typography.h5.fontSize,
fontWeight: theme.typography.h5.fontWeight,
padding: theme.spacing(0.5, 1),
whiteSpace: 'nowrap',
width: '100%',
background: theme.colors.background.secondary,
}),
subTitle: css({
color: theme.colors.text.secondary,
fontSize: theme.typography.bodySmall.fontSize,
}),
};
};

View File

@@ -44,6 +44,7 @@ export function TopSearchBar() {
toggleSwitcherModal
).map((item) => enrichWithInteractionTracking(item, false));
const helpNode = configItems.find((item) => item.id === 'help');
const profileNode = configItems.find((item) => item.id === 'profile');
const signInNode = configItems.find((item) => item.id === 'signin');
@@ -64,11 +65,13 @@ export function TopSearchBar() {
/>
</div>
<div className={styles.actions}>
<Tooltip placement="bottom" content="Help menu (todo)">
<button className={styles.actionItem}>
<Icon name="question-circle" size="lg" />
</button>
</Tooltip>
{helpNode && (
<Dropdown overlay={<TopNavBarMenu node={helpNode} />}>
<button className={styles.actionItem}>
<Icon name="question-circle" size="lg" />
</button>
</Dropdown>
)}
<Tooltip placement="bottom" content="Grafana news (todo)">
<button className={styles.actionItem}>
<Icon name="rss" size="lg" />

View File

@@ -1,11 +1,13 @@
import React from 'react';
import { LinkTarget } from '@grafana/data';
import { config } from '@grafana/runtime';
import { Icon, IconName } from '@grafana/ui';
export interface FooterLink {
target: LinkTarget;
text: string;
id?: string;
id: string;
icon?: IconName;
url?: string;
}
@@ -13,16 +15,22 @@ export interface FooterLink {
export let getFooterLinks = (): FooterLink[] => {
return [
{
target: '_blank',
id: 'documentation',
text: 'Documentation',
icon: 'document-info',
url: 'https://grafana.com/docs/grafana/latest/?utm_source=grafana_footer',
},
{
target: '_blank',
id: 'support',
text: 'Support',
icon: 'question-circle',
url: 'https://grafana.com/products/enterprise/?utm_source=grafana_footer',
},
{
target: '_blank',
id: 'community',
text: 'Community',
icon: 'comments-alt',
url: 'https://community.grafana.com/?utm_source=grafana_footer',
@@ -45,7 +53,12 @@ export let getVersionLinks = (): FooterLink[] => {
const links: FooterLink[] = [];
const stateInfo = licenseInfo.stateInfo ? ` (${licenseInfo.stateInfo})` : '';
links.push({ text: `${buildInfo.edition}${stateInfo}`, url: licenseInfo.licenseUrl });
links.push({
target: '_blank',
id: 'version',
text: `${buildInfo.edition}${stateInfo}`,
url: licenseInfo.licenseUrl,
});
if (buildInfo.hideVersion) {
return links;
@@ -56,6 +69,8 @@ export let getVersionLinks = (): FooterLink[] => {
const docsVersion = isBeta ? 'next' : 'latest';
links.push({
target: '_blank',
id: 'version',
text: `v${buildInfo.version} (${buildInfo.commit})`,
url: hasReleaseNotes
? `https://grafana.com/docs/grafana/${docsVersion}/release-notes/release-notes-${versionSlug}/`
@@ -64,6 +79,7 @@ export let getVersionLinks = (): FooterLink[] => {
if (buildInfo.hasUpdate) {
links.push({
target: '_blank',
id: 'updateVersion',
text: `New version available!`,
icon: 'download-alt',
@@ -109,7 +125,7 @@ Footer.displayName = 'Footer';
function FooterItem({ item }: { item: FooterLink }) {
const content = item.url ? (
<a href={item.url} target="_blank" rel="noopener noreferrer" id={item.id}>
<a href={item.url} target={item.target} rel="noopener noreferrer" id={item.id}>
{item.text}
</a>
) : (