mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Dashboard] Redesign empty screen in readonly mode (#54073)
* [Dashboard] Make empty screen nicer in readonly mode * Adding contact-the-owner part * Updating text
This commit is contained in:
parent
8ac233f303
commit
ab76ee5a58
5 changed files with 443 additions and 27 deletions
|
@ -1,5 +1,367 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`DashboardEmptyScreen renders correctly with readonly mode 1`] = `
|
||||
<DashboardEmptyScreen
|
||||
http={
|
||||
Object {
|
||||
"addLoadingCountSource": [MockFunction],
|
||||
"anonymousPaths": Object {
|
||||
"isAnonymous": [MockFunction],
|
||||
"register": [MockFunction],
|
||||
},
|
||||
"basePath": BasePath {
|
||||
"basePath": "",
|
||||
"get": [Function],
|
||||
"prepend": [Function],
|
||||
"remove": [Function],
|
||||
},
|
||||
"delete": [MockFunction],
|
||||
"fetch": [MockFunction],
|
||||
"get": [MockFunction],
|
||||
"getLoadingCount$": [MockFunction],
|
||||
"head": [MockFunction],
|
||||
"intercept": [MockFunction],
|
||||
"options": [MockFunction],
|
||||
"patch": [MockFunction],
|
||||
"post": [MockFunction],
|
||||
"put": [MockFunction],
|
||||
}
|
||||
}
|
||||
intl={
|
||||
Object {
|
||||
"defaultFormats": Object {},
|
||||
"defaultLocale": "en",
|
||||
"formatDate": [Function],
|
||||
"formatHTMLMessage": [Function],
|
||||
"formatMessage": [Function],
|
||||
"formatNumber": [Function],
|
||||
"formatPlural": [Function],
|
||||
"formatRelative": [Function],
|
||||
"formatTime": [Function],
|
||||
"formats": Object {
|
||||
"date": Object {
|
||||
"full": Object {
|
||||
"day": "numeric",
|
||||
"month": "long",
|
||||
"weekday": "long",
|
||||
"year": "numeric",
|
||||
},
|
||||
"long": Object {
|
||||
"day": "numeric",
|
||||
"month": "long",
|
||||
"year": "numeric",
|
||||
},
|
||||
"medium": Object {
|
||||
"day": "numeric",
|
||||
"month": "short",
|
||||
"year": "numeric",
|
||||
},
|
||||
"short": Object {
|
||||
"day": "numeric",
|
||||
"month": "numeric",
|
||||
"year": "2-digit",
|
||||
},
|
||||
},
|
||||
"number": Object {
|
||||
"currency": Object {
|
||||
"style": "currency",
|
||||
},
|
||||
"percent": Object {
|
||||
"style": "percent",
|
||||
},
|
||||
},
|
||||
"relative": Object {
|
||||
"days": Object {
|
||||
"units": "day",
|
||||
},
|
||||
"hours": Object {
|
||||
"units": "hour",
|
||||
},
|
||||
"minutes": Object {
|
||||
"units": "minute",
|
||||
},
|
||||
"months": Object {
|
||||
"units": "month",
|
||||
},
|
||||
"seconds": Object {
|
||||
"units": "second",
|
||||
},
|
||||
"years": Object {
|
||||
"units": "year",
|
||||
},
|
||||
},
|
||||
"time": Object {
|
||||
"full": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
"timeZoneName": "short",
|
||||
},
|
||||
"long": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
"timeZoneName": "short",
|
||||
},
|
||||
"medium": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
},
|
||||
"short": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
},
|
||||
},
|
||||
},
|
||||
"formatters": Object {
|
||||
"getDateTimeFormat": [Function],
|
||||
"getMessageFormat": [Function],
|
||||
"getNumberFormat": [Function],
|
||||
"getPluralFormat": [Function],
|
||||
"getRelativeFormat": [Function],
|
||||
},
|
||||
"locale": "en",
|
||||
"messages": Object {},
|
||||
"now": [Function],
|
||||
"onError": [Function],
|
||||
"textComponent": Symbol(react.fragment),
|
||||
"timeZone": null,
|
||||
}
|
||||
}
|
||||
isReadonlyMode={true}
|
||||
onLinkClick={[MockFunction]}
|
||||
showLinkToVisualize={true}
|
||||
uiSettings={
|
||||
Object {
|
||||
"get": [MockFunction] {
|
||||
"calls": Array [
|
||||
Array [
|
||||
"theme:darkMode",
|
||||
],
|
||||
Array [
|
||||
"theme:darkMode",
|
||||
],
|
||||
Array [
|
||||
"theme:darkMode",
|
||||
],
|
||||
Array [
|
||||
"theme:darkMode",
|
||||
],
|
||||
],
|
||||
"results": Array [
|
||||
Object {
|
||||
"type": "return",
|
||||
"value": undefined,
|
||||
},
|
||||
Object {
|
||||
"type": "return",
|
||||
"value": undefined,
|
||||
},
|
||||
Object {
|
||||
"type": "return",
|
||||
"value": undefined,
|
||||
},
|
||||
Object {
|
||||
"type": "return",
|
||||
"value": undefined,
|
||||
},
|
||||
],
|
||||
},
|
||||
"get$": [MockFunction],
|
||||
"getAll": [MockFunction],
|
||||
"getSaved$": [MockFunction],
|
||||
"getUpdate$": [MockFunction],
|
||||
"getUpdateErrors$": [MockFunction],
|
||||
"isCustom": [MockFunction],
|
||||
"isDeclared": [MockFunction],
|
||||
"isDefault": [MockFunction],
|
||||
"isOverridden": [MockFunction],
|
||||
"overrideLocalDefault": [MockFunction],
|
||||
"remove": [MockFunction],
|
||||
"set": [MockFunction],
|
||||
}
|
||||
}
|
||||
>
|
||||
<I18nProvider>
|
||||
<IntlProvider
|
||||
defaultLocale="en"
|
||||
formats={
|
||||
Object {
|
||||
"date": Object {
|
||||
"full": Object {
|
||||
"day": "numeric",
|
||||
"month": "long",
|
||||
"weekday": "long",
|
||||
"year": "numeric",
|
||||
},
|
||||
"long": Object {
|
||||
"day": "numeric",
|
||||
"month": "long",
|
||||
"year": "numeric",
|
||||
},
|
||||
"medium": Object {
|
||||
"day": "numeric",
|
||||
"month": "short",
|
||||
"year": "numeric",
|
||||
},
|
||||
"short": Object {
|
||||
"day": "numeric",
|
||||
"month": "numeric",
|
||||
"year": "2-digit",
|
||||
},
|
||||
},
|
||||
"number": Object {
|
||||
"currency": Object {
|
||||
"style": "currency",
|
||||
},
|
||||
"percent": Object {
|
||||
"style": "percent",
|
||||
},
|
||||
},
|
||||
"relative": Object {
|
||||
"days": Object {
|
||||
"units": "day",
|
||||
},
|
||||
"hours": Object {
|
||||
"units": "hour",
|
||||
},
|
||||
"minutes": Object {
|
||||
"units": "minute",
|
||||
},
|
||||
"months": Object {
|
||||
"units": "month",
|
||||
},
|
||||
"seconds": Object {
|
||||
"units": "second",
|
||||
},
|
||||
"years": Object {
|
||||
"units": "year",
|
||||
},
|
||||
},
|
||||
"time": Object {
|
||||
"full": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
"timeZoneName": "short",
|
||||
},
|
||||
"long": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
"timeZoneName": "short",
|
||||
},
|
||||
"medium": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
"second": "numeric",
|
||||
},
|
||||
"short": Object {
|
||||
"hour": "numeric",
|
||||
"minute": "numeric",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
locale="en"
|
||||
messages={Object {}}
|
||||
textComponent={Symbol(react.fragment)}
|
||||
>
|
||||
<PseudoLocaleWrapper>
|
||||
<EuiPage
|
||||
className="dshStartScreen"
|
||||
restrictWidth="500px"
|
||||
>
|
||||
<div
|
||||
className="euiPage euiPage--restrictWidth-custom dshStartScreen"
|
||||
style={
|
||||
Object {
|
||||
"maxWidth": "500px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<EuiPageBody>
|
||||
<main
|
||||
className="euiPageBody"
|
||||
>
|
||||
<EuiPageContent
|
||||
className="dshStartScreen__pageContent"
|
||||
horizontalPosition="center"
|
||||
paddingSize="none"
|
||||
verticalPosition="center"
|
||||
>
|
||||
<EuiPanel
|
||||
className="euiPageContent euiPageContent--verticalCenter euiPageContent--horizontalCenter dshStartScreen__pageContent"
|
||||
paddingSize="none"
|
||||
>
|
||||
<div
|
||||
className="euiPanel euiPageContent euiPageContent--verticalCenter euiPageContent--horizontalCenter dshStartScreen__pageContent"
|
||||
>
|
||||
<EuiImage
|
||||
alt=""
|
||||
url="/plugins/kibana/home/assets/welcome_graphic_light_2x.png"
|
||||
>
|
||||
<figure
|
||||
className="euiImage"
|
||||
role="figure"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
className="euiImage__img"
|
||||
src="/plugins/kibana/home/assets/welcome_graphic_light_2x.png"
|
||||
/>
|
||||
</figure>
|
||||
</EuiImage>
|
||||
<EuiText
|
||||
size="m"
|
||||
>
|
||||
<div
|
||||
className="euiText euiText--medium"
|
||||
>
|
||||
<p
|
||||
style={
|
||||
Object {
|
||||
"fontWeight": "bold",
|
||||
}
|
||||
}
|
||||
>
|
||||
This dashboard is empty.
|
||||
</p>
|
||||
</div>
|
||||
</EuiText>
|
||||
<EuiText
|
||||
color="subdued"
|
||||
size="m"
|
||||
>
|
||||
<div
|
||||
className="euiText euiText--medium"
|
||||
>
|
||||
<EuiTextColor
|
||||
color="subdued"
|
||||
component="div"
|
||||
>
|
||||
<div
|
||||
className="euiTextColor euiTextColor--subdued"
|
||||
>
|
||||
You need additional privileges to edit this dashboard.
|
||||
</div>
|
||||
</EuiTextColor>
|
||||
</div>
|
||||
</EuiText>
|
||||
</div>
|
||||
</EuiPanel>
|
||||
</EuiPageContent>
|
||||
</main>
|
||||
</EuiPageBody>
|
||||
</div>
|
||||
</EuiPage>
|
||||
</PseudoLocaleWrapper>
|
||||
</IntlProvider>
|
||||
</I18nProvider>
|
||||
</DashboardEmptyScreen>
|
||||
`;
|
||||
|
||||
exports[`DashboardEmptyScreen renders correctly with visualize paragraph 1`] = `
|
||||
<DashboardEmptyScreen
|
||||
http={
|
||||
|
|
|
@ -38,8 +38,7 @@ describe('DashboardEmptyScreen', () => {
|
|||
|
||||
function mountComponent(props?: DashboardEmptyScreenProps) {
|
||||
const compProps = props || defaultProps;
|
||||
const comp = mountWithIntl(<DashboardEmptyScreen {...compProps} />);
|
||||
return comp;
|
||||
return mountWithIntl(<DashboardEmptyScreen {...compProps} />);
|
||||
}
|
||||
|
||||
test('renders correctly with visualize paragraph', () => {
|
||||
|
@ -52,8 +51,10 @@ describe('DashboardEmptyScreen', () => {
|
|||
test('renders correctly without visualize paragraph', () => {
|
||||
const component = mountComponent({ ...defaultProps, ...{ showLinkToVisualize: false } });
|
||||
expect(component).toMatchSnapshot();
|
||||
const paragraph = findTestSubject(component, 'linkToVisualizeParagraph');
|
||||
expect(paragraph.length).toBe(0);
|
||||
const linkToVisualizeParagraph = findTestSubject(component, 'linkToVisualizeParagraph');
|
||||
expect(linkToVisualizeParagraph.length).toBe(0);
|
||||
const enterEditModeParagraph = component.find('.dshStartScreen__panelDesc');
|
||||
expect(enterEditModeParagraph.length).toBe(1);
|
||||
});
|
||||
|
||||
test('when specified, prop onVisualizeClick is called correctly', () => {
|
||||
|
@ -66,4 +67,11 @@ describe('DashboardEmptyScreen', () => {
|
|||
button.simulate('click');
|
||||
expect(onVisualizeClick).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('renders correctly with readonly mode', () => {
|
||||
const component = mountComponent({ ...defaultProps, ...{ isReadonlyMode: true } });
|
||||
expect(component).toMatchSnapshot();
|
||||
const paragraph = component.find('.dshStartScreen__panelDesc');
|
||||
expect(paragraph.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -161,6 +161,12 @@ export class DashboardAppController {
|
|||
dashboardStateManager.getIsViewMode() &&
|
||||
!dashboardConfig.getHideWriteControls();
|
||||
|
||||
const getIsEmptyInReadonlyMode = () =>
|
||||
!dashboardStateManager.getPanels().length &&
|
||||
!getShouldShowEditHelp() &&
|
||||
!getShouldShowViewHelp() &&
|
||||
dashboardConfig.getHideWriteControls();
|
||||
|
||||
const addVisualization = () => {
|
||||
navActions[TopNavIds.VISUALIZE]();
|
||||
};
|
||||
|
@ -193,7 +199,10 @@ export class DashboardAppController {
|
|||
}
|
||||
};
|
||||
|
||||
const getEmptyScreenProps = (shouldShowEditHelp: boolean): DashboardEmptyScreenProps => {
|
||||
const getEmptyScreenProps = (
|
||||
shouldShowEditHelp: boolean,
|
||||
isEmptyInReadOnlyMode: boolean
|
||||
): DashboardEmptyScreenProps => {
|
||||
const emptyScreenProps: DashboardEmptyScreenProps = {
|
||||
onLinkClick: shouldShowEditHelp ? $scope.showAddPanel : $scope.enterEditMode,
|
||||
showLinkToVisualize: shouldShowEditHelp,
|
||||
|
@ -203,6 +212,9 @@ export class DashboardAppController {
|
|||
if (shouldShowEditHelp) {
|
||||
emptyScreenProps.onVisualizeClick = addVisualization;
|
||||
}
|
||||
if (isEmptyInReadOnlyMode) {
|
||||
emptyScreenProps.isReadonlyMode = true;
|
||||
}
|
||||
return emptyScreenProps;
|
||||
};
|
||||
|
||||
|
@ -219,6 +231,7 @@ export class DashboardAppController {
|
|||
}
|
||||
const shouldShowEditHelp = getShouldShowEditHelp();
|
||||
const shouldShowViewHelp = getShouldShowViewHelp();
|
||||
const isEmptyInReadonlyMode = getIsEmptyInReadonlyMode();
|
||||
return {
|
||||
id: dashboardStateManager.savedDashboard.id || '',
|
||||
filters: queryFilter.getFilters(),
|
||||
|
@ -231,7 +244,7 @@ export class DashboardAppController {
|
|||
viewMode: dashboardStateManager.getViewMode(),
|
||||
panels: embeddablesMap,
|
||||
isFullScreenMode: dashboardStateManager.getFullScreenMode(),
|
||||
isEmptyState: shouldShowEditHelp || shouldShowViewHelp,
|
||||
isEmptyState: shouldShowEditHelp || shouldShowViewHelp || isEmptyInReadonlyMode,
|
||||
useMargins: dashboardStateManager.getUseMargins(),
|
||||
lastReloadRequestTime,
|
||||
title: dashboardStateManager.getTitle(),
|
||||
|
@ -275,9 +288,12 @@ export class DashboardAppController {
|
|||
dashboardContainer.renderEmpty = () => {
|
||||
const shouldShowEditHelp = getShouldShowEditHelp();
|
||||
const shouldShowViewHelp = getShouldShowViewHelp();
|
||||
const isEmptyState = shouldShowEditHelp || shouldShowViewHelp;
|
||||
const isEmptyInReadOnlyMode = getIsEmptyInReadonlyMode();
|
||||
const isEmptyState = shouldShowEditHelp || shouldShowViewHelp || isEmptyInReadOnlyMode;
|
||||
return isEmptyState ? (
|
||||
<DashboardEmptyScreen {...getEmptyScreenProps(shouldShowEditHelp)} />
|
||||
<DashboardEmptyScreen
|
||||
{...getEmptyScreenProps(shouldShowEditHelp, isEmptyInReadOnlyMode)}
|
||||
/>
|
||||
) : null;
|
||||
};
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ export interface DashboardEmptyScreenProps {
|
|||
onVisualizeClick?: () => void;
|
||||
uiSettings: IUiSettingsClient;
|
||||
http: HttpStart;
|
||||
isReadonlyMode?: boolean;
|
||||
}
|
||||
|
||||
export function DashboardEmptyScreen({
|
||||
|
@ -45,6 +46,7 @@ export function DashboardEmptyScreen({
|
|||
onVisualizeClick,
|
||||
uiSettings,
|
||||
http,
|
||||
isReadonlyMode,
|
||||
}: DashboardEmptyScreenProps) {
|
||||
const IS_DARK_THEME = uiSettings.get('theme:darkMode');
|
||||
const emptyStateGraphicURL = IS_DARK_THEME
|
||||
|
@ -98,25 +100,42 @@ export function DashboardEmptyScreen({
|
|||
constants.addExistingVisualizationLinkText,
|
||||
constants.addExistingVisualizationLinkAriaLabel
|
||||
);
|
||||
const viewMode = (
|
||||
<EuiPage className="dshStartScreen" restrictWidth="500px">
|
||||
<EuiPageBody>
|
||||
<EuiPageContent
|
||||
verticalPosition="center"
|
||||
horizontalPosition="center"
|
||||
paddingSize="none"
|
||||
className="dshStartScreen__pageContent"
|
||||
>
|
||||
<EuiImage url={http.basePath.prepend(emptyStateGraphicURL)} alt="" />
|
||||
<EuiText size="m">
|
||||
<p style={{ fontWeight: 'bold' }}>{constants.fillDashboardTitle}</p>
|
||||
</EuiText>
|
||||
<EuiSpacer size="m" />
|
||||
<div className="dshStartScreen__panelDesc">{enterEditModeParagraph}</div>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
const page = (mainText: string, showAdditionalParagraph?: boolean, additionalText?: string) => {
|
||||
return (
|
||||
<EuiPage className="dshStartScreen" restrictWidth="500px">
|
||||
<EuiPageBody>
|
||||
<EuiPageContent
|
||||
verticalPosition="center"
|
||||
horizontalPosition="center"
|
||||
paddingSize="none"
|
||||
className="dshStartScreen__pageContent"
|
||||
>
|
||||
<EuiImage url={http.basePath.prepend(emptyStateGraphicURL)} alt="" />
|
||||
<EuiText size="m">
|
||||
<p style={{ fontWeight: 'bold' }}>{mainText}</p>
|
||||
</EuiText>
|
||||
{additionalText ? (
|
||||
<EuiText size="m" color="subdued">
|
||||
{additionalText}
|
||||
</EuiText>
|
||||
) : null}
|
||||
{showAdditionalParagraph ? (
|
||||
<React.Fragment>
|
||||
<EuiSpacer size="m" />
|
||||
<div className="dshStartScreen__panelDesc">{enterEditModeParagraph}</div>
|
||||
</React.Fragment>
|
||||
) : null}
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
);
|
||||
};
|
||||
const readonlyMode = page(
|
||||
constants.emptyDashboardTitle,
|
||||
false,
|
||||
constants.emptyDashboardAdditionalPrivilege
|
||||
);
|
||||
const viewMode = page(constants.fillDashboardTitle, true);
|
||||
const editMode = (
|
||||
<div data-test-subj="emptyDashboardWidget" className="dshEmptyWidget">
|
||||
{enterViewModeParagraph}
|
||||
|
@ -124,5 +143,6 @@ export function DashboardEmptyScreen({
|
|||
{linkToVisualizeParagraph}
|
||||
</div>
|
||||
);
|
||||
return <I18nProvider>{showLinkToVisualize ? editMode : viewMode}</I18nProvider>;
|
||||
const actionableMode = showLinkToVisualize ? editMode : viewMode;
|
||||
return <I18nProvider>{isReadonlyMode ? readonlyMode : actionableMode}</I18nProvider>;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,16 @@
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
/** READONLY VIEW CONSTANTS **/
|
||||
export const emptyDashboardTitle: string = i18n.translate('kbn.dashboard.emptyDashboardTitle', {
|
||||
defaultMessage: 'This dashboard is empty.',
|
||||
});
|
||||
export const emptyDashboardAdditionalPrivilege = i18n.translate(
|
||||
'kbn.dashboard.emptyDashboardAdditionalPrivilege',
|
||||
{
|
||||
defaultMessage: 'You need additional privileges to edit this dashboard.',
|
||||
}
|
||||
);
|
||||
/** VIEW MODE CONSTANTS **/
|
||||
export const fillDashboardTitle: string = i18n.translate('kbn.dashboard.fillDashboardTitle', {
|
||||
defaultMessage: 'This dashboard is empty. Let\u2019s fill it up!',
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue