mirror of https://github.com/grafana/grafana
Navigation: Landing pages behind feature toggles (#54576)
* super quick attempt * feature toggle everything * only construct alertNav if there are navChildren * fix toggle name * plugin landing pages poc * add apps route + put behind feature toggle * use toIconName * rename to NavLandingPage * feature toggle new routes * don't modify GetServerAdminNode * some fairly hacky code to check if the plugin has a root page * remove trailing slashpull/54696/head
parent
6d2352735d
commit
8d489dfd9b
@ -0,0 +1,82 @@ |
||||
import { css } from '@emotion/css'; |
||||
import React from 'react'; |
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data'; |
||||
import { toIconName, useStyles2 } from '@grafana/ui'; |
||||
import { Page } from 'app/core/components/Page/Page'; |
||||
import { useNavModel } from 'app/core/hooks/useNavModel'; |
||||
|
||||
import { NavLandingPageCard } from './NavLandingPageCard'; |
||||
|
||||
interface Props { |
||||
navId: string; |
||||
} |
||||
|
||||
export function NavLandingPage({ navId }: Props) { |
||||
const { node } = useNavModel(navId); |
||||
const styles = useStyles2(getStyles); |
||||
const directChildren = node.children?.filter((child) => !child.hideFromTabs && !child.children); |
||||
const nestedChildren = node.children?.filter((child) => child.children && child.children.length); |
||||
|
||||
return ( |
||||
<Page navId={node.id}> |
||||
<Page.Contents> |
||||
<div className={styles.content}> |
||||
{directChildren && directChildren.length > 0 && ( |
||||
<section className={styles.grid}> |
||||
{directChildren?.map((child) => ( |
||||
<NavLandingPageCard |
||||
key={child.id} |
||||
description={child.description} |
||||
icon={child.icon ? toIconName(child.icon) : undefined} |
||||
text={child.text} |
||||
url={child.url ?? ''} |
||||
/> |
||||
))} |
||||
</section> |
||||
)} |
||||
{nestedChildren?.map((child) => ( |
||||
<section key={child.id}> |
||||
<div style={{ display: 'flex', alignItems: 'center' }}> |
||||
<h2 className={styles.nestedTitle}>{child.text}</h2> |
||||
</div> |
||||
<div className={styles.nestedDescription}>{child.subTitle}</div> |
||||
<div className={styles.grid}> |
||||
{child.children?.map((child) => ( |
||||
<NavLandingPageCard |
||||
key={child.id} |
||||
description={child.description} |
||||
icon={child.icon ? toIconName(child.icon) : undefined} |
||||
text={child.text} |
||||
url={child.url ?? ''} |
||||
/> |
||||
))} |
||||
</div> |
||||
</section> |
||||
))} |
||||
</div> |
||||
</Page.Contents> |
||||
</Page> |
||||
); |
||||
} |
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({ |
||||
content: css({ |
||||
display: 'flex', |
||||
flexDirection: 'column', |
||||
gap: theme.spacing(2), |
||||
}), |
||||
grid: css({ |
||||
display: 'grid', |
||||
gap: theme.spacing(2), |
||||
gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))', |
||||
gridAutoRows: '200px', |
||||
padding: theme.spacing(2, 1), |
||||
}), |
||||
nestedTitle: css({ |
||||
margin: theme.spacing(2, 0), |
||||
}), |
||||
nestedDescription: css({ |
||||
color: theme.colors.text.secondary, |
||||
}), |
||||
}); |
||||
@ -0,0 +1,29 @@ |
||||
import { css } from '@emotion/css'; |
||||
import React from 'react'; |
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data'; |
||||
import { Card, Icon, IconName, useStyles2 } from '@grafana/ui'; |
||||
|
||||
interface Props { |
||||
description?: string; |
||||
icon?: IconName; |
||||
text: string; |
||||
url: string; |
||||
} |
||||
|
||||
export function NavLandingPageCard({ description, icon, text, url }: Props) { |
||||
const styles = useStyles2(getStyles); |
||||
return ( |
||||
<Card className={styles.card} href={url}> |
||||
<Card.Heading>{text}</Card.Heading> |
||||
<Card.Figure align={'center'}>{icon && <Icon name={icon} size="xxxl" />}</Card.Figure> |
||||
<Card.Description>{description}</Card.Description> |
||||
</Card> |
||||
); |
||||
} |
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({ |
||||
card: css({ |
||||
marginBottom: 0, |
||||
}), |
||||
}); |
||||
@ -1,3 +1,14 @@ |
||||
import { NavLinkDTO } from '@grafana/data'; |
||||
|
||||
export function isSoloRoute(path: string): boolean { |
||||
return /(d-solo|dashboard-solo)/.test(path?.toLowerCase()); |
||||
} |
||||
|
||||
export function pluginHasRootPage(pluginId: string, navTree: NavLinkDTO[]): boolean { |
||||
return Boolean( |
||||
navTree |
||||
.find((navLink) => navLink.id === 'apps') |
||||
?.children?.find((app) => app.id === `plugin-page-${pluginId}`) |
||||
?.children?.some((page) => page.url?.endsWith(`/a/${pluginId}`)) |
||||
); |
||||
} |
||||
|
||||
Loading…
Reference in new issue