Move DashboardEmptyScreen inside DashboardViewport (#51939) (#52637)

* Prototyping adding Visualization to Dashboard

* i18n fixes

* Remvoing dashboard empty screen directive

* Updating test for empty dashboard screen

* Removing unused state variable

* Adding a test for DashboardViewPort

* i18n & minor fixes

* Fixing fullscreen mode view

* Fixing failing functional test (hopefully)

* Minor style fix

* Fixing EUI text, rendering empty screen OR the panels

* Fixing empty screen in fullscreen mode

* Update snapshot

* Trying to render empty screen from Angular controller

* refactor: 💡 don't pass renderEmpty through inputs

And make sure isEmptyState is not stale.

* Fixing tests after Vadim's commit

* Removing unnecessary isEmptyStateProps

* Skipping failing test

* Removing unnecessary en.json file

* Re-adding emptyState, reintroducing functional test

* Fixing ja-JP file

* Undoing my thing to the functional test
This commit is contained in:
Maja Grubic 2019-12-11 14:03:18 +00:00 committed by GitHub
parent 0ce02453ea
commit 580bd82835
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 461 additions and 217 deletions

View file

@ -193,78 +193,160 @@ exports[`DashboardEmptyScreen renders correctly with visualize paragraph 1`] = `
textComponent={Symbol(react.fragment)}
>
<PseudoLocaleWrapper>
<EuiIcon
color="subdued"
size="xxl"
type="dashboardApp"
<EuiPage
className="dshStartScreen"
restrictWidth="36em"
>
<EuiIconEmpty
className="euiIcon euiIcon--xxLarge euiIcon--subdued euiIcon--app euiIcon-isLoading"
focusable="false"
style={null}
>
<svg
className="euiIcon euiIcon--xxLarge euiIcon--subdued euiIcon--app euiIcon-isLoading"
focusable="false"
height={16}
style={null}
viewBox="0 0 16 16"
width={16}
xmlns="http://www.w3.org/2000/svg"
/>
</EuiIconEmpty>
</EuiIcon>
<h2>
This dashboard is empty. Lets fill it up!
</h2>
<p>
<span>
Click the
<EuiLink
aria-label="Add visualization"
data-test-subj="emptyDashboardAddPanelButton"
onClick={[MockFunction]}
>
<button
aria-label="Add visualization"
className="euiLink euiLink--primary"
data-test-subj="emptyDashboardAddPanelButton"
onClick={[MockFunction]}
type="button"
>
Add
</button>
</EuiLink>
button in the menu bar above to add a visualization to the dashboard.
</span>
</p>
<p
className="linkToVisualizeParagraph"
>
<FormattedMessage
defaultMessage="If you haven't set up any visualizations yet, {visualizeAppLink} to create your first visualization"
id="kbn.dashboard.addVisualizationDescription3"
values={
<div
className="euiPage euiPage--restrictWidth-custom dshStartScreen"
style={
Object {
"visualizeAppLink": <a
className="euiLink"
href="#/visualize"
>
visit the Visualize app
</a>,
"maxWidth": "36em",
}
}
>
If you haven't set up any visualizations yet,
<a
className="euiLink"
href="#/visualize"
<EuiPageBody
component="main"
restrictWidth={false}
>
visit the Visualize app
</a>
to create your first visualization
</FormattedMessage>
</p>
<main
className="euiPageBody"
>
<EuiPageContent
horizontalPosition="center"
panelPaddingSize="l"
verticalPosition="center"
>
<EuiPanel
className="euiPageContent euiPageContent--verticalCenter euiPageContent--horizontalCenter"
paddingSize="l"
>
<div
className="euiPanel euiPanel--paddingLarge euiPageContent euiPageContent--verticalCenter euiPageContent--horizontalCenter"
>
<EuiIcon
color="subdued"
size="xxl"
type="dashboardApp"
>
<EuiIconEmpty
className="euiIcon euiIcon--xxLarge euiIcon--subdued euiIcon--app euiIcon-isLoading"
focusable="false"
style={null}
>
<svg
className="euiIcon euiIcon--xxLarge euiIcon--subdued euiIcon--app euiIcon-isLoading"
focusable="false"
height={16}
style={null}
viewBox="0 0 16 16"
width={16}
xmlns="http://www.w3.org/2000/svg"
/>
</EuiIconEmpty>
</EuiIcon>
<EuiSpacer
size="s"
>
<div
className="euiSpacer euiSpacer--s"
/>
</EuiSpacer>
<EuiText
grow={true}
>
<div
className="euiText euiText--medium"
>
<h2
key="0.5"
>
This dashboard is empty. Lets fill it up!
</h2>
</div>
</EuiText>
<EuiSpacer
size="m"
>
<div
className="euiSpacer euiSpacer--m"
/>
</EuiSpacer>
<EuiText
size="m"
>
<div
className="euiText euiText--medium"
>
<p>
Click the
<EuiLink
aria-label="Add visualization"
data-test-subj="emptyDashboardAddPanelButton"
onClick={[MockFunction]}
>
<button
aria-label="Add visualization"
className="euiLink euiLink--primary"
data-test-subj="emptyDashboardAddPanelButton"
onClick={[MockFunction]}
type="button"
>
Add
</button>
</EuiLink>
button in the menu bar above to add a visualization to the dashboard.
</p>
</div>
</EuiText>
<EuiSpacer
size="m"
>
<div
className="euiSpacer euiSpacer--m"
/>
</EuiSpacer>
<EuiText
data-test-subj="linkToVisualizeParagraph"
>
<div
className="euiText euiText--medium"
data-test-subj="linkToVisualizeParagraph"
>
<p>
<FormattedMessage
defaultMessage="If you haven't set up any visualizations yet, {visualizeAppLink} to create your first visualization"
id="kbn.dashboard.addVisualizationDescription3"
values={
Object {
"visualizeAppLink": <a
className="euiLink"
href="#/visualize"
>
visit the Visualize app
</a>,
}
}
>
If you haven't set up any visualizations yet,
<a
className="euiLink"
href="#/visualize"
>
visit the Visualize app
</a>
to create your first visualization
</FormattedMessage>
</p>
</div>
</EuiText>
</div>
</EuiPanel>
</EuiPageContent>
</main>
</EuiPageBody>
</div>
</EuiPage>
</PseudoLocaleWrapper>
</IntlProvider>
</I18nProvider>
@ -464,51 +546,119 @@ exports[`DashboardEmptyScreen renders correctly without visualize paragraph 1`]
textComponent={Symbol(react.fragment)}
>
<PseudoLocaleWrapper>
<EuiIcon
color="subdued"
size="xxl"
type="dashboardApp"
<EuiPage
className="dshStartScreen"
restrictWidth="36em"
>
<EuiIconEmpty
className="euiIcon euiIcon--xxLarge euiIcon--subdued euiIcon--app euiIcon-isLoading"
focusable="false"
style={null}
<div
className="euiPage euiPage--restrictWidth-custom dshStartScreen"
style={
Object {
"maxWidth": "36em",
}
}
>
<svg
className="euiIcon euiIcon--xxLarge euiIcon--subdued euiIcon--app euiIcon-isLoading"
focusable="false"
height={16}
style={null}
viewBox="0 0 16 16"
width={16}
xmlns="http://www.w3.org/2000/svg"
/>
</EuiIconEmpty>
</EuiIcon>
<h2>
This dashboard is empty. Lets fill it up!
</h2>
<p>
<span>
Click the
<EuiLink
aria-label="Edit dashboard"
data-test-subj=""
onClick={[MockFunction]}
<EuiPageBody
component="main"
restrictWidth={false}
>
<button
aria-label="Edit dashboard"
className="euiLink euiLink--primary"
data-test-subj=""
onClick={[MockFunction]}
type="button"
<main
className="euiPageBody"
>
Edit
</button>
</EuiLink>
button in the menu bar above to start working on your new dashboard.
</span>
</p>
<EuiPageContent
horizontalPosition="center"
panelPaddingSize="l"
verticalPosition="center"
>
<EuiPanel
className="euiPageContent euiPageContent--verticalCenter euiPageContent--horizontalCenter"
paddingSize="l"
>
<div
className="euiPanel euiPanel--paddingLarge euiPageContent euiPageContent--verticalCenter euiPageContent--horizontalCenter"
>
<EuiIcon
color="subdued"
size="xxl"
type="dashboardApp"
>
<EuiIconEmpty
className="euiIcon euiIcon--xxLarge euiIcon--subdued euiIcon--app euiIcon-isLoading"
focusable="false"
style={null}
>
<svg
className="euiIcon euiIcon--xxLarge euiIcon--subdued euiIcon--app euiIcon-isLoading"
focusable="false"
height={16}
style={null}
viewBox="0 0 16 16"
width={16}
xmlns="http://www.w3.org/2000/svg"
/>
</EuiIconEmpty>
</EuiIcon>
<EuiSpacer
size="s"
>
<div
className="euiSpacer euiSpacer--s"
/>
</EuiSpacer>
<EuiText
grow={true}
>
<div
className="euiText euiText--medium"
>
<h2
key="0.5"
>
This dashboard is empty. Lets fill it up!
</h2>
</div>
</EuiText>
<EuiSpacer
size="m"
>
<div
className="euiSpacer euiSpacer--m"
/>
</EuiSpacer>
<EuiText
size="m"
>
<div
className="euiText euiText--medium"
>
<p>
Click the
<EuiLink
aria-label="Edit dashboard"
data-test-subj=""
onClick={[MockFunction]}
>
<button
aria-label="Edit dashboard"
className="euiLink euiLink--primary"
data-test-subj=""
onClick={[MockFunction]}
type="button"
>
Edit
</button>
</EuiLink>
button in the menu bar above to start working on your new dashboard.
</p>
</div>
</EuiText>
</div>
</EuiPanel>
</EuiPageContent>
</main>
</EuiPageBody>
</div>
</EuiPage>
</PseudoLocaleWrapper>
</IntlProvider>
</I18nProvider>

View file

@ -18,7 +18,9 @@
*/
import React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { DashboardEmptyScreen, Props } from '../dashboard_empty_screen';
import { DashboardEmptyScreen, DashboardEmptyScreenProps } from '../dashboard_empty_screen';
// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
describe('DashboardEmptyScreen', () => {
const defaultProps = {
@ -26,7 +28,7 @@ describe('DashboardEmptyScreen', () => {
onLinkClick: jest.fn(),
};
function mountComponent(props?: Props) {
function mountComponent(props?: DashboardEmptyScreenProps) {
const compProps = props || defaultProps;
const comp = mountWithIntl(<DashboardEmptyScreen {...compProps} />);
return comp;
@ -35,14 +37,14 @@ describe('DashboardEmptyScreen', () => {
test('renders correctly with visualize paragraph', () => {
const component = mountComponent();
expect(component).toMatchSnapshot();
const paragraph = component.find('.linkToVisualizeParagraph');
const paragraph = findTestSubject(component, 'linkToVisualizeParagraph');
expect(paragraph.length).toBe(1);
});
test('renders correctly without visualize paragraph', () => {
const component = mountComponent({ ...defaultProps, ...{ showLinkToVisualize: false } });
expect(component).toMatchSnapshot();
const paragraph = component.find('.linkToVisualizeParagraph');
const paragraph = findTestSubject(component, 'linkToVisualizeParagraph');
expect(paragraph.length).toBe(0);
});
});

View file

@ -6,9 +6,5 @@
.dshStartScreen {
text-align: center;
padding: $euiSize;
> * {
max-width: 36em !important;
}
padding: $euiSizeS;
}

View file

@ -131,7 +131,6 @@ function createLocalAngularModule(core: AppMountContext['core'], navigation: Nav
'app/dashboard/State',
'app/dashboard/ConfirmModal',
'app/dashboard/icon',
'app/dashboard/emptyScreen',
]);
return dashboardAngularModule;
}

View file

@ -48,24 +48,6 @@
>
</kbn-top-nav>
<div ng-show="getShouldShowEditHelp() || getShouldShowViewHelp()" class="dshStartScreen">
<div class="euiPanel euiPanel--paddingLarge euiPageContent euiPageContent--horizontalCenter">
<br><br>
<div ng-show="getShouldShowEditHelp()" class="euiText">
<dashboard-empty-screen on-link-click="showAddPanel"
show-link-to-visualize="true"
/>
</div>
<div ng-show="getShouldShowViewHelp()" class="euiText">
<dashboard-empty-screen show-link-to-visualize="false"
on-link-click="enterEditMode"
/>
</div>
</div>
</div>
<h1 class="euiScreenReaderOnly">{{screenTitle}}</h1>
<div id="dashboardViewport"></div>

View file

@ -21,9 +21,10 @@ import _ from 'lodash';
import { i18n } from '@kbn/i18n';
import React from 'react';
import angular from 'angular';
import { uniq } from 'lodash';
import { uniq, noop } from 'lodash';
import { Subscription } from 'rxjs';
import { DashboardEmptyScreen, DashboardEmptyScreenProps } from './dashboard_empty_screen';
import {
subscribeWithScope,
@ -40,8 +41,6 @@ import {
import { FilterStateManager, IndexPattern } from '../../../data/public';
import { Query, SavedQuery, IndexPatternsContract } from '../../../../../plugins/data/public';
import './dashboard_empty_screen_directive';
import {
DashboardContainer,
DASHBOARD_CONTAINER_TYPE,
@ -146,6 +145,16 @@ export class DashboardAppController {
}
$scope.showSaveQuery = dashboardCapabilities.saveQuery as boolean;
$scope.getShouldShowEditHelp = () =>
!dashboardStateManager.getPanels().length &&
dashboardStateManager.getIsEditMode() &&
!dashboardConfig.getHideWriteControls();
$scope.getShouldShowViewHelp = () =>
!dashboardStateManager.getPanels().length &&
dashboardStateManager.getIsViewMode() &&
!dashboardConfig.getHideWriteControls();
const updateIndexPatterns = (container?: DashboardContainer) => {
if (!container || isErrorEmbeddable(container)) {
return;
@ -174,6 +183,17 @@ export class DashboardAppController {
}
};
const getEmptyScreenProps = (shouldShowEditHelp: boolean): DashboardEmptyScreenProps => {
const emptyScreenProps: DashboardEmptyScreenProps = {
onLinkClick: shouldShowEditHelp ? $scope.showAddPanel : $scope.enterEditMode,
showLinkToVisualize: shouldShowEditHelp,
};
if (shouldShowEditHelp) {
emptyScreenProps.onVisualizeClick = noop;
}
return emptyScreenProps;
};
const getDashboardInput = (): DashboardContainerInput => {
const embeddablesMap: {
[key: string]: DashboardPanelState;
@ -185,6 +205,8 @@ export class DashboardAppController {
if (dashboardContainer && !isErrorEmbeddable(dashboardContainer)) {
expandedPanelId = dashboardContainer.getInput().expandedPanelId;
}
const shouldShowEditHelp = $scope.getShouldShowEditHelp();
const shouldShowViewHelp = $scope.getShouldShowViewHelp();
return {
id: dashboardStateManager.savedDashboard.id || '',
filters: queryFilter.getFilters(),
@ -197,6 +219,7 @@ export class DashboardAppController {
viewMode: dashboardStateManager.getViewMode(),
panels: embeddablesMap,
isFullScreenMode: dashboardStateManager.getFullScreenMode(),
isEmptyState: shouldShowEditHelp || shouldShowViewHelp,
useMargins: dashboardStateManager.getUseMargins(),
lastReloadRequestTime,
title: dashboardStateManager.getTitle(),
@ -237,6 +260,15 @@ export class DashboardAppController {
if (!isErrorEmbeddable(container)) {
dashboardContainer = container;
dashboardContainer.renderEmpty = () => {
const shouldShowEditHelp = $scope.getShouldShowEditHelp();
const shouldShowViewHelp = $scope.getShouldShowViewHelp();
const isEmptyState = shouldShowEditHelp || shouldShowViewHelp;
return isEmptyState ? (
<DashboardEmptyScreen {...getEmptyScreenProps(shouldShowEditHelp)} />
) : null;
};
updateIndexPatterns(dashboardContainer);
outputSubscription = dashboardContainer.getOutput$().subscribe(() => {
@ -337,15 +369,6 @@ export class DashboardAppController {
updateBreadcrumbs();
dashboardStateManager.registerChangeListener(updateBreadcrumbs);
$scope.getShouldShowEditHelp = () =>
!dashboardStateManager.getPanels().length &&
dashboardStateManager.getIsEditMode() &&
!dashboardConfig.getHideWriteControls();
$scope.getShouldShowViewHelp = () =>
!dashboardStateManager.getPanels().length &&
dashboardStateManager.getIsViewMode() &&
!dashboardConfig.getHideWriteControls();
const getChangesFromAppStateForContainerState = () => {
const appStateDashboardInput = getDashboardInput();
if (!dashboardContainer || isErrorEmbeddable(dashboardContainer)) {
@ -736,6 +759,8 @@ export class DashboardAppController {
}
};
navActions[TopNavIds.VISUALIZE] = async () => {};
navActions[TopNavIds.OPTIONS] = anchorElement => {
showOptionsPopover({
anchorElement,

View file

@ -18,29 +18,43 @@
*/
import React from 'react';
import { I18nProvider, FormattedMessage } from '@kbn/i18n/react';
import { EuiIcon, EuiLink } from '@elastic/eui';
import {
EuiIcon,
EuiLink,
EuiSpacer,
EuiPageContent,
EuiPageBody,
EuiPage,
EuiText,
} from '@elastic/eui';
import * as constants from './dashboard_empty_screen_constants';
export interface Props {
export interface DashboardEmptyScreenProps {
showLinkToVisualize: boolean;
onLinkClick: () => void;
onVisualizeClick?: () => void;
}
export function DashboardEmptyScreen({ showLinkToVisualize, onLinkClick }: Props) {
export function DashboardEmptyScreen({
showLinkToVisualize,
onLinkClick,
}: DashboardEmptyScreenProps) {
const linkToVisualizeParagraph = (
<p className="linkToVisualizeParagraph">
<FormattedMessage
id="kbn.dashboard.addVisualizationDescription3"
defaultMessage="If you haven't set up any visualizations yet, {visualizeAppLink} to create your first visualization"
values={{
visualizeAppLink: (
<a className="euiLink" href="#/visualize">
{constants.visualizeAppLinkTest}
</a>
),
}}
/>
</p>
<EuiText data-test-subj="linkToVisualizeParagraph">
<p>
<FormattedMessage
id="kbn.dashboard.addVisualizationDescription3"
defaultMessage="If you haven't set up any visualizations yet, {visualizeAppLink} to create your first visualization"
values={{
visualizeAppLink: (
<a className="euiLink" href="#/visualize">
{constants.visualizeAppLinkTest}
</a>
),
}}
/>
</p>
</EuiText>
);
const paragraph = (
description1: string,
@ -50,15 +64,15 @@ export function DashboardEmptyScreen({ showLinkToVisualize, onLinkClick }: Props
dataTestSubj?: string
) => {
return (
<p>
<span>
<EuiText size="m">
<p>
{description1}
<EuiLink onClick={onLinkClick} aria-label={ariaLabel} data-test-subj={dataTestSubj || ''}>
{linkText}
</EuiLink>
{description2}
</span>
</p>
</p>
</EuiText>
);
};
const addVisualizationParagraph = (
@ -70,6 +84,7 @@ export function DashboardEmptyScreen({ showLinkToVisualize, onLinkClick }: Props
constants.addVisualizationLinkAriaLabel,
'emptyDashboardAddPanelButton'
)}
<EuiSpacer size="m" />
{linkToVisualizeParagraph}
</React.Fragment>
);
@ -81,11 +96,19 @@ export function DashboardEmptyScreen({ showLinkToVisualize, onLinkClick }: Props
);
return (
<I18nProvider>
<React.Fragment>
<EuiIcon type="dashboardApp" size="xxl" color="subdued" />
<h2>{constants.fillDashboardTitle}</h2>
{showLinkToVisualize ? addVisualizationParagraph : enterEditModeParagraph}
</React.Fragment>
<EuiPage className="dshStartScreen" restrictWidth={'36em'}>
<EuiPageBody>
<EuiPageContent verticalPosition="center" horizontalPosition="center">
<EuiIcon type="dashboardApp" size="xxl" color="subdued" />
<EuiSpacer size="s" />
<EuiText grow={true}>
<h2 key={0.5}>{constants.fillDashboardTitle}</h2>
</EuiText>
<EuiSpacer size="m" />
{showLinkToVisualize ? addVisualizationParagraph : enterEditModeParagraph}
</EuiPageContent>
</EuiPageBody>
</EuiPage>
</I18nProvider>
);
}

View file

@ -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' }],
]);
});

View file

@ -26,4 +26,5 @@ export const TopNavIds = {
ENTER_EDIT_MODE: 'enterEditMode',
CLONE: 'clone',
FULL_SCREEN: 'fullScreenMode',
VISUALIZE: 'visualize',
};

View file

@ -90,6 +90,8 @@ export type DashboardReactContext = KibanaReactContext<DashboardContainerOptions
export class DashboardContainer extends Container<InheritedChildInput, DashboardContainerInput> {
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<InheritedChildInput, Dashboard
ReactDOM.render(
<I18nProvider>
<KibanaContextProvider services={this.options}>
<DashboardViewport container={this} />
<DashboardViewport renderEmpty={this.renderEmpty} container={this} />
</KibanaContextProvider>
</I18nProvider>,
dom

View file

@ -34,7 +34,7 @@
.dshLayout-isMaximizedPanel {
height: 100% !important; /* 1. */
width: 100%;
position: absolute;
position: absolute !important;
}
/**

View file

@ -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(
<I18nProvider>
<KibanaContextProvider services={options}>
<DashboardViewport {...props} />
</KibanaContextProvider>
</I18nProvider>
);
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(
<I18nProvider>
<KibanaContextProvider services={options}>
<DashboardViewport {...props} />
</KibanaContextProvider>
</I18nProvider>
);
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(

View file

@ -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<DashboardViewportProps, State> {
@ -44,26 +46,40 @@ export class DashboardViewport extends React.Component<DashboardViewportProps, S
private mounted: boolean = false;
constructor(props: DashboardViewportProps) {
super(props);
const { isFullScreenMode, panels, useMargins, title } = this.props.container.getInput();
const {
isFullScreenMode,
panels,
useMargins,
title,
isEmptyState,
} = this.props.container.getInput();
this.state = {
isFullScreenMode,
panels,
useMargins,
title,
isEmptyState,
};
}
public componentDidMount() {
this.mounted = true;
this.subscription = this.props.container.getInput$().subscribe(() => {
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<DashboardViewportProps, S
});
};
public render() {
private renderEmptyScreen() {
const { renderEmpty } = this.props;
const { isFullScreenMode } = this.state;
return (
<div className="dshDashboardEmptyScreen">
{isFullScreenMode && (
<this.context.services.ExitFullScreenButton
onExitFullScreenMode={this.onExitFullScreenMode}
/>
)}
{renderEmpty && renderEmpty()}
</div>
);
}
private renderContainerScreen() {
const { container } = this.props;
const { isFullScreenMode, panels, title, description, useMargins } = this.state;
return (
<div
data-shared-items-count={Object.values(this.state.panels).length}
data-shared-items-count={Object.values(panels).length}
data-shared-items-container
data-title={this.state.title}
data-description={this.state.description}
className={
this.state.useMargins ? 'dshDashboardViewport-withMargins' : 'dshDashboardViewport'
}
data-title={title}
data-description={description}
className={useMargins ? 'dshDashboardViewport-withMargins' : 'dshDashboardViewport'}
>
{this.state.isFullScreenMode && (
{isFullScreenMode && (
<this.context.services.ExitFullScreenButton
onExitFullScreenMode={this.onExitFullScreenMode}
/>
@ -103,4 +133,13 @@ export class DashboardViewport extends React.Component<DashboardViewportProps, S
</div>
);
}
public render() {
return (
<React.Fragment>
{this.state.isEmptyState ? this.renderEmptyScreen() : null}
{this.renderContainerScreen()}
</React.Fragment>
);
}
}

View file

@ -240,6 +240,7 @@ export abstract class Container<
...this.input.panels,
[panelState.explicitInput.id]: panelState,
},
isEmptyState: false,
} as Partial<TContainerInput>);
return await this.untilEmbeddableLoaded<TEmbeddable>(panelState.explicitInput.id);

View file

@ -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.
*/

View file

@ -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);

View file

@ -17,6 +17,10 @@ export function doesInheritTimeRange(embeddable: Embeddable<TimeRangeInput>) {
// 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;

View file

@ -12741,4 +12741,4 @@
"xpack.licensing.welcomeBanner.licenseIsExpiredDescription.updateYourLicenseLinkText": "ライセンスを更新",
"xpack.licensing.welcomeBanner.licenseIsExpiredTitle": "ご使用の {licenseType} ライセンスは期限切れです"
}
}
}