-
- Click the
-
-
-
- button in the menu bar above to add a visualization to the dashboard.
-
-
-
-
- visit the Visualize app
- ,
+ "maxWidth": "36em",
}
}
>
- If you haven't set up any visualizations yet,
-
- visit the Visualize app
-
- to create your first visualization
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This dashboard is empty. Let’s fill it up!
+
+
+
+
+
+
+
+
+
+ Click the
+
+
+
+ button in the menu bar above to add a visualization to the dashboard.
+
+
+
+
+
+
+
+
+
+
+ visit the Visualize app
+ ,
+ }
+ }
+ >
+ If you haven't set up any visualizations yet,
+
+ visit the Visualize app
+
+ to create your first visualization
+
+
+
+
+ {showLinkToVisualize ? addVisualizationParagraph : enterEditModeParagraph}
+
+
+
);
}
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_empty_screen_directive.ts b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_empty_screen_directive.ts
deleted file mode 100644
index 5ebefd817ca4a..0000000000000
--- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_empty_screen_directive.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.
- */
-// @ts-ignore
-import angular from 'angular';
-import { DashboardEmptyScreen } from './dashboard_empty_screen';
-
-angular
- .module('app/dashboard/emptyScreen', ['react'])
- .directive('dashboardEmptyScreen', function(reactDirective: any) {
- return reactDirective(DashboardEmptyScreen, [
- ['showLinkToVisualize', { watchDepth: 'value' }],
- ['onLinkClick', { watchDepth: 'reference' }],
- ]);
- });
diff --git a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/top_nav_ids.ts b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/top_nav_ids.ts
index 9df86f2ca3cce..c67d6891c18e7 100644
--- a/src/legacy/core_plugins/kibana/public/dashboard/top_nav/top_nav_ids.ts
+++ b/src/legacy/core_plugins/kibana/public/dashboard/top_nav/top_nav_ids.ts
@@ -26,4 +26,5 @@ export const TopNavIds = {
ENTER_EDIT_MODE: 'enterEditMode',
CLONE: 'clone',
FULL_SCREEN: 'fullScreenMode',
+ VISUALIZE: 'visualize',
};
diff --git a/src/plugins/dashboard_embeddable_container/public/embeddable/dashboard_container.tsx b/src/plugins/dashboard_embeddable_container/public/embeddable/dashboard_container.tsx
index 684aa93779bc1..021a1a9d1e64a 100644
--- a/src/plugins/dashboard_embeddable_container/public/embeddable/dashboard_container.tsx
+++ b/src/plugins/dashboard_embeddable_container/public/embeddable/dashboard_container.tsx
@@ -90,6 +90,8 @@ export type DashboardReactContext = KibanaReactContext {
public readonly type = DASHBOARD_CONTAINER_TYPE;
+ public renderEmpty?: undefined | (() => React.ReactNode);
+
constructor(
initialInput: DashboardContainerInput,
private readonly options: DashboardContainerOptions,
@@ -124,7 +126,7 @@ export class DashboardContainer extends Container
-
+
,
dom
diff --git a/src/plugins/dashboard_embeddable_container/public/embeddable/grid/_dashboard_grid.scss b/src/plugins/dashboard_embeddable_container/public/embeddable/grid/_dashboard_grid.scss
index 24b813ec58964..0bd356522c7fa 100644
--- a/src/plugins/dashboard_embeddable_container/public/embeddable/grid/_dashboard_grid.scss
+++ b/src/plugins/dashboard_embeddable_container/public/embeddable/grid/_dashboard_grid.scss
@@ -34,7 +34,7 @@
.dshLayout-isMaximizedPanel {
height: 100% !important; /* 1. */
width: 100%;
- position: absolute;
+ position: absolute !important;
}
/**
diff --git a/src/plugins/dashboard_embeddable_container/public/embeddable/viewport/dashboard_viewport.test.tsx b/src/plugins/dashboard_embeddable_container/public/embeddable/viewport/dashboard_viewport.test.tsx
index a2f7b8dc28fb0..e3d9b8552f060 100644
--- a/src/plugins/dashboard_embeddable_container/public/embeddable/viewport/dashboard_viewport.test.tsx
+++ b/src/plugins/dashboard_embeddable_container/public/embeddable/viewport/dashboard_viewport.test.tsx
@@ -121,6 +121,24 @@ test('renders DashboardViewport with no visualizations', () => {
component.unmount();
});
+test('renders DashboardEmptyScreen', () => {
+ const renderEmptyScreen = jest.fn();
+ const { props, options } = getProps({ renderEmpty: renderEmptyScreen });
+ props.container.updateInput({ isEmptyState: true });
+ const component = mount(
+
+
+
+
+
+ );
+ const dashboardEmptyScreenDiv = component.find('.dshDashboardEmptyScreen');
+ expect(dashboardEmptyScreenDiv.length).toBe(1);
+ expect(renderEmptyScreen).toHaveBeenCalled();
+
+ component.unmount();
+});
+
test('renders exit full screen button when in full screen mode', async () => {
const { props, options } = getProps();
props.container.updateInput({ isFullScreenMode: true });
@@ -153,6 +171,39 @@ test('renders exit full screen button when in full screen mode', async () => {
component.unmount();
});
+test('renders exit full screen button when in full screen mode and empty screen', async () => {
+ const renderEmptyScreen = jest.fn();
+ renderEmptyScreen.mockReturnValue(React.createElement('div'));
+ const { props, options } = getProps({ renderEmpty: renderEmptyScreen });
+ props.container.updateInput({ isEmptyState: true, isFullScreenMode: true });
+ const component = mount(
+
+
+
+
+
+ );
+ expect(
+ (component
+ .find('.dshDashboardEmptyScreen')
+ .childAt(0)
+ .type() as any).name
+ ).toBe('ExitFullScreenButton');
+
+ props.container.updateInput({ isFullScreenMode: false });
+ component.update();
+ await nextTick();
+
+ expect(
+ (component
+ .find('.dshDashboardEmptyScreen')
+ .childAt(0)
+ .type() as any).name
+ ).not.toBe('ExitFullScreenButton');
+
+ component.unmount();
+});
+
test('DashboardViewport unmount unsubscribes', async done => {
const { props, options } = getProps();
const component = mount(
diff --git a/src/plugins/dashboard_embeddable_container/public/embeddable/viewport/dashboard_viewport.tsx b/src/plugins/dashboard_embeddable_container/public/embeddable/viewport/dashboard_viewport.tsx
index 13407e5e33725..e7fd379898dd1 100644
--- a/src/plugins/dashboard_embeddable_container/public/embeddable/viewport/dashboard_viewport.tsx
+++ b/src/plugins/dashboard_embeddable_container/public/embeddable/viewport/dashboard_viewport.tsx
@@ -26,6 +26,7 @@ import { context } from '../../../../kibana_react/public';
export interface DashboardViewportProps {
container: DashboardContainer;
+ renderEmpty?: () => React.ReactNode;
}
interface State {
@@ -34,6 +35,7 @@ interface State {
title: string;
description?: string;
panels: { [key: string]: PanelState };
+ isEmptyState?: boolean;
}
export class DashboardViewport extends React.Component {
@@ -44,26 +46,40 @@ export class DashboardViewport extends React.Component {
- const { isFullScreenMode, useMargins, title, description } = this.props.container.getInput();
+ const {
+ isFullScreenMode,
+ useMargins,
+ title,
+ description,
+ isEmptyState,
+ } = this.props.container.getInput();
if (this.mounted) {
this.setState({
isFullScreenMode,
description,
useMargins,
title,
+ isEmptyState,
});
}
});
@@ -82,19 +98,33 @@ export class DashboardViewport extends React.Component
+ {isFullScreenMode && (
+
+ )}
+ {renderEmpty && renderEmpty()}
+
+ );
+ }
+
+ private renderContainerScreen() {
const { container } = this.props;
+ const { isFullScreenMode, panels, title, description, useMargins } = this.state;
return (
- {this.state.isFullScreenMode && (
+ {isFullScreenMode && (
@@ -103,4 +133,13 @@ export class DashboardViewport extends React.Component
);
}
+
+ public render() {
+ return (
+
+ {this.state.isEmptyState ? this.renderEmptyScreen() : null}
+ {this.renderContainerScreen()}
+
+ );
+ }
}
diff --git a/src/plugins/embeddable/public/lib/containers/container.ts b/src/plugins/embeddable/public/lib/containers/container.ts
index bce16747ed48e..71e7cca3552bb 100644
--- a/src/plugins/embeddable/public/lib/containers/container.ts
+++ b/src/plugins/embeddable/public/lib/containers/container.ts
@@ -240,6 +240,7 @@ export abstract class Container<
...this.input.panels,
[panelState.explicitInput.id]: panelState,
},
+ isEmptyState: false,
} as Partial);
return await this.untilEmbeddableLoaded(panelState.explicitInput.id);
diff --git a/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts b/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts
index 33cb146a056cb..0197582778940 100644
--- a/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts
+++ b/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts
@@ -28,7 +28,7 @@ export interface EmbeddableInput {
id: string;
lastReloadRequestTime?: number;
hidePanelTitles?: boolean;
-
+ isEmptyState?: boolean;
/**
* List of action IDs that this embeddable should not render.
*/
diff --git a/test/functional/apps/dashboard/full_screen_mode.js b/test/functional/apps/dashboard/full_screen_mode.js
index e18fd47b39b16..bf549ec21a6d3 100644
--- a/test/functional/apps/dashboard/full_screen_mode.js
+++ b/test/functional/apps/dashboard/full_screen_mode.js
@@ -78,7 +78,6 @@ export default function ({ getService, getPageObjects }) {
const logoButton = await PageObjects.dashboard.getExitFullScreenLogoButton();
await logoButton.moveMouseTo();
await PageObjects.dashboard.clickExitFullScreenTextButton();
-
await retry.try(async () => {
const isChromeVisible = await PageObjects.common.isChromeVisible();
expect(isChromeVisible).to.be(true);
diff --git a/x-pack/plugins/advanced_ui_actions/public/does_inherit_time_range.ts b/x-pack/plugins/advanced_ui_actions/public/does_inherit_time_range.ts
index 4cfe581b7eac5..d1568a5ab96ce 100644
--- a/x-pack/plugins/advanced_ui_actions/public/does_inherit_time_range.ts
+++ b/x-pack/plugins/advanced_ui_actions/public/does_inherit_time_range.ts
@@ -17,6 +17,10 @@ export function doesInheritTimeRange(embeddable: Embeddable) {
// Note: this logic might not work in a container nested world... the explicit input
// may be on the root... or any of the interim parents.
+ // if it's a dashboard emptys screen, there will be no embeddable
+ if (!parent.getInput().panels[embeddable.id]) {
+ return false;
+ }
// If there is no explicit input defined on the parent then this embeddable inherits the
// time range from whatever the time range of the parent is.
return parent.getInput().panels[embeddable.id].explicitInput.timeRange === undefined;
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 98ffcf33febcb..1190bffb2956d 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -12741,4 +12741,4 @@
"xpack.licensing.welcomeBanner.licenseIsExpiredDescription.updateYourLicenseLinkText": "ライセンスを更新",
"xpack.licensing.welcomeBanner.licenseIsExpiredTitle": "ご使用の {licenseType} ライセンスは期限切れです"
}
-}
+}
\ No newline at end of file