Skip to content

Commit e7c30a8

Browse files
custom dashboard checkpoint
1 parent f633be4 commit e7c30a8

File tree

12 files changed

+268
-112
lines changed

12 files changed

+268
-112
lines changed

packages/grafana-runtime/src/services/LocationService.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export interface LocationService {
2121
getHistory: () => H.History;
2222
getSearch: () => URLSearchParams;
2323
getSearchObject: () => UrlQueryMap;
24+
fnPathnameChange: (path: string, queryParams: any) => void;
2425

2526
/**
2627
* This is from the old LocationSrv interface

public/app/core/components/Page/Page.tsx

+7-19
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { css, cx } from '@emotion/css';
22
import { useLayoutEffect } from 'react';
33

44
import { GrafanaTheme2, PageLayoutType } from '@grafana/data';
5-
import { config } from '@grafana/runtime';
65
import { useStyles2 } from '@grafana/ui';
76
import { useGrafana } from 'app/core/context/GrafanaContext';
87

@@ -94,24 +93,13 @@ Page.Contents = PageContents;
9493

9594
const getStyles = (theme: GrafanaTheme2) => {
9695
return {
97-
wrapper: css(
98-
config.featureToggles.bodyScrolling
99-
? {
100-
label: 'page-wrapper',
101-
display: 'flex',
102-
flex: '1 1 0',
103-
flexDirection: 'column',
104-
position: 'relative',
105-
}
106-
: {
107-
label: 'page-wrapper',
108-
height: '100%',
109-
display: 'flex',
110-
flex: '1 1 0',
111-
flexDirection: 'column',
112-
minHeight: 0,
113-
}
114-
),
96+
wrapper: css({
97+
label: 'page-wrapper',
98+
display: 'flex',
99+
flex: '1 1 0',
100+
flexDirection: 'column',
101+
position: 'relative',
102+
}),
115103
pageContent: css({
116104
label: 'page-content',
117105
flexGrow: 1,

public/app/core/reducers/fn-slice.ts

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { AnyObject } from '../../fn-app/types';
66

77
export interface FnGlobalState {
88
FNDashboard: boolean;
9+
isCustomDashboard: boolean;
910
uid: string;
1011
slug: string;
1112
version: number;
@@ -48,6 +49,7 @@ export const FN_STATE_KEY = 'fnGlobalState';
4849
export const INITIAL_FN_STATE: FnGlobalState = {
4950
// NOTE: initial value is false
5051
FNDashboard: false,
52+
isCustomDashboard: false,
5153
uid: '',
5254
slug: '',
5355
version: 1,

public/app/features/dashboard/components/AddPanelButton/AddPanelButton.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ import AddPanelMenu from './AddPanelMenu';
1010
export interface Props {
1111
dashboard: DashboardModel;
1212
onToolbarAddMenuOpen?: () => void;
13+
isFNDashboard?: boolean;
1314
}
1415

15-
const AddPanelButton = ({ dashboard, onToolbarAddMenuOpen }: Props) => {
16+
const AddPanelButton = ({ dashboard, onToolbarAddMenuOpen, isFNDashboard }: Props) => {
1617
const [isMenuOpen, setIsMenuOpen] = useState(false);
1718

1819
useEffect(() => {
@@ -29,8 +30,8 @@ const AddPanelButton = ({ dashboard, onToolbarAddMenuOpen }: Props) => {
2930
onVisibleChange={setIsMenuOpen}
3031
>
3132
<Button
32-
variant="secondary"
33-
size="sm"
33+
variant="primary"
34+
size={isFNDashboard ? 'md' : 'sm'}
3435
fill="outline"
3536
data-testid={selectors.components.PageToolbar.itemButton('Add button')}
3637
>

public/app/features/dashboard/containers/DashboardPage.tsx

+71-24
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,25 @@
11
import { cx } from '@emotion/css';
22
import { Portal } from '@mui/material';
3-
import React, { PureComponent } from 'react';
3+
import { PureComponent } from 'react';
44
import { connect, ConnectedProps, MapDispatchToProps, MapStateToProps } from 'react-redux';
55

66
import { NavModel, NavModelItem, TimeRange, PageLayoutType, locationUtil } from '@grafana/data';
77
import { selectors } from '@grafana/e2e-selectors';
88
import { config, locationService } from '@grafana/runtime';
9-
import { Themeable2, withTheme2, ToolbarButtonRow } from '@grafana/ui';
9+
import { Themeable2, withTheme2, ToolbarButtonRow, ToolbarButton, ModalsController } from '@grafana/ui';
1010
import { notifyApp } from 'app/core/actions';
11+
import { ScrollRefElement } from 'app/core/components/NativeScrollbar';
1112
import { Page } from 'app/core/components/Page/Page';
1213
import { EntityNotFound } from 'app/core/components/PageNotFound/EntityNotFound';
1314
import { GrafanaContext, GrafanaContextType } from 'app/core/context/GrafanaContext';
1415
import { createErrorNotification } from 'app/core/copy/appNotification';
16+
import { t } from 'app/core/internationalization';
1517
import { getKioskMode } from 'app/core/navigation/kiosk';
1618
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
1719
import { FnGlobalState } from 'app/core/reducers/fn-slice';
1820
import { getNavModel } from 'app/core/selectors/navModel';
1921
import { PanelModel } from 'app/features/dashboard/state';
22+
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
2023
import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher';
2124
import { updateTimeZoneForSession } from 'app/features/profile/state/reducers';
2225
import { getPageNavFromSlug, getRootContentNavModel } from 'app/features/storage/StorageFolderPage';
@@ -26,6 +29,7 @@ import { PanelEditEnteredEvent, PanelEditExitedEvent } from 'app/types/events';
2629

2730
import { cancelVariables, templateVarsChangedInUrl } from '../../variables/state/actions';
2831
import { findTemplateVarChanges } from '../../variables/utils';
32+
import AddPanelButton from '../components/AddPanelButton/AddPanelButton';
2933
import { AddWidgetModal } from '../components/AddWidgetModal/AddWidgetModal';
3034
import { DashNav } from '../components/DashNav';
3135
import { DashNavTimeControls } from '../components/DashNav/DashNavTimeControls';
@@ -36,6 +40,7 @@ import { DashboardPrompt } from '../components/DashboardPrompt/DashboardPrompt';
3640
import { DashboardSettings } from '../components/DashboardSettings';
3741
import { PanelInspector } from '../components/Inspector/PanelInspector';
3842
import { PanelEditor } from '../components/PanelEditor/PanelEditor';
43+
import { SaveDashboardDrawer } from '../components/SaveDashboard/SaveDashboardDrawer';
3944
import { SubMenu } from '../components/SubMenu/SubMenu';
4045
import { DashboardGrid } from '../dashgrid/DashboardGrid';
4146
import { liveTimer } from '../dashgrid/liveTimer';
@@ -72,7 +77,7 @@ export type MapStateToDashboardPageProps = MapStateToProps<
7277
Pick<DashboardState, 'initPhase' | 'initError'> & {
7378
dashboard: ReturnType<DashboardState['getModel']>;
7479
navIndex: StoreState['navIndex'];
75-
} & Pick<FnGlobalState, 'FNDashboard' | 'controlsContainer'>,
80+
} & Pick<FnGlobalState, 'FNDashboard' | 'controlsContainer' | 'isCustomDashboard'>,
7681
OwnProps,
7782
StoreState
7883
>;
@@ -93,6 +98,7 @@ export const mapStateToProps: MapStateToDashboardPageProps = (state) => ({
9398
dashboard: state.dashboard.getModel(),
9499
navIndex: state.navIndex,
95100
FNDashboard: state.fnGlobalState.FNDashboard,
101+
isCustomDashboard: state.fnGlobalState.isCustomDashboard,
96102
controlsContainer: state.fnGlobalState.controlsContainer,
97103
});
98104

@@ -128,7 +134,7 @@ export interface State {
128134
showLoadingState: boolean;
129135
panelNotFound: boolean;
130136
editPanelAccessDenied: boolean;
131-
scrollElement?: HTMLDivElement;
137+
scrollElement?: ScrollRefElement;
132138
pageNav?: NavModelItem;
133139
sectionNav?: NavModel;
134140
}
@@ -152,9 +158,9 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
152158

153159
componentDidMount() {
154160
this.initDashboard();
155-
const { FNDashboard } = this.props;
161+
const { FNDashboard, isCustomDashboard } = this.props;
156162

157-
if (!FNDashboard) {
163+
if (!FNDashboard || isCustomDashboard) {
158164
this.forceRouteReloadCounter = (this.props.history.location?.state as any)?.routeReloadCounter || 0;
159165
}
160166
}
@@ -192,13 +198,13 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
192198
}
193199

194200
componentDidUpdate(prevProps: Props, prevState: State) {
195-
const { dashboard, match, templateVarsChangedInUrl, FNDashboard } = this.props;
201+
const { dashboard, match, templateVarsChangedInUrl, FNDashboard, isCustomDashboard } = this.props;
196202

197203
if (!dashboard) {
198204
return;
199205
}
200206

201-
if (!FNDashboard) {
207+
if (!FNDashboard || isCustomDashboard) {
202208
const routeReloadCounter = (this.props.history.location?.state as any)?.routeReloadCounter;
203209

204210
if (
@@ -354,7 +360,7 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
354360
this.setState({ updateScrollTop: 0 });
355361
};
356362

357-
setScrollRef = (scrollElement: HTMLDivElement): void => {
363+
setScrollRef = (scrollElement: ScrollRefElement): void => {
358364
this.setState({ scrollElement });
359365
};
360366

@@ -378,7 +384,7 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
378384
}
379385

380386
render() {
381-
const { dashboard, initError, queryParams, FNDashboard, controlsContainer } = this.props;
387+
const { dashboard, initError, queryParams, FNDashboard, controlsContainer, isCustomDashboard } = this.props;
382388
const { editPanel, viewPanel, pageNav, sectionNav } = this.state;
383389
const kioskMode = getKioskMode(this.props.queryParams);
384390

@@ -391,12 +397,22 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
391397

392398
const showToolbar = FNDashboard || (kioskMode !== KioskMode.Full && !queryParams.editview);
393399

400+
const isFNDashboardEditable = (isCustomDashboard && FNDashboard) || !FNDashboard;
401+
402+
console.log('Edit Panel: ', { editPanel, sectionNav, pageNav, isFNDashboardEditable });
403+
console.log('Dashboard settings: ', { editView: queryParams.editview, pageNav, sectionNav, isFNDashboardEditable });
404+
console.log('Add Widget: ', {
405+
isFNDashboardEditable,
406+
addWidget: queryParams.addWidget,
407+
configToggle: config.featureToggles.vizAndWidgetSplit,
408+
});
409+
394410
const pageClassName = cx({
395411
'panel-in-fullscreen': Boolean(viewPanel),
396412
'page-hidden': Boolean(queryParams.editview || editPanel),
397413
});
398414

399-
if (dashboard.meta.dashboardNotFound) {
415+
if (dashboard.meta.dashboardNotFound && !FNDashboard) {
400416
return (
401417
<Page navId="dashboards/browse" layout={PageLayoutType.Canvas} pageNav={{ text: 'Not found' }}>
402418
<EntityNotFound entity="Dashboard" />
@@ -417,27 +433,56 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
417433
);
418434

419435
return (
420-
<React.Fragment>
436+
<>
421437
<Page
422438
navModel={sectionNav}
423439
pageNav={pageNav}
424440
layout={PageLayoutType.Canvas}
425441
className={pageClassName}
426-
// scrollRef={this.setScrollRef}
427-
// scrollTop={updateScrollTop}
428-
style={{ minHeight: '550px' }}
442+
onSetScrollRef={this.setScrollRef}
429443
>
430444
{showToolbar && (
431445
<header data-testid={selectors.pages.Dashboard.DashNav.navV2}>
432446
{FNDashboard ? (
433-
FNTimeRange
447+
<div
448+
style={{
449+
display: 'flex',
450+
justifyContent: 'flex-end',
451+
gap: 4,
452+
}}
453+
>
454+
{isCustomDashboard && (
455+
<>
456+
<ModalsController key="button-save">
457+
{({ showModal, hideModal }) => (
458+
<ToolbarButton
459+
tooltip={t('dashboard.toolbar.save', 'Save dashboard')}
460+
icon="save"
461+
onClick={() => {
462+
showModal(SaveDashboardDrawer, {
463+
dashboard,
464+
onDismiss: hideModal,
465+
});
466+
}}
467+
/>
468+
)}
469+
</ModalsController>
470+
<AddPanelButton
471+
onToolbarAddMenuOpen={DashboardInteractions.toolbarAddClick}
472+
dashboard={dashboard}
473+
key="panel-add-dropdown"
474+
isFNDashboard
475+
/>
476+
</>
477+
)}
478+
{FNTimeRange}
479+
</div>
434480
) : (
435481
<DashNav
436482
dashboard={dashboard}
437483
title={dashboard.title}
438484
folderTitle={dashboard.meta.folderTitle}
439485
isFullscreen={!!viewPanel}
440-
// onAddPanel={this.onAddPanel}
441486
kioskMode={kioskMode}
442487
hideTimePicker={dashboard.timepicker.hidden}
443488
/>
@@ -453,14 +498,14 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
453498
)}
454499
<DashboardGrid
455500
dashboard={dashboard}
456-
isEditable={!!dashboard.meta.canEdit && !FNDashboard}
501+
isEditable={isFNDashboardEditable && !!dashboard.meta.canEdit}
457502
viewPanel={viewPanel}
458503
editPanel={editPanel}
459504
/>
460505

461506
{inspectPanel && !FNDashboard && <PanelInspector dashboard={dashboard} panel={inspectPanel} />}
462507
</Page>
463-
{editPanel && !FNDashboard && sectionNav && pageNav && (
508+
{editPanel && sectionNav && pageNav && isFNDashboardEditable && (
464509
<PanelEditor
465510
dashboard={dashboard}
466511
sourcePanel={editPanel}
@@ -469,24 +514,26 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
469514
pageNav={pageNav}
470515
/>
471516
)}
472-
{queryParams.editview && !FNDashboard && pageNav && sectionNav && (
517+
{queryParams.editview && pageNav && sectionNav && isFNDashboardEditable && (
473518
<DashboardSettings
474519
dashboard={dashboard}
475520
editview={queryParams.editview}
476521
pageNav={pageNav}
477522
sectionNav={sectionNav}
478523
/>
479524
)}
480-
{!FNDashboard && queryParams.addWidget && config.featureToggles.vizAndWidgetSplit && <AddWidgetModal />}
481-
</React.Fragment>
525+
{isFNDashboardEditable && queryParams.addWidget && config.featureToggles.vizAndWidgetSplit && (
526+
<AddWidgetModal />
527+
)}
528+
</>
482529
);
483530
}
484531
}
485532

486533
function updateStatePageNavFromProps(props: Props, state: State): State {
487-
const { dashboard, FNDashboard } = props;
534+
const { dashboard, FNDashboard, isCustomDashboard } = props;
488535

489-
if (!dashboard || FNDashboard) {
536+
if (!dashboard || (FNDashboard && !isCustomDashboard)) {
490537
return state;
491538
}
492539

0 commit comments

Comments
 (0)