[Dashboard] Retain Viewmode State in Session (#112302) (#112707)

* Made dashboard retain viewmode state in session. This means filters and query will be kept over reloads and navigations
This commit is contained in:
Devon Thomson 2021-09-21 14:06:29 -06:00 committed by GitHub
parent 8b66056672
commit 1334789c5d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 125 additions and 78 deletions

View file

@ -237,27 +237,25 @@ export const useDashboardAppState = ({
.pipe(debounceTime(DashboardConstants.CHANGE_CHECK_DEBOUNCE))
.subscribe((states) => {
const [lastSaved, current] = states;
const unsavedChanges =
current.viewMode === ViewMode.EDIT ? diffDashboardState(lastSaved, current) : {};
const unsavedChanges = diffDashboardState(lastSaved, current);
let savedTimeChanged = false;
const savedTimeChanged =
lastSaved.timeRestore &&
!areTimeRangesEqual(
{
from: savedDashboard?.timeFrom,
to: savedDashboard?.timeTo,
},
timefilter.getTime()
);
/**
* changes to the time filter should only be considered 'unsaved changes' when
* changes to the dashboard should only be considered 'unsaved changes' when
* editing the dashboard
*/
if (current.viewMode === ViewMode.EDIT) {
savedTimeChanged =
lastSaved.timeRestore &&
!areTimeRangesEqual(
{
from: savedDashboard?.timeFrom,
to: savedDashboard?.timeTo,
},
timefilter.getTime()
);
}
const hasUnsavedChanges = Object.keys(unsavedChanges).length > 0 || savedTimeChanged;
const hasUnsavedChanges =
current.viewMode === ViewMode.EDIT &&
(Object.keys(unsavedChanges).length > 0 || savedTimeChanged);
setDashboardAppState((s) => ({ ...s, hasUnsavedChanges }));
unsavedChanges.viewMode = current.viewMode; // always push view mode into session store.

View file

@ -11,6 +11,7 @@ import { Storage } from '../../services/kibana_utils';
import { NotificationsStart } from '../../services/core';
import { panelStorageErrorStrings } from '../../dashboard_strings';
import { DashboardState } from '../../types';
import { ViewMode } from '../../services/embeddable';
export const DASHBOARD_PANELS_UNSAVED_ID = 'unsavedDashboard';
const DASHBOARD_PANELS_SESSION_KEY = 'dashboardStateManagerPanels';
@ -69,6 +70,7 @@ export class DashboardSessionStorage {
const dashboardsWithUnsavedChanges: string[] = [];
Object.keys(dashboardStatesInSpace).map((dashboardId) => {
if (
dashboardStatesInSpace[dashboardId].viewMode === ViewMode.EDIT &&
Object.keys(dashboardStatesInSpace[dashboardId]).some(
(stateKey) => stateKey !== 'viewMode'
)

View file

@ -12,6 +12,9 @@ import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const PageObjects = getPageObjects(['dashboard', 'header', 'visualize', 'settings', 'common']);
const browser = getService('browser');
const queryBar = getService('queryBar');
const filterBar = getService('filterBar');
const esArchiver = getService('esArchiver');
const testSubjects = getService('testSubjects');
const kibanaServer = getService('kibanaServer');
@ -19,9 +22,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
let originalPanelCount = 0;
let unsavedPanelCount = 0;
const testQuery = 'Test Query';
// FLAKY: https://github.com/elastic/kibana/issues/91191
describe.skip('dashboard unsaved panels', () => {
describe('dashboard unsaved state', () => {
before(async () => {
await esArchiver.load('test/functional/fixtures/es_archiver/dashboard/current/kibana');
await kibanaServer.uiSettings.replace({
@ -31,79 +34,123 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.dashboard.preserveCrossAppState();
await PageObjects.dashboard.loadSavedDashboard('few panels');
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.dashboard.waitForRenderComplete();
originalPanelCount = await PageObjects.dashboard.getPanelCount();
});
it('does not show unsaved changes badge when there are no unsaved changes', async () => {
await testSubjects.missingOrFail('dashboardUnsavedChangesBadge');
describe('view mode state', () => {
before(async () => {
await queryBar.setQuery(testQuery);
await filterBar.addFilter('bytes', 'exists');
await queryBar.submitQuery();
});
const validateQueryAndFilter = async () => {
const query = await queryBar.getQueryString();
expect(query).to.eql(testQuery);
const filterCount = await filterBar.getFilterCount();
expect(filterCount).to.eql(1);
};
it('persists after navigating to the listing page and back', async () => {
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.dashboard.gotoDashboardLandingPage();
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.dashboard.loadSavedDashboard('few panels');
await PageObjects.dashboard.waitForRenderComplete();
await validateQueryAndFilter();
});
it('persists after navigating to Visualize and back', async () => {
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.visualize.gotoVisualizationLandingPage();
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.common.navigateToApp('dashboards');
await PageObjects.dashboard.loadSavedDashboard('few panels');
await PageObjects.dashboard.waitForRenderComplete();
await validateQueryAndFilter();
});
it('persists after a hard refresh', async () => {
await browser.refresh();
const alert = await browser.getAlert();
await alert?.accept();
await PageObjects.dashboard.waitForRenderComplete();
await validateQueryAndFilter();
});
after(async () => {
// discard changes made in view mode
await PageObjects.dashboard.switchToEditMode();
await PageObjects.dashboard.clickCancelOutOfEditMode();
});
});
it('shows the unsaved changes badge after adding panels', async () => {
await PageObjects.dashboard.switchToEditMode();
// add an area chart by value
await dashboardAddPanel.clickEditorMenuButton();
await dashboardAddPanel.clickAggBasedVisualizations();
await PageObjects.visualize.clickAreaChart();
await PageObjects.visualize.clickNewSearch();
await PageObjects.visualize.saveVisualizationAndReturn();
describe('edit mode state', () => {
const addPanels = async () => {
// add an area chart by value
await dashboardAddPanel.clickEditorMenuButton();
await dashboardAddPanel.clickAggBasedVisualizations();
await PageObjects.visualize.clickAreaChart();
await PageObjects.visualize.clickNewSearch();
await PageObjects.visualize.saveVisualizationAndReturn();
// add a metric by reference
await dashboardAddPanel.addVisualization('Rendering-Test: metric');
// add a metric by reference
await dashboardAddPanel.addVisualization('Rendering-Test: metric');
};
await PageObjects.header.waitUntilLoadingHasFinished();
await testSubjects.existOrFail('dashboardUnsavedChangesBadge');
});
it('does not show unsaved changes badge when there are no unsaved changes', async () => {
await testSubjects.missingOrFail('dashboardUnsavedChangesBadge');
});
it('has correct number of panels', async () => {
unsavedPanelCount = await PageObjects.dashboard.getPanelCount();
expect(unsavedPanelCount).to.eql(originalPanelCount + 2);
});
it('shows the unsaved changes badge after adding panels', async () => {
await PageObjects.dashboard.switchToEditMode();
await addPanels();
await PageObjects.header.waitUntilLoadingHasFinished();
await testSubjects.existOrFail('dashboardUnsavedChangesBadge');
});
it('retains unsaved panel count after navigating to listing page and back', async () => {
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.dashboard.gotoDashboardLandingPage();
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.dashboard.loadSavedDashboard('few panels');
await PageObjects.dashboard.switchToEditMode();
const currentPanelCount = await PageObjects.dashboard.getPanelCount();
expect(currentPanelCount).to.eql(unsavedPanelCount);
});
it('has correct number of panels', async () => {
unsavedPanelCount = await PageObjects.dashboard.getPanelCount();
expect(unsavedPanelCount).to.eql(originalPanelCount + 2);
});
it('retains unsaved panel count after navigating to another app and back', async () => {
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.visualize.gotoVisualizationLandingPage();
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.common.navigateToApp('dashboards');
await PageObjects.dashboard.loadSavedDashboard('few panels');
await PageObjects.dashboard.switchToEditMode();
const currentPanelCount = await PageObjects.dashboard.getPanelCount();
expect(currentPanelCount).to.eql(unsavedPanelCount);
});
it('retains unsaved panel count after navigating to listing page and back', async () => {
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.dashboard.gotoDashboardLandingPage();
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.dashboard.loadSavedDashboard('few panels');
const currentPanelCount = await PageObjects.dashboard.getPanelCount();
expect(currentPanelCount).to.eql(unsavedPanelCount);
});
it('resets to original panel count upon entering view mode', async () => {
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.dashboard.clickCancelOutOfEditMode();
await PageObjects.header.waitUntilLoadingHasFinished();
const currentPanelCount = await PageObjects.dashboard.getPanelCount();
expect(currentPanelCount).to.eql(originalPanelCount);
});
it('retains unsaved panel count after navigating to another app and back', async () => {
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.visualize.gotoVisualizationLandingPage();
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.common.navigateToApp('dashboards');
await PageObjects.dashboard.loadSavedDashboard('few panels');
const currentPanelCount = await PageObjects.dashboard.getPanelCount();
expect(currentPanelCount).to.eql(unsavedPanelCount);
});
it('shows unsaved changes badge in view mode if changes have not been discarded', async () => {
await testSubjects.existOrFail('dashboardUnsavedChangesBadge');
});
it('resets to original panel count after discarding changes', async () => {
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.dashboard.clickCancelOutOfEditMode();
await PageObjects.header.waitUntilLoadingHasFinished();
const currentPanelCount = await PageObjects.dashboard.getPanelCount();
expect(currentPanelCount).to.eql(originalPanelCount);
expect(PageObjects.dashboard.getIsInViewMode()).to.eql(true);
});
it('retains unsaved panel count after returning to edit mode', async () => {
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.dashboard.switchToEditMode();
await PageObjects.header.waitUntilLoadingHasFinished();
const currentPanelCount = await PageObjects.dashboard.getPanelCount();
expect(currentPanelCount).to.eql(unsavedPanelCount);
});
it('does not show unsaved changes badge after saving', async () => {
await PageObjects.dashboard.saveDashboard('Unsaved State Test');
await PageObjects.header.waitUntilLoadingHasFinished();
await testSubjects.missingOrFail('dashboardUnsavedChangesBadge');
it('does not show unsaved changes badge after saving', async () => {
await PageObjects.dashboard.switchToEditMode();
await addPanels();
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.dashboard.saveDashboard('Unsaved State Test');
await PageObjects.header.waitUntilLoadingHasFinished();
await testSubjects.missingOrFail('dashboardUnsavedChangesBadge');
});
});
});
}