Dashboard: Hide sidebar in kiosk mode (#115387)
* Dashboard: Hide sidebar in kiosk mode * Fix kiosk url sync issues * Fixes * fixes * Update
This commit is contained in:
@@ -1817,7 +1817,7 @@
|
||||
},
|
||||
"public/app/features/dashboard-scene/edit-pane/DashboardEditPaneSplitter.tsx": {
|
||||
"react-hooks/rules-of-hooks": {
|
||||
"count": 4
|
||||
"count": 5
|
||||
}
|
||||
},
|
||||
"public/app/features/dashboard-scene/inspect/HelpWizard/HelpWizard.tsx": {
|
||||
|
||||
@@ -97,7 +97,13 @@ export const Dropdown = React.memo(({ children, overlay, placement, offset, root
|
||||
see https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/no-static-element-interactions.md#case-the-event-handler-is-only-being-used-to-capture-bubbled-events
|
||||
*/}
|
||||
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */}
|
||||
<div ref={refs.setFloating} style={floatingStyles} onClick={onOverlayClicked} onKeyDown={handleKeys}>
|
||||
<div
|
||||
ref={refs.setFloating}
|
||||
style={floatingStyles}
|
||||
onClick={onOverlayClicked}
|
||||
onKeyDown={handleKeys}
|
||||
{...getFloatingProps()}
|
||||
>
|
||||
<CSSTransition
|
||||
nodeRef={transitionRef}
|
||||
appear={true}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { css, cx } from '@emotion/css';
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useLayoutEffect } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { config, useChromeHeaderHeight } from '@grafana/runtime';
|
||||
import { useSceneObjectState } from '@grafana/scenes';
|
||||
import { ElementSelectionContext, useSidebar, useStyles2, Sidebar } from '@grafana/ui';
|
||||
import { AppChromeUpdate } from 'app/core/components/AppChrome/AppChromeUpdate';
|
||||
import NativeScrollbar, { DivScrollElement } from 'app/core/components/NativeScrollbar';
|
||||
import { useGrafana } from 'app/core/context/GrafanaContext';
|
||||
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
||||
import { KioskMode } from 'app/types/dashboard';
|
||||
|
||||
import { DashboardScene } from '../scene/DashboardScene';
|
||||
import { NavToolbarActions } from '../scene/NavToolbarActions';
|
||||
@@ -29,10 +30,9 @@ export function DashboardEditPaneSplitter({ dashboard, isEditing, body, controls
|
||||
const headerHeight = useChromeHeaderHeight();
|
||||
const { editPane } = dashboard.state;
|
||||
const styles = useStyles2(getStyles, headerHeight ?? 0);
|
||||
const hasUid = Boolean(dashboard.state.uid);
|
||||
const canStar = Boolean(dashboard.state.meta.canStar);
|
||||
|
||||
//const [isCollapsed, setIsCollapsed] = useEditPaneCollapsed();
|
||||
const { chrome } = useGrafana();
|
||||
const { kioskMode } = chrome.useState();
|
||||
const isInKioskMode = kioskMode === KioskMode.Full;
|
||||
|
||||
if (!config.featureToggles.dashboardNewLayouts) {
|
||||
return (
|
||||
@@ -46,6 +46,11 @@ export function DashboardEditPaneSplitter({ dashboard, isEditing, body, controls
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds star button and left side actions to app chrome breadcrumb area
|
||||
*/
|
||||
useUpdateAppChromeActions(dashboard);
|
||||
|
||||
/**
|
||||
* Enable / disable selection based on dashboard isEditing state
|
||||
*/
|
||||
@@ -59,12 +64,6 @@ export function DashboardEditPaneSplitter({ dashboard, isEditing, body, controls
|
||||
|
||||
const { selectionContext, openPane } = useSceneObjectState(editPane, { shouldActivateOrKeepAlive: true });
|
||||
|
||||
const onBodyRef = (ref: HTMLDivElement | null) => {
|
||||
if (ref) {
|
||||
dashboard.onSetScrollRef(new DivScrollElement(ref));
|
||||
}
|
||||
};
|
||||
|
||||
const sidebarContext = useSidebar({
|
||||
hasOpenPane: Boolean(openPane),
|
||||
contentMargin: 1,
|
||||
@@ -88,39 +87,77 @@ export function DashboardEditPaneSplitter({ dashboard, isEditing, body, controls
|
||||
editPane.clearSelection();
|
||||
};
|
||||
|
||||
const onBodyRef = (ref: HTMLDivElement | null) => {
|
||||
if (ref) {
|
||||
dashboard.onSetScrollRef(new DivScrollElement(ref));
|
||||
}
|
||||
};
|
||||
|
||||
function renderBody() {
|
||||
// In kiosk mode the full document body scrolls so we don't need to wrap in our own scrollbar
|
||||
if (isInKioskMode) {
|
||||
return (
|
||||
<div
|
||||
className={cx(styles.bodyWrapper, styles.bodyWrapperKiosk)}
|
||||
data-testid={selectors.components.DashboardEditPaneSplitter.primaryBody}
|
||||
>
|
||||
<NativeScrollbar onSetScrollRef={dashboard.onSetScrollRef}>{body}</NativeScrollbar>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.bodyWrapper}
|
||||
data-testid={selectors.components.DashboardEditPaneSplitter.primaryBody}
|
||||
{...sidebarContext.outerWrapperProps}
|
||||
>
|
||||
<div className={styles.scrollContainer} ref={onBodyRef} onPointerDown={onClearSelection}>
|
||||
{body}
|
||||
</div>
|
||||
|
||||
<Sidebar contextValue={sidebarContext}>
|
||||
<DashboardEditPaneRenderer editPane={editPane} dashboard={dashboard} isDocked={sidebarContext.isDocked} />
|
||||
</Sidebar>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<ElementSelectionContext.Provider value={selectionContext}>
|
||||
<AppChromeUpdate
|
||||
breadcrumbActions={
|
||||
<>
|
||||
{hasUid && canStar && <StarButton dashboard={dashboard} />}
|
||||
{hasUid && canStar && <PublicDashboardBadge dashboard={dashboard} />}
|
||||
{renderDynamicNavActions()}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<div className={cx(styles.controlsWrapperSticky)} onPointerDown={onClearSelection}>
|
||||
<div className={styles.controlsWrapperSticky} onPointerDown={onClearSelection}>
|
||||
{controls}
|
||||
</div>
|
||||
<div className={styles.bodyWrapper} {...sidebarContext.outerWrapperProps}>
|
||||
<div
|
||||
className={styles.bodyWithToolbar}
|
||||
data-testid={selectors.components.DashboardEditPaneSplitter.primaryBody}
|
||||
ref={onBodyRef}
|
||||
onPointerDown={onClearSelection}
|
||||
>
|
||||
{body}
|
||||
</div>
|
||||
<Sidebar contextValue={sidebarContext}>
|
||||
<DashboardEditPaneRenderer editPane={editPane} dashboard={dashboard} isDocked={sidebarContext.isDocked} />
|
||||
</Sidebar>
|
||||
</div>
|
||||
{renderBody()}
|
||||
</ElementSelectionContext.Provider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function useUpdateAppChromeActions(dashboard: DashboardScene) {
|
||||
const { chrome } = useGrafana();
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const hasUid = Boolean(dashboard.state.uid);
|
||||
const canStar = Boolean(dashboard.state.meta.canStar);
|
||||
|
||||
const breadcrumbActions = (
|
||||
<>
|
||||
{hasUid && canStar && <StarButton dashboard={dashboard} />}
|
||||
{hasUid && canStar && <PublicDashboardBadge dashboard={dashboard} />}
|
||||
{renderDynamicNavActions()}
|
||||
</>
|
||||
);
|
||||
|
||||
chrome.update({ breadcrumbActions });
|
||||
|
||||
return () => {
|
||||
chrome.update({ breadcrumbActions: undefined });
|
||||
};
|
||||
}, [chrome, dashboard]);
|
||||
}
|
||||
|
||||
function renderDynamicNavActions() {
|
||||
const dashboard = getDashboardSrv().getCurrent()!;
|
||||
const showProps = { dashboard };
|
||||
@@ -152,13 +189,17 @@ function getStyles(theme: GrafanaTheme2, headerHeight: number) {
|
||||
bodyWrapper: css({
|
||||
label: 'body-wrapper',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
flexDirection: 'column',
|
||||
flexGrow: 1,
|
||||
position: 'relative',
|
||||
flex: '1 1 0',
|
||||
overflow: 'hidden',
|
||||
}),
|
||||
bodyWithToolbar: css({
|
||||
bodyWrapperKiosk: css({
|
||||
padding: theme.spacing(0, 2, 2, 2),
|
||||
overflow: 'unset',
|
||||
}),
|
||||
scrollContainer: css({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexGrow: 1,
|
||||
|
||||
@@ -32,7 +32,7 @@ import { PanelModel } from 'app/features/dashboard/state/PanelModel';
|
||||
import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher';
|
||||
import { DashboardJson } from 'app/features/manage-dashboards/types';
|
||||
import { VariablesChanged } from 'app/features/variables/types';
|
||||
import { DashboardDTO, DashboardMeta, KioskMode, SaveDashboardResponseDTO } from 'app/types/dashboard';
|
||||
import { DashboardDTO, DashboardMeta, SaveDashboardResponseDTO } from 'app/types/dashboard';
|
||||
import { ShowConfirmModalEvent } from 'app/types/events';
|
||||
|
||||
import {
|
||||
@@ -140,8 +140,6 @@ export interface DashboardSceneState extends SceneObjectState {
|
||||
editPanel?: PanelEditor;
|
||||
/** Scene object that handles the current drawer or modal */
|
||||
overlay?: SceneObject;
|
||||
/** Kiosk mode */
|
||||
kioskMode?: KioskMode;
|
||||
/** Share view */
|
||||
shareView?: string;
|
||||
/** Renders panels in grid and filtered */
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { SceneQueryRunner, VizPanel } from '@grafana/scenes';
|
||||
import { KioskMode } from 'app/types/dashboard';
|
||||
|
||||
import { DashboardScene } from './DashboardScene';
|
||||
import { DefaultGridLayoutManager } from './layout-default/DefaultGridLayoutManager';
|
||||
@@ -22,25 +21,6 @@ describe('DashboardSceneUrlSync', () => {
|
||||
layout.state.grid.setState({ UNSAFE_fitPanels: true });
|
||||
expect(scene.urlSync?.getUrlState().autofitpanels).toBe('true');
|
||||
});
|
||||
|
||||
it('Should set kiosk mode when url has kiosk', () => {
|
||||
const scene = buildTestScene();
|
||||
|
||||
scene.urlSync?.updateFromUrl({ kiosk: 'invalid' });
|
||||
expect(scene.state.kioskMode).toBe(undefined);
|
||||
scene.urlSync?.updateFromUrl({ kiosk: '' });
|
||||
expect(scene.state.kioskMode).toBe(KioskMode.Full);
|
||||
scene.urlSync?.updateFromUrl({ kiosk: 'true' });
|
||||
expect(scene.state.kioskMode).toBe(KioskMode.Full);
|
||||
});
|
||||
|
||||
it('Should get the kiosk mode from the scene state', () => {
|
||||
const scene = buildTestScene();
|
||||
|
||||
expect(scene.urlSync?.getUrlState().kiosk).toBe(undefined);
|
||||
scene.setState({ kioskMode: KioskMode.Full });
|
||||
expect(scene.urlSync?.getUrlState().kiosk).toBe('true');
|
||||
});
|
||||
});
|
||||
|
||||
describe('entering edit mode', () => {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { SceneObjectUrlSyncHandler, SceneObjectUrlValues, VizPanel } from '@grafana/scenes';
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
import { KioskMode } from 'app/types/dashboard';
|
||||
|
||||
import { buildPanelEditScene } from '../panel-edit/PanelEditor';
|
||||
import { createDashboardEditViewFor } from '../settings/utils';
|
||||
@@ -15,7 +14,7 @@ export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler {
|
||||
constructor(private _scene: DashboardScene) {}
|
||||
|
||||
getKeys(): string[] {
|
||||
return ['inspect', 'viewPanel', 'editPanel', 'editview', 'autofitpanels', 'kiosk', 'shareView'];
|
||||
return ['inspect', 'viewPanel', 'editPanel', 'editview', 'autofitpanels', 'shareView'];
|
||||
}
|
||||
|
||||
getUrlState(): SceneObjectUrlValues {
|
||||
@@ -26,7 +25,6 @@ export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler {
|
||||
viewPanel: state.viewPanel,
|
||||
editview: state.editview?.getUrlKey(),
|
||||
editPanel: state.editPanel?.getUrlKey() || undefined,
|
||||
kiosk: state.kioskMode === KioskMode.Full ? 'true' : undefined,
|
||||
shareView: state.shareView,
|
||||
orgId: contextSrv.user.orgId.toString(),
|
||||
};
|
||||
@@ -117,12 +115,6 @@ export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler {
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof values.kiosk === 'string') {
|
||||
if (values.kiosk === 'true' || values.kiosk === '') {
|
||||
update.kioskMode = KioskMode.Full;
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(update).length > 0) {
|
||||
this._scene.setState(update);
|
||||
}
|
||||
|
||||
+5
-4
@@ -1,6 +1,6 @@
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { Button } from '@grafana/ui';
|
||||
import { ToolbarButton } from '@grafana/ui';
|
||||
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
|
||||
import { trackDashboardSceneEditButtonClicked } from 'app/features/dashboard-scene/utils/tracking';
|
||||
import { playlistSrv } from 'app/features/playlist/PlaylistSrv';
|
||||
@@ -17,10 +17,11 @@ export const EditDashboardSwitch = ({ dashboard }: ToolbarActionProps) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
<ToolbarButton
|
||||
tooltip={tooltip}
|
||||
data-testid={selectors.components.NavToolbar.editDashboard.editButton}
|
||||
variant="secondary"
|
||||
icon={dashboard.state.isEditing ? 'edit' : 'pen'}
|
||||
variant="canvas"
|
||||
onClick={(evt) => {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
@@ -37,6 +38,6 @@ export const EditDashboardSwitch = ({ dashboard }: ToolbarActionProps) => {
|
||||
{dashboard.state.isEditing
|
||||
? t('dashboard.toolbar.edit-button.exit', 'Exit edit')
|
||||
: t('dashboard.toolbar.edit-button.enter', 'Edit')}
|
||||
</Button>
|
||||
</ToolbarButton>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user