diff --git a/src/core_plugins/kibana/public/dashboard/components/__snapshots__/exit_full_screen_button.test.js.snap b/src/core_plugins/kibana/public/dashboard/components/__snapshots__/exit_full_screen_button.test.js.snap new file mode 100644 index 000000000000..8f8032a483f4 --- /dev/null +++ b/src/core_plugins/kibana/public/dashboard/components/__snapshots__/exit_full_screen_button.test.js.snap @@ -0,0 +1,33 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`is rendered 1`] = ` +
+ +
+`; diff --git a/src/core_plugins/kibana/public/dashboard/components/exit_full_screen_button.js b/src/core_plugins/kibana/public/dashboard/components/exit_full_screen_button.js new file mode 100644 index 000000000000..02cfe1d3fe20 --- /dev/null +++ b/src/core_plugins/kibana/public/dashboard/components/exit_full_screen_button.js @@ -0,0 +1,56 @@ +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import chrome from 'ui/chrome'; + +import { + KuiButton, +} from 'ui_framework/components'; + +import { + keyCodes, +} from 'ui_framework/services'; + +export class ExitFullScreenButton extends PureComponent { + + onKeyDown = (e) => { + if (e.keyCode === keyCodes.ESCAPE) { + this.props.onExitFullScreenMode(); + } + }; + + componentWillMount() { + document.addEventListener('keydown', this.onKeyDown, false); + chrome.setVisible(false); + } + + componentWillUnmount() { + document.removeEventListener('keydown', this.onKeyDown, false); + chrome.setVisible(true); + } + + render() { + return ( +
+ + + + Exit full screen + + + +
+ ); + } +} + +ExitFullScreenButton.propTypes = { + onExitFullScreenMode: PropTypes.func.isRequired, +}; + diff --git a/src/core_plugins/kibana/public/dashboard/components/exit_full_screen_button.test.js b/src/core_plugins/kibana/public/dashboard/components/exit_full_screen_button.test.js new file mode 100644 index 000000000000..77e2512c453f --- /dev/null +++ b/src/core_plugins/kibana/public/dashboard/components/exit_full_screen_button.test.js @@ -0,0 +1,78 @@ +jest.mock('ui/chrome', + () => ({ + getKibanaVersion: () => '6.0.0', + setVisible: () => {}, + }), { virtual: true }); + +import React from 'react'; +import { render, mount } from 'enzyme'; +import sinon from 'sinon'; +import chrome from 'ui/chrome'; + +import { + ExitFullScreenButton, +} from './exit_full_screen_button'; + +import { keyCodes } from 'ui_framework/services'; + + +test('is rendered', () => { + const component = render( + {}}/> + ); + + expect(component) + .toMatchSnapshot(); +}); + +describe('onExitFullScreenMode', () => { + test('is called when the button is pressed', () => { + const onExitHandler = sinon.stub(); + + const component = mount( + + ); + + component.find('button').simulate('click'); + + sinon.assert.calledOnce(onExitHandler); + }); + + test('is called when the ESC key is pressed', () => { + const onExitHandler = sinon.stub(); + + mount(); + + const escapeKeyEvent = new KeyboardEvent('keydown', { keyCode: keyCodes.ESCAPE }); + document.dispatchEvent(escapeKeyEvent); + + sinon.assert.calledOnce(onExitHandler); + }); +}); + +describe('chrome.setVisible', () => { + test('is called with false when the component is rendered', () => { + chrome.setVisible = sinon.stub(); + + const component = mount( + {}} /> + ); + + component.find('button').simulate('click'); + + sinon.assert.calledOnce(chrome.setVisible); + sinon.assert.calledWith(chrome.setVisible, false); + }); + + test('is called with true the component is unmounted', () => { + const component = mount( + {}} /> + ); + + chrome.setVisible = sinon.stub(); + component.unmount(); + + sinon.assert.calledOnce(chrome.setVisible); + sinon.assert.calledWith(chrome.setVisible, true); + }); +}); diff --git a/src/core_plugins/kibana/public/dashboard/dashboard_app.html b/src/core_plugins/kibana/public/dashboard/dashboard_app.html index 02f0c9cbf9aa..ac2057236a08 100644 --- a/src/core_plugins/kibana/public/dashboard/dashboard_app.html +++ b/src/core_plugins/kibana/public/dashboard/dashboard_app.html @@ -2,21 +2,6 @@ class="app-container dashboard-container" ng-class="{'dashboard-container-with-margins': model.useMargins}" > -
-
- - - Exit full screen - - -
-
diff --git a/src/core_plugins/kibana/public/dashboard/dashboard_app.js b/src/core_plugins/kibana/public/dashboard/dashboard_app.js index ee91f54365a5..a8c4ceffc8f2 100644 --- a/src/core_plugins/kibana/public/dashboard/dashboard_app.js +++ b/src/core_plugins/kibana/public/dashboard/dashboard_app.js @@ -18,7 +18,6 @@ import { DashboardStateManager } from './dashboard_state_manager'; import { saveDashboard } from './lib'; import { showCloneModal } from './top_nav/show_clone_modal'; import { migrateLegacyQuery } from 'ui/utils/migrateLegacyQuery'; -import { keyCodes } from 'ui_framework/services'; import { DashboardContainerAPI } from './dashboard_container_api'; import * as filterActions from 'ui/doc_table/actions/filter'; import { FilterManagerProvider } from 'ui/filter_manager'; @@ -98,7 +97,6 @@ app.directive('dashboardApp', function ($injector) { description: dashboardStateManager.getDescription(), }; $scope.panels = dashboardStateManager.getPanels(); - $scope.fullScreenMode = dashboardStateManager.getFullScreenMode(); $scope.indexPatterns = dashboardStateManager.getPanelIndexPatterns(); }; @@ -268,50 +266,19 @@ app.directive('dashboardApp', function ($injector) { }).catch(notify.error); }; - $scope.showFilterBar = () => filterBar.getFilters().length > 0 || !$scope.fullScreenMode; - let onRouteChange; - const setFullScreenMode = (fullScreenMode) => { - $scope.fullScreenMode = fullScreenMode; - dashboardStateManager.setFullScreenMode(fullScreenMode); - chrome.setVisible(!fullScreenMode); - $scope.$broadcast('reLayout'); - - // Make sure that if we exit the dashboard app, the chrome becomes visible again - // (e.g. if the user clicks the back button). - if (fullScreenMode) { - onRouteChange = $scope.$on('$routeChangeStart', () => { - chrome.setVisible(true); - onRouteChange(); - }); - } else if (onRouteChange) { - onRouteChange(); - } - }; - - $scope.$watch('fullScreenMode', () => setFullScreenMode(dashboardStateManager.getFullScreenMode())); - - $scope.exitFullScreenMode = () => setFullScreenMode(false); - - document.addEventListener('keydown', (e) => { - if (e.keyCode === keyCodes.ESCAPE) { - setFullScreenMode(false); - } - }, false); + $scope.showFilterBar = () => filterBar.getFilters().length > 0 || !dashboardStateManager.getFullScreenMode(); $scope.showAddPanel = () => { - if ($scope.fullScreenMode) { - $scope.exitFullScreenMode(); - } + dashboardStateManager.setFullScreenMode(false); $scope.kbnTopNav.open('add'); }; $scope.enterEditMode = () => { - if ($scope.fullScreenMode) { - $scope.exitFullScreenMode(); - } + dashboardStateManager.setFullScreenMode(false); $scope.kbnTopNav.click('edit'); }; const navActions = {}; - navActions[TopNavIds.FULL_SCREEN] = () => setFullScreenMode(true); + navActions[TopNavIds.FULL_SCREEN] = () => + dashboardStateManager.setFullScreenMode(true); navActions[TopNavIds.EXIT_EDIT_MODE] = () => onChangeViewMode(DashboardViewMode.VIEW); navActions[TopNavIds.ENTER_EDIT_MODE] = () => onChangeViewMode(DashboardViewMode.EDIT); navActions[TopNavIds.CLONE] = () => { diff --git a/src/core_plugins/kibana/public/dashboard/dashboard_state_manager.js b/src/core_plugins/kibana/public/dashboard/dashboard_state_manager.js index e795937a910b..669007e3615c 100644 --- a/src/core_plugins/kibana/public/dashboard/dashboard_state_manager.js +++ b/src/core_plugins/kibana/public/dashboard/dashboard_state_manager.js @@ -45,7 +45,7 @@ import { * - maximizedPanelId * * State that is shared and needs to be synced: - * - fullScreenMode - changes only propagate from AppState -> Store + * - fullScreenMode - changes propagate from AppState -> Store and from Store -> AppState. * - viewMode - changes only propagate from AppState -> Store * - panels - changes propagate from AppState -> Store and from Store -> AppState. * @@ -152,21 +152,22 @@ export class DashboardStateManager { } _handleStoreChanges() { - if (this._areStoreAndAppStatePanelsEqual()) { - return; + let dirty = false; + if (!this._areStoreAndAppStatePanelsEqual()) { + const panels = getPanels(store.getState()); + this.appState.panels = []; + Object.values(panels).map(panel => { + this.appState.panels.push(panel); + }); + dirty = true; } - const state = store.getState(); - // The only state that the store deals with that appState cares about is the panels array. Every other state change - // (that appState cares about) is initiated from appState (e.g. view mode). - this.appState.panels = []; - _.map(getPanels(state), panel => { - this.appState.panels.push(panel); - }); + const fullScreen = getFullScreenMode(store.getState()); + if (fullScreen !== this.getFullScreenMode()) { + this.setFullScreenMode(fullScreen); + } - this.changeListeners.forEach(function (listener) { - return listener({ dirty: true, clean: false }); - }); + this.changeListeners.forEach(listener => listener({ dirty })); this.saveState(); } diff --git a/src/core_plugins/kibana/public/dashboard/styles/index.less b/src/core_plugins/kibana/public/dashboard/styles/index.less index 412095e3dc32..70e9813ed4d9 100644 --- a/src/core_plugins/kibana/public/dashboard/styles/index.less +++ b/src/core_plugins/kibana/public/dashboard/styles/index.less @@ -4,7 +4,7 @@ @import "~react-grid-layout/css/styles.css"; @import "~react-resizable/css/styles.css"; -.fullScreenModePlaceholder { +.exitFullScreenButton { text-align: center; width: 100%; height: 0; diff --git a/src/core_plugins/kibana/public/dashboard/viewport/dashboard_viewport.js b/src/core_plugins/kibana/public/dashboard/viewport/dashboard_viewport.js index 1105afdde8bf..af969bd0e23c 100644 --- a/src/core_plugins/kibana/public/dashboard/viewport/dashboard_viewport.js +++ b/src/core_plugins/kibana/public/dashboard/viewport/dashboard_viewport.js @@ -1,6 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { DashboardGrid } from '../grid'; +import { ExitFullScreenButton } from '../components/exit_full_screen_button'; export function DashboardViewport({ getContainerApi, @@ -10,6 +11,8 @@ export function DashboardViewport({ title, description, useMargins, + isFullScreenMode, + onExitFullScreenMode, }) { return (
+ { isFullScreenMode && } { const maximizedPanelId = getMaximizedPanelId(dashboard); @@ -10,10 +18,15 @@ const mapStateToProps = ({ dashboard }) => { description: getDescription(dashboard), title: getTitle(dashboard), useMargins: getUseMargins(dashboard), + isFullScreenMode: getFullScreenMode(dashboard), }; }; -export const DashboardViewportContainer = connect( - mapStateToProps -)(DashboardViewport); +const mapDispatchToProps = (dispatch) => ({ + onExitFullScreenMode: () => dispatch(updateIsFullScreenMode(false)), +}); +export const DashboardViewportContainer = connect( + mapStateToProps, + mapDispatchToProps, +)(DashboardViewport);