mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
* 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:
parent
0ce02453ea
commit
580bd82835
18 changed files with 461 additions and 217 deletions
|
@ -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. Let’s 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. Let’s 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. Let’s 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. Let’s 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>
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,9 +6,5 @@
|
|||
|
||||
.dshStartScreen {
|
||||
text-align: center;
|
||||
padding: $euiSize;
|
||||
|
||||
> * {
|
||||
max-width: 36em !important;
|
||||
}
|
||||
padding: $euiSizeS;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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' }],
|
||||
]);
|
||||
});
|
|
@ -26,4 +26,5 @@ export const TopNavIds = {
|
|||
ENTER_EDIT_MODE: 'enterEditMode',
|
||||
CLONE: 'clone',
|
||||
FULL_SCREEN: 'fullScreenMode',
|
||||
VISUALIZE: 'visualize',
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
.dshLayout-isMaximizedPanel {
|
||||
height: 100% !important; /* 1. */
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
position: absolute !important;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -12741,4 +12741,4 @@
|
|||
"xpack.licensing.welcomeBanner.licenseIsExpiredDescription.updateYourLicenseLinkText": "ライセンスを更新",
|
||||
"xpack.licensing.welcomeBanner.licenseIsExpiredTitle": "ご使用の {licenseType} ライセンスは期限切れです"
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue