[8.x] [ES|QL] Present ES|QL as an equal to data views on the "no data views" screen (#194077) (#195684)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[ES|QL] Present ES|QL as an equal to data views on the "no data
views" screen
(#194077)](https://github.com/elastic/kibana/pull/194077)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Tim
Sullivan","email":"tsullivan@users.noreply.github.com"},"sourceCommit":{"committedDate":"2024-10-09T21:44:30Z","message":"[ES|QL]
Present ES|QL as an equal to data views on the \"no data views\" screen
(#194077)\n\n## Summary\r\n\r\nResolves
https://github.com/elastic/kibana/issues/176291\r\n\r\n###
Screenshots\r\n\r\n#### Discover/Dashboard/Visualize\r\n<img
width=\"1517\"
alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/c8fb4faf-8d2d-4043-a757-6f092d1fceb3\">\r\n\r\n####
Stack Management > Data view\r\n<img width=\"1519\"
alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/bb3f28bd-0ce9-4c49-ace1-1be875e17ba3\">\r\n\r\n####
If User does not have privilege to create a Data View\r\n<img
width=\"1336\"
alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/bb3f4cbc-8213-41c0-bf09-bd47a9d264e4\">\r\n\r\n###
Checklist\r\n\r\nDelete any items that are not applicable to this
PR.\r\n\r\n- [x] Use a new SVG resource for the ES|QL illustration\r\n-
[x] Any text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n-
[x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [ ] [Flaky
Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\r\nused on any tests changed\r\n- [x] Any UI touched in this PR is
usable by keyboard only (learn more\r\nabout [keyboard
accessibility](https://webaim.org/techniques/keyboard/))\r\n- [ ] Any UI
touched in this PR does not create any new axe failures\r\n(run axe in
browser:\r\n[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),\r\n[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))\r\n-
[x] This renders correctly on smaller devices using a
responsive\r\nlayout. (You can test this [in
your\r\nbrowser](https://www.browserstack.com/guide/responsive-testing-on-local-server))\r\n-
[x] This was checked for
[cross-browser\r\ncompatibility](https://www.elastic.co/support/matrix#matrix_browsers)\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>\r\nCo-authored-by:
Liam Thompson
<32779855+leemthompo@users.noreply.github.com>\r\nCo-authored-by: Andrea
Del Rio
<delrio.andre@gmail.com>","sha":"56e1e68b307bc62445f04ae6f443f4854a308547","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","Team:SharedUX","v8.16.0","backport:version"],"title":"[ES|QL]
Present ES|QL as an equal to data views on the \"no data views\"
screen","number":194077,"url":"https://github.com/elastic/kibana/pull/194077","mergeCommit":{"message":"[ES|QL]
Present ES|QL as an equal to data views on the \"no data views\" screen
(#194077)\n\n## Summary\r\n\r\nResolves
https://github.com/elastic/kibana/issues/176291\r\n\r\n###
Screenshots\r\n\r\n#### Discover/Dashboard/Visualize\r\n<img
width=\"1517\"
alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/c8fb4faf-8d2d-4043-a757-6f092d1fceb3\">\r\n\r\n####
Stack Management > Data view\r\n<img width=\"1519\"
alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/bb3f28bd-0ce9-4c49-ace1-1be875e17ba3\">\r\n\r\n####
If User does not have privilege to create a Data View\r\n<img
width=\"1336\"
alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/bb3f4cbc-8213-41c0-bf09-bd47a9d264e4\">\r\n\r\n###
Checklist\r\n\r\nDelete any items that are not applicable to this
PR.\r\n\r\n- [x] Use a new SVG resource for the ES|QL illustration\r\n-
[x] Any text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n-
[x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [ ] [Flaky
Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\r\nused on any tests changed\r\n- [x] Any UI touched in this PR is
usable by keyboard only (learn more\r\nabout [keyboard
accessibility](https://webaim.org/techniques/keyboard/))\r\n- [ ] Any UI
touched in this PR does not create any new axe failures\r\n(run axe in
browser:\r\n[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),\r\n[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))\r\n-
[x] This renders correctly on smaller devices using a
responsive\r\nlayout. (You can test this [in
your\r\nbrowser](https://www.browserstack.com/guide/responsive-testing-on-local-server))\r\n-
[x] This was checked for
[cross-browser\r\ncompatibility](https://www.elastic.co/support/matrix#matrix_browsers)\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>\r\nCo-authored-by:
Liam Thompson
<32779855+leemthompo@users.noreply.github.com>\r\nCo-authored-by: Andrea
Del Rio
<delrio.andre@gmail.com>","sha":"56e1e68b307bc62445f04ae6f443f4854a308547"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/194077","number":194077,"mergeCommit":{"message":"[ES|QL]
Present ES|QL as an equal to data views on the \"no data views\" screen
(#194077)\n\n## Summary\r\n\r\nResolves
https://github.com/elastic/kibana/issues/176291\r\n\r\n###
Screenshots\r\n\r\n#### Discover/Dashboard/Visualize\r\n<img
width=\"1517\"
alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/c8fb4faf-8d2d-4043-a757-6f092d1fceb3\">\r\n\r\n####
Stack Management > Data view\r\n<img width=\"1519\"
alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/bb3f28bd-0ce9-4c49-ace1-1be875e17ba3\">\r\n\r\n####
If User does not have privilege to create a Data View\r\n<img
width=\"1336\"
alt=\"image\"\r\nsrc=\"https://github.com/user-attachments/assets/bb3f4cbc-8213-41c0-bf09-bd47a9d264e4\">\r\n\r\n###
Checklist\r\n\r\nDelete any items that are not applicable to this
PR.\r\n\r\n- [x] Use a new SVG resource for the ES|QL illustration\r\n-
[x] Any text added follows [EUI's
writing\r\nguidelines](https://elastic.github.io/eui/#/guidelines/writing),
uses\r\nsentence case text and includes
[i18n\r\nsupport](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)\r\n-
[x] [Unit or
functional\r\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\r\nwere
updated or added to match the most common scenarios\r\n- [ ] [Flaky
Test\r\nRunner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1)
was\r\nused on any tests changed\r\n- [x] Any UI touched in this PR is
usable by keyboard only (learn more\r\nabout [keyboard
accessibility](https://webaim.org/techniques/keyboard/))\r\n- [ ] Any UI
touched in this PR does not create any new axe failures\r\n(run axe in
browser:\r\n[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),\r\n[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))\r\n-
[x] This renders correctly on smaller devices using a
responsive\r\nlayout. (You can test this [in
your\r\nbrowser](https://www.browserstack.com/guide/responsive-testing-on-local-server))\r\n-
[x] This was checked for
[cross-browser\r\ncompatibility](https://www.elastic.co/support/matrix#matrix_browsers)\r\n\r\n---------\r\n\r\nCo-authored-by:
kibanamachine
<42973632+kibanamachine@users.noreply.github.com>\r\nCo-authored-by:
Liam Thompson
<32779855+leemthompo@users.noreply.github.com>\r\nCo-authored-by: Andrea
Del Rio
<delrio.andre@gmail.com>","sha":"56e1e68b307bc62445f04ae6f443f4854a308547"}},{"branch":"8.x","label":"v8.16.0","branchLabelMappingKey":"^v8.16.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Tim Sullivan <tsullivan@users.noreply.github.com>
This commit is contained in:
Kibana Machine 2024-10-10 10:50:53 +11:00 committed by GitHub
parent ac9540e5f0
commit ce302a91fa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 2133 additions and 235 deletions

View file

@ -22,12 +22,14 @@ import { getHasApiKeys$ } from '../lib/get_has_api_keys';
export interface Props {
/** Handler for successfully creating a new data view. */
onDataViewCreated: (dataView: unknown) => void;
/** Handler for when try ES|QL is clicked and user has been navigated to try ES|QL in discover. */
onESQLNavigationComplete?: () => void;
/** if set to true allows creation of an ad-hoc dataview from data view editor */
allowAdHocDataView?: boolean;
/** if the kibana instance is customly branded */
showPlainSpinner: boolean;
/** If the cluster has data, this handler allows the user to try ES|QL */
onTryESQL?: () => void;
/** Handler for when try ES|QL is clicked and user has been navigated to try ES|QL in discover. */
onESQLNavigationComplete?: () => void;
}
type AnalyticsNoDataPageProps = Props &
@ -119,9 +121,10 @@ const flavors: {
*/
export const AnalyticsNoDataPage: React.FC<AnalyticsNoDataPageProps> = ({
onDataViewCreated,
onESQLNavigationComplete,
allowAdHocDataView,
showPlainSpinner,
onTryESQL,
onESQLNavigationComplete,
...services
}) => {
const { prependBasePath, kibanaGuideDocLink, getHttp: get, pageFlavor } = services;
@ -138,8 +141,9 @@ export const AnalyticsNoDataPage: React.FC<AnalyticsNoDataPageProps> = ({
{...{
noDataConfig,
onDataViewCreated,
onESQLNavigationComplete,
allowAdHocDataView,
onTryESQL,
onESQLNavigationComplete,
showPlainSpinner,
}}
/>

View file

@ -29,8 +29,8 @@ export default {
export const Analytics = (params: AnalyticsNoDataPageStorybookParams) => {
return (
<AnalyticsNoDataPageProvider {...mock.getServices(params)}>
<Component {...mock.getProps()} />
<AnalyticsNoDataPageProvider {...mock.getProps(params)} {...mock.getServices(params)}>
<Component {...mock.getProps(params)} />
</AnalyticsNoDataPageProvider>
);
};

View file

@ -14,6 +14,7 @@ import {
getAnalyticsNoDataPageServicesMock,
getAnalyticsNoDataPageServicesMockWithCustomBranding,
} from '@kbn/shared-ux-page-analytics-no-data-mocks';
import { NoDataViewsPrompt } from '@kbn/shared-ux-prompt-no-data-views';
import { AnalyticsNoDataPageProvider } from './services';
import { AnalyticsNoDataPage as Component } from './analytics_no_data_page.component';
@ -29,28 +30,86 @@ describe('AnalyticsNoDataPage', () => {
jest.resetAllMocks();
});
it('renders correctly', async () => {
const component = mountWithIntl(
<AnalyticsNoDataPageProvider {...services}>
<AnalyticsNoDataPage onDataViewCreated={onDataViewCreated} allowAdHocDataView={true} />
</AnalyticsNoDataPageProvider>
);
describe('loading state', () => {
it('renders correctly', async () => {
const component = mountWithIntl(
<AnalyticsNoDataPageProvider {...services}>
<AnalyticsNoDataPage onDataViewCreated={onDataViewCreated} allowAdHocDataView={true} />
</AnalyticsNoDataPageProvider>
);
await act(() => new Promise(setImmediate));
await act(() => new Promise(setImmediate));
expect(component.find(Component).length).toBe(1);
expect(component.find(Component).props().onDataViewCreated).toBe(onDataViewCreated);
expect(component.find(Component).props().allowAdHocDataView).toBe(true);
expect(component.find(Component).length).toBe(1);
expect(component.find(Component).props().onDataViewCreated).toBe(onDataViewCreated);
expect(component.find(Component).props().allowAdHocDataView).toBe(true);
});
it('passes correct boolean value to showPlainSpinner', async () => {
const component = mountWithIntl(
<AnalyticsNoDataPageProvider {...servicesWithCustomBranding}>
<AnalyticsNoDataPage onDataViewCreated={onDataViewCreated} allowAdHocDataView={true} />
</AnalyticsNoDataPageProvider>
);
await act(async () => {
component.update();
});
expect(component.find(Component).length).toBe(1);
expect(component.find(Component).props().showPlainSpinner).toBe(true);
});
});
it('passes correct boolean value to showPlainSpinner', () => {
const component = mountWithIntl(
<AnalyticsNoDataPageProvider {...servicesWithCustomBranding}>
<AnalyticsNoDataPage onDataViewCreated={onDataViewCreated} allowAdHocDataView={true} />
</AnalyticsNoDataPageProvider>
);
describe('with ES data', () => {
jest.spyOn(services, 'hasESData').mockResolvedValue(true);
jest.spyOn(services, 'hasUserDataView').mockResolvedValue(false);
expect(component.find(Component).length).toBe(1);
expect(component.find(Component).props().showPlainSpinner).toBe(true);
it('renders the prompt to create a data view', async () => {
const onTryESQL = jest.fn();
await act(async () => {
const component = mountWithIntl(
<AnalyticsNoDataPageProvider {...services}>
<AnalyticsNoDataPage
onDataViewCreated={onDataViewCreated}
allowAdHocDataView={true}
onTryESQL={onTryESQL}
/>
</AnalyticsNoDataPageProvider>
);
await new Promise(setImmediate);
component.update();
expect(component.find(Component).length).toBe(1);
expect(component.find(NoDataViewsPrompt).length).toBe(1);
});
});
it('renders the prompt to create a data view with a custom onTryESQL action', async () => {
const onTryESQL = jest.fn();
await act(async () => {
const component = mountWithIntl(
<AnalyticsNoDataPageProvider {...services}>
<AnalyticsNoDataPage
onDataViewCreated={onDataViewCreated}
allowAdHocDataView={true}
onTryESQL={onTryESQL}
/>
</AnalyticsNoDataPageProvider>
);
await new Promise(setImmediate);
component.update();
const tryESQLLink = component.find('button[data-test-subj="tryESQLLink"]');
expect(tryESQLLink.length).toBe(1);
tryESQLLink.simulate('click');
expect(onTryESQL).toHaveBeenCalled();
});
});
});
});

View file

@ -20,8 +20,9 @@ import { AnalyticsNoDataPage as Component } from './analytics_no_data_page.compo
*/
export const AnalyticsNoDataPage = ({
onDataViewCreated,
onESQLNavigationComplete,
allowAdHocDataView,
onTryESQL,
onESQLNavigationComplete,
}: AnalyticsNoDataPageProps) => {
const { customBranding, ...services } = useServices();
const showPlainSpinner = useObservable(customBranding.hasCustomBranding$) ?? false;
@ -33,6 +34,7 @@ export const AnalyticsNoDataPage = ({
allowAdHocDataView={allowAdHocDataView}
onDataViewCreated={onDataViewCreated}
onESQLNavigationComplete={onESQLNavigationComplete}
onTryESQL={onTryESQL}
/>
);
};

View file

@ -23,6 +23,7 @@
"@kbn/i18n-react",
"@kbn/core-http-browser",
"@kbn/core-http-browser-mocks",
"@kbn/shared-ux-prompt-no-data-views",
],
"exclude": [
"target/**/*",

View file

@ -18,9 +18,14 @@ import type {
} from '@kbn/shared-ux-page-analytics-no-data-types';
import { of } from 'rxjs';
interface PropArguments {
useCustomOnTryESQL: boolean;
}
type ServiceArguments = Pick<AnalyticsNoDataPageServices, 'kibanaGuideDocLink' | 'customBranding'>;
export type Params = ArgumentParams<{}, ServiceArguments> & KibanaNoDataPageStorybookParams;
export type Params = ArgumentParams<PropArguments, ServiceArguments> &
KibanaNoDataPageStorybookParams;
const kibanaNoDataMock = new KibanaNoDataPageStorybookMock();
@ -30,7 +35,13 @@ export class StorybookMock extends AbstractStorybookMock<
{},
ServiceArguments
> {
propArguments = {};
propArguments = {
// requires hasESData to be toggled to true
useCustomOnTryESQL: {
control: 'boolean',
defaultValue: false,
},
};
serviceArguments = {
kibanaGuideDocLink: {
control: 'text',
@ -59,9 +70,10 @@ export class StorybookMock extends AbstractStorybookMock<
};
}
getProps() {
getProps(params: Params) {
return {
onDataViewCreated: action('onDataViewCreated'),
onTryESQL: params.useCustomOnTryESQL ? action('onTryESQL-from-props') : undefined,
};
}
}

View file

@ -70,6 +70,8 @@ export interface AnalyticsNoDataPageProps {
onDataViewCreated: (dataView: unknown) => void;
/** if set to true allows creation of an ad-hoc data view from data view editor */
allowAdHocDataView?: boolean;
/** If the cluster has data, this handler allows the user to try ES|QL */
onTryESQL?: () => void;
/** Handler for when try ES|QL is clicked and user has been navigated to try ES|QL in discover. */
onESQLNavigationComplete?: () => void;
}

View file

@ -20,9 +20,10 @@ import { useServices } from './services';
*/
export const KibanaNoDataPage = ({
onDataViewCreated,
onESQLNavigationComplete,
noDataConfig,
allowAdHocDataView,
onTryESQL,
onESQLNavigationComplete,
showPlainSpinner,
}: KibanaNoDataPageProps) => {
// These hooks are temporary, until this component is moved to a package.
@ -58,8 +59,9 @@ export const KibanaNoDataPage = ({
return (
<NoDataViewsPrompt
onDataViewCreated={onDataViewCreated}
onESQLNavigationComplete={onESQLNavigationComplete}
allowAdHocDataView={allowAdHocDataView}
onTryESQL={onTryESQL}
onESQLNavigationComplete={onESQLNavigationComplete}
/>
);
}

View file

@ -60,6 +60,8 @@ export interface KibanaNoDataPageProps {
allowAdHocDataView?: boolean;
/** Set to true if the kibana is customly branded */
showPlainSpinner: boolean;
/** If the cluster has data, this handler allows the user to try ES|QL */
onTryESQL?: () => void;
/** Handler for when try ES|QL is clicked and user has been navigated to try ES|QL in discover. */
onESQLNavigationComplete?: () => void;
}

View file

@ -1,76 +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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { EuiButton, EuiLink, EuiSpacer, EuiText } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import React from 'react';
interface NoDataButtonProps {
onClickCreate: (() => void) | undefined;
canCreateNewDataView: boolean;
onTryESQL?: () => void;
esqlDocLink?: string;
}
const createDataViewText = i18n.translate('sharedUXPackages.noDataViewsPrompt.addDataViewText', {
defaultMessage: 'Create data view',
});
export const NoDataButtonLink = ({
onClickCreate,
canCreateNewDataView,
onTryESQL,
esqlDocLink,
}: NoDataButtonProps) => {
if (!onTryESQL && !canCreateNewDataView) {
return null;
}
return (
<>
{canCreateNewDataView && (
<EuiButton
onClick={onClickCreate}
iconType="plusInCircle"
fill={true}
data-test-subj="createDataViewButton"
>
{createDataViewText}
</EuiButton>
)}
{canCreateNewDataView && onTryESQL && <EuiSpacer />}
{onTryESQL && (
<EuiText size="xs" color={'subdued'}>
<FormattedMessage
id="sharedUXPackages.no_data_views.esqlMessage"
defaultMessage="Alternatively, you can query your data directly using ES|QL. {docsLink}"
values={{
docsLink: esqlDocLink && (
<EuiLink href={esqlDocLink} target="_blank">
<FormattedMessage
id="sharedUXPackages.no_data_views.esqlDocsLink"
defaultMessage="Learn more."
/>
</EuiLink>
),
}}
/>
<EuiSpacer size={'s'} />
<EuiButton color="success" onClick={onTryESQL} size="s" data-test-subj="tryESQLLink">
<FormattedMessage
id="sharedUXPackages.no_data_views.esqlButtonLabel"
defaultMessage="Language: ES|QL"
/>
</EuiButton>
</EuiText>
)}
</>
);
};

View file

@ -26,5 +26,14 @@ export const DataViewIllustration = () => {
}
`;
return <img src={svg} css={css} alt="Data view illustration" />;
return (
<img
src={svg}
css={css}
alt="Data view illustration"
data-test-subj="DataViewIllustration"
width="110"
height="100"
/>
);
};

View file

@ -13,9 +13,10 @@ import { FormattedMessage } from '@kbn/i18n-react';
interface Props {
href: string;
['data-test-subj']?: string;
}
export function DocumentationLink({ href }: Props) {
export function DocumentationLink({ href, ['data-test-subj']: dataTestSubj }: Props) {
return (
<dl>
<EuiTitle size="xxs">
@ -28,7 +29,7 @@ export function DocumentationLink({ href }: Props) {
</EuiTitle>
&emsp;
<dd className="eui-displayInline">
<EuiLink href={href} target="_blank" external>
<EuiLink href={href} target="_blank" data-test-subj={dataTestSubj} external>
<FormattedMessage
id="sharedUXPackages.noDataViewsPrompt.readDocumentation"
defaultMessage="Read the docs"

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 622 KiB

View file

@ -0,0 +1,24 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import React from 'react';
import png from './esql_illustration.svg';
export const EsqlIllustration = () => {
return (
<img
src={png}
alt="ES|QL illustration"
data-test-subj="EsqlIllustration"
width="140"
height="92"
/>
);
};

View file

@ -9,7 +9,7 @@
import React from 'react';
import { mountWithIntl } from '@kbn/test-jest-helpers';
import { EuiButton, EuiEmptyPrompt } from '@elastic/eui';
import { EuiButton, EuiCard } from '@elastic/eui';
import { NoDataViewsPrompt } from './no_data_views.component';
import { DocumentationLink } from './documentation_link';
@ -19,36 +19,64 @@ describe('<NoDataViewsPromptComponent />', () => {
<NoDataViewsPrompt
onClickCreate={jest.fn()}
canCreateNewDataView={true}
dataViewsDocLink={'dummy'}
dataViewsDocLink="doc-link-data-views"
esqlDocLink="doc-link-esql"
onTryESQL={jest.fn()}
/>
);
expect(component.find(EuiEmptyPrompt).length).toBe(1);
expect(component.find(EuiButton).length).toBe(1);
expect(component.find(DocumentationLink).length).toBe(1);
expect(component.find(EuiCard).length).toBe(2);
expect(component.find(EuiButton).length).toBe(2);
expect(component.find(DocumentationLink).length).toBe(2);
expect(component.find('EuiButton[data-test-subj="createDataViewButton"]').length).toBe(1);
expect(component.find('DocumentationLink[data-test-subj="docLinkDataViews"]').length).toBe(1);
expect(component.find('EuiButton[data-test-subj="tryESQLLink"]').length).toBe(1);
expect(component.find('DocumentationLink[data-test-subj="docLinkEsql"]').length).toBe(1);
});
test('does not render button if canCreateNewDataViews is false', () => {
test('does not render "Create data view" button if canCreateNewDataViews is false', () => {
const component = mountWithIntl(<NoDataViewsPrompt canCreateNewDataView={false} />);
expect(component.find(EuiButton).length).toBe(0);
expect(component.find('EuiButton[data-test-subj="createDataViewButton"]').length).toBe(0);
});
test('does not documentation link if linkToDocumentation is not provided', () => {
test('does not render documentation links if links to documentation are not provided', () => {
const component = mountWithIntl(
<NoDataViewsPrompt onClickCreate={jest.fn()} canCreateNewDataView={true} />
);
expect(component.find(DocumentationLink).length).toBe(0);
expect(component.find('DocumentationLink[data-test-subj="docLinkDataViews"]').length).toBe(0);
expect(component.find('DocumentationLink[data-test-subj="docLinkEsql"]').length).toBe(0);
});
test('onClickCreate', () => {
const onClickCreate = jest.fn();
const component = mountWithIntl(
<NoDataViewsPrompt canCreateNewDataView={true} onClickCreate={onClickCreate} />
<NoDataViewsPrompt
canCreateNewDataView={true}
onClickCreate={onClickCreate}
dataViewsDocLink="doc-link/data-view"
/>
);
component.find('button').simulate('click');
component.find('button[data-test-subj="createDataViewButton"]').simulate('click');
expect(onClickCreate).toHaveBeenCalledTimes(1);
});
test('onClickTryEsql', () => {
const onClickTryEsql = jest.fn();
const component = mountWithIntl(
<NoDataViewsPrompt
canCreateNewDataView={false}
onTryESQL={onClickTryEsql}
esqlDocLink="doc-link/esql"
/>
);
component.find('button[data-test-subj="tryESQLLink"]').simulate('click');
expect(onClickTryEsql).toHaveBeenCalledTimes(1);
});
});

View file

@ -7,19 +7,159 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import React from 'react';
import { css } from '@emotion/react';
import React from 'react';
import { EuiEmptyPrompt, EuiPanel } from '@elastic/eui';
import {
EuiButton,
EuiCard,
EuiFlexGroup,
EuiFlexItem,
EuiHorizontalRule,
EuiSpacer,
EuiText,
EuiTextAlign,
EuiToolTip,
useEuiPaddingCSS,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { withSuspense } from '@kbn/shared-ux-utility';
import { NoDataViewsPromptComponentProps } from '@kbn/shared-ux-prompt-no-data-views-types';
import { DocumentationLink } from './documentation_link';
import { NoDataButtonLink } from './actions';
import { DataViewIllustration } from './data_view_illustration';
import { EsqlIllustration } from './esql_illustration';
// Using raw value because it is content dependent
const MAX_WIDTH = 830;
// max width value to use in pixels
const MAX_WIDTH = 770;
const PromptAddDataViews = ({
onClickCreate,
canCreateNewDataView,
dataViewsDocLink,
emptyPromptColor,
}: Pick<
NoDataViewsPromptComponentProps,
'onClickCreate' | 'canCreateNewDataView' | 'dataViewsDocLink' | 'emptyPromptColor'
>) => {
const icon = <DataViewIllustration />;
const title = (
<FormattedMessage
id="sharedUXPackages.noDataViewsPrompt.createDataView"
defaultMessage="Create a data view"
/>
);
const description = (
<>
{canCreateNewDataView ? (
<FormattedMessage
id="sharedUXPackages.noDataViewsPrompt.dataViewExplanation"
defaultMessage="Data views identify the Elasticsearch data you want to explore. You can point data views to one or more data streams, indices, and index aliases, such as your log data from yesterday, or all indices that contain your log data."
/>
) : (
<FormattedMessage
id="sharedUXPackages.noDataViewsPrompt.noPermission.dataViewExplanation"
defaultMessage="Data views identify the Elasticsearch data that you want to explore. To create data views, ask your administrator for the required permissions."
/>
)}
</>
);
const footer = dataViewsDocLink ? (
<>
{canCreateNewDataView ? (
<EuiButton onClick={onClickCreate} fill={true} data-test-subj="createDataViewButton">
<FormattedMessage
id="sharedUXPackages.noDataViewsPrompt.addDataViewText"
defaultMessage="Create data view"
/>
</EuiButton>
) : (
<EuiToolTip
position="right"
content={
<FormattedMessage
id="sharedUXPackages.noDataViewsPrompt.addDataViewTooltipNoPrivilege"
defaultMessage="Ask your administrator for the permissions required to create a data view."
/>
}
>
<EuiButton disabled data-test-subj="createDataViewButton">
<FormattedMessage
id="sharedUXPackages.noDataViewsPrompt.addDataViewTextNoPrivilege"
defaultMessage="Create data view"
/>
</EuiButton>
</EuiToolTip>
)}
<EuiHorizontalRule />
<DocumentationLink href={dataViewsDocLink} data-test-subj="docLinkDataViews" />
</>
) : undefined;
return (
<EuiCard
hasBorder={true}
data-test-subj="noDataViewsPromptCreateDataView"
display={emptyPromptColor}
{...{ icon, title, description, footer }}
/>
);
};
const PromptTryEsql = ({
onTryESQL,
esqlDocLink,
emptyPromptColor,
}: Pick<
NoDataViewsPromptComponentProps,
'onClickCreate' | 'onTryESQL' | 'esqlDocLink' | 'emptyPromptColor'
>) => {
if (!onTryESQL) {
// we need to handle the case where the Try ES|QL click handler is not set because
// onTryESQL is set via a useEffect that has asynchronous dependencies
return null;
}
const icon = <EsqlIllustration />;
const title = (
<FormattedMessage
id="sharedUXPackages.noDataViewsPrompt.esqlPanel.title"
defaultMessage="Query your data with ES|QL"
/>
);
const description = (
<FormattedMessage
id="sharedUXPackages.noDataViewsPrompt.esqlExplanation"
defaultMessage="ES|QL is a next-generation piped query language and compute engine developed by Elastic for filtering, transforming, and analyzing data. ES|QL helps streamline your workflows for fast, efficient data processing."
/>
);
const footer = (
<>
<EuiButton onClick={onTryESQL} fill={true} data-test-subj="tryESQLLink">
<FormattedMessage
id="sharedUXPackages.noDataViewsPrompt.tryEsqlText"
defaultMessage="Try ES|QL"
/>
</EuiButton>
<EuiHorizontalRule />
{esqlDocLink && <DocumentationLink href={esqlDocLink} data-test-subj="docLinkEsql" />}
</>
);
return (
<EuiCard
hasBorder={true}
data-test-subj="noDataViewsPromptTryEsql"
display={emptyPromptColor}
{...{ icon, title, description, footer }}
/>
);
};
/**
* A presentational component that is shown in cases when there are no data views created yet.
@ -32,70 +172,57 @@ export const NoDataViewsPrompt = ({
esqlDocLink,
emptyPromptColor = 'plain',
}: NoDataViewsPromptComponentProps) => {
const title = canCreateNewDataView ? (
<h2>
<FormattedMessage
id="sharedUXPackages.noDataViewsPrompt.youHaveData"
defaultMessage="You have data in Elasticsearch."
/>
<br />
<FormattedMessage
id="sharedUXPackages.noDataViewsPrompt.nowCreate"
defaultMessage="Now, create a data view."
/>
</h2>
) : (
<h2>
<FormattedMessage
id="sharedUXPackages.noDataViewsPrompt.noPermission.title"
defaultMessage="You need permission to create data views"
/>
</h2>
);
const body = canCreateNewDataView ? (
<p>
<FormattedMessage
id="sharedUXPackages.noDataViewsPrompt.dataViewExplanation"
defaultMessage="Data views identify the Elasticsearch data you want to explore. You can point data views to one or more data streams, indices, and index aliases, such as your log data from yesterday, or all indices that contain your log data."
/>
</p>
) : (
<p>
<FormattedMessage
id="sharedUXPackages.noDataViewsPrompt.noPermission.dataViewExplanation"
defaultMessage="Data views identify the Elasticsearch data that you want to explore. To create data views, ask your administrator for the required permissions."
/>
</p>
);
const footer = dataViewsDocLink ? <DocumentationLink href={dataViewsDocLink} /> : undefined;
// Load this illustration lazily
const Illustration = withSuspense(
React.lazy(() =>
import('./data_view_illustration').then(({ DataViewIllustration }) => {
return { default: DataViewIllustration };
})
),
<EuiPanel color="subdued" style={{ width: 226, height: 206 }} />
);
const icon = <Illustration />;
const actions = (
<NoDataButtonLink {...{ onClickCreate, canCreateNewDataView, onTryESQL, esqlDocLink }} />
);
const cssStyles = [
css`
max-width: ${MAX_WIDTH}px;
`,
useEuiPaddingCSS('top').m,
useEuiPaddingCSS('right').m,
useEuiPaddingCSS('left').m,
];
return (
<EuiEmptyPrompt
<EuiFlexGroup
alignItems="center"
justifyContent="spaceEvenly"
data-test-subj="noDataViewsPrompt"
layout="horizontal"
css={css`
max-width: ${MAX_WIDTH}px !important; // Necessary to override EuiEmptyPrompt to fit content
flex-grow: 0;
`}
color={emptyPromptColor}
{...{ actions, icon, title, body, footer }}
/>
>
<EuiFlexItem css={cssStyles}>
<EuiText>
<EuiTextAlign textAlign="center">
<h2>
<FormattedMessage
id="sharedUXPackages.noDataViewsPrompt.youHaveData"
defaultMessage="How do you want to explore your Elasticsearch data?"
/>
</h2>
</EuiTextAlign>
</EuiText>
<EuiSpacer size="xl" />
<EuiFlexGroup>
<EuiFlexItem>
<PromptAddDataViews
{...{
onClickCreate,
canCreateNewDataView,
dataViewsDocLink,
emptyPromptColor,
}}
/>
</EuiFlexItem>
<EuiFlexItem>
<PromptTryEsql
{...{
onTryESQL,
esqlDocLink,
emptyPromptColor,
}}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
);
};

View file

@ -27,12 +27,15 @@ type CloseDataViewEditorFn = ReturnType<NoDataViewsPromptServices['openDataViewE
*/
export const NoDataViewsPrompt = ({
onDataViewCreated,
onESQLNavigationComplete,
allowAdHocDataView = false,
onTryESQL: onTryESQLProp,
onESQLNavigationComplete,
}: NoDataViewsPromptProps) => {
const { canCreateNewDataView, openDataViewEditor, dataViewsDocLink, onTryESQL, esqlDocLink } =
const { canCreateNewDataView, openDataViewEditor, dataViewsDocLink, esqlDocLink, ...services } =
useServices();
const onTryESQL = onTryESQLProp ?? services.onTryESQL;
const closeDataViewEditor = useRef<CloseDataViewEditorFn>();
useEffect(() => {

View file

@ -16,8 +16,6 @@
],
"kbn_references": [
"@kbn/i18n-react",
"@kbn/i18n",
"@kbn/shared-ux-utility",
"@kbn/test-jest-helpers",
"@kbn/shared-ux-prompt-no-data-views-types",
"@kbn/shared-ux-prompt-no-data-views-mocks",

View file

@ -34,17 +34,19 @@ export class StorybookMock extends AbstractStorybookMock<
defaultValue: true,
},
dataViewsDocLink: {
options: ['some/link', undefined],
control: { type: 'radio' },
},
esqlDocLink: {
options: ['some/link', undefined],
options: ['dataviews/link', undefined],
control: { type: 'radio' },
defaultValue: 'dataviews/link',
},
canTryEsql: {
control: 'boolean',
defaultValue: true,
},
esqlDocLink: {
options: ['esql/link', undefined],
control: { type: 'radio' },
defaultValue: 'esql/link',
},
};
dependencies = [];
@ -59,7 +61,7 @@ export class StorybookMock extends AbstractStorybookMock<
let onTryESQL;
if (canTryEsql !== false) {
onTryESQL = action('onTryESQL');
onTryESQL = action('onTryESQL-from-services');
}
return {

View file

@ -42,7 +42,7 @@ export interface NoDataViewsPromptServices {
openDataViewEditor: (options: DataViewEditorOptions) => () => void;
/** A link to information about Data Views in Kibana */
dataViewsDocLink: string;
/** Get a handler for trying ES|QL */
/** If the cluster has data, this handler allows the user to try ES|QL */
onTryESQL: (() => void) | undefined;
/** A link to the documentation for ES|QL */
esqlDocLink: string;
@ -92,7 +92,7 @@ export interface NoDataViewsPromptComponentProps {
emptyPromptColor?: EuiEmptyPromptProps['color'];
/** Click handler for create button. **/
onClickCreate?: () => void;
/** Handler for someone wanting to try ES|QL. */
/** If the cluster has data, this handler allows the user to try ES|QL */
onTryESQL?: () => void;
/** Link to documentation on ES|QL. */
esqlDocLink?: string;
@ -104,6 +104,10 @@ export interface NoDataViewsPromptProps {
allowAdHocDataView?: boolean;
/** Handler for successfully creating a new data view. */
onDataViewCreated: (dataView: unknown) => void;
/** If the cluster has data, this handler allows the user to try ES|QL */
onTryESQL?: () => void;
/** Handler for when try ES|QL is clicked and user has been navigated to try ES|QL in discover. */
onESQLNavigationComplete?: () => void;
/** Empty prompt color **/
emptyPromptColor?: PanelColor;
}

View file

@ -20,6 +20,7 @@
],
"optionalPlugins": [
"noDataPage",
"share",
"spaces"
],
"requiredBundles": [

View file

@ -26,7 +26,7 @@ import { RouteComponentProps, useLocation, withRouter } from 'react-router-dom';
import useObservable from 'react-use/lib/useObservable';
import { reactRouterNavigate, useKibana } from '@kbn/kibana-react-plugin/public';
import { NoDataViewsPromptComponent } from '@kbn/shared-ux-prompt-no-data-views';
import { NoDataViewsPromptComponent, useOnTryESQL } from '@kbn/shared-ux-prompt-no-data-views';
import type { SpacesContextProps } from '@kbn/spaces-plugin/public';
import { DataViewType } from '@kbn/data-views-plugin/public';
import { RollupDeprecationTooltip } from '@kbn/rollup';
@ -86,6 +86,7 @@ export const IndexPatternTable = ({
application,
chrome,
dataViews,
share,
IndexPatternEditor,
spaces,
overlays,
@ -116,6 +117,12 @@ export const IndexPatternTable = ({
const hasDataView = useObservable(dataViewController.hasDataView$, defaults.hasDataView);
const hasESData = useObservable(dataViewController.hasESData$, defaults.hasEsData);
const useOnTryESQLParams = {
locatorClient: share?.url.locators,
navigateToApp: application.navigateToApp,
};
const onTryESQL = useOnTryESQL(useOnTryESQLParams);
const handleOnChange = ({ queryText, error }: { queryText: string; error: unknown }) => {
if (!error) {
setQuery(queryText);
@ -370,6 +377,8 @@ export const IndexPatternTable = ({
onClickCreate={() => setShowCreateDialog(true)}
canCreateNewDataView={application.capabilities.indexPatterns.save as boolean}
dataViewsDocLink={docLinks.links.indexPatterns.introduction}
onTryESQL={onTryESQL}
esqlDocLink={docLinks.links.query.queryESQL}
emptyPromptColor={'subdued'}
/>
</>

View file

@ -17,6 +17,7 @@ import { StartServicesAccessor } from '@kbn/core/public';
import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import { ManagementAppMountParams } from '@kbn/management-plugin/public';
import { NoDataViewsPromptKibanaProvider } from '@kbn/shared-ux-prompt-no-data-views';
import {
IndexPatternTableWithRouter,
EditIndexPatternContainer,
@ -64,11 +65,13 @@ export async function mountManagementSection(
dataViews,
fieldFormats,
unifiedSearch,
share,
spaces,
savedObjectsManagement,
},
indexPatternManagementStart,
] = await getStartServices();
const canSave = dataViews.getCanSaveSync();
if (!canSave) {
@ -89,6 +92,7 @@ export async function mountManagementSection(
chrome,
uiSettings,
settings,
share,
notifications,
overlays,
unifiedSearch,
@ -115,23 +119,29 @@ export async function mountManagementSection(
ReactDOM.render(
<KibanaRenderContextProvider {...startServices}>
<KibanaContextProvider services={deps}>
<Router history={params.history}>
<Routes>
<Route path={['/create']}>
<IndexPatternTableWithRouter canSave={canSave} showCreateDialog={true} />
</Route>
<Route path={createEditPath}>
<CreateEditFieldContainer />
</Route>
<Route path={['/dataView/:id']}>
<EditIndexPatternContainer />
</Route>
<Redirect path={'/patterns*'} to={'dataView*'} />
<Route path={['/']}>
<IndexPatternTableWithRouter canSave={canSave} />
</Route>
</Routes>
</Router>
<NoDataViewsPromptKibanaProvider
coreStart={{ ...startServices, docLinks, application }}
dataViewEditor={dataViewEditor}
share={share}
>
<Router history={params.history}>
<Routes>
<Route path={['/create']}>
<IndexPatternTableWithRouter canSave={canSave} showCreateDialog={true} />
</Route>
<Route path={createEditPath}>
<CreateEditFieldContainer />
</Route>
<Route path={['/dataView/:id']}>
<EditIndexPatternContainer />
</Route>
<Redirect path={'/patterns*'} to={'dataView*'} />
<Route path={['/']}>
<IndexPatternTableWithRouter canSave={canSave} />
</Route>
</Routes>
</Router>
</NoDataViewsPromptKibanaProvider>
</KibanaContextProvider>
</KibanaRenderContextProvider>,
params.element

View file

@ -21,6 +21,7 @@ import { ManagementSetup } from '@kbn/management-plugin/public';
import { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public';
import { SpacesPluginStart } from '@kbn/spaces-plugin/public';
import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public';
import { SharePluginStart } from '@kbn/share-plugin/public';
export interface IndexPatternManagementSetupDependencies {
management: ManagementSetup;
@ -34,6 +35,7 @@ export interface IndexPatternManagementStartDependencies {
dataViewEditor: DataViewEditorStart;
dataViews: DataViewsPublicPluginStart;
fieldFormats: FieldFormatsStart;
share?: SharePluginStart;
spaces?: SpacesPluginStart;
unifiedSearch: UnifiedSearchPublicPluginStart;
savedObjectsManagement: SavedObjectsManagementPluginStart;

View file

@ -29,6 +29,7 @@ import type { SpacesPluginStart } from '@kbn/spaces-plugin/public';
import type { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public';
import type { SettingsStart } from '@kbn/core-ui-settings-browser';
import type { NoDataPagePluginSetup } from '@kbn/no-data-page-plugin/public';
import { SharePluginStart } from '@kbn/share-plugin/public';
import type { IndexPatternManagementStart } from '.';
import type { DataViewMgmtService } from './management_app/data_view_management_service';
@ -53,6 +54,7 @@ export interface IndexPatternManagmentContext extends StartServices {
fieldFormatEditors: IndexPatternFieldEditorStart['fieldFormatEditors'];
IndexPatternEditor: DataViewEditorStart['IndexPatternEditorComponent'];
fieldFormats: FieldFormatsStart;
share?: SharePluginStart;
spaces?: SpacesPluginStart;
savedObjectsManagement: SavedObjectsManagementPluginStart;
noDataPage?: NoDataPagePluginSetup;

View file

@ -45,6 +45,7 @@
"@kbn/code-editor",
"@kbn/react-kibana-mount",
"@kbn/rollup",
"@kbn/share-plugin",
],
"exclude": [
"target/**/*",

View file

@ -0,0 +1,33 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const kibanaServer = getService('kibanaServer');
const testSubjects = getService('testSubjects');
const esql = getService('esql');
const PageObjects = getPageObjects(['discover', 'dashboard']);
describe('No Data Views: Try ES|QL', () => {
before(async () => {
await kibanaServer.savedObjects.cleanStandardList();
});
it('enables user to create a dashboard with ES|QL from no-data-prompt', async () => {
await PageObjects.dashboard.navigateToApp();
await testSubjects.existOrFail('noDataViewsPrompt');
await testSubjects.click('tryESQLLink');
await PageObjects.discover.expectOnDiscover();
await esql.expectEsqlStatement('FROM logs* | LIMIT 10');
});
});
}

View file

@ -37,5 +37,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./dashboard_snapshots'));
loadTestFile(require.resolve('./embeddable_library'));
loadTestFile(require.resolve('./dashboard_esql_chart'));
loadTestFile(require.resolve('./dashboard_esql_no_data'));
});
}

View file

@ -0,0 +1,34 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const kibanaServer = getService('kibanaServer');
const testSubjects = getService('testSubjects');
const esql = getService('esql');
const PageObjects = getPageObjects(['settings', 'common', 'discover']);
describe('No Data Views: Try ES|QL', () => {
before(async () => {
await kibanaServer.savedObjects.cleanStandardList();
});
it('navigates to Discover and presents an ES|QL query', async () => {
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaIndexPatterns();
await testSubjects.existOrFail('noDataViewsPrompt');
await testSubjects.click('tryESQLLink');
await PageObjects.discover.expectOnDiscover();
await esql.expectEsqlStatement('FROM logs* | LIMIT 10');
});
});
}

View file

@ -38,6 +38,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./data_views/_legacy_url_redirect'));
loadTestFile(require.resolve('./data_views/_exclude_index_pattern'));
loadTestFile(require.resolve('./data_views/_index_pattern_filter'));
loadTestFile(require.resolve('./data_views/_try_esql'));
loadTestFile(require.resolve('./data_views/_scripted_fields_filter'));
loadTestFile(require.resolve('./_import_objects'));
loadTestFile(require.resolve('./data_views/_test_huge_fields'));

View file

@ -32,6 +32,12 @@ export class DiscoverPageObject extends FtrService {
private readonly defaultFindTimeout = this.config.get('timeouts.find');
/** Ensures that navigation to discover has completed */
public async expectOnDiscover() {
await this.testSubjects.existOrFail('discoverNewButton');
await this.testSubjects.existOrFail('discoverOpenButton');
}
public async getChartTimespan() {
return await this.testSubjects.getAttribute('unifiedHistogramChart', 'data-time-range');
}

View file

@ -7,12 +7,19 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import expect from '@kbn/expect';
import { FtrService } from '../ftr_provider_context';
export class ESQLService extends FtrService {
private readonly retry = this.ctx.getService('retry');
private readonly testSubjects = this.ctx.getService('testSubjects');
/** Ensures that the ES|QL code editor is loaded with a given statement */
public async expectEsqlStatement(statement: string) {
const codeEditor = await this.testSubjects.find('ESQLEditor');
expect(await codeEditor.getAttribute('innerText')).to.contain(statement);
}
public async getHistoryItems(): Promise<string[][]> {
const queryHistory = await this.testSubjects.find('ESQLEditor-queryHistory');
const tableBody = await this.retry.try(async () => queryHistory.findByTagName('tbody'));

View file

@ -7274,9 +7274,6 @@
"sharedUXPackages.fileUpload.uploadCompleteButtonLabel": "Chargement terminé",
"sharedUXPackages.fileUpload.uploadDoneToolTipContent": "Votre fichier a bien été chargé !",
"sharedUXPackages.fileUpload.uploadingButtonLabel": "Chargement",
"sharedUXPackages.no_data_views.esqlButtonLabel": "Langue : ES|QL",
"sharedUXPackages.no_data_views.esqlDocsLink": "En savoir plus.",
"sharedUXPackages.no_data_views.esqlMessage": "Vous pouvez aussi rechercher vos données en utilisant directement ES|QL. {docsLink}",
"sharedUXPackages.noDataConfig.addIntegrationsDescription": "Utilisez Elastic Agent pour collecter des données et créer des solutions Analytics.",
"sharedUXPackages.noDataConfig.addIntegrationsTitle": "Ajouter des intégrations",
"sharedUXPackages.noDataConfig.analytics": "Analyse",
@ -7298,8 +7295,6 @@
"sharedUXPackages.noDataViewsPrompt.dataViewExplanation": "Les vues de données identifient les données Elasticsearch que vous souhaitez explorer. Vous pouvez faire pointer des vues de données vers un ou plusieurs flux de données, index et alias d'index, tels que vos données de log d'hier, ou vers tous les index contenant vos données de log.",
"sharedUXPackages.noDataViewsPrompt.learnMore": "Envie d'en savoir plus ?",
"sharedUXPackages.noDataViewsPrompt.noPermission.dataViewExplanation": "Les vues de données identifient les données Elasticsearch que vous souhaitez explorer. Pour créer des vues de données, demandez les autorisations requises à votre administrateur.",
"sharedUXPackages.noDataViewsPrompt.noPermission.title": "Vous devez disposer d'une autorisation pour pouvoir créer des vues de données",
"sharedUXPackages.noDataViewsPrompt.nowCreate": "Créez à présent une vue de données.",
"sharedUXPackages.noDataViewsPrompt.readDocumentation": "Lisez les documents",
"sharedUXPackages.noDataViewsPrompt.youHaveData": "Vous avez des données dans Elasticsearch.",
"sharedUXPackages.prompt.errors.notFound.body": "Désolé, la page que vous recherchez est introuvable. Elle a peut-être été retirée ou renommée, ou peut-être qu'elle n'a jamais existé.",

View file

@ -7028,9 +7028,6 @@
"sharedUXPackages.fileUpload.uploadCompleteButtonLabel": "アップロード完了",
"sharedUXPackages.fileUpload.uploadDoneToolTipContent": "ファイルは正常にアップロードされました。",
"sharedUXPackages.fileUpload.uploadingButtonLabel": "アップロード中",
"sharedUXPackages.no_data_views.esqlButtonLabel": "言語ES|QL",
"sharedUXPackages.no_data_views.esqlDocsLink": "詳細情報",
"sharedUXPackages.no_data_views.esqlMessage": "あるいは、直接ES|QLを使用してデータをクエリできます。{docsLink}",
"sharedUXPackages.noDataConfig.addIntegrationsDescription": "Elasticエージェントを使用して、データを収集し、分析ソリューションを構築します。",
"sharedUXPackages.noDataConfig.addIntegrationsTitle": "統合の追加",
"sharedUXPackages.noDataConfig.analytics": "分析",
@ -7052,8 +7049,6 @@
"sharedUXPackages.noDataViewsPrompt.dataViewExplanation": "データビューは、探索するElasticsearchデータを特定します。昨日からのログデータ、ログデータを含むすべてのインデックスなど、1つ以上のデータストリーム、インデックス、インデックスエイリアスをデータビューで参照できます。",
"sharedUXPackages.noDataViewsPrompt.learnMore": "詳細について",
"sharedUXPackages.noDataViewsPrompt.noPermission.dataViewExplanation": "データビューは、探索するElasticsearchデータを特定します。データビューを作成するには、必要な権限を管理者に依頼してください。",
"sharedUXPackages.noDataViewsPrompt.noPermission.title": "データビューを作成するための権限が必要です。",
"sharedUXPackages.noDataViewsPrompt.nowCreate": "ここでデータビューを作成します。",
"sharedUXPackages.noDataViewsPrompt.readDocumentation": "<b>ドキュメント</b><b>を読む</b>",
"sharedUXPackages.noDataViewsPrompt.youHaveData": "Elasticsearchにデータがあります。",
"sharedUXPackages.prompt.errors.notFound.body": "申し訳ございません。お探しのページは見つかりませんでした。削除または名前変更されたか、そもそも存在していなかった可能性があります。",

View file

@ -7043,9 +7043,6 @@
"sharedUXPackages.fileUpload.uploadCompleteButtonLabel": "上传完成",
"sharedUXPackages.fileUpload.uploadDoneToolTipContent": "您的文件已成功上传!",
"sharedUXPackages.fileUpload.uploadingButtonLabel": "正在上传",
"sharedUXPackages.no_data_views.esqlButtonLabel": "语言ES|QL",
"sharedUXPackages.no_data_views.esqlDocsLink": "了解详情。",
"sharedUXPackages.no_data_views.esqlMessage": "或者,您可以直接使用 ES|QL 查询数据。{docsLink}",
"sharedUXPackages.noDataConfig.addIntegrationsDescription": "使用 Elastic 代理收集数据并增建分析解决方案。",
"sharedUXPackages.noDataConfig.addIntegrationsTitle": "添加集成",
"sharedUXPackages.noDataConfig.analytics": "分析",
@ -7067,8 +7064,6 @@
"sharedUXPackages.noDataViewsPrompt.dataViewExplanation": "数据视图标识您要浏览的 Elasticsearch 数据。您可以将数据视图指向一个或多个数据流、索引和索引别名(例如昨天的日志数据),或包含日志数据的所有索引。",
"sharedUXPackages.noDataViewsPrompt.learnMore": "希望了解详情?",
"sharedUXPackages.noDataViewsPrompt.noPermission.dataViewExplanation": "数据视图标识您要浏览的 Elasticsearch 数据。要创建数据视图,请联系管理员获得所需权限。",
"sharedUXPackages.noDataViewsPrompt.noPermission.title": "您需要权限以创建数据视图",
"sharedUXPackages.noDataViewsPrompt.nowCreate": "现在,创建数据视图。",
"sharedUXPackages.noDataViewsPrompt.readDocumentation": "阅读文档",
"sharedUXPackages.noDataViewsPrompt.youHaveData": "您在 Elasticsearch 中有数据。",
"sharedUXPackages.prompt.errors.notFound.body": "抱歉,找不到您要查找的页面。该页面可能已移除、重命名,或可能根本不存在。",

View file

@ -131,10 +131,12 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
expect(navLinks).to.eql(['Stack Management']);
});
it(`index pattern listing doesn't show create button`, async () => {
it(`index pattern listing shows disabled create button`, async () => {
await settings.clickKibanaIndexPatterns();
await testSubjects.existOrFail('noDataViewsPrompt');
await testSubjects.missingOrFail('createDataViewButton');
const createDataViewButton = await testSubjects.find('createDataViewButton');
const isDisabled = await createDataViewButton.getAttribute('disabled');
expect(isDisabled).to.be('true');
});
it(`shows read-only badge`, async () => {