diff --git a/.betterer.results b/.betterer.results
index 01d1a68627b..8e643b38615 100644
--- a/.betterer.results
+++ b/.betterer.results
@@ -1003,9 +1003,8 @@ exports[`better eslint`] = {
"public/app/core/components/AppChrome/MegaMenu/MegaMenuItem.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
],
- "public/app/core/components/AppChrome/News/NewsContainer.tsx:5381": [
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"]
+ "public/app/core/components/AppChrome/News/NewsDrawer.tsx:5381": [
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
],
"public/app/core/components/AppChrome/News/NewsWrapper.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
@@ -1016,10 +1015,12 @@ exports[`better eslint`] = {
"public/app/core/components/AppChrome/QuickAdd/QuickAdd.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
],
+ "public/app/core/components/AppChrome/TopBar/ProfileButton.tsx:5381": [
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"]
+ ],
"public/app/core/components/AppChrome/TopBar/SingleTopBar.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "0"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"],
- [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "2"]
+ [0, 0, 0, "No untranslated strings in text props. Wrap text with or use t()", "1"]
],
"public/app/core/components/AppChrome/TopBar/TopSearchBarCommandPaletteTrigger.tsx:5381": [
[0, 0, 0, "\'@grafana/ui/src/themes/mixins\' import is restricted from being used by a pattern. Import from the public export instead.", "0"]
diff --git a/public/app/core/components/AppChrome/News/NewsContainer.test.tsx b/public/app/core/components/AppChrome/News/NewsContainer.test.tsx
deleted file mode 100644
index 3b1ed747283..00000000000
--- a/public/app/core/components/AppChrome/News/NewsContainer.test.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import { render, screen } from '@testing-library/react';
-import userEvent from '@testing-library/user-event';
-
-import { NewsContainer } from './NewsContainer';
-
-const setup = () => {
- const { container } = render();
-
- return { container };
-};
-
-describe('News', () => {
- it('should render the drawer when the drawer button is clicked', async () => {
- setup();
-
- await userEvent.click(screen.getByRole('button'));
- expect(screen.getByText('Latest from the blog')).toBeInTheDocument();
- });
-});
diff --git a/public/app/core/components/AppChrome/News/NewsContainer.tsx b/public/app/core/components/AppChrome/News/NewsContainer.tsx
deleted file mode 100644
index 2d4cec5a628..00000000000
--- a/public/app/core/components/AppChrome/News/NewsContainer.tsx
+++ /dev/null
@@ -1,84 +0,0 @@
-import { css } from '@emotion/css';
-import { useToggle } from 'react-use';
-
-import { GrafanaTheme2 } from '@grafana/data';
-import { selectors } from '@grafana/e2e-selectors';
-import { IconButton, Drawer, ToolbarButton, useStyles2, Text } from '@grafana/ui';
-import { t } from 'app/core/internationalization';
-import { DEFAULT_FEED_URL } from 'app/plugins/panel/news/constants';
-
-import { NewsWrapper } from './NewsWrapper';
-
-interface NewsContainerProps {
- className?: string;
-}
-
-export function NewsContainer({ className }: NewsContainerProps) {
- const [showNewsDrawer, onToggleShowNewsDrawer] = useToggle(false);
- const styles = useStyles2(getStyles);
-
- return (
- <>
-
- {showNewsDrawer && (
-
- {t('news.title', 'Latest from the blog')}
-
-
-
-
-
-
-
- }
- onClose={onToggleShowNewsDrawer}
- size="md"
- >
-
-
- )}
- >
- );
-}
-
-const getStyles = (theme: GrafanaTheme2) => {
- return {
- title: css({
- display: `flex`,
- alignItems: `center`,
- justifyContent: `center`,
- gap: theme.spacing(2),
- borderBottom: `1px solid ${theme.colors.border.weak}`,
- }),
- grot: css({
- display: `flex`,
- alignItems: `center`,
- justifyContent: `center`,
- padding: theme.spacing(2, 0),
-
- img: {
- width: `75px`,
- height: `75px`,
- },
- }),
- actions: css({
- position: 'absolute',
- right: theme.spacing(1),
- top: theme.spacing(2),
- }),
- };
-};
diff --git a/public/app/core/components/AppChrome/News/NewsDrawer.tsx b/public/app/core/components/AppChrome/News/NewsDrawer.tsx
new file mode 100644
index 00000000000..7211823430d
--- /dev/null
+++ b/public/app/core/components/AppChrome/News/NewsDrawer.tsx
@@ -0,0 +1,78 @@
+import { css } from '@emotion/css';
+
+import { GrafanaTheme2 } from '@grafana/data';
+import { selectors } from '@grafana/e2e-selectors';
+import { IconButton, Drawer, useStyles2, Text } from '@grafana/ui';
+import { t } from 'app/core/internationalization';
+import { DEFAULT_FEED_URL } from 'app/plugins/panel/news/constants';
+
+import { NewsWrapper } from './NewsWrapper';
+
+interface NewsContainerProps {
+ className?: string;
+ onClose: () => void;
+}
+
+export function NewsContainer({ onClose }: NewsContainerProps) {
+ const styles = useStyles2(getStyles);
+
+ return (
+
+ {t('news.title', 'Latest from the blog')}
+
+
+
+
+
+
+
+ }
+ onClose={onClose}
+ size="md"
+ >
+
+
+ );
+}
+
+const getStyles = (theme: GrafanaTheme2) => {
+ return {
+ title: css({
+ display: `flex`,
+ alignItems: `center`,
+ justifyContent: `center`,
+ gap: theme.spacing(2),
+ borderBottom: `1px solid ${theme.colors.border.weak}`,
+ }),
+ grot: css({
+ display: `flex`,
+ alignItems: `center`,
+ justifyContent: `center`,
+ padding: theme.spacing(2, 0),
+
+ img: {
+ width: `75px`,
+ height: `75px`,
+ },
+ }),
+ actions: css({
+ position: 'absolute',
+ right: theme.spacing(1),
+ top: theme.spacing(2),
+ }),
+ };
+};
diff --git a/public/app/core/components/AppChrome/TopBar/ProfileButton.tsx b/public/app/core/components/AppChrome/TopBar/ProfileButton.tsx
new file mode 100644
index 00000000000..3faa5f24876
--- /dev/null
+++ b/public/app/core/components/AppChrome/TopBar/ProfileButton.tsx
@@ -0,0 +1,71 @@
+import { css } from '@emotion/css';
+import { cloneDeep } from 'lodash';
+import { useToggle } from 'react-use';
+
+import { GrafanaTheme2, NavModelItem } from '@grafana/data';
+import { config } from '@grafana/runtime';
+import { Dropdown, Menu, MenuItem, ToolbarButton, useStyles2 } from '@grafana/ui';
+import { contextSrv } from 'app/core/core';
+import { t } from 'app/core/internationalization';
+
+import { enrichWithInteractionTracking } from '../MegaMenu/utils';
+import { NewsContainer } from '../News/NewsDrawer';
+
+import { TopNavBarMenu } from './TopNavBarMenu';
+
+export interface Props {
+ profileNode: NavModelItem;
+}
+
+export function ProfileButton({ profileNode }: Props) {
+ const styles = useStyles2(getStyles);
+ const node = enrichWithInteractionTracking(cloneDeep(profileNode), false);
+ const [showNewsDrawer, onToggleShowNewsDrawer] = useToggle(false);
+
+ if (!node) {
+ return null;
+ }
+
+ const renderMenu = () => (
+
+ {config.newsFeedEnabled && (
+ <>
+
+
+ >
+ )}
+
+ );
+
+ return (
+ <>
+
+
+
+ {showNewsDrawer && }
+ >
+ );
+}
+
+const getStyles = (theme: GrafanaTheme2) => {
+ return {
+ profileButton: css({
+ padding: theme.spacing(0, 0.5),
+ img: {
+ borderRadius: theme.shape.radius.circle,
+ height: '24px',
+ marginRight: 0,
+ width: '24px',
+ },
+ }),
+ };
+};
diff --git a/public/app/core/components/AppChrome/TopBar/SingleTopBar.tsx b/public/app/core/components/AppChrome/TopBar/SingleTopBar.tsx
index 819ab7371af..225db772a11 100644
--- a/public/app/core/components/AppChrome/TopBar/SingleTopBar.tsx
+++ b/public/app/core/components/AppChrome/TopBar/SingleTopBar.tsx
@@ -16,10 +16,10 @@ import { Breadcrumbs } from '../../Breadcrumbs/Breadcrumbs';
import { buildBreadcrumbs } from '../../Breadcrumbs/utils';
import { HistoryContainer } from '../History/HistoryContainer';
import { enrichHelpItem } from '../MegaMenu/utils';
-import { NewsContainer } from '../News/NewsContainer';
import { QuickAdd } from '../QuickAdd/QuickAdd';
import { TOP_BAR_LEVEL_HEIGHT } from '../types';
+import { ProfileButton } from './ProfileButton';
import { SignInLink } from './SignInLink';
import { TopNavBarMenu } from './TopNavBarMenu';
import { TopSearchBarCommandPaletteTrigger } from './TopSearchBarCommandPaletteTrigger';
@@ -80,7 +80,6 @@ export const SingleTopBar = memo(function SingleTopBar({
)}
- {config.newsFeedEnabled && }
{!contextSrv.user.isSignedIn && }
- {profileNode && (
- } placement="bottom-end">
-
-
- )}
+ {profileNode && }
);
@@ -132,15 +122,6 @@ const getStyles = (theme: GrafanaTheme2, menuDockedAndOpen: boolean) => ({
height: theme.spacing(3),
width: theme.spacing(3),
}),
- profileButton: css({
- padding: theme.spacing(0, 0.5),
- img: {
- borderRadius: theme.shape.radius.circle,
- height: '24px',
- marginRight: 0,
- width: '24px',
- },
- }),
kioskToggle: css({
[theme.breakpoints.down('lg')]: {
display: 'none',
diff --git a/public/app/core/components/AppChrome/TopBar/TopNavBarMenu.tsx b/public/app/core/components/AppChrome/TopBar/TopNavBarMenu.tsx
index 27abaf86a77..7afb4c50bcc 100644
--- a/public/app/core/components/AppChrome/TopBar/TopNavBarMenu.tsx
+++ b/public/app/core/components/AppChrome/TopBar/TopNavBarMenu.tsx
@@ -8,9 +8,10 @@ import { enrichWithInteractionTracking } from '../MegaMenu/utils';
export interface TopNavBarMenuProps {
node: NavModelItem;
+ children?: React.ReactNode;
}
-export function TopNavBarMenu({ node: nodePlain }: TopNavBarMenuProps) {
+export function TopNavBarMenu({ node: nodePlain, children }: TopNavBarMenuProps) {
const styles = useStyles2(getStyles);
const node = enrichWithInteractionTracking(cloneDeep(nodePlain), false);
@@ -37,6 +38,7 @@ export function TopNavBarMenu({ node: nodePlain }: TopNavBarMenuProps) {
);
})}
+ {children}
);
}
diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json
index 6f9cda10abe..7781727270f 100644
--- a/public/locales/en-US/grafana.json
+++ b/public/locales/en-US/grafana.json
@@ -2430,7 +2430,8 @@
"list-label": "Navigation",
"open": "Open menu",
"undock": "Undock menu"
- }
+ },
+ "rss-button": "Latest from the blog"
},
"news": {
"drawer": {
diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json
index 6b3f2d081a7..006e0f4a07f 100644
--- a/public/locales/pseudo-LOCALE/grafana.json
+++ b/public/locales/pseudo-LOCALE/grafana.json
@@ -2430,7 +2430,8 @@
"list-label": "Ńävįģäŧįőʼn",
"open": "Øpęʼn męʼnū",
"undock": "Ůʼnđőčĸ męʼnū"
- }
+ },
+ "rss-button": "Ŀäŧęşŧ ƒřőm ŧĥę þľőģ"
},
"news": {
"drawer": {