mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Shared UX] Move No Data Cards to packages (#134503)
* [Shared UX] Move No Data Cards to packages * Consolidate cards * Fix some Regex mistakes * Adjust Storybook a bit * Adjust tests and context arrangement * Fix bugs; kill bad snapshots; improve tests
This commit is contained in:
parent
02bc0e97bb
commit
e1eb3db916
52 changed files with 1341 additions and 978 deletions
|
@ -210,6 +210,7 @@
|
|||
"@kbn/shared-ux-avatar-solution": "link:bazel-bin/packages/shared-ux/avatar/solution",
|
||||
"@kbn/shared-ux-button-exit-full-screen": "link:bazel-bin/packages/shared-ux/button/exit_full_screen",
|
||||
"@kbn/shared-ux-button-toolbar": "link:bazel-bin/packages/shared-ux/button_toolbar",
|
||||
"@kbn/shared-ux-card-no-data": "link:bazel-bin/packages/shared-ux/card/no_data",
|
||||
"@kbn/shared-ux-components": "link:bazel-bin/packages/kbn-shared-ux-components",
|
||||
"@kbn/shared-ux-link-redirect-app": "link:bazel-bin/packages/shared-ux/link/redirect_app",
|
||||
"@kbn/shared-ux-page-analytics-no-data": "link:bazel-bin/packages/shared-ux/page/analytics_no_data",
|
||||
|
@ -750,6 +751,7 @@
|
|||
"@types/kbn__shared-ux-avatar-solution": "link:bazel-bin/packages/shared-ux/avatar/solution/npm_module_types",
|
||||
"@types/kbn__shared-ux-button-exit-full-screen": "link:bazel-bin/packages/shared-ux/button/exit_full_screen/npm_module_types",
|
||||
"@types/kbn__shared-ux-button-toolbar": "link:bazel-bin/packages/shared-ux/button_toolbar/npm_module_types",
|
||||
"@types/kbn__shared-ux-card-no-data": "link:bazel-bin/packages/shared-ux/card/no_data/npm_module_types",
|
||||
"@types/kbn__shared-ux-components": "link:bazel-bin/packages/kbn-shared-ux-components/npm_module_types",
|
||||
"@types/kbn__shared-ux-link-redirect-app": "link:bazel-bin/packages/shared-ux/link/redirect_app/npm_module_types",
|
||||
"@types/kbn__shared-ux-page-analytics-no-data": "link:bazel-bin/packages/shared-ux/page/analytics_no_data/npm_module_types",
|
||||
|
|
|
@ -146,6 +146,7 @@ filegroup(
|
|||
"//packages/shared-ux/avatar/solution:build",
|
||||
"//packages/shared-ux/button_toolbar:build",
|
||||
"//packages/shared-ux/button/exit_full_screen:build",
|
||||
"//packages/shared-ux/card/no_data:build",
|
||||
"//packages/shared-ux/link/redirect_app:build",
|
||||
"//packages/shared-ux/page/analytics_no_data:build",
|
||||
"//packages/shared-ux/page/kibana_no_data:build",
|
||||
|
@ -279,6 +280,7 @@ filegroup(
|
|||
"//packages/shared-ux/avatar/solution:build_types",
|
||||
"//packages/shared-ux/button_toolbar:build_types",
|
||||
"//packages/shared-ux/button/exit_full_screen:build_types",
|
||||
"//packages/shared-ux/card/no_data:build_types",
|
||||
"//packages/shared-ux/link/redirect_app:build_types",
|
||||
"//packages/shared-ux/page/analytics_no_data:build_types",
|
||||
"//packages/shared-ux/page/kibana_no_data:build_types",
|
||||
|
|
|
@ -45,6 +45,7 @@ RUNTIME_DEPS = [
|
|||
"//packages/shared-ux/avatar/solution",
|
||||
"//packages/shared-ux/link/redirect_app",
|
||||
"//packages/shared-ux/prompt/no_data_views",
|
||||
"//packages/shared-ux/card/no_data",
|
||||
"//packages/kbn-shared-ux-services",
|
||||
"//packages/kbn-shared-ux-storybook",
|
||||
"//packages/kbn-shared-ux-utility",
|
||||
|
@ -74,6 +75,7 @@ TYPES_DEPS = [
|
|||
"//packages/shared-ux/avatar/solution:npm_module_types",
|
||||
"//packages/shared-ux/link/redirect_app:npm_module_types",
|
||||
"//packages/shared-ux/prompt/no_data_views:npm_module_types",
|
||||
"//packages/shared-ux/card/no_data:npm_module_types",
|
||||
"//packages/kbn-shared-ux-services:npm_module_types",
|
||||
"//packages/kbn-shared-ux-storybook:npm_module_types",
|
||||
"//packages/kbn-shared-ux-utility:npm_module_types",
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
export { NoDataCard, ElasticAgentCard, NoDataPage, NoDataConfigPage } from './no_data_page';
|
||||
export { NoDataPage, NoDataConfigPage } from './no_data_page';
|
||||
export { KibanaPageTemplate } from './page_template';
|
||||
export type { KibanaPageTemplateProps } from './types';
|
||||
export type { NoDataPageProps } from './no_data_page';
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`NoDataPage render 1`] = `
|
||||
<div
|
||||
className="kbnNoDataPageContents"
|
||||
data-test-subj="kbnNoDataPage"
|
||||
>
|
||||
<EuiText
|
||||
textAlign="center"
|
||||
>
|
||||
<KibanaSolutionAvatar
|
||||
iconType="logoKibana"
|
||||
name="Analytics"
|
||||
size="xxl"
|
||||
/>
|
||||
<EuiSpacer
|
||||
size="l"
|
||||
/>
|
||||
<h1>
|
||||
Welcome to Elastic Analytics!
|
||||
</h1>
|
||||
<EuiTextColor
|
||||
color="subdued"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="Add your data to get started, or {link} about {solution}."
|
||||
id="sharedUXComponents.noDataPage.intro"
|
||||
values={
|
||||
Object {
|
||||
"link": <EuiLink
|
||||
href="test"
|
||||
target="_blank"
|
||||
>
|
||||
<FormattedMessage
|
||||
defaultMessage="learn more"
|
||||
id="sharedUXComponents.noDataPage.intro.link"
|
||||
values={Object {}}
|
||||
/>
|
||||
</EuiLink>,
|
||||
"solution": "Analytics",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
</EuiTextColor>
|
||||
</EuiText>
|
||||
<EuiSpacer
|
||||
size="xxl"
|
||||
/>
|
||||
<ElasticAgentCard
|
||||
key="empty-page-agent-action"
|
||||
/>
|
||||
</div>
|
||||
`;
|
|
@ -6,7 +6,6 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { NoDataCard, ElasticAgentCard } from './no_data_card';
|
||||
export { NoDataPage } from './no_data_page';
|
||||
export type { NoDataPageProps } from './types';
|
||||
export { NoDataConfigPage, NoDataConfigPageWithSolutionNavBar } from './no_data_config_page';
|
||||
|
|
|
@ -1,104 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ElasticAgentCardComponent props button 1`] = `
|
||||
<NoDataCard
|
||||
button="Button"
|
||||
description="Use Elastic Agent for a simple, unified way to collect data from your machines."
|
||||
image={
|
||||
<EuiImage
|
||||
alt=""
|
||||
size="fullWidth"
|
||||
style={
|
||||
Object {
|
||||
"background": "aliceblue",
|
||||
"height": 240,
|
||||
"objectFit": "cover",
|
||||
"width": "max(100%, 360px)",
|
||||
}
|
||||
}
|
||||
url="test-file-stub"
|
||||
/>
|
||||
}
|
||||
title="Add Elastic Agent"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`ElasticAgentCardComponent props href 1`] = `
|
||||
<NoDataCard
|
||||
description="Use Elastic Agent for a simple, unified way to collect data from your machines."
|
||||
href="some path"
|
||||
image={
|
||||
<EuiImage
|
||||
alt=""
|
||||
size="fullWidth"
|
||||
style={
|
||||
Object {
|
||||
"background": "aliceblue",
|
||||
"height": 240,
|
||||
"objectFit": "cover",
|
||||
"width": "max(100%, 360px)",
|
||||
}
|
||||
}
|
||||
url="test-file-stub"
|
||||
/>
|
||||
}
|
||||
title="Add Elastic Agent"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`ElasticAgentCardComponent renders 1`] = `
|
||||
<NoDataCard
|
||||
description="Use Elastic Agent for a simple, unified way to collect data from your machines."
|
||||
image={
|
||||
<EuiImage
|
||||
alt=""
|
||||
size="fullWidth"
|
||||
style={
|
||||
Object {
|
||||
"background": "aliceblue",
|
||||
"height": 240,
|
||||
"objectFit": "cover",
|
||||
"width": "max(100%, 360px)",
|
||||
}
|
||||
}
|
||||
url="test-file-stub"
|
||||
/>
|
||||
}
|
||||
title="Add Elastic Agent"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`ElasticAgentCardComponent renders with canAccessFleet false 1`] = `
|
||||
<NoDataCard
|
||||
description={
|
||||
<EuiTextColor
|
||||
color="default"
|
||||
>
|
||||
This integration is not yet enabled. Your administrator has the required permissions to turn it on.
|
||||
</EuiTextColor>
|
||||
}
|
||||
image={
|
||||
<EuiImage
|
||||
alt=""
|
||||
size="fullWidth"
|
||||
style={
|
||||
Object {
|
||||
"background": "aliceblue",
|
||||
"height": 240,
|
||||
"objectFit": "cover",
|
||||
"width": "max(100%, 360px)",
|
||||
}
|
||||
}
|
||||
url="test-file-stub"
|
||||
/>
|
||||
}
|
||||
isDisabled={true}
|
||||
title={
|
||||
<EuiTextColor
|
||||
color="default"
|
||||
>
|
||||
Contact your administrator
|
||||
</EuiTextColor>
|
||||
}
|
||||
/>
|
||||
`;
|
|
@ -1,75 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ElasticAgentCard renders 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="euiPanel euiPanel--paddingLarge euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--isClickable euiCard euiCard--centerAligned euiCard--isClickable css-1hu4pg0-EuiCard"
|
||||
>
|
||||
<div
|
||||
class="euiCard__top"
|
||||
>
|
||||
<div
|
||||
class="euiCard__image"
|
||||
>
|
||||
<figure
|
||||
class="euiImage euiImage--fullWidth"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
class="euiImage__img"
|
||||
src="test-file-stub"
|
||||
style="height: 240px; object-fit: cover; background: aliceblue;"
|
||||
/>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiCard__content"
|
||||
>
|
||||
<span
|
||||
class="euiTitle euiCard__title css-1mr6cet-euiTitle-s"
|
||||
id="generated-idTitle"
|
||||
>
|
||||
<a
|
||||
aria-describedby="generated-idDescription"
|
||||
class="euiCard__titleAnchor"
|
||||
href="/app/integrations/browse"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<span
|
||||
class="euiScreenReaderOnly"
|
||||
>
|
||||
Add Elastic Agent
|
||||
</span>
|
||||
</a>
|
||||
</span>
|
||||
<div
|
||||
class="euiText euiText--small euiCard__description"
|
||||
id="generated-idDescription"
|
||||
>
|
||||
<p>
|
||||
Use Elastic Agent for a simple, unified way to collect data from your machines.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiCard__footer"
|
||||
>
|
||||
<button
|
||||
class="euiButton euiButton--primary euiButton--fill"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="euiButtonContent euiButton__content"
|
||||
>
|
||||
<span
|
||||
class="euiButton__text"
|
||||
>
|
||||
Add Elastic Agent
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -1,231 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`NoDataCard props button 1`] = `
|
||||
<div
|
||||
class="euiPanel euiPanel--paddingLarge euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiCard euiCard--centerAligned css-1hu4pg0-EuiCard"
|
||||
>
|
||||
<div
|
||||
class="euiCard__content"
|
||||
>
|
||||
<span
|
||||
class="euiTitle euiCard__title css-1mr6cet-euiTitle-s"
|
||||
id="generated-idTitle"
|
||||
>
|
||||
<span
|
||||
class="euiScreenReaderOnly"
|
||||
>
|
||||
Card title
|
||||
</span>
|
||||
</span>
|
||||
<div
|
||||
class="euiText euiText--small euiCard__description"
|
||||
id="generated-idDescription"
|
||||
>
|
||||
<p>
|
||||
Description
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiCard__footer"
|
||||
>
|
||||
<button
|
||||
class="euiButton euiButton--primary euiButton--fill"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="euiButtonContent euiButton__content"
|
||||
>
|
||||
<span
|
||||
class="euiButton__text"
|
||||
>
|
||||
Button
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`NoDataCard props extends EuiCardProps 1`] = `
|
||||
<div
|
||||
class="euiPanel euiPanel--paddingLarge euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiCard euiCard--centerAligned custom_class css-1hu4pg0-EuiCard"
|
||||
>
|
||||
<div
|
||||
class="euiCard__content"
|
||||
>
|
||||
<span
|
||||
class="euiTitle euiCard__title css-1mr6cet-euiTitle-s"
|
||||
id="generated-idTitle"
|
||||
>
|
||||
<span
|
||||
class="euiScreenReaderOnly"
|
||||
>
|
||||
Card title
|
||||
</span>
|
||||
</span>
|
||||
<div
|
||||
class="euiText euiText--small euiCard__description"
|
||||
id="generated-idDescription"
|
||||
>
|
||||
<p>
|
||||
Description
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiCard__footer"
|
||||
>
|
||||
<button
|
||||
class="euiButton euiButton--primary euiButton--fill"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="euiButtonContent euiButton__content"
|
||||
>
|
||||
<span
|
||||
class="euiButton__text"
|
||||
>
|
||||
Button
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`NoDataCard props href 1`] = `
|
||||
<div
|
||||
class="euiPanel euiPanel--paddingLarge euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--isClickable euiCard euiCard--centerAligned euiCard--isClickable css-1hu4pg0-EuiCard"
|
||||
>
|
||||
<div
|
||||
class="euiCard__content"
|
||||
>
|
||||
<span
|
||||
class="euiTitle euiCard__title css-1mr6cet-euiTitle-s"
|
||||
id="generated-idTitle"
|
||||
>
|
||||
<a
|
||||
aria-describedby="generated-idDescription"
|
||||
class="euiCard__titleAnchor"
|
||||
href="#"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<span
|
||||
class="euiScreenReaderOnly"
|
||||
>
|
||||
Card title
|
||||
</span>
|
||||
</a>
|
||||
</span>
|
||||
<div
|
||||
class="euiText euiText--small euiCard__description"
|
||||
id="generated-idDescription"
|
||||
>
|
||||
<p>
|
||||
Description
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiCard__footer"
|
||||
>
|
||||
<button
|
||||
class="euiButton euiButton--primary euiButton--fill"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="euiButtonContent euiButton__content"
|
||||
>
|
||||
<span
|
||||
class="euiButton__text"
|
||||
>
|
||||
Button
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`NoDataCard props isDisabled 1`] = `
|
||||
<div
|
||||
class="euiPanel euiPanel--paddingLarge euiPanel--borderRadiusMedium euiPanel--subdued euiPanel--noShadow euiPanel--noBorder euiCard euiCard--centerAligned euiCard-isDisabled css-1hu4pg0-EuiCard"
|
||||
>
|
||||
<div
|
||||
class="euiCard__content"
|
||||
>
|
||||
<span
|
||||
class="euiTitle euiCard__title css-1mr6cet-euiTitle-s"
|
||||
id="generated-idTitle"
|
||||
>
|
||||
<button
|
||||
aria-describedby=" generated-idDescription"
|
||||
class="euiCard__titleButton"
|
||||
disabled=""
|
||||
>
|
||||
<span
|
||||
class="euiScreenReaderOnly"
|
||||
>
|
||||
Card title
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
<div
|
||||
class="euiText euiText--small euiCard__description"
|
||||
id="generated-idDescription"
|
||||
>
|
||||
<p>
|
||||
Description
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`NoDataCard renders 1`] = `
|
||||
<div
|
||||
class="euiPanel euiPanel--paddingLarge euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiCard euiCard--centerAligned css-1hu4pg0-EuiCard"
|
||||
>
|
||||
<div
|
||||
class="euiCard__content"
|
||||
>
|
||||
<span
|
||||
class="euiTitle euiCard__title css-1mr6cet-euiTitle-s"
|
||||
id="generated-idTitle"
|
||||
>
|
||||
<span
|
||||
class="euiScreenReaderOnly"
|
||||
>
|
||||
Card title
|
||||
</span>
|
||||
</span>
|
||||
<div
|
||||
class="euiText euiText--small euiCard__description"
|
||||
id="generated-idDescription"
|
||||
>
|
||||
<p>
|
||||
Description
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiCard__footer"
|
||||
>
|
||||
<button
|
||||
class="euiButton euiButton--primary euiButton--fill"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="euiButtonContent euiButton__content"
|
||||
>
|
||||
<span
|
||||
class="euiButton__text"
|
||||
>
|
||||
Card title
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import { ElasticAgentCardComponent } from './elastic_agent_card.component';
|
||||
import { NoDataCard } from './no_data_card';
|
||||
|
||||
describe('ElasticAgentCardComponent', () => {
|
||||
test('renders', () => {
|
||||
const component = shallow(<ElasticAgentCardComponent canAccessFleet={true} />);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('renders with canAccessFleet false', () => {
|
||||
const component = shallow(<ElasticAgentCardComponent canAccessFleet={false} />);
|
||||
expect(component.find(NoDataCard).props().isDisabled).toBe(true);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('props', () => {
|
||||
test('button', () => {
|
||||
const component = shallow(
|
||||
<ElasticAgentCardComponent button="Button" canAccessFleet={true} />
|
||||
);
|
||||
expect(component.find(NoDataCard).props().button).toBe('Button');
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('href', () => {
|
||||
const component = shallow(
|
||||
<ElasticAgentCardComponent canAccessFleet={true} href={'some path'} />
|
||||
);
|
||||
expect(component.find(NoDataCard).props().href).toBe('some path');
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,83 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiImage, EuiTextColor } from '@elastic/eui';
|
||||
import { ElasticAgentCardProps } from './types';
|
||||
import { NoDataCard } from './no_data_card';
|
||||
import ElasticAgentCardIllustration from './assets/elastic_agent_card.svg';
|
||||
|
||||
export type ElasticAgentCardComponentProps = ElasticAgentCardProps & {
|
||||
canAccessFleet: boolean;
|
||||
};
|
||||
|
||||
const noPermissionTitle = i18n.translate(
|
||||
'sharedUXComponents.noDataPage.elasticAgentCard.noPermission.title',
|
||||
{
|
||||
defaultMessage: `Contact your administrator`,
|
||||
}
|
||||
);
|
||||
|
||||
const noPermissionDescription = i18n.translate(
|
||||
'sharedUXComponents.noDataPage.elasticAgentCard.noPermission.description',
|
||||
{
|
||||
defaultMessage: `This integration is not yet enabled. Your administrator has the required permissions to turn it on.`,
|
||||
}
|
||||
);
|
||||
|
||||
const elasticAgentCardTitle = i18n.translate(
|
||||
'sharedUXComponents.noDataPage.elasticAgentCard.title',
|
||||
{
|
||||
defaultMessage: 'Add Elastic Agent',
|
||||
}
|
||||
);
|
||||
|
||||
const elasticAgentCardDescription = i18n.translate(
|
||||
'sharedUXComponents.noDataPage.elasticAgentCard.description',
|
||||
{
|
||||
defaultMessage: `Use Elastic Agent for a simple, unified way to collect data from your machines.`,
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Creates a specific NoDataCard pointing users to Integrations when `canAccessFleet`
|
||||
*/
|
||||
export const ElasticAgentCardComponent: FunctionComponent<ElasticAgentCardComponentProps> = ({
|
||||
canAccessFleet,
|
||||
title = elasticAgentCardTitle,
|
||||
description,
|
||||
...cardRest
|
||||
}) => {
|
||||
const props = canAccessFleet
|
||||
? {
|
||||
title,
|
||||
description: description || elasticAgentCardDescription,
|
||||
}
|
||||
: {
|
||||
title: <EuiTextColor color="default">{noPermissionTitle}</EuiTextColor>,
|
||||
description: <EuiTextColor color="default">{noPermissionDescription}</EuiTextColor>,
|
||||
isDisabled: true,
|
||||
};
|
||||
|
||||
const image = (
|
||||
<EuiImage
|
||||
size="fullWidth"
|
||||
style={{
|
||||
width: 'max(100%, 360px)',
|
||||
height: 240,
|
||||
objectFit: 'cover',
|
||||
background: 'aliceblue',
|
||||
}}
|
||||
url={ElasticAgentCardIllustration}
|
||||
alt=""
|
||||
/>
|
||||
);
|
||||
|
||||
return <NoDataCard image={image} {...props} {...cardRest} />;
|
||||
};
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
ElasticAgentCardComponent as Component,
|
||||
ElasticAgentCardComponentProps as ComponentProps,
|
||||
} from './elastic_agent_card.component';
|
||||
|
||||
import { ElasticAgentCard } from './elastic_agent_card';
|
||||
|
||||
export default {
|
||||
title: 'Page Template/No Data/Elastic Agent Data Card',
|
||||
description: 'A solution-specific wrapper around NoDataCard, to be used on NoData page',
|
||||
};
|
||||
|
||||
type Params = Pick<ComponentProps, 'canAccessFleet'>;
|
||||
|
||||
export const PureComponent = (params: Params) => {
|
||||
return <Component {...params} />;
|
||||
};
|
||||
|
||||
PureComponent.argTypes = {
|
||||
canAccessFleet: {
|
||||
control: 'boolean',
|
||||
defaultValue: true,
|
||||
},
|
||||
description: {
|
||||
control: 'text',
|
||||
defaultValue: '',
|
||||
},
|
||||
};
|
||||
|
||||
export const ConnectedComponent = () => {
|
||||
return <ElasticAgentCard href="#" />;
|
||||
};
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { ReactWrapper } from 'enzyme';
|
||||
import React from 'react';
|
||||
import { mountWithIntl } from '@kbn/test-jest-helpers';
|
||||
import {
|
||||
SharedUxServicesProvider,
|
||||
SharedUxServices,
|
||||
mockServicesFactory,
|
||||
} from '@kbn/shared-ux-services';
|
||||
|
||||
import { ElasticAgentCard } from './elastic_agent_card';
|
||||
import { ElasticAgentCardComponent } from './elastic_agent_card.component';
|
||||
|
||||
describe('ElasticAgentCard', () => {
|
||||
let services: SharedUxServices;
|
||||
let mount: (element: JSX.Element) => ReactWrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
services = mockServicesFactory();
|
||||
mount = (element: JSX.Element) =>
|
||||
mountWithIntl(<SharedUxServicesProvider {...services}>{element}</SharedUxServicesProvider>);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
test('renders', () => {
|
||||
const component = mount(<ElasticAgentCard />);
|
||||
expect(component.render()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('href', () => {
|
||||
test('returns href if href is given', () => {
|
||||
const component = mount(<ElasticAgentCard href={'/take/me/somewhere'} />);
|
||||
expect(component.find(ElasticAgentCardComponent).props().href).toBe('/take/me/somewhere');
|
||||
});
|
||||
|
||||
test('returns prefix + category if href is not given', () => {
|
||||
const component = mount(<ElasticAgentCard category={'solutions'} />);
|
||||
expect(component.find(ElasticAgentCardComponent).props().href).toBe(
|
||||
'/app/integrations/browse/solutions'
|
||||
);
|
||||
});
|
||||
|
||||
test('returns prefix if nor category nor href are given', () => {
|
||||
const component = mount(<ElasticAgentCard />);
|
||||
expect(component.find(ElasticAgentCardComponent).props().href).toBe(
|
||||
'/app/integrations/browse'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('description', () => {
|
||||
test('renders custom description if provided', () => {
|
||||
const component = mount(
|
||||
<ElasticAgentCard description="Build seamless search experiences faster." />
|
||||
);
|
||||
expect(component.find(ElasticAgentCardComponent).props().description).toBe(
|
||||
'Build seamless search experiences faster.'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('canAccessFleet', () => {
|
||||
test('passes in the right parameter', () => {
|
||||
const component = mount(<ElasticAgentCard />);
|
||||
expect(component.find(ElasticAgentCardComponent).props().canAccessFleet).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
import { useApplication, useHttp, usePermissions } from '@kbn/shared-ux-services';
|
||||
import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app';
|
||||
import useObservable from 'react-use/lib/useObservable';
|
||||
|
||||
import { ElasticAgentCardProps } from './types';
|
||||
import { ElasticAgentCardComponent } from './elastic_agent_card.component';
|
||||
|
||||
export const ElasticAgentCard = (props: ElasticAgentCardProps) => {
|
||||
const { canAccessFleet } = usePermissions();
|
||||
const { addBasePath } = useHttp();
|
||||
const { navigateToUrl, currentAppId$ } = useApplication();
|
||||
const currentAppId = useObservable(currentAppId$);
|
||||
|
||||
const { href: srcHref, category, description } = props;
|
||||
|
||||
const href = useMemo(() => {
|
||||
if (srcHref) {
|
||||
return srcHref;
|
||||
}
|
||||
|
||||
// TODO: get this URL from a locator
|
||||
const prefix = '/app/integrations/browse';
|
||||
|
||||
if (category) {
|
||||
return addBasePath(`${prefix}/${category}`);
|
||||
}
|
||||
|
||||
return addBasePath(prefix);
|
||||
}, [addBasePath, srcHref, category]);
|
||||
|
||||
return (
|
||||
<RedirectAppLinks {...{ currentAppId, navigateToUrl }}>
|
||||
<ElasticAgentCardComponent {...{ ...props, href, canAccessFleet, description }} />
|
||||
</RedirectAppLinks>
|
||||
);
|
||||
};
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { NoDataCard } from './no_data_card';
|
||||
import type { NoDataCardProps } from './types';
|
||||
|
||||
export default {
|
||||
title: 'Page Template/No Data/No Data Card',
|
||||
description: 'A wrapper around EuiCard, to be used on NoData page',
|
||||
};
|
||||
|
||||
type Params = Pick<NoDataCardProps, 'button' | 'description'>;
|
||||
|
||||
export const PureComponent = (params: Params) => {
|
||||
return <NoDataCard title={'Add data'} {...params} />;
|
||||
};
|
||||
|
||||
PureComponent.argTypes = {
|
||||
button: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
defaultValue: 'Button text',
|
||||
},
|
||||
description: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
defaultValue: 'This is a description',
|
||||
},
|
||||
};
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import { EuiButton, EuiCard, EuiScreenReaderOnly } from '@elastic/eui';
|
||||
|
||||
import type { NoDataCardProps } from './types';
|
||||
import { NoDataCardStyles } from './no_data_card.styles';
|
||||
|
||||
const defaultDescription = i18n.translate(
|
||||
'sharedUXComponents.pageTemplate.noDataCard.description',
|
||||
{
|
||||
defaultMessage: `Proceed without collecting data`,
|
||||
}
|
||||
);
|
||||
|
||||
export const NoDataCard: FunctionComponent<NoDataCardProps> = ({
|
||||
title: titleProp,
|
||||
button,
|
||||
description,
|
||||
isDisabled,
|
||||
...cardRest
|
||||
}) => {
|
||||
const styles = NoDataCardStyles();
|
||||
|
||||
const footer = () => {
|
||||
// Don't render the footer action if disabled
|
||||
if (isDisabled) {
|
||||
return;
|
||||
}
|
||||
// Render a custom footer action if the button is not a simple string
|
||||
if (button && typeof button !== 'string') {
|
||||
return button;
|
||||
}
|
||||
// Default footer action is a button with the provided or default string
|
||||
return <EuiButton fill>{button || titleProp}</EuiButton>;
|
||||
};
|
||||
|
||||
const cardDescription = description || defaultDescription;
|
||||
|
||||
// Fix the need for an a11y title even though the button exists by setting to screen reader only
|
||||
const title = titleProp ? (
|
||||
<EuiScreenReaderOnly>
|
||||
<span>{titleProp}</span>
|
||||
</EuiScreenReaderOnly>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<EuiCard
|
||||
css={styles}
|
||||
paddingSize="l"
|
||||
title={title!}
|
||||
description={cardDescription}
|
||||
footer={footer()}
|
||||
isDisabled={isDisabled}
|
||||
{...cardRest}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { EuiCardProps } from '@elastic/eui';
|
||||
import { MouseEventHandler, ReactNode } from 'react';
|
||||
|
||||
export type NoDataCardProps = Partial<Omit<EuiCardProps, 'layout'>> & {
|
||||
/**
|
||||
* Provide just a string for the button's label, or a whole component;
|
||||
* The button will be hidden completely if `isDisabled=true`
|
||||
*/
|
||||
button?: string | ReactNode;
|
||||
/**
|
||||
* Remapping `onClick` to any element
|
||||
*/
|
||||
onClick?: MouseEventHandler<HTMLElement>;
|
||||
/**
|
||||
* Description for the card;
|
||||
* If not provided, the default will be used
|
||||
*/
|
||||
description?: string | ReactNode;
|
||||
};
|
||||
|
||||
export type ElasticAgentCardProps = NoDataCardProps & {
|
||||
/**
|
||||
* Category to auto-select within Fleet
|
||||
*/
|
||||
category?: string;
|
||||
};
|
|
@ -7,24 +7,27 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { mountWithIntl } from '@kbn/test-jest-helpers';
|
||||
import { NoDataCard } from '@kbn/shared-ux-card-no-data';
|
||||
import { SharedUxServicesProvider, mockServicesFactory } from '@kbn/shared-ux-services';
|
||||
|
||||
import { NoDataPage } from './no_data_page';
|
||||
import { shallowWithIntl } from '@kbn/test-jest-helpers';
|
||||
import { ElasticAgentCard } from './no_data_card';
|
||||
|
||||
describe('NoDataPage', () => {
|
||||
test('render', () => {
|
||||
const component = shallowWithIntl(
|
||||
<NoDataPage
|
||||
solution="Analytics"
|
||||
action={{
|
||||
elasticAgent: {},
|
||||
}}
|
||||
logo={'logoKibana'}
|
||||
docsLink="test"
|
||||
/>
|
||||
const component = mountWithIntl(
|
||||
<SharedUxServicesProvider {...mockServicesFactory()}>
|
||||
<NoDataPage
|
||||
solution="Analytics"
|
||||
action={{
|
||||
elasticAgent: {},
|
||||
}}
|
||||
logo={'logoKibana'}
|
||||
docsLink="test"
|
||||
/>
|
||||
</SharedUxServicesProvider>
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
expect(component.find('h1').html()).toContain('Welcome to Elastic Analytics!');
|
||||
expect(component.find(ElasticAgentCard).length).toBe(1);
|
||||
expect(component.find(NoDataCard).length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
import React, { useMemo, FunctionComponent } from 'react';
|
||||
import useObservable from 'react-use/lib/useObservable';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { EuiLink, EuiSpacer, EuiText, EuiTextColor } from '@elastic/eui';
|
||||
|
@ -14,7 +15,8 @@ import { i18n } from '@kbn/i18n';
|
|||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { KibanaSolutionAvatar } from '@kbn/shared-ux-avatar-solution';
|
||||
|
||||
import { ElasticAgentCard } from './no_data_card';
|
||||
import { useSharedUxServices } from '@kbn/shared-ux-services';
|
||||
import { NoDataCard, NoDataCardProvider } from '@kbn/shared-ux-card-no-data';
|
||||
import { NoDataPageProps } from './types';
|
||||
|
||||
export const NoDataPage: FunctionComponent<NoDataPageProps> = ({
|
||||
|
@ -25,6 +27,19 @@ export const NoDataPage: FunctionComponent<NoDataPageProps> = ({
|
|||
pageTitle,
|
||||
...rest
|
||||
}) => {
|
||||
const services = useSharedUxServices();
|
||||
|
||||
// TODO: clintandrewhall - including the `NoDataCardProvider` here is a temporary solution
|
||||
// to consumers using this context to populate the NoDataPage. This will likely be removed soon,
|
||||
// when NoDataPage is moved to its own package.
|
||||
const currentAppId = useObservable(services.application.currentAppId$);
|
||||
const noDataCardServices = {
|
||||
currentAppId,
|
||||
addBasePath: services.http.addBasePath,
|
||||
canAccessFleet: services.permissions.canAccessFleet,
|
||||
navigateToUrl: services.application.navigateToUrl,
|
||||
};
|
||||
|
||||
const actionKeys = Object.keys(action);
|
||||
|
||||
const actionCard = useMemo(() => {
|
||||
|
@ -34,7 +49,7 @@ export const NoDataPage: FunctionComponent<NoDataPageProps> = ({
|
|||
const actionKey = actionKeys[0];
|
||||
const key =
|
||||
actionKey === 'elasticAgent' ? 'empty-page-agent-action' : `empty-page-${actionKey}-action`;
|
||||
return <ElasticAgentCard key={key} {...action[actionKey]} />;
|
||||
return <NoDataCard key={key} {...action[actionKey]} />;
|
||||
}, [action, actionKeys]);
|
||||
|
||||
const title =
|
||||
|
@ -74,7 +89,7 @@ export const NoDataPage: FunctionComponent<NoDataPageProps> = ({
|
|||
</EuiTextColor>
|
||||
</EuiText>
|
||||
<EuiSpacer size="xxl" />
|
||||
{actionCard}
|
||||
<NoDataCardProvider {...noDataCardServices}>{actionCard}</NoDataCardProvider>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
*/
|
||||
|
||||
import { CommonProps } from '@elastic/eui';
|
||||
import { ElasticAgentCardProps } from './no_data_card';
|
||||
import { NoDataCardProps } from '@kbn/shared-ux-card-no-data';
|
||||
|
||||
export type NoDataPageActions = ElasticAgentCardProps;
|
||||
export type NoDataPageActions = NoDataCardProps;
|
||||
|
||||
export interface NoDataPageProps extends CommonProps {
|
||||
/**
|
||||
|
|
|
@ -15,4 +15,7 @@ module.exports = {
|
|||
'../../../kbn-shared-ux*/**/*.stories.+(tsx|mdx)',
|
||||
'../../../../src/plugins/shared_ux/**/*.stories.+(tsx|mdx)',
|
||||
],
|
||||
reactOptions: {
|
||||
strictMode: true,
|
||||
},
|
||||
};
|
||||
|
|
143
packages/shared-ux/card/no_data/BUILD.bazel
Normal file
143
packages/shared-ux/card/no_data/BUILD.bazel
Normal file
|
@ -0,0 +1,143 @@
|
|||
load("@npm//@bazel/typescript:index.bzl", "ts_config")
|
||||
load("@build_bazel_rules_nodejs//:index.bzl", "js_library")
|
||||
load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project")
|
||||
|
||||
PKG_DIRNAME = "no_data"
|
||||
PKG_REQUIRE_NAME = "@kbn/shared-ux-card-no-data"
|
||||
|
||||
SOURCE_FILES = glob(
|
||||
[
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.mdx",
|
||||
"src/**/*.svg",
|
||||
],
|
||||
exclude = [
|
||||
"**/*.test.*",
|
||||
],
|
||||
)
|
||||
|
||||
SRCS = SOURCE_FILES
|
||||
|
||||
filegroup(
|
||||
name = "srcs",
|
||||
srcs = SRCS,
|
||||
)
|
||||
|
||||
NPM_MODULE_EXTRA_FILES = [
|
||||
"package.json",
|
||||
]
|
||||
|
||||
# In this array place runtime dependencies, including other packages and NPM packages
|
||||
# which must be available for this code to run.
|
||||
#
|
||||
# To reference other packages use:
|
||||
# "//repo/relative/path/to/package"
|
||||
# eg. "//packages/kbn-utils"
|
||||
#
|
||||
# To reference a NPM package use:
|
||||
# "@npm//name-of-package"
|
||||
# eg. "@npm//lodash"
|
||||
RUNTIME_DEPS = [
|
||||
"@npm//@elastic/eui",
|
||||
"@npm//@storybook/addon-actions",
|
||||
"@npm//enzyme",
|
||||
"@npm//react",
|
||||
"//packages/kbn-i18n-react",
|
||||
"//packages/kbn-i18n",
|
||||
"//packages/shared-ux/link/redirect_app",
|
||||
]
|
||||
|
||||
# In this array place dependencies necessary to build the types, which will include the
|
||||
# :npm_module_types target of other packages and packages from NPM, including @types/*
|
||||
# packages.
|
||||
#
|
||||
# To reference the types for another package use:
|
||||
# "//repo/relative/path/to/package:npm_module_types"
|
||||
# eg. "//packages/kbn-utils:npm_module_types"
|
||||
#
|
||||
# References to NPM packages work the same as RUNTIME_DEPS
|
||||
TYPES_DEPS = [
|
||||
"@npm//@elastic/eui",
|
||||
"@npm//@storybook/addon-actions",
|
||||
"@npm//@types/enzyme",
|
||||
"@npm//@types/jest",
|
||||
"@npm//@types/node",
|
||||
"@npm//@types/react",
|
||||
"//packages/kbn-ambient-ui-types",
|
||||
"//packages/kbn-i18n-react:npm_module_types",
|
||||
"//packages/kbn-i18n:npm_module_types",
|
||||
"//packages/shared-ux/link/redirect_app:npm_module_types",
|
||||
]
|
||||
|
||||
jsts_transpiler(
|
||||
name = "target_node",
|
||||
srcs = SRCS,
|
||||
build_pkg_name = package_name(),
|
||||
)
|
||||
|
||||
jsts_transpiler(
|
||||
name = "target_web",
|
||||
srcs = SRCS,
|
||||
build_pkg_name = package_name(),
|
||||
web = True,
|
||||
additional_args = [
|
||||
"--copy-files",
|
||||
"--quiet"
|
||||
],
|
||||
)
|
||||
|
||||
ts_config(
|
||||
name = "tsconfig",
|
||||
src = "tsconfig.json",
|
||||
deps = [
|
||||
"//:tsconfig.base.json",
|
||||
"//:tsconfig.bazel.json",
|
||||
],
|
||||
)
|
||||
|
||||
ts_project(
|
||||
name = "tsc_types",
|
||||
args = ['--pretty'],
|
||||
srcs = SRCS,
|
||||
deps = TYPES_DEPS,
|
||||
declaration = True,
|
||||
emit_declaration_only = True,
|
||||
out_dir = "target_types",
|
||||
root_dir = "src",
|
||||
tsconfig = ":tsconfig",
|
||||
)
|
||||
|
||||
js_library(
|
||||
name = PKG_DIRNAME,
|
||||
srcs = NPM_MODULE_EXTRA_FILES,
|
||||
deps = RUNTIME_DEPS + [":target_node", ":target_web"],
|
||||
package_name = PKG_REQUIRE_NAME,
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
pkg_npm(
|
||||
name = "npm_module",
|
||||
deps = [":" + PKG_DIRNAME],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "build",
|
||||
srcs = [":npm_module"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
pkg_npm_types(
|
||||
name = "npm_module_types",
|
||||
srcs = SRCS,
|
||||
deps = [":tsc_types"],
|
||||
package_name = PKG_REQUIRE_NAME,
|
||||
tsconfig = ":tsconfig",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "build_types",
|
||||
srcs = [":npm_module_types"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
29
packages/shared-ux/card/no_data/README.mdx
Normal file
29
packages/shared-ux/card/no_data/README.mdx
Normal file
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
id: sharedUX/Components/NoDataCard
|
||||
slug: /shared-ux/components/no-data-card
|
||||
title: No Data Card
|
||||
summary: A card displayed when no data is available is available in Kibana.
|
||||
tags: ['shared-ux', 'component']
|
||||
date: 2022-06-15
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
A wrapper around `EuiCard` tailored for use in Kibana solutions when no data is available.
|
||||
|
||||
## Usage
|
||||
|
||||
All of the `EuiCard` props are available with the exception of `layout`. A default `description` and `button` are provided, but can be overridden in specific use cases.
|
||||
|
||||
The `NoDataCard` connected component uses:
|
||||
|
||||
- `navLinks.integrations` from `coreStart.application.capabilities` to determine if the user has access to the Integrations page.
|
||||
- `addBasePath` from `coreStart` to navigate to the Integrations page.
|
||||
|
||||
## API
|
||||
| Export | Description |
|
||||
|---|---|
|
||||
| `NoDataCardProvider` | Provides contextual services to `NoDataCard`. |
|
||||
| `NoDataCardKibanaProvider` | Maps Kibana dependencies to provide contextual services to `NoDataCard`. |
|
||||
| `NoDataCard` | Uses a `Provider` to access contextual services to populate props on the `NoDataCardComponent`. |
|
||||
| `NoDataCardComponent` | The pure component, a pre-configured **EuiCard**. |
|
|
@ -5,6 +5,9 @@
|
|||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
export { NoDataCard } from './no_data_card';
|
||||
export { ElasticAgentCard } from './elastic_agent_card';
|
||||
export type { NoDataCardProps, ElasticAgentCardProps } from './types';
|
||||
|
||||
module.exports = {
|
||||
preset: '@kbn/test',
|
||||
rootDir: '../../../..',
|
||||
roots: ['<rootDir>/packages/shared-ux/card/no_data'],
|
||||
};
|
8
packages/shared-ux/card/no_data/package.json
Normal file
8
packages/shared-ux/card/no_data/package.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"name": "@kbn/shared-ux-card-no-data",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"main": "./target_node/index.js",
|
||||
"browser": "./target_web/index.js",
|
||||
"license": "SSPL-1.0 OR Elastic License 2.0"
|
||||
}
|
117
packages/shared-ux/card/no_data/src/__snapshots__/no_data_card.component.test.tsx.snap
generated
Normal file
117
packages/shared-ux/card/no_data/src/__snapshots__/no_data_card.component.test.tsx.snap
generated
Normal file
|
@ -0,0 +1,117 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`NoDataCardComponent props button 1`] = `
|
||||
<EuiCard
|
||||
css={
|
||||
Object {
|
||||
"marginInline": "auto",
|
||||
"maxWidth": 400,
|
||||
}
|
||||
}
|
||||
description="Use Elastic Agent for a simple, unified way to collect data from your machines."
|
||||
footer={
|
||||
<EuiButton
|
||||
fill={true}
|
||||
>
|
||||
Button
|
||||
</EuiButton>
|
||||
}
|
||||
image={<Image />}
|
||||
isDisabled={false}
|
||||
paddingSize="l"
|
||||
title={
|
||||
<EuiScreenReaderOnly>
|
||||
<span>
|
||||
Add Elastic Agent
|
||||
</span>
|
||||
</EuiScreenReaderOnly>
|
||||
}
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`NoDataCardComponent props href 1`] = `
|
||||
<EuiCard
|
||||
css={
|
||||
Object {
|
||||
"marginInline": "auto",
|
||||
"maxWidth": 400,
|
||||
}
|
||||
}
|
||||
description="Use Elastic Agent for a simple, unified way to collect data from your machines."
|
||||
footer={
|
||||
<EuiButton
|
||||
fill={true}
|
||||
>
|
||||
Add Elastic Agent
|
||||
</EuiButton>
|
||||
}
|
||||
href="some path"
|
||||
image={<Image />}
|
||||
isDisabled={false}
|
||||
paddingSize="l"
|
||||
title={
|
||||
<EuiScreenReaderOnly>
|
||||
<span>
|
||||
Add Elastic Agent
|
||||
</span>
|
||||
</EuiScreenReaderOnly>
|
||||
}
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`NoDataCardComponent renders 1`] = `
|
||||
<EuiCard
|
||||
css={
|
||||
Object {
|
||||
"marginInline": "auto",
|
||||
"maxWidth": 400,
|
||||
}
|
||||
}
|
||||
description="Use Elastic Agent for a simple, unified way to collect data from your machines."
|
||||
footer={
|
||||
<EuiButton
|
||||
fill={true}
|
||||
>
|
||||
Add Elastic Agent
|
||||
</EuiButton>
|
||||
}
|
||||
image={<Image />}
|
||||
isDisabled={false}
|
||||
paddingSize="l"
|
||||
title={
|
||||
<EuiScreenReaderOnly>
|
||||
<span>
|
||||
Add Elastic Agent
|
||||
</span>
|
||||
</EuiScreenReaderOnly>
|
||||
}
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`NoDataCardComponent renders with canAccessFleet false 1`] = `
|
||||
<EuiCard
|
||||
css={
|
||||
Object {
|
||||
"marginInline": "auto",
|
||||
"maxWidth": 400,
|
||||
}
|
||||
}
|
||||
description={
|
||||
<EuiTextColor
|
||||
color="default"
|
||||
>
|
||||
This integration is not yet enabled. Your administrator has the required permissions to turn it on.
|
||||
</EuiTextColor>
|
||||
}
|
||||
image={<Image />}
|
||||
isDisabled={true}
|
||||
paddingSize="l"
|
||||
title={
|
||||
<EuiTextColor
|
||||
color="default"
|
||||
>
|
||||
Contact your administrator
|
||||
</EuiTextColor>
|
||||
}
|
||||
/>
|
||||
`;
|
356
packages/shared-ux/card/no_data/src/__snapshots__/no_data_card.test.tsx.snap
generated
Normal file
356
packages/shared-ux/card/no_data/src/__snapshots__/no_data_card.test.tsx.snap
generated
Normal file
|
@ -0,0 +1,356 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`NoDataCard props button 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="euiPanel euiPanel--paddingLarge euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--isClickable euiCard euiCard--centerAligned euiCard--isClickable css-1hu4pg0-EuiCard"
|
||||
>
|
||||
<div
|
||||
class="euiCard__top"
|
||||
>
|
||||
<div
|
||||
class="euiCard__image"
|
||||
>
|
||||
<figure
|
||||
class="euiImage euiImage--fullWidth"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
class="euiImage__img"
|
||||
src="test-file-stub"
|
||||
style="width:max(100%, 360px);height:240px;object-fit:cover;background:aliceblue"
|
||||
/>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiCard__content"
|
||||
>
|
||||
<span
|
||||
class="euiTitle euiCard__title css-1mr6cet-euiTitle-s"
|
||||
id="generated-idTitle"
|
||||
>
|
||||
<a
|
||||
aria-describedby="generated-idDescription"
|
||||
class="euiCard__titleAnchor"
|
||||
href="/app/integrations/browse"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<span
|
||||
class="euiScreenReaderOnly"
|
||||
>
|
||||
Card title
|
||||
</span>
|
||||
</a>
|
||||
</span>
|
||||
<div
|
||||
class="euiText euiText--small euiCard__description"
|
||||
id="generated-idDescription"
|
||||
>
|
||||
<p>
|
||||
Description
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiCard__footer"
|
||||
>
|
||||
<button
|
||||
class="euiButton euiButton--primary euiButton--fill"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="euiButtonContent euiButton__content"
|
||||
>
|
||||
<span
|
||||
class="euiButton__text"
|
||||
>
|
||||
Button
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`NoDataCard props extends EuiCardProps 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="euiPanel euiPanel--paddingLarge euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--isClickable euiCard euiCard--centerAligned euiCard--isClickable custom_class css-1hu4pg0-EuiCard"
|
||||
>
|
||||
<div
|
||||
class="euiCard__top"
|
||||
>
|
||||
<div
|
||||
class="euiCard__image"
|
||||
>
|
||||
<figure
|
||||
class="euiImage euiImage--fullWidth"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
class="euiImage__img"
|
||||
src="test-file-stub"
|
||||
style="width:max(100%, 360px);height:240px;object-fit:cover;background:aliceblue"
|
||||
/>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiCard__content"
|
||||
>
|
||||
<span
|
||||
class="euiTitle euiCard__title css-1mr6cet-euiTitle-s"
|
||||
id="generated-idTitle"
|
||||
>
|
||||
<a
|
||||
aria-describedby="generated-idDescription"
|
||||
class="euiCard__titleAnchor"
|
||||
href="/app/integrations/browse"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<span
|
||||
class="euiScreenReaderOnly"
|
||||
>
|
||||
Card title
|
||||
</span>
|
||||
</a>
|
||||
</span>
|
||||
<div
|
||||
class="euiText euiText--small euiCard__description"
|
||||
id="generated-idDescription"
|
||||
>
|
||||
<p>
|
||||
Description
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiCard__footer"
|
||||
>
|
||||
<button
|
||||
class="euiButton euiButton--primary euiButton--fill"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="euiButtonContent euiButton__content"
|
||||
>
|
||||
<span
|
||||
class="euiButton__text"
|
||||
>
|
||||
Button
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`NoDataCard props href 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="euiPanel euiPanel--paddingLarge euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--isClickable euiCard euiCard--centerAligned euiCard--isClickable css-1hu4pg0-EuiCard"
|
||||
>
|
||||
<div
|
||||
class="euiCard__top"
|
||||
>
|
||||
<div
|
||||
class="euiCard__image"
|
||||
>
|
||||
<figure
|
||||
class="euiImage euiImage--fullWidth"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
class="euiImage__img"
|
||||
src="test-file-stub"
|
||||
style="width:max(100%, 360px);height:240px;object-fit:cover;background:aliceblue"
|
||||
/>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiCard__content"
|
||||
>
|
||||
<span
|
||||
class="euiTitle euiCard__title css-1mr6cet-euiTitle-s"
|
||||
id="generated-idTitle"
|
||||
>
|
||||
<a
|
||||
aria-describedby="generated-idDescription"
|
||||
class="euiCard__titleAnchor"
|
||||
href="#"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<span
|
||||
class="euiScreenReaderOnly"
|
||||
>
|
||||
Card title
|
||||
</span>
|
||||
</a>
|
||||
</span>
|
||||
<div
|
||||
class="euiText euiText--small euiCard__description"
|
||||
id="generated-idDescription"
|
||||
>
|
||||
<p>
|
||||
Description
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiCard__footer"
|
||||
>
|
||||
<button
|
||||
class="euiButton euiButton--primary euiButton--fill"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="euiButtonContent euiButton__content"
|
||||
>
|
||||
<span
|
||||
class="euiButton__text"
|
||||
>
|
||||
Button
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`NoDataCard props no access to Fleet 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="euiPanel euiPanel--paddingLarge euiPanel--borderRadiusMedium euiPanel--subdued euiPanel--noShadow euiPanel--noBorder euiCard euiCard--centerAligned euiCard-isDisabled css-1hu4pg0-EuiCard"
|
||||
>
|
||||
<div
|
||||
class="euiCard__top"
|
||||
>
|
||||
<div
|
||||
class="euiCard__image"
|
||||
>
|
||||
<figure
|
||||
class="euiImage euiImage--fullWidth"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
class="euiImage__img"
|
||||
src="test-file-stub"
|
||||
style="width:max(100%, 360px);height:240px;object-fit:cover;background:aliceblue"
|
||||
/>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiCard__content"
|
||||
>
|
||||
<span
|
||||
class="euiTitle euiCard__title css-1mr6cet-euiTitle-s"
|
||||
id="generated-idTitle"
|
||||
>
|
||||
<button
|
||||
aria-describedby=" generated-idDescription"
|
||||
class="euiCard__titleButton"
|
||||
disabled=""
|
||||
>
|
||||
<span
|
||||
class="euiTextColor euiTextColor--default"
|
||||
>
|
||||
Contact your administrator
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
<div
|
||||
class="euiText euiText--small euiCard__description"
|
||||
id="generated-idDescription"
|
||||
>
|
||||
<p>
|
||||
<span
|
||||
class="euiTextColor euiTextColor--default"
|
||||
>
|
||||
This integration is not yet enabled. Your administrator has the required permissions to turn it on.
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`NoDataCard renders 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="euiPanel euiPanel--paddingLarge euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPanel--isClickable euiCard euiCard--centerAligned euiCard--isClickable css-1hu4pg0-EuiCard"
|
||||
>
|
||||
<div
|
||||
class="euiCard__top"
|
||||
>
|
||||
<div
|
||||
class="euiCard__image"
|
||||
>
|
||||
<figure
|
||||
class="euiImage euiImage--fullWidth"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
class="euiImage__img"
|
||||
src="test-file-stub"
|
||||
style="width:max(100%, 360px);height:240px;object-fit:cover;background:aliceblue"
|
||||
/>
|
||||
</figure>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiCard__content"
|
||||
>
|
||||
<span
|
||||
class="euiTitle euiCard__title css-1mr6cet-euiTitle-s"
|
||||
id="generated-idTitle"
|
||||
>
|
||||
<a
|
||||
aria-describedby="generated-idDescription"
|
||||
class="euiCard__titleAnchor"
|
||||
href="/app/integrations/browse"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<span
|
||||
class="euiScreenReaderOnly"
|
||||
>
|
||||
Card title
|
||||
</span>
|
||||
</a>
|
||||
</span>
|
||||
<div
|
||||
class="euiText euiText--small euiCard__description"
|
||||
id="generated-idDescription"
|
||||
>
|
||||
<p>
|
||||
Description
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="euiCard__footer"
|
||||
>
|
||||
<button
|
||||
class="euiButton euiButton--primary euiButton--fill"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="euiButtonContent euiButton__content"
|
||||
>
|
||||
<span
|
||||
class="euiButton__text"
|
||||
>
|
||||
Card title
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 93 KiB |
19
packages/shared-ux/card/no_data/src/index.ts
Normal file
19
packages/shared-ux/card/no_data/src/index.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { NoDataCard } from './no_data_card';
|
||||
export type { Props as NoDataCardProps } from './no_data_card';
|
||||
|
||||
export { NoDataCardKibanaProvider, NoDataCardProvider } from './services';
|
||||
export type { NoDataCardKibanaDependencies, NoDataCardServices } from './services';
|
||||
|
||||
export {
|
||||
getMockServices as getNoDataCardMockServices,
|
||||
getStoryArgTypes as getNoDataCardStoryArgTypes,
|
||||
getStoryServices as getNoDataCardStoryServices,
|
||||
} from './mocks';
|
88
packages/shared-ux/card/no_data/src/mocks.ts
Normal file
88
packages/shared-ux/card/no_data/src/mocks.ts
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import {
|
||||
getRedirectAppLinksMockServices,
|
||||
getRedirectAppLinksStoryArgTypes,
|
||||
getRedirectAppLinksStoryServices,
|
||||
} from '@kbn/shared-ux-link-redirect-app';
|
||||
|
||||
import { NoDataCardServices } from './services';
|
||||
|
||||
/**
|
||||
* Parameters drawn from the Storybook arguments collection that customize a component story.
|
||||
*/
|
||||
export type Params = Record<keyof ReturnType<typeof getStoryArgTypes>, any>;
|
||||
|
||||
/**
|
||||
* Returns Storybook-compatible service abstractions for the `NoDataCard` Provider.
|
||||
*/
|
||||
export const getStoryServices = (params: Params) => {
|
||||
const services: NoDataCardServices = {
|
||||
...getRedirectAppLinksStoryServices(),
|
||||
...params,
|
||||
addBasePath: (path) => {
|
||||
action('addBasePath')(path);
|
||||
return path;
|
||||
},
|
||||
};
|
||||
|
||||
return services;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the Storybook arguments for `NoDataCard`, for its stories and for
|
||||
* consuming component stories.
|
||||
*/
|
||||
export const getStoryArgTypes = () => ({
|
||||
...getRedirectAppLinksStoryArgTypes(),
|
||||
canAccessFleet: {
|
||||
control: 'boolean',
|
||||
defaultValue: true,
|
||||
},
|
||||
category: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
defaultValue: '',
|
||||
},
|
||||
title: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
defaultValue: '',
|
||||
},
|
||||
description: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
defaultValue: '',
|
||||
},
|
||||
button: {
|
||||
control: {
|
||||
type: 'text',
|
||||
},
|
||||
defaultValue: '',
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns the Jest-compatible service abstractions for the `NoDataCard` Provider.
|
||||
*/
|
||||
export const getMockServices = (params?: Params) => {
|
||||
const { canAccessFleet } = params || { canAccessFleet: true };
|
||||
|
||||
const services: NoDataCardServices = {
|
||||
...getRedirectAppLinksMockServices(),
|
||||
canAccessFleet,
|
||||
addBasePath: (path) => path,
|
||||
};
|
||||
|
||||
return services;
|
||||
};
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { NoDataCard } from './no_data_card.component';
|
||||
|
||||
describe('NoDataCardComponent', () => {
|
||||
test('renders', () => {
|
||||
const component = shallow(<NoDataCard canAccessFleet={true} />);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('renders with canAccessFleet false', () => {
|
||||
const component = shallow(<NoDataCard canAccessFleet={false} />);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('props', () => {
|
||||
test('button', () => {
|
||||
const component = shallow(<NoDataCard button="Button" canAccessFleet={true} />);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('href', () => {
|
||||
const component = shallow(<NoDataCard canAccessFleet={true} href={'some path'} />);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
136
packages/shared-ux/card/no_data/src/no_data_card.component.tsx
Normal file
136
packages/shared-ux/card/no_data/src/no_data_card.component.tsx
Normal file
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { MouseEventHandler, ReactNode } from 'react';
|
||||
import {
|
||||
EuiButton,
|
||||
EuiCard,
|
||||
EuiScreenReaderOnly,
|
||||
EuiTextColor,
|
||||
EuiCardProps,
|
||||
EuiImage,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { NoDataCardStyles } from './no_data_card.styles';
|
||||
import ElasticAgentCardIllustration from './assets/elastic_agent_card.svg';
|
||||
|
||||
export type Props = Partial<
|
||||
Omit<EuiCardProps, 'layout' | 'isDisabled' | 'button' | 'onClick' | 'description'>
|
||||
> & {
|
||||
/**
|
||||
* Provide just a string for the button's label, or a whole component;
|
||||
* The button will be hidden completely if `isDisabled=true`
|
||||
*/
|
||||
button?: string | ReactNode;
|
||||
/** Remapping `onClick` to any element */
|
||||
onClick?: MouseEventHandler<HTMLElement>;
|
||||
/**
|
||||
* Description for the card;
|
||||
* If not provided, the default will be used
|
||||
*/
|
||||
description?: string | ReactNode;
|
||||
/** Category to auto-select within Fleet */
|
||||
category?: string;
|
||||
/** True if the person has permission to access Fleet, false otherwise */
|
||||
canAccessFleet?: boolean;
|
||||
};
|
||||
|
||||
const noPermissionTitle = i18n.translate('sharedUXPackages.card.noData.noPermission.title', {
|
||||
defaultMessage: `Contact your administrator`,
|
||||
});
|
||||
|
||||
const noPermissionDescription = i18n.translate(
|
||||
'sharedUXPackages.card.noData.noPermission.description',
|
||||
{
|
||||
defaultMessage: `This integration is not yet enabled. Your administrator has the required permissions to turn it on.`,
|
||||
}
|
||||
);
|
||||
|
||||
const defaultTitle = i18n.translate('sharedUXPackages.card.noData.title', {
|
||||
defaultMessage: 'Add Elastic Agent',
|
||||
});
|
||||
|
||||
const defaultDescription = i18n.translate('sharedUXPackages.card.noData.description', {
|
||||
defaultMessage: `Use Elastic Agent for a simple, unified way to collect data from your machines.`,
|
||||
});
|
||||
|
||||
const Image = () => (
|
||||
<EuiImage
|
||||
size="fullWidth"
|
||||
style={{
|
||||
width: 'max(100%, 360px)',
|
||||
height: 240,
|
||||
objectFit: 'cover',
|
||||
background: 'aliceblue',
|
||||
}}
|
||||
url={ElasticAgentCardIllustration}
|
||||
alt=""
|
||||
/>
|
||||
);
|
||||
|
||||
/**
|
||||
* Creates a specific NoDataCard pointing users to Integrations when `canAccessFleet`
|
||||
*/
|
||||
export const NoDataCard = ({
|
||||
title: titleProp,
|
||||
description: descriptionProp,
|
||||
canAccessFleet,
|
||||
button,
|
||||
...props
|
||||
}: Props) => {
|
||||
const styles = NoDataCardStyles();
|
||||
|
||||
const footer = () => {
|
||||
// Don't render the footer action if disabled
|
||||
if (!canAccessFleet) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Render a custom footer action if the button is not a simple string
|
||||
if (button && typeof button !== 'string') {
|
||||
return button;
|
||||
}
|
||||
|
||||
// Default footer action is a button with the provided or default string
|
||||
return <EuiButton fill>{button || titleProp || defaultTitle}</EuiButton>;
|
||||
};
|
||||
|
||||
const title = () => {
|
||||
if (!canAccessFleet) {
|
||||
return <EuiTextColor color="default">{noPermissionTitle}</EuiTextColor>;
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiScreenReaderOnly>
|
||||
<span>{titleProp || defaultTitle}</span>
|
||||
</EuiScreenReaderOnly>
|
||||
);
|
||||
};
|
||||
|
||||
const description = () => {
|
||||
if (!canAccessFleet) {
|
||||
return <EuiTextColor color="default">{noPermissionDescription}</EuiTextColor>;
|
||||
}
|
||||
|
||||
return descriptionProp || defaultDescription;
|
||||
};
|
||||
|
||||
return (
|
||||
<EuiCard
|
||||
css={styles}
|
||||
paddingSize="l"
|
||||
title={title()}
|
||||
description={description()}
|
||||
footer={footer()}
|
||||
isDisabled={!canAccessFleet}
|
||||
image={<Image />}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
45
packages/shared-ux/card/no_data/src/no_data_card.stories.tsx
Normal file
45
packages/shared-ux/card/no_data/src/no_data_card.stories.tsx
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { Params, getStoryArgTypes, getStoryServices } from './mocks';
|
||||
|
||||
import { NoDataCard as Component } from './no_data_card.component';
|
||||
import { NoDataCard as ConnectedComponent } from './no_data_card';
|
||||
import { NoDataCardProvider } from './services';
|
||||
|
||||
import mdx from '../README.mdx';
|
||||
|
||||
export default {
|
||||
title: 'No Data/Card',
|
||||
description: 'A solution-specific wrapper around `EuiCard`, to be used on `NoData` page',
|
||||
parameters: {
|
||||
docs: {
|
||||
page: mdx,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const argTypes = getStoryArgTypes();
|
||||
|
||||
export const NoDataCard = (params: Params) => {
|
||||
return (
|
||||
<NoDataCardProvider {...getStoryServices(params)}>
|
||||
<ConnectedComponent {...params} />
|
||||
</NoDataCardProvider>
|
||||
);
|
||||
};
|
||||
|
||||
NoDataCard.argTypes = argTypes;
|
||||
|
||||
export const NoDataCardComponent = (params: Params) => {
|
||||
return <Component {...params} />;
|
||||
};
|
||||
|
||||
NoDataCardComponent.argTypes = argTypes;
|
|
@ -6,11 +6,23 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { render } from 'enzyme';
|
||||
import { render as enzymeRender } from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import { NoDataCard } from './no_data_card';
|
||||
import { NoDataCardProvider } from './services';
|
||||
|
||||
const services = {
|
||||
addBasePath: (path: string) => path,
|
||||
navigateToUrl: () => {},
|
||||
};
|
||||
|
||||
describe('NoDataCard', () => {
|
||||
const render = (element: React.ReactElement, canAccessFleet: boolean = true) =>
|
||||
enzymeRender(
|
||||
<NoDataCardProvider {...{ canAccessFleet, ...services }}>{element}</NoDataCardProvider>
|
||||
);
|
||||
|
||||
test('renders', () => {
|
||||
const component = render(<NoDataCard title="Card title" description="Description" />);
|
||||
expect(component).toMatchSnapshot();
|
||||
|
@ -31,14 +43,10 @@ describe('NoDataCard', () => {
|
|||
expect(component).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('isDisabled', () => {
|
||||
test('no access to Fleet', () => {
|
||||
const component = render(
|
||||
<NoDataCard
|
||||
isDisabled={true}
|
||||
button="Button"
|
||||
title="Card title"
|
||||
description="Description"
|
||||
/>
|
||||
<NoDataCard button="Button" title="Card title" description="Description" />,
|
||||
false
|
||||
);
|
||||
expect(component).toMatchSnapshot();
|
||||
});
|
41
packages/shared-ux/card/no_data/src/no_data_card.tsx
Normal file
41
packages/shared-ux/card/no_data/src/no_data_card.tsx
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
import { RedirectAppLinksContainer } from '@kbn/shared-ux-link-redirect-app';
|
||||
|
||||
import { NoDataCard as Component, Props as ComponentProps } from './no_data_card.component';
|
||||
|
||||
import { useServices } from './services';
|
||||
|
||||
export type Props = Omit<ComponentProps, 'canAccessFleet'>;
|
||||
|
||||
export const NoDataCard = ({ href: srcHref, category, description, ...props }: Props) => {
|
||||
const { canAccessFleet, addBasePath } = useServices();
|
||||
|
||||
const href = useMemo(() => {
|
||||
if (srcHref) {
|
||||
return srcHref;
|
||||
}
|
||||
|
||||
// TODO: get this URL from a locator
|
||||
const prefix = '/app/integrations/browse';
|
||||
|
||||
if (category) {
|
||||
return addBasePath(`${prefix}/${category}`);
|
||||
}
|
||||
|
||||
return addBasePath(prefix);
|
||||
}, [addBasePath, srcHref, category]);
|
||||
|
||||
return (
|
||||
<RedirectAppLinksContainer>
|
||||
<Component {...{ ...props, href, canAccessFleet, description }} />
|
||||
</RedirectAppLinksContainer>
|
||||
);
|
||||
};
|
97
packages/shared-ux/card/no_data/src/services.tsx
Normal file
97
packages/shared-ux/card/no_data/src/services.tsx
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { FC, useContext } from 'react';
|
||||
import {
|
||||
RedirectAppLinksServices,
|
||||
RedirectAppLinksKibanaDependencies,
|
||||
RedirectAppLinksProvider,
|
||||
RedirectAppLinksKibanaProvider,
|
||||
} from '@kbn/shared-ux-link-redirect-app';
|
||||
|
||||
/**
|
||||
* A list of services that are consumed by this component.
|
||||
*/
|
||||
interface Services {
|
||||
addBasePath: (path: string) => string;
|
||||
canAccessFleet: boolean;
|
||||
}
|
||||
|
||||
const Context = React.createContext<Services | null>(null);
|
||||
|
||||
/**
|
||||
* Services that are consumed by this component and its dependencies.
|
||||
*/
|
||||
export type NoDataCardServices = Services & RedirectAppLinksServices;
|
||||
|
||||
/**
|
||||
* A Context Provider that provides services to the component and its dependencies.
|
||||
*/
|
||||
export const NoDataCardProvider: FC<NoDataCardServices> = ({ children, ...services }) => {
|
||||
const { addBasePath, canAccessFleet } = services;
|
||||
|
||||
return (
|
||||
<Context.Provider value={{ addBasePath, canAccessFleet }}>
|
||||
<RedirectAppLinksProvider {...services}>{children}</RedirectAppLinksProvider>
|
||||
</Context.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
interface KibanaDependencies {
|
||||
coreStart: {
|
||||
http: {
|
||||
basePath: {
|
||||
prepend: (path: string) => string;
|
||||
};
|
||||
};
|
||||
application: {
|
||||
capabilities: {
|
||||
navLinks: Record<string, boolean>;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
/**
|
||||
* An interface containing a collection of Kibana plugins and services required to
|
||||
* render this component as well as its dependencies.
|
||||
*/
|
||||
export type NoDataCardKibanaDependencies = KibanaDependencies & RedirectAppLinksKibanaDependencies;
|
||||
|
||||
/**
|
||||
* Kibana-specific Provider that maps dependencies to services.
|
||||
*/
|
||||
export const NoDataCardKibanaProvider: FC<NoDataCardKibanaDependencies> = ({
|
||||
children,
|
||||
...dependencies
|
||||
}) => {
|
||||
const value: Services = {
|
||||
addBasePath: dependencies.coreStart.http.basePath.prepend,
|
||||
canAccessFleet: dependencies.coreStart.application.capabilities.navLinks.integrations,
|
||||
};
|
||||
|
||||
return (
|
||||
<Context.Provider {...{ value }}>
|
||||
<RedirectAppLinksKibanaProvider {...dependencies}>{children}</RedirectAppLinksKibanaProvider>
|
||||
</Context.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* React hook for accessing pre-wired services.
|
||||
*/
|
||||
export function useServices() {
|
||||
const context = useContext(Context);
|
||||
|
||||
if (!context) {
|
||||
throw new Error(
|
||||
'NoDataCard Context is missing. Ensure your component or React root is wrapped with NoDataCardContext.'
|
||||
);
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
20
packages/shared-ux/card/no_data/tsconfig.json
Normal file
20
packages/shared-ux/card/no_data/tsconfig.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"extends": "../../../../tsconfig.bazel.json",
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"outDir": "target_types",
|
||||
"rootDir": "src",
|
||||
"stripInternal": false,
|
||||
"types": [
|
||||
"jest",
|
||||
"node",
|
||||
"react",
|
||||
"@emotion/react/types/css-prop",
|
||||
"@kbn/ambient-ui-types"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
}
|
|
@ -10,16 +10,27 @@ export { RedirectAppLinks as RedirectAppLinksContainer } from './redirect_app_li
|
|||
export { RedirectAppLinks as RedirectAppLinksComponent } from './redirect_app_links.component';
|
||||
export { RedirectAppLinksKibanaProvider, RedirectAppLinksProvider } from './services';
|
||||
|
||||
export type {
|
||||
Services as RedirectAppLinksServices,
|
||||
KibanaDependencies as RedirectAppLinksKibanaDependencies,
|
||||
} from './services';
|
||||
|
||||
export {
|
||||
getMockServices as getRedirectAppLinksMockServices,
|
||||
getStoryArgTypes as getRedirectAppLinksStoryArgTypes,
|
||||
getStoryServices as getRedirectAppLinksStoryServices,
|
||||
} from './mocks';
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import { RedirectAppLinks as RedirectAppLinksContainer } from './redirect_app_links';
|
||||
import {
|
||||
Services,
|
||||
KibanaServices,
|
||||
KibanaDependencies,
|
||||
RedirectAppLinksKibanaProvider,
|
||||
RedirectAppLinksProvider,
|
||||
} from './services';
|
||||
|
||||
const isKibanaContract = (services: any): services is KibanaServices => {
|
||||
const isKibanaContract = (services: any): services is KibanaDependencies => {
|
||||
return typeof services.coreStart !== 'undefined';
|
||||
};
|
||||
|
||||
|
@ -28,12 +39,22 @@ const isKibanaContract = (services: any): services is KibanaServices => {
|
|||
* `RedirectAppLinksKibanaProvider` based on the services provided, creating a single component
|
||||
* with which consumers can wrap their components or solutions.
|
||||
*/
|
||||
export const RedirectAppLinks: FC<Services | KibanaServices> = ({ children, ...services }) => {
|
||||
export const RedirectAppLinks: FC<Services | KibanaDependencies> = ({ children, ...services }) => {
|
||||
const container = <RedirectAppLinksContainer>{children}</RedirectAppLinksContainer>;
|
||||
|
||||
return isKibanaContract(services) ? (
|
||||
<RedirectAppLinksKibanaProvider {...services}>{container}</RedirectAppLinksKibanaProvider>
|
||||
) : (
|
||||
<RedirectAppLinksProvider {...services}>{container}</RedirectAppLinksProvider>
|
||||
if (isKibanaContract(services)) {
|
||||
const { coreStart } = services;
|
||||
return (
|
||||
<RedirectAppLinksKibanaProvider {...{ coreStart }}>
|
||||
{container}
|
||||
</RedirectAppLinksKibanaProvider>
|
||||
);
|
||||
}
|
||||
|
||||
const { navigateToUrl, currentAppId } = services;
|
||||
return (
|
||||
<RedirectAppLinksProvider {...{ currentAppId, navigateToUrl }}>
|
||||
{container}
|
||||
</RedirectAppLinksProvider>
|
||||
);
|
||||
};
|
||||
|
|
46
packages/shared-ux/link/redirect_app/src/mocks.ts
Normal file
46
packages/shared-ux/link/redirect_app/src/mocks.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { action } from '@storybook/addon-actions';
|
||||
|
||||
import { Services } from './services';
|
||||
|
||||
/**
|
||||
* Parameters drawn from the Storybook arguments collection that customize a component story.
|
||||
*/
|
||||
export type Params = Record<keyof ReturnType<typeof getStoryArgTypes>, any>;
|
||||
|
||||
/**
|
||||
* Returns Storybook-compatible service abstractions for the `NoDataCard` Provider.
|
||||
*/
|
||||
export const getStoryServices = () => {
|
||||
const services: Services = {
|
||||
navigateToUrl: action('navigateToUrl'),
|
||||
currentAppId: 'currentAppId',
|
||||
};
|
||||
|
||||
return services;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the Storybook arguments for `NoDataCard`, for its stories and for
|
||||
* consuming component stories.
|
||||
*/
|
||||
export const getStoryArgTypes = () => ({});
|
||||
|
||||
/**
|
||||
* Returns the Jest-compatible service abstractions for the `NoDataCard` Provider.
|
||||
*/
|
||||
export const getMockServices = () => {
|
||||
const services: Services = {
|
||||
navigateToUrl: jest.fn(),
|
||||
currentAppId: 'currentAppId',
|
||||
};
|
||||
|
||||
return services;
|
||||
};
|
|
@ -29,12 +29,7 @@ export interface Props extends DetailedHTMLProps<HTMLAttributes<HTMLDivElement>,
|
|||
* </RedirectAppLinks>
|
||||
* ```
|
||||
*/
|
||||
export const RedirectAppLinks: FC<Props> = ({
|
||||
children,
|
||||
navigateToUrl,
|
||||
currentAppId,
|
||||
...otherProps
|
||||
}) => {
|
||||
export const RedirectAppLinks: FC<Props> = ({ children, navigateToUrl, currentAppId }) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const handleClick: MouseEventHandler<HTMLDivElement> = useCallback(
|
||||
|
@ -50,7 +45,7 @@ export const RedirectAppLinks: FC<Props> = ({
|
|||
|
||||
return (
|
||||
// eslint-disable-next-line jsx-a11y/click-events-have-key-events
|
||||
<div {...otherProps} ref={containerRef} onClick={handleClick}>
|
||||
<div ref={containerRef} onClick={handleClick}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -10,11 +10,12 @@ import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
|||
import React from 'react';
|
||||
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { RedirectAppLinks } from '.';
|
||||
import { RedirectAppLinks as Component } from '.';
|
||||
import { getStoryArgTypes, getStoryServices } from './mocks';
|
||||
import mdx from '../README.mdx';
|
||||
|
||||
export default {
|
||||
title: 'Redirect App Links',
|
||||
title: 'Link',
|
||||
description:
|
||||
'An "area of effect" component which intercepts clicks on anchor elements and redirects them to Kibana solutions without a page refresh.',
|
||||
parameters: {
|
||||
|
@ -24,16 +25,10 @@ export default {
|
|||
},
|
||||
};
|
||||
|
||||
export const Component = () => {
|
||||
const navigateToUrl = async (url: string) => {
|
||||
action('navigateToUrl')(url);
|
||||
};
|
||||
|
||||
const currentAppId = 'abc123';
|
||||
|
||||
export const RedirectAppLinks = () => {
|
||||
return (
|
||||
<>
|
||||
<RedirectAppLinks {...{ currentAppId, navigateToUrl }}>
|
||||
<Component {...getStoryServices()}>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
|
@ -54,7 +49,7 @@ export const Component = () => {
|
|||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</RedirectAppLinks>
|
||||
</Component>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
|
@ -69,3 +64,5 @@ export const Component = () => {
|
|||
</>
|
||||
);
|
||||
};
|
||||
|
||||
RedirectAppLinks.argTypes = getStoryArgTypes();
|
||||
|
|
|
@ -6,15 +6,10 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { FC } from 'react';
|
||||
|
||||
import { useServices } from './services';
|
||||
import {
|
||||
RedirectAppLinks as Component,
|
||||
Props as ComponentProps,
|
||||
} from './redirect_app_links.component';
|
||||
|
||||
type Props = Omit<ComponentProps, 'navigateToUrl' | 'currentAppId'>;
|
||||
import { RedirectAppLinks as Component } from './redirect_app_links.component';
|
||||
|
||||
/**
|
||||
* A service-enabled component that provides Kibana-specific functionality to the `RedirectAppLinks`
|
||||
|
@ -27,4 +22,6 @@ type Props = Omit<ComponentProps, 'navigateToUrl' | 'currentAppId'>;
|
|||
* </RedirectAppLinks>
|
||||
* ```
|
||||
*/
|
||||
export const RedirectAppLinks = (props: Props) => <Component {...useServices()} {...props} />;
|
||||
export const RedirectAppLinks: FC<{}> = ({ children }) => (
|
||||
<Component {...useServices()}>{children}</Component>
|
||||
);
|
||||
|
|
|
@ -25,8 +25,9 @@ const RedirectAppLinksContext = React.createContext<Services | null>(null);
|
|||
* Contextual services Provider.
|
||||
*/
|
||||
export const RedirectAppLinksProvider: FC<Services> = ({ children, ...services }) => {
|
||||
const { navigateToUrl, currentAppId } = services;
|
||||
return (
|
||||
<RedirectAppLinksContext.Provider value={{ ...services }}>
|
||||
<RedirectAppLinksContext.Provider value={{ navigateToUrl, currentAppId }}>
|
||||
{children}
|
||||
</RedirectAppLinksContext.Provider>
|
||||
);
|
||||
|
@ -35,7 +36,7 @@ export const RedirectAppLinksProvider: FC<Services> = ({ children, ...services }
|
|||
/**
|
||||
* Kibana-specific contextual services to be adapted for this component.
|
||||
*/
|
||||
export interface KibanaServices {
|
||||
export interface KibanaDependencies {
|
||||
coreStart: {
|
||||
application: {
|
||||
currentAppId$: Observable<string | undefined>;
|
||||
|
@ -47,7 +48,7 @@ export interface KibanaServices {
|
|||
/**
|
||||
* Kibana-specific contextual services Provider.
|
||||
*/
|
||||
export const RedirectAppLinksKibanaProvider: FC<KibanaServices> = ({ children, coreStart }) => {
|
||||
export const RedirectAppLinksKibanaProvider: FC<KibanaDependencies> = ({ children, coreStart }) => {
|
||||
const { navigateToUrl, currentAppId$ } = coreStart.application;
|
||||
const currentAppId = useObservable(currentAppId$, undefined);
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ import mdx from '../README.mdx';
|
|||
import { Params, getStoryArgTypes, getStoryServices } from './mocks';
|
||||
|
||||
export default {
|
||||
title: 'No Data/Analytics',
|
||||
title: 'No Data/Analytics Page',
|
||||
description: 'An Analytics-specific version of KibanaNoDataPage.',
|
||||
parameters: {
|
||||
docs: {
|
||||
|
|
|
@ -16,7 +16,7 @@ import { KibanaNoDataPageProvider } from './services';
|
|||
import mdx from '../README.mdx';
|
||||
|
||||
export default {
|
||||
title: 'No Data/Kibana',
|
||||
title: 'No Data/Kibana Page',
|
||||
description: 'A component to display when there is no data available',
|
||||
parameters: {
|
||||
docs: {
|
||||
|
|
|
@ -14,6 +14,12 @@ import {
|
|||
getNoDataViewsPromptMockServices,
|
||||
} from '@kbn/shared-ux-prompt-no-data-views';
|
||||
|
||||
import {
|
||||
getNoDataCardMockServices,
|
||||
getNoDataCardStoryArgTypes,
|
||||
getNoDataCardStoryServices,
|
||||
} from '@kbn/shared-ux-card-no-data';
|
||||
|
||||
import { KibanaNoDataPageServices } from './services';
|
||||
|
||||
// TODO: clintandrewhall - this looks (and is) a bit complicated because the No Data View
|
||||
|
@ -32,6 +38,8 @@ export const getStoryServices = (params: StoryParams) => {
|
|||
const { canCreateNewDataView, dataViewsDocLink, openDataViewEditor } =
|
||||
getNoDataViewsPromptStorybookServices(params);
|
||||
|
||||
const { addBasePath, canAccessFleet } = getNoDataCardStoryServices(params);
|
||||
|
||||
// Workaround to leverage the services package.
|
||||
const { application, data, docLinks, editors, http, permissions, platform } =
|
||||
servicesFactory(params);
|
||||
|
@ -47,6 +55,8 @@ export const getStoryServices = (params: StoryParams) => {
|
|||
canCreateNewDataView,
|
||||
dataViewsDocLink,
|
||||
openDataViewEditor,
|
||||
addBasePath,
|
||||
canAccessFleet,
|
||||
};
|
||||
|
||||
return services;
|
||||
|
@ -75,6 +85,7 @@ export const getStoryArgTypes = () => ({
|
|||
defaultValue: false,
|
||||
},
|
||||
...getNoDataViewsPromptStoryArgTypes(),
|
||||
...getNoDataCardStoryArgTypes(),
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -84,6 +95,8 @@ export const getMockServices = (params?: MockServicesFactoryParams) => {
|
|||
const { canCreateNewDataView, dataViewsDocLink, openDataViewEditor } =
|
||||
getNoDataViewsPromptMockServices();
|
||||
|
||||
const { addBasePath, canAccessFleet } = getNoDataCardMockServices();
|
||||
|
||||
const { application, data, docLinks, editors, http, permissions, platform } =
|
||||
mockServicesFactory(params);
|
||||
|
||||
|
@ -98,6 +111,8 @@ export const getMockServices = (params?: MockServicesFactoryParams) => {
|
|||
canCreateNewDataView,
|
||||
dataViewsDocLink,
|
||||
openDataViewEditor,
|
||||
addBasePath,
|
||||
canAccessFleet,
|
||||
};
|
||||
|
||||
return services;
|
||||
|
|
|
@ -13,6 +13,8 @@ import {
|
|||
NoDataViewsPromptKibanaProvider,
|
||||
} from '@kbn/shared-ux-prompt-no-data-views';
|
||||
|
||||
import { NoDataCardProvider, NoDataCardKibanaProvider } from '@kbn/shared-ux-card-no-data';
|
||||
|
||||
import { LegacyServicesProvider, getLegacyServices } from './legacy_services';
|
||||
|
||||
/**
|
||||
|
@ -85,7 +87,9 @@ export const KibanaNoDataPageProvider: FC<KibanaNoDataPageServices> = ({
|
|||
}) => (
|
||||
<KibanaNoDataPageContext.Provider value={services}>
|
||||
<NoDataViewsPromptProvider {...services}>
|
||||
<LegacyServicesProvider {...getLegacyServices(services)}>{children}</LegacyServicesProvider>
|
||||
<NoDataCardProvider {...services}>
|
||||
<LegacyServicesProvider {...getLegacyServices(services)}>{children}</LegacyServicesProvider>
|
||||
</NoDataCardProvider>
|
||||
</NoDataViewsPromptProvider>
|
||||
</KibanaNoDataPageContext.Provider>
|
||||
);
|
||||
|
@ -159,7 +163,9 @@ export const KibanaNoDataPageKibanaProvider: FC<KibanaNoDataPageKibanaDependenci
|
|||
return (
|
||||
<KibanaNoDataPageContext.Provider value={value}>
|
||||
<NoDataViewsPromptKibanaProvider {...dependencies}>
|
||||
<LegacyServicesProvider {...getLegacyServices(value)}>{children}</LegacyServicesProvider>
|
||||
<NoDataCardKibanaProvider {...dependencies}>
|
||||
<LegacyServicesProvider {...getLegacyServices(value)}>{children}</LegacyServicesProvider>
|
||||
</NoDataCardKibanaProvider>
|
||||
</NoDataViewsPromptKibanaProvider>
|
||||
</KibanaNoDataPageContext.Provider>
|
||||
);
|
||||
|
|
|
@ -5336,11 +5336,10 @@
|
|||
"share.urlService.redirect.RedirectManager.missingParamLocator": "ID du localisateur non spécifié. Spécifiez le paramètre de recherche \"l\" dans l'URL ; ce devrait être un ID de localisateur existant.",
|
||||
"share.urlService.redirect.RedirectManager.missingParamParams": "Paramètres du localisateur non spécifiés. Spécifiez le paramètre de recherche \"p\" dans l'URL ; ce devrait être un objet sérialisé JSON des paramètres du localisateur.",
|
||||
"share.urlService.redirect.RedirectManager.missingParamVersion": "Version des paramètres du localisateur non spécifiée. Spécifiez le paramètre de recherche \"v\" dans l'URL ; ce devrait être la version de Kibana au moment de la génération des paramètres du localisateur.",
|
||||
"sharedUXComponents.noDataPage.elasticAgentCard.description": "Utilisez Elastic Agent pour collecter de manière simple et unifiée les données de vos machines.",
|
||||
"sharedUXComponents.noDataPage.elasticAgentCard.noPermission.description": "Cette intégration n'est pas encore activée. Votre administrateur possède les autorisations requises pour l’activer.",
|
||||
"sharedUXComponents.noDataPage.elasticAgentCard.noPermission.title": "Contactez votre administrateur",
|
||||
"sharedUXComponents.noDataPage.elasticAgentCard.title": "Ajouter Elastic Agent",
|
||||
"sharedUXComponents.pageTemplate.noDataCard.description": "Continuer sans collecter de données",
|
||||
"sharedUXPackages.card.noData.description": "Utilisez Elastic Agent pour collecter de manière simple et unifiée les données de vos machines.",
|
||||
"sharedUXPackages.card.noData.noPermission.description": "Cette intégration n'est pas encore activée. Votre administrateur possède les autorisations requises pour l’activer.",
|
||||
"sharedUXPackages.card.noData.noPermission.title": "Contactez votre administrateur",
|
||||
"sharedUXPackages.card.noData.title": "Ajouter Elastic Agent",
|
||||
"sharedUXPackages.buttonToolbar.buttons.addFromLibrary.libraryButtonLabel": "Ajouter depuis la bibliothèque",
|
||||
"sharedUXPackages.noDataViewsPrompt.learnMore": "Envie d'en savoir plus ?",
|
||||
"sharedUXPackages.noDataViewsPrompt.readDocumentation": "Lisez les documents",
|
||||
|
|
|
@ -5438,11 +5438,10 @@
|
|||
"share.urlService.redirect.RedirectManager.missingParamLocator": "ロケーターIDが指定されていません。URLで「l」検索パラメーターを指定します。これは既存のロケーターIDにしてください。",
|
||||
"share.urlService.redirect.RedirectManager.missingParamParams": "ロケーターパラメーターが指定されていません。URLで「p」検索パラメーターを指定します。これはロケーターパラメーターのJSONシリアル化オブジェクトにしてください。",
|
||||
"share.urlService.redirect.RedirectManager.missingParamVersion": "ロケーターパラメーターバージョンが指定されていません。URLで「v」検索パラメーターを指定します。これはロケーターパラメーターが生成されたときのKibanaのリリースバージョンです。",
|
||||
"sharedUXComponents.noDataPage.elasticAgentCard.description": "Elasticエージェントを使用すると、シンプルで統一された方法でコンピューターからデータを収集するできます。",
|
||||
"sharedUXComponents.noDataPage.elasticAgentCard.noPermission.description": "この統合はまだ有効ではありません。管理者にはオンにするために必要なアクセス権があります。",
|
||||
"sharedUXComponents.noDataPage.elasticAgentCard.noPermission.title": "管理者にお問い合わせください",
|
||||
"sharedUXComponents.noDataPage.elasticAgentCard.title": "Elasticエージェントの追加",
|
||||
"sharedUXComponents.pageTemplate.noDataCard.description": "データを収集せずに続行",
|
||||
"sharedUXPackages.card.noData.description": "Elasticエージェントを使用すると、シンプルで統一された方法でコンピューターからデータを収集するできます。",
|
||||
"sharedUXPackages.card.noData.noPermission.description": "この統合はまだ有効ではありません。管理者にはオンにするために必要なアクセス権があります。",
|
||||
"sharedUXPackages.card.noData.noPermission.title": "管理者にお問い合わせください",
|
||||
"sharedUXPackages.card.noData.title": "Elasticエージェントの追加",
|
||||
"sharedUXPackages.buttonToolbar.buttons.addFromLibrary.libraryButtonLabel": "ライブラリから追加",
|
||||
"sharedUXPackages.noDataViewsPrompt.learnMore": "詳細について",
|
||||
"sharedUXPackages.noDataViewsPrompt.readDocumentation": "<b>ドキュメント</b><b>を読む</b>",
|
||||
|
|
|
@ -5449,11 +5449,10 @@
|
|||
"share.urlService.redirect.RedirectManager.missingParamLocator": "未指定定位器 ID。在 URL 中指定“l”搜索参数,其应为现有定位器 ID。",
|
||||
"share.urlService.redirect.RedirectManager.missingParamParams": "定位器参数未指定。在 URL 中指定“p”搜索参数,其应为定位器参数的 JSON 序列化对象。",
|
||||
"share.urlService.redirect.RedirectManager.missingParamVersion": "定位器参数版本未指定。在 URL 中指定“v”搜索参数,其应为生成定位器参数时 Kibana 的版本。",
|
||||
"sharedUXComponents.noDataPage.elasticAgentCard.description": "使用 Elastic 代理以简单统一的方式从您的计算机中收集数据。",
|
||||
"sharedUXComponents.noDataPage.elasticAgentCard.noPermission.description": "尚未启用此集成。您的管理员具有打开它所需的权限。",
|
||||
"sharedUXComponents.noDataPage.elasticAgentCard.noPermission.title": "请联系您的管理员",
|
||||
"sharedUXComponents.noDataPage.elasticAgentCard.title": "添加 Elastic 代理",
|
||||
"sharedUXComponents.pageTemplate.noDataCard.description": "继续,而不收集数据",
|
||||
"sharedUXPackages.card.noData.description": "使用 Elastic 代理以简单统一的方式从您的计算机中收集数据。",
|
||||
"sharedUXPackages.card.noData.noPermission.description": "尚未启用此集成。您的管理员具有打开它所需的权限。",
|
||||
"sharedUXPackages.card.noData.noPermission.title": "请联系您的管理员",
|
||||
"sharedUXPackages.card.noData.title": "添加 Elastic 代理",
|
||||
"sharedUXPackages.buttonToolbar.buttons.addFromLibrary.libraryButtonLabel": "从库中添加",
|
||||
"sharedUXPackages.noDataViewsPrompt.learnMore": "希望了解详情?",
|
||||
"sharedUXPackages.noDataViewsPrompt.readDocumentation": "阅读文档",
|
||||
|
|
|
@ -3351,6 +3351,10 @@
|
|||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/shared-ux-card-no-data@link:bazel-bin/packages/shared-ux/card/no_data":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/shared-ux-components@link:bazel-bin/packages/kbn-shared-ux-components":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
@ -6722,6 +6726,10 @@
|
|||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@types/kbn__shared-ux-card-no-data@link:bazel-bin/packages/shared-ux/card/no_data/npm_module_types":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@types/kbn__shared-ux-components@link:bazel-bin/packages/kbn-shared-ux-components/npm_module_types":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue