[7.x] Backport dashboard typescript #32703 and #33332 (#38263)

* rename PanelsMap to PanelStateMap to better reflect content (#33332)

* Backports #32703 and #33332 to 7.x
This commit is contained in:
Stacey Gammon 2019-06-06 12:09:36 -04:00 committed by GitHub
parent cd0324858c
commit 987f13b96e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 225 additions and 154 deletions

View file

@ -121,6 +121,7 @@
"@kbn/ui-framework": "1.0.0",
"@types/json-stable-stringify": "^1.0.32",
"@types/lodash.clonedeep": "^4.5.4",
"@types/react-grid-layout": "^0.16.7",
"@types/recompose": "^0.30.5",
"JSONStream": "1.3.5",
"abortcontroller-polyfill": "^1.1.9",

View file

@ -21,7 +21,7 @@
import { createAction } from 'redux-actions';
import { KibanaAction } from '../../selectors/types';
import { PanelId, PanelsMap, PanelState } from '../selectors';
import { PanelId, PanelState, PanelStateMap } from '../selectors';
export enum PanelActionTypeKeys {
DELETE_PANEL = 'DELETE_PANEL',
@ -39,7 +39,7 @@ export interface UpdatePanelAction
extends KibanaAction<PanelActionTypeKeys.UPDATE_PANEL, PanelState> {}
export interface UpdatePanelsAction
extends KibanaAction<PanelActionTypeKeys.UPDATE_PANELS, PanelsMap> {}
extends KibanaAction<PanelActionTypeKeys.UPDATE_PANELS, PanelStateMap> {}
export interface ResetPanelTitleAction
extends KibanaAction<PanelActionTypeKeys.RESET_PANEL_TITLE, PanelId> {}
@ -52,7 +52,8 @@ export interface SetPanelTitleActionPayload {
export interface SetPanelTitleAction
extends KibanaAction<PanelActionTypeKeys.SET_PANEL_TITLE, SetPanelTitleActionPayload> {}
export interface SetPanelsAction extends KibanaAction<PanelActionTypeKeys.SET_PANELS, PanelsMap> {}
export interface SetPanelsAction
extends KibanaAction<PanelActionTypeKeys.SET_PANELS, PanelStateMap> {}
export type PanelActions =
| DeletePanelAction
@ -68,5 +69,5 @@ export const resetPanelTitle = createAction<PanelId>(PanelActionTypeKeys.RESET_P
export const setPanelTitle = createAction<SetPanelTitleActionPayload>(
PanelActionTypeKeys.SET_PANEL_TITLE
);
export const updatePanels = createAction<PanelsMap>(PanelActionTypeKeys.UPDATE_PANELS);
export const setPanels = createAction<PanelsMap>(PanelActionTypeKeys.SET_PANELS);
export const updatePanels = createAction<PanelStateMap>(PanelActionTypeKeys.UPDATE_PANELS);
export const setPanels = createAction<PanelStateMap>(PanelActionTypeKeys.SET_PANELS);

View file

@ -27,9 +27,13 @@ import { DashboardViewMode } from './dashboard_view_mode';
* end of the title.
* @returns {string} A title to display to the user based on the above parameters.
*/
export function getDashboardTitle(title, viewMode, isDirty) {
export function getDashboardTitle(
title: string,
viewMode: DashboardViewMode,
isDirty: boolean
): string {
const isEditMode = viewMode === DashboardViewMode.EDIT;
let displayTitle;
let displayTitle: string;
if (isEditMode && isDirty) {
displayTitle = i18n.translate('kbn.dashboard.strings.dashboardUnsavedEditTitle', {

View file

@ -88,6 +88,6 @@ test('adjusts z-index of focused panel to be higher than siblings', () => {
const panelElements = component.find('Connect(InjectIntl(DashboardPanelUi))');
panelElements.first().prop('onPanelFocused')('1');
const [gridItem1, gridItem2] = component.update().findWhere(el => el.key() === '1' || el.key() === '2');
expect(gridItem1.props.style.zIndex).toEqual('2');
expect(gridItem1.props.style.zIndex).toEqual(2);
expect(gridItem2.props.style.zIndex).toEqual('auto');
});

View file

@ -17,27 +17,29 @@
* under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import { injectI18n } from '@kbn/i18n/react';
import _ from 'lodash';
import ReactGridLayout from 'react-grid-layout';
import classNames from 'classnames';
import _ from 'lodash';
import React from 'react';
import ReactGridLayout, { Layout } from 'react-grid-layout';
import 'react-grid-layout/css/styles.css';
import 'react-resizable/css/styles.css';
import { PanelUtils } from '../panel/panel_utils';
import { DashboardViewMode } from '../dashboard_view_mode';
import { DashboardPanel } from '../panel';
// @ts-ignore
import sizeMe from 'react-sizeme';
import { EmbeddableFactory } from 'ui/embeddable';
import { toastNotifications } from 'ui/notify';
import {
DashboardConstants,
DASHBOARD_GRID_COLUMN_COUNT,
DASHBOARD_GRID_HEIGHT,
DashboardConstants,
} from '../dashboard_constants';
import sizeMe from 'react-sizeme';
import { DashboardViewMode } from '../dashboard_view_mode';
import { DashboardPanel } from '../panel';
import { PanelUtils } from '../panel/panel_utils';
import { PanelState, PanelStateMap, Pre61PanelState, PanelId } from '../selectors/types';
import { GridData } from '../types';
const config = { monitorWidth: true };
let lastValidGridSize = 0;
/**
@ -45,7 +47,7 @@ let lastValidGridSize = 0;
* taller than the current grid.
* see https://github.com/elastic/kibana/issues/14710.
*/
function ensureWindowScrollsToBottom(layout, oldResizeItem, l, placeholder, event) {
function ensureWindowScrollsToBottom(event: { clientY: number; pageY: number }) {
// The buffer is to handle the case where the browser is maximized and it's impossible for the mouse to move below
// the screen, out of the window. see https://github.com/elastic/kibana/issues/14737
const WINDOW_BUFFER = 10;
@ -62,6 +64,14 @@ function ResponsiveGrid({
children,
maximizedPanelId,
useMargins,
}: {
size: { width: number };
isViewMode: boolean;
layout: Layout[];
onLayoutChange: () => void;
children: JSX.Element[];
maximizedPanelId: string;
useMargins: boolean;
}) {
// This is to prevent a bug where view mode changes when the panel is expanded. View mode changes will trigger
// the grid to re-render, but when a panel is expanded, the size will be 0. Minimizing the panel won't cause the
@ -94,8 +104,7 @@ function ResponsiveGrid({
draggableHandle={isViewMode ? '.doesnt-exist' : '.dshPanel__dragger'}
layout={layout}
onLayoutChange={onLayoutChange}
measureBeforeMount={false}
onResize={ensureWindowScrollsToBottom}
onResize={({}, {}, {}, {}, event) => ensureWindowScrollsToBottom(event)}
>
{children}
</ReactGridLayout>
@ -104,15 +113,43 @@ function ResponsiveGrid({
// Using sizeMe sets up the grid to be re-rendered automatically not only when the window size changes, but also
// when the container size changes, so it works for Full Screen mode switches.
const config = { monitorWidth: true };
const ResponsiveSizedGrid = sizeMe(config)(ResponsiveGrid);
interface Props extends ReactIntl.InjectedIntlProps {
panels: PanelStateMap;
getEmbeddableFactory: (panelType: string) => EmbeddableFactory;
dashboardViewMode: DashboardViewMode.EDIT | DashboardViewMode.VIEW;
onPanelsUpdated: (updatedPanels: PanelStateMap) => void;
maximizedPanelId?: string;
useMargins: boolean;
}
class DashboardGridUi extends React.Component {
constructor(props) {
interface State {
focusedPanelIndex?: string;
isLayoutInvalid: boolean;
layout?: GridData[];
}
interface PanelLayout extends Layout {
i: string;
}
class DashboardGridUi extends React.Component<Props, State> {
// A mapping of panelIndexes to grid items so we can set the zIndex appropriately on the last focused
// item.
private gridItems = {} as { [key: string]: HTMLDivElement | null };
// A mapping of panel type to embeddable handlers. Because this function reaches out of react and into angular,
// if done in the render method, it appears to be triggering a scope.apply, which appears to be trigging a setState
// call inside TSVB visualizations. Moving the function out of render appears to fix the issue. See
// https://github.com/elastic/kibana/issues/14802 for more info.
// This is probably a better implementation anyway so the handlers are cached.
// @type {Object.<string, EmbeddableFactory>}
private embeddableFactoryMap: { [s: string]: EmbeddableFactory } = {};
constructor(props: Props) {
super(props);
// A mapping of panelIndexes to grid items so we can set the zIndex appropriately on the last focused
// item.
this.gridItems = {};
let isLayoutInvalid = false;
let layout;
@ -127,41 +164,36 @@ class DashboardGridUi extends React.Component {
}),
text: error.message,
});
window.location = `#${DashboardConstants.LANDING_PAGE_PATH}`;
window.location.hash = DashboardConstants.LANDING_PAGE_PATH;
}
this.state = {
focusedPanelIndex: undefined,
layout,
isLayoutInvalid,
};
// A mapping of panel type to embeddable handlers. Because this function reaches out of react and into angular,
// if done in the render method, it appears to be triggering a scope.apply, which appears to be trigging a setState
// call inside TSVB visualizations. Moving the function out of render appears to fix the issue. See
// https://github.com/elastic/kibana/issues/14802 for more info.
// This is probably a better implementation anyway so the handlers are cached.
// @type {Object.<string, EmbeddableFactory>}
this.embeddableFactoryMap = {};
}
buildLayoutFromPanels() {
public buildLayoutFromPanels(): GridData[] {
return _.map(this.props.panels, panel => {
// panel version numbers added in 6.1. Any panel without version number is assumed to be 6.0.0
const panelVersion = panel.version ? PanelUtils.parseVersion(panel.version) : PanelUtils.parseVersion('6.0.0');
const panelVersion =
'version' in panel
? PanelUtils.parseVersion(panel.version)
: PanelUtils.parseVersion('6.0.0');
if (panelVersion.major < 6 || (panelVersion.major === 6 && panelVersion.minor < 1)) {
PanelUtils.convertPanelDataPre_6_1(panel);
panel = PanelUtils.convertPanelDataPre_6_1(panel as Pre61PanelState);
}
if (panelVersion.major < 6 || (panelVersion.major === 6 && panelVersion.minor < 3)) {
PanelUtils.convertPanelDataPre_6_3(panel, this.props.useMargins);
PanelUtils.convertPanelDataPre_6_3(panel as PanelState, this.props.useMargins);
}
return panel.gridData;
return (panel as PanelState).gridData;
});
}
createEmbeddableFactoriesMap(panels) {
public createEmbeddableFactoriesMap(panels: PanelStateMap) {
Object.values(panels).map(panel => {
if (!this.embeddableFactoryMap[panel.type]) {
this.embeddableFactoryMap[panel.type] = this.props.getEmbeddableFactory(panel.type);
@ -169,46 +201,46 @@ class DashboardGridUi extends React.Component {
});
}
componentWillMount() {
public componentWillMount() {
this.createEmbeddableFactoriesMap(this.props.panels);
}
componentWillReceiveProps(nextProps) {
public componentWillReceiveProps(nextProps: Props) {
this.createEmbeddableFactoriesMap(nextProps.panels);
}
onLayoutChange = (layout) => {
public onLayoutChange = (layout: PanelLayout[]) => {
const { onPanelsUpdated, panels } = this.props;
const updatedPanels = layout.reduce((updatedPanelsAcc, panelLayout) => {
updatedPanelsAcc[panelLayout.i] = {
...panels[panelLayout.i],
panelIndex: panelLayout.i,
gridData: _.pick(panelLayout, ['x', 'y', 'w', 'h', 'i'])
};
return updatedPanelsAcc;
}, []);
const updatedPanels = layout.reduce(
(updatedPanelsAcc, panelLayout) => {
updatedPanelsAcc[panelLayout.i] = {
...panels[panelLayout.i],
panelIndex: panelLayout.i,
gridData: _.pick<GridData, PanelLayout>(panelLayout, ['x', 'y', 'w', 'h', 'i']),
};
return updatedPanelsAcc;
},
{} as PanelStateMap
);
onPanelsUpdated(updatedPanels);
};
onPanelFocused = focusedPanelIndex => {
public onPanelFocused = (focusedPanelIndex: PanelId): void => {
this.setState({ focusedPanelIndex });
};
onPanelBlurred = blurredPanelIndex => {
public onPanelBlurred = (blurredPanelIndex: PanelId): void => {
if (this.state.focusedPanelIndex === blurredPanelIndex) {
this.setState({ focusedPanelIndex: undefined });
}
};
renderDOM() {
const {
panels,
maximizedPanelId
} = this.props;
public renderDOM() {
const { panels, maximizedPanelId } = this.props;
const { focusedPanelIndex } = this.state;
// Part of our unofficial API - need to render in a consistent order for plugins.
const panelsInOrder = Object.keys(panels).map(key => panels[key]);
const panelsInOrder = Object.keys(panels).map((key: string) => panels[key] as PanelState);
panelsInOrder.sort((panelA, panelB) => {
if (panelA.gridData.y === panelB.gridData.y) {
return panelA.gridData.x - panelB.gridData.x;
@ -226,10 +258,12 @@ class DashboardGridUi extends React.Component {
});
return (
<div
style={{ zIndex: focusedPanelIndex === panel.panelIndex ? '2' : 'auto' }}
style={{ zIndex: focusedPanelIndex === panel.panelIndex ? 2 : 'auto' }}
className={classes}
key={panel.panelIndex}
ref={reactGridItem => { this.gridItems[panel.panelIndex] = reactGridItem; }}
ref={reactGridItem => {
this.gridItems[panel.panelIndex] = reactGridItem;
}}
>
<DashboardPanel
panelId={panel.panelIndex}
@ -242,7 +276,7 @@ class DashboardGridUi extends React.Component {
});
}
render() {
public render() {
if (this.state.isLayoutInvalid) {
return null;
}
@ -263,13 +297,4 @@ class DashboardGridUi extends React.Component {
}
}
DashboardGridUi.propTypes = {
panels: PropTypes.object.isRequired,
getEmbeddableFactory: PropTypes.func.isRequired,
dashboardViewMode: PropTypes.oneOf([DashboardViewMode.EDIT, DashboardViewMode.VIEW]).isRequired,
onPanelsUpdated: PropTypes.func.isRequired,
maximizedPanelId: PropTypes.string,
useMargins: PropTypes.bool.isRequired,
};
export const DashboardGrid = injectI18n(DashboardGridUi);

View file

@ -18,26 +18,36 @@
*/
import { connect } from 'react-redux';
import { DashboardGrid } from './dashboard_grid';
import { Dispatch } from 'redux';
import { updatePanels } from '../actions';
import {
getPanels,
getViewMode,
getUseMargins,
} from '../selectors';
import { getPanels, getUseMargins, getViewMode } from '../selectors';
import { DashboardViewMode, PanelStateMap } from '../selectors/types';
import { DashboardGrid } from './dashboard_grid';
const mapStateToProps = ({ dashboard }) => ({
interface DashboardGridContainerStateProps {
panels: PanelStateMap;
dashboardViewMode: DashboardViewMode;
useMargins: boolean;
}
interface DashboardGridContainerDispatchProps {
onPanelsUpdated(updatedPanels: PanelStateMap): void;
}
const mapStateToProps = ({ dashboard }: any): any => ({
panels: getPanels(dashboard),
dashboardViewMode: getViewMode(dashboard),
useMargins: getUseMargins(dashboard),
});
const mapDispatchToProps = (dispatch) => ({
onPanelsUpdated: updatedPanels => dispatch(updatePanels(updatedPanels)),
const mapDispatchToProps = (dispatch: Dispatch) => ({
onPanelsUpdated: (updatedPanels: PanelStateMap) => dispatch(updatePanels(updatedPanels)),
});
export const DashboardGridContainer = connect(
export const DashboardGridContainer = connect<
DashboardGridContainerStateProps,
DashboardGridContainerDispatchProps
>(
mapStateToProps,
mapDispatchToProps
)(DashboardGrid);

View file

@ -36,8 +36,8 @@ import { PanelHeader } from './panel_header';
export interface DashboardPanelProps {
viewOnlyMode: boolean;
onPanelFocused?: (panelIndex: PanelId) => {};
onPanelBlurred?: (panelIndex: PanelId) => {};
onPanelFocused?: (panelIndex: PanelId) => void;
onPanelBlurred?: (panelIndex: PanelId) => void;
error?: string | object;
destroy: () => void;
containerState: ContainerState;

View file

@ -21,7 +21,8 @@ import { i18n } from '@kbn/i18n';
import _ from 'lodash';
import chrome from 'ui/chrome';
import { DEFAULT_PANEL_HEIGHT, DEFAULT_PANEL_WIDTH } from '../dashboard_constants';
import { GridData, PanelState } from '../selectors';
import { PanelState, Pre61PanelState } from '../selectors';
import { GridData } from '../types';
const PANEL_HEIGHT_SCALE_FACTOR = 5;
const PANEL_HEIGHT_SCALE_FACTOR_WITH_MARGINS = 4;
@ -35,15 +36,7 @@ export interface SemanticVersion {
export class PanelUtils {
// 6.1 switched from gridster to react grid. React grid uses different variables for tracking layout
// eslint-disable-next-line @typescript-eslint/camelcase
public static convertPanelDataPre_6_1(panel: {
panelIndex: any; // earlier versions allowed panelIndex to be a number or a string
gridData: GridData;
col: number;
row: number;
size_x: number;
size_y: number;
version: string;
}): Partial<PanelState> {
public static convertPanelDataPre_6_1(panel: Pre61PanelState): PanelState {
['col', 'row'].forEach(key => {
if (!_.has(panel, key)) {
throw new Error(
@ -70,7 +63,7 @@ export class PanelUtils {
delete panel.row;
delete panel.col;
return panel;
return panel as PanelState;
}
// 6.3 changed the panel dimensions to allow finer control over sizing
@ -80,7 +73,7 @@ export class PanelUtils {
// eslint-disable-next-line @typescript-eslint/camelcase
public static convertPanelDataPre_6_3(
panel: {
gridData: { w: number; x: number; h: number; y: number };
gridData: GridData;
version: string;
},
useMargins: boolean

View file

@ -20,20 +20,20 @@
import _ from 'lodash';
import { Reducer } from 'redux';
import { PanelActions, PanelActionTypeKeys, SetPanelTitleActionPayload } from '../actions';
import { PanelId, PanelsMap, PanelState } from '../selectors';
import { PanelId, PanelState, PanelStateMap } from '../selectors';
const deletePanel = (panels: PanelsMap, panelId: PanelId): PanelsMap => {
const deletePanel = (panels: PanelStateMap, panelId: PanelId): PanelStateMap => {
const panelsCopy = { ...panels };
delete panelsCopy[panelId];
return panelsCopy;
};
const updatePanel = (panels: PanelsMap, panelState: PanelState): PanelsMap => ({
const updatePanel = (panels: PanelStateMap, panelState: PanelState): PanelStateMap => ({
...panels,
[panelState.panelIndex]: panelState,
});
const updatePanels = (panels: PanelsMap, updatedPanels: PanelsMap): PanelsMap => {
const updatePanels = (panels: PanelStateMap, updatedPanels: PanelStateMap): PanelStateMap => {
const panelsCopy = { ...panels };
Object.values(updatedPanels).forEach(panel => {
panelsCopy[panel.panelIndex] = panel;
@ -41,7 +41,7 @@ const updatePanels = (panels: PanelsMap, updatedPanels: PanelsMap): PanelsMap =>
return panelsCopy;
};
const resetPanelTitle = (panels: PanelsMap, panelId: PanelId) => ({
const resetPanelTitle = (panels: PanelStateMap, panelId: PanelId) => ({
...panels,
[panelId]: {
...panels[panelId],
@ -49,7 +49,7 @@ const resetPanelTitle = (panels: PanelsMap, panelId: PanelId) => ({
},
});
const setPanelTitle = (panels: PanelsMap, payload: SetPanelTitleActionPayload) => ({
const setPanelTitle = (panels: PanelStateMap, payload: SetPanelTitleActionPayload) => ({
...panels,
[payload.panelId]: {
...panels[payload.panelId],
@ -57,9 +57,9 @@ const setPanelTitle = (panels: PanelsMap, payload: SetPanelTitleActionPayload) =
},
});
const setPanels = (panels: PanelsMap, newPanels: PanelsMap) => _.cloneDeep(newPanels);
const setPanels = ({}, newPanels: PanelStateMap) => _.cloneDeep(newPanels);
export const panelsReducer: Reducer<PanelsMap> = (panels = {}, action): PanelsMap => {
export const panelsReducer: Reducer<PanelStateMap> = (panels = {}, action): PanelStateMap => {
switch ((action as PanelActions).type) {
case PanelActionTypeKeys.DELETE_PANEL:
return deletePanel(panels, action.payload);

View file

@ -34,14 +34,14 @@ import {
EmbeddableReduxState,
EmbeddablesMap,
PanelId,
PanelsMap,
PanelState,
PanelStateMap,
} from './types';
export const getPanels = (dashboard: DashboardState): PanelsMap => dashboard.panels;
export const getPanels = (dashboard: DashboardState): Readonly<PanelStateMap> => dashboard.panels;
export const getPanel = (dashboard: DashboardState, panelId: PanelId): PanelState =>
getPanels(dashboard)[panelId];
getPanels(dashboard)[panelId] as PanelState;
export const getPanelType = (dashboard: DashboardState, panelId: PanelId): string =>
getPanel(dashboard, panelId).type;

View file

@ -19,7 +19,9 @@
import { EmbeddableMetadata, Filters, Query, RefreshConfig, TimeRange } from 'ui/embeddable';
import { DashboardViewMode } from '../dashboard_view_mode';
import { GridData } from '../types';
export type DashboardViewMode = DashboardViewMode;
export interface ViewState {
readonly viewMode: DashboardViewMode;
readonly isFullScreenMode: boolean;
@ -33,14 +35,6 @@ export interface ViewState {
readonly filters: Filters;
}
export interface GridData {
readonly w: number;
readonly h: number;
readonly x: number;
readonly y: number;
readonly i: string;
}
export type PanelId = string;
export type SavedObjectId = string;
@ -65,8 +59,23 @@ export interface EmbeddableReduxState {
readonly lastReloadRequestTime: number;
}
export interface PanelsMap {
readonly [panelId: string]: PanelState;
export interface Pre61PanelState {
size_x: number;
size_y: number;
row: number;
col: number;
panelIndex: any; // earlier versions allowed this to be number or string
id: string;
type: string;
// Embeddableconfig didn't actually exist on older panel states but `migrate_app_state.js` handles
// stuffing it on.
embeddableConfig: any;
gridData?: GridData; // only needed because of in place updates to convert this to newest version
version?: string; // only needed because of in place updates to convert this to newest version
}
export interface PanelStateMap {
[panelId: string]: PanelState | Pre61PanelState;
}
export interface EmbeddablesMap {
@ -80,7 +89,7 @@ export interface DashboardMetadata {
export interface DashboardState {
readonly view: ViewState;
readonly panels: PanelsMap;
readonly panels: PanelStateMap;
readonly embeddables: EmbeddablesMap;
readonly metadata: DashboardMetadata;
}

View file

@ -0,0 +1,26 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
export interface GridData {
w: number;
h: number;
x: number;
y: number;
i: string;
}

View file

@ -18,9 +18,9 @@
*/
import React from 'react';
import PropTypes from 'prop-types';
import { DashboardGrid } from '../grid';
import { EmbeddableFactory } from 'ui/embeddable';
import { ExitFullScreenButton } from 'ui/exit_full_screen';
import { DashboardGrid } from '../grid';
export function DashboardViewport({
maximizedPanelId,
@ -31,6 +31,15 @@ export function DashboardViewport({
useMargins,
isFullScreenMode,
onExitFullScreenMode,
}: {
maximizedPanelId: string;
getEmbeddableFactory: (panelType: string) => EmbeddableFactory;
panelCount: number;
title: string;
description: string;
useMargins: boolean;
isFullScreenMode: boolean;
onExitFullScreenMode: () => void;
}) {
return (
<div
@ -40,7 +49,7 @@ export function DashboardViewport({
data-description={description}
className={useMargins ? 'dshDashboardViewport-withMargins' : 'dshDashboardViewport'}
>
{ isFullScreenMode && <ExitFullScreenButton onExitFullScreenMode={onExitFullScreenMode} /> }
{isFullScreenMode && <ExitFullScreenButton onExitFullScreenMode={onExitFullScreenMode} />}
<DashboardGrid
getEmbeddableFactory={getEmbeddableFactory}
maximizedPanelId={maximizedPanelId}
@ -48,12 +57,3 @@ export function DashboardViewport({
</div>
);
}
DashboardViewport.propTypes = {
getEmbeddableFactory: PropTypes.func,
maximizedPanelId: PropTypes.string,
panelCount: PropTypes.number,
title: PropTypes.string,
description: PropTypes.string,
useMargins: PropTypes.bool.isRequired,
};

View file

@ -17,39 +17,37 @@
* under the License.
*/
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import chrome from 'ui/chrome';
import { injectI18n, FormattedMessage } from '@kbn/i18n/react';
import {
KuiButton,
} from '@kbn/ui-framework/components';
// @ts-ignore
import { KuiButton } from '@kbn/ui-framework/components';
import {
keyCodes,
EuiScreenReaderOnly,
} from '@elastic/eui';
import { EuiScreenReaderOnly, keyCodes } from '@elastic/eui';
class ExitFullScreenButtonUi extends PureComponent {
interface Props extends ReactIntl.InjectedIntlProps {
onExitFullScreenMode: () => void;
}
onKeyDown = (e) => {
class ExitFullScreenButtonUi extends PureComponent<Props> {
public onKeyDown = (e: KeyboardEvent) => {
if (e.keyCode === keyCodes.ESCAPE) {
this.props.onExitFullScreenMode();
}
};
componentWillMount() {
public componentWillMount() {
document.addEventListener('keydown', this.onKeyDown, false);
chrome.setVisible(false);
}
componentWillUnmount() {
public componentWillUnmount() {
document.removeEventListener('keydown', this.onKeyDown, false);
chrome.setVisible(true);
}
render() {
public render() {
const { intl } = this.props;
return (
@ -62,9 +60,7 @@ class ExitFullScreenButtonUi extends PureComponent {
/>
</p>
</EuiScreenReaderOnly>
<div
className="dshExitFullScreenButton"
>
<div className="dshExitFullScreenButton">
<KuiButton
type="hollow"
aria-label={intl.formatMessage({
@ -74,13 +70,16 @@ class ExitFullScreenButtonUi extends PureComponent {
className="dshExitFullScreenButton__mode"
onClick={this.props.onExitFullScreenMode}
>
<span className="dshExitFullScreenButton__logo" data-test-subj="exitFullScreenModeLogo"/>
<span
className="dshExitFullScreenButton__logo"
data-test-subj="exitFullScreenModeLogo"
/>
<span className="dshExitFullScreenButton__text" data-test-subj="exitFullScreenModeText">
<FormattedMessage
id="common.ui.exitFullScreenButton.exitFullScreenModeButtonLabel"
defaultMessage="Exit full screen"
/>
<span className="kuiIcon fa fa-angle-left"/>
<span className="kuiIcon fa fa-angle-left" />
</span>
</KuiButton>
</div>
@ -89,8 +88,4 @@ class ExitFullScreenButtonUi extends PureComponent {
}
}
ExitFullScreenButtonUi.propTypes = {
onExitFullScreenMode: PropTypes.func.isRequired,
};
export const ExitFullScreenButton = injectI18n(ExitFullScreenButtonUi);

View file

@ -3886,6 +3886,13 @@
dependencies:
"@types/react" "*"
"@types/react-grid-layout@^0.16.7":
version "0.16.7"
resolved "https://registry.yarnpkg.com/@types/react-grid-layout/-/react-grid-layout-0.16.7.tgz#53d5f5034deb0c60e25a0fa578141e9a0982011f"
integrity sha512-A3tW9xySd03KGONkp8gP4+QRLuT1Mcx++m0hO0nZIM4H/Qwz8GsiDv+9okbmHk5HcsHwY5Jdsn6Cv50hwoNG+A==
dependencies:
"@types/react" "*"
"@types/react-intl@^2.3.15":
version "2.3.17"
resolved "https://registry.yarnpkg.com/@types/react-intl/-/react-intl-2.3.17.tgz#e1fc6e46e8af58bdef9531259d509380a8a99e8e"