diff --git a/src/core_plugins/kibana/public/dashboard/dashboard.html b/src/core_plugins/kibana/public/dashboard/dashboard.html index be061053e81a..93036579a4d8 100644 --- a/src/core_plugins/kibana/public/dashboard/dashboard.html +++ b/src/core_plugins/kibana/public/dashboard/dashboard.html @@ -57,12 +57,12 @@ -
+

This dashboard is empty. Let's fill it up!

Click the Add button in the menu bar above to add a visualization to the dashboard.
If you haven't setup a visualization yet visit "Visualize" to create your first visualization.

-
+

This dashboard is empty. Let's fill it up!

Click the Edit button in the menu bar above to start working on your new dashboard.

diff --git a/src/core_plugins/kibana/public/dashboard/dashboard.js b/src/core_plugins/kibana/public/dashboard/dashboard.js index c58efc5de319..4854d8fe8230 100644 --- a/src/core_plugins/kibana/public/dashboard/dashboard.js +++ b/src/core_plugins/kibana/public/dashboard/dashboard.js @@ -119,8 +119,8 @@ app.directive('dashboardApp', function (Notifier, courier, AppState, timefilter, dashboardState.getIsDirty(timefilter)); $scope.newDashboard = () => { kbnUrl.change(DashboardConstants.CREATE_NEW_DASHBOARD_URL, {}); }; $scope.saveState = () => dashboardState.saveState(); - $scope.showEditHelpText = () => !dashboardState.getPanels().length && dashboardState.getIsEditMode(); - $scope.showViewHelpText = () => !dashboardState.getPanels().length && dashboardState.getIsViewMode(); + $scope.getShouldShowEditHelp = () => !dashboardState.getPanels().length && dashboardState.getIsEditMode(); + $scope.getShouldShowViewHelp = () => !dashboardState.getPanels().length && dashboardState.getIsViewMode(); $scope.toggleExpandPanel = (panelIndex) => { if ($scope.expandedPanel && $scope.expandedPanel.panelIndex === panelIndex) { @@ -179,8 +179,8 @@ app.directive('dashboardApp', function (Notifier, courier, AppState, timefilter, const onChangeViewMode = (newMode) => { const isPageRefresh = newMode === dashboardState.getViewMode(); - const leavingEditMode = !isPageRefresh && newMode === DashboardViewMode.VIEW; - const willLoseChanges = leavingEditMode && dashboardState.getIsDirty(timefilter); + const isLeavingEditMode = !isPageRefresh && newMode === DashboardViewMode.VIEW; + const willLoseChanges = isLeavingEditMode && dashboardState.getIsDirty(timefilter); if (!willLoseChanges) { updateViewMode(newMode); @@ -189,8 +189,10 @@ app.directive('dashboardApp', function (Notifier, courier, AppState, timefilter, function revertChangesAndExitEditMode() { dashboardState.resetState(); - const refreshUrl = dashboardState.getReloadDashboardUrl(); - kbnUrl.change(refreshUrl.url, refreshUrl.options); + const refreshUrl = dash.id ? + DashboardConstants.EXISTING_DASHBOARD_URL : DashboardConstants.CREATE_NEW_DASHBOARD_URL; + const refreshUrlOptions = dash.id ? { id: dash.id } : {}; + kbnUrl.change(refreshUrl, refreshUrlOptions); // This is only necessary for new dashboards, which will default to Edit mode. updateViewMode(DashboardViewMode.VIEW); @@ -203,7 +205,7 @@ app.directive('dashboardApp', function (Notifier, courier, AppState, timefilter, } confirmModal( - DashboardStrings.getUnsavedChangesWarningMessage(dashboardState.getChangedFiltersForDisplay(timefilter)), + getUnsavedChangesWarningMessage(dashboardState.getChangedFilterTypes(timefilter)), { onConfirm: revertChangesAndExitEditMode, onCancel: _.noop, diff --git a/src/core_plugins/kibana/public/dashboard/dashboard_state.js b/src/core_plugins/kibana/public/dashboard/dashboard_state.js index 81c752593b36..36e1ed43a13b 100644 --- a/src/core_plugins/kibana/public/dashboard/dashboard_state.js +++ b/src/core_plugins/kibana/public/dashboard/dashboard_state.js @@ -57,19 +57,19 @@ function areTimesEqual(timeA, timeB) { export class DashboardState { /** * - * @param dashboard {SavedDashboard} + * @param savedDashboard {SavedDashboard} * @param AppState {AppState} */ - constructor(dashboard, AppState) { - this.dashboard = dashboard; + constructor(savedDashboard, AppState) { + this.savedDashboard = savedDashboard; - this.stateDefaults = getStateDefaults(this.dashboard); + this.stateDefaults = getStateDefaults(this.savedDashboard); this.appState = new AppState(this.stateDefaults); this.uiState = this.appState.makeStateful('uiState'); this.isDirty = false; - // We can't compare the filters stored on this.appState to this.dashboard because in order to apply + // We can't compare the filters stored on this.appState to this.savedDashboard because in order to apply // the filters to the visualizations, we need to save it on the dashboard. We keep track of the original // filter state in order to let the user know if their filters changed and provide this specific information //in the 'lose changes' warning message. @@ -88,8 +88,8 @@ export class DashboardState { // The right way to fix this might be to ensure the defaults object stored on state is a deep // clone, but given how much code uses the state object, I determined that to be too risky of a change for // now. TODO: revisit this! - this.stateDefaults = getStateDefaults(this.dashboard); - // The original query won't be restored by the above because the query on this.dashboard is applied + this.stateDefaults = getStateDefaults(this.savedDashboard); + // The original query won't be restored by the above because the query on this.savedDashboard is applied // in place in order for it to affect the visualizations. this.stateDefaults.query = this.lastSavedDashboardFilters.query; // Need to make a copy to ensure they are not overwritten. @@ -102,13 +102,13 @@ export class DashboardState { } /** - * Returns an object which contains the current filter state of this.dashboard. + * Returns an object which contains the current filter state of this.savedDashboard. * @returns {{timeTo: String, timeFrom: String, filterBars: Array, query: Object}} */ getFilterState() { return { - timeTo: this.dashboard.timeTo, - timeFrom: this.dashboard.timeFrom, + timeTo: this.savedDashboard.timeTo, + timeFrom: this.savedDashboard.timeFrom, filterBars: this.getDashboardFilterBars(), query: this.getDashboardQuery() }; @@ -153,15 +153,15 @@ export class DashboardState { * @returns {boolean} */ getIsTimeSavedWithDashboard() { - return this.dashboard.timeRestore; + return this.savedDashboard.timeRestore; } getDashboardFilterBars() { - return FilterUtils.getFilterBarsForDashboard(this.dashboard); + return FilterUtils.getFilterBarsForDashboard(this.savedDashboard); } getDashboardQuery() { - return FilterUtils.getQueryFilterForDashboard(this.dashboard); + return FilterUtils.getQueryFilterForDashboard(this.savedDashboard); } getLastSavedFilterBars() { @@ -260,19 +260,10 @@ export class DashboardState { } /** - * @return {boolean} True if filters (query, filter bar filters, and time picker if time is stored - * with the dashboard) have changed since the last saved state (or if the dashboard hasn't been saved, - * the default state). + * @param timeFilter + * @returns {Array.} An array of user friendly strings indicating the filter types that have changed. */ - getFiltersChanged(timeFilter) { - return ( - this.getQueryChanged() || - this.getFilterBarChanged() || - (this.dashboard.timeRestore && this.getTimeChanged(timeFilter)) - ); - } - - getChangedFiltersForDisplay(timeFilter) { + getChangedFilterTypes(timeFilter) { const changedFilters = []; if (this.getFilterBarChanged()) { changedFilters.push('filter'); @@ -280,12 +271,21 @@ export class DashboardState { if (this.getQueryChanged()) { changedFilters.push('query'); } - if (this.dashboard.timeRestore && this.getTimeChanged(timeFilter)) { + if (this.savedDashboard.timeRestore && this.getTimeChanged(timeFilter)) { changedFilters.push('time range'); } return changedFilters; } + /** + * @return {boolean} True if filters (query, filter bar filters, and time picker if time is stored + * with the dashboard) have changed since the last saved state (or if the dashboard hasn't been saved, + * the default state). + */ + getFiltersChanged(timeFilter) { + return this.getChangedFilterTypes(timeFilter).length > 0; + } + /** * Updates timeFilter to match the time saved with the dashboard. * @param timeFilter @@ -293,23 +293,23 @@ export class DashboardState { */ syncTimefilterWithDashboard(timeFilter, quickTimeRanges) { if (!this.getIsTimeSavedWithDashboard()) { - throw 'The time is not saved with this dashboard so should not be synced.'; + throw new Error('The time is not saved with this dashboard so should not be synced.'); } - timeFilter.time.to = this.dashboard.timeTo; - timeFilter.time.from = this.dashboard.timeFrom; - const isMoment = moment(this.dashboard.timeTo).isValid(); + timeFilter.time.to = this.savedDashboard.timeTo; + timeFilter.time.from = this.savedDashboard.timeFrom; + const isMoment = moment(this.savedDashboard.timeTo).isValid(); if (isMoment) { timeFilter.time.mode = 'absolute'; } else { const quickTime = _.find( quickTimeRanges, - (timeRange) => timeRange.from === this.dashboard.timeFrom && timeRange.to === this.dashboard.timeTo); + (timeRange) => timeRange.from === this.savedDashboard.timeFrom && timeRange.to === this.savedDashboard.timeTo); timeFilter.time.mode = quickTime ? 'quick' : 'relative'; } - if (this.dashboard.refreshInterval) { - timeFilter.refreshInterval = this.dashboard.refreshInterval; + if (this.savedDashboard.refreshInterval) { + timeFilter.refreshInterval = this.savedDashboard.refreshInterval; } } @@ -333,19 +333,19 @@ export class DashboardState { this.saveState(); const timeRestoreObj = _.pick(timeFilter.refreshInterval, ['display', 'pause', 'section', 'value']); - this.dashboard.title = this.appState.title; - this.dashboard.timeRestore = this.appState.timeRestore; - this.dashboard.panelsJSON = toJson(this.appState.panels); - this.dashboard.uiStateJSON = toJson(this.uiState.getChanges()); - this.dashboard.timeFrom = this.dashboard.timeRestore ? convertTimeToString(timeFilter.time.from) : undefined; - this.dashboard.timeTo = this.dashboard.timeRestore ? convertTimeToString(timeFilter.time.to) : undefined; - this.dashboard.refreshInterval = this.dashboard.timeRestore ? timeRestoreObj : undefined; - this.dashboard.optionsJSON = toJson(this.appState.options); - - return this.dashboard.save() + this.savedDashboard.title = this.appState.title; + this.savedDashboard.timeRestore = this.appState.timeRestore; + this.savedDashboard.panelsJSON = toJson(this.appState.panels); + this.savedDashboard.uiStateJSON = toJson(this.uiState.getChanges()); + this.savedDashboard.timeFrom = this.savedDashboard.timeRestore ? convertTimeToString(timeFilter.time.from) : undefined; + this.savedDashboard.timeTo = this.savedDashboard.timeRestore ? convertTimeToString(timeFilter.time.to) : undefined; + this.savedDashboard.refreshInterval = this.savedDashboard.timeRestore ? timeRestoreObj : undefined; + this.savedDashboard.optionsJSON = toJson(this.appState.options); + + return this.savedDashboard.save() .then((id) => { this.lastSavedDashboardFilters = this.getFilterState(); - this.stateDefaults = getStateDefaults(this.dashboard); + this.stateDefaults = getStateDefaults(this.savedDashboard); this.stateDefaults.viewMode = DashboardViewMode.VIEW; // Make sure new app state defaults are using the new defaults. this.appState.setDefaults(this.stateDefaults); @@ -361,11 +361,11 @@ export class DashboardState { applyFilters(query, filters) { this.appState.query = query; if (this.appState.query) { - this.dashboard.searchSource.set('filter', _.union(filters, [{ + this.savedDashboard.searchSource.set('filter', _.union(filters, [{ query: this.appState.query }])); } else { - this.dashboard.searchSource.set('filter', filters); + this.savedDashboard.searchSource.set('filter', filters); } this.saveState(); @@ -401,17 +401,6 @@ export class DashboardState { if (this.stateMonitor) { this.stateMonitor.destroy(); } - this.dashboard.destroy(); - } - - /** - * Returns a url and url options that can be used to reload the page. - * @returns {{url: string, options: Object}} - */ - getReloadDashboardUrl() { - const url = this.dashboard.id ? - DashboardConstants.EXISTING_DASHBOARD_URL : DashboardConstants.CREATE_NEW_DASHBOARD_URL; - const options = this.dashboard.id ? { id: this.dashboard.id } : {}; - return { url, options }; + this.savedDashboard.destroy(); } } diff --git a/src/core_plugins/kibana/public/dashboard/panel/panel.html b/src/core_plugins/kibana/public/dashboard/panel/panel.html index 407252c5460f..9f7368cb75a3 100644 --- a/src/core_plugins/kibana/public/dashboard/panel/panel.html +++ b/src/core_plugins/kibana/public/dashboard/panel/panel.html @@ -6,7 +6,8 @@ diff --git a/test/functional/apps/dashboard/_dashboard.js b/test/functional/apps/dashboard/_dashboard.js index 586647dee8c4..ed84012deffd 100644 --- a/test/functional/apps/dashboard/_dashboard.js +++ b/test/functional/apps/dashboard/_dashboard.js @@ -82,13 +82,17 @@ bdd.describe('dashboard tab', function describeIndexTests() { }); }); -bdd.it('filters when a pie chart slice is clicked', async function () { - let descriptions = await PageObjects.dashboard.getFilterDescriptions(1000); - expect(descriptions.length).to.equal(0); +bdd.describe('filters', async function () { + bdd.it('are not selected by default', async function () { + const descriptions = await PageObjects.dashboard.getFilterDescriptions(1000); + expect(descriptions.length).to.equal(0); + }); - await PageObjects.dashboard.filterOnPieSlice(); - descriptions = await PageObjects.dashboard.getFilterDescriptions(); - expect(descriptions.length).to.equal(1); + bdd.it('are added when a pie chart slice is clicked', async function () { + await PageObjects.dashboard.filterOnPieSlice(); + const descriptions = await PageObjects.dashboard.getFilterDescriptions(); + expect(descriptions.length).to.equal(1); + }); }); bdd.it('retains dark theme in state', async function () {