@ -1,12 +1,11 @@
import { css , cx } from '@emotion/css' ;
import { useLocation } from 'react-router-dom' ;
import { useMedia } from 'react-use' ;
import { GrafanaTheme2 , PageLayoutType } from '@grafana/data' ;
import { selectors } from '@grafana/e2e-selectors' ;
import { config } from '@grafana/runtime' ;
import { useChromeHeaderHeight } from '@grafana/runtime' ;
import { SceneComponentProps } from '@grafana/scenes' ;
import { CustomScrollbar , useStyles2 , useTheme2 } from '@grafana/ui' ;
import { useStyles2 } from '@grafana/ui' ;
import NativeScrollbar from 'app/core/components/NativeScrollbar' ;
import { Page } from 'app/core/components/Page/Page' ;
import { EntityNotFound } from 'app/core/components/PageNotFound/EntityNotFound' ;
import { getNavModel } from 'app/core/selectors/navModel' ;
@ -18,7 +17,8 @@ import { NavToolbarActions } from './NavToolbarActions';
export function DashboardSceneRenderer ( { model } : SceneComponentProps < DashboardScene > ) {
const { controls , overlay , editview , editPanel , isEmpty , meta } = model . useState ( ) ;
const styles = useStyles2 ( getStyles ) ;
const headerHeight = useChromeHeaderHeight ( ) ;
const styles = useStyles2 ( getStyles , headerHeight ) ;
const location = useLocation ( ) ;
const navIndex = useSelector ( ( state ) = > state . navIndex ) ;
const pageNav = model . getPageNav ( location , navIndex ) ;
@ -59,6 +59,7 @@ export function DashboardSceneRenderer({ model }: SceneComponentProps<DashboardS
< Page navModel = { navModel } pageNav = { pageNav } layout = { PageLayoutType . Custom } >
{ editPanel && < editPanel.Component model = { editPanel } / > }
{ ! editPanel && (
< NativeScrollbar divId = "page-scrollbar" autoHeightMin = { '100%' } >
< div className = { cx ( styles . pageContainer , hasControls && styles . pageContainerWithControls ) } >
< NavToolbarActions dashboard = { model } / >
{ controls && (
@ -66,50 +67,35 @@ export function DashboardSceneRenderer({ model }: SceneComponentProps<DashboardS
< controls.Component model = { controls } / >
< / div >
) }
< PanelsContainer
// This id is used by the image renderer to scroll through the dashboard
id = "page-scrollbar"
className = { styles . panelsContainer }
testId = { selectors . pages . Dashboard . DashNav . scrollContainer }
>
< div className = { cx ( styles . canvasContent ) } > { body } < / div >
< / PanelsContainer >
< / div >
< / NativeScrollbar >
) }
{ overlay && < overlay.Component model = { overlay } / > }
< / Page >
) ;
}
function getStyles ( theme : GrafanaTheme2 ) {
function getStyles ( theme : GrafanaTheme2 , headerHeight : number | undefined ) {
return {
pageContainer : css (
{
pageContainer : css ( {
display : 'grid' ,
gridTemplateAreas : `
"panels" ` ,
gridTemplateColumns : ` 1fr ` ,
gridTemplateRows : '1fr' ,
height : '100%' ,
flexGrow : 1 ,
[ theme . breakpoints . down ( 'sm' ) ] : {
display : 'flex' ,
flexDirection : 'column' ,
} ,
} ,
config . featureToggles . bodyScrolling && {
position : 'absolute' ,
width : '100%' ,
}
) ,
} ) ,
pageContainerWithControls : css ( {
gridTemplateAreas : `
"controls"
"panels" ` ,
gridTemplateRows : 'auto 1fr' ,
} ) ,
panelsContainer : css ( {
gridArea : 'panels' ,
} ) ,
controlsWrapper : css ( {
display : 'flex' ,
flexDirection : 'column' ,
@ -119,6 +105,13 @@ function getStyles(theme: GrafanaTheme2) {
':empty' : {
display : 'none' ,
} ,
// Make controls sticky on larger screens (> mobile)
[ theme . breakpoints . up ( 'md' ) ] : {
position : 'sticky' ,
zIndex : theme.zIndex.activePanel ,
background : theme.colors.background.canvas ,
top : headerHeight ,
} ,
} ) ,
canvasContent : css ( {
label : 'canvas-content' ,
@ -126,7 +119,9 @@ function getStyles(theme: GrafanaTheme2) {
flexDirection : 'column' ,
padding : theme.spacing ( 0 , 2 ) ,
flexBasis : '100%' ,
gridArea : 'panels' ,
flexGrow : 1 ,
minWidth : 0 ,
} ) ,
body : css ( {
label : 'body' ,
@ -141,34 +136,3 @@ function getStyles(theme: GrafanaTheme2) {
} ) ,
} ;
}
interface PanelsContainerProps {
id : string ;
children : React.ReactNode ;
className? : string ;
testId? : string ;
}
/ * *
* Removes the scrollbar on mobile and uses a custom scrollbar on desktop
* /
const PanelsContainer = ( { id , children , className , testId } : PanelsContainerProps ) = > {
const theme = useTheme2 ( ) ;
const isMobile = useMedia ( ` (max-width: ${ theme . breakpoints . values . sm } px) ` ) ;
const styles = useStyles2 ( ( ) = > ( {
nonScrollable : css ( {
height : '100%' ,
display : 'flex' ,
flexDirection : 'column' ,
} ) ,
} ) ) ;
return isMobile ? (
< div id = { id } className = { cx ( className , styles . nonScrollable ) } data - testid = { testId } >
{ children }
< / div >
) : (
< CustomScrollbar divId = { id } autoHeightMin = { '100%' } className = { className } testId = { testId } >
{ children }
< / CustomScrollbar >
) ;
} ;