diff --git a/public/app/core/components/AppChrome/TopBar/SignInLink.tsx b/public/app/core/components/AppChrome/TopBar/SignInLink.tsx
new file mode 100644
index 00000000000..adfdefe4f36
--- /dev/null
+++ b/public/app/core/components/AppChrome/TopBar/SignInLink.tsx
@@ -0,0 +1,29 @@
+import { css } from '@emotion/css';
+import React from 'react';
+import { useLocation } from 'react-router-dom';
+
+import { locationUtil } from '@grafana/data';
+import { useStyles2 } from '@grafana/ui';
+
+export function SignInLink() {
+ const location = useLocation();
+ const styles = useStyles2(getStyles);
+ const loginUrl = locationUtil.getUrlForPartial(location, { forceLogin: 'true' });
+
+ return (
+
+ Sign in
+
+ );
+}
+
+const getStyles = () => {
+ return {
+ link: css({
+ whiteSpace: 'nowrap',
+ '&:hover': {
+ textDecoration: 'underline',
+ },
+ }),
+ };
+};
diff --git a/public/app/core/components/AppChrome/TopSearchBar.tsx b/public/app/core/components/AppChrome/TopSearchBar.tsx
index 042b4ee3153..820ad4f6eee 100644
--- a/public/app/core/components/AppChrome/TopSearchBar.tsx
+++ b/public/app/core/components/AppChrome/TopSearchBar.tsx
@@ -7,6 +7,7 @@ import { contextSrv } from 'app/core/core';
import { useSelector } from 'app/types';
import { NewsContainer } from './News/NewsContainer';
+import { SignInLink } from './TopBar/SignInLink';
import { TopNavBarMenu } from './TopBar/TopNavBarMenu';
import { TopSearchBarInput } from './TopSearchBarInput';
import { TOP_BAR_LEVEL_HEIGHT } from './types';
@@ -35,6 +36,7 @@ export function TopSearchBar() {
)}
+ {!contextSrv.user.isSignedIn && }
{profileNode && (
}>
{
display: 'flex',
gap: theme.spacing(0.5),
justifyContent: 'flex-end',
+ alignItems: 'center',
}),
profileButton: css({
img: {
diff --git a/public/app/core/components/NavBar/utils.test.ts b/public/app/core/components/NavBar/utils.test.ts
index 02b1fde248f..833c65ae3a4 100644
--- a/public/app/core/components/NavBar/utils.test.ts
+++ b/public/app/core/components/NavBar/utils.test.ts
@@ -3,37 +3,12 @@ import { Location } from 'history';
import { GrafanaConfig, locationUtil, NavModelItem } from '@grafana/data';
import { ContextSrv, setContextSrv } from 'app/core/services/context_srv';
-import { updateConfig } from '../../config';
-
-import { enrichConfigItems, getActiveItem, getForcedLoginUrl, isMatchOrChildMatch, isSearchActive } from './utils';
+import { enrichConfigItems, getActiveItem, isMatchOrChildMatch, isSearchActive } from './utils';
jest.mock('../../app_events', () => ({
publish: jest.fn(),
}));
-describe('getForcedLoginUrl', () => {
- it.each`
- appSubUrl | url | expected
- ${''} | ${'/whatever?a=1&b=2'} | ${'/whatever?a=1&b=2&forceLogin=true'}
- ${'/grafana'} | ${'/whatever?a=1&b=2'} | ${'/grafana/whatever?a=1&b=2&forceLogin=true'}
- ${'/grafana/test'} | ${'/whatever?a=1&b=2'} | ${'/grafana/test/whatever?a=1&b=2&forceLogin=true'}
- ${'/grafana'} | ${''} | ${'/grafana?forceLogin=true'}
- ${'/grafana'} | ${'/whatever'} | ${'/grafana/whatever?forceLogin=true'}
- ${'/grafana'} | ${'/whatever/'} | ${'/grafana/whatever/?forceLogin=true'}
- `(
- "when appUrl set to '$appUrl' and appSubUrl set to '$appSubUrl' then result should be '$expected'",
- ({ appSubUrl, url, expected }) => {
- updateConfig({
- appSubUrl,
- });
-
- const result = getForcedLoginUrl(url);
-
- expect(result).toBe(expected);
- }
- );
-});
-
describe('enrichConfigItems', () => {
let mockItems: NavModelItem[];
const mockLocation: Location = {
diff --git a/public/app/core/components/NavBar/utils.ts b/public/app/core/components/NavBar/utils.ts
index 24eeaa5212f..60160e40f6a 100644
--- a/public/app/core/components/NavBar/utils.ts
+++ b/public/app/core/components/NavBar/utils.ts
@@ -1,8 +1,7 @@
import { Location } from 'history';
import { locationUtil, NavModelItem, NavSection } from '@grafana/data';
-import { reportInteraction } from '@grafana/runtime';
-import { getConfig } from 'app/core/config';
+import { config, reportInteraction } from '@grafana/runtime';
import { contextSrv } from 'app/core/services/context_srv';
import { ShowModalReactEvent } from '../../../types/events';
@@ -16,13 +15,6 @@ export const NAV_MENU_PORTAL_CONTAINER_ID = 'navbar-menu-portal-container';
export const getNavMenuPortalContainer = () => document.getElementById(NAV_MENU_PORTAL_CONTAINER_ID) ?? document.body;
-export const getForcedLoginUrl = (url: string) => {
- const queryParams = new URLSearchParams(url.split('?')[1]);
- queryParams.append('forceLogin', 'true');
-
- return `${getConfig().appSubUrl}${url.split('?')[0]}?${queryParams.toString()}`;
-};
-
export const enrichConfigItems = (items: NavModelItem[], location: Location) => {
const { isSignedIn, user } = contextSrv;
const onOpenShortcuts = () => {
@@ -41,8 +33,8 @@ export const enrichConfigItems = (items: NavModelItem[], location: Location