* Reactify full screen mode placeholder and remove from scope With the toggle in react, no need to store it on scope. * Simplify implementation by bringing in chrome.setVisible internal to the react placeholder * rename FullScreenModePlaceholder => ExitFullScreenButton * div => KuiButton * Add jest tests for exit full screen button * Use native Object.values.map instead of loadash _.map * get rid of unnecessary return value
This commit is contained in:
parent
2a4600f84a
commit
3a7b2f52be
|
@ -0,0 +1,33 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`is rendered 1`] = `
|
||||
<div
|
||||
class="exitFullScreenButton"
|
||||
>
|
||||
<button
|
||||
aria-label="Exit full screen mode"
|
||||
class="kuiButton exitFullScreenMode"
|
||||
type="hollow"
|
||||
>
|
||||
<span
|
||||
class="kuiButton__inner"
|
||||
>
|
||||
<span>
|
||||
<span
|
||||
class="exitFullScreenModeLogo"
|
||||
data-test-subj="exitFullScreenModeLogo"
|
||||
/>
|
||||
<span
|
||||
class="exitFullScreenModeText"
|
||||
data-test-subj="exitFullScreenModeText"
|
||||
>
|
||||
Exit full screen
|
||||
<span
|
||||
class="kuiIcon fa fa-angle-left"
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
|
@ -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 (
|
||||
<div
|
||||
className="exitFullScreenButton"
|
||||
>
|
||||
<KuiButton
|
||||
type="hollow"
|
||||
aria-label="Exit full screen mode"
|
||||
className="exitFullScreenMode"
|
||||
onClick={this.props.onExitFullScreenMode}
|
||||
>
|
||||
<span className="exitFullScreenModeLogo" data-test-subj="exitFullScreenModeLogo"/>
|
||||
<span className="exitFullScreenModeText" data-test-subj="exitFullScreenModeText">
|
||||
Exit full screen
|
||||
<span className="kuiIcon fa fa-angle-left"/>
|
||||
</span>
|
||||
</KuiButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ExitFullScreenButton.propTypes = {
|
||||
onExitFullScreenMode: PropTypes.func.isRequired,
|
||||
};
|
||||
|
|
@ -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(
|
||||
<ExitFullScreenButton onExitFullScreenMode={() => {}}/>
|
||||
);
|
||||
|
||||
expect(component)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('onExitFullScreenMode', () => {
|
||||
test('is called when the button is pressed', () => {
|
||||
const onExitHandler = sinon.stub();
|
||||
|
||||
const component = mount(
|
||||
<ExitFullScreenButton onExitFullScreenMode={onExitHandler} />
|
||||
);
|
||||
|
||||
component.find('button').simulate('click');
|
||||
|
||||
sinon.assert.calledOnce(onExitHandler);
|
||||
});
|
||||
|
||||
test('is called when the ESC key is pressed', () => {
|
||||
const onExitHandler = sinon.stub();
|
||||
|
||||
mount(<ExitFullScreenButton onExitFullScreenMode={onExitHandler} />);
|
||||
|
||||
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(
|
||||
<ExitFullScreenButton onExitFullScreenMode={() => {}} />
|
||||
);
|
||||
|
||||
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(
|
||||
<ExitFullScreenButton onExitFullScreenMode={() => {}} />
|
||||
);
|
||||
|
||||
chrome.setVisible = sinon.stub();
|
||||
component.unmount();
|
||||
|
||||
sinon.assert.calledOnce(chrome.setVisible);
|
||||
sinon.assert.calledWith(chrome.setVisible, true);
|
||||
});
|
||||
});
|
|
@ -2,21 +2,6 @@
|
|||
class="app-container dashboard-container"
|
||||
ng-class="{'dashboard-container-with-margins': model.useMargins}"
|
||||
>
|
||||
<div class="fullScreenModePlaceholder">
|
||||
<div
|
||||
aria-label="Exit full screen mode"
|
||||
kbn-accessible-click
|
||||
ng-if="fullScreenMode"
|
||||
class="exitFullScreenMode"
|
||||
ng-click="exitFullScreenMode()"
|
||||
>
|
||||
<span class="exitFullScreenModeLogo" data-test-subj="exitFullScreenModeLogo"></span>
|
||||
<span class="exitFullScreenModeText" data-test-subj="exitFullScreenModeText">
|
||||
Exit full screen
|
||||
<span class="kuiIcon fa fa-angle-left"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Local nav. -->
|
||||
<kbn-top-nav name="dashboard" config="topNavMenu">
|
||||
<!-- Transcluded elements. -->
|
||||
|
|
|
@ -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] = () => {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
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).
|
||||
let dirty = false;
|
||||
if (!this._areStoreAndAppStatePanelsEqual()) {
|
||||
const panels = getPanels(store.getState());
|
||||
this.appState.panels = [];
|
||||
_.map(getPanels(state), panel => {
|
||||
Object.values(panels).map(panel => {
|
||||
this.appState.panels.push(panel);
|
||||
});
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
this.changeListeners.forEach(function (listener) {
|
||||
return listener({ dirty: true, clean: false });
|
||||
});
|
||||
const fullScreen = getFullScreenMode(store.getState());
|
||||
if (fullScreen !== this.getFullScreenMode()) {
|
||||
this.setFullScreenMode(fullScreen);
|
||||
}
|
||||
|
||||
this.changeListeners.forEach(listener => listener({ dirty }));
|
||||
this.saveState();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 (
|
||||
<div
|
||||
|
@ -19,6 +22,7 @@ export function DashboardViewport({
|
|||
data-description={description}
|
||||
className={useMargins ? 'dashboard-viewport-with-margins' : 'dashboard-viewport'}
|
||||
>
|
||||
{ isFullScreenMode && <ExitFullScreenButton onExitFullScreenMode={onExitFullScreenMode} /> }
|
||||
<DashboardGrid
|
||||
getEmbeddableFactory={getEmbeddableFactory}
|
||||
getContainerApi={getContainerApi}
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { DashboardViewport } from './dashboard_viewport';
|
||||
import { getMaximizedPanelId, getPanels, getTitle, getDescription, getUseMargins } from '../selectors';
|
||||
import { updateIsFullScreenMode } from '../actions';
|
||||
import {
|
||||
getMaximizedPanelId,
|
||||
getPanels,
|
||||
getTitle,
|
||||
getDescription,
|
||||
getUseMargins,
|
||||
getFullScreenMode,
|
||||
} from '../selectors';
|
||||
|
||||
const mapStateToProps = ({ dashboard }) => {
|
||||
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);
|
||||
|
|
Loading…
Reference in a new issue