Navigation: Remove bodyScrolling toggle and make it default (#93652)

* remove bodyScrolling toggle and make it default

* fix e2e tests
This commit is contained in:
Ashley Harrison 2024-09-24 13:23:18 +01:00 committed by GitHub
parent 7f7fed8c3c
commit a54308138f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 95 additions and 265 deletions

View File

@ -12,7 +12,7 @@ describe('Dashboards', () => {
e2e.components.Panels.Panel.title('Panel #1').should('be.visible'); e2e.components.Panels.Panel.title('Panel #1').should('be.visible');
// scroll to the bottom // scroll to the bottom
cy.get('#page-scrollbar').scrollTo('bottom', { cy.scrollTo('bottom', {
timeout: 5 * 1000, timeout: 5 * 1000,
}); });

View File

@ -12,7 +12,7 @@ describe('Dashboards', () => {
e2e.components.Panels.Panel.title('Panel #1').should('be.visible'); e2e.components.Panels.Panel.title('Panel #1').should('be.visible');
// scroll to the bottom // scroll to the bottom
cy.get('#page-scrollbar').scrollTo('bottom', { cy.scrollTo('bottom', {
timeout: 5 * 1000, timeout: 5 * 1000,
}); });

View File

@ -64,7 +64,7 @@ describe('Auto-migrate graph panel', () => {
e2e.pages.Dashboard.Annotations.marker().should('exist'); e2e.pages.Dashboard.Annotations.marker().should('exist');
}); });
cy.get('#pageContent .scrollbar-view').first().scrollTo('bottom'); cy.scrollTo('bottom');
e2e.components.Panels.Panel.title('05:00') e2e.components.Panels.Panel.title('05:00')
.should('exist') .should('exist')

View File

@ -64,7 +64,7 @@ describe('Auto-migrate graph panel', () => {
e2e.pages.Dashboard.Annotations.marker().should('exist'); e2e.pages.Dashboard.Annotations.marker().should('exist');
}); });
cy.get('#pageContent .scrollbar-view').first().scrollTo('bottom'); cy.scrollTo('bottom');
e2e.components.Panels.Panel.title('05:00') e2e.components.Panels.Panel.title('05:00')
.should('exist') .should('exist')

View File

@ -199,7 +199,6 @@ export interface FeatureToggles {
alertingApiServer?: boolean; alertingApiServer?: boolean;
dashboardRestoreUI?: boolean; dashboardRestoreUI?: boolean;
cloudWatchRoundUpEndTime?: boolean; cloudWatchRoundUpEndTime?: boolean;
bodyScrolling?: boolean;
cloudwatchMetricInsightsCrossAccount?: boolean; cloudwatchMetricInsightsCrossAccount?: boolean;
prometheusAzureOverrideAudience?: boolean; prometheusAzureOverrideAudience?: boolean;
alertingFilterV2?: boolean; alertingFilterV2?: boolean;

View File

@ -15,7 +15,6 @@ export interface WithContextMenuProps {
export const WithContextMenu = ({ children, renderMenuItems, focusOnOpen = true }: WithContextMenuProps) => { export const WithContextMenu = ({ children, renderMenuItems, focusOnOpen = true }: WithContextMenuProps) => {
const [isMenuOpen, setIsMenuOpen] = useState(false); const [isMenuOpen, setIsMenuOpen] = useState(false);
const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 }); const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 });
const isBodyScrolling = window.grafanaBootData?.settings.featureToggles.bodyScrolling;
return ( return (
<> <>
{children({ {children({
@ -23,7 +22,7 @@ export const WithContextMenu = ({ children, renderMenuItems, focusOnOpen = true
setIsMenuOpen(true); setIsMenuOpen(true);
setMenuPosition({ setMenuPosition({
x: e.pageX, x: e.pageX,
y: isBodyScrolling ? e.pageY - window.scrollY : e.pageY, y: e.pageY - window.scrollY,
}); });
}, },
})} })}

View File

@ -55,21 +55,13 @@ export function PortalContainer() {
} }
const getStyles = (theme: GrafanaTheme2) => { const getStyles = (theme: GrafanaTheme2) => {
const isBodyScrolling = window.grafanaBootData?.settings.featureToggles.bodyScrolling;
return { return {
grafanaPortalContainer: css( grafanaPortalContainer: css({
isBodyScrolling position: 'fixed',
? { top: 0,
position: 'fixed', width: '100%',
top: 0, zIndex: theme.zIndex.portal,
width: '100%', }),
zIndex: theme.zIndex.portal,
}
: {
position: 'absolute',
width: '100%',
}
),
}; };
}; };

View File

@ -5,9 +5,6 @@ import { GrafanaTheme2, ThemeTypographyVariant } from '@grafana/data';
import { getFocusStyles } from '../mixins'; import { getFocusStyles } from '../mixins';
export function getElementStyles(theme: GrafanaTheme2) { export function getElementStyles(theme: GrafanaTheme2) {
// TODO can we get the feature toggle in a better way?
const isBodyScrolling = window.grafanaBootData?.settings.featureToggles.bodyScrolling;
return css({ return css({
'*, *::before, *::after': { '*, *::before, *::after': {
boxSizing: 'inherit', boxSizing: 'inherit',
@ -40,26 +37,24 @@ export function getElementStyles(theme: GrafanaTheme2) {
body: { body: {
height: '100%', height: '100%',
width: '100%', width: '100%',
position: isBodyScrolling ? 'unset' : 'absolute', position: 'unset',
color: theme.colors.text.primary, color: theme.colors.text.primary,
backgroundColor: theme.colors.background.canvas, backgroundColor: theme.colors.background.canvas,
// react select tries prevent scrolling by setting overflow/padding-right on the body
// Need type assertion here due to the use of !important
// see https://github.com/frenic/csstype/issues/114#issuecomment-697201978
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
overflowY: 'auto !important' as 'auto',
paddingRight: '0 !important',
'@media print': {
overflow: 'visible',
},
'@page': {
margin: 0,
size: 'auto',
padding: 0,
},
...theme.typography.body, ...theme.typography.body,
...(isBodyScrolling && {
// react select tries prevent scrolling by setting overflow/padding-right on the body
// Need type assertion here due to the use of !important
// see https://github.com/frenic/csstype/issues/114#issuecomment-697201978
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
overflowY: 'auto !important' as 'auto',
paddingRight: '0 !important',
'@media print': {
overflow: 'visible',
},
'@page': {
margin: 0,
size: 'auto',
padding: 0,
},
}),
}, },
'h1, .h1': getVariantStyles(theme.typography.h1), 'h1, .h1': getVariantStyles(theme.typography.h1),

View File

@ -5,42 +5,21 @@ import { GrafanaTheme2 } from '@grafana/data';
export function getPageStyles(theme: GrafanaTheme2) { export function getPageStyles(theme: GrafanaTheme2) {
const maxWidthBreakpoint = const maxWidthBreakpoint =
theme.breakpoints.values.xxl + theme.spacing.gridSize * 2 + theme.components.sidemenu.width; theme.breakpoints.values.xxl + theme.spacing.gridSize * 2 + theme.components.sidemenu.width;
const isBodyScrolling = window.grafanaBootData?.settings.featureToggles.bodyScrolling;
return css({ return css({
'.grafana-app': isBodyScrolling '.grafana-app': {
? { display: 'flex',
display: 'flex', flexDirection: 'column',
flexDirection: 'column', height: '100vh',
height: '100vh', },
}
: {
display: 'flex',
alignItems: 'stretch',
position: 'absolute',
width: '100%',
height: '100%',
top: 0,
left: 0,
},
'.main-view': isBodyScrolling '.main-view': {
? { display: 'flex',
display: 'flex', flexDirection: 'column',
flexDirection: 'column', flexGrow: 1,
flexGrow: 1, position: 'relative',
position: 'relative', minWidth: 0,
minWidth: 0, },
}
: {
position: 'relative',
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
height: '100%',
flex: '1 1 0',
minWidth: 0,
},
'.page-scrollbar-content': { '.page-scrollbar-content': {
display: 'flex', display: 'flex',

View File

@ -1371,17 +1371,6 @@ var (
Owner: awsDatasourcesSquad, Owner: awsDatasourcesSquad,
Expression: "true", Expression: "true",
}, },
{
Name: "bodyScrolling",
Description: "Adjusts Page to make body the scrollable element",
Stage: FeatureStagePublicPreview,
Owner: grafanaFrontendPlatformSquad,
Expression: "false", // enabled by default
FrontendOnly: true,
AllowSelfServe: true,
HideFromDocs: true,
HideFromAdminPage: false,
},
{ {
Name: "cloudwatchMetricInsightsCrossAccount", Name: "cloudwatchMetricInsightsCrossAccount",
Description: "Enables cross account observability for Cloudwatch Metric Insights query builder", Description: "Enables cross account observability for Cloudwatch Metric Insights query builder",

View File

@ -180,7 +180,6 @@ passScopeToDashboardApi,experimental,@grafana/dashboards-squad,false,false,false
alertingApiServer,experimental,@grafana/alerting-squad,false,true,false alertingApiServer,experimental,@grafana/alerting-squad,false,true,false
dashboardRestoreUI,experimental,@grafana/grafana-frontend-platform,false,false,false dashboardRestoreUI,experimental,@grafana/grafana-frontend-platform,false,false,false
cloudWatchRoundUpEndTime,GA,@grafana/aws-datasources,false,false,false cloudWatchRoundUpEndTime,GA,@grafana/aws-datasources,false,false,false
bodyScrolling,preview,@grafana/grafana-frontend-platform,false,false,true
cloudwatchMetricInsightsCrossAccount,preview,@grafana/aws-datasources,false,false,true cloudwatchMetricInsightsCrossAccount,preview,@grafana/aws-datasources,false,false,true
prometheusAzureOverrideAudience,deprecated,@grafana/partner-datasources,false,false,false prometheusAzureOverrideAudience,deprecated,@grafana/partner-datasources,false,false,false
alertingFilterV2,experimental,@grafana/alerting-squad,false,false,false alertingFilterV2,experimental,@grafana/alerting-squad,false,false,false

1 Name Stage Owner requiresDevMode RequiresRestart FrontendOnly
180 alertingApiServer experimental @grafana/alerting-squad false true false
181 dashboardRestoreUI experimental @grafana/grafana-frontend-platform false false false
182 cloudWatchRoundUpEndTime GA @grafana/aws-datasources false false false
bodyScrolling preview @grafana/grafana-frontend-platform false false true
183 cloudwatchMetricInsightsCrossAccount preview @grafana/aws-datasources false false true
184 prometheusAzureOverrideAudience deprecated @grafana/partner-datasources false false false
185 alertingFilterV2 experimental @grafana/alerting-squad false false false

View File

@ -731,10 +731,6 @@ const (
// Round up end time for metric queries to the next minute to avoid missing data // Round up end time for metric queries to the next minute to avoid missing data
FlagCloudWatchRoundUpEndTime = "cloudWatchRoundUpEndTime" FlagCloudWatchRoundUpEndTime = "cloudWatchRoundUpEndTime"
// FlagBodyScrolling
// Adjusts Page to make body the scrollable element
FlagBodyScrolling = "bodyScrolling"
// FlagCloudwatchMetricInsightsCrossAccount // FlagCloudwatchMetricInsightsCrossAccount
// Enables cross account observability for Cloudwatch Metric Insights query builder // Enables cross account observability for Cloudwatch Metric Insights query builder
FlagCloudwatchMetricInsightsCrossAccount = "cloudwatchMetricInsightsCrossAccount" FlagCloudwatchMetricInsightsCrossAccount = "cloudwatchMetricInsightsCrossAccount"

View File

@ -595,6 +595,7 @@
"name": "bodyScrolling", "name": "bodyScrolling",
"resourceVersion": "1721723807004", "resourceVersion": "1721723807004",
"creationTimestamp": "2024-07-01T10:28:39Z", "creationTimestamp": "2024-07-01T10:28:39Z",
"deletionTimestamp": "2024-09-24T09:17:00Z",
"annotations": { "annotations": {
"grafana.app/updatedTimestamp": "2024-07-23 08:36:47.004393 +0000 UTC" "grafana.app/updatedTimestamp": "2024-07-23 08:36:47.004393 +0000 UTC"
} }

View File

@ -3,7 +3,7 @@ import classNames from 'classnames';
import { PropsWithChildren, useEffect } from 'react'; import { PropsWithChildren, useEffect } from 'react';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { config, locationSearchToObject, locationService } from '@grafana/runtime'; import { locationSearchToObject, locationService } from '@grafana/runtime';
import { useStyles2, LinkButton, useTheme2 } from '@grafana/ui'; import { useStyles2, LinkButton, useTheme2 } from '@grafana/ui';
import { useGrafana } from 'app/core/context/GrafanaContext'; import { useGrafana } from 'app/core/context/GrafanaContext';
import { useMediaQueryChange } from 'app/core/hooks/useMediaQueryChange'; import { useMediaQueryChange } from 'app/core/hooks/useMediaQueryChange';
@ -121,10 +121,8 @@ export function AppChrome({ children }: Props) {
)} )}
<main <main
className={cx(styles.pageContainer, { className={cx(styles.pageContainer, {
[styles.pageContainerMenuDocked]: [styles.pageContainerMenuDocked]: menuDockedAndOpen || isScopesDashboardsOpen,
config.featureToggles.bodyScrolling && (menuDockedAndOpen || isScopesDashboardsOpen), [styles.pageContainerMenuDockedScopes]: menuDockedAndOpen && isScopesDashboardsOpen,
[styles.pageContainerMenuDockedScopes]:
config.featureToggles.bodyScrolling && menuDockedAndOpen && isScopesDashboardsOpen,
})} })}
id="pageContent" id="pageContent"
> >
@ -148,7 +146,7 @@ const getStyles = (theme: GrafanaTheme2, searchBarHidden: boolean) => {
flexDirection: 'column', flexDirection: 'column',
paddingTop: TOP_BAR_LEVEL_HEIGHT * 2, paddingTop: TOP_BAR_LEVEL_HEIGHT * 2,
flexGrow: 1, flexGrow: 1,
height: config.featureToggles.bodyScrolling ? 'auto' : '100%', height: 'auto',
}), }),
contentNoSearchBar: css({ contentNoSearchBar: css({
paddingTop: TOP_BAR_LEVEL_HEIGHT, paddingTop: TOP_BAR_LEVEL_HEIGHT,
@ -167,27 +165,17 @@ const getStyles = (theme: GrafanaTheme2, searchBarHidden: boolean) => {
display: 'block', display: 'block',
}, },
}, },
config.featureToggles.bodyScrolling {
? { position: 'fixed',
position: 'fixed', height: `calc(100% - ${searchBarHidden ? TOP_BAR_LEVEL_HEIGHT : TOP_BAR_LEVEL_HEIGHT * 2}px)`,
height: `calc(100% - ${searchBarHidden ? TOP_BAR_LEVEL_HEIGHT : TOP_BAR_LEVEL_HEIGHT * 2}px)`, zIndex: 2,
zIndex: 2, }
}
: {
zIndex: theme.zIndex.navbarFixed,
}
),
scopesDashboardsContainer: css(
config.featureToggles.bodyScrolling
? {
position: 'fixed',
height: `calc(100% - ${searchBarHidden ? TOP_BAR_LEVEL_HEIGHT : TOP_BAR_LEVEL_HEIGHT * 2}px)`,
zIndex: 1,
}
: {
zIndex: theme.zIndex.navbarFixed,
}
), ),
scopesDashboardsContainer: css({
position: 'fixed',
height: `calc(100% - ${searchBarHidden ? TOP_BAR_LEVEL_HEIGHT : TOP_BAR_LEVEL_HEIGHT * 2}px)`,
zIndex: 1,
}),
scopesDashboardsContainerDocked: css({ scopesDashboardsContainerDocked: css({
left: MENU_WIDTH, left: MENU_WIDTH,
}), }),
@ -200,49 +188,24 @@ const getStyles = (theme: GrafanaTheme2, searchBarHidden: boolean) => {
background: theme.colors.background.primary, background: theme.colors.background.primary,
flexDirection: 'column', flexDirection: 'column',
}), }),
panes: css( panes: css({
{ display: 'flex',
display: 'flex', flexDirection: 'column',
flexDirection: 'column', flexGrow: 1,
flexGrow: 1, label: 'page-panes',
label: 'page-panes', }),
},
!config.featureToggles.bodyScrolling && {
height: '100%',
minHeight: 0,
width: '100%',
[theme.breakpoints.up('md')]: {
flexDirection: 'row',
},
}
),
pageContainerMenuDocked: css({ pageContainerMenuDocked: css({
paddingLeft: MENU_WIDTH, paddingLeft: MENU_WIDTH,
}), }),
pageContainerMenuDockedScopes: css({ pageContainerMenuDockedScopes: css({
paddingLeft: `calc(${MENU_WIDTH} * 2)`, paddingLeft: `calc(${MENU_WIDTH} * 2)`,
}), }),
pageContainer: css( pageContainer: css({
{ label: 'page-container',
label: 'page-container', display: 'flex',
display: 'flex', flexDirection: 'column',
flexDirection: 'column', flexGrow: 1,
flexGrow: 1, }),
},
!config.featureToggles.bodyScrolling && {
minHeight: 0,
minWidth: 0,
overflow: 'auto',
'@media print': {
overflow: 'visible',
},
'@page': {
margin: 0,
size: 'auto',
padding: 0,
},
}
),
skipLink: css({ skipLink: css({
position: 'fixed', position: 'fixed',
top: -1000, top: -1000,

View File

@ -3,7 +3,6 @@ import { useEffect, useState } from 'react';
import * as React from 'react'; import * as React from 'react';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { config } from '@grafana/runtime';
import { useStyles2 } from '@grafana/ui'; import { useStyles2 } from '@grafana/ui';
import { Branding } from '../Branding/Branding'; import { Branding } from '../Branding/Branding';
@ -37,9 +36,7 @@ export const LoginLayout = ({ children, branding, isChangingPassword }: React.Pr
return ( return (
<Branding.LoginBackground <Branding.LoginBackground
className={cx(loginStyles.container, startAnim && loginStyles.loginAnim, branding?.loginBackground, { className={cx(loginStyles.container, startAnim && loginStyles.loginAnim, branding?.loginBackground)}
[loginStyles.containerBodyScrolling]: config.featureToggles.bodyScrolling,
})}
> >
<div className={loginStyles.loginMain}> <div className={loginStyles.loginMain}>
<div className={cx(loginStyles.loginContent, loginBoxBackground, 'login-content-box')}> <div className={cx(loginStyles.loginContent, loginBoxBackground, 'login-content-box')}>
@ -89,6 +86,7 @@ export const getLoginStyles = (theme: GrafanaTheme2) => {
minHeight: '100%', minHeight: '100%',
backgroundPosition: 'center', backgroundPosition: 'center',
backgroundRepeat: 'no-repeat', backgroundRepeat: 'no-repeat',
flex: 1,
minWidth: '100%', minWidth: '100%',
marginLeft: 0, marginLeft: 0,
display: 'flex', display: 'flex',
@ -96,9 +94,6 @@ export const getLoginStyles = (theme: GrafanaTheme2) => {
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
}), }),
containerBodyScrolling: css({
flex: 1,
}),
loginAnim: css({ loginAnim: css({
['&:before']: { ['&:before']: {
opacity: 1, opacity: 1,

View File

@ -1,9 +1,4 @@
import { css, cx } from '@emotion/css';
import { useEffect, useRef } from 'react'; import { useEffect, useRef } from 'react';
import { config } from '@grafana/runtime';
import { useStyles2 } from '@grafana/ui';
export interface Props { export interface Props {
children: React.ReactNode; children: React.ReactNode;
onSetScrollRef?: (ref: ScrollRefElement) => void; onSetScrollRef?: (ref: ScrollRefElement) => void;
@ -16,30 +11,16 @@ export interface ScrollRefElement {
} }
// Shim to provide API-compatibility for Page's scroll-related props // Shim to provide API-compatibility for Page's scroll-related props
// when bodyScrolling is enabled, this is a no-op
// TODO remove this shim completely when bodyScrolling is enabled
export default function NativeScrollbar({ children, onSetScrollRef, divId }: Props) { export default function NativeScrollbar({ children, onSetScrollRef, divId }: Props) {
const styles = useStyles2(getStyles);
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
useEffect(() => { useEffect(() => {
if (config.featureToggles.bodyScrolling && onSetScrollRef) { if (onSetScrollRef) {
onSetScrollRef(new DivScrollElement(document.documentElement)); onSetScrollRef(new DivScrollElement(document.documentElement));
} }
if (!config.featureToggles.bodyScrolling && ref.current && onSetScrollRef) {
onSetScrollRef(new DivScrollElement(ref.current));
}
}, [ref, onSetScrollRef]); }, [ref, onSetScrollRef]);
return config.featureToggles.bodyScrolling ? ( return children;
children
) : (
// Set the .scrollbar-view class to help e2e tests find this, like in CustomScrollbar
<div ref={ref} className={cx(styles.nativeScrollbars, 'scrollbar-view')} id={divId}>
{children}
</div>
);
} }
class DivScrollElement { class DivScrollElement {
@ -61,17 +42,3 @@ class DivScrollElement {
this.element.scrollTo(x, y); this.element.scrollTo(x, y);
} }
} }
function getStyles() {
return {
nativeScrollbars: css({
label: 'native-scroll-container',
minHeight: `calc(100% + 0px)`, // I don't know, just copied from custom scrollbars
maxHeight: `calc(100% + 0px)`, // I don't know, just copied from custom scrollbars
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflow: 'auto',
}),
};
}

View File

@ -2,7 +2,6 @@ import { css, cx } from '@emotion/css';
import { useLayoutEffect } from 'react'; import { useLayoutEffect } from 'react';
import { GrafanaTheme2, PageLayoutType } from '@grafana/data'; import { GrafanaTheme2, PageLayoutType } from '@grafana/data';
import { config } from '@grafana/runtime';
import { useStyles2 } from '@grafana/ui'; import { useStyles2 } from '@grafana/ui';
import { useGrafana } from 'app/core/context/GrafanaContext'; import { useGrafana } from 'app/core/context/GrafanaContext';
@ -94,24 +93,13 @@ Page.Contents = PageContents;
const getStyles = (theme: GrafanaTheme2) => { const getStyles = (theme: GrafanaTheme2) => {
return { return {
wrapper: css( wrapper: css({
config.featureToggles.bodyScrolling label: 'page-wrapper',
? { display: 'flex',
label: 'page-wrapper', flex: '1 1 0',
display: 'flex', flexDirection: 'column',
flex: '1 1 0', position: 'relative',
flexDirection: 'column', }),
position: 'relative',
}
: {
label: 'page-wrapper',
height: '100%',
display: 'flex',
flex: '1 1 0',
flexDirection: 'column',
minHeight: 0,
}
),
pageContent: css({ pageContent: css({
label: 'page-content', label: 'page-content',
flexGrow: 1, flexGrow: 1,

View File

@ -23,11 +23,7 @@ export interface PageProps extends HTMLAttributes<HTMLDivElement> {
subTitle?: React.ReactNode; subTitle?: React.ReactNode;
/** Control the page layout. */ /** Control the page layout. */
layout?: PageLayoutType; layout?: PageLayoutType;
/** /** Can be used to get the scroll container element to access scroll position */
* TODO: Not sure we should deprecated it given the sidecar project?
* @deprecated this will be removed when bodyScrolling is enabled by default
* Can be used to get the scroll container element to access scroll position
* */
onSetScrollRef?: (ref: ScrollRefElement) => void; onSetScrollRef?: (ref: ScrollRefElement) => void;
} }

View File

@ -1,7 +1,7 @@
import { createContext, useCallback, useContext } from 'react'; import { createContext, useCallback, useContext } from 'react';
import { GrafanaConfig } from '@grafana/data'; import { GrafanaConfig } from '@grafana/data';
import { LocationService, locationService, BackendSrv, config } from '@grafana/runtime'; import { LocationService, locationService, BackendSrv } from '@grafana/runtime';
import { AppChromeService } from '../components/AppChrome/AppChromeService'; import { AppChromeService } from '../components/AppChrome/AppChromeService';
import { NewFrontendAssetsChecker } from '../services/NewFrontendAssetsChecker'; import { NewFrontendAssetsChecker } from '../services/NewFrontendAssetsChecker';
@ -48,7 +48,7 @@ export function useChromeHeaderHeight() {
const { chrome } = useGrafana(); const { chrome } = useGrafana();
const { kioskMode, searchBarHidden, chromeless } = chrome.useState(); const { kioskMode, searchBarHidden, chromeless } = chrome.useState();
if (kioskMode || chromeless || !config.featureToggles.bodyScrolling) { if (kioskMode || chromeless) {
return 0; return 0;
} else if (searchBarHidden) { } else if (searchBarHidden) {
return SINGLE_HEADER_BAR_HEIGHT; return SINGLE_HEADER_BAR_HEIGHT;

View File

@ -1,7 +1,6 @@
import { css, cx } from '@emotion/css'; import { css } from '@emotion/css';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { config } from '@grafana/runtime';
import { useStyles2 } from '@grafana/ui'; import { useStyles2 } from '@grafana/ui';
import { BouncingLoader } from '../components/BouncingLoader/BouncingLoader'; import { BouncingLoader } from '../components/BouncingLoader/BouncingLoader';
@ -10,12 +9,7 @@ export function GrafanaRouteLoading() {
const styles = useStyles2(getStyles); const styles = useStyles2(getStyles);
return ( return (
<div <div className={styles.loadingPage}>
className={cx({
[styles.loadingPage]: !config.featureToggles.bodyScrolling,
[styles.loadingPageBodyScrolling]: config.featureToggles.bodyScrolling,
})}
>
<BouncingLoader /> <BouncingLoader />
</div> </div>
); );
@ -23,14 +17,6 @@ export function GrafanaRouteLoading() {
const getStyles = (theme: GrafanaTheme2) => ({ const getStyles = (theme: GrafanaTheme2) => ({
loadingPage: css({ loadingPage: css({
backgroundColor: theme.colors.background.primary,
height: '100%',
flexDrection: 'column',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}),
loadingPageBodyScrolling: css({
backgroundColor: theme.colors.background.primary, backgroundColor: theme.colors.background.primary,
flex: 1, flex: 1,
flexDrection: 'column', flexDrection: 'column',

View File

@ -2,7 +2,7 @@ import { css } from '@emotion/css';
import * as React from 'react'; import * as React from 'react';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { config, useChromeHeaderHeight } from '@grafana/runtime'; import { useChromeHeaderHeight } from '@grafana/runtime';
import { Icon, Input, useStyles2 } from '@grafana/ui'; import { Icon, Input, useStyles2 } from '@grafana/ui';
import { t } from 'app/core/internationalization'; import { t } from 'app/core/internationalization';
@ -28,7 +28,7 @@ export interface Props {
export const Search = ({ onChange, value }: Props) => { export const Search = ({ onChange, value }: Props) => {
const chromeHeaderHeight = useChromeHeaderHeight(); const chromeHeaderHeight = useChromeHeaderHeight();
const styles = useStyles2(getStyles, config.featureToggles.bodyScrolling ? (chromeHeaderHeight ?? 0) : 0); const styles = useStyles2(getStyles, chromeHeaderHeight ?? 0);
return ( return (
<div className={styles.searchContainer}> <div className={styles.searchContainer}>

View File

@ -2,7 +2,6 @@ import { css, cx } from '@emotion/css';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors'; import { selectors } from '@grafana/e2e-selectors';
import { config } from '@grafana/runtime';
import { SceneComponentProps } from '@grafana/scenes'; import { SceneComponentProps } from '@grafana/scenes';
import { Button, ToolbarButton, useStyles2 } from '@grafana/ui'; import { Button, ToolbarButton, useStyles2 } from '@grafana/ui';
@ -35,9 +34,7 @@ export function PanelEditorRenderer({ model }: SceneComponentProps<PanelEditor>)
<NavToolbarActions dashboard={dashboard} /> <NavToolbarActions dashboard={dashboard} />
<div <div
{...containerProps} {...containerProps}
className={cx(containerProps.className, { className={cx(containerProps.className, styles.content)}
[styles.content]: config.featureToggles.bodyScrolling,
})}
data-testid={selectors.components.PanelEditor.General.content} data-testid={selectors.components.PanelEditor.General.content}
> >
<div {...primaryProps} className={cx(primaryProps.className, styles.body)}> <div {...primaryProps} className={cx(primaryProps.className, styles.body)}>

View File

@ -1,4 +1,4 @@
import { css, cx } from '@emotion/css'; import { css } from '@emotion/css';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { AppPlugin, GrafanaTheme2, PluginContextProvider, UrlQueryMap } from '@grafana/data'; import { AppPlugin, GrafanaTheme2, PluginContextProvider, UrlQueryMap } from '@grafana/data';
@ -45,7 +45,7 @@ export function PluginDetailsBody({ plugin, queryParams, pageId }: Props): JSX.E
if (pageId === PluginTabIds.OVERVIEW) { if (pageId === PluginTabIds.OVERVIEW) {
return ( return (
<div <div
className={cx(styles.readme, styles.container)} className={styles.readme}
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: plugin.details?.readme ?? 'No plugin help or readme markdown file was found', __html: plugin.details?.readme ?? 'No plugin help or readme markdown file was found',
}} }}
@ -55,7 +55,7 @@ export function PluginDetailsBody({ plugin, queryParams, pageId }: Props): JSX.E
if (pageId === PluginTabIds.VERSIONS) { if (pageId === PluginTabIds.VERSIONS) {
return ( return (
<div className={styles.container}> <div>
<VersionList versions={plugin.details?.versions} installedVersion={plugin.installedVersion} /> <VersionList versions={plugin.details?.versions} installedVersion={plugin.installedVersion} />
</div> </div>
); );
@ -67,7 +67,7 @@ export function PluginDetailsBody({ plugin, queryParams, pageId }: Props): JSX.E
if (pageId === PluginTabIds.CONFIG && pluginConfig?.angularConfigCtrl) { if (pageId === PluginTabIds.CONFIG && pluginConfig?.angularConfigCtrl) {
return ( return (
<div className={styles.container}> <div>
<AppConfigCtrlWrapper app={pluginConfig as AppPlugin} /> <AppConfigCtrlWrapper app={pluginConfig as AppPlugin} />
</div> </div>
); );
@ -102,7 +102,7 @@ export function PluginDetailsBody({ plugin, queryParams, pageId }: Props): JSX.E
for (const configPage of pluginConfig.configPages) { for (const configPage of pluginConfig.configPages) {
if (pageId === configPage.id) { if (pageId === configPage.id) {
return ( return (
<div className={styles.container}> <div>
<PluginContextProvider meta={pluginConfig.meta}> <PluginContextProvider meta={pluginConfig.meta}>
<configPage.body plugin={pluginConfig} query={queryParams} /> <configPage.body plugin={pluginConfig} query={queryParams} />
</PluginContextProvider> </PluginContextProvider>
@ -114,7 +114,7 @@ export function PluginDetailsBody({ plugin, queryParams, pageId }: Props): JSX.E
if (pageId === PluginTabIds.USAGE && pluginConfig) { if (pageId === PluginTabIds.USAGE && pluginConfig) {
return ( return (
<div className={styles.container}> <div>
<PluginUsage plugin={pluginConfig?.meta} /> <PluginUsage plugin={pluginConfig?.meta} />
</div> </div>
); );
@ -122,25 +122,20 @@ export function PluginDetailsBody({ plugin, queryParams, pageId }: Props): JSX.E
if (pageId === PluginTabIds.DASHBOARDS && pluginConfig) { if (pageId === PluginTabIds.DASHBOARDS && pluginConfig) {
return ( return (
<div className={styles.container}> <div>
<PluginDashboards plugin={pluginConfig?.meta} /> <PluginDashboards plugin={pluginConfig?.meta} />
</div> </div>
); );
} }
return ( return (
<div className={styles.container}> <div>
<p>Page not found.</p> <p>Page not found.</p>
</div> </div>
); );
} }
export const getStyles = (theme: GrafanaTheme2) => ({ export const getStyles = (theme: GrafanaTheme2) => ({
container: config.featureToggles.bodyScrolling
? css({})
: css({
height: '100%',
}),
readme: css({ readme: css({
'& img': { '& img': {
maxWidth: '100%', maxWidth: '100%',

View File

@ -113,15 +113,9 @@ export const getStyles = (theme: GrafanaTheme2) => {
gap: theme.spacing(1), gap: theme.spacing(1),
}), }),
// Needed due to block formatting context // Needed due to block formatting context
tabContent: config.featureToggles.bodyScrolling tabContent: css({
? css({ paddingLeft: '5px',
paddingLeft: '5px', }),
})
: css({
overflow: 'auto',
height: '100%',
paddingLeft: '5px',
}),
}; };
}; };