diff --git a/packages/grafana-data/src/utils/location.test.ts b/packages/grafana-data/src/utils/location.test.ts index a496cb4a6f1..d297507b554 100644 --- a/packages/grafana-data/src/utils/location.test.ts +++ b/packages/grafana-data/src/utils/location.test.ts @@ -80,4 +80,22 @@ describe('locationUtil', () => { expect(urlWithoutMaster).toBe('/subUrl/grafana/'); }); }); + + describe('updateSearchParams', () => { + beforeEach(() => { + locationUtil.initialize({ + config: {} as any, + getVariablesUrlParams: (() => {}) as any, + getTimeRangeForUrl: (() => {}) as any, + }); + }); + + test('absolute url', () => { + const newURL = locationUtil.updateSearchParams( + 'http://www.domain.com:1234/test?a=1&b=2#hashtag', + '?a=newValue&newKey=hello' + ); + expect(newURL).toBe('http://www.domain.com:1234/test?a=newValue&b=2&newKey=hello#hashtag'); + }); + }); }); diff --git a/packages/grafana-data/src/utils/location.ts b/packages/grafana-data/src/utils/location.ts index f252ac9e660..55f9c658f0a 100644 --- a/packages/grafana-data/src/utils/location.ts +++ b/packages/grafana-data/src/utils/location.ts @@ -42,6 +42,14 @@ const assureBaseUrl = (url: string): string => { return url; }; +const updateSearchParams = (href: string, searchParams: string) => { + const curURL = new URL(href); + const urlSearchParams = new URLSearchParams(searchParams); + urlSearchParams.forEach((val, key) => curURL.searchParams.set(key, val)); + + return curURL.href; +}; + interface LocationUtilDependencies { config: GrafanaConfig; getTimeRangeForUrl: () => RawTimeRange; @@ -63,6 +71,7 @@ export const locationUtil = { }, stripBaseFromUrl, assureBaseUrl, + updateSearchParams, getTimeRangeUrlParams: () => { if (!getTimeRangeUrlParams) { return null; diff --git a/packages/grafana-ui/src/components/PageLayout/PageToolbar.story.tsx b/packages/grafana-ui/src/components/PageLayout/PageToolbar.story.tsx index d2d927ababf..502cabf497d 100644 --- a/packages/grafana-ui/src/components/PageLayout/PageToolbar.story.tsx +++ b/packages/grafana-ui/src/components/PageLayout/PageToolbar.story.tsx @@ -27,8 +27,8 @@ export const Examples = () => { pageIcon="apps" title="A very long dashboard name" parent="A long folder name" - onClickTitle={() => action('Title clicked')} - onClickParent={() => action('Parent clicked')} + titleHref="" + parentHref="" leftItems={[ , , diff --git a/packages/grafana-ui/src/components/PageLayout/PageToolbar.tsx b/packages/grafana-ui/src/components/PageLayout/PageToolbar.tsx index 96e2d60356e..e112353718d 100644 --- a/packages/grafana-ui/src/components/PageLayout/PageToolbar.tsx +++ b/packages/grafana-ui/src/components/PageLayout/PageToolbar.tsx @@ -7,14 +7,16 @@ import { Icon } from '../Icon/Icon'; import { styleMixins } from '../../themes'; import { IconButton } from '../IconButton/IconButton'; import { selectors } from '@grafana/e2e-selectors'; +import { Link } from '..'; +import { getFocusStyles } from '../../themes/mixins'; export interface Props { pageIcon?: IconName; title: string; parent?: string; onGoBack?: () => void; - onClickTitle?: () => void; - onClickParent?: () => void; + titleHref?: string; + parentHref?: string; leftItems?: ReactNode[]; children?: ReactNode; className?: string; @@ -23,18 +25,7 @@ export interface Props { /** @alpha */ export const PageToolbar: FC = React.memo( - ({ - title, - parent, - pageIcon, - onGoBack, - children, - onClickTitle, - onClickParent, - leftItems, - isFullscreen, - className, - }) => { + ({ title, parent, pageIcon, onGoBack, children, titleHref, parentHref, leftItems, isFullscreen, className }) => { const styles = useStyles2(getStyles); /** @@ -56,7 +47,7 @@ export const PageToolbar: FC = React.memo(
{pageIcon && !onGoBack && (
- +
)} {onGoBack && ( @@ -72,17 +63,24 @@ export const PageToolbar: FC = React.memo( />
)} - {parent && onClickParent && ( - + {parent && parentHref && ( + <> + + {parent} + + {titleHref && ( + + / + + )} + )} - {onClickTitle && ( - + )} - {!onClickTitle &&
{title}
} + {!titleHref &&
{title}
} {leftItems?.map((child, index) => (
{child} @@ -109,21 +107,18 @@ PageToolbar.displayName = 'PageToolbar'; const getStyles = (theme: GrafanaTheme2) => { const { spacing, typography } = theme; - const titleStyles = ` - font-size: ${typography.size.lg}; - padding: ${spacing(0.5, 1, 0.5, 1)}; - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - max-width: 240px; + const focusStyle = getFocusStyles(theme); + const titleStyles = css` + font-size: ${typography.size.lg}; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + max-width: 240px; + border-radius: 2px; - // clear default button styles - background: none; - border: none; - - @media ${styleMixins.mediaUp(theme.v1.breakpoints.xl)} { - max-width: unset; - } + @media ${styleMixins.mediaUp(theme.v1.breakpoints.xl)} { + max-width: unset; + } `; return { @@ -142,6 +137,7 @@ const getStyles = (theme: GrafanaTheme2) => { display: none; @media ${styleMixins.mediaUp(theme.v1.breakpoints.md)} { display: flex; + padding-right: ${theme.spacing(1)}; align-items: center; } `, @@ -154,12 +150,17 @@ const getStyles = (theme: GrafanaTheme2) => { parentIcon: css` margin-left: ${theme.spacing(0.5)}; `, - titleText: css` - ${titleStyles}; + titleText: titleStyles, + titleLink: css` + &:focus-visible { + ${focusStyle} + } + `, + titleDivider: css` + padding: ${spacing(0, 0.5, 0, 0.5)}; `, parentLink: css` display: none; - padding-right: 0; @media ${styleMixins.mediaUp(theme.v1.breakpoints.md)} { display: unset; } diff --git a/public/app/features/dashboard/components/DashNav/DashNav.tsx b/public/app/features/dashboard/components/DashNav/DashNav.tsx index 946b04b4422..7d2b540e082 100644 --- a/public/app/features/dashboard/components/DashNav/DashNav.tsx +++ b/public/app/features/dashboard/components/DashNav/DashNav.tsx @@ -7,7 +7,7 @@ import { playlistSrv } from 'app/features/playlist/PlaylistSrv'; import { DashNavButton } from './DashNavButton'; import { DashNavTimeControls } from './DashNavTimeControls'; import { ButtonGroup, ModalsController, ToolbarButton, PageToolbar } from '@grafana/ui'; -import { textUtil } from '@grafana/data'; +import { locationUtil, textUtil } from '@grafana/data'; // State import { updateTimeZoneForSession } from 'app/features/profile/state/reducers'; // Types @@ -57,10 +57,6 @@ class DashNav extends PureComponent { super(props); } - onFolderNameClick = () => { - locationService.partial({ search: 'open', folder: 'current' }); - }; - onClose = () => { locationService.partial({ viewPanel: null }); }; @@ -96,10 +92,6 @@ class DashNav extends PureComponent { this.forceUpdate(); }; - onDashboardNameClick = () => { - locationService.partial({ search: 'open' }); - }; - addCustomContent(actions: DashNavButtonModel[], buttons: ReactNode[]) { actions.map((action, index) => { const Component = action.component; @@ -250,13 +242,16 @@ class DashNav extends PureComponent { const { isFullscreen, title, folderTitle } = this.props; const onGoBack = isFullscreen ? this.onClose : undefined; + const titleHref = locationUtil.updateSearchParams(window.location.href, '?search=open'); + const parentHref = locationUtil.updateSearchParams(window.location.href, '?search=open&folder=current'); + return (