mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
* gridData id changed to i * to.equal panel_state.ts * clarifying return values in panel_utils *fixed imports in dpc consitency across id -> i for gridData, improvment made in dashboard_panel.tsx changed DashboardPanelUiProps onPanelFocused and onPanelBlurred (panel: PanelState) to (panel: string) resolved Problems in TS * Resolved ts testing type errors with DashboardPanelUiProps in dashboard_panel.tsx Updated snapshot for dashboard_panel.test.tsx * Code review from Emma with changes partial PanelState and defining size_x and size_y to numbers adding comment to clarify panel.panelIndex.toString() * test commit to take in Emmas feedback and show the panel_utils and types.ts modified to show panelIndex string | number reverting panel_utils and types.ts changes with panelIndex, keeping other changes from Emmas suggestions * Fixed panel_utils and panel_utils test to create updatedPanel object instead of mutation Revert mutation in panel utils removed ts-ignore statements in __tests__/panel_state.ts
This commit is contained in:
parent
b0c599835d
commit
4282f1917d
11 changed files with 326 additions and 199 deletions
|
@ -18,19 +18,29 @@
|
|||
*/
|
||||
|
||||
import expect from 'expect.js';
|
||||
|
||||
import { PanelState } from '../../selectors';
|
||||
import { createPanelState } from '../panel_state';
|
||||
|
||||
function createPanelWithDimensions(x, y, w, h) {
|
||||
function createPanelWithDimensions(x: number, y: number, w: number, h: number): PanelState {
|
||||
return {
|
||||
id: 'foo',
|
||||
version: '6.3.0',
|
||||
type: 'bar',
|
||||
panelIndex: 'test',
|
||||
title: 'test title',
|
||||
gridData: {
|
||||
x, y, w, h
|
||||
}
|
||||
x,
|
||||
y,
|
||||
w,
|
||||
h,
|
||||
i: 'an id',
|
||||
},
|
||||
embeddableConfig: {},
|
||||
};
|
||||
}
|
||||
|
||||
describe('Panel state', function () {
|
||||
it('finds a spot on the right', function () {
|
||||
describe('Panel state', () => {
|
||||
it('finds a spot on the right', () => {
|
||||
// Default setup after a single panel, of default size, is on the grid
|
||||
const panels = [createPanelWithDimensions(0, 0, 24, 30)];
|
||||
|
||||
|
@ -39,7 +49,7 @@ describe('Panel state', function () {
|
|||
expect(panel.gridData.y).to.equal(0);
|
||||
});
|
||||
|
||||
it('finds a spot on the right when the panel is taller than any other panel on the grid', function () {
|
||||
it('finds a spot on the right when the panel is taller than any other panel on the grid', () => {
|
||||
// Should be a little empty spot on the right.
|
||||
const panels = [
|
||||
createPanelWithDimensions(0, 0, 24, 45),
|
||||
|
@ -51,7 +61,7 @@ describe('Panel state', function () {
|
|||
expect(panel.gridData.y).to.equal(30);
|
||||
});
|
||||
|
||||
it('finds an empty spot in the middle of the grid', function () {
|
||||
it('finds an empty spot in the middle of the grid', () => {
|
||||
const panels = [
|
||||
createPanelWithDimensions(0, 0, 48, 5),
|
||||
createPanelWithDimensions(0, 5, 4, 30),
|
|
@ -17,38 +17,28 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
// TODO: remove this when EUI supports types for this.
|
||||
// @ts-ignore: implicit any for JS file
|
||||
import { takeMountedSnapshot } from '@elastic/eui/lib/test';
|
||||
import _ from 'lodash';
|
||||
import { mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { DashboardPanel } from './dashboard_panel';
|
||||
import { DashboardViewMode } from '../dashboard_view_mode';
|
||||
import { PanelError } from '../panel/panel_error';
|
||||
import { store } from '../../store';
|
||||
import { getEmbeddableFactoryMock } from '../__tests__/get_embeddable_factories_mock';
|
||||
|
||||
import {
|
||||
updateViewMode,
|
||||
setPanels,
|
||||
updateTimeRange,
|
||||
embeddableIsInitialized,
|
||||
} from '../actions';
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { store } from '../../store';
|
||||
// @ts-ignore: implicit any for JS file
|
||||
import { getEmbeddableFactoryMock } from '../__tests__/get_embeddable_factories_mock';
|
||||
import { embeddableIsInitialized, setPanels, updateTimeRange, updateViewMode } from '../actions';
|
||||
import { DashboardViewMode } from '../dashboard_view_mode';
|
||||
import { DashboardPanel, DashboardPanelUiProps } from './dashboard_panel';
|
||||
|
||||
import {
|
||||
takeMountedSnapshot,
|
||||
} from '@elastic/eui/lib/test';
|
||||
import { PanelError } from './panel_error';
|
||||
|
||||
function getProps(props = {}) {
|
||||
function getProps(props = {}): DashboardPanelUiProps {
|
||||
const defaultTestProps = {
|
||||
panel: { panelIndex: 'foo1' },
|
||||
viewOnlyMode: false,
|
||||
destroy: () => {},
|
||||
initialized: true,
|
||||
lastReloadRequestTime: 0,
|
||||
embeddableIsInitialized: () => {},
|
||||
embeddableIsInitializing: () => {},
|
||||
embeddableStateChanged: () => {},
|
||||
embeddableError: () => {},
|
||||
embeddableFactory: getEmbeddableFactoryMock(),
|
||||
};
|
||||
return _.defaultsDeep(props, defaultTestProps);
|
||||
|
@ -57,22 +47,47 @@ function getProps(props = {}) {
|
|||
beforeAll(() => {
|
||||
store.dispatch(updateTimeRange({ to: 'now', from: 'now-15m' }));
|
||||
store.dispatch(updateViewMode(DashboardViewMode.EDIT));
|
||||
store.dispatch(setPanels({ 'foo1': { panelIndex: 'foo1' } }));
|
||||
store.dispatch(
|
||||
setPanels({
|
||||
foo1: {
|
||||
panelIndex: 'foo1',
|
||||
id: 'hi',
|
||||
version: '123',
|
||||
type: 'viz',
|
||||
embeddableConfig: {},
|
||||
gridData: {
|
||||
x: 1,
|
||||
y: 1,
|
||||
w: 1,
|
||||
h: 1,
|
||||
i: 'hi',
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
const metadata = { title: 'my embeddable title', editUrl: 'editme' };
|
||||
store.dispatch(embeddableIsInitialized({ metadata, panelId: 'foo1' }));
|
||||
});
|
||||
|
||||
test('DashboardPanel matches snapshot', () => {
|
||||
const component = mountWithIntl(<Provider store={store}><DashboardPanel.WrappedComponent {...getProps()} /></Provider>);
|
||||
const component = mountWithIntl(
|
||||
<Provider store={store}>
|
||||
<DashboardPanel.WrappedComponent {...getProps()} />
|
||||
</Provider>
|
||||
);
|
||||
expect(takeMountedSnapshot(component)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('renders an error when error prop is passed', () => {
|
||||
const props = getProps({
|
||||
error: 'Simulated error'
|
||||
error: 'Simulated error',
|
||||
});
|
||||
|
||||
const component = mountWithIntl(<Provider store={store}><DashboardPanel.WrappedComponent {...props} /></Provider>);
|
||||
const component = mountWithIntl(
|
||||
<Provider store={store}>
|
||||
<DashboardPanel.WrappedComponent {...props} />
|
||||
</Provider>
|
||||
);
|
||||
const panelError = component.find(PanelError);
|
||||
expect(panelError.length).toBe(1);
|
||||
});
|
|
@ -17,34 +17,68 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { injectI18n } from '@kbn/i18n/react';
|
||||
import { EuiLoadingChart, EuiPanel } from '@elastic/eui';
|
||||
import { InjectedIntl, injectI18n } from '@kbn/i18n/react';
|
||||
import classNames from 'classnames';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { PanelHeader } from './panel_header';
|
||||
import { PanelError } from './panel_error';
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
EuiPanel,
|
||||
EuiLoadingChart,
|
||||
} from '@elastic/eui';
|
||||
ContainerState,
|
||||
Embeddable,
|
||||
EmbeddableFactory,
|
||||
EmbeddableMetadata,
|
||||
EmbeddableState,
|
||||
} from 'ui/embeddable';
|
||||
import { EmbeddableErrorAction } from '../actions';
|
||||
import { PanelId, PanelState } from '../selectors';
|
||||
import { PanelError } from './panel_error';
|
||||
import { PanelHeader } from './panel_header';
|
||||
|
||||
class DashboardPanelUi extends React.Component {
|
||||
constructor(props) {
|
||||
export interface DashboardPanelProps {
|
||||
viewOnlyMode: boolean;
|
||||
onPanelFocused?: (panelIndex: PanelId) => {};
|
||||
onPanelBlurred?: (panelIndex: PanelId) => {};
|
||||
error?: string | object;
|
||||
destroy: () => void;
|
||||
containerState: ContainerState;
|
||||
embeddableFactory: EmbeddableFactory;
|
||||
lastReloadRequestTime?: number;
|
||||
embeddableStateChanged: (embeddableStateChanges: EmbeddableState) => void;
|
||||
embeddableIsInitialized: (embeddableIsInitializing: EmbeddableMetadata) => void;
|
||||
embeddableError: (errorMessage: EmbeddableErrorAction) => void;
|
||||
embeddableIsInitializing: () => void;
|
||||
initialized: boolean;
|
||||
panel: PanelState;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export interface DashboardPanelUiProps extends DashboardPanelProps {
|
||||
intl: InjectedIntl;
|
||||
}
|
||||
|
||||
interface State {
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
class DashboardPanelUi extends React.Component<DashboardPanelUiProps, State> {
|
||||
[panel: string]: any;
|
||||
public mounted: boolean;
|
||||
public embeddable!: Embeddable;
|
||||
constructor(props: DashboardPanelUiProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
error: props.embeddableFactory ? null : props.intl.formatMessage({
|
||||
id: 'kbn.dashboard.panel.noEmbeddableFactoryErrorMessage',
|
||||
defaultMessage: 'No factory found for embeddable',
|
||||
}),
|
||||
error: props.embeddableFactory
|
||||
? null
|
||||
: props.intl.formatMessage({
|
||||
id: 'kbn.dashboard.panel.noEmbeddableFactoryErrorMessage',
|
||||
defaultMessage: 'No factory found for embeddable',
|
||||
}),
|
||||
};
|
||||
|
||||
this.mounted = false;
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
public async componentDidMount() {
|
||||
this.mounted = true;
|
||||
const {
|
||||
initialized,
|
||||
|
@ -58,8 +92,9 @@ class DashboardPanelUi extends React.Component {
|
|||
|
||||
if (!initialized) {
|
||||
embeddableIsInitializing();
|
||||
embeddableFactory.create(panel, embeddableStateChanged)
|
||||
.then((embeddable) => {
|
||||
embeddableFactory
|
||||
.create(panel, embeddableStateChanged)
|
||||
.then((embeddable: Embeddable) => {
|
||||
if (this.mounted) {
|
||||
this.embeddable = embeddable;
|
||||
embeddableIsInitialized(embeddable.metadata);
|
||||
|
@ -68,7 +103,7 @@ class DashboardPanelUi extends React.Component {
|
|||
embeddable.destroy();
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
.catch((error: { message: EmbeddableErrorAction }) => {
|
||||
if (this.mounted) {
|
||||
embeddableError(error.message);
|
||||
}
|
||||
|
@ -76,7 +111,7 @@ class DashboardPanelUi extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
public componentWillUnmount() {
|
||||
this.props.destroy();
|
||||
this.mounted = false;
|
||||
if (this.embeddable) {
|
||||
|
@ -84,21 +119,21 @@ class DashboardPanelUi extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
onFocus = () => {
|
||||
public onFocus = () => {
|
||||
const { onPanelFocused, panel } = this.props;
|
||||
if (onPanelFocused) {
|
||||
onPanelFocused(panel.panelIndex);
|
||||
}
|
||||
};
|
||||
|
||||
onBlur = () => {
|
||||
public onBlur = () => {
|
||||
const { onPanelBlurred, panel } = this.props;
|
||||
if (onPanelBlurred) {
|
||||
onPanelBlurred(panel.panelIndex);
|
||||
}
|
||||
};
|
||||
|
||||
renderEmbeddableViewport() {
|
||||
public renderEmbeddableViewport() {
|
||||
const classes = classNames('panel-content', {
|
||||
'panel-content-isLoading': !this.props.initialized,
|
||||
});
|
||||
|
@ -107,16 +142,14 @@ class DashboardPanelUi extends React.Component {
|
|||
<div
|
||||
id="embeddedPanel"
|
||||
className={classes}
|
||||
ref={panelElement => this.panelElement = panelElement}
|
||||
ref={panelElement => (this.panelElement = panelElement)}
|
||||
>
|
||||
{!this.props.initialized && (
|
||||
<EuiLoadingChart size="l" mono/>
|
||||
)}
|
||||
{!this.props.initialized && <EuiLoadingChart size="l" mono />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
public shouldComponentUpdate(nextProps: DashboardPanelUiProps) {
|
||||
if (this.embeddable && !_.isEqual(nextProps.containerState, this.props.containerState)) {
|
||||
this.embeddable.onContainerStateChanged(nextProps.containerState);
|
||||
}
|
||||
|
@ -125,27 +158,26 @@ class DashboardPanelUi extends React.Component {
|
|||
this.embeddable.reload();
|
||||
}
|
||||
|
||||
return nextProps.error !== this.props.error ||
|
||||
nextProps.initialized !== this.props.initialized;
|
||||
return nextProps.error !== this.props.error || nextProps.initialized !== this.props.initialized;
|
||||
}
|
||||
|
||||
renderEmbeddedError() {
|
||||
public renderEmbeddedError() {
|
||||
return <PanelError error={this.props.error} />;
|
||||
}
|
||||
|
||||
renderContent() {
|
||||
public renderContent() {
|
||||
const { error } = this.props;
|
||||
if (error) {
|
||||
return this.renderEmbeddedError(error);
|
||||
return this.renderEmbeddedError();
|
||||
} else {
|
||||
return this.renderEmbeddableViewport();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
const { viewOnlyMode, panel } = this.props;
|
||||
const classes = classNames('dshPanel', this.props.className, {
|
||||
'dshPanel--editing': !viewOnlyMode
|
||||
'dshPanel--editing': !viewOnlyMode,
|
||||
});
|
||||
return (
|
||||
<EuiPanel
|
||||
|
@ -155,10 +187,7 @@ class DashboardPanelUi extends React.Component {
|
|||
onBlur={this.onBlur}
|
||||
paddingSize="none"
|
||||
>
|
||||
<PanelHeader
|
||||
panelId={panel.panelIndex}
|
||||
embeddable={this.embeddable}
|
||||
/>
|
||||
<PanelHeader panelId={panel.panelIndex} embeddable={this.embeddable} />
|
||||
|
||||
{this.renderContent()}
|
||||
</EuiPanel>
|
||||
|
@ -166,35 +195,4 @@ class DashboardPanelUi extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
DashboardPanelUi.propTypes = {
|
||||
viewOnlyMode: PropTypes.bool.isRequired,
|
||||
onPanelFocused: PropTypes.func,
|
||||
onPanelBlurred: PropTypes.func,
|
||||
error: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.object
|
||||
]),
|
||||
destroy: PropTypes.func.isRequired,
|
||||
containerState: PropTypes.shape({
|
||||
timeRange: PropTypes.object,
|
||||
refreshConfig: PropTypes.object,
|
||||
filters: PropTypes.array,
|
||||
query: PropTypes.object,
|
||||
embeddableCustomization: PropTypes.object,
|
||||
hidePanelTitles: PropTypes.bool.isRequired,
|
||||
}),
|
||||
embeddableFactory: PropTypes.shape({
|
||||
create: PropTypes.func,
|
||||
}).isRequired,
|
||||
lastReloadRequestTime: PropTypes.number.isRequired,
|
||||
embeddableStateChanged: PropTypes.func.isRequired,
|
||||
embeddableIsInitialized: PropTypes.func.isRequired,
|
||||
embeddableError: PropTypes.func.isRequired,
|
||||
embeddableIsInitializing: PropTypes.func.isRequired,
|
||||
initialized: PropTypes.bool.isRequired,
|
||||
panel: PropTypes.shape({
|
||||
panelIndex: PropTypes.string,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
export const DashboardPanel = injectI18n(DashboardPanelUi);
|
|
@ -17,21 +17,22 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { DashboardPanelContainer } from './dashboard_panel_container';
|
||||
import { store } from '../../store';
|
||||
// @ts-ignore: implicit for any JS file
|
||||
import { getEmbeddableFactoryMock } from '../__tests__/get_embeddable_factories_mock';
|
||||
import { setPanels, updateTimeRange, updateViewMode } from '../actions';
|
||||
import { DashboardViewMode } from '../dashboard_view_mode';
|
||||
import { PanelError } from '../panel/panel_error';
|
||||
import { store } from '../../store';
|
||||
import {
|
||||
updateViewMode,
|
||||
setPanels, updateTimeRange,
|
||||
} from '../actions';
|
||||
import { Provider } from 'react-redux';
|
||||
import { getEmbeddableFactoryMock } from '../__tests__/get_embeddable_factories_mock';
|
||||
DashboardPanelContainer,
|
||||
DashboardPanelContainerOwnProps,
|
||||
} from './dashboard_panel_container';
|
||||
|
||||
function getProps(props = {}) {
|
||||
function getProps(props = {}): DashboardPanelContainerOwnProps {
|
||||
const defaultTestProps = {
|
||||
panelId: 'foo1',
|
||||
embeddableFactory: getEmbeddableFactoryMock(),
|
||||
|
@ -42,17 +43,38 @@ function getProps(props = {}) {
|
|||
beforeAll(() => {
|
||||
store.dispatch(updateViewMode(DashboardViewMode.EDIT));
|
||||
store.dispatch(updateTimeRange({ to: 'now', from: 'now-15m' }));
|
||||
store.dispatch(setPanels({ 'foo1': { panelIndex: 'foo1' } }));
|
||||
store.dispatch(
|
||||
setPanels({
|
||||
foo1: {
|
||||
panelIndex: 'foo1',
|
||||
id: 'hi',
|
||||
version: '123',
|
||||
type: 'viz',
|
||||
embeddableConfig: {},
|
||||
gridData: {
|
||||
x: 1,
|
||||
y: 1,
|
||||
w: 1,
|
||||
h: 1,
|
||||
i: 'hi',
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('renders an error when embeddableFactory.create throws an error', (done) => {
|
||||
test('renders an error when embeddableFactory.create throws an error', done => {
|
||||
const props = getProps();
|
||||
props.embeddableFactory.create = () => {
|
||||
return new Promise(() => {
|
||||
throw new Error('simulated error');
|
||||
});
|
||||
};
|
||||
const component = mountWithIntl(<Provider store={store}><DashboardPanelContainer {...props} /></Provider>);
|
||||
const component = mountWithIntl(
|
||||
<Provider store={store}>
|
||||
<DashboardPanelContainer {...props} />
|
||||
</Provider>
|
||||
);
|
||||
setTimeout(() => {
|
||||
component.update();
|
||||
const panelError = component.find(PanelError);
|
||||
|
@ -60,4 +82,3 @@ test('renders an error when embeddableFactory.create throws an error', (done) =>
|
|||
done();
|
||||
}, 0);
|
||||
});
|
||||
|
|
@ -17,26 +17,66 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { DashboardPanel } from './dashboard_panel';
|
||||
import { DashboardViewMode } from '../dashboard_view_mode';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import { Action } from 'redux-actions';
|
||||
import { ThunkDispatch } from 'redux-thunk';
|
||||
import {
|
||||
deletePanel, embeddableError, embeddableIsInitialized, embeddableIsInitializing, embeddableStateChanged,
|
||||
ContainerState,
|
||||
EmbeddableFactory,
|
||||
EmbeddableMetadata,
|
||||
EmbeddableState,
|
||||
} from 'ui/embeddable';
|
||||
import { CoreKibanaState } from '../../selectors';
|
||||
import {
|
||||
deletePanel,
|
||||
embeddableError,
|
||||
EmbeddableErrorAction,
|
||||
embeddableIsInitialized,
|
||||
embeddableIsInitializing,
|
||||
embeddableStateChanged,
|
||||
} from '../actions';
|
||||
|
||||
import { DashboardViewMode } from '../dashboard_view_mode';
|
||||
import {
|
||||
getContainerState,
|
||||
getEmbeddable,
|
||||
getFullScreenMode,
|
||||
getViewMode,
|
||||
getEmbeddableError,
|
||||
getPanelType, getContainerState, getPanel, getEmbeddableInitialized,
|
||||
getEmbeddableInitialized,
|
||||
getFullScreenMode,
|
||||
getPanel,
|
||||
getPanelType,
|
||||
getViewMode,
|
||||
PanelId,
|
||||
PanelState,
|
||||
} from '../selectors';
|
||||
import { DashboardPanel } from './dashboard_panel';
|
||||
|
||||
const mapStateToProps = ({ dashboard }, { embeddableFactory, panelId }) => {
|
||||
export interface DashboardPanelContainerOwnProps {
|
||||
panelId: PanelId;
|
||||
embeddableFactory: EmbeddableFactory;
|
||||
}
|
||||
|
||||
interface DashboardPanelContainerStateProps {
|
||||
error?: string | object;
|
||||
viewOnlyMode: boolean;
|
||||
containerState: ContainerState;
|
||||
initialized: boolean;
|
||||
panel: PanelState;
|
||||
lastReloadRequestTime?: number;
|
||||
}
|
||||
|
||||
export interface DashboardPanelContainerDispatchProps {
|
||||
destroy: () => void;
|
||||
embeddableIsInitializing: () => void;
|
||||
embeddableIsInitialized: (metadata: EmbeddableMetadata) => void;
|
||||
embeddableStateChanged: (embeddableState: EmbeddableState) => void;
|
||||
embeddableError: (errorMessage: EmbeddableErrorAction) => void;
|
||||
}
|
||||
|
||||
const mapStateToProps = (
|
||||
{ dashboard }: CoreKibanaState,
|
||||
{ embeddableFactory, panelId }: DashboardPanelContainerOwnProps
|
||||
) => {
|
||||
const embeddable = getEmbeddable(dashboard, panelId);
|
||||
let error = null;
|
||||
if (!embeddableFactory) {
|
||||
|
@ -60,35 +100,26 @@ const mapStateToProps = ({ dashboard }, { embeddableFactory, panelId }) => {
|
|||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = (dispatch, { panelId }) => ({
|
||||
destroy: () => (
|
||||
dispatch(deletePanel(panelId))
|
||||
),
|
||||
embeddableIsInitializing: () => (
|
||||
dispatch(embeddableIsInitializing(panelId))
|
||||
),
|
||||
embeddableIsInitialized: (metadata) => (
|
||||
dispatch(embeddableIsInitialized({ panelId, metadata }))
|
||||
),
|
||||
embeddableStateChanged: (embeddableState) => (
|
||||
dispatch(embeddableStateChanged({ panelId, embeddableState }))
|
||||
),
|
||||
embeddableError: (errorMessage) => (
|
||||
dispatch(embeddableError({ panelId, error: errorMessage }))
|
||||
)
|
||||
const mapDispatchToProps = (
|
||||
dispatch: ThunkDispatch<CoreKibanaState, {}, Action<any>>,
|
||||
{ panelId }: DashboardPanelContainerOwnProps
|
||||
): DashboardPanelContainerDispatchProps => ({
|
||||
destroy: () => dispatch(deletePanel(panelId)),
|
||||
embeddableIsInitializing: () => dispatch(embeddableIsInitializing(panelId)),
|
||||
embeddableIsInitialized: (metadata: EmbeddableMetadata) =>
|
||||
dispatch(embeddableIsInitialized({ panelId, metadata })),
|
||||
embeddableStateChanged: (embeddableState: EmbeddableState) =>
|
||||
dispatch(embeddableStateChanged({ panelId, embeddableState })),
|
||||
embeddableError: (errorMessage: EmbeddableErrorAction) =>
|
||||
dispatch(embeddableError({ panelId, error: errorMessage })),
|
||||
});
|
||||
|
||||
export const DashboardPanelContainer = connect(
|
||||
export const DashboardPanelContainer = connect<
|
||||
DashboardPanelContainerStateProps,
|
||||
DashboardPanelContainerDispatchProps,
|
||||
DashboardPanelContainerOwnProps,
|
||||
CoreKibanaState
|
||||
>(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(DashboardPanel);
|
||||
|
||||
DashboardPanelContainer.propTypes = {
|
||||
panelId: PropTypes.string.isRequired,
|
||||
/**
|
||||
* @type {EmbeddableFactory}
|
||||
*/
|
||||
embeddableFactory: PropTypes.shape({
|
||||
create: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
};
|
|
@ -17,13 +17,17 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
jest.mock('ui/chrome',
|
||||
jest.mock(
|
||||
'ui/chrome',
|
||||
() => ({
|
||||
getKibanaVersion: () => '6.3.0',
|
||||
}), { virtual: true });
|
||||
}),
|
||||
{ virtual: true }
|
||||
);
|
||||
|
||||
import { DEFAULT_PANEL_HEIGHT, DEFAULT_PANEL_WIDTH } from '../dashboard_constants';
|
||||
import { PanelUtils } from './panel_utils';
|
||||
import { DEFAULT_PANEL_WIDTH, DEFAULT_PANEL_HEIGHT } from '../dashboard_constants';
|
||||
import { createPanelState } from './panel_state';
|
||||
|
||||
test('parseVersion', () => {
|
||||
const { major, minor } = PanelUtils.parseVersion('6.2.0');
|
||||
|
@ -33,8 +37,24 @@ test('parseVersion', () => {
|
|||
|
||||
test('convertPanelDataPre_6_1 gives supplies width and height when missing', () => {
|
||||
const panelData = [
|
||||
{ col: 3, id: 'foo1', row: 1, type: 'visualization', panelIndex: 1 },
|
||||
{ col: 3, id: 'foo2', row: 1, size_x: 3, size_y: 2, type: 'visualization', panelIndex: 2 }
|
||||
{
|
||||
col: 3,
|
||||
id: 'foo1',
|
||||
row: 1,
|
||||
type: 'visualization',
|
||||
panelIndex: 1,
|
||||
gridData: createPanelState,
|
||||
},
|
||||
{
|
||||
col: 3,
|
||||
id: 'foo2',
|
||||
row: 1,
|
||||
size_x: 3,
|
||||
size_y: 2,
|
||||
type: 'visualization',
|
||||
panelIndex: 2,
|
||||
gridData: createPanelState,
|
||||
},
|
||||
];
|
||||
panelData.forEach(oldPanel => PanelUtils.convertPanelDataPre_6_1(oldPanel));
|
||||
expect(panelData[0].gridData.w).toBe(DEFAULT_PANEL_WIDTH);
|
||||
|
@ -54,9 +74,9 @@ test('convertPanelDataPre_6_3 scales panel dimensions', () => {
|
|||
x: 2,
|
||||
y: 5,
|
||||
},
|
||||
version: '6.2.0'
|
||||
version: '6.2.0',
|
||||
};
|
||||
const updatedPanel = PanelUtils.convertPanelDataPre_6_3(oldPanel);
|
||||
const updatedPanel = PanelUtils.convertPanelDataPre_6_3(oldPanel, false);
|
||||
expect(updatedPanel.gridData.w).toBe(28);
|
||||
expect(updatedPanel.gridData.h).toBe(15);
|
||||
expect(updatedPanel.gridData.x).toBe(8);
|
||||
|
@ -72,7 +92,7 @@ test('convertPanelDataPre_6_3 with margins scales panel dimensions', () => {
|
|||
x: 2,
|
||||
y: 5,
|
||||
},
|
||||
version: '6.2.0'
|
||||
version: '6.2.0',
|
||||
};
|
||||
const updatedPanel = PanelUtils.convertPanelDataPre_6_3(oldPanel, true);
|
||||
expect(updatedPanel.gridData.w).toBe(28);
|
||||
|
|
|
@ -17,25 +17,41 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { DEFAULT_PANEL_WIDTH, DEFAULT_PANEL_HEIGHT } from '../dashboard_constants';
|
||||
import _ from 'lodash';
|
||||
import chrome from 'ui/chrome';
|
||||
import { DEFAULT_PANEL_HEIGHT, DEFAULT_PANEL_WIDTH } from '../dashboard_constants';
|
||||
import { GridData, PanelState } from '../selectors';
|
||||
|
||||
const PANEL_HEIGHT_SCALE_FACTOR = 5;
|
||||
const PANEL_HEIGHT_SCALE_FACTOR_WITH_MARGINS = 4;
|
||||
const PANEL_WIDTH_SCALE_FACTOR = 4;
|
||||
|
||||
export class PanelUtils {
|
||||
export interface SemanticVersion {
|
||||
major: number;
|
||||
minor: number;
|
||||
}
|
||||
|
||||
export class PanelUtils {
|
||||
// 6.1 switched from gridster to react grid. React grid uses different variables for tracking layout
|
||||
static convertPanelDataPre_6_1(panel) { // eslint-disable-line 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> {
|
||||
['col', 'row'].forEach(key => {
|
||||
if (!_.has(panel, key)) {
|
||||
throw new Error(i18n.translate('kbn.dashboard.panel.unableToMigratePanelDataForSixOneZeroErrorMessage', {
|
||||
defaultMessage: 'Unable to migrate panel data for "6.1.0" backwards compatibility, panel does not contain expected field: {key}',
|
||||
values: { key },
|
||||
}));
|
||||
throw new Error(
|
||||
i18n.translate('kbn.dashboard.panel.unableToMigratePanelDataForSixOneZeroErrorMessage', {
|
||||
defaultMessage:
|
||||
'Unable to migrate panel data for "6.1.0" backwards compatibility, panel does not contain expected field: {key}',
|
||||
values: { key },
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -44,7 +60,7 @@ export class PanelUtils {
|
|||
y: panel.row - 1,
|
||||
w: panel.size_x || DEFAULT_PANEL_WIDTH,
|
||||
h: panel.size_y || DEFAULT_PANEL_HEIGHT,
|
||||
i: panel.panelIndex.toString()
|
||||
i: panel.panelIndex.toString(),
|
||||
};
|
||||
panel.version = chrome.getKibanaVersion();
|
||||
panel.panelIndex = panel.panelIndex.toString();
|
||||
|
@ -60,18 +76,32 @@ export class PanelUtils {
|
|||
// 1) decrease column height from 100 to 20.
|
||||
// 2) increase rows from 12 to 48
|
||||
// Need to scale pre 6.3 panels so they maintain the same layout
|
||||
static convertPanelDataPre_6_3(panel, useMargins) { // eslint-disable-line camelcase
|
||||
public static convertPanelDataPre_6_3(
|
||||
panel: {
|
||||
gridData: { w: number; x: number; h: number; y: number };
|
||||
version: string;
|
||||
},
|
||||
useMargins: boolean
|
||||
) {
|
||||
['w', 'x', 'h', 'y'].forEach(key => {
|
||||
if (!_.has(panel.gridData, key)) {
|
||||
throw new Error(i18n.translate('kbn.dashboard.panel.unableToMigratePanelDataForSixThreeZeroErrorMessage', {
|
||||
defaultMessage: 'Unable to migrate panel data for "6.3.0" backwards compatibility, panel does not contain expected field: {key}',
|
||||
values: { key },
|
||||
}));
|
||||
throw new Error(
|
||||
i18n.translate(
|
||||
'kbn.dashboard.panel.unableToMigratePanelDataForSixThreeZeroErrorMessage',
|
||||
{
|
||||
defaultMessage:
|
||||
'Unable to migrate panel data for "6.3.0" backwards compatibility, panel does not contain expected field: {key}',
|
||||
values: { key },
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// see https://github.com/elastic/kibana/issues/20635 on why the scale factor changes when margins are being used
|
||||
const heightScaleFactor = useMargins ? PANEL_HEIGHT_SCALE_FACTOR_WITH_MARGINS : PANEL_HEIGHT_SCALE_FACTOR;
|
||||
const heightScaleFactor = useMargins
|
||||
? PANEL_HEIGHT_SCALE_FACTOR_WITH_MARGINS
|
||||
: PANEL_HEIGHT_SCALE_FACTOR;
|
||||
|
||||
panel.gridData.w = panel.gridData.w * PANEL_WIDTH_SCALE_FACTOR;
|
||||
panel.gridData.x = panel.gridData.x * PANEL_WIDTH_SCALE_FACTOR;
|
||||
|
@ -82,38 +112,40 @@ export class PanelUtils {
|
|||
return panel;
|
||||
}
|
||||
|
||||
static parseVersion(version = '6.0.0') {
|
||||
public static parseVersion(version = '6.0.0'): SemanticVersion {
|
||||
const versionSplit = version.split('.');
|
||||
if (versionSplit.length < 3) {
|
||||
throw new Error(i18n.translate('kbn.dashboard.panel.invalidVersionErrorMessage', {
|
||||
defaultMessage: 'Invalid version, {version}, expected {semver}',
|
||||
values: {
|
||||
version,
|
||||
semver: '<major>.<minor>.<patch>',
|
||||
},
|
||||
}));
|
||||
throw new Error(
|
||||
i18n.translate('kbn.dashboard.panel.invalidVersionErrorMessage', {
|
||||
defaultMessage: 'Invalid version, {version}, expected {semver}',
|
||||
values: {
|
||||
version,
|
||||
semver: '<major>.<minor>.<patch>',
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
return {
|
||||
major: parseInt(versionSplit[0], 10),
|
||||
minor: parseInt(versionSplit[1], 10)
|
||||
minor: parseInt(versionSplit[1], 10),
|
||||
};
|
||||
}
|
||||
|
||||
static initPanelIndexes(panels) {
|
||||
public static initPanelIndexes(panels: PanelState[]): void {
|
||||
// find the largest panelIndex in all the panels
|
||||
let maxIndex = this.getMaxPanelIndex(panels);
|
||||
|
||||
// ensure that all panels have a panelIndex
|
||||
panels.forEach(function (panel) {
|
||||
panels.forEach(panel => {
|
||||
if (!panel.panelIndex) {
|
||||
panel.panelIndex = maxIndex++;
|
||||
panel.panelIndex = (maxIndex++).toString();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static getMaxPanelIndex(panels) {
|
||||
let maxId = panels.reduce(function (id, panel) {
|
||||
return Math.max(id, panel.panelIndex || id);
|
||||
public static getMaxPanelIndex(panels: PanelState[]): number {
|
||||
let maxId = panels.reduce((id, panel) => {
|
||||
return Math.max(id, Number(panel.panelIndex || id));
|
||||
}, 0);
|
||||
return ++maxId;
|
||||
}
|
|
@ -43,11 +43,11 @@ describe('embeddableIsInitializing', () => {
|
|||
test('clears the error', () => {
|
||||
store.dispatch(embeddableIsInitializing('foo1'));
|
||||
const initialized = getEmbeddableInitialized(store.getState(), 'foo1');
|
||||
expect(initialized).toBe(false);
|
||||
expect(initialized).toEqual(false);
|
||||
});
|
||||
|
||||
test('and clears the error', () => {
|
||||
const error = getEmbeddableError(store.getState(), 'foo1');
|
||||
expect(error).toBe(undefined);
|
||||
expect(error).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -48,7 +48,7 @@ export interface PanelState {
|
|||
readonly id: SavedObjectId;
|
||||
readonly version: string;
|
||||
readonly type: string;
|
||||
readonly panelIndex: PanelId;
|
||||
panelIndex: PanelId;
|
||||
readonly embeddableConfig: any;
|
||||
readonly gridData: GridData;
|
||||
readonly title?: string;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue