mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
* rename PanelsMap to PanelStateMap to better reflect content (#33332) * Backports #32703 and #33332 to 7.x
This commit is contained in:
parent
cd0324858c
commit
987f13b96e
17 changed files with 225 additions and 154 deletions
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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', {
|
|
@ -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');
|
||||
});
|
||||
|
|
|
@ -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);
|
|
@ -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);
|
||||
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
26
src/legacy/core_plugins/kibana/public/dashboard/types.ts
Normal file
26
src/legacy/core_plugins/kibana/public/dashboard/types.ts
Normal 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;
|
||||
}
|
|
@ -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,
|
||||
};
|
|
@ -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);
|
|
@ -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"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue