From 5a4f69080702ad33145d5b09e13ee9ccc8cc9800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 10 Apr 2020 16:37:26 +0200 Subject: [PATCH] NewPanelEditor: Enable new edit mode (#23405) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * WIP: initial commit to transition to new edit mode * More old edit cleanup * Minor update * Refactoring url edit/fullscreen state to simplify logic, now seperate states * Fixed tests and part of the explore integration * Updated snapshot * Fix alert rule links * Fixed issue going back from explore * Updated snapshots * Fixes and changes * Fixed bridge srv issue * Fixed add panel issue * Removed console log * Removed render * Tests: fixes e2e smoketest * Make description optional * Fixed typings * e2e fixes * removed import * updated snapshot Co-authored-by: Hugo Häggmark --- e2e/start-server | 2 +- e2e/suite1/specs/queryVariableCrud.spec.ts | 12 +- e2e/suite1/specs/smoketests.spec.ts | 39 +---- .../src/types/OptionsUIRegistryBuilder.ts | 2 +- .../grafana-data/src/types/fieldOverrides.ts | 2 +- packages/grafana-data/src/types/panel.ts | 2 +- packages/grafana-data/src/utils/Registry.ts | 2 +- packages/grafana-e2e/src/pages/dashboard.ts | 1 - packages/grafana-e2e/src/pages/index.ts | 8 + .../DataLinksInlineEditor.tsx | 2 +- .../ThresholdsEditorNew/ThresholdsEditor.tsx | 2 +- .../ValueMappingsEditor.tsx | 2 +- pkg/services/alerting/eval_context.go | 2 +- .../core/components/BackButton/BackButton.tsx | 5 +- .../app/core/components/sidemenu/SideMenu.tsx | 2 +- .../__snapshots__/SideMenu.test.tsx.snap | 1 + public/app/core/services/bridge_srv.ts | 18 +- public/app/core/services/keybindingSrv.ts | 21 +-- public/app/core/utils/explore.ts | 4 +- .../app/features/alerting/AlertRuleItem.tsx | 2 +- .../__snapshots__/AlertRuleItem.test.tsx.snap | 4 +- .../features/annotations/annotations_srv.ts | 4 +- .../AddPanelWidget/AddPanelWidget.tsx | 15 +- .../AddPanelWidget/_AddPanelWidget.scss | 2 +- .../AddPanelWidget.test.tsx.snap | 2 +- .../dashboard/components/DashNav/DashNav.tsx | 6 +- .../DashboardSettings/DashboardSettings.tsx | 3 +- .../PanelEditor/FieldConfigEditor.tsx | 2 +- .../components/PanelEditor/PanelEditor.tsx | 9 +- .../components/PanelEditor/state/actions.ts | 2 +- .../containers/DashboardPage.test.tsx | 53 ++---- .../dashboard/containers/DashboardPage.tsx | 157 ++++++++---------- .../dashboard/containers/SoloPanelPage.tsx | 2 +- .../__snapshots__/DashboardPage.test.tsx.snap | 24 +-- .../dashboard/dashgrid/DashboardGrid.test.tsx | 4 +- .../dashboard/dashgrid/DashboardGrid.tsx | 27 ++- .../dashboard/dashgrid/DashboardPanel.tsx | 46 ++--- .../dashboard/dashgrid/PanelChrome.tsx | 14 +- .../dashboard/dashgrid/PanelChromeAngular.tsx | 8 +- .../dashgrid/PanelHeader/PanelHeader.tsx | 7 +- .../__snapshots__/DashboardGrid.test.tsx.snap | 38 +---- .../dashboard/services/ChangeTracker.ts | 1 + .../dashboard/services/DashboardSrv.ts | 48 +----- .../dashboard/state/DashboardModel.ts | 27 +-- .../features/dashboard/state/PanelModel.ts | 19 ++- .../features/dashboard/utils/getPanelMenu.ts | 30 +--- .../app/features/explore/ExploreToolbar.tsx | 20 +-- public/app/features/panel/panel_ctrl.ts | 2 +- public/app/features/panel/panel_directive.ts | 12 -- .../datasource/dashboard/runSharedRequest.ts | 2 +- public/app/plugins/panel/bargauge/module.tsx | 3 +- public/app/types/dashboard.ts | 2 - public/sass/components/_panel_editor.scss | 5 - 53 files changed, 250 insertions(+), 481 deletions(-) diff --git a/e2e/start-server b/e2e/start-server index 35304629e5b..69cdb3b4874 100755 --- a/e2e/start-server +++ b/e2e/start-server @@ -18,6 +18,7 @@ else cp -r ./bin $RUNDIR cp -r ./public $RUNDIR + cp -r ./tools $RUNDIR mkdir $RUNDIR/conf mkdir $PROV_DIR @@ -25,7 +26,6 @@ else mkdir $PROV_DIR/dashboards cp ./conf/defaults.ini $RUNDIR/conf/defaults.ini - cp ./conf/sample.ini $RUNDIR/conf/custom.ini fi echo -e "Copy provisioning setup from devenv" diff --git a/e2e/suite1/specs/queryVariableCrud.spec.ts b/e2e/suite1/specs/queryVariableCrud.spec.ts index e6de8908ccc..6dec80d8513 100644 --- a/e2e/suite1/specs/queryVariableCrud.spec.ts +++ b/e2e/suite1/specs/queryVariableCrud.spec.ts @@ -209,7 +209,7 @@ const assertAdding3dependantQueryVariablesScenario = (queryVariables: QueryVaria e2e.pages.SaveDashboardModal.save().click(); e2e.flows.assertSuccessNotification(); - e2e.pages.Dashboard.Toolbar.backArrow().click(); + e2e.pages.Components.BackButton.backArrow().click(); assertVariableLabelsAndComponents(asserts); @@ -264,7 +264,7 @@ const assertDuplicateItem = (queryVariables: QueryVariableData[]) => { e2e.pages.SaveDashboardModal.save().click(); e2e.flows.assertSuccessNotification(); - e2e.pages.Dashboard.Toolbar.backArrow().click(); + e2e.pages.Components.BackButton.backArrow().click(); e2e.pages.Dashboard.SubMenu.submenuItemLabels(newItem.label).should('be.visible'); e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts(newItem.selectedOption) @@ -300,7 +300,7 @@ const assertDeleteItem = (queryVariables: QueryVariableData[]) => { e2e.pages.SaveDashboardModal.save().click(); e2e.flows.assertSuccessNotification(); - e2e.pages.Dashboard.Toolbar.backArrow().click(); + e2e.pages.Components.BackButton.backArrow().click(); e2e.pages.Dashboard.SubMenu.submenuItemLabels(itemToDelete.label).should('not.exist'); @@ -347,7 +347,7 @@ const assertUpdateItem = (data: QueryVariableData[]) => { e2e.pages.Dashboard.Settings.Variables.Edit.General.generalHideSelect().select(''); e2e.pages.Dashboard.Settings.Variables.Edit.ConstantVariable.constantOptionsQueryInput().type(updatedItem.query); - e2e.pages.Dashboard.Toolbar.backArrow().click(); + e2e.pages.Components.BackButton.backArrow().click(); e2e() .window() @@ -407,7 +407,7 @@ const assertMoveDownItem = (data: QueryVariableData[]) => { }); }); - e2e.pages.Dashboard.Toolbar.backArrow().click(); + e2e.pages.Components.BackButton.backArrow().click(); assertVariableLabelsAndComponents(queryVariables); @@ -552,7 +552,7 @@ const assertMoveUpItem = (data: QueryVariableData[]) => { }); }); - e2e.pages.Dashboard.Toolbar.backArrow().click(); + e2e.pages.Components.BackButton.backArrow().click(); assertVariableLabelsAndComponents(queryVariables); diff --git a/e2e/suite1/specs/smoketests.spec.ts b/e2e/suite1/specs/smoketests.spec.ts index 2fba0010689..d600d49a2c0 100644 --- a/e2e/suite1/specs/smoketests.spec.ts +++ b/e2e/suite1/specs/smoketests.spec.ts @@ -15,48 +15,15 @@ e2e.scenario({ e2e.pages.Dashboard.Toolbar.toolbarItems('Add panel').click(); e2e.pages.AddDashboard.ctaButtons('Add Query').click(); - e2e.pages.Dashboard.Panels.EditPanel.tabItems('Queries').click(); e2e.pages.Dashboard.Panels.DataSource.TestData.QueryTab.scenarioSelect().select('CSV Metric Values'); - e2e.pages.Dashboard.Panels.EditPanel.tabItems('Visualization').click(); - e2e.pages.Dashboard.Panels.Visualization.Graph.VisualizationTab.xAxisSection() .contains('Show') .click(); - e2e.flows.saveDashboard(); + e2e.pages.Components.BackButton.backArrow().click(); - e2e.pages.Dashboard.Toolbar.backArrow().click(); - - e2e.pages.Dashboard.Panels.Panel.title('Panel Title').click(); - - e2e.pages.Dashboard.Panels.Panel.headerItems('Share').click(); - - e2e.pages.SharePanelModal.linkToRenderedImage().then(($a: any) => { - // extract the fully qualified href property - const url = $a.prop('href'); - - // Test that the image renderer returns 200 OK - e2e().request({ method: 'GET', url, timeout: 120000 }); - - // Download image - if (!e2e.env('CIRCLE_SHA1')) { - return; - } - - const theOutputImage = `${e2e.config().screenshotsFolder}/received/smoke-test-scenario.png`; - const theTruthImage = `${e2e.config().screenshotsFolder}/expected/smoke-test-scenario.png`; - - e2e().wrap( - e2e.imgSrcToBlob(url).then((blob: any) => { - e2e.blobToBase64String(blob).then((base64String: string) => { - const data = base64String.replace(/^data:image\/\w+;base64,/, ''); - e2e().writeFile(theOutputImage, data, 'base64'); - }); - }) - ); - e2e().wait(1000); // give the io a chance to flush image to disk - e2e().compareSnapshot({ pathToFileA: theOutputImage, pathToFileB: theTruthImage }); - }); + // e2e.pages.Dashboard.Panels.Panel.title('Panel Title').click(); + // e2e.pages.Dashboard.Panels.Panel.headerItems('Inspect').click(); }, }); diff --git a/packages/grafana-data/src/types/OptionsUIRegistryBuilder.ts b/packages/grafana-data/src/types/OptionsUIRegistryBuilder.ts index 758872fd4e3..9586383214a 100644 --- a/packages/grafana-data/src/types/OptionsUIRegistryBuilder.ts +++ b/packages/grafana-data/src/types/OptionsUIRegistryBuilder.ts @@ -49,7 +49,7 @@ export interface OptionsEditorItem ex interface OptionEditorConfig { id: keyof TOptions & string; name: string; - description: string; + description?: string; settings?: TSettings; defaultValue?: TValue; } diff --git a/packages/grafana-data/src/types/fieldOverrides.ts b/packages/grafana-data/src/types/fieldOverrides.ts index ad15f871f4e..725280161f4 100644 --- a/packages/grafana-data/src/types/fieldOverrides.ts +++ b/packages/grafana-data/src/types/fieldOverrides.ts @@ -77,7 +77,7 @@ export interface FieldConfigEditorConfig 'last') /** diff --git a/packages/grafana-e2e/src/pages/dashboard.ts b/packages/grafana-e2e/src/pages/dashboard.ts index b4df0d81ea7..a6111d2dce2 100644 --- a/packages/grafana-e2e/src/pages/dashboard.ts +++ b/packages/grafana-e2e/src/pages/dashboard.ts @@ -4,7 +4,6 @@ export const Dashboard = pageFactory({ url: (uid: string) => `/d/${uid}`, selectors: { toolbarItems: (button: string) => `Dashboard navigation bar button ${button}`, - backArrow: 'Dashboard settings Go Back button', navBar: () => '.navbar', }, }); diff --git a/packages/grafana-e2e/src/pages/index.ts b/packages/grafana-e2e/src/pages/index.ts index 150ab912d82..70cef42b378 100644 --- a/packages/grafana-e2e/src/pages/index.ts +++ b/packages/grafana-e2e/src/pages/index.ts @@ -15,6 +15,7 @@ import { SaveDashboardModal } from './saveDashboardModal'; import { Panel } from './panel'; import { SharePanelModal } from './sharePanelModal'; import { ConstantVariable, QueryVariable, VariableGeneral, Variables, VariablesSubMenu } from './variables'; +import { pageFactory } from '../support'; export const Pages = { Login, @@ -53,4 +54,11 @@ export const Pages = { SaveDashboardAsModal, SaveDashboardModal, SharePanelModal, + Components: { + BackButton: pageFactory({ + selectors: { + backArrow: 'Go Back button', + }, + }), + }, }; diff --git a/packages/grafana-ui/src/components/DataLinks/DataLinksInlineEditor/DataLinksInlineEditor.tsx b/packages/grafana-ui/src/components/DataLinks/DataLinksInlineEditor/DataLinksInlineEditor.tsx index 47825e9c53c..d595eda6086 100644 --- a/packages/grafana-ui/src/components/DataLinks/DataLinksInlineEditor/DataLinksInlineEditor.tsx +++ b/packages/grafana-ui/src/components/DataLinks/DataLinksInlineEditor/DataLinksInlineEditor.tsx @@ -100,7 +100,7 @@ export const DataLinksInlineEditor: React.FC = ({ li )} - diff --git a/packages/grafana-ui/src/components/ThresholdsEditorNew/ThresholdsEditor.tsx b/packages/grafana-ui/src/components/ThresholdsEditorNew/ThresholdsEditor.tsx index 9da1d516e99..9aff7433142 100644 --- a/packages/grafana-ui/src/components/ThresholdsEditorNew/ThresholdsEditor.tsx +++ b/packages/grafana-ui/src/components/ThresholdsEditorNew/ThresholdsEditor.tsx @@ -200,7 +200,7 @@ export class ThresholdsEditor extends PureComponent { return (
- diff --git a/packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.tsx b/packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.tsx index c8265cc4b3d..a29aa5815db 100644 --- a/packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.tsx +++ b/packages/grafana-ui/src/components/ValueMappingsEditor/ValueMappingsEditor.tsx @@ -68,7 +68,7 @@ export const ValueMappingsEditor: React.FC = ({ valueMappings, onChange, diff --git a/public/app/core/components/sidemenu/SideMenu.tsx b/public/app/core/components/sidemenu/SideMenu.tsx index 1a0496c089e..815edd2bbd3 100644 --- a/public/app/core/components/sidemenu/SideMenu.tsx +++ b/public/app/core/components/sidemenu/SideMenu.tsx @@ -20,7 +20,7 @@ export class SideMenu extends PureComponent { ,
- +  Close diff --git a/public/app/core/components/sidemenu/__snapshots__/SideMenu.test.tsx.snap b/public/app/core/components/sidemenu/__snapshots__/SideMenu.test.tsx.snap index 5ad4b33d432..43f473ec29d 100644 --- a/public/app/core/components/sidemenu/__snapshots__/SideMenu.test.tsx.snap +++ b/public/app/core/components/sidemenu/__snapshots__/SideMenu.test.tsx.snap @@ -16,6 +16,7 @@ Array [ > { - const angularUrl = this.$location.url(); const state = store.getState(); - if (state.location.url !== angularUrl) { + + this.angularUrl = this.$location.url(); + + if (state.location.url !== this.angularUrl) { store.dispatch( updateLocation({ path: this.$location.path(), @@ -44,6 +48,8 @@ export class BridgeSrv { }); this.$rootScope.$on('$routeChangeSuccess', (evt, data) => { + this.angularUrl = this.$location.url(); + store.dispatch( updateLocation({ path: this.$location.path(), @@ -56,9 +62,12 @@ export class BridgeSrv { // Listen for changes in redux location -> update angular location store.subscribe(() => { const state = store.getState(); - const angularUrl = this.$location.url(); const url = state.location.url; - if (angularUrl !== url) { + + if (this.angularUrl !== url) { + // store angular url right away as otherwise we end up syncing multiple times + this.angularUrl = url; + this.$timeout(() => { this.$location.url(url); // some state changes should not trigger new browser history @@ -66,6 +75,7 @@ export class BridgeSrv { this.$location.replace(); } }); + console.log('store updating angular $location.url', url); } diff --git a/public/app/core/services/keybindingSrv.ts b/public/app/core/services/keybindingSrv.ts index 0b8bead18e7..e69b851f5a5 100644 --- a/public/app/core/services/keybindingSrv.ts +++ b/public/app/core/services/keybindingSrv.ts @@ -8,7 +8,6 @@ import { store } from 'app/store/store'; import { AppEventEmitter, CoreEvents } from 'app/types'; import Mousetrap from 'mousetrap'; -import { PanelEvents } from '@grafana/data'; import 'mousetrap-global-bind'; import { ContextSrv } from './context_srv'; import { ILocationService, IRootScopeService, ITimeoutService } from 'angular'; @@ -133,8 +132,9 @@ export class KeybindingSrv { return; } - if (search.fullscreen) { - appEvents.emit(PanelEvents.panelChangeView, { fullscreen: false, edit: false }); + if (search.viewPanel) { + delete search.viewPanel; + this.$location.search(search); return; } @@ -213,23 +213,16 @@ export class KeybindingSrv { // edit panel this.bind('e', () => { if (dashboard.canEditPanelById(dashboard.meta.focusPanelId)) { - appEvents.emit(PanelEvents.panelChangeView, { - fullscreen: true, - edit: true, - panelId: dashboard.meta.focusPanelId, - toggle: true, - }); + const search = _.extend(this.$location.search(), { editPanel: dashboard.meta.focusPanelId }); + this.$location.search(search); } }); // view panel this.bind('v', () => { if (dashboard.meta.focusPanelId) { - appEvents.emit(PanelEvents.panelChangeView, { - fullscreen: true, - panelId: dashboard.meta.focusPanelId, - toggle: true, - }); + const search = _.extend(this.$location.search(), { viewPanel: dashboard.meta.focusPanelId }); + this.$location.search(search); } }); diff --git a/public/app/core/utils/explore.ts b/public/app/core/utils/explore.ts index ac46a2d3889..90518506908 100644 --- a/public/app/core/utils/explore.ts +++ b/public/app/core/utils/explore.ts @@ -15,7 +15,6 @@ import { LogRowModel, LogsDedupStrategy, LogsModel, - PanelModel, RawTimeRange, TimeFragment, TimeRange, @@ -33,6 +32,7 @@ import { ExploreUrlState, QueryOptions, QueryTransaction } from 'app/types/explo import { config } from '../config'; import { TimeSrv } from 'app/features/dashboard/services/TimeSrv'; import { DataSourceSrv } from '@grafana/runtime'; +import { PanelModel } from 'app/features/dashboard/state'; export const DEFAULT_RANGE = { from: 'now-1h', @@ -105,7 +105,7 @@ export async function getExploreUrl(args: GetExploreUrlArguments): Promise { render() { const { rule, onTogglePause } = this.props; - const ruleUrl = `${rule.url}?panelId=${rule.panelId}&fullscreen&edit&tab=alert`; + const ruleUrl = `${rule.url}?editPanel=${rule.panelId}&tab=alert`; return (
  • diff --git a/public/app/features/alerting/__snapshots__/AlertRuleItem.test.tsx.snap b/public/app/features/alerting/__snapshots__/AlertRuleItem.test.tsx.snap index ef50083269d..265301913ba 100644 --- a/public/app/features/alerting/__snapshots__/AlertRuleItem.test.tsx.snap +++ b/public/app/features/alerting/__snapshots__/AlertRuleItem.test.tsx.snap @@ -19,7 +19,7 @@ exports[`Render should render component 1`] = ` className="alert-rule-item__name" > { // combine the annotations and flatten results let annotations: AnnotationEvent[] = flattenDeep(results[0]); - // when in edit mode we need to use editSourceId - let panelFilterId = options.panel.editSourceId ?? options.panel.id; + // when in edit mode we need to use this function to get the saved id + let panelFilterId = options.panel.getSavedId(); // filter out annotations that do not belong to requesting panel annotations = annotations.filter(item => { diff --git a/public/app/features/dashboard/components/AddPanelWidget/AddPanelWidget.tsx b/public/app/features/dashboard/components/AddPanelWidget/AddPanelWidget.tsx index 9d775db825f..d18b414c620 100644 --- a/public/app/features/dashboard/components/AddPanelWidget/AddPanelWidget.tsx +++ b/public/app/features/dashboard/components/AddPanelWidget/AddPanelWidget.tsx @@ -86,16 +86,13 @@ export class AddPanelWidgetUnconnected extends React.Component { const location: LocationUpdate = { query: { - panelId: newPanel.id, - edit: true, - fullscreen: true, + editPanel: newPanel.id, }, partial: true, }; - if (tab === 'visualization') { - location.query.tab = 'visualization'; - location.query.openVizPicker = true; + if (tab === 'visualize') { + location.query.tab = 'visualize'; } reduxStore.dispatch(updateLocation(location)); @@ -173,10 +170,8 @@ export class AddPanelWidgetUnconnected extends React.Component {
  • - {this.renderOptionLink('search', 'Add Query', this.onCreateNewPanel)} - {this.renderOptionLink('chart-line', 'Choose Visualization', () => - this.onCreateNewPanel('visualization') - )} + {this.renderOptionLink('database', 'Add Query', this.onCreateNewPanel)} + {this.renderOptionLink('chart-line', 'Choose Visualization', () => this.onCreateNewPanel('visualize'))}
    diff --git a/public/app/features/dashboard/components/DashNav/DashNav.tsx b/public/app/features/dashboard/components/DashNav/DashNav.tsx index 6a256cc250d..113cfcb858f 100644 --- a/public/app/features/dashboard/components/DashNav/DashNav.tsx +++ b/public/app/features/dashboard/components/DashNav/DashNav.tsx @@ -2,7 +2,6 @@ import React, { PureComponent, FC } from 'react'; import { connect } from 'react-redux'; import { css } from 'emotion'; -import { e2e } from '@grafana/e2e'; // Utils & Services import { appEvents } from 'app/core/app_events'; import { PlaylistSrv } from 'app/features/playlist/playlist_srv'; @@ -22,7 +21,6 @@ import { sanitizeUrl } from 'app/core/utils/text'; export interface OwnProps { dashboard: DashboardModel; - isEditing: boolean; isFullscreen: boolean; $injector: any; updateLocation: typeof updateLocation; @@ -61,7 +59,7 @@ class DashNav extends PureComponent { onClose = () => { this.props.updateLocation({ - query: { panelId: null, edit: null, fullscreen: null, tab: null }, + query: { edit: null, viewPanel: null }, partial: true, }); }; @@ -140,7 +138,7 @@ class DashNav extends PureComponent { renderBackButton() { return (
    - +
    ); } diff --git a/public/app/features/dashboard/components/DashboardSettings/DashboardSettings.tsx b/public/app/features/dashboard/components/DashboardSettings/DashboardSettings.tsx index 53025410595..7ec39850add 100644 --- a/public/app/features/dashboard/components/DashboardSettings/DashboardSettings.tsx +++ b/public/app/features/dashboard/components/DashboardSettings/DashboardSettings.tsx @@ -7,7 +7,6 @@ import { AngularComponent, getAngularLoader } from '@grafana/runtime'; // Types import { DashboardModel } from '../../state/DashboardModel'; import { BackButton } from 'app/core/components/BackButton/BackButton'; -import { e2e } from '@grafana/e2e'; import { updateLocation } from 'app/core/actions'; import { CustomScrollbar } from '@grafana/ui'; @@ -51,7 +50,7 @@ export class DashboardSettings extends PureComponent {
    - +
    {haveFolder &&
    {folderTitle} /
    } diff --git a/public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx b/public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx index 6c3a8c0dc28..9991c884313 100644 --- a/public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx +++ b/public/app/features/dashboard/components/PanelEditor/FieldConfigEditor.tsx @@ -89,7 +89,7 @@ export const OverrideFieldConfigEditor: React.FC = props => { const renderAddOverride = () => { return ( {
    @@ -410,6 +409,7 @@ const getStyles = stylesFactory((theme: GrafanaTheme, props: Props) => { cursor: col-resize; width: ${paneSpaceing}; border-right-width: 1px; + margin-top: 18px; ` ), resizerH: cx( @@ -418,9 +418,10 @@ const getStyles = stylesFactory((theme: GrafanaTheme, props: Props) => { height: ${paneSpaceing}; cursor: row-resize; position: relative; - top: 49px; + top: 0px; z-index: 1; border-top-width: 1px; + margin-left: ${paneSpaceing}; ` ), tabsWrapper: css` diff --git a/public/app/features/dashboard/components/PanelEditor/state/actions.ts b/public/app/features/dashboard/components/PanelEditor/state/actions.ts index 71043d3aa1d..80a9f06cdc3 100644 --- a/public/app/features/dashboard/components/PanelEditor/state/actions.ts +++ b/public/app/features/dashboard/components/PanelEditor/state/actions.ts @@ -14,7 +14,7 @@ import store from '../../../../../core/store'; export function initPanelEditor(sourcePanel: PanelModel, dashboard: DashboardModel): ThunkResult { return dispatch => { - const panel = dashboard.initPanelEditor(sourcePanel); + const panel = dashboard.initEditPanel(sourcePanel); const queryRunner = panel.getQueryRunner(); const querySubscription = queryRunner.getData(false).subscribe({ diff --git a/public/app/features/dashboard/containers/DashboardPage.test.tsx b/public/app/features/dashboard/containers/DashboardPage.test.tsx index 5d55ea3518f..3b45f401f50 100644 --- a/public/app/features/dashboard/containers/DashboardPage.test.tsx +++ b/public/app/features/dashboard/containers/DashboardPage.test.tsx @@ -62,8 +62,6 @@ function dashboardPageScenario(description: string, scenarioFn: (ctx: ScenarioCo urlUid: '11', $injector: {}, routeInfo: DashboardRouteInfo.Normal, - urlEdit: false, - urlFullscreen: false, initPhase: DashboardInitPhase.NotStarted, isInitSlow: false, initDashboard: jest.fn(), @@ -133,55 +131,39 @@ describe('DashboardPage', () => { ctx.mount(); ctx.setDashboardProp(); ctx.wrapper?.setProps({ - urlFullscreen: true, - urlEdit: true, - urlPanelId: '1', + urlEditPanelId: '1', }); }); - it('Should update model state to fullscreen & edit', () => { - expect(ctx.dashboard).not.toBe(null); - expect(ctx.dashboard?.meta.fullscreen).toBe(true); - expect(ctx.dashboard?.meta.isEditing).toBe(true); - }); - it('Should update component state to fullscreen and edit', () => { const state = ctx.wrapper?.state(); expect(state).not.toBe(null); - expect(state?.isEditing).toBe(true); - expect(state?.isFullscreen).toBe(true); + expect(state?.editPanel).toBeDefined(); }); }); - dashboardPageScenario('When user goes back to dashboard from panel edit', ctx => { + dashboardPageScenario('When user goes back to dashboard from view panel', ctx => { ctx.setup(() => { ctx.mount(); ctx.setDashboardProp(); ctx.wrapper?.setState({ scrollTop: 100 }); ctx.wrapper?.setProps({ - urlFullscreen: true, - urlEdit: true, - urlPanelId: '1', + urlEditPanelId: '1', }); ctx.wrapper?.setProps({ - urlFullscreen: false, - urlEdit: false, - urlPanelId: (null as unknown) as string, + urlEditPanelId: undefined, }); }); it('Should update model state normal state', () => { - expect(ctx.dashboard).not.toBe(null); - expect(ctx.dashboard?.meta.fullscreen).toBe(false); - expect(ctx.dashboard?.meta.isEditing).toBe(false); + expect(ctx.dashboard.panelInEdit).toBeUndefined(); }); it('Should update component state to normal and restore scrollTop', () => { const state = ctx.wrapper?.state(); expect(ctx.wrapper).not.toBe(null); expect(state).not.toBe(null); - expect(state?.isEditing).toBe(false); - expect(state?.isFullscreen).toBe(false); + expect(state?.editPanel).toBe(null); expect(state?.scrollTop).toBe(100); }); }); @@ -228,9 +210,7 @@ describe('DashboardPage', () => { schemaVersion: 17, }); ctx.wrapper?.setProps({ - urlEdit: true, - urlFullscreen: true, - urlPanelId: '0', + urlEditPanelId: '0', }); }); @@ -238,8 +218,7 @@ describe('DashboardPage', () => { const state = ctx.wrapper?.state(); expect(ctx.wrapper).not.toBe(null); expect(state).not.toBe(null); - expect(state?.fullscreenPanel).not.toBe(null); - expect(state?.fullscreenPanel?.id).toBe(0); + expect(state?.editPanel).not.toBe(null); }); }); @@ -258,13 +237,12 @@ describe('DashboardPage', () => { }); }); - describe('mapStateToProps with bool fullscreen', () => { + describe('mapStateToProps with editPanel', () => { const props = mapStateToProps({ location: { routeParams: {}, query: { - fullscreen: true, - edit: false, + editPanel: '1', }, }, panelEditorNew: {}, @@ -273,8 +251,7 @@ describe('DashboardPage', () => { }, } as any); - expect(props.urlFullscreen).toBe(true); - expect(props.urlEdit).toBe(false); + expect(props.urlEditPanelId).toBe('1'); }); describe('mapStateToProps with string edit true', () => { @@ -282,8 +259,7 @@ describe('DashboardPage', () => { location: { routeParams: {}, query: { - fullscreen: false, - edit: 'true', + viewPanel: '2', }, }, panelEditorNew: {}, @@ -292,7 +268,6 @@ describe('DashboardPage', () => { }, } as any); - expect(props.urlFullscreen).toBe(false); - expect(props.urlEdit).toBe(true); + expect(props.urlViewPanelId).toBe('2'); }); }); diff --git a/public/app/features/dashboard/containers/DashboardPage.tsx b/public/app/features/dashboard/containers/DashboardPage.tsx index b27eb150561..d6e2ea1560d 100644 --- a/public/app/features/dashboard/containers/DashboardPage.tsx +++ b/public/app/features/dashboard/containers/DashboardPage.tsx @@ -15,7 +15,7 @@ import { DashNav } from '../components/DashNav'; import { AngularSubMenu } from '../components/SubMenu'; import { DashboardSettings } from '../components/DashboardSettings'; import { PanelEditor } from '../components/PanelEditor/PanelEditor'; -import { Alert, CustomScrollbar, Portal } from '@grafana/ui'; +import { Alert, CustomScrollbar } from '@grafana/ui'; // Redux import { initDashboard } from '../state/initDashboard'; import { cleanUpDashboard } from '../state/reducers'; @@ -41,13 +41,12 @@ export interface Props { editview?: string; urlPanelId?: string; urlFolderId?: string; - urlEditPanel?: string; inspectPanelId?: string; $scope: any; $injector: any; routeInfo: DashboardRouteInfo; - urlEdit: boolean; - urlFullscreen: boolean; + urlEditPanelId?: string; + urlViewPanelId?: string; initPhase: DashboardInitPhase; isInitSlow: boolean; dashboard: DashboardModel | null; @@ -61,9 +60,8 @@ export interface Props { } export interface State { - isEditing: boolean; - isFullscreen: boolean; - fullscreenPanel: PanelModel | null; + editPanel: PanelModel | null; + viewPanel: PanelModel | null; scrollTop: number; updateScrollTop?: number; rememberScrollTop: number; @@ -72,10 +70,9 @@ export interface State { export class DashboardPage extends PureComponent { state: State = { - isEditing: false, - isFullscreen: false, + editPanel: null, + viewPanel: null, showLoadingState: false, - fullscreenPanel: null, scrollTop: 0, rememberScrollTop: 0, }; @@ -101,7 +98,8 @@ export class DashboardPage extends PureComponent { } componentDidUpdate(prevProps: Props) { - const { dashboard, urlEdit, urlFullscreen, urlPanelId, urlUid } = this.props; + const { dashboard, urlEditPanelId, urlViewPanelId, urlUid } = this.props; + const { editPanel, viewPanel } = this.state; if (!dashboard) { return; @@ -118,56 +116,64 @@ export class DashboardPage extends PureComponent { return; } - // Sync url state with model - if (urlFullscreen !== dashboard.meta.fullscreen || urlEdit !== dashboard.meta.isEditing) { - if (urlPanelId && !isNaN(parseInt(urlPanelId, 10))) { - this.onEnterFullscreen(dashboard, urlPanelId); - } else { - this.onLeaveFullscreen(dashboard); - } + // entering edit mode + if (!editPanel && urlEditPanelId) { + this.getPanelByIdFromUrlParam(urlEditPanelId, panel => { + this.setState({ editPanel: panel }); + }); + } + + // leaving edit mode + if (editPanel && !urlEditPanelId) { + this.setState({ editPanel: null }); + } + + // entering view mode + if (!viewPanel && urlViewPanelId) { + this.getPanelByIdFromUrlParam(urlViewPanelId, panel => { + this.setPanelFullscreenClass(true); + dashboard.initViewPanel(panel); + this.setState({ + viewPanel: panel, + rememberScrollTop: this.state.scrollTop, + }); + }); + } + + // leaving view mode + if (viewPanel && !urlViewPanelId) { + this.setPanelFullscreenClass(false); + dashboard.exitViewPanel(viewPanel); + this.setState( + { viewPanel: null, updateScrollTop: this.state.rememberScrollTop }, + this.triggerPanelsRendering.bind(this) + ); } } - onEnterFullscreen(dashboard: DashboardModel, urlPanelId: string) { - const { urlEdit, urlFullscreen } = this.props; + getPanelByIdFromUrlParam(urlPanelId: string, callback: (panel: PanelModel) => void) { + const { dashboard } = this.props; const panelId = parseInt(urlPanelId!, 10); - dashboard; - // need to expand parent row if this panel is inside a row - dashboard.expandParentRowFor(panelId); + const panel = dashboard!.getPanelById(panelId); - const panel = dashboard.getPanelById(panelId); - - if (panel) { - dashboard.setViewMode(panel, urlFullscreen, urlEdit); - this.setState({ - isEditing: urlEdit && dashboard.meta.canEdit === true, - isFullscreen: urlFullscreen, - fullscreenPanel: panel, - rememberScrollTop: this.state.scrollTop, + if (!panel) { + // Panel not found + this.props.notifyApp(createErrorNotification(`Panel with id ${urlPanelId} not found`)); + // Clear url state + this.props.updateLocation({ + query: { + edit: null, + fullscreen: null, + panelId: null, + }, + partial: true, }); - this.setPanelFullscreenClass(urlFullscreen); - } else { - this.handleFullscreenPanelNotFound(urlPanelId); - } - } - - onLeaveFullscreen(dashboard: DashboardModel) { - if (this.state.fullscreenPanel) { - dashboard.setViewMode(this.state.fullscreenPanel, false, false); + return; } - this.setState( - { - isEditing: false, - isFullscreen: false, - fullscreenPanel: null, - updateScrollTop: this.state.rememberScrollTop, - }, - this.triggerPanelsRendering.bind(this) - ); - - this.setPanelFullscreenClass(false); + dashboard!.expandParentRowFor(panelId); + callback(panel); } triggerPanelsRendering() { @@ -179,20 +185,6 @@ export class DashboardPage extends PureComponent { } } - handleFullscreenPanelNotFound(urlPanelId: string) { - // Panel not found - this.props.notifyApp(createErrorNotification(`Panel with id ${urlPanelId} not found`)); - // Clear url state - this.props.updateLocation({ - query: { - edit: null, - fullscreen: null, - panelId: null, - }, - partial: true, - }); - } - setPanelFullscreenClass(isFullscreen: boolean) { $('body').toggleClass('panel-in-fullscreen', isFullscreen); } @@ -252,13 +244,13 @@ export class DashboardPage extends PureComponent { isInitSlow, initError, inspectPanelId, - urlEditPanel, inspectTab, isNewEditorOpen, updateLocation, } = this.props; - const { isEditing, isFullscreen, scrollTop, updateScrollTop } = this.state; + const { editPanel, viewPanel, scrollTop, updateScrollTop } = this.state; + const { featureToggles } = getConfig(); if (!dashboard) { if (isInitSlow) { @@ -274,21 +266,13 @@ export class DashboardPage extends PureComponent { // Find the panel to inspect const inspectPanel = inspectPanelId ? dashboard.getPanelById(parseInt(inspectPanelId, 10)) : null; - // find panel being edited - const editPanel = urlEditPanel ? dashboard.getPanelById(parseInt(urlEditPanel, 10)) : null; // Only trigger render when the scroll has moved by 25 const approximateScrollTop = Math.round(scrollTop / 25) * 25; return (
    - +
    { {initError && this.renderInitFailedState()}
    - {!getConfig().featureToggles.newVariables && } - {!editPanel && getConfig().featureToggles.newVariables && } + {!editPanel && !featureToggles.newVariables && } + {!editPanel && featureToggles.newVariables && } @@ -314,11 +298,7 @@ export class DashboardPage extends PureComponent {
    {inspectPanel && } - {editPanel && ( - - - - )} + {editPanel && } {editview && }
    ); @@ -331,10 +311,9 @@ export const mapStateToProps = (state: StoreState) => ({ urlType: state.location.routeParams.type, editview: state.location.query.editview, urlPanelId: state.location.query.panelId, - urlEditPanel: state.location.query.editPanel, urlFolderId: state.location.query.folderId, - urlFullscreen: !!state.location.query.fullscreen, - urlEdit: !!state.location.query.edit, + urlEditPanelId: state.location.query.editPanel, + urlViewPanelId: state.location.query.viewPanel, inspectPanelId: state.location.query.inspect, initPhase: state.dashboard.initPhase, isInitSlow: state.dashboard.isInitSlow, diff --git a/public/app/features/dashboard/containers/SoloPanelPage.tsx b/public/app/features/dashboard/containers/SoloPanelPage.tsx index 6653a7dc540..6fa93e8e04f 100644 --- a/public/app/features/dashboard/containers/SoloPanelPage.tsx +++ b/public/app/features/dashboard/containers/SoloPanelPage.tsx @@ -89,7 +89,7 @@ export class SoloPanelPage extends Component { return (
    - +
    ); } diff --git a/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap b/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap index 3b45648dca4..e4d6386e3c0 100644 --- a/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap +++ b/public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap @@ -40,8 +40,6 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1` "canSave": true, "canShare": true, "canStar": true, - "fullscreen": false, - "isEditing": false, "showSettings": true, }, "originalTemplating": Array [], @@ -98,7 +96,6 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1` "version": 0, } } - isEditing={false} isFullscreen={false} onAddPanel={[Function]} /> @@ -156,8 +153,6 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1` "canSave": true, "canShare": true, "canStar": true, - "fullscreen": false, - "isEditing": false, "showSettings": true, }, "originalTemplating": Array [], @@ -252,8 +247,6 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1` "canSave": true, "canShare": true, "canStar": true, - "fullscreen": false, - "isEditing": false, "showSettings": true, }, "originalTemplating": Array [], @@ -310,9 +303,9 @@ exports[`DashboardPage Dashboard init completed Should render dashboard grid 1` "version": 0, } } - isEditing={false} - isFullscreen={false} + editPanel={null} scrollTop={0} + viewPanel={null} />
    @@ -378,8 +371,6 @@ exports[`DashboardPage When dashboard has editview url state should render setti "canSave": true, "canShare": true, "canStar": true, - "fullscreen": false, - "isEditing": false, "showSettings": true, }, "originalTemplating": Array [], @@ -436,7 +427,6 @@ exports[`DashboardPage When dashboard has editview url state should render setti "version": 0, } } - isEditing={false} isFullscreen={false} onAddPanel={[Function]} /> @@ -494,8 +484,6 @@ exports[`DashboardPage When dashboard has editview url state should render setti "canSave": true, "canShare": true, "canStar": true, - "fullscreen": false, - "isEditing": false, "showSettings": true, }, "originalTemplating": Array [], @@ -590,8 +578,6 @@ exports[`DashboardPage When dashboard has editview url state should render setti "canSave": true, "canShare": true, "canStar": true, - "fullscreen": false, - "isEditing": false, "showSettings": true, }, "originalTemplating": Array [], @@ -648,9 +634,9 @@ exports[`DashboardPage When dashboard has editview url state should render setti "version": 0, } } - isEditing={false} - isFullscreen={false} + editPanel={null} scrollTop={0} + viewPanel={null} />
    @@ -692,8 +678,6 @@ exports[`DashboardPage When dashboard has editview url state should render setti "canSave": true, "canShare": true, "canStar": true, - "fullscreen": false, - "isEditing": false, "showSettings": true, }, "originalTemplating": Array [], diff --git a/public/app/features/dashboard/dashgrid/DashboardGrid.test.tsx b/public/app/features/dashboard/dashgrid/DashboardGrid.test.tsx index 63e7ae03af5..15e74d631f9 100644 --- a/public/app/features/dashboard/dashgrid/DashboardGrid.test.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardGrid.test.tsx @@ -57,8 +57,8 @@ function dashboardGridScenario(description: string, scenarioFn: (ctx: ScenarioCo setupFn = fn; }, props: { - isEditing: false, - isFullscreen: false, + editPanel: null, + viewPanel: null, scrollTop: 0, dashboard: getTestDashboard(), }, diff --git a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx index 0f0ba9db0a5..2eaacce225e 100644 --- a/public/app/features/dashboard/dashgrid/DashboardGrid.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardGrid.tsx @@ -15,7 +15,6 @@ import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN, GRID_COLUMN_COUNT } from 'app/core import { DashboardPanel } from './DashboardPanel'; import { DashboardModel, PanelModel } from '../state'; import { CoreEvents } from 'app/types'; -import { PanelEvents } from '@grafana/data'; import { panelAdded, panelRemoved } from '../state/PanelModel'; let lastGridWidth = 1200; @@ -33,7 +32,7 @@ interface GridWrapperProps { className: string; isResizable?: boolean; isDraggable?: boolean; - isFullscreen?: boolean; + viewPanel: PanelModel | null; } function GridWrapper({ @@ -48,7 +47,7 @@ function GridWrapper({ className, isResizable, isDraggable, - isFullscreen, + viewPanel, }: GridWrapperProps) { const width = size.width > 0 ? size.width : lastGridWidth; @@ -56,7 +55,7 @@ function GridWrapper({ if (width !== lastGridWidth) { if (ignoreNextWidthChange) { ignoreNextWidthChange = false; - } else if (!isFullscreen && Math.abs(width - lastGridWidth) > 8) { + } else if (!viewPanel && Math.abs(width - lastGridWidth) > 8) { onWidthChange(); lastGridWidth = width; } @@ -95,8 +94,8 @@ const SizedReactLayoutGrid = sizeMe({ monitorWidth: true })(GridWrapper); export interface Props { dashboard: DashboardModel; - isEditing: boolean; - isFullscreen: boolean; + editPanel: PanelModel | null; + viewPanel: PanelModel | null; scrollTop: number; isNewEditorOpen?: boolean; } @@ -111,7 +110,6 @@ export class DashboardGrid extends PureComponent { dashboard.on(panelAdded, this.triggerForceUpdate); dashboard.on(panelRemoved, this.triggerForceUpdate); dashboard.on(CoreEvents.repeatsProcessed, this.triggerForceUpdate); - dashboard.on(PanelEvents.viewModeChanged, this.onViewModeChanged); dashboard.on(CoreEvents.rowCollapsed, this.triggerForceUpdate); dashboard.on(CoreEvents.rowExpanded, this.triggerForceUpdate); } @@ -121,7 +119,6 @@ export class DashboardGrid extends PureComponent { dashboard.off(panelAdded, this.triggerForceUpdate); dashboard.off(panelRemoved, this.triggerForceUpdate); dashboard.off(CoreEvents.repeatsProcessed, this.triggerForceUpdate); - dashboard.off(PanelEvents.viewModeChanged, this.onViewModeChanged); dashboard.off(CoreEvents.rowCollapsed, this.triggerForceUpdate); dashboard.off(CoreEvents.rowExpanded, this.triggerForceUpdate); } @@ -181,10 +178,6 @@ export class DashboardGrid extends PureComponent { } }; - onViewModeChanged = () => { - ignoreNextWidthChange = true; - }; - updateGridPos = (item: ReactGridLayout.Layout, layout: ReactGridLayout.Layout[]) => { this.panelMap[item.i!].updateGridPos(item); @@ -207,7 +200,7 @@ export class DashboardGrid extends PureComponent { }; isInView = (panel: PanelModel): boolean => { - if (panel.fullscreen || panel.isEditing) { + if (panel.isViewing || panel.isEditing) { return true; } @@ -248,7 +241,7 @@ export class DashboardGrid extends PureComponent { const panelElements = []; for (const panel of this.props.dashboard.panels) { - const panelClasses = classNames({ 'react-grid-item--fullscreen': panel.fullscreen }); + const panelClasses = classNames({ 'react-grid-item--fullscreen': panel.isViewing }); const id = panel.id.toString(); panel.isInView = this.isInView(panel); @@ -276,14 +269,14 @@ export class DashboardGrid extends PureComponent { panel={panel} dashboard={this.props.dashboard} isEditing={panel.isEditing} - isFullscreen={panel.fullscreen} + isViewing={panel.isViewing} isInView={panel.isInView} /> ); } render() { - const { dashboard, isFullscreen } = this.props; + const { dashboard, viewPanel } = this.props; return ( { onDragStop={this.onDragStop} onResize={this.onResize} onResizeStop={this.onResizeStop} - isFullscreen={isFullscreen} + viewPanel={viewPanel} > {this.renderPanels()} diff --git a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx index d9a0a14ea39..f1a2da00efa 100644 --- a/public/app/features/dashboard/dashgrid/DashboardPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardPanel.tsx @@ -6,8 +6,6 @@ import { connect, MapStateToProps, MapDispatchToProps } from 'react-redux'; // Components import { PanelChrome } from './PanelChrome'; -import { PanelEditor } from '../panel_editor/PanelEditor'; -import { PanelResizer } from './PanelResizer'; import { PanelChromeAngular } from './PanelChromeAngular'; // Actions @@ -23,8 +21,7 @@ export interface OwnProps { panel: PanelModel; dashboard: DashboardModel; isEditing: boolean; - isInEditMode?: boolean; - isFullscreen: boolean; + isViewing: boolean; isInView: boolean; } @@ -74,12 +71,10 @@ export class DashboardPanelUnconnected extends PureComponent { }; renderPanel(plugin: PanelPlugin) { - const { dashboard, panel, isFullscreen, isEditing, isInView, isInEditMode, updateLocation } = this.props; - - const autoSizerStyle = { height: isEditing ? '100%' : '' }; + const { dashboard, panel, isViewing, isInView, isEditing, updateLocation } = this.props; return ( - + {({ width, height }) => { if (width === 0) { return null; @@ -91,7 +86,8 @@ export class DashboardPanelUnconnected extends PureComponent { plugin={plugin} panel={panel} dashboard={dashboard} - isFullscreen={isFullscreen} + isViewing={isViewing} + isEditing={isEditing} isInView={isInView} width={width} height={height} @@ -104,9 +100,9 @@ export class DashboardPanelUnconnected extends PureComponent { plugin={plugin} panel={panel} dashboard={dashboard} - isFullscreen={isFullscreen} + isViewing={isViewing} + isEditing={isEditing} isInView={isInView} - isInEditMode={isInEditMode} width={width} height={height} updateLocation={updateLocation} @@ -118,7 +114,7 @@ export class DashboardPanelUnconnected extends PureComponent { } render() { - const { panel, dashboard, isFullscreen, isEditing, plugin } = this.props; + const { isViewing, plugin } = this.props; const { isLazy } = this.state; // if we have not loaded plugin exports yet, wait @@ -131,34 +127,14 @@ export class DashboardPanelUnconnected extends PureComponent { return null; } - const editorContainerClasses = classNames({ - 'panel-editor-container': isEditing, - 'panel-height-helper': !isEditing, - }); - const panelWrapperClass = classNames({ 'panel-wrapper': true, - 'panel-wrapper--edit': isEditing, - 'panel-wrapper--view': isFullscreen && !isEditing, + 'panel-wrapper--view': isViewing, }); return ( -
    - ( -
    - {this.renderPanel(plugin)} -
    - )} - /> - {panel.isEditing && } +
    + {this.renderPanel(plugin)}
    ); } diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index cd0f048bbb0..46ed3d07d1c 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -33,9 +33,9 @@ export interface Props { panel: PanelModel; dashboard: DashboardModel; plugin: PanelPlugin; - isFullscreen: boolean; + isViewing: boolean; + isEditing?: boolean; isInView: boolean; - isInEditMode?: boolean; width: number; height: number; updateLocation: typeof updateLocation; @@ -69,7 +69,8 @@ export class PanelChrome extends PureComponent { } componentDidMount() { - const { panel, dashboard, isInEditMode } = this.props; + const { panel, dashboard, isEditing } = this.props; + panel.events.on(PanelEvents.refresh, this.onRefresh); panel.events.on(PanelEvents.render, this.onRender); dashboard.panelInitialized(this.props.panel); @@ -85,7 +86,7 @@ export class PanelChrome extends PureComponent { isFirstLoad: false, }); } else { - if (isInEditMode) { + if (isEditing) { this.querySubscription = panel .getQueryRunner() .getData() @@ -319,7 +320,7 @@ export class PanelChrome extends PureComponent { } render() { - const { dashboard, panel, isFullscreen, width, height, updateLocation } = this.props; + const { dashboard, panel, isViewing, isEditing, width, height, updateLocation } = this.props; const { errorMessage, data } = this.state; const { transparent } = panel; @@ -340,7 +341,8 @@ export class PanelChrome extends PureComponent { scopedVars={panel.scopedVars} links={panel.links} error={errorMessage} - isFullscreen={isFullscreen} + isEditing={isEditing} + isViewing={isViewing} data={data} updateLocation={updateLocation} /> diff --git a/public/app/features/dashboard/dashgrid/PanelChromeAngular.tsx b/public/app/features/dashboard/dashgrid/PanelChromeAngular.tsx index b419b95e1ab..0baa755bf6c 100644 --- a/public/app/features/dashboard/dashgrid/PanelChromeAngular.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChromeAngular.tsx @@ -24,7 +24,8 @@ interface OwnProps { panel: PanelModel; dashboard: DashboardModel; plugin: PanelPlugin; - isFullscreen: boolean; + isViewing: boolean; + isEditing: boolean; isInView: boolean; width: number; height: number; @@ -217,7 +218,7 @@ export class PanelChromeAngularUnconnected extends PureComponent { } render() { - const { dashboard, panel, isFullscreen, plugin, angularComponent, updateLocation } = this.props; + const { dashboard, panel, isViewing, isEditing, plugin, angularComponent, updateLocation } = this.props; const { errorMessage, data, alertState } = this.state; const { transparent } = panel; @@ -246,7 +247,8 @@ export class PanelChromeAngularUnconnected extends PureComponent { angularComponent={angularComponent} links={panel.links} error={errorMessage} - isFullscreen={isFullscreen} + isViewing={isViewing} + isEditing={isEditing} data={data} updateLocation={updateLocation} /> diff --git a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.tsx b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.tsx index 02361606e1b..04d5b664d1b 100644 --- a/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.tsx +++ b/public/app/features/dashboard/dashgrid/PanelHeader/PanelHeader.tsx @@ -25,7 +25,8 @@ export interface Props { angularComponent?: AngularComponent | null; links?: DataLink[]; error?: string; - isFullscreen: boolean; + isViewing: boolean; + isEditing: boolean; data: PanelData; updateLocation: typeof updateLocation; } @@ -121,13 +122,13 @@ export class PanelHeader extends Component { }; render() { - const { panel, scopedVars, error, isFullscreen, data } = this.props; + const { panel, scopedVars, error, isViewing, isEditing, data } = this.props; const { menuItems } = this.state; const title = templateSrv.replaceWithText(panel.title, scopedVars); const panelHeaderClass = classNames({ 'panel-header': true, - 'grid-drag-handle': !isFullscreen, + 'grid-drag-handle': !(isViewing || isEditing), }); // dedupe on severity diff --git a/public/app/features/dashboard/dashgrid/__snapshots__/DashboardGrid.test.tsx.snap b/public/app/features/dashboard/dashgrid/__snapshots__/DashboardGrid.test.tsx.snap index 3efe96c594b..a8e39e9555b 100644 --- a/public/app/features/dashboard/dashgrid/__snapshots__/DashboardGrid.test.tsx.snap +++ b/public/app/features/dashboard/dashgrid/__snapshots__/DashboardGrid.test.tsx.snap @@ -4,7 +4,6 @@ exports[`DashboardGrid Can render dashboard grid Should render 1`] = `
    { - const urlParams = this.$location.search(); - - // handle toggle logic - // I hate using these truthy converters (!!) but in this case - // I think it's appropriate. edit can be null/false/undefined and - // here i want all of those to compare the same - if (fullscreen === urlParams.fullscreen && edit === !!urlParams.edit) { - const paramsToRemove = ['fullscreen', 'edit', 'panelId', 'tab']; - for (const key of paramsToRemove) { - delete urlParams[key]; - } - - this.$location.search(urlParams); - return; - } - - const newUrlParams = { - ...urlParams, - fullscreen: fullscreen || undefined, - edit: edit || undefined, - tab: edit ? urlParams.tab : undefined, - panelId, - }; - - Object.keys(newUrlParams).forEach(key => { - if (newUrlParams[key] === undefined) { - delete newUrlParams[key]; - } - }); - - this.$location.search(newUrlParams); - }; - saveJSONDashboard(json: string) { const parsedJson = JSON.parse(json); return getBackendSrv().saveDashboard(parsedJson, { diff --git a/public/app/features/dashboard/state/DashboardModel.ts b/public/app/features/dashboard/state/DashboardModel.ts index 65703152229..d1768872e63 100644 --- a/public/app/features/dashboard/state/DashboardModel.ts +++ b/public/app/features/dashboard/state/DashboardModel.ts @@ -51,6 +51,7 @@ export class DashboardModel { gnetId: any; panels: PanelModel[]; panelInEdit?: PanelModel; + panelInView: PanelModel; // ------------------ // not persisted @@ -69,6 +70,7 @@ export class DashboardModel { originalTime: true, originalTemplating: true, panelInEdit: true, + panelInView: true, getVariablesFromState: true, }; @@ -144,8 +146,6 @@ export class DashboardModel { meta.canEdit = meta.canEdit !== false; meta.showSettings = meta.canEdit; meta.canMakeEditable = meta.canSave && !this.editable; - meta.fullscreen = false; - meta.isEditing = false; if (!this.editable) { meta.canEdit = false; @@ -263,15 +263,6 @@ export class DashboardModel { } } - setViewMode(panel: PanelModel, fullscreen: boolean, isEditing: boolean) { - this.meta.fullscreen = fullscreen; - this.meta.isEditing = isEditing && this.meta.canEdit; - - panel.setViewMode(fullscreen, this.meta.isEditing); - - this.events.emit(PanelEvents.viewModeChanged, panel); - } - timeRangeUpdated(timeRange: TimeRange) { this.events.emit(CoreEvents.timeRangeUpdated, timeRange); if (getConfig().featureToggles.newVariables) { @@ -317,14 +308,24 @@ export class DashboardModel { } otherPanelInFullscreen(panel: PanelModel) { - return (this.meta.fullscreen && !panel.fullscreen) || this.panelInEdit; + return (this.panelInEdit || this.panelInView) && !(panel.isViewing || panel.isEditing); } - initPanelEditor(sourcePanel: PanelModel): PanelModel { + initEditPanel(sourcePanel: PanelModel): PanelModel { this.panelInEdit = sourcePanel.getEditClone(); return this.panelInEdit; } + initViewPanel(panel: PanelModel) { + this.panelInView = panel; + panel.setIsViewing(true); + } + + exitViewPanel(panel: PanelModel) { + this.panelInView = undefined; + panel.setIsViewing(false); + } + exitPanelEditor() { this.panelInEdit = undefined; } diff --git a/public/app/features/dashboard/state/PanelModel.ts b/public/app/features/dashboard/state/PanelModel.ts index f19bf39fcb6..405f563f340 100644 --- a/public/app/features/dashboard/state/PanelModel.ts +++ b/public/app/features/dashboard/state/PanelModel.ts @@ -37,7 +37,7 @@ export interface GridPos { const notPersistedProperties: { [str: string]: boolean } = { events: true, - fullscreen: true, + isViewing: true, isEditing: true, isInView: true, hasRefreshed: true, @@ -134,7 +134,7 @@ export class PanelModel implements DataConfigSource { transparent: boolean; // non persisted - fullscreen: boolean; + isViewing: boolean; isEditing: boolean; isInView: boolean; hasRefreshed: boolean; @@ -215,10 +215,8 @@ export class PanelModel implements DataConfigSource { return model; } - setViewMode(fullscreen: boolean, isEditing: boolean) { - this.fullscreen = fullscreen; - this.isEditing = isEditing; - this.events.emit(PanelEvents.viewModeChanged); + setIsViewing(isViewing: boolean) { + this.isViewing = isViewing; } updateGridPos(newPos: GridPos) { @@ -394,6 +392,7 @@ export class PanelModel implements DataConfigSource { sourceModel.editSourceId = this.id; const clone = new PanelModel(sourceModel); + clone.isEditing = true; const sourceQueryRunner = this.getQueryRunner(); // pipe last result to new clone query runner @@ -466,6 +465,14 @@ export class PanelModel implements DataConfigSource { this.getQueryRunner().resendLastResult(); } + + /* + * Panel have a different id while in edit mode (to more easily be able to discard changes) + * Use this to always get the underlying source id + * */ + getSavedId(): number { + return this.editSourceId ?? this.id; + } } function getPluginVersion(plugin: PanelPlugin): string { diff --git a/public/app/features/dashboard/utils/getPanelMenu.ts b/public/app/features/dashboard/utils/getPanelMenu.ts index e8e9ca79994..dd04b8204dd 100644 --- a/public/app/features/dashboard/utils/getPanelMenu.ts +++ b/public/app/features/dashboard/utils/getPanelMenu.ts @@ -1,6 +1,5 @@ import { updateLocation } from 'app/core/actions'; import { store } from 'app/store/store'; -import config from 'app/core/config'; import { getDataSourceSrv, getLocationSrv, AngularComponent } from '@grafana/runtime'; import { PanelMenuItem } from '@grafana/data'; import { copyPanel, duplicatePanel, editPanelJson, removePanel, sharePanel } from 'app/features/dashboard/utils/panel'; @@ -22,9 +21,7 @@ export function getPanelMenu( store.dispatch( updateLocation({ query: { - panelId: panel.id, - edit: null, - fullscreen: true, + viewPanel: panel.id, }, partial: true, }) @@ -32,20 +29,6 @@ export function getPanelMenu( }; const onEditPanel = (event: React.MouseEvent) => { - event.preventDefault(); - store.dispatch( - updateLocation({ - query: { - panelId: panel.id, - edit: true, - fullscreen: true, - }, - partial: true, - }) - ); - }; - - const onNewEditPanel = (event: React.MouseEvent) => { event.preventDefault(); store.dispatch( updateLocation({ @@ -143,18 +126,9 @@ export function getPanelMenu( shortcut: 'p i', }); - if (config.featureToggles.newEdit) { - menu.push({ - text: 'New edit', - iconClassName: 'edit', - onClick: onNewEditPanel, - shortcut: 'p i', - }); - } - const subMenu: PanelMenuItem[] = []; - if (!panel.fullscreen && dashboard.canEditPanel(panel)) { + if (dashboard.canEditPanel(panel) && !(panel.isViewing || panel.isEditing)) { subMenu.push({ text: 'Duplicate', onClick: onDuplicatePanel, diff --git a/public/app/features/explore/ExploreToolbar.tsx b/public/app/features/explore/ExploreToolbar.tsx index 179cd962609..a863c7ba56c 100644 --- a/public/app/features/explore/ExploreToolbar.tsx +++ b/public/app/features/explore/ExploreToolbar.tsx @@ -1,4 +1,3 @@ -import omitBy from 'lodash/omitBy'; import React, { PureComponent } from 'react'; import { connect } from 'react-redux'; import { hot } from 'react-hot-loader'; @@ -133,18 +132,15 @@ export class UnConnectedExploreToolbar extends PureComponent { }); } - const dashViewOptions = { - fullscreen: withChanges || dash.meta.fullscreen, - edit: withChanges || dash.meta.isEditing, - }; + const query: any = {}; - this.props.updateLocation({ - path: `/d/${dash.uid}/:${titleSlug}`, - query: { - ...omitBy(dashViewOptions, v => !v), - panelId: originPanelId, - }, - }); + if (withChanges || dash.panelInEdit) { + query.editPanel = originPanelId; + } else if (dash.panelInView) { + query.viewPanel = originPanelId; + } + + this.props.updateLocation({ path: `/d/${dash.uid}/:${titleSlug}`, query }); }; // Remove explore specific parameters from queries diff --git a/public/app/features/panel/panel_ctrl.ts b/public/app/features/panel/panel_ctrl.ts index 611176c1eef..d49f3ed56b3 100644 --- a/public/app/features/panel/panel_ctrl.ts +++ b/public/app/features/panel/panel_ctrl.ts @@ -95,7 +95,7 @@ export class PanelCtrl { } otherPanelInFullscreenMode() { - return this.dashboard.meta.fullscreen && !this.panel.fullscreen; + return this.dashboard.otherPanelInFullscreen(this.panel); } render(payload?: any) { diff --git a/public/app/features/panel/panel_directive.ts b/public/app/features/panel/panel_directive.ts index 85511280fa4..07b46b76ee0 100644 --- a/public/app/features/panel/panel_directive.ts +++ b/public/app/features/panel/panel_directive.ts @@ -65,17 +65,6 @@ module.directive('grafanaPanel', ($rootScope, $document, $timeout) => { }); } - function onViewModeChanged() { - // first wait one pass for dashboard fullscreen view mode to take effect (classses being applied) - setTimeout(() => { - // then wait another cycle (this might not be needed) - $timeout(() => { - ctrl.render(); - resizeScrollableContent(); - }); - }, 10); - } - function onPanelModelRender(payload?: any) { ctrl.height = scope.$parent.$parent.size.height; ctrl.width = scope.$parent.$parent.size.width; @@ -89,7 +78,6 @@ module.directive('grafanaPanel', ($rootScope, $document, $timeout) => { panel.events.on(PanelEvents.refresh, onPanelModelRefresh); panel.events.on(PanelEvents.render, onPanelModelRender); panel.events.on(PanelEvents.panelSizeChanged, onPanelSizeChanged); - panel.events.on(PanelEvents.viewModeChanged, onViewModeChanged); scope.$on('$destroy', () => { elem.off(); diff --git a/public/app/plugins/datasource/dashboard/runSharedRequest.ts b/public/app/plugins/datasource/dashboard/runSharedRequest.ts index 50d9c09a5ac..e243db51129 100644 --- a/public/app/plugins/datasource/dashboard/runSharedRequest.ts +++ b/public/app/plugins/datasource/dashboard/runSharedRequest.ts @@ -43,7 +43,7 @@ export function runSharedRequest(options: QueryRunnerOptions): Observable(BarGaugePanel) .addRadio({ path: 'displayMode', name: 'Display mode', - description: 'Controls the bar style', settings: { options: displayModes, }, @@ -24,7 +23,7 @@ export const plugin = new PanelPlugin(BarGaugePanel) name: 'Show unfilled area', description: 'When enabled renders the unfilled region as gray', defaultValue: true, - showIf: options => options.displayMode !== 'lcd', + showIf: (options: BarGaugeOptions) => options.displayMode !== 'lcd', }); }) .setPanelChangeHandler(sharedSingleStatPanelChangedHandler) diff --git a/public/app/types/dashboard.ts b/public/app/types/dashboard.ts index 0c77795c6ba..16cf58b4fcf 100644 --- a/public/app/types/dashboard.ts +++ b/public/app/types/dashboard.ts @@ -18,9 +18,7 @@ export interface DashboardMeta { canAdmin?: boolean; url?: string; folderId?: number; - fullscreen?: boolean; fromExplore?: boolean; - isEditing?: boolean; canMakeEditable?: boolean; submenuEnabled?: boolean; provisioned?: boolean; diff --git a/public/sass/components/_panel_editor.scss b/public/sass/components/_panel_editor.scss index 0b7b2cb014f..2e006b6eac5 100644 --- a/public/sass/components/_panel_editor.scss +++ b/public/sass/components/_panel_editor.scss @@ -8,11 +8,6 @@ height: 100%; position: relative; - &--edit { - height: 40%; - margin: 0 $dashboard-padding; - } - &--view { flex: 1 1 0; height: 90%;